Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (798)
Games in Android Showcase (234)
games submitted by our members
Games in WIP (865)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: 1 2 [3]
  ignore  |  Print  
  Extremely Fast sine/cosine  (Read 50592 times)
0 Members and 1 Guest are viewing this topic.
Offline Icecore
« Reply #60 - Posted 2015-10-06 16:17:28 »

open source - A truly rational person: "it's a complete waste of time"
And it is )
Open source != work for free.
Open source != you ought someone something.
Open source == help if you want.

http://sarah.thesharps.us/2015/10/05/closing-a-door
>I have hope that the Linux kernel community will change over time.
Imho: It doesn't: public open source is same like == Dota, CS
Any one can enter -
Many unmannered and unethical – no respect , only EGO

Last known State: Reassembled in Cyberspace
End Transmission....
..
.
Journey began Now)
Offline Riven
Administrator

« JGO Overlord »


Medals: 1369
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #61 - Posted 2015-10-06 17:14:00 »

When @theagentd or @Riven don know what Roquen's on about though I know we're doomed, because they're amongst the two most intelligent people on the board.

I am as easy to flatter as the next guy, but I think you overdid it slightly. You decided to name two amongst the best two ? What kind of marketing ploy is that! All I know is it worked and I will be having sweet dreams.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
Offline ziozio
« Reply #62 - Posted 2015-10-06 18:19:36 »

Quote
Quote
yet you expect every single person who read (and actually want to understand) your comments to spend a significant amount of time Googling to decode your posts.
Or simply post:  can you explain this?  Look above I talk about dependency chains.  Explaining that for the widest audience would require giving a complete overview of how modern CPUs work. Or dig up a bunch of URLs for people to wade through.  

Roquen

One end of the spectrum is to post in extreme detail and provide a complete overview on the topic that even the stupidest of people can understand (high effort  / low reward for effort) and at the other end is to post a set of facts without any explanation at all and no detail as to why the facts are relevant to the conversation (very low effort / still low reward for effort). You tend to post very much on the later side of the scale, the fact that there is little or no explanation means you get very little interaction with the community even if the community is knowledgeable in the field you are posting about.

I'm not asking you to change your style as its a personal choice and you are free to post what you want and in any style you want but I wanted to illustrate why people get frustrated with the confusion nature of your posts.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline philfrei
« Reply #63 - Posted 2015-10-06 18:35:15 »

Personally I think it's a good idea to periodically think about the opportunity cost associated with any decision you make and decide from there if it's a good idea.

This also applies to the reader's decisions about what to read!

Yes, organizing the presentation of material so that it can be grasped easily takes time, is at the heart of a good writer's craft.

All this said, I have found many of Roquen's posts to be helpful. Some could be more helpful, but that is an ongoing challenge for all of us.

music and music apps: http://adonax.com
Offline princec

« JGO Spiffy Duke »


Medals: 1106
Projects: 3
Exp: 20 years


Eh? Who? What? ... Me?


« Reply #64 - Posted 2015-10-06 18:56:35 »

When @theagentd or @Riven don know what Roquen's on about though I know we're doomed, because they're amongst the two most intelligent people on the board.

I am as easy to flatter as the next guy, but I think you overdid it slightly. You decided to name two amongst the best two ? What kind of marketing ploy is that! All I know is it worked and I will be having sweet dreams.
Medal for being a smartarse!  Roll Eyes

Cas Smiley

Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #65 - Posted 2015-10-06 19:01:28 »

(eval (macroexpand roquen))

EDIT: sumOfAngles wasn't computing the full data set  Emo

Code: (how bad is it?) http://pastebin.java-gaming.org/af3b23b7d3a1b

Results:

Benchmark                Mode  Samples      Score  Score error  Units
b.Test.scalarFastMath    avgt       10   2514.448      255.431  us/op
b.Test.scalarJavaMath    avgt       10  80388.179      350.451  us/op
b.Test.sumOfAngles       avgt       10   3061.736       24.541  us/op


Couldn't get jitwatch working though, so it probably could be better.
Offline KaiHH

