Hello Guest

1/4 of a second to set a single Uniform Variable

  • 5 Replies
  • 5779 Views
1/4 of a second to set a single Uniform Variable
« on: October 20, 2016, 18:50:13 »
OS: Linux Mint 18
Java Version: 1.8.0_91
LWJGL Version: 3.0.0 build 90
OpenGL Version: 4.5.0 NVIDIA 367.57

I got lags by programming a game with LWJGL 3, so i measured a few durations in my render methode until I noticed that once a few thousand times i set a uniform variable it takes way too long for no reason I noticed.
So I made a minimal program to show my problem: https://www.dropbox.com/s/u3q8vfoc47unzxa/minimal.zip?dl=0

Here is also a Video in which i show exactly waht i mean: https://www.youtube.com/watch?v=VkFPx_vsL9s

I think it's rather my fault then a bug but until I allready got somethink I am quite sure about it's a bug (http://forum.lwjgl.org/index.php?topic=6334.0) i am not sure anymore.

Any clue ?

Thank's for your effort.

*

Kai

Re: 1/4 of a second to set a single Uniform Variable
« Reply #1 on: October 20, 2016, 19:05:15 »
--> Garbage Collection!

Using a Memory Profiler, it shows that your demo application allocates huge amounts of direct byte buffers every frame.
The following is a snapshot of your demo application's profile:

The timeline above is in m:ss.

Over the course of one minute of running time, it allocated over 3 million direct byte buffers:


Suggestions for improvement:
- don't allocate a direct byte buffer inside Matrix4f.toFloatBuffer() but have that method take a ByteBuffer/FloatBuffer as parameter and put the matrix elements array to it
- cache a single FloatBuffer maybe inside of your Shader class and have its setUniformMat4f() method pass that to the Matrix4f.toFloatBuffer(FloatBuffer)
- implement Matrix4f.toFloatBuffer(FloatBuffer) as:
Code: [Select]
public FloatBuffer toFloatBuffer(FloatBuffer fb){
    fb.put(elements).flip();
    return fb;
}
« Last Edit: October 20, 2016, 19:29:41 by Kai »

Re: 1/4 of a second to set a single Uniform Variable
« Reply #2 on: October 20, 2016, 19:28:03 »
Regarding GC, one thing that I have noticed in my game is that if I do not cap the frame rate GC is no longer able to keep up. The problem for me is that most of the time my game does not do very much on the CPU side, as I have a largely static world. This means that it is able to run really fast (In older versions I was getting around 10000fps with simple instanced 3d models). At these rates, any temporary allocations in java add up really quickly. Because the main loop hogs the entire thread, garbage collection never gets a chance to clean up your mess. This then causes bad stutters because it has to "stop" your game for a moment in order to actually run. When limiting framerate, in my case I used v-sync, the issue immediately disappeared. Please note that I am by no means very experienced with how java works on a low level, so these are just my two cents.

Re: 1/4 of a second to set a single Uniform Variable
« Reply #3 on: October 20, 2016, 20:03:41 »
Huge Thank's you two, I not only solved the problem I also learned about Java in general.

*

Offline SHC

  • **
  • 94
    • GoHarsha.com
Re: 1/4 of a second to set a single Uniform Variable
« Reply #4 on: October 22, 2016, 18:35:00 »
For maths, I advise you to look into JOML library. It can be found at http://joml.org.

Re: 1/4 of a second to set a single Uniform Variable
« Reply #5 on: October 24, 2016, 20:01:09 »
Another thing I would like to add: The BufferUtils class in LWJGL stems from LWJGL 2, and should really not be used any more. You should rather use the new memory utilities which, when you get used to them are quite nice. You can do direct stack allocations (something you cant do in native java) for short lived buffers like this:
Code: [Select]
try(MemoryStack memStack = stackPush()) {
    ByteBuffer buffer = stackAlloc(10); //Allocates 10 bytes, there are lots of more methods for allocating e.g. float buffers
    //Fill your buffer with data and send it to opengl
} //stackPop() is automatically called at the end of the try block, all buffers created in this block can no longer be used after this
For buffers which will live for longer than the scope of a try block you can allocate on the heap using the following. This is not as fast, but still much better than using javas built in buffer methods. You should also use this for larger (>1K bytes) buffers:
Code: [Select]
ByteBuffer buffer = memAlloc(10);
//When you are done with your buffer, later in the program:
memFree(buffer);
I recommend you read up on this on the LWJGL blog (http://blog.lwjgl.org/memory-management-in-lwjgl-3/) or by checking out these demos/intros, specifically 2, 3 and 4 (https://github.com/LWJGL/lwjgl3-demos/tree/master/src/org/lwjgl/demo/intro).

Edit: Fixed some misinformation about stack allocations
« Last Edit: November 01, 2016, 18:03:13 by seventh-chord »