implement custom uniforms for material shaders

This commit is contained in:
Ricardo Luís Vaz Silva 2025-01-08 06:07:29 -03:00 committed by Nash Muhandes
commit ea09ae1461
13 changed files with 406 additions and 299 deletions

View file

@ -1142,7 +1142,7 @@ UniformField UserUniforms::GetField(const FString &name)
return {field.Type, UniformStruct + field.Offset};
}
void UserUniforms::WriteUniforms(UniformStructHolder &Uniforms)
void UserUniforms::WriteUniforms(UniformStructHolder &Uniforms) const
{
Uniforms.Clear();
Uniforms.addr = UniformStruct;

View file

@ -26,6 +26,9 @@ enum class UniformType
Mat4
};
const char* GetTypeStr(UniformType type);
struct UserUniformValue
{
UniformType Type = UniformType::Undefined;
@ -136,7 +139,7 @@ public:
//must only be called once
void LoadUniforms(const TMap<FString, UserUniformValue> &Uniforms);
void WriteUniforms(UniformStructHolder &Data);
void WriteUniforms(UniformStructHolder &Data) const;
int UniformStructSize = 0;

View file

@ -131,7 +131,7 @@ VkVertexFormat *VkRenderPassManager::GetVertexFormat(int index)
return &VertexFormats[index];
}
VulkanPipelineLayout* VkRenderPassManager::GetPipelineLayout(bool levelmesh)
VulkanPipelineLayout* VkRenderPassManager::GetPipelineLayout(bool levelmesh, int UserUniformSize)
{
auto &layout = PipelineLayouts[levelmesh];
if (layout)
@ -144,6 +144,12 @@ VulkanPipelineLayout* VkRenderPassManager::GetPipelineLayout(bool levelmesh)
builder.AddSetLayout(levelmesh ? descriptors->GetLevelMeshLayout() : descriptors->GetRSBufferLayout());
builder.AddSetLayout(descriptors->GetBindlessLayout());
builder.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants));
if(UserUniformSize > 0)
{
builder.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushConstants), UserUniformSize);
}
builder.DebugName("VkRenderPassManager.PipelineLayout");
layout = builder.Create(fb->GetDevice());
return layout.get();
@ -273,15 +279,25 @@ VulkanRenderPass *VkRenderPassSetup::GetRenderPass(int clearTargets)
return RenderPasses[clearTargets].get();
}
VulkanPipeline *VkRenderPassSetup::GetPipeline(const VkPipelineKey &key)
VulkanPipeline *VkRenderPassSetup::GetPipeline(const VkPipelineKey &key, UniformStructHolder &Uniforms)
{
auto &item = Pipelines[key];
if (!item)
item = CreatePipeline(key);
return item.get();
auto item = Pipelines.find(key);
if (item == Pipelines.end())
{
Uniforms.Clear();
auto pipeline = CreatePipeline(key, Uniforms);
auto ptr = pipeline.get();
Pipelines.insert(std::pair<VkPipelineKey, PipelineData>{key, PipelineData{std::move(pipeline), Uniforms}});
return ptr;
}
else
{
Uniforms = item->second.Uniforms;
return item->second.pipeline.get();
}
}
std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipelineKey &key)
std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipelineKey &key, UniformStructHolder &Uniforms)
{
GraphicsPipelineBuilder builder;
builder.Cache(fb->GetRenderPassManager()->GetCache());
@ -365,10 +381,12 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipeli
builder.RasterizationSamples((VkSampleCountFlagBits)PassKey.Samples);
builder.Layout(fb->GetRenderPassManager()->GetPipelineLayout(key.ShaderKey.UseLevelMesh));
builder.Layout(fb->GetRenderPassManager()->GetPipelineLayout(key.ShaderKey.UseLevelMesh, program->Uniforms.sz));
builder.RenderPass(GetRenderPass(0));
builder.DebugName("VkRenderPassSetup.Pipeline");
Uniforms = program->Uniforms;
return builder.Create(fb->GetDevice());
}