JGO Kernel


Medals: 708



« Reply #66 - Posted 2015-10-07 09:24:36 »

@BurntPizza, would you mind including theagentd's interpolation method also in your benchmark, as in the following:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
    public static final float PI = (float) java.lang.Math.PI;
    private static final float PI2 = PI * 2;
    private static final float PIHalf = PI * 0.5f;
    private static final int lookupBits = 9;
    private static final int lookupTableSize = 1 << lookupBits;
    private static final int lookupTableSizeMinus1 = lookupTableSize - 1;
    private static final int lookupTableSizeWithMargin = lookupTableSize + 1;
    private static final float sinTable[] = new float[lookupTableSizeWithMargin];
    private static final float pi2OverLookupSize = PI2 / lookupTableSize;
    private static final float lookupSizeOverPi2 = lookupTableSize / PI2;
    static {
        for (int i = 0; i < lookupTableSizeWithMargin; i++) {
            double d = i * pi2OverLookupSize;
            sinTable[i] = (float) java.lang.Math.sin(d);
        }
    }
    public static float sin(float rad) {
        float index = rad * lookupSizeOverPi2;
        int ii = (int) index;
        float alpha = index - ii;
        int i = ii & lookupTableSizeMinus1;
        float sin1 = sinTable[i];
        float sin2 = sinTable[i + 1];
        return sin1 * (1.0f - alpha) + sin2 * alpha;
    }
    public static float cos(float rad) {
        return sin(rad + PIHalf);
    }
Offline Spasi
« Reply #67 - Posted 2015-10-07 11:34:10 »

EDIT: sumOfAngles wasn't computing the full data set  Emo

What was the problem exactly? I cleaned your code a bit and verified output equivalence of the 3 implementations and got:

1  
2  
3  
scalarFastMath    3110,062 us/op
scalarJavaMath  101201,482 us/op
sumOfAngles       2280,336 us/op


Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #68 - Posted 2015-10-07 17:20:51 »

What was the problem exactly?

