sales@scenomics.com +1 650 396 9674

Create Compute Shader

In this exercise you'll learn to create a document that contains a compute shader, how to write a texture with the compute shader, and how to visualize the results of the compute shader with a vertex and fragment shader. This exercise requires GLSL version 400 or higher.

Start The Shader Application

  1. Start the Shader app. (Start » Programs » Scenomics » Shader) or (Windows® key and then type 'Shader' to find the app icon.)

    The application displays a splash screen and then the application desktop appears. The main menu is composed of three items that contain commands relevant to the current context (which is an empty document). The interface changes when you create a new file or load a file from disk.

    This is a picture of the desktop.

Create New Document

  1. Select File » New » Shader from the main menu.

    The software displays a wizard that allows you to specify the parameters of your new shader. The Profile shown below is 460 core, but you will see the highest GLSL version available on your machine.

    This is a picture of the new project dialog.
  2. Type User Compute Exercise in the field named Name.
  3. Set Profile to the highest value available such as 460 core.

    Note that your GPU must support GLSL 400 or higher to proceed. Otherwise you will not be able to set Add Compute Support to true and you cannot proceed with this exercise.

  4. Set Add Compute Support to True.
  5. Hit ENTER or click OK when you are finished.

    The application creates a new shader document and the main menu options change. You can see the hierarchy on the left, the rendered shader with geometry in the middle, and the property sheet on the right. Shader compiler error messages are shown in the output window below. The shader sets the fragment color to red and does no other work.

    This is a picture of the workspace.

    If the shader compiled successfully, you should see a red square in the center of the worksheet.

  6. Select File » Save from the main menu.

Examine The Document

  1. Select Desktop » Clear Output from the main menu.
  2. Examine the main menu and select Graph » State » Expand All Tree Items. ( Or hit ALT + X ).

    This expands the graph so that you can see all the nodes. As you can see, this shader is a composite data structure made from a set of atomic types. The template document used to create the new file was built with a Scenome application named Graph. You can build your own templates, but this is covered in a later exercise.

    NOTE: You can hover over each node icon, in the image below, for a description of the node and its function.

    That covers the basic information about the graph.

Examine The <Program>

  1. Examine the hierarchy and move the mouse over the <Program> named Compute.

    Notice that you can see the GLSL version, profile, and source code locations. Many nodes, but not all of them, display useful information if you hover over them.

    This is a picture of the Program node tooltip.

Copy The Compute Shader Path

  1. Examine the hierarchy and right click over the <Program> node named Compute. This is a picture showing the location of the Compute program.
  2. Choose Copy Source Path » Compute Shader from the listed options.

    This copies the absolute path to the fragment shader source code to the Windows® clipboard. For example, a file path like the following: D:\Release6\Content\Library\Shader\User Basic Exercise\460\user_basic_exercise_fragment_shader.glsl is copied to the clipboard.

Implement Compute Shader

  1. Start a text editor of your choice and select the option to open a file from disk.
  2. Select CTRL + V to paste the fragment shader file path (into the place in the dialog where you specify the file to open) and open the file.

    This is the basic compute shader. Note that your #version 460 declaration might be different, depending on the highest GLSL version on your machine.

    // #version 460
    // The version number is automatically injected by the application.
    // It is included above for reference purposes only.
    
    #extension GL_ARB_compute_variable_group_size : enable
    #ifdef GL_ARB_compute_variable_group_size
       layout( local_size_variable ) in;
    #else
       layout( local_size_x = 32, local_size_y = 32, local_size_z = 1 ) in;
    #endif
    
    void main(void)
    {
    }
    
  3. Replace your compute shader source code with the following:

    Copy Text To Clipboard

    // #version 460
    // The version number is automatically injected by the application.
    // It is included above for reference purposes only.
    
    #extension GL_ARB_compute_variable_group_size : enable
    #ifdef GL_ARB_compute_variable_group_size
       layout( local_size_variable ) in;
    #else
       layout( local_size_x = 32, local_size_y = 32, local_size_z = 1 ) in;
    #endif
    
    layout( rgba32f ) uniform image2D dst_image;
    
    void main(void)
    {
       ivec2 pos = ivec2( gl_GlobalInvocationID );
    
       vec4 results = vec4( 0.50, 0.5, 0.5, 1.0 );
       imageStore( dst_image, pos, results );
    }
    
  4. Save the changes to the compute shader.
  5. Return to the running Shader application.

