Compute shader "Texture has an unsized format." but its format is Texture.F_rgba8

Hi all,

I’m trying to use a compute shader to make an adjacency matrix for the boundaries between cells in a voronoi diagram. The voronoi diagram texture and the texture containing the boundary information are created by this method, as btex, and used only in simple render to texture methods with quads, their format is not altered:

# Create a GraphicsBuffer to render to, we'll get the textures out of this
finalBuffer = engine.makeOutput(pipe, "Buffer", 0, fb_prop, win_prop, flags)
ftex = pc.Texture("final texture")
ftex.setup2dTexture(texW, texH, pc.Texture.T_unsigned_byte, pc.Texture.F_rgba8)
ftex.setWrapV(pc.Texture.WM_clamp)
ftex.setWrapU(pc.Texture.WM_repeat)
ftex.setClearColor((0,0,0,0))
finalBuffer.addRenderTexture(ftex, pc.GraphicsOutput.RTM_copy_ram)
def makeBuffer(textureName: str):
    buffer = engine.makeOutput(pipe, "Buffer", 0, fb_prop, win_prop, flags, finalBuffer.getGsg(), finalBuffer)
    btex = pc.Texture(textureName)
    btex.setup2dTexture(texW, texH, pc.Texture.T_unsigned_byte, pc.Texture.F_rgba8)
    btex.setWrapV(pc.Texture.WM_clamp)
    btex.setWrapU(pc.Texture.WM_repeat)
    btex.setClearColor((0,0,0,0))
    buffer.addRenderTexture(btex, pc.GraphicsOutput.RTM_copy_ram)
    return buffer, btex

The method calling the compute shader is:

def boundaryGraph(seeds: int, voronoi: pc.Texture, boundaries: pc.Texture):
    # Create the texture we'll render to
    boundaryCounts = pc.Texture()
    boundaryCounts.setup2dTexture(seeds, seeds, pc.Texture.T_unsigned_byte, pc.Texture.F_rgba8)
    boundaryCounts.setClearColor((0,0,0,0))
    # Create the node
    node = pc.NodePath("compute")
    # Assign the shader and the shader inputs
    node.set_shader(boundaryCountShader)
    node.set_shader_input("voronoi", voronoi)
    node.set_shader_input("boundaries", boundaries)
    node.set_shader_input("counts", boundaryCounts)
    # Retrieve the underlying ShaderAttrib
    sattr = node.get_attrib(pc.ShaderAttrib)
    # Dispatch the compute shader
    engine.dispatch_compute((int(texW / 32), int(texH / 32), 1), sattr, finalBuffer.getGsg())
    return boundaryCounts

The compute shader itself is:

#version 430

// Set the number of invocations in the work group.
layout (local_size_x = 32, local_size_y = 32) in;

// Declare uniforms we will use
layout (rgba8) uniform readonly image2D voronoi;
layout (rgba8) uniform readonly image2D boundaries;
layout (rgba8) uniform image2D counts;

//============================================================
vec4 EncodeData (in ivec2 texcoord) {
    vec4 count = imageLoad(counts, texcoord);
    if (imageLoad(boundaries, texcoord).r == 1.0) {
        // diverging
        int count_d = int(count.b*256) + (int(count.r*256) << 8);
        count_d += 1;
        count.r = count_d >> 8;
        count.b = count_d & 0x00FF;
    } else {
        // converging
        int count_c = int(count.g*256) + (int(count.a*256) << 8);
        count_c += 1;
        count.g = count_c >> 8;
        count.a = count_c & 0x00FF;
    }
    return count;
}

//============================================================
void main (void)
{
    ivec2 texcoord = ivec2(gl_GlobalInvocationID.xy);
    int ourPlate = int(imageLoad(voronoi, texcoord).b*256);
    int theirPlate = 256 - int(imageLoad(boundaries, texcoord).a*256);
    ivec2 boundary = ivec2(ourPlate-1, theirPlate-1);
    vec4 pixel = EncodeData(texcoord);
    imageStore(counts, boundary, pixel);
}

When I run this, the shader compiles and executes, but I get some warnings from the Graphics State Guardian and the texture renders out with its clear colour:

:display:gsg:glgsg(error): Texture boundaries has an unsized format.  Textures bound to a shader as an image need a sized format.
:display:gsg:glgsg(error): Texture voronoi has an unsized format.  Textures bound to a shader as an image need a sized format.

I’ve read the panda3d documentation for compute shaders that mentions using a sized format for them, but all of the textures I’m using use the sized format Texture.F_rgba8 so I’m not sure what the cause of the error is. I’ve also tried the compute shader with layout (rgba32f) and the result is identical.

Any help is greatly appreciated!

What happens if you set gl-immutable-texture-storage true in Config.prc?

Thanks for the advice!

I’ve added the following code to my program as I would prefer to modify the config from the program so that it still works if I choose to distribute my program:

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

This resolves the warning problem, but the resulting image is still rendered out with just the clear colour.

Edit:

Hmmm… There seems to be a deeper issue with the texture beyond the use of a compute shader. I’ve temporarily modified my code as follows, removing the dispatch call to the compute shader and changing the clear colour to red:

def boundaryGraph(seeds, voronoi: pc.Texture, boundaries: pc.Texture):
    # Create the texture we'll render to
    boundaryCounts = pc.Texture()
    boundaryCounts.setClearColor((1,0,0,1))
    boundaryCounts.setup2dTexture(seeds, seeds, pc.Texture.T_unsigned_byte, pc.Texture.F_rgba8)
    boundaryCounts.clearImage()
    # Create the node
    node = pc.NodePath("compute")
    # Assign the shader and the shader inputs
    node.set_shader(boundaryCountShader)
    node.set_shader_input("voronoi", voronoi)
    node.set_shader_input("boundaries", boundaries)
    node.set_shader_input("counts", boundaryCounts)
    # Retrieve the underlying ShaderAttrib
    sattr = node.get_attrib(pc.ShaderAttrib)
    # Dispatch the compute shader
    # engine.dispatch_compute((int(texW / 32), int(texH / 32), 1), sattr, finalBuffer.getGsg())
    return boundaryCounts

Saving boundaryCounts with:

def storeTextureAsImage(texture: pc.Texture, filename: str):
    frame = pc.PNMImage()
    texture.store(frame)
    storeFrameAsImage(frame, filename)

def storeFrameAsImage(frame: pc.PNMImage, filename: str):
    frame.write(path_p3d+filename+".png")

results in just a blank image with (0,0,0,0) in every pixel, which doesn’t happen for the 5 other textures I save with the same methods every time I run the code. Am I misunderstanding the role of clear colour somehow? Based on the following description from the docs I would expect the image to come back red, which leads me to believe something else is the problem.

Sets the color that will be used to fill the texture image in absence of any image data. It is used when any of the setupTexture() functions or clearImage() is called and image data is not provided using read() or modifyRamImage().

The result is the same if I set the clear colour after the call to setup2dTexture() and with and without calling boundaryCounts.clearImage()

The texture data is not transferred from the graphics card back to CPU memory automatically. You need to call base.graphicsEngine.extractTextureData to make this happen.