Notice the loop bounds on the old pastebin:
for (int i = 0; i < state.N; i += 4) { // derp; should be N * 4


Write those unit tests kids!


@KaiHH

Benchmark                 Mode  Samples      Score  Score error  Units
b.Test.scalarAgentMath    avgt       10   5209.268      103.278  us/op
b.Test.scalarJavaMath     avgt       10  83709.325      960.562  us/op
b.Test.scalarRivenMath    avgt       10   2654.518      266.021  us/op
b.Test.sumOfAngles        avgt       10   3174.081      396.252  us/op


@Spasi

Could I see your sumOfAngles implementation? I'm curious as to how fastMath and sumOfAngles switched places.
I'm running this on jdk1.8_05, i7 4700MQ


Latest: http://pastebin.java-gaming.org/f3b2b4d7a3b1a
Offline Spasi
« Reply #69 - Posted 2015-10-07 18:24:56 »

Using your latest version:

1  
2  
Test.scalarRivenMath 2970,533 us/op
Test.sumOfAngles     2346,007 us/op

Could I see your sumOfAngles implementation? I'm curious as to how fastMath and sumOfAngles switched places.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
public void sumOfAngles(PrecompState state) {
   state.t += 1 / 60f;
 
   float sint = Riven.sin(state.t);
   float cost = Riven.cos(state.t);
   for (int i = 0; i < state.N; i++) {
      float cosx = state.in[i * 4 + 0];
      float sinx = state.in[i * 4 + 1];
      float cosy = state.in[i * 4 + 2];
      float siny = state.in[i * 4 + 3];
      state.out[i * 2 + 0] = cosx * sint + sinx * cost;
      state.out[i * 2 + 1] = cosy * sint + siny * cost;
   }
}

Results:

1  
2  
Test.scalarRivenMath 2972,617 us/op
Test.sumOfAngles     2227,220 us/op

Running on a 3.1GHz Sandy Bridge i5 with Java 8u60 x64.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Spasi
« Reply #70 - Posted 2015-10-07 19:13:15 »

Results on MBP 2015 (3.1GHz Broadwell i7, JDK 8u40 x64)

1  
2  
3  
4  
5  
6  
// latest version
scalarRivenMath  2005.062 ± 122.597  us/op
sumOfAngles      2192.988 ± 404.607  us/op
// my sumOfAngles
scalarRivenMath  2024.594 ± 233.004  us/op
sumOfAngles      2031.853 ± 146.233  us/op

Disclaimer: I saw high variance between runs, which I attribute to CPU thermal throttling. Had to let the laptop cool down a bit before getting consistent results. This might be affecting you too.

The problem seems to be related to less efficient cache use on laptop CPUs. sumOfAngles becomes almost 40% faster with 1/10th the workload (N = 100_000).
Offline theagentd
« Reply #71 - Posted 2015-10-07 22:15:51 »

Disclaimer: I saw high variance between runs, which I attribute to CPU thermal throttling. Had to let the laptop cool down a bit before getting consistent results. This might be affecting you too.

Use ThrottleStop. It saved me when my computer was throttling during games. You can even underclock your computer with it to get consistent results.

Myomyomyo.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #72 - Posted 2015-10-08 00:30:49 »

Hmm, things definitely get more interesting at lower input sizes. That caching-[regression?] is pretty disappointing.

http://pastebin.java-gaming.org/3b2bd5a7b3a16


N = 1_000_000

Benchmark                 Mode  Samples      Score  Score error  Units
b.Test.scalarAgentMath    avgt       10   5433.443       24.239  us/op
b.Test.scalarJavaMath     avgt       10  89490.975      592.976  us/op
b.Test.scalarRivenMath    avgt       10   2642.146       60.246  us/op
b.Test.sumOfAngles        avgt       10   3321.570       23.815  us/op
b.Test.sumOfAnglesSOA     avgt       10   3300.369       46.918  us/op

N = 100_000

Benchmark                 Mode  Samples     Score  Score error  Units
b.Test.scalarAgentMath    avgt       10   507.983        4.833  us/op
b.Test.scalarJavaMath     avgt       10  8739.558       67.833  us/op
b.Test.scalarRivenMath    avgt       10   219.087        4.151  us/op
b.Test.sumOfAngles        avgt       10   143.886       10.281  us/op
b.Test.sumOfAnglesSOA     avgt       10    70.607        3.719  us/op

N = 10_000

Benchmark                 Mode  Samples    Score  Score error  Units
b.Test.scalarAgentMath    avgt       10   51.648        0.908  us/op
b.Test.scalarJavaMath     avgt       10  903.191        4.800  us/op
b.Test.scalarRivenMath    avgt       10   22.001        0.099  us/op
b.Test.sumOfAngles        avgt       10   14.263        0.107  us/op
b.Test.sumOfAnglesSOA     avgt       10    4.646        0.019  us/op


I got jitwatch working and can confirm the SOA variant is using packed arithmetic.
Offline theagentd
« Reply #73 - Posted 2015-10-08 01:27:59 »

What does "Score error" mean?

Myomyomyo.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #74 - Posted 2015-10-08 01:30:00 »

http://stackoverflow.com/questions/24723527/what-does-openjdk-jmh-score-error-exactly-mean
Offline theagentd
« Reply #75 - Posted 2015-10-08 01:31:12 »

Right. What about precision? What's the accuracy of those methods? It'd be interesting to see the performance/quality tradeoffs they make.

Myomyomyo.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #76 - Posted 2015-10-08 01:39:16 »

Output of abs(expected - actual) across whole array, at N = 1_000_000:
(Ignore DoubleXX, all the comp is done in single precision)


agent
DoubleSummaryStatistics{count=2000000, sum=33.722636, min=0.000000, average=0.000017, max=0.000083}

sumOfAnglesSOA
DoubleSummaryStatistics{count=2000000, sum=745.938334, min=0.000000, average=0.000373, max=0.001289}

riven
DoubleSummaryStatistics{count=2000000, sum=488.212535, min=0.000000, average=0.000244, max=0.000762}

sumOfAngles
DoubleSummaryStatistics{count=2000000, sum=745.938334, min=0.000000, average=0.000373, max=0.001289}
Offline theagentd
« Reply #77 - Posted 2015-10-08 01:42:07 »

Interesting. I wasn't expecting mine to beat the SOA. Are you sure you're not getting an unrealistic amount of compound error?

Myomyomyo.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #78 - Posted 2015-10-08 01:50:10 »

The real thing to notice is that the sumOfAngles (both variants) were using Riven's trig. Using Math.sin/cos it has very little error:

sumOfAngles
DoubleSummaryStatistics{count=20000, sum=0.025140, min=0.000000, average=0.000001, max=0.000004}

sumOfAnglesSOA
DoubleSummaryStatistics{count=20000, sum=0.025140, min=0.000000, average=0.000001, max=0.000004}

agent
DoubleSummaryStatistics{count=20000, sum=0.250961, min=0.000000, average=0.000013, max=0.000083}

riven
DoubleSummaryStatistics{count=20000, sum=5.018816, min=0.000000, average=0.000251, max=0.000762}


Speed is basically unaffected, as expected:


N = 10_000

Benchmark                 Mode  Samples    Score  Score error  Units
b.Test.scalarAgentMath    avgt       10   50.894        1.278  us/op
b.Test.scalarJavaMath     avgt       10  876.978        6.797  us/op
b.Test.scalarRivenMath    avgt       10   21.657        0.129  us/op
b.Test.sumOfAngles        avgt       10   14.239        0.094  us/op
b.Test.sumOfAnglesSOA     avgt       10    4.663        0.041  us/op


I probably should have remembered to do this earlier.
Offline theagentd
« Reply #79 - Posted 2015-10-08 03:00:11 »

Aha, that explains it! Yes, this is more in line with what I expected. Thank you.


I found a bug in my implementation. To floor the index, I simply do (int)index. This produces wrong results for negative angles. A correct implementation should use (float)Math.floor(index) instead.

I also found an optimization:
sin1 + (sin2 - sin1) * alpha
is faster than the original
alpha * sin1 + (1 - alpha) * sin2
by a solid 10-15%.

My full code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
   private static final int SIN_BITS, SIN_MASK, SIN_COUNT;
   private static final float radToIndex, cosOffset;
   public static final float[] sin;
   
   static {
      SIN_BITS = 9;
      SIN_MASK = ~(-1 << SIN_BITS);
      SIN_COUNT = SIN_MASK + 1;
     
      float radFull = (float) (Math.PI * 2.0);
      radToIndex = SIN_COUNT / radFull;
     
      cosOffset = (float)(Math.PI / 2);
     
      sin = new float[SIN_COUNT+1];
     
      for (int i = 0; i <= SIN_COUNT; i++) {
         sin[i] = (float) Math.sin(Math.PI * 2 * i / SIN_COUNT);
      }
   }
   
   public static final float sin(float rad) {
      float index = rad * radToIndex;
      //float floor = (float)Math.floor(index); //Correct
      float floor = (int)index;                 //Fast, only for positive angles
     
      float alpha = index - floor;
     
      int i = (int)(index) & SIN_MASK;
     
      float sin1 = sin[i+0];
      float sin2 = sin[i+1];
     
      return sin1 + (sin2 - sin1) * alpha;
   }
   
   
   public static final float cos(float rad) {
      return sin(rad + cosOffset);
   }

Myomyomyo.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #80 - Posted 2015-10-08 03:12:54 »

Yep, from 50.8 us/104 ops to 46.4. But with Math.floor its about 90.

Current code, hopefully last one for today: http://pastebin.java-gaming.org/b2bda6b7a361e
Offline ziozio
« Reply #81 - Posted 2015-10-08 05:18:23 »

These latest benchmarks aren't really giving a fair comparison. There are 2 measurable items

1) The method for calculating Sin  or Cos = Java / Agent / Riven
2) The method for calculating Sin (t+x) and Sin (t+y)  = Scalar / Sum of Angles/ Sum of Angles SOA