View file

@ -69,21 +69,27 @@ public:
bool operator!=(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) != 0; }
};
struct PipelineData
{
std::unique_ptr<VulkanPipeline> pipeline;
UniformStructHolder Uniforms;
};
class VkRenderPassSetup
{
public:
VkRenderPassSetup(VulkanRenderDevice* fb, const VkRenderPassKey &key);
VulkanRenderPass *GetRenderPass(int clearTargets);
VulkanPipeline *GetPipeline(const VkPipelineKey &key);
VulkanPipeline *GetPipeline(const VkPipelineKey &key, UniformStructHolder &Uniforms);
VkRenderPassKey PassKey;
std::unique_ptr<VulkanRenderPass> RenderPasses[8];
std::map<VkPipelineKey, std::unique_ptr<VulkanPipeline>> Pipelines;
std::map<VkPipelineKey, PipelineData> Pipelines;
private:
std::unique_ptr<VulkanRenderPass> CreateRenderPass(int clearTargets);
std::unique_ptr<VulkanPipeline> CreatePipeline(const VkPipelineKey &key);
std::unique_ptr<VulkanPipeline> CreatePipeline(const VkPipelineKey &key, UniformStructHolder &Uniforms);
VulkanRenderDevice* fb = nullptr;
};
@ -109,7 +115,7 @@ public:
VkRenderPassSetup *GetRenderPass(const VkRenderPassKey &key);
int GetVertexFormat(const std::vector<size_t>& bufferStrides, const std::vector<FVertexBufferAttribute>& attrs);
VkVertexFormat *GetVertexFormat(int index);
VulkanPipelineLayout* GetPipelineLayout(bool levelmesh);
VulkanPipelineLayout* GetPipelineLayout(bool levelmesh, int UserUniformSize);
VkPPRenderPassSetup* GetPPRenderPass(const VkPPRenderPassKey& key);

View file

