Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (480)
Games in Android Showcase (110)
games submitted by our members
Games in WIP (547)
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
  ignore  |  Print  
  My na(t)ive PerlinNoise impl. is 3 times slower than server VM  (Read 8417 times)
0 Members and 1 Guest are viewing this topic.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Posted 2010-08-19 13:02:34 »

I'm pretty much a C n00b, but I thought I would be able to implement Perlin Noise in C that would be at least as fast as my Java version. Guess not! Here is my 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  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
int   perm[512];

void initPerlinNoise()
   {
      int i, permutation[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
         14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 };

      for (i = 0; i < 256; i++)
         perm[256 + i] = perm[i] = permutation[i];
   }


inline float fade(float t)
   {
      return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
   }

inline float lerp(float t, float a, float b)
   {
      return a + t * (b - a);
   }

inline float grad(int hash, float x, float y, float z)
   {
      int h = hash & 15;
      float u = (h < 8) ? x : y;
      float v = (h < 4) ? y : ((h == 12 || h == 14) ? x : z);
      return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
   }

inline float noise(float x, float y, float z)
   {
      float fx = floor(x);
      float fy = floor(y);
      float fz = floor(z);

      int gx = (int) fx & 0xFF;
      int gy = (int) fy & 0xFF;
      int gz = (int) fz & 0xFF;

      float u = fade(x -= fx);
      float v = fade(y -= fy);
      float w = fade(z -= fz);

      int a0 = perm[gx + 0] + gy;
      int b0 = perm[gx + 1] + gy;
      int aa = perm[a0 + 0] + gz;
      int ab = perm[a0 + 1] + gz;
      int ba = perm[b0 + 0] + gz;
      int bb = perm[b0 + 1] + gz;

      float a1 = grad(perm[bb + 1], x - 1, y - 1, z - 1);
      float a2 = grad(perm[ab + 1], x - 0, y - 1, z - 1);
      float a3 = grad(perm[ba + 1], x - 1, y - 0, z - 1);
      float a4 = grad(perm[aa + 1], x - 0, y - 0, z - 1);
      float a5 = grad(perm[bb + 0], x - 1, y - 1, z - 0);
      float a6 = grad(perm[ab + 0], x - 0, y - 1, z - 0);
      float a7 = grad(perm[ba + 0], x - 1, y - 0, z - 0);
      float a8 = grad(perm[aa + 0], x - 0, y - 0, z - 0);

      float a2_1 = a2+u*(a1-a2);       //lerp(u, a2, a1);
     float a4_3 = a4+u*(a3-a4);       //lerp(u, a4, a3);
     float a6_5 = a6+u*(a5-a6);       //lerp(u, a6, a5);
     float a8_7 = a8+u*(a7-a8);       //lerp(u, a8, a7);
     float a8_5 = a8_7+v*(a6_5-a8_7); //lerp(v, a8_7, a6_5);
     float a4_1 = a4_3+v*(a2_1-a4_3); //lerp(v, a4_3, a2_1);
     float a8_1 = a8_5+w*(a4_1-a8_5); //lerp(w, a8_5, a4_1);

      return a8_1;
   }

   //

JNIEXPORT void JNICALL Java_pack_Code_perlin_1noise
   (JNIEnv *env, jclass clz, jlong off, jint pow)
{
   // pow=5  => dim=32  => 32*32*32 samples
  int bits, mask, elems, i, x, y, z, pow0, pow1, pow2;
   bits = 1 << pow;
   mask = bits - 1;
   elems = bits*bits*bits;

   pow0 = pow << 0;
   pow1 = pow << 1;
   pow2 = pow << 2;

   float* p = asFloat4Pointer(env, off);

   for(i=elems-1; i>=0; i--)
   {
      x = (i>>pow0) & mask;
      y = (i>>pow1) & mask;
      z = (i>>pow2) & mask;

      p[i] = noise(x*0.01f, y*0.01f, z*0.01f);
   }  
}


My Java code is pretty much identical.


The benchmark is: fill a grid of 32*32*32 (32768 samples)
C: 8.2ms (avg of 64 runs, no jni overhead as I benchmark it 100% in C)
Java: 2.3ms (after warmup, avg of 64 runs)

Java server VM is 3.5x faster?

What rookie mistake did I make? I expected HotSpot to check all array-indices as 'random' array access like these: perm[perm[gx + 1] + 1] should be rather hard for HotSpot to optimize away, or so I thought.