To be a fair comparison you need to test all the possible combinations, so far Sum of Angles / Sum of Angles SOA are only tested using the slowest sin and cos method (Java)
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #82 - Posted 2015-10-08 05:34:34 »

This particular benchmark is for the sin(t + xi,yj) scenario, as presented by agentd.
The 2nd 'kind' of calculation was to implement roquen's insight on how to algebraically optimize this scenario, as opposed to making the naive formula fast.
You'll also notice that the sumOfAngles technique's speed is almost completely independent of the speed of the underlying trig functions. So java stdlib is used for accuracy.
Offline Roquen

JGO Kernel


Medals: 518



« Reply #83 - Posted 2015-10-08 09:26:55 »

A note on lerp:  f(t) = (1-t)a + tb

It can be expressed in three forms
1) (1-t)a + tb
2) a - at + bt
3) a +t(b-a)

They all have have the same max-chain length (3):
(1-t)a + t b:
  t00 = (1-f) | t01 = t * b
  t10 = t00 * a
  t20 = t10 + t01


a - t a + t b:
  t00 = t * a | t01 = t * b
  t10 = a - t00
  t20 = t10 + t01


a + t(b - a):
  t00 = b - a
  t10 = t * t00
  t20 = a + t10


Forms (1) & (2) requires 4 operations and (3) requires, well, 3.

