Improve VkShaderProgram to handle more program types

Fix memory alignment issues with the shader/pipeline keys
This commit is contained in:
Magnus Norddahl 2023-04-06 17:56:44 +02:00 committed by Christoph Oelckers
parent e5848ead7a
commit 3b93dd3d7e
3 changed files with 67 additions and 146 deletions

View file

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

View file

@ -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<VulkanShader> VkShaderManager::LoadVertShader(FString shadername, const char *vert_lump, const char *defines)

View file

@ -8,6 +8,7 @@
#include "name.h"
#include "hw_renderstate.h"
#include <list>
#include <map>
#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<VulkanShader> LoadVertShader(FString shadername, const char *vert_lump, const char *defines);
std::unique_ptr<VulkanShader> 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<VkShaderProgram> mMaterialShaders[MAX_PASS_TYPES];
std::vector<VkShaderProgram> mMaterialShadersNAT[MAX_PASS_TYPES];
std::vector<VkShaderProgram> mEffectShaders[MAX_PASS_TYPES];
uint8_t compilePass = 0, compileState = 0;
int compileIndex = 0;
std::map<VkShaderKey, VkShaderProgram> programs;
std::list<VkPPShader*> PPShaders;
};