I compile C code with:
1  
gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at    -march=pentium3 -mfpmath=sse -fomit-frame-pointer -funroll-loops -O3
Unrolling the loop manually didn't change anything either.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline gouessej
« Reply #1 - Posted 2010-08-19 13:13:37 »

You're using a mix of C and C++, the keyword "inline" comes from C++.

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #2 - Posted 2010-08-19 13:18:57 »

http://www.greenend.org.uk/rjk/2003/03/inline.html

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Roquen
« Reply #3 - Posted 2010-08-19 13:21:14 »

inline has been a part of C since C99 spec and most compilers supported well before that.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #4 - Posted 2010-08-19 13:22:41 »

That, and I'm still curious why my C code is so slow.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline gouessej
« Reply #5 - Posted 2010-08-19 13:35:58 »

That, and I'm still curious why my C code is so slow.
How floor() is implemented in C?

Offline Roquen
« Reply #6 - Posted 2010-08-19 13:47:07 »

I think you've hit a good point.  floor will be the one function called.  The default floor most likely works by changing the FPU's control register.  You can try adding the --fast-math option.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #7 - Posted 2010-08-19 13:54:59 »

Hm... that commandline param didn't help, but:

1  
2  
3  
      float fx = x; //floor(x);
     float fy = y; //floor(y);
     float fz = z; //floor(z);

=> 0.25ms

Although incorrect, it's significantly faster.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #8 - Posted 2010-08-19 14:03:23 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
      int ix = (int)x;
      int iy = (int)y;
      int iz = (int)z;

      int gx = ix & 0xFF;
      int gy = iy & 0xFF;
      int gz = iz & 0xFF;

      float u = fade(x -= ix);
      float v = fade(y -= iy);
      float w = fade(z -= iz);


=> C: 3.3ms
=> J: 2.3ms

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #9 - Posted 2010-08-19 14:30:12 »

Reordered some operations, so that it stays in FPU or CPU mode longer:

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  
41  
42  
43  
44  
45  
46  
47  
48  
inline float noise(float x, float y, float z)
   {
      int ix,iy,iz;
      int gx,gy,gz;
      float u,v,w;
      int a0,b0,aa,ab,ba,bb;
      float aa0,ab0,ba0,bb0;
      float aa1,ab1,ba1,bb1;
      float a1,a2,a3,a4,a5,a6,a7,a8;
      float a8_5, a4_1;

      ix = (int)x; x-=ix;
      iy = (int)y; y-=iy;
      iz = (int)z; z-=iz;      

      gx = ix & 0xFF;
      gy = iy & 0xFF;
      gz = iz & 0xFF;
     
      a0 = gy+perm[gx];
      b0 = gy+perm[gx + 1];
      aa = gz+perm[a0];
      ab = gz+perm[a0 + 1];
      ba = gz+perm[b0];
      bb = gz+perm[b0 + 1];

      aa0 = perm[aa]; aa1 = perm[aa + 1];
      ab0 = perm[ab]; ab1 = perm[ab + 1];
      ba0 = perm[ba]; ba1 = perm[ba + 1];
      bb0 = perm[bb]; bb1 = perm[bb + 1];

      a1 = grad(bb1, x - 1, y - 1, z - 1);
      a2 = grad(ab1, x    , y - 1, z - 1);
      a3 = grad(ba1, x - 1, y    , z - 1);
      a4 = grad(aa1, x    , y    , z - 1);
      a5 = grad(bb0, x - 1, y - 1, z    );
      a6 = grad(ab0, x    , y - 1, z    );
      a7 = grad(ba0, x - 1, y    , z    );
      a8 = grad(aa0, x    , y    , z    );

      u = fade(x);
      v = fade(y);
      w = fade(z);

      a8_5 = lerp(v, lerp(u, a8, a7), lerp(u, a6, a5));
      a4_1 = lerp(v, lerp(u, a4, a3), lerp(u, a2, a1));
      return lerp(w, a8_5, a4_1);
   }

=> C: 2.9ms
=> J: 2.3ms


So what am I missing here? Maybe MinGW/GCC is simply not generating efficient machine code?

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Roquen
« Reply #10 - Posted 2010-08-19 14:40:36 »

I hardly ever use GCC.  If you're using linux, you can grab the intel compiler for non-com. usage.  At least for comparison purposes.  Maybe you need a newer version of GCC??  Also, why limit the instruction set to P3?  Isn't P4 a reasonable bottom end? 
Offline princec

JGO Kernel


