From 3b93dd3d7e22d6ec07c1e3f4dfa343f90fb6df29 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Thu, 6 Apr 2023 17:56:44 +0200 Subject: [PATCH] Improve VkShaderProgram to handle more program types Fix memory alignment issues with the shader/pipeline keys --- .../rendering/vulkan/renderer/vk_renderpass.h | 36 +++-- .../rendering/vulkan/shaders/vk_shader.cpp | 144 ++++-------------- .../rendering/vulkan/shaders/vk_shader.h | 33 ++-- 3 files changed, 67 insertions(+), 146 deletions(-) diff --git a/src/common/rendering/vulkan/renderer/vk_renderpass.h b/src/common/rendering/vulkan/renderer/vk_renderpass.h index 23b5f4dbfa..af772e30f3 100644 --- a/src/common/rendering/vulkan/renderer/vk_renderpass.h +++ b/src/common/rendering/vulkan/renderer/vk_renderpass.h @@ -17,36 +17,42 @@ class GraphicsPipelineBuilder; class VkPipelineKey { public: - FRenderStyle RenderStyle; - union { struct { - uint32_t DrawType : 3; - uint32_t CullMode : 2; - uint32_t ColorMask : 4; - uint32_t DepthWrite : 1; - uint32_t DepthTest : 1; - uint32_t DepthClamp : 1; - uint32_t DepthBias : 1; - uint32_t DepthFunc : 2; - uint32_t StencilTest : 1; - uint32_t StencilPassOp : 2; - uint32_t Unused : 14; + uint64_t DrawType : 3; + uint64_t CullMode : 2; + uint64_t ColorMask : 4; + uint64_t DepthWrite : 1; + uint64_t DepthTest : 1; + uint64_t DepthClamp : 1; + uint64_t DepthBias : 1; + uint64_t DepthFunc : 2; + uint64_t StencilTest : 1; + uint64_t StencilPassOp : 2; + uint64_t Unused : 46; }; - uint32_t AsDWORD = 0; + uint64_t AsQWORD = 0; }; - VkShaderKey ShaderKey; int VertexFormat = 0; int NumTextureLayers = 0; + VkShaderKey ShaderKey; + FRenderStyle RenderStyle; + + int Padding = 0; // for 64 bit alignment + bool operator<(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) < 0; } bool operator==(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) == 0; } bool operator!=(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) != 0; } }; +static_assert(sizeof(FRenderStyle) == 4, "sizeof(FRenderStyle) is not its expected size!"); +static_assert(sizeof(VkShaderKey) == 16, "sizeof(VkShaderKey) is not its expected size!"); +static_assert(sizeof(VkPipelineKey) == 16 + 16 + 8, "sizeof(VkPipelineKey) is not its expected size!"); // If this assert fails, the flags union no longer adds up to 64 bits. Or there are gaps in the class so the memcmp doesn't work. + class VkRenderPassKey { public: diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index c4ce837f11..8a06831704 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -30,89 +30,6 @@ #include "version.h" #include "cmdlib.h" -bool VkShaderManager::CompileNextShader() -{ - const char *mainvp = "shaders/scene/vert_main.glsl"; - const char *mainfp = "shaders/scene/frag_surface.glsl"; - int i = compileIndex; - - if (compileState == 0) - { - // regular material shaders - - VkShaderProgram prog; - prog.vert = LoadVertShader(defaultshaders[i].ShaderName, mainvp, defaultshaders[i].Defines); - prog.frag = LoadFragShader(defaultshaders[i].ShaderName, mainfp, defaultshaders[i].material_lump, defaultshaders[i].mateffect_lump, defaultshaders[i].lightmodel_lump, defaultshaders[i].Defines, true, compilePass == GBUFFER_PASS); - mMaterialShaders[compilePass].push_back(std::move(prog)); - - compileIndex++; - if (defaultshaders[compileIndex].ShaderName == nullptr) - { - compileIndex = 0; - compileState++; - } - } - else if (compileState == 1) - { - // NAT material shaders - - VkShaderProgram natprog; - natprog.vert = LoadVertShader(defaultshaders[i].ShaderName, mainvp, defaultshaders[i].Defines); - natprog.frag = LoadFragShader(defaultshaders[i].ShaderName, mainfp, defaultshaders[i].material_lump, defaultshaders[i].mateffect_lump, defaultshaders[i].lightmodel_lump, defaultshaders[i].Defines, false, compilePass == GBUFFER_PASS); - mMaterialShadersNAT[compilePass].push_back(std::move(natprog)); - - compileIndex++; - if (compileIndex == SHADER_NoTexture) - { - compileIndex = 0; - compileState++; - if (usershaders.Size() == 0) compileState++; - } - } - else if (compileState == 2) - { - // user shaders - - const FString& name = ExtractFileBase(usershaders[i].shader); - FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines; - - VkShaderProgram prog; - prog.vert = LoadVertShader(name, mainvp, defines); - prog.frag = LoadFragShader(name, mainfp, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].mateffect_lump, defaultshaders[usershaders[i].shaderType].lightmodel_lump, defines, true, compilePass == GBUFFER_PASS); - mMaterialShaders[compilePass].push_back(std::move(prog)); - - compileIndex++; - if (compileIndex >= (int)usershaders.Size()) - { - compileIndex = 0; - compileState++; - } - } - else if (compileState == 3) - { - // Effect shaders - - VkShaderProgram prog; - prog.vert = LoadVertShader(effectshaders[i].ShaderName, mainvp, effectshaders[i].defines); - prog.frag = LoadFragShader(effectshaders[i].ShaderName, effectshaders[i].fp1, effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].fp4, effectshaders[i].defines, true, compilePass == GBUFFER_PASS); - mEffectShaders[compilePass].push_back(std::move(prog)); - - compileIndex++; - if (compileIndex >= MAX_EFFECTS) - { - compileIndex = 0; - compilePass++; - if (compilePass == MAX_PASS_TYPES) - { - compileIndex = -1; // we're done. - return true; - } - compileState = 0; - } - } - return false; -} - VkShaderManager::VkShaderManager(VulkanRenderDevice* fb) : fb(fb) { CompileNextShader(); @@ -128,41 +45,42 @@ void VkShaderManager::Deinit() RemoveVkPPShader(PPShaders.back()); } -VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key, EPassType passType) +VkShaderProgram* VkShaderManager::Get(const VkShaderKey& k, EPassType passType) { - if (key.SpecialEffect != EFF_NONE) + VkShaderKey key = k; + key.GBufferPass = passType; + auto& program = programs[key]; + if (!program.frag) { - return GetEffect(key.SpecialEffect, passType); - } - else - { - return Get(key.EffectState, key.AlphaTest, passType); - } -} + const char* mainvp = "shaders/scene/vert_main.glsl"; + const char* mainfp = "shaders/scene/frag_surface.glsl"; -VkShaderProgram *VkShaderManager::GetEffect(int effect, EPassType passType) -{ - if (compileIndex == -1 && effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[passType][effect].frag) - { - return &mEffectShaders[passType][effect]; - } - return nullptr; -} + if (key.SpecialEffect != EFF_NONE) + { + const auto& desc = effectshaders[key.SpecialEffect]; + program.vert = LoadVertShader(desc.ShaderName, mainvp, desc.defines); + program.frag = LoadFragShader(desc.ShaderName, desc.fp1, desc.fp2, desc.fp3, desc.fp4, desc.defines, true, key.GBufferPass); + } + else + { + if (key.EffectState < FIRST_USER_SHADER) + { + const auto& desc = defaultshaders[key.EffectState]; + program.vert = LoadVertShader(desc.ShaderName, mainvp, desc.Defines); + program.frag = LoadFragShader(desc.ShaderName, mainfp, desc.material_lump, desc.mateffect_lump, desc.lightmodel_lump, desc.Defines, key.AlphaTest, key.GBufferPass); + } + else + { + const auto& desc = usershaders[key.EffectState]; + const FString& name = ExtractFileBase(desc.shader); + FString defines = defaultshaders[desc.shaderType].Defines + desc.defines; -VkShaderProgram *VkShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType) -{ - if (compileIndex != -1) - return &mMaterialShaders[0][0]; - // indices 0-2 match the warping modes, 3 no texture, the following are custom - if (!alphateston && eff < SHADER_NoTexture) - { - return &mMaterialShadersNAT[passType][eff]; // Non-alphatest shaders are only created for default, warp1+2. The rest won't get used anyway + program.vert = LoadVertShader(name, mainvp, defines); + program.frag = LoadFragShader(name, mainfp, desc.shader, defaultshaders[desc.shaderType].mateffect_lump, defaultshaders[desc.shaderType].lightmodel_lump, defines, key.AlphaTest, key.GBufferPass); + } + } } - else if (eff < (unsigned int)mMaterialShaders[passType].size()) - { - return &mMaterialShaders[passType][eff]; - } - return nullptr; + return &program; } std::unique_ptr VkShaderManager::LoadVertShader(FString shadername, const char *vert_lump, const char *defines) diff --git a/src/common/rendering/vulkan/shaders/vk_shader.h b/src/common/rendering/vulkan/shaders/vk_shader.h index a4b15edf93..067eb28ca8 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.h +++ b/src/common/rendering/vulkan/shaders/vk_shader.h @@ -8,6 +8,7 @@ #include "name.h" #include "hw_renderstate.h" #include +#include #define SHADER_MIN_REQUIRED_TEXTURE_LAYERS 11 @@ -65,16 +66,17 @@ public: { struct { - uint32_t AlphaTest : 1; - uint32_t Simple2D : 1; // uFogEnabled == -3 - uint32_t TextureMode : 3; // uTextureMode & 0xffff - uint32_t ClampY : 1; // uTextureMode & TEXF_ClampY - uint32_t Brightmap : 1; // uTextureMode & TEXF_Brightmap - uint32_t Detailmap : 1; // uTextureMode & TEXF_Detailmap - uint32_t Glowmap : 1; // uTextureMode & TEXF_Glowmap - uint32_t Unused : 23; + uint64_t AlphaTest : 1; // !NO_ALPHATEST + uint64_t Simple2D : 1; // uFogEnabled == -3 + uint64_t TextureMode : 3; // uTextureMode & 0xffff + uint64_t ClampY : 1; // uTextureMode & TEXF_ClampY + uint64_t Brightmap : 1; // uTextureMode & TEXF_Brightmap + uint64_t Detailmap : 1; // uTextureMode & TEXF_Detailmap + uint64_t Glowmap : 1; // uTextureMode & TEXF_Glowmap + uint64_t GBufferPass : 1; // GBUFFER_PASS + uint64_t Unused : 54; }; - uint32_t AsDWORD = 0; + uint64_t AsQWORD = 0; }; int SpecialEffect = 0; @@ -85,6 +87,8 @@ public: bool operator!=(const VkShaderKey& other) const { return memcmp(this, &other, sizeof(VkShaderKey)) != 0; } }; +static_assert(sizeof(VkShaderKey) == 16, "sizeof(VkShaderKey) is not its expected size!"); // If this assert fails, the flags union no longer adds up to 64 bits. Or there are gaps in the class so the memcmp doesn't work. + class VkShaderProgram { public: @@ -102,7 +106,7 @@ public: VkShaderProgram* Get(const VkShaderKey& key, EPassType passType); - bool CompileNextShader(); + bool CompileNextShader() { return true; } VkPPShader* GetVkShader(PPShader* shader); @@ -110,9 +114,6 @@ public: void RemoveVkPPShader(VkPPShader* shader); private: - VkShaderProgram* GetEffect(int effect, EPassType passType); - VkShaderProgram* Get(unsigned int eff, bool alphateston, EPassType passType); - std::unique_ptr LoadVertShader(FString shadername, const char *vert_lump, const char *defines); std::unique_ptr LoadFragShader(FString shadername, const char *frag_lump, const char *material_lump, const char* mateffect_lump, const char *lightmodel_lump, const char *defines, bool alphatest, bool gbufferpass); @@ -124,11 +125,7 @@ private: VulkanRenderDevice* fb = nullptr; - std::vector mMaterialShaders[MAX_PASS_TYPES]; - std::vector mMaterialShadersNAT[MAX_PASS_TYPES]; - std::vector mEffectShaders[MAX_PASS_TYPES]; - uint8_t compilePass = 0, compileState = 0; - int compileIndex = 0; + std::map programs; std::list PPShaders; };