Difficulty with getting compute shader code in docs to work

Hi all,

I’m trying to learn how to use compute shaders in panda3d by starting directly from the code in the documentation:

https://docs.panda3d.org/1.10/python/programming/shaders/compute-shaders

Almost entirely copy/pasted directly from the docs, the python code is:

from panda3d.core import Shader, Texture, ConfigVariableBool, NodePath, ShaderAttrib, PNMImage
from direct.showbase.ShowBase import ShowBase

immutableTextureStore = ConfigVariableBool("gl-immutable-texture-storage", False)
immutableTextureStore.setValue(True)
print ("immutable texture storage", immutableTextureStore.getValue())

base = ShowBase()

# Setup the textures
myTex1 = Texture()
myTex2 = Texture()
myTex1.setup_2d_texture(512, 512, Texture.T_unsigned_byte, Texture.F_rgba8)
myTex2.setup_2d_texture(512, 512, Texture.T_unsigned_byte, Texture.F_rgba8)
myTex1.set_clear_color((1,0,0,1))
myTex2.set_clear_color((0,0,0,1))
myTex1.clear_image()

# Create a dummy node and apply the shader to it
shader = Shader.load_compute(Shader.SL_GLSL, "compute_shader.glsl")
dummy = NodePath("dummy")
dummy.set_shader(shader)
dummy.set_shader_input("fromTex", myTex1)
dummy.set_shader_input("toTex", myTex2)

# Retrieve the underlying ShaderAttrib
sattr = dummy.get_attrib(ShaderAttrib)

# Dispatch the compute shader, right now!
base.graphicsEngine.dispatch_compute((32, 32, 1), sattr, base.win.get_gsg())

# Store the output
frame = PNMImage()
myTex2.store(frame)
frame.write("/c/Users/domin/Documents/Projects/Python/Shader Test/test.png")

and the shader code is:

#version 430

// Set the number of invocations in the work group.
// In this case, we operate on the image in 16x16 pixel tiles.
layout (local_size_x = 16, local_size_y = 16) in;

// Declare the texture inputs
layout (rgba8) uniform readonly image2D fromTex;
uniform writeonly image2D toTex;

void main() {
  // Acquire the coordinates to the texel we are to process.
  ivec2 texelCoords = ivec2(gl_GlobalInvocationID.xy);

  // Read the pixel from the first texture.
  vec4 pixel = imageLoad(fromTex, texelCoords);

  // Swap the red and green channels.
  pixel.rg = pixel.gr;

  // Now write the modified pixel to the second texture.
  imageStore(toTex, texelCoords, pixel);
}

The only changes I made were code to set gl-immutable-texture-storage = True, code to setup the textures, including changing the format from rgb8 to rgba8, changing alpha to 1 so the results will be visible, adding code to save the output texture, and adding a format layout to the input texture uniform in the shader code (without which it won’t compile).

format layouts are required for any image2D that is read from, according to the opengl documentation:
https://www.khronos.org/opengl/wiki/Image_Load_Store

“So if you want to read from an image, you must declare the format.”

The output from running this code is:

immutable texture storage True
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)

But no image is saved at the specified filepath.

Any help is appreciated!

myTex2.store(frame) will indicate if the store has succeeded. I would ensure the call to store the data is succeeding. Not a solution, but maybe a starting point…

store will return a boolean on success/fail.

1 Like

Thanks for the heads up!

I modified the storage code to be as follows:

# Store the output
frame = PNMImage()
print ("storing output texture in image:", myTex2.store(frame))
frame.write("/c/Users/domin/Documents/Projects/Python/Shader Test/test.png")

Now the output is:

immutable texture storage True
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
storing output texture in image: False

So you were right about that. I have some inkling what the problem is from previous trials I have run and it seems to be an issue with the texture not having a RAM image, even when set_clear_color() is called.

print ("has RAM image:", myTex2.hasRamImage())

yields:

has RAM image: False

Giving myTex1 a RAM image however does not appear to fix the problem though:

# Setup the textures
pixel = np.array([255,0,0,255], dtype=np.dtype('B'))
image = np.tile(pixel, (512,512))
myTex1 = Texture()
myTex2 = Texture()
myTex1.setup_2d_texture(512, 512, Texture.T_unsigned_byte, Texture.F_rgba8)
myTex2.setup_2d_texture(512, 512, Texture.T_unsigned_byte, Texture.F_rgba8)
myTex1.set_clear_color((1,0,0,1))
myTex2.set_clear_color((0,0,0,1))
myTex1.setRamImageAs(image, "RGBA")

As this still yields:

immutable texture storage True
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
has RAM image: False
storing output texture in image: False

Whereas, changing this to:

myTex2.setRamImageAs(image, "RGBA")

and commenting out the dispatch_compute() call yields:

immutable texture storage True
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
has RAM image: True
storing output texture in image: True

and correctly saves a 512x512 red square, so it seems to be on the shader side, i.e. writing to myTex2 isn’t working, or myTex2 is being written to but no transfer from texture memory to RAM is taking place.

You need to extract the data. Add:
base.graphicsEngine.extractTextureData(myTex2,base.win.get_gsg())

base.graphicsEngine.dispatchCompute((32, 32, 1), sattr, base.win.get_gsg())
base.graphicsEngine.extractTextureData(myTex2,base.win.get_gsg())

# Store the output
frame = PNMImage()
print("Tex Store:",myTex2.store(frame))
frame.write("test.png")
1 Like