4
\$\begingroup\$

I'm working on a compute shader for a 3d texture where I want to evaluate pixels in a specific order, is there any way to do this? let's say that I have made a texture using the following code;

tex = new RenderTexture(64, 64, 0);
tex.volumeDepth = 6;
tex.isVolume = true;
tex.enableRandomWrite = true;
tex.Create();

Now let's say that for example I want to make the following computation on the gpu rather then cpu:

tex[0]=basetex //all values where z =0 are some basic 2d texture
for(int z=1;z<6;z++){ //loop over every z level
 for(int x=1;x<64;x++){ //loop over every x level
        for(int y=1;y<64;y++){ //loop over every y value
         tex[x][y][z-1]=tex[x-1][y][z-1]+tex[x-1][y][z-1]+tex[x][y-1][z-1]+tex[x][y+1][z-1] //add all values next to this pixel on the z value bellow
      }
  }
}
\$\endgroup\$
  • \$\begingroup\$ So, what you want is a blur shader? \$\endgroup\$ – aaaaaaaaaaaa Mar 20 '15 at 14:36
  • 1
    \$\begingroup\$ A 3d blur shader is a good example the real shader is a lot more complex (the real computation consits of a min/max minmap pushed into a 3d space containging several different data types but the details are not tht interesting). So any shader where the output is a 3d texture where the values of each layer are dependend on the one before will do. \$\endgroup\$ – Thijser Mar 20 '15 at 16:48
  • \$\begingroup\$ Your question really is too vague. Is this about graphics at all? If not, wouldn't a dedicated GPGPU interface like OpenCL, CUDA or Stream be more appropriate? \$\endgroup\$ – aaaaaaaaaaaa Mar 20 '15 at 17:00
  • 1
    \$\begingroup\$ I need these computations within a unity environment to then pass on to a normal shader. Basically I'm implementing this: groups.csail.mit.edu/graphics/mmvs \$\endgroup\$ – Thijser Mar 20 '15 at 17:25
1
+100
\$\begingroup\$

The individual invocations within a work group will be executed "in parallel". The main purpose of the distinction between work group count and local size is that the different compute shader invocations within a work group can communicate through a set of shared​ variables and special functions. Invocations in different work groups (within the same compute shader dispatch) cannot effectively communicate. Not without potentially deadlocking the system.

You can assign work groups for your compute shader for some limited communication, but in general the compute shader runs in parallel without a defined order, trying to force it to run sequentially would slow down the GPU to a crawl since it's power comes from running all operations in parallel.

In this case the solution is to write to a separate texture from the one your reading from, if you don't the operation will be undefined and could cause all sorts of problems because of said multitasking.

If your input is a 1024x1024x30 3d texture for instance, and you wish to process it in sequence along the w coordinate then you create a 1024x1024x30 output image, then use glDispatchCompute to define as many workgroups as you need, or define layout(local_size_x = X​, local_size_y = Y​, local_size_z = Z​) in; in your compute shader

#version 430
#define width 1024
#define height 1024

layout(local_size_x=width, local_size_y=height) in;
uniform layer;
writeonly uniform image3D output;
uniform sampler3D input;

// gl_GlobalInvocationID is a uvec3 variable giving the global ID of the thread,
// gl_LocalInvocationID is the local index within the work group, and
// gl_WorkGroupID is the work group's index
void main() {
  ivec2 pos = ivec3(gl_GlobalInvocationID.xy, layer);
  imageStore(output, pos, texture3D(input, pos));
}

Code is untested but should hopefully be enough to get you started. If you create more than one workgroup you have to multiply that with the layout used in the shader to get the total size of the work space.

for (int layer = 0; layer < 30; layer++) {
  glUniform1i(layerPosition, layer);
  glDispatchCompute(1,1,1); // X: 1*1024  Y: 1*1024  Z: 1*1
}

Remember to not write to the same texture your reading from, so if you want to use the previous computations result you will need to swap textures around between executions as well.

\$\endgroup\$
  • \$\begingroup\$ But that should be an order by which we can largely paralise this computation right? after all each layer is only dependend on the one before and has no interdependance so there should be a way to force the system to first compute the first layer then wait till it's done and then do the second one. \$\endgroup\$ – Thijser Mar 22 '15 at 20:57
  • \$\begingroup\$ @Thijser Yes, although you can use the output from the first layer to the next layer even though the GPU isn't ready yet, it will queue things up so it gets run properly. It's only when you want to read the final output back to the CPU that you will be forced to wait for the GPU, which is synchronized for you automatically (you can't read it until it's done). There is also a concept of Sync Objects that allows you to check if the GPU has finished with the operation yet, and if not do something else for a while. \$\endgroup\$ – Daniel Carlsson Mar 23 '15 at 0:17
  • \$\begingroup\$ So my question is how do I do this in a unity shader? How do I force say a 1024/1024/30 3d texture to be evaluted in order? \$\endgroup\$ – Thijser Mar 23 '15 at 6:48
  • \$\begingroup\$ @Thijser I updated the answer, in short, execute your compute shader once per slice \$\endgroup\$ – Daniel Carlsson Mar 23 '15 at 10:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.