Visualizing an AtomicCounter
This program runs a compute shader for every pixel of an image. When processing each pixel, an atomic counter is incremented. The counter-value is visualized on every pixel using a unique color. This is not very useful, but demonstrates how to create and use an AtomicCounterBuffer and how to reset its value.
If you take a screenshot of the produced image, you can use photo-editing software to verify that every pixel has a unique color (in Gimp: use the Fuzzy Select tool, threshold 0, Sample merged).
See https://wikis.khronos.org/opengl/Atomic_Counter
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extra.imageFit.imageFit
import org.openrndr.math.IntVector3
import kotlin.system.measureNanoTime
fun main() = application {
program {
val img = colorBuffer(1920, 1080, type = ColorType.FLOAT32)
// The atomic counter incremented each time the compute shader processes one pixel
val counter = AtomicCounterBuffer.create(1)
val csInc = computeStyle {
computeTransform = """
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
// atomically increments the value of the counter and returns its prior value.
uint m = atomicCounterIncrement(b_counter[0]);
// Convert the current counter value into an rgb value
float r = float(m % 256) / 256.0;
float g = float((m / 256) % 256) / 256.0;
float b = float((m / 65536) % 256) / 256.0;
imageStore(p_img, pixel, vec4(r, g, b, 1.0));
""".trimIndent()
}
// The default `workGroupSize` is (1, 1, 1). Here we choose a larger one for performance.
// The width and height should be divisible by these values: 1920/32 = 60, 1080/24 = 45.
// Larger values were not accepted in my AMD GPU because their product was larger than 1000.
csInc.workGroupSize = IntVector3(32, 24, 1)
extend {
// Run the shader to give a unique color to each pixel in the image
println(measureNanoTime {
csInc.buffer("counter", counter)
csInc.image("img", img.imageBinding(0, ImageAccess.WRITE))
csInc.execute(img.width / csInc.workGroupSize.x, img.height / csInc.workGroupSize.y)
} / 1_000_000.0)
// Draw the texture generated by the compute shader
drawer.clear(ColorRGBa.PINK)
drawer.imageFit(img, drawer.bounds)
// Reset the counter before the next animation frame
counter.reset()
}
}
}
The program prints the compute shader execution times in milliseconds. With my laptop the values look like these:
0.126686
0.185455
0.252961
0.264633
0.405766
0.205853
0.170187
0.345844
0.244926
0.153526
0.187509
0.206926
0.31114
0.204381
I haven’t done much testing, but since we have 16.66 milliseconds to render our frame to achieve 60 FPS, those values look acceptable.
I did try in Gimp and the color of each pixel seemed to be unique. I could have written more code to verify that… maybe another day 