Compute shader or pixel shader for a complex algorithm?

Hi. I am working on a terrain water simulation algorithm on GPU. I already implemented the algorithm with pixel shaders. The problem is it is very time consuming and needs thousands of iterations to solve the case. I used 5 MRT shaders. Each shader has some textures as input and after processing outputs some other textures.

The process is completely bandwith-limited. Most of the GPU time is spent on sampling RenderTextures, do a little computation and then write the result to output. Consider the overall picture: There is a heightmap for a terrain. At every point of the terrain there is a value for how much water is above it. So to calculate the water movement we need to do many read and write operations. Here is part of what’s going on in the shaders.

  • fetch current value for water height
  • fetch terrain height
  • compute a flowmap and write to render target
  • fetch flowmap and compute new water values
  • write new water value
  • compute velocity and write to target
  • fetch velocity
  • apply horizontal gaussian blur on velocity

And the list goes to 20. These operations need to be done in every iteration. So for 2000 iterations we have to do 40k read/write operations per pixel. I can’t do all the operations in a single pass because for example in step 3 the flowmap is calculated for every pixel and in step 5 the flowmap is sampled for a pixel and its 4 neighbors and then then new water value is calculated. With pixel shaders there is no getaway. These steps should be done.

Now with all these details said, what if we use compute shaders? Can we minimize the fetches? I saw people talking about shared memory. Can we do all operations on a small chunk of data and then move forward to next chunk? Consider this: We fetch some values for 1000 pixels, process them, store them in a fast memory, use the memory in next step, write new values, and so on? The idea is to localize access to memory to boost performance. Can we do this specific job with unity compute shaders? All the data are present in RenderTextures and live in GPU during the whole process. Only some value uniforms are passed from CPU to GPU. Another thing is Rendering is not an issue during the process. During the simulation we can have 5-10 frames per second and be happy with that.

OK. After more study I found the answers to these qusetions:

  • Yes we can minimize the fetches in compute shaders to the extent that is impossible with pixel shaders.
  • Yes there is a group shared memory that can hold temporary data that need to be accessed/updated multiple times during the runtime of a thread.
  • Yes we can execute compute shader for a manually defined number of pixels (threads in DirectCompute analogy). This is the most important concept in compute shaders. You execute kernels (analogous to a pixel shader) for a specified threads and move on to next chunk of data.
  • Yes we can write to RW(ReadWrite) buffers or texture and then sync the results. A memory sync ensures all threads have written their value to UAVs (RWTexture) and ran their code to the buffer or UAV. After that this data can be fetched by threads and it is guaranteed to have the values written to it.
  • Yes a strong point of compute shaders is to localize memory access patterns. It is completely feasible to convert a multipass image effect pixel shader to a single pass compute shader. This point alone can make enormous performance boost in the process

For people who might find this thread Here is a very good tutorial series. The unity and Microsoft doc both worth nothing. All other sources are also very disappointing. There is one exception: This book about directx11 has a few chapters on DirectCompute and is written exceptionally well. It explains the theory and the implementation and the codes themselves are well commented. So if you want to start DirectCompute this is the only resource worth reading.

4 Likes

thanks for posting this man. I’m about to do a kinda Ping Pong style thing and im really wondering if i should bite the bullet and learn compute. It just seems like another thing to learn… did you end up getting much better performance through compute?