Medals: 361
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #11 - Posted 2010-08-19 14:54:02 »

Plenty of P3's about.

Cas Smiley

Offline CaptainJester

JGO Knight


Medals: 12
Projects: 2
Exp: 14 years


Make it work; make it better.


« Reply #12 - Posted 2010-08-19 15:20:16 »

Would your noise function even get inlined?  If I remember correctly, the larger a function is the less chance it has of becoming inlined.  You should be able to check how much inlining is happening by removing the inline directives and check the resulting exe file size.  If there is a significant difference then a lot of inlining is happening.  I thought that if you are trying to call one inline function from another inline function it sinificantly reduces that chance of anything being inlined.  Try removing inline from noise() and see if it has any effect.

Offline Roquen
« Reply #13 - Posted 2010-08-19 15:32:16 »

An easier way to verify would be to just add "-c -S" and inspect the asm output.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #14 - Posted 2010-08-19 15:32:36 »

4x inline: 2906 microseconds
3x inline: 3000 microseconds
0x inline: 3006 microseconds

Barely a difference...

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #15 - Posted 2010-08-19 15:42:41 »

An easier way to verify would be to just add "-c -S" and inspect the asm output.

ASM dump: http://pastebin.com/DWuQyw7H

I'm way too new at this to draw any conclusions.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline gouessej
« Reply #16 - Posted 2010-08-19 16:10:14 »

Lol it seems difficult to beat Java :p

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #17 - Posted 2010-08-19 16:18:11 »

Optimized grad() by hardcoding the branches in a switch Grin
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  
inline float grad(int hash, float x, float y, float z)
   {  
   //float u = (h < 8) ? x : y;
  //float v = (h < 4) ? y : ((h == 12 || h == 14) ? x : z);
  //return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);

   switch(hash & 0xF)
   {
      case 0x0: return  x + y;
      case 0x1: return -x + y;
      case 0x2: return  x - y;
      case 0x3: return -x - y;
      case 0x4: return  x + x;
      case 0x5: return -x + x;
      case 0x6: return  x - x;
      case 0x7: return -x - x;
      case 0x8: return  y + x;
      case 0x9: return -y + x;
      case 0xA: return  y - x;
      case 0xB: return -y - x;
      case 0xC: return  y + z;
      case 0xD: return -y + x;
      case 0xE: return  y - x;
      case 0xF: return -y - z;
      default: return 0; // never happens
  }
   }


C: 1.5ms
J: 2.3ms (new grad() made java version slower, so not using it)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Roquen
« Reply #18 - Posted 2010-08-19 16:22:29 »


Well, noise itself is a leaf function (fully inlined).  Noise itself is not getting inlined. Line 714: call _noise.

You can (most likely) reduce the calling overhead by changing the prototype of noise to:

static inline float __fastcall noise(float x, float y, float z)

(register calling instead of stack call convention - which is good for GCC, MS & Intel)

My guess here is that the JIT is using SSE2 which will be a big win.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #19 - Posted 2010-08-19 16:35:19 »

Well, noise itself is a leaf function (fully inlined).  Noise itself is not getting inlined. Line 714: call _noise.

You can (most likely) reduce the calling overhead by changing the prototype of noise to:

static inline float __fastcall noise(float x, float y, float z)

(register calling instead of stack call convention - which is good for GCC, MS & Intel)

Interesting stuff, it didn't make any difference however. The hardcoded grad() seemed to be the bottleneck, and the call-overhead of noise() is probably negligible.

My guess here is that the JIT is using SSE2 which will be a big win.
Could you tell me how? Would the costs of switching between CPU/FPU be removed? HotSpot can't do SIMD yet.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Roquen
« Reply #20 - Posted 2010-08-20 09:03:52 »

HotSpot doesn't generate any parallel computations, but it will use SSE instructions when available.  You can grep in the source for supports_sse & supports_sse2.  There are a number of instructs which can have a big impact, even when computing a single result at a time. 
Offline tom
« Reply #21 - Posted 2010-08-20 09:46:46 »

One advantage with SSE is that x87 registers are stack based while the SSE registers directly addressable.

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #22 - Posted 2010-08-20 10:33:16 »

One advantage with SSE is that x87 registers are stack based while the SSE registers directly addressable.

Isn't code like _mm_mul_ss already SSE, or is that x87? The ASM dump is littered with instructions like MULSS.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline tom
« Reply #23 - Posted 2010-08-20 11:46:16 »

