From f7c1d4cccb88438de89dac88fa9e343d334e8251 Mon Sep 17 00:00:00 2001 From: bjorn Date: Thu, 1 Feb 2024 11:47:11 -0800 Subject: [PATCH] core/spv: return pointer to set/binding decorations instead of value; --- src/core/spv.c | 48 ++++++++++++++++++++++++++------- src/core/spv.h | 6 ++--- src/modules/graphics/graphics.c | 30 +++++++++++---------- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/core/spv.c b/src/core/spv.c index 44764de1..fb989463 100644 --- a/src/core/spv.c +++ b/src/core/spv.c @@ -20,8 +20,7 @@ typedef union { uint16_t name; } attribute; struct { - uint8_t set; - uint8_t binding; + uint16_t decoration; uint16_t name; } variable; struct { @@ -220,8 +219,12 @@ static spv_result spv_parse_decoration(spv_context* spv, const uint32_t* op, spv case 1: spv->cache[id].flag.number = op[3]; break; // SpecID case 6: spv->cache[id].type.arrayStride = op[3]; break; // ArrayStride (overrides name) case 30: spv->cache[id].attribute.location = op[3]; break; // Location - case 33: spv->cache[id].variable.binding = op[3]; break; // Binding - case 34: spv->cache[id].variable.set = op[3]; break; // Set + case 33: + case 34: + if (spv->cache[id].variable.decoration == 0xffff) { + spv->cache[id].variable.decoration = op - spv->words; + } + break; default: break; } @@ -352,11 +355,8 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i return spv_parse_field(spv, type, info->pushConstants, info); } - uint32_t set = spv->cache[variableId].variable.set; - uint32_t binding = spv->cache[variableId].variable.binding; - // Ignore output variables (storageClass 3) and anything without a set/binding decoration - if (storageClass == 3 || set == 0xff || binding == 0xff) { + if (storageClass == 3 || spv->cache[variableId].variable.decoration == 0xffff) { return SPV_OK; } @@ -374,8 +374,36 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i spv_resource* resource = &info->resources[info->resourceCount++]; - resource->set = set; - resource->binding = binding; + // Resolve the set/binding pointers. The cache stores the index of the first set/binding + // decoration word, we need to search for the "other" one. + const uint32_t* word = spv->words + spv->cache[variableId].variable.decoration; + bool set = word[2] == 34; + uint32_t other = set ? 33 : 34; + + if (set) { + resource->set = &word[3]; + } else { + resource->binding = &word[3]; + } + + for (;;) { + const uint32_t* next = word + OP_LENGTH(word); + + if (word == next || next > spv->edge || OP_CODE(next) != 71 || OP_CODE(next) != 72) { + break; + } + + word = next; + + if (OP_CODE(word) == 71 && word[1] == variableId && word[2] == other) { + if (set) { + resource->binding = &word[3]; + } else { + resource->set = &word[3]; + } + break; + } + } // If it's an array, read the array size and unwrap the inner type if (OP_CODE(type) == 28) { // OpTypeArray diff --git a/src/core/spv.h b/src/core/spv.h index 9b646c13..785ebda0 100644 --- a/src/core/spv.h +++ b/src/core/spv.h @@ -79,10 +79,10 @@ enum { }; typedef struct { - uint32_t set; - uint32_t binding; - uint32_t arraySize; + const uint32_t* set; + const uint32_t* binding; const char* name; + uint32_t arraySize; spv_resource_type type; spv_texture_dimension dimension; uint32_t textureFlags; diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 239f4f0e..39faddeb 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -2857,15 +2857,17 @@ Shader* lovrShaderCreate(const ShaderInfo* info) { for (uint32_t i = 0; i < spv[s].resourceCount; i++) { spv_resource* resource = &spv[s].resources[i]; - if (resource->set != userSet) { + if (*resource->set != userSet) { continue; } + uint32_t binding = *resource->binding; + lovrCheck(resource->arraySize == 0, "Arrays of resources in shaders are not currently supported"); - lovrCheck(resource->type != SPV_COMBINED_TEXTURE_SAMPLER, "Shader variable (%d) is a%s, which is not supported%s", resource->binding, " combined texture sampler", " (use e.g. texture2D instead of sampler2D)"); - lovrCheck(resource->type != SPV_UNIFORM_TEXEL_BUFFER, "Shader variable (%d) is a%s, which is not supported%s", resource->binding, " uniform texel buffer", ""); - lovrCheck(resource->type != SPV_STORAGE_TEXEL_BUFFER, "Shader variable (%d) is a%s, which is not supported%s", resource->binding, " storage texel buffer", ""); - lovrCheck(resource->type != SPV_INPUT_ATTACHMENT, "Shader variable (%d) is a%s, which is not supported%s", resource->binding, "n input attachment", ""); + lovrCheck(resource->type != SPV_COMBINED_TEXTURE_SAMPLER, "Shader variable (%d) is a%s, which is not supported%s", binding, " combined texture sampler", " (use e.g. texture2D instead of sampler2D)"); + lovrCheck(resource->type != SPV_UNIFORM_TEXEL_BUFFER, "Shader variable (%d) is a%s, which is not supported%s", binding, " uniform texel buffer", ""); + lovrCheck(resource->type != SPV_STORAGE_TEXEL_BUFFER, "Shader variable (%d) is a%s, which is not supported%s", binding, " storage texel buffer", ""); + lovrCheck(resource->type != SPV_INPUT_ATTACHMENT, "Shader variable (%d) is a%s, which is not supported%s", binding, "n input attachment", ""); static const gpu_slot_type resourceTypes[] = { [SPV_UNIFORM_BUFFER] = GPU_SLOT_UNIFORM_BUFFER, @@ -2893,8 +2895,8 @@ Shader* lovrShaderCreate(const ShaderInfo* info) { // It's ok to reuse binding slots (within or across stages), but the type must be consistent for (uint32_t j = 0; j < shader->resourceCount; j++) { ShaderResource* other = &shader->resources[j]; - if (other->binding == resource->binding) { - lovrCheck(other->type == resourceTypes[resource->type], "Shader variable with binding number %d is declared multiple times with inconsistent types", resource->binding); + if (other->binding == binding) { + lovrCheck(other->type == resourceTypes[resource->type], "Shader variable with binding number %d is declared multiple times with inconsistent types", binding); slots[j].stages |= stageMap[stage]; shader->resources[j].phase |= stagePhase[stage]; append = false; @@ -2912,17 +2914,17 @@ Shader* lovrShaderCreate(const ShaderInfo* info) { lovrThrow("Shader resource count exceeds resourcesPerShader limit (%d)", MAX_SHADER_RESOURCES); } - lovrCheck(resource->binding < 32, "Max resource binding number is %d", 32 - 1); + lovrCheck(binding < 32, "Max resource binding number is %d", 32 - 1); slots[index] = (gpu_slot) { - .number = resource->binding, + .number = binding, .type = resourceTypes[resource->type], .stages = stageMap[stage] }; shader->resources[index] = (ShaderResource) { .hash = hash, - .binding = resource->binding, + .binding = binding, .type = resourceTypes[resource->type], .phase = stagePhase[stage] }; @@ -2954,10 +2956,10 @@ Shader* lovrShaderCreate(const ShaderInfo* info) { bool texture = resource->type == SPV_SAMPLED_TEXTURE || resource->type == SPV_STORAGE_TEXTURE; bool sampler = resource->type == SPV_SAMPLER; bool storage = resource->type == SPV_STORAGE_BUFFER || resource->type == SPV_STORAGE_TEXTURE; - shader->bufferMask |= (buffer << resource->binding); - shader->textureMask |= (texture << resource->binding); - shader->samplerMask |= (sampler << resource->binding); - shader->storageMask |= (storage << resource->binding); + shader->bufferMask |= (buffer << binding); + shader->textureMask |= (texture << binding); + shader->samplerMask |= (sampler << binding); + shader->storageMask |= (storage << binding); if (storage) { shader->resources[index].cache = stage == STAGE_COMPUTE ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_STORAGE_READ;