Tuesday, March 15, 2011

HLSL, Threading and more..

Before I give a wall of words, I'll start with 2 screenshots.


I wanted to stress test my renderer, so I generated a 128x128x128 chunk.  It took ~15 seconds to go from launch -> drawing on the screen.

The last few days, I've spent a lot of time getting multi-threading working, reading about HLSL and optimizing my code.

My first issue was getting multi-threading working.  Whenever a new chunk would be generated, the game would pause for a second while I build it and then draw it to the screen.  I could optimize and optimize and work on my code to make it as fast as possible, but at the end of the day the possibility of the pause is still there.  By building my chunk on a different thread I am removing the possibility of that pause.  Now while the concept sounds easy, it's a bit harder to implement.  Issues arise when you have one thread trying to modify a variable at the same time as another thread.  Luckily there are ways to combat this and it's just a matter of organizing your code to make sure that never happens.  This means locking a variable before you change it, not letting another thread touch the variable until it's unlocked and unlocking it when you're done.

After I got that working, I started having issues with my Draw() function.  I was generating more vertices than DrawUserPrimitives() can handle.  Originally, I was building all my mesh and adding it to a giant VertexPositionNormalTexture[] array.  I was then pushing that array to the GPU everytime I called Draw() (which was every single frame).  If you remember from my older posts, pushing data to the GPU is one of the most expensive things you can do.  After reading about my issue some more, I also learned that DrawUserPrimitives() is only meant for objects that will be changing between frames.  Vertex Buffers are meant for meshes that are constant, such as my terrain.  With a Vertex Buffer, you generate the mesh, push it to the GPU once and the GPU stores it on there.  When it comes time to drawing, the GPU pulls your mesh from its own RAM and draws it from there (saving you lots of time).  You still want to change the mesh as little as possible to save on sending a new mesh to the GPU, but at least you only have to send the mesh once per change instead of once per Draw() call.

Between the Vertex Buffer and the threading, generating a 16x128x16 chunk and displaying it to the screen is instantaneous.  I'm really, really happy with the speed so far.

Next on my list was shaders and HLSL is the shading language used in XNA.  The book I have briefly covers HLSL.  I also found this website which has several shader tutorials.  The C# code on that site is for XNA3 but the HLSL code had no issues.  I can't say that I fully understand HLSL yet.  It's going to take me a while before I get it down, but using that site I was able to create a shader that'll do for now.

So that's where I currently am.  The rest of the week will be spent reading about HLSL and trying to understand it more.  I've also started working on a world generation algorithm.  I've been reading a lot about different ways to go about it so I'm really just experimenting now.

So until next time..