Skip to content

Compute shaders

Romain Milbert edited this page Apr 21, 2024 · 6 revisions

Compute shaders are their own kind of shaders; they can be given code as usual from a file path or inline code, and are operated through a ComputeShaderProgram object:

Raz::ComputeShaderProgram computeProgram(Raz::ComputeShader::loadFromSource(R"(
  layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

  layout(rgba8, binding = 0) uniform readonly restrict image2D uniInput;
  layout(r16f, binding = 0) uniform writeonly restrict image3D uniOutput;

  void main() {
    // As we run the shader with 32x32x32 invocations (see below), each of the gl_GlobalInvocationID's
    //  3 components will be between [0; 31] and will represent the integer coordinates within our textures
    // See https://www.khronos.org/opengl/wiki/Compute_Shader#Inputs
    ivec3 pixelCoords = ivec3(gl_GlobalInvocationID.xyz);
    
    vec3 inputValue = imageLoad(uniInput, pixelCoords.xy).rgb;

    // Even though we have a single-channel output texture, it presumably doesn't hurt to set the first 3 (RGB)
    //  ones, as they should be discarded since we *need* to give it 4 anyway (https://docs.gl/sl4/imageStore)
    // Incidentally, it allows to have a grayscale color when setting an RGB texture without any change
    //  here. Most likely not useful, but could help spotting an incorrect setup?
    imageStore(uniOutput, pixelCoords, vec4(vec3(dot(inputValue, inputValue)), 1.0));
  }
)"));

// Setup necessary stuff (attributes, textures (see relevant section below), ...)

// Executing the compute shader requires from 1 to 3 arguments, which will be the number of work groups
//  on each dimension (https://docs.gl/gl4/glDispatchCompute)
// Note that at the time of writing, this sets a memory barrier for *all* types
//  (https://docs.gl/gl4/glMemoryBarrier); this may be more fine-grained later
computeProgram.execute(32, 32, 32);

Using textures

Textures can be used in compute shaders only as image textures. The textures themselves are declared as usual, but added differently to the shader program:

// "Simple" RGB layouts do not exist; if you need 3 channels, use an RGBA colorspace. Double-check that
//  it's the case in the shader too
// The R11G11B10 layout should be supported later
const auto inputTexture  = Raz::Texture2D::create(32, 32,
                                                  Raz::TextureColorspace::RGBA,
                                                  Raz::TextureDataType::BYTE);
const auto outputTexture = Raz::Texture3D::create(32, 32, 32,
                                                  Raz::TextureColorspace::GRAY,
                                                  Raz::TextureDataType::FLOAT16);

// Texture images can be set as either read only, write only, or both
// This must match the potential specification in the shader
computeProgram.setImageTexture(inputTexture, "uniInput", Raz::ImageTextureUsage::READ);
computeProgram.setImageTexture(outputTexture, "uniOutput", Raz::ImageTextureUsage::WRITE);
computeProgram.initImageTextures();
Clone this wiki locally