I can't think of a reason why (2) might be a "better" choice...just mentioned for completeness. Personally I use (3) as my default.  The one less instruction to issue means that it's generally faster.  It also is in a form that allows performing a fused-add-multiply (FMA)...but I don't believe HotSpot generates these instructions yet.  With an FMA opcode the chain-length and operation count become 2:

  t00 = b - a
  t10 = t * t00 + a


The potentially big BUT here is this, if t=1:

a + 1.0*(b - a) = a + (b - a)

which generally in floating point does not result in 'b'.  So if having the exact values at the end-point is desired you want to avoid (3).  Both (1) & (2) are exact at the endpoints.  (2) must be computed as: (a - at) + bt for this to be true.

-------
A note on Math.floor:  This has stricter contract than the problem requires and could be replaced with something like:

 floor = x >= 0 ? x : x-1


DISCLAIMER: I actually have though this through enough...the x-1 could a be problem...humm....
Also newer hardware have a floor opcode..don't know if HotSpot emits or not, the numbers above seem to be saying no.



Offline princec

« JGO Spiffy Duke »


Medals: 1106
Projects: 3
Exp: 20 years


Eh? Who? What? ... Me?


« Reply #84 - Posted 2015-10-08 09:55:02 »

My 2p: at least with the games code I've written over the last 10 years we almost always want f(1.0)==b, that is, exact. /me ponders why this specific and very common operation never made it into a single machine code instruction.

Cas Smiley

Offline Roquen

JGO Kernel


Medals: 518



« Reply #85 - Posted 2015-10-08 11:42:06 »

Intel has been thinking about it.  The various FMA ops are hard and lerp is harder.  I know both Michael Abrash and Tom Forsyth pushed for LERP while at Intel.  Abrash gave a presentation on Larrabee where the first slide read:  "I never did get that lerp instruction!"

Oh I didn't have my thinky thinky cap on earlier, form (2) converts into FMA:
  t00 = a - t*a             // different opcode from FMA related set
  t10 = t * b + t00
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 486
Exp: 7 years



« Reply #86 - Posted 2015-10-08 14:35:31 »

@Roquen
From what I can gather HotSpot wouldn't be allowed to emit FMA ops because they yield different results:
http://stackoverflow.com/questions/22562510/does-java-strictfp-modifier-have-any-effect-on-modern-cpus#comment34358602_22562510
Related: http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2015-January/016809.html
Offline Riven
Administrator

« JGO Overlord »


Medals: 1369
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #87 - Posted 2015-10-08 16:36:43 »

@theagentd / BurntPizza: use my FastFloor impl instead of (int)Math.floor(...).

