Hello there, here I am with something I don’t understand about multiple compute shaders run.
Here’s the code
import org.intellij.lang.annotations.Language
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extra.noise.uniform
import org.openrndr.math.Vector2
fun main() = application {
configure {
width = 1000
height = 1000
// fullscreen = Fullscreen.CURRENT_DISPLAY_MODE
}
program {
var t = 0.0
@Language("GLSL")
val glslA = """
#version 430
layout(local_size_x = 4, local_size_y = 1) in;
uniform float t;
layout(std430, binding=0) buffer particlesColor {
float col[];
};
void main(){
const uint id = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 4;
col[id] = abs(cos(t * 2.0 * id));
}
""".trimIndent()
val computeA = ComputeShader.fromCode(glslA, "computeA")
@Language("GLSL")
val glslB = """
#version 430
layout(local_size_x = 4, local_size_y = 1) in;
uniform float t;
struct Part {
vec2 pos;
};
layout(std430, binding=1) buffer particlesBuffer {
Part positions[];
};
void main(){
const uint id = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 4;
positions[id].pos.x = 0.5 + 0.2 * sin( id * 0.1 * t);
positions[id].pos.y = 0.5 + 0.2 * cos( id * 0.1 * t);
}
""".trimIndent()
val computeB = ComputeShader.fromCode(glslB, "computeB")
val bufferA = shaderStorageBuffer(shaderStorageFormat {
member("cols", BufferMemberType.FLOAT, 4)
}).also{
it.put {
for (i in 0 until 4) {
write((1.0).toFloat())
}
}
}
val bufferB = shaderStorageBuffer(shaderStorageFormat {
member("parts", BufferMemberType.VECTOR2_FLOAT, 4)
}).also{
it.put {
for (i in 0 until 4){
write(Vector2.uniform(0.0, 1.0))
}
}
}
//computeA.buffer("particlesBuffer", bufferB)
computeA.buffer("particlesColor", bufferA)
computeB.buffer("particlesBuffer", bufferB)
extend {
computeA.uniform("t", t)
computeB.uniform("t", t)
drawer.stroke = null
drawer.fill = ColorRGBa.WHITE
drawer.shadeStyle = shadeStyle {
fragmentTransform = """
vec2 uv = c_boundsPosition.xy;
float c = 0.0;
for (int i = 0; i < b_parts.parts.length(); i++){
float l = length(b_parts.parts[i] - uv);
float d = 1.0 - smoothstep(0.01, 0.081, l);
c += d * b_cols.cols[i];
}
x_fill.rgb = vec3(c);
""".trimIndent()
buffer("cols", bufferA)
buffer("parts", bufferB)
}
computeA.execute(1, 1)
computeB.execute(1, 1)
drawer.rectangle(drawer.bounds)
t += 0.01
}
}
}
Here’s a little bit of context. I have two compute shaders, computeA and computeB. The shader computeA takes care of writing on a buffer called bufferA which controls the brightness, and the shader computeB writes on a buffer called bufferB which controls the position of the each particle. These particles are then rendered in the fragment shader. Notice that computeA does not have binding for bufferB, and computeB does not have a binding for bufferA.
Now, this is the strange thing that happens: if you run the code, you’ll see the particles moving but not “blinking”. On the other hand, if you uncomment the line
//computeA.buffer("particlesBuffer", bufferB)
the blinking will appear, even though, as said above, computeA has no binding to bufferB.
I suspect this has to do with some weird memory management behaviour, but it is nevertheless very confusing. For some added fun, just swap the lines
computeA.buffer("particlesColor", bufferA)
computeA.buffer("particlesBuffer", bufferB)
and you’ll get a different behaviour…
Let me know if you can replicate the issue.