Didn't look at the ASM dump before now  persecutioncomplex Yes the code uses SSE. x87 instructions starts with f, like fmul, fadd etc.

Offline Roquen
« Reply #24 - Posted 2010-08-20 11:57:48 »

All of the _mm_* intrinsic functions are wrappers for SIMD opcodes (MMX on).  And _mm_mul_ss is a SSE-1 instruction.  As for the asm dump, you mean yours?  If so, then well, you told it to use SSE-1!

@tom: reduce memory motion is part of the picture.  In most cases the opcodes themselves will have a larger impact.  Simple example:

Lowering f2i looks like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
if (single_sse)
  emit(cvtsi2ss(..));   // single op convert reg to reg
else if (double_sse)
  emit(cvtsi2sd(..));   // single op convert reg to reg
else {
  emit(fldcw(..));   // BAM!! Change control word. wait til everything is done and change internal hardware state.
 emit(fist(...));     // store result to memory
 emit(mov(...));   // load result to dst reg
 emit(fldcw(..));   // BAM!!  What? Again?  Darn gotta restore it.
}
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #25 - Posted 2010-08-20 12:06:56 »

All of the _mm_* intrinsic functions are wrappers for SIMD opcodes (MMX on).  And _mm_mul_ss is a SSE-1 instruction.  As for the asm dump, you mean yours?  If so, then well, you told it to use SSE-1!
Well, we were trying to find out why the C code was slower than the JVM code (the math still is slower in C, I removed the branching bottleneck), and your and tom's suggestion/insinuation was that the JVM uses SSE, but as the C version uses SSE too, it means it can't be related to SSE. That was what confused me, so I double checked.

Anyway, thanks for your tips and suggestions, as it helped me getting rid of floor() and looking at the grad-function for alternative optimizations.


HotSpot doesn't generate any parallel computations, but it will use SSE instructions when available.  You can grep in the source for supports_sse & supports_sse2.  There are a number of instructs which can have a big impact, even when computing a single result at a time. 

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Roquen
« Reply #26 - Posted 2010-08-23 08:40:35 »

I missed this reply.  My "guess" is that you're on a machine newer than a P3 and the JIT is using newer instructions.  I would be very curious to see what the JIT is producing.  I've never run across a case where the VM was producing faster code.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #27 - Posted 2010-08-23 14:05:18 »

I missed this reply. 
This forum is 'messed up' -- I miss a lot of replies too, I often have to manually check whether there are new replies... the RSS feed is kind of a solution...

My "guess" is that you're on a machine newer than a P3 and the JIT is using newer instructions.  I would be very curious to see what the JIT is producing.  I've never run across a case where the VM was producing faster code.
I changed the "-march" to "pentium4" (I don't know the names of modern archs) and it didn't help. It could be that GCC is simply doing inefficient SSE: not reusing registers or converting intermediate SSE results to x87 when it inlines a method.

If I replace this code:
1  
2  
3  
4  
   inline float lerp(float t, float a, float b)
   {
      return a + t * (b - a);
   }

with (to my knowlegde) theoretically the same:
1  
#define lerp(t, a, b) (a + t * (b - a))

the result is 10% slower, so GCC is not generating the same assembly code in this trivial example.

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

Senior Member




Giving Java a second chance after ludumdare fiasco


« Reply #28 - Posted 2010-08-25 19:28:20 »

Have you tried running the C code natively and NOT via JNI and measured the time that way?  I think what you are seeing is over head from using JNI and NOT C.  JNI is notoriously slow.

Edit: never mind, you said that you did run the C version without JNI in the original post, my bad.  This is indeed a surprising result!
Offline Wildern

Junior Member





« Reply #29 - Posted 2010-09-10 23:01:45 »

You could try profiling the C version with gprof
http://www.cs.duke.edu/~ola/courses/programming/gprof.html
Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

atombrot (23 views)
2014-08-19 09:29:53

Tekkerue (22 views)
2014-08-16 06:45:27

Tekkerue (21 views)
2014-08-16 06:22:17

Tekkerue (12 views)
2014-08-16 06:20:21

Tekkerue (19 views)
2014-08-16 06:12:11

Rayexar (57 views)
2014-08-11 02:49:23

BurntPizza (37 views)
2014-08-09 21:09:32

BurntPizza (29 views)
2014-08-08 02:01:56

Norakomi (36 views)
2014-08-06 19:49:38

BurntPizza (66 views)
2014-08-03 02:57:17
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!