Rebuild The Document

  1. Select Graph » Build All from the main menu.

    The software presents the build warning dialog.

    The software warns you that document rebuilds can cause changes to shader appearance. During a rebuild, the shader source for all <Program> nodes and ( and <Program> nodes specified by <ProgramExecute> nodes ) in the document is traversed and built into a .BOX file that contains nodes representing all declared uniforms, uniform buffers, shader buffers, and structs. This information is used to automatically add the correct resources to the document and to delete obsolete resources.

  2. Click OK to proceed.

    The application rebuilds the document and displays build information in the output window.

    --- <Building Project 'D:\Release6\Content\Library\Shader\User Compute Exercise\User Compute Exercise.box'> ---
    
    Rebuilding Shader Resource Documents...
    
    Rebuild succeeded: D:\release6\content\library\shader\user compute exercise\460\user_compute_exercise_compute_shader.glsl
    Rebuild succeeded: D:\release6\content\library\shader\user compute exercise\460\user_compute_exercise_vertex_shader.glsl
    Rebuild succeeded: D:\release6\content\library\shader\user compute exercise\460\user_compute_exercise_fragment_shader.glsl
    
    Updating document contents...
    
    Adding to <SamplerPaletteNode> 'Samplers' <SamplerNode> 'layout( rgba32f ) uniform image2D dst_image'
    
    Build completed.
    

