From 377d269f4a9e7fa600765ccf87d00a611b08a706 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 27 Jan 2023 02:36:10 +0100 Subject: [PATCH] vulkan: remap bindings to prevent clash Vulkan does not differentiate between between bindings for textures and buffers, as opposed to opengl. This can lead to situations, where different uniforms get assigned the same binding leading to a crash, when trying to update a descriptor set with the wrong type. This patch solves this problem, by remapping the bindings when needed. --- src/modules/graphics/vulkan/Graphics.cpp | 6 +- src/modules/graphics/vulkan/Shader.cpp | 129 +++++++++++++++++------ 2 files changed, 99 insertions(+), 36 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 2ee3ca231..869ff8531 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -579,9 +579,9 @@ void Graphics::initCapabilities() capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = properties.limits.maxImageDimensionCube; capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = properties.limits.maxTexelBufferElements; capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = properties.limits.maxStorageBufferRange; - capabilities.limits[LIMIT_THREADGROUPS_X] = properties.limits.maxComputeWorkGroupSize[0]; - capabilities.limits[LIMIT_THREADGROUPS_Y] = properties.limits.maxComputeWorkGroupSize[1]; - capabilities.limits[LIMIT_THREADGROUPS_Z] = properties.limits.maxComputeWorkGroupSize[2]; + capabilities.limits[LIMIT_THREADGROUPS_X] = properties.limits.maxComputeWorkGroupCount[0]; + capabilities.limits[LIMIT_THREADGROUPS_Y] = properties.limits.maxComputeWorkGroupCount[1]; + capabilities.limits[LIMIT_THREADGROUPS_Z] = properties.limits.maxComputeWorkGroupCount[2]; capabilities.limits[LIMIT_RENDER_TARGETS] = properties.limits.maxColorAttachments; capabilities.limits[LIMIT_TEXTURE_MSAA] = static_cast(getMsaaCount(64)); capabilities.limits[LIMIT_ANISOTROPY] = properties.limits.maxSamplerAnisotropy; diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 7bf87ad0f..8d71c7f43 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -144,6 +144,72 @@ static const TBuiltInResource defaultTBuiltInResource = { static const uint32_t STREAMBUFFER_DEFAULT_SIZE = 16; static const uint32_t DESCRIPTOR_POOL_SIZE = 1; +class BindingMapper +{ +public: + uint32_t operator()(spirv_cross::CompilerGLSL &comp, std::vector &spirv, const std::string &name, const spirv_cross::ID &id) + { + auto it = bindingMappings.find(name); + if (it == bindingMappings.end()) + { + auto binding = comp.get_decoration(id, spv::DecorationBinding); + + if (isFreeBinding(binding)) + { + bindingMappings[name] = binding; + return binding; + } + else + { + uint32_t freeBinding = getFreeBinding(); + + uint32_t binaryBindingOffset; + if (!comp.get_binary_offset_for_decoration(id, spv::DecorationBinding, binaryBindingOffset)) + throw love::Exception("could not get binary offset for binding"); + + spirv[binaryBindingOffset] = freeBinding; + + bindingMappings[name] = freeBinding; + + return freeBinding; + } + } + else + return it->second; + }; + + +private: + uint32_t getFreeBinding() { + for (uint32_t i = 0;; i++) + { + bool free = true; + for (const auto &entry : bindingMappings) + { + if (entry.second == i) + { + free = false; + break; + } + } + if (free) + return i; + } + } + + bool isFreeBinding(uint32_t binding) { + for (const auto &entry : bindingMappings) + { + if (entry.second == binding) + return false; + } + return true; + } + + std::map bindingMappings; + +}; + static VkShaderStageFlagBits getStageBit(ShaderStageType type) { switch (type) @@ -735,14 +801,16 @@ void Shader::compileShaders() uniformInfos.clear(); + BindingMapper bindingMapper; + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { auto shaderStage = (ShaderStageType)i; auto glslangStage = getGlslShaderType(shaderStage); auto intermediate = program->getIntermediate(glslangStage); - if (intermediate == nullptr) { + + if (intermediate == nullptr) continue; - } spv::SpvBuildLogger logger; glslang::SpvOptions opt; @@ -751,31 +819,6 @@ void Shader::compileShaders() std::vector spirv; GlslangToSpv(*intermediate, spirv, &logger, &opt); - std::string msgs = logger.getAllMessages(); - - VkShaderModuleCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = spirv.size() * sizeof(uint32_t); - createInfo.pCode = spirv.data(); - - auto device = vgfx->getDevice(); - - VkShaderModule shaderModule; - - if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { - throw love::Exception("failed to create shader module"); - } - - shaderModules.push_back(shaderModule); - - VkPipelineShaderStageCreateInfo shaderStageInfo{}; - shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageInfo.stage = getStageBit((ShaderStageType)i); - shaderStageInfo.module = shaderModule; - shaderStageInfo.pName = "main"; - - shaderStages.push_back(shaderStageInfo); - spirv_cross::CompilerGLSL comp(spirv); // we only care about variables that are actually getting used. @@ -792,7 +835,7 @@ void Shader::compileShaders() auto defaultUniformBlockSize = comp.get_declared_struct_size(type); localUniformStagingData.resize(defaultUniformBlockSize); localUniformData.resize(defaultUniformBlockSize); - localUniformLocation = comp.get_decoration(resource.id, spv::DecorationBinding); + localUniformLocation = bindingMapper(comp, spirv, resource.name, resource.id); memset(localUniformStagingData.data(), 0, defaultUniformBlockSize); memset(localUniformData.data(), 0, defaultUniformBlockSize); @@ -813,7 +856,7 @@ void Shader::compileShaders() const SPIRType &imagetype = comp.get_type(basetype.image.type); graphics::Shader::UniformInfo info; - info.location = comp.get_decoration(r.id, spv::DecorationBinding); + info.location = bindingMapper(comp, spirv, r.name, r.id); info.baseType = UNIFORM_SAMPLER; info.name = r.name; info.count = type.array.empty() ? 1 : type.array[0]; @@ -894,7 +937,7 @@ void Shader::compileShaders() if (!fillUniformReflectionData(u)) continue; - u.location = comp.get_decoration(r.id, spv::DecorationBinding); + u.location = bindingMapper(comp, spirv, r.name, r.id); u.buffers = new love::graphics::Buffer *[u.count]; for (int i = 0; i < u.count; i++) @@ -917,13 +960,11 @@ void Shader::compileShaders() continue; u.textures = new love::graphics::Texture *[u.count]; - u.location = comp.get_decoration(r.id, spv::DecorationBinding); + u.location = bindingMapper(comp, spirv, r.name, r.id); for (int i = 0; i < u.count; i++) u.textures[i] = nullptr; - // some stuff missing ? - uniformInfos[u.name] = u; } @@ -936,6 +977,28 @@ void Shader::compileShaders() attributes[name] = attributeLocation; } } + + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = spirv.size() * sizeof(uint32_t); + createInfo.pCode = spirv.data(); + + auto device = vgfx->getDevice(); + + VkShaderModule shaderModule; + + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) + throw love::Exception("failed to create shader module"); + + shaderModules.push_back(shaderModule); + + VkPipelineShaderStageCreateInfo shaderStageInfo{}; + shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageInfo.stage = getStageBit((ShaderStageType)i); + shaderStageInfo.module = shaderModule; + shaderStageInfo.pName = "main"; + + shaderStages.push_back(shaderStageInfo); } delete program;