core/spv: return pointer to set/binding decorations instead of value;

This commit is contained in:
bjorn 2024-02-01 11:47:11 -08:00
parent eecd201f57
commit f7c1d4cccb
3 changed files with 57 additions and 27 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;