Examine The Document

  1. Select Graph » State » Expand All Tree Items from the main menu.

    The graph looks like this. There is a new <SamplerNode> named layout( rgba32f ) uniform image2D dst_image that matches the declarations in the shader source code. In the image below, the new node is highlighted red for illustrative purposes (but it isn't highlighted in the document).

    This is a picture of the graph.

Create A New <Texture>

  1. Examine the hierarchy.
  2. Right click over the <SamplerNode> named layout( rgba32f ) uniform image2D dst_image and select Create Texture... from the listed options.

    The application displays the create texture dialog.

    This is a picture of the create texture dialog.
  3. Set Name to user_compute_dst.
  4. Set Channel 0 Initial Value to 1.0.
  5. Set Channel 1 Initial Value to 1.0.
  6. Set Channel 2 Initial Value to 1.0.
  7. Set Channel 3 Initial Value to 1.0.
  8. Click OK or hit ENTER when you are finished.

    The software asks if you wish to save the file to disk.

    This is a picture of the save texture prompt.
  9. Click YES.

    The software presents the file save dialog and prompts you to specify the filename to use to save the texture to disk. We'll just use the file name user_compute_dst.image created by the application, but we could type our own file name here if we needed.

    This is a picture of the file save dialog.
  10. Click Save.

    The application saves the file to disk and adds a new <Texture> node to the graph.

Examine The Document

  1. Examine the main menu and select Graph » State » Expand All Tree Items. ( Or hit ALT + X ).

    This is the new <Texture> that the compute shader writes. We filled it with basic values of 1.0 so that we can debug easily and check if our writes are working. In the next section, we'll learn how to visualize the texture written by the compute shader so that we know it works.

    This is a picture of the graph.
  2. Select File » Save from the main menu.

Copy The Fragment Shader Path

  1. Examine the hierarchy.
  2. Right click over the <Program> named Program and select Copy Source Path » Fragment Shader. This is a picture of the graph.

    This copies the absolute path to the fragment shader source code to the Windows® clipboard. For example, a file path like the following: D:\Release6\Content\Library\Shader\User Compute Exercise\460\user_compute_exercise_fragment_shader.glsl is copied to the clipboard.

Implement The Fragment Shader

  1. Start a text editor of your choice and select the option to open a file from disk.
  2. Select CTRL + V to paste the fragment shader file path (into the place in the dialog where you specify the file to open) and open the file.

    This is the fragment shader. Note that your #version 460 declaration might be different, depending on the highest GLSL version on your machine. You can still follow this exercise even if the shader code is different. Just follow the steps below and make the same changes in the same locations.

    // #version NNN
    // The version number is automatically injected by the application.
    // It is included above for reference purposes only.
    #include <SPA_Version.glsl>
    #include "user_standard_pipeline_attributes.glsl"
    
    in Data { vertexData attributes; } DataIn;
    out vec4 fragColor;
    
    void main(void)
    {
       fragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
    }
    
  3. Replace your fragment shader source with the following:

    Copy Text To Clipboard

    // #version NNN
    // The version number is automatically injected by the application.
    // It is included above for reference purposes only.
    #include <SPA_Version.glsl>
    #include "user_compute_exercise_attributes.glsl"
    
    in Data { vertexData attributes; } DataIn;
    
    // We need width and height in case
    // the GLSL version is less than 430.
    // the function imageSize() is only
    // available for GLSL 430 and higher.
    uniform int image_width;
    uniform int image_height;
    layout( rgba32f ) uniform image2D dst_image;
    
    out vec4 fragColor;
    
    void main(void)
    {
       fragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
    
    #if __version__ < 430
       ivec2 src_dims = ivec2( image_width, image_height );
    #else
       ivec2 src_dims = imageSize( dst_image );
    #endif
    
       ivec2 coords = ivec2( DataIn.attributes.texcoord * vec2( src_dims ) );
       vec4 pix = imageLoad( dst_image, coords );
    
       fragColor = pix;
    }
    

    This shader has to support versions GLSL 400 to 460. The imageSize() function is only available in GLSL 430 and higher. Note that we have some preprocessor statements that define src_dims differently depending on the GLSL version.

  4. Save the shader source code and return to the running Shader application.

Rebuild The Document

  1. Select Graph » Build All from the main menu.

    The application rebuilds the document and displays build information in the output window.

    --- <Building Project 'D:\Release6\Content\Library\Shader\User Compute Exercise\User Compute Exercise.box'> ---
    
    Rebuilding Shader Resource Documents...
    
    Rebuild succeeded: D:\release6\content\library\shader\user compute exercise\460\user_compute_exercise_compute_shader.glsl
    Rebuild succeeded: D:\release6\content\library\shader\user compute exercise\460\user_compute_exercise_vertex_shader.glsl
    Rebuild succeeded: D:\release6\content\library\shader\user compute exercise\460\user_compute_exercise_fragment_shader.glsl
    
    Updating document contents...
    
    Adding to <UniformPaletteNode> 'Uniforms' <Int32Node> 'uniform int image_width'
    Adding to <UniformPaletteNode> 'Uniforms' <Int32Node> 'uniform int image_height'
    Adding to <SamplerPaletteNode> 'Samplers' <SamplerNode> 'layout( rgba32f ) uniform image2D dst_image'
    
    Build completed.
    

Examine The Document

  1. Select Graph » State » Expand All Tree Items from the main menu.

    The graph looks like this. There are two new <Int32Nodes> named uniform int image_width and uniform int image_height, along with a new <SamplerNode> named layout( rgba32f ) uniform image2D dst_image that matches the declarations in the shader source code. In the image below, the new node is highlighted red for illustrative purposes (but it isn't highlighted in the document).

    This is a picture of the graph.

Connect <SamplerNode> To <Texture>

  1. Examine the hierarchy.
  2. Right click over the new <SamplerNode> named layout( rgba32f ) uniform image2D dst_image and choose Select Texture... from the listed options.

    The software displays the node select dialog that allows you to choose a <Texture> to connect to the <SamplerNode>.

    This is a picture of the node select dialog.
  3. Left click user_compute_dst and click OK or hit ENTER when you are finished.

    This connects the <SamplerNode> to the <Texture> node.

  4. Select File » Save from the main menu.

Create Texture Width Data Capture

  1. Examine the hierarchy.
  2. Right click over the <Int32Node> named uniform int image_width and select Add Single Data Capture... from the listed options.

    The software presents a dialog that allows you to select a <DataCapture> object type to create, and the node from which you wish to capture data.

    This is a picture of the data capture dialog.
  3. Select TextureDataCaptureWidth from the field named <DataCapture> Type.
  4. Select user_compute_dst from the field named <DataCapture> Target Type.

    This might look like it is already selected if it's the only option, but you have to explicitly select it in order to set the data source.

  5. Click OK or hit ENTER when you are done.

    The software adds a data capture object to the <Int32Node> named uniform int image_width.

Examine Texture Width Data Capture

  1. Examine the hierarchy and move the mouse over the <Int32Node> named uniform int image_width.

    The tooltip shows the value of uniform int image_width matches the width of the user_compute_dst.

    This is a picture of the graph.

Create Texture Height Data Capture

  1. Examine the hierarchy.
  2. Right click over the <Int32Node> named uniform int image_height and select Add Single Data Capture... from the listed options.

    The software presents a dialog that allows you to select a <DataCapture> object type to create, and the node from which you wish to capture data.

    This is a picture of the data capture dialog.
  3. Select TextureDataCaptureHeight from the field named <DataCapture> Type.
  4. Select user_compute_dst from the field named <DataCapture> Target Type.

    This might look like it is already selected if it's the only option, but you have to explicitly select it in order to set the data source.

  5. Click OK or hit ENTER when you are done.

    The software adds a data capture object to the <Int32Node> named uniform int image_height.

Examine Texture Height Data Capture

  1. Examine the hierarchy and move the mouse over the <Int32Node> named uniform int image_height.

    The tooltip shows the value of uniform int image_height matches the width of the user_compute_dst.

    This is a picture of the graph.

Examine Results

  1. Examine the worksheet.

    The rendered output is gray. This looks like it correctly matches the values written by the compute shader, but what if we want to be certain?

    This is a picture of the worksheet.

    Recall that in the compute shader we used the following code to write values to the texture:

    vec4 results = vec4( 0.5, 0.5, 0.5, 1.0 );
    imageStore( dst_image, pos, results );
    

    Let's verify the results and make sure that the correct values are written to the texture.

Create Readback To Verify Results

  1. Examine the hierarchy. This is a picture of the worksheet.
  2. Right click over the <Texture> named user_compute_dst and select Readback... from the listed options.

    The software displays a dialog that allows you to choose the type of variable to store the readback. Since this is is a four channel float32 texture, we can store the results in a <Float32ArrayNode> or in a <Float32VectorArrayNode>. Note that the variable type doesn't change the results of the readback, but it can be easier to understand the values if you use a vector type to store vector data.

    This is a picture of the readback dialog.
  3. Select <Float32VectorArrayNode> and click OK or hit ENTER when you are finished.

    The software adds the readback nodes to the document and displays information about where to find the readback nodes.

Find Readback

  1. Examine the main menu and select Graph » State » Expand All Tree Items. ( Or hit ALT + X ).

    The new readback nodes are at the end of the graph.

    This is a picture of the graph.

Verify Results

  1. Double click the <Float32VectorArrayNode> named user_compute_dst_read_back.

    This displays node properties in the property editor. Scroll down to the bottom.

    You can see a range of properties is displayed, and all the values look correct.

    This is a picture of the property editor.

Dump To Disk

  1. Right click over the <Float32VectorArrayNode> named vec4 user_compute_dst_read_back[1048576].
  2. Select Dump To Output from the listed options.

    The software dumps the contents of the variable node to disk and opens a Windows® Explorer window to the folder containing the dumped variable. Notice the file user_compute_dst_read_back.txt.

    This is a picture of the folder containing the dumped output.
  3. Open the file user_compute_dst_read_back.txt in a text editor.

    The contents look like this:

    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    0.5, 0.5, 0.5, 1,
    

Modify Compute Shader

  1. Return to the text editor and find the compute shader source code.

    // #version 460
    // The version number is automatically injected by the application.
    // It is included above for reference purposes only.
    
    #extension GL_ARB_compute_variable_group_size : enable
    #ifdef GL_ARB_compute_variable_group_size
       layout( local_size_variable ) in;
    #else
       layout( local_size_x = 32, local_size_y = 32, local_size_z = 1 ) in;
    #endif
    
    layout( rgba32f ) uniform image2D dst_image;
    
    void main(void)
    {
       ivec2 pos = ivec2( gl_GlobalInvocationID );
    
       vec4 results = vec4( 0.50, 0.5, 0.5, 1.0 );
       imageStore( dst_image, pos, results );
    }
    
  2. Find the line that declares and initializes vec4 results.

    vec4 results = vec4( 0.50, 0.5, 0.5, 1.0 );
    
  3. Change the vector initialization to the following:

    vec4 results = vec4( 0.75, 0.5, 0.5, 1.0 );
    
  4. Save the compute shader source code and return to the running Shader application.

Verify Compute Shader Changes

  1. Return to the running Shader application.

    The worksheet looks like this:

    This is a picture of the desktop.
  2. Examine the hierarchy and double click the <Float32VectorArrayNode> named vec4 user_compute_dst_read_back[1048576].

    This re-displays node properties in the property editor. Scroll down to the bottom.

    You can see a range of properties is displayed, and all the values match the change we just made to the compute shader.

    This is a picture of the property editor.

    You can see the results exactly match the values written by the compute shader.

Check OpenGL Extension Support

  1. Examine the hierarchy.
  2. Right click over the <Program> named Compute and select Query Extension... from the listed options.

    The application displays a dialog that allows you to enter the name of an extension.

    This is a picture of the query extension dialog.
  3. Type GL_ARB_compute_variable_group_size and click OK or hit ENTER when you are done. Make sure to type the exact name or the query will return false.

    The software displays a popup message indicating whether or not the extension is supported by your GPU.

    This is a picture of the query extension results.

    It's important to know this because we can set these values in the application if the extension is supported. If the extension is not supported, then we know that we have to use the values for local_size_x, local_size_y, and local_size_z specified in the compute shader source.

Examine Workgroup Sizes

  1. Examine the hierarchy and find the <ProgramExecute> node named ExecuteCompute.

    This node is near the bottom of the graph.

  2. Move the mouse over the <ProgramExecute> node and a tooltip appears. This is a picture of the graph.

    You here you can see the Work Group Dimensions and Local Group Dimensions stored by the <ProgramExecute> node. If your GPU supports GL_ARB_compute_variable_group_size then you can set Local Group Dimensions by changing the values set by the <ProgramExecute> node. Otherwise you have to set the values of local_size_x, local_size_y, and local_size_z in the compute shader. The compute shader source code contains the declaration layout( local_size_x = 32, local_size_y = 32, local_size_z = 1 ) in;. This is where you set the values in the compute shader if your GPU does not support GL_ARB_compute_variable_group_size.

    These values reflect a core compute shader principle called occupancy. Workgroups share resources such as shared memory and memory barriers. Usually these values must be set and optimized differently for each compute shader workload.

  3. Select File » Save from the main menu.

Explore Directory Structure

  1. Select Tools » Deselect All from the main menu.
  2. Select File » Open Containing Folder from the main menu.

    The software opens the directory containing your new shader document. Each of these folders contains the shader code for the corresponding GLSL version. In this example, we created a GLSL shader for the highest version on your machine. For example, the shader code we want to find is in the 460 folder or similar. You might not need all of these variations, and you can delete variations that you don't need. Since this is a compute shader document, it supports GLSL versions from 400 to 460.

    This is a picture of the directory that contains your new shader document.

    New documents that manage shader source code elements and document dependencies are added to this directory by the application from time-to-time.

  3. Double click the folder named 460. This is a picture of the directory that contains your new shader source code.
    File Description
    USER_COMPUTE_EXERCISE_ATTRIBUTES.GLSLA struct containing the vertex attributes layout for this document. This is an ASCII file that can be opened in any text editor.
    USER_COMPUTE_EXERCISE_COMPUTE_SHADER.BOX.BLDThe compute shader build file. A .BLD document contains nodes that represent GLSL items such as shader buffers and uniforms. This document is not created or managed by the user, but it can be opened with the Shader app and inspected.
    USER_COMPUTE_EXERCISE_COMPUTE_SHADER.GLSLContains the compute shader source code. This is an ASCII file that can be opened in any text editor.
    USER_COMPUTE_EXERCISE_FRAGMENT_SHADER.BOX.BLDThe fragment shader build file. A .BLD document contains nodes that represent GLSL items such as shader buffers and uniforms. This document is not created or managed by the user, but it can be opened with the Shader app and inspected.
    USER_COMPUTE_EXERCISE_FRAGMENT_SHADER.GLSLContains the fragment shader source code. This is an ASCII file that can be opened in any text editor.
    USER_COMPUTE_EXERCISE_VERTEX_SHADER.BOX.BLDThe vertex shader build file. A .BLD document contains nodes that represent GLSL items such as shader buffers and uniforms. This document is not created or managed by the user, but it can be opened with the Shader app and inspected.
    USER_COMPUTE_EXERCISE_VERTEX_SHADER.GLSLContains the vertex shader source code. This is an ASCII file that can be opened in any text editor.
  4. Close the Windows® Explorer window and return to the Shader application.

    This exercise is complete. Return to tutorials.