# New computeStyle builder

**URL:** <https://openrndr.discourse.group/t/new-computestyle-builder/739>\
**Category:** Tutorials\
**Created:** [January 28, 2025, 5:09pm UTC](https://openrndr.discourse.group/t/new-computestyle-builder/739 "2025-01-28T17:09:16Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [January 28, 2025, 5:09pm UTC](https://openrndr.discourse.group/t/new-computestyle-builder/739/1 "2025-01-28T17:09:16Z")

</div>

There is a new way to create compute shaders: the `computeStyle` builder. It follows a pattern similar to that of `shadeStyle`.

A few fixes landed in this code last week, so I can now share a small example of how it can simplify the creation of compute shader programs that use SSBO’s and structs. _Note: only if you are building `openrndr` yourself since it’s not yet published to maven._

Let’s assume we have a compute shader program like this:

> **imports**
>
> ```kotlin
> import org.intellij.lang.annotations.Language
> import org.openrndr.application
> import org.openrndr.draw.*
> import org.openrndr.math.Vector2
> import org.openrndr.math.Vector3
> import java.nio.ByteBuffer
> import java.nio.ByteOrder
> 
> ```

```kotlin
fun main() = application {
    program {
        // Define SSBO format
        val fmt = shaderStorageFormat {
            primitive("time", BufferPrimitiveType.FLOAT32)
            primitive("vertex", BufferPrimitiveType.VECTOR2_FLOAT32, 3)
            struct("Particle", "particles", 5) {
                primitive("pos", BufferPrimitiveType.VECTOR3_FLOAT32)
                primitive("age", BufferPrimitiveType.FLOAT32)
            }
        }
        println("Study the padding in the format:\n$fmt\n")

        // Create SSBO
        val ssbo = shaderStorageBuffer(fmt)

        // Populate SSBO
        ssbo.put {
            write(3.0.toFloat()) // time
            repeat(3) {
                write(Vector2(1.1, 1.2)) // vertex
            }
            repeat(5) {
                write(1.0.toFloat()) // age
                write(Vector3(2.1, 2.2, 2.3))// pos
            }
        }

        // GLSL shader code
        @Language("GLSL")
        val glsl = """
            #version 430
            layout(local_size_x = 1, local_size_y = 1) in;
            
            struct Particle {
              float age;
              vec3 pos;
            };
            
            // A buffer using variables of different types:
            // a float, an array of vec2 and an array of struct.
            // These variable become available as globals.
            layout(std430, binding=0) buffer myData {
                float time;
                vec2 vertex[3];
                Particle particles[5];
            };
            
            void main() {                
                time = 3.3;
                vertex[0] = vec2(7.01);
                vertex[1] = vec2(7.02);
                vertex[2] = vec2(7.03);
                particles[0].pos = vec3(112.0);
                particles[0].age = 111.0;
            }
        """.trimIndent()
        
        // Create Compute Shader
        val cs = ComputeShader.fromCode(glsl, "compute02")
        cs.buffer("myData", ssbo)

        // Download SSBO data to CPU
        val byteBufferBeforeExecute = ByteBuffer.allocateDirect(ssbo.format.size).order(ByteOrder.nativeOrder())
        byteBufferBeforeExecute.rewind()
        ssbo.read(byteBufferBeforeExecute)

        // Execute compute shader
        cs.execute(1, 1, 1)

        // Download SSBO data to CPU
        val byteBufferAfterExecute = ByteBuffer.allocateDirect(ssbo.format.size).order(ByteOrder.nativeOrder())
        byteBufferAfterExecute.rewind()
        ssbo.read(byteBufferAfterExecute)

        // Debugging
        byteBufferBeforeExecute.rewind()
        byteBufferAfterExecute.rewind()
        repeat(ssbo.format.size / 4) {
            println("$it: ${byteBufferBeforeExecute.float} -> ${byteBufferAfterExecute.float}")
        }
    }
}

```

This program is just for testing purposes, as it doesn’t do any massively parallel computation, nor produce any graphical output, but just alter a few numbers to verify that id works as expected.

Look at the `glsl` variable: it declares a bunch of required boilerplate. By using the `shadeStyle { }` builder, we can just keep what’s inside the GLSL `main()` function, and discard the rest, which gets generated automatically. The simplified code looks like this:

> **imports**
>
> ```kotlin
> import org.intellij.lang.annotations.Language
> import org.openrndr.application
> import org.openrndr.draw.*
> import org.openrndr.math.Vector2
> import org.openrndr.math.Vector3
> import java.nio.ByteBuffer
> import java.nio.ByteOrder
> 
> ```

```kotlin
fun main() = application {
    program {
        // Define SSBO format
        val fmt = shaderStorageFormat {
            primitive("time", BufferPrimitiveType.FLOAT32)
            primitive("vertex", BufferPrimitiveType.VECTOR2_FLOAT32, 3)
            struct("Particle", "particles", 5) {
                primitive("pos", BufferPrimitiveType.VECTOR3_FLOAT32)
                primitive("age", BufferPrimitiveType.FLOAT32)
            }
        }
        println("Study the padding in the format:\n$fmt\n")

        // Create SSBO
        val ssbo = shaderStorageBuffer(fmt)

        // Populate SSBO
        ssbo.put {
            write(3.0.toFloat()) // time
            repeat(3) {
                write(Vector2(1.1, 1.2)) // vertex
            }
            repeat(5) {
                write(1.0.toFloat()) // age
                write(Vector3(2.1, 2.2, 2.3))// pos
            }
        }

        // Create Compute Shader
        val cs = computeStyle {
            computeTransform = """
                b_myData.time = 3.3;
                b_myData.vertex[0] = vec2(7.01);
                b_myData.vertex[1] = vec2(7.02);
                b_myData.vertex[2] = vec2(7.03);
                b_myData.particles[0].pos = vec3(112.0);
                b_myData.particles[0].age = 111.0;                
            """.trimIndent()
        }
        cs.buffer("myData", ssbo)

        // Download SSBO data to CPU
        val byteBufferBeforeExecute = ByteBuffer.allocateDirect(ssbo.format.size).order(ByteOrder.nativeOrder())
        byteBufferBeforeExecute.rewind()
        ssbo.read(byteBufferBeforeExecute)

        // Execute compute shader
        cs.execute(1, 1, 1)

        // Download SSBO data to CPU
        val byteBufferAfterExecute = ByteBuffer.allocateDirect(ssbo.format.size).order(ByteOrder.nativeOrder())
        byteBufferAfterExecute.rewind()
        ssbo.read(byteBufferAfterExecute)

        // Debugging
        byteBufferBeforeExecute.rewind()
        byteBufferAfterExecute.rewind()
        repeat(ssbo.format.size / 4) {
            println("$it: ${byteBufferBeforeExecute.float} -> ${byteBufferAfterExecute.float}")
        }
    }
}

```

Note how we use `b_myData`. This is because the SSBO gets a name specified when calling `cs.buffer(...)`. When using only one SSBO naming it is not necessary, but it’s good practice as it shows where the data came from (a buffer) and makes it easy to use multiple such buffers, each with a unique name.

Thanks to the `shadeStyle` feature, the updated code is 21 lines shorter, and more importantly, there are fewer opportunities for typos by not having to declare the `struct` or the buffer layout yourself in the GLSL code.

Note: to specify the workgroup size we can use the following: `cs.workGroupSize = IntVector3(32, 32, 1)` with whatever values we consider proper.

Time to play with this 🙂

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [January 31, 2025, 6:01pm UTC](https://openrndr.discourse.group/t/new-computestyle-builder/739/2 "2025-01-31T18:01:57Z")

</div>

Several demos which at some point I may turn into videos, tutorials or a workshop:

> <https://github.com/openrndr/orx/pull/354>
>
> Adding 11 computeStyle demos. Demos 1 to 10 are written sequentially and make se…nse one after the other. The last demo is actually older and I will either change it so it makes sense in this sequence (to introduce a new concept after demo 10), or maybe remove it and add something more interesting.