1  
2  
3  
4  
5  
6  
// valid input range: -BIG_ENOUGH .. INFINITY-ish
private static final int BIG_ENOUGH_INT = 65536; // whatever makes sense
private static final float BIG_ENOUGH_FLOAT = BIG_ENOUGH_INT;
public static int fastFloor(float x) {
   return (int)(x + BIG_ENOUGH_FLOAT) - BIG_ENOUGH_INT;
}
You trade input range for accuracy.

Not a single branch, nor cache trashing lookup table Pointing

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
Offline Roquen

JGO Kernel


Medals: 518



« Reply #88 - Posted 2015-10-08 19:52:27 »

I wholeheartedly endorse Riven's floor.  The domain reduction shouldn't be an issue here.  In cases where it is then the range is probably problematic elsewhere anyway.  It will flush small negative numbers to positive zero however...that small window is important not to forget.

On fma.   Hotspot could not issue in strictfp cases, otherwise it would be legal.  All of this is more in the future kinda thing anyway...fma ops are slower atm if memory serves.

All of this is from cell...otherwise I would have been more verbose.  For real even.

EDIT: for typos

More notes of FMA.  The compiler dev list chain starts with "a bug".  Someone added to the compiler a transform that would convert "Math.pow(x,2)" into x*x.  The later is more faster and more accurate.  The bug filed was that the interpreter would compute Math.pow(x,2) and once the code got compiled it would compute x*x..thus different results between the two.  The proposed solution was to fix the interpreter to detect and actually compute x*x as well.

The persons who's e-mail you linked was saying:  This is not a bug, the method is not marked strictfp..the transform should be considered legal.  And if you go down this route of attempting to have bit exact results across the interpreter and all compilers then you're unreasonably restricting the optimizations the compiler can perform because it's impossible for the interpreter to know how the code is going to be optimized...in fact it's part of the interpreter's job to collection information so the compiler can do it's thing.  The usage of FMA comes up in that context.  That author missed an important point:  This is exactly the situation the JVM was in before it started using SSE registers.  Computation would be performed on x87 in 64 bit mode...all 32-bit float ops were generating different results than the interpreter in non-strictfp methods.

The stackover thread doesn't apply.  It basically about strictfp on modern CPU doesn't do much of anything since hotspot isn't taking advantage of operations like FMA at the moment.
Offline mooman219
« Reply #89 - Posted 2015-10-14 22:33:13 »

@theagentd / BurntPizza: use my FastFloor impl instead of (int)Math.floor(...).

1  
2  
3  
4  
5  
6  
// valid input range: -BIG_ENOUGH .. INFINITY-ish
private static final int BIG_ENOUGH_INT = 65536; // whatever makes sense
private static final float BIG_ENOUGH_FLOAT = BIG_ENOUGH_INT;
public static int fastFloor(float x) {
   return (int)(x + BIG_ENOUGH_FLOAT) - BIG_ENOUGH_INT;
}
You trade input range for accuracy.

Not a single branch, nor cache trashing lookup table Pointing

Casual reminder to make the type of
1  
BIG_ENOUGH_FLOAT
to double otherwise there are rounding errors.
For example, the fastFloor function returns 100 for 99.999F when using just floats.
Pages: 1 2 [3]
  ignore  |  Print  
 
 

 
Riven (45 views)
2019-09-04 15:33:17

hadezbladez (4025 views)
2018-11-16 13:46:03

hadezbladez (1468 views)
2018-11-16 13:41:33

hadezbladez (4048 views)
2018-11-16 13:35:35

hadezbladez (780 views)
2018-11-16 13:32:03

EgonOlsen (4098 views)
2018-06-10 19:43:48

EgonOlsen (4685 views)
2018-06-10 19:43:44

EgonOlsen (2762 views)
2018-06-10 19:43:20

DesertCoockie (3657 views)
2018-05-13 18:23:11

nelsongames (3867 views)
2018-04-24 18:15:36
Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04:08

Deployment and Packaging
by gouessej
2018-08-22 08:03:45
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!