@ -138,14 +138,14 @@ FString VkPPShader::CreateUniformBlockDecl(const char* name, const std::vector<U
decl.Format("layout(%s) uniform %s\n{\n", layout.GetChars(), name);
for (size_t i = 0; i < fields.size(); i++)
{
decl.AppendFormat("\t%s %s;\n", GetTypeStr(fields[i].Type), fields[i].Name);
decl.AppendFormat("\t%s %s;\n", GetTypeStr(fields[i].Type), fields[i].Name.GetChars());
}
decl += "};\n";
return decl;
}
const char* VkPPShader::GetTypeStr(UniformType type)
const char* GetTypeStr(UniformType type)
{
switch (type)
{

View file

@ -25,7 +25,6 @@ public:
private:
FString LoadShaderCode(const FString &lumpname, const FString &defines, int version);
static FString CreateUniformBlockDecl(const char* name, const std::vector<UniformFieldDesc>& fields, int bindingpoint);
static const char* GetTypeStr(UniformType type);
ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system);
FString LoadPublicShaderLump(const char* lumpname);

View file

@ -113,9 +113,9 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key)
};
const auto& desc = effectshaders[key.SpecialEffect];
program.vert = LoadVertShader(desc.ShaderName, mainvp, vert_nocustom, desc.defines, key);
program.vert = LoadVertShader(desc.ShaderName, mainvp, vert_nocustom, desc.defines, key, nullptr);
if (!key.NoFragmentShader)
program.frag = LoadFragShader(desc.ShaderName, desc.fp1, desc.fp2, desc.fp3, desc.fp4, desc.fp5, desc.defines, key);
program.frag = LoadFragShader(desc.ShaderName, desc.fp1, desc.fp2, desc.fp3, desc.fp4, desc.fp5, desc.defines, key, nullptr);
}
else
{
@ -153,9 +153,9 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key)
if (key.EffectState < FIRST_USER_SHADER)
{
const auto& desc = defaultshaders[key.EffectState];
program.vert = LoadVertShader(desc.ShaderName, mainvp, vert_nocustom, desc.Defines, key);
program.vert = LoadVertShader(desc.ShaderName, mainvp, vert_nocustom, desc.Defines, key, nullptr);
if (!key.NoFragmentShader)
program.frag = LoadFragShader(desc.ShaderName, mainfp, desc.material_lump, desc.mateffect_lump, desc.lightmodel_lump_shared, desc.lightmodel_lump, desc.Defines, key);
program.frag = LoadFragShader(desc.ShaderName, mainfp, desc.material_lump, desc.mateffect_lump, desc.lightmodel_lump_shared, desc.lightmodel_lump, desc.Defines, key, nullptr);
}
else
{
@ -163,15 +163,45 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key)
const FString& name = ExtractFileBase(desc.shader.GetChars());
FString defines = defaultshaders[desc.shaderType].Defines + desc.defines;
program.vert = LoadVertShader(name, mainvp, desc.vertshader.IsEmpty() ? vert_nocustom : desc.vertshader.GetChars(), defines.GetChars(), key);
program.vert = LoadVertShader(name, mainvp, desc.vertshader.IsEmpty() ? vert_nocustom : desc.vertshader.GetChars(), defines.GetChars(), key, &desc.Uniforms);
if (!key.NoFragmentShader)
program.frag = LoadFragShader(name, mainfp, desc.shader.GetChars(), defaultshaders[desc.shaderType].mateffect_lump, defaultshaders[desc.shaderType].lightmodel_lump_shared, defaultshaders[desc.shaderType].lightmodel_lump, defines.GetChars(), key);
program.frag = LoadFragShader(name, mainfp, desc.shader.GetChars(), defaultshaders[desc.shaderType].mateffect_lump, defaultshaders[desc.shaderType].lightmodel_lump_shared, defaultshaders[desc.shaderType].lightmodel_lump, defines.GetChars(), key, &desc.Uniforms);
desc.Uniforms.WriteUniforms(program.Uniforms);
}
}
return &program;
}
std::unique_ptr<VulkanShader> VkShaderManager::LoadVertShader(FString shadername, const char *vert_lump, const char *vert_lump_custom, const char *defines, const VkShaderKey& key)
static void addPushConstants(FString &layoutBlock, const UserUniforms *uniforms)
{
layoutBlock << "// This must match the PushConstants struct\n";
layoutBlock << "layout(push_constant) uniform PushConstants\n";
layoutBlock << "{\n";
layoutBlock << "#if defined(USE_LEVELMESH)\n";
layoutBlock << " int unused0;\n";
layoutBlock << " int unused1;\n";
layoutBlock << "#else\n";
layoutBlock << " int uDataIndex; // surfaceuniforms index\n";
layoutBlock << " int uLightIndex; // dynamic lights\n";
layoutBlock << "#endif\n";
layoutBlock << " int uBoneIndexBase; // bone animation\n";
layoutBlock << " int uFogballIndex; // fog balls\n";
if(uniforms && uniforms->UniformStructSize)
{
for(auto &field : uniforms->Fields)
{
//layoutBlock.AppendFormat(" layout(offset = %d) %s %s;\n", (field.Offset + sizeof(PushConstants)), GetTypeStr(field.Type), field.Name.GetChars());
layoutBlock.AppendFormat(" %s %s;\n", GetTypeStr(field.Type), field.Name.GetChars());
}
}
layoutBlock << "};\n";
}
std::unique_ptr<VulkanShader> VkShaderManager::LoadVertShader(FString shadername, const char *vert_lump, const char *vert_lump_custom, const char *defines, const VkShaderKey& key, const UserUniforms *uniforms)
{
FString definesBlock;
definesBlock << defines;
@ -192,9 +222,11 @@ std::unique_ptr<VulkanShader> VkShaderManager::LoadVertShader(FString shadername
definesBlock << "#define USE_LEVELMESH\n";
FString layoutBlock;
addPushConstants(layoutBlock, uniforms);
layoutBlock << LoadPrivateShaderLump("shaders/scene/layout_shared.glsl").GetChars() << "\n";
layoutBlock << LoadPrivateShaderLump("shaders/scene/layout_vert.glsl").GetChars() << "\n";
FString codeBlock;
codeBlock << LoadPrivateShaderLump(vert_lump).GetChars() << "\n";
codeBlock << LoadPublicShaderLump(vert_lump_custom).GetChars() << "\n";
@ -211,7 +243,7 @@ std::unique_ptr<VulkanShader> VkShaderManager::LoadVertShader(FString shadername
.Create(shadername.GetChars(), fb->GetDevice());
}
std::unique_ptr<VulkanShader> VkShaderManager::LoadFragShader(FString shadername, const char *frag_lump, const char *material_lump, const char* mateffect_lump, const char *light_lump_shared, const char *light_lump, const char *defines, const VkShaderKey& key)
std::unique_ptr<VulkanShader> VkShaderManager::LoadFragShader(FString shadername, const char *frag_lump, const char *material_lump, const char* mateffect_lump, const char *light_lump_shared, const char *light_lump, const char *defines, const VkShaderKey& key, const UserUniforms *uniforms)
{
FString definesBlock;
if (fb->IsRayQueryEnabled()) definesBlock << "\n#define SUPPORTS_RAYQUERY\n";
@ -299,9 +331,11 @@ std::unique_ptr<VulkanShader> VkShaderManager::LoadFragShader(FString shadername
else definesBlock << "#define uFogEnabled 0\n";
FString layoutBlock;
addPushConstants(layoutBlock, uniforms);
layoutBlock << LoadPrivateShaderLump("shaders/scene/layout_shared.glsl").GetChars() << "\n";
layoutBlock << LoadPrivateShaderLump("shaders/scene/layout_frag.glsl").GetChars() << "\n";
FString codeBlock;
codeBlock << LoadPrivateShaderLump(frag_lump).GetChars() << "\n";

View file

@ -10,6 +10,8 @@
#include <list>
#include <map>
#include "hwrenderer/postprocessing/hw_useruniforms.h"
class ShaderIncludeResult;
class VulkanRenderDevice;
class VulkanDevice;
@ -122,6 +124,8 @@ class VkShaderProgram
public:
std::unique_ptr<VulkanShader> vert;
std::unique_ptr<VulkanShader> frag;
UniformStructHolder Uniforms;
};
class VkShaderManager
@ -146,8 +150,8 @@ public:
VulkanShader* GetLightTilesShader() { return LightTiles.get(); }
private:
std::unique_ptr<VulkanShader> LoadVertShader(FString shadername, const char *vert_lump, const char *vert_lump_custom, const char *defines, const VkShaderKey& key);
std::unique_ptr<VulkanShader> LoadFragShader(FString shadername, const char *frag_lump, const char *material_lump, const char* mateffect_lump, const char *light_lump_shared, const char *lightmodel_lump, const char *defines, const VkShaderKey& key);
std::unique_ptr<VulkanShader> LoadVertShader(FString shadername, const char *vert_lump, const char *vert_lump_custom, const char *defines, const VkShaderKey& key, const UserUniforms *uniforms);
std::unique_ptr<VulkanShader> LoadFragShader(FString shadername, const char *frag_lump, const char *material_lump, const char* mateffect_lump, const char *light_lump_shared, const char *lightmodel_lump, const char *defines, const VkShaderKey& key, const UserUniforms *uniforms);
ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system);

View file

@ -330,7 +330,7 @@ void VkRenderState::ApplyRenderPass(int dt)
if (changingPipeline)
{
mCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mPassSetup->GetPipeline(pipelineKey));
mCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mPassSetup->GetPipeline(pipelineKey, mUniforms));
mPipelineKey = pipelineKey;
}
}
@ -444,7 +444,12 @@ void VkRenderState::ApplyPushConstants()
mPushConstants.uBoneIndexBase = mBoneIndexBase;
mPushConstants.uFogballIndex = mFogballIndex >= 0 ? (mFogballIndex % MAX_FOGBALL_DATA) : -1;
mCommandBuffer->pushConstants(fb->GetRenderPassManager()->GetPipelineLayout(mPipelineKey.ShaderKey.UseLevelMesh), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, (uint32_t)sizeof(PushConstants), &mPushConstants);
mCommandBuffer->pushConstants(fb->GetRenderPassManager()->GetPipelineLayout(mPipelineKey.ShaderKey.UseLevelMesh, mUniforms.sz), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, (uint32_t)sizeof(PushConstants), &mPushConstants);
if(mUniforms.sz > 0)
{
mCommandBuffer->pushConstants(fb->GetRenderPassManager()->GetPipelineLayout(mPipelineKey.ShaderKey.UseLevelMesh, mUniforms.sz), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, (uint32_t)sizeof(PushConstants), (uint32_t)mUniforms.sz, mUniforms.addr);
}
}
void VkRenderState::ApplyMatrices()
@ -510,7 +515,7 @@ void VkRenderState::ApplyBufferSets()
if (mViewpointOffset != mLastViewpointOffset || matrixOffset != mLastMatricesOffset || surfaceUniformsOffset != mLastSurfaceUniformsOffset || lightsOffset != mLastLightsOffset || fogballsOffset != mLastFogballsOffset)
{
auto descriptors = fb->GetDescriptorSetManager();
VulkanPipelineLayout* layout = fb->GetRenderPassManager()->GetPipelineLayout(mPipelineKey.ShaderKey.UseLevelMesh);
VulkanPipelineLayout* layout = fb->GetRenderPassManager()->GetPipelineLayout(mPipelineKey.ShaderKey.UseLevelMesh, mUniforms.sz);
uint32_t offsets[5] = { mViewpointOffset, matrixOffset, surfaceUniformsOffset, lightsOffset, fogballsOffset };
mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptors->GetFixedSet());
@ -1092,14 +1097,14 @@ void VkRenderState::ApplyLevelMeshPipeline(VulkanCommandBuffer* cmdbuffer, VkPip
pushConstants.uBoneIndexBase = -1;
pushConstants.uFogballIndex = -1;
VulkanPipelineLayout* layout = fb->GetRenderPassManager()->GetPipelineLayout(pipelineKey.ShaderKey.UseLevelMesh);
VulkanPipelineLayout* layout = fb->GetRenderPassManager()->GetPipelineLayout(pipelineKey.ShaderKey.UseLevelMesh, mUniforms.sz);
uint32_t viewpointOffset = mViewpointOffset;
uint32_t matrixOffset = mRSBuffers->MatrixBuffer->Offset();
uint32_t fogballsOffset = 0;
uint32_t offsets[] = { viewpointOffset, matrixOffset, fogballsOffset };
auto descriptors = fb->GetDescriptorSetManager();
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mPassSetup->GetPipeline(pipelineKey));
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mPassSetup->GetPipeline(pipelineKey, mUniforms));
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptors->GetFixedSet());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 1, descriptors->GetLevelMeshSet(), 3, offsets);
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 2, descriptors->GetBindlessSet());

View file

@ -101,6 +101,7 @@ protected:
bool mDepthClamp = true;
VulkanCommandBuffer *mCommandBuffer = nullptr;
VkPipelineKey mPipelineKey = {};
UniformStructHolder mUniforms;
VkRenderPassSetup* mPassSetup = nullptr;
int mClearTargets = 0;
bool mNeedApply = true;

View file

@ -114,8 +114,7 @@ struct UserShaderDesc
bool disablealphatest = false;
uint8_t shaderFlags = 0;
TMap<FString, UserUniformValue> Uniforms;
UserUniforms UniformData;
UserUniforms Uniforms;
};
extern TArray<UserShaderDesc> usershaders;