Skip to content

Can't use read-only compute shader storage images on Vulkan/Linux due to descriptor mismatch #15250

@lukexi

Description

@lukexi

I get the following error when trying to use a shader storage image on Linux with the Vulkan backend (SDL 3.4.2)

Validation Error: [ VUID-VkComputePipelineCreateInfo-layout-07990 ] | MessageID = 0x830b328a
vkCreateComputePipelines(): pCreateInfos[0].stage SPIR-V (VK_SHADER_STAGE_COMPUTE_BIT) uses descriptor [Set 0, Binding 0, variable "u_src"] which has a VkDescriptorType mismatch.
  VkDescriptorSetLayoutBinding::descriptorType is VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
  Possible VkDescriptorType for the SPIR-V variable are: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
Info on SPIR-V mapping for each type:
 - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE is an OpTypeImage, with Sampled = 2, in UniformConstant
Full list of mappings can be found at https://docs.vulkan.org/spec/latest/chapters/interfaces.html#interfaces-resources-storage-class-correspondence
(VkDescriptorSetLayout from VkPipelineLayoutCreateInfo::pSetLayouts[0]).
The Vulkan spec states: If a resource variable is declared in a shader, layout is not VK_NULL_HANDLE, and the descriptor type is not VK_DESCRIPTOR_TYPE_MUTABLE_EXT, the corresponding descriptor set in layout must match the descriptor type (https://docs.vulkan.org/spec/latest/chapters/pipelines.html#VUID-VkComputePipelineCreateInfo-layout-07990)
Objects: 2
    [0] VkShaderModule 0x300000000030
    [1] VkDescriptorSetLayout 0x310000000031

Here's the minimal test program:

#include <SDL3/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>


int main(int argc, char const *argv[]) {
    SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
    SDL_Init(SDL_INIT_VIDEO);

    SDL_GPUDevice* device = SDL_CreateGPUDevice(
        SDL_GPU_SHADERFORMAT_SPIRV,
        true, // debug mode
        NULL // driver - NULL to auto-select
    );
    float w = 600; float h = 600;
    SDL_Window* window = SDL_CreateWindow("hi", w, h, SDL_WINDOW_HIGH_PIXEL_DENSITY);
    SDL_ClaimWindowForGPUDevice(device, window);

    char* file = "sdl-gpu-test.comp.spv";
    size_t code_size; void* code = SDL_LoadFile(file, &code_size);
    SDL_GPUComputePipeline* compute_pipeline = SDL_CreateGPUComputePipeline(
        device,
        &(SDL_GPUComputePipelineCreateInfo){
            .code_size=code_size,
            .code=code,
            .entrypoint="main",
            .format=SDL_GPU_SHADERFORMAT_SPIRV,
            .num_samplers=0,
            .num_uniform_buffers=0,
            .num_readonly_storage_textures=1,
            .num_readonly_storage_buffers=0,
            .num_readwrite_storage_textures=1,
            .num_readwrite_storage_buffers=0,
            .threadcount_x=32,
            .threadcount_y=32,
            .threadcount_z=1, 
        });
    if (!compute_pipeline) {
        printf("%s\n", SDL_GetError());
        exit(-1);
    }

    SDL_GPUTexture* texture1 = SDL_CreateGPUTexture(device, 
        &(SDL_GPUTextureCreateInfo){
            .type = SDL_GPU_TEXTURETYPE_2D,
            .format = SDL_GPU_TEXTUREFORMAT_R8_UINT,
            .usage = SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ,
            .width = w,
            .height = h,
            .layer_count_or_depth = 1,
            .num_levels = 1,
        });
    SDL_GPUTexture* texture2 = SDL_CreateGPUTexture(device, 
        &(SDL_GPUTextureCreateInfo){
            .type = SDL_GPU_TEXTURETYPE_2D,
            .format = SDL_GPU_TEXTUREFORMAT_R8_UINT,
            .usage = SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE,
            .width = w,
            .height = h,
            .layer_count_or_depth = 1,
            .num_levels = 1,
            .sample_count = SDL_GPU_SAMPLECOUNT_1,
        });

    SDL_GPUCommandBuffer* cmd_buf = SDL_AcquireGPUCommandBuffer(device);

    SDL_GPUComputePass* compute_pass = SDL_BeginGPUComputePass(cmd_buf,
        (SDL_GPUStorageTextureReadWriteBinding[]){
            { .texture = texture2,
              .cycle = true
            },
        }, 1,
        NULL, 0); // buffers
    SDL_BindGPUComputePipeline(compute_pass, compute_pipeline);
    SDL_BindGPUComputeStorageTextures(compute_pass, 
        0, (SDL_GPUTexture*[]){texture1}, 1);
    SDL_DispatchGPUCompute(compute_pass, ceil(w/32), ceil(h/32), 1);
    SDL_EndGPUComputePass(compute_pass);

    SDL_SubmitGPUCommandBuffer(cmd_buf);

    return 0;
}

and corresponding compute shader GLSL

#version 460
#define SDL_COMP_READ_ONLY_BINDING_SET  0 // read-only tex+buffers
#define SDL_COMP_READ_WRITE_BINDING_SET 1 // read-write tex+buffers
#define SDL_COMP_UNIFORM_BINDING_SET    2 // uniforms

layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;

layout(set = SDL_COMP_READ_ONLY_BINDING_SET, binding = 0, r8ui)  uniform readonly uimage2D u_src;
layout(set = SDL_COMP_READ_WRITE_BINDING_SET, binding = 0, r8ui) uniform writeonly uimage2D u_dst;

void main() {
    ivec2 p = ivec2(gl_GlobalInvocationID.xy);
    imageStore(u_dst, p, imageLoad(u_src, p));
}

Compiled and run with

glslang -V sdl-gpu-test.comp -o sdl-gpu-test.comp.spv && clang sdl-gpu-test.c -lSDL3 -lm && ./a.out

(Removing the "readonly" specifier also gives the same result)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions