diff --git a/source/graphics/Material.cpp b/source/graphics/Material.cpp index fa8d4444d6..40f6f025d8 100644 --- a/source/graphics/Material.cpp +++ b/source/graphics/Material.cpp @@ -35,11 +35,13 @@ void CMaterial::SetShaderEffect(const CStr& effect) void CMaterial::AddShaderDefine(const char* key, const char* value) { m_ShaderDefines.Add(key, value); + m_CombinedShaderDefines.clear(); } void CMaterial::AddConditionalDefine(const char* defname, const char* defvalue, int type, std::vector &args) { m_ConditionalDefines.Add(defname, defvalue, type, args); + m_CombinedShaderDefines.clear(); } void CMaterial::AddStaticUniform(const char* key, const CVector4D& value) @@ -57,4 +59,31 @@ void CMaterial::AddSampler(const TextureSampler& texture) void CMaterial::AddRenderQuery(const char* key) { m_RenderQueries.Add(key); -} \ No newline at end of file +} + +// Set up m_CombinedShaderDefines so that index i contains m_ShaderDefines, plus +// the extra defines from m_ConditionalDefines[j] for all j where bit j is set in i. +// This lets GetShaderDefines() cheaply return the defines for any combination of conditions. +// +// (This might scale badly if we had a large number of conditional defines per material, +// but currently we don't expect to have many.) +void CMaterial::RecomputeCombinedShaderDefines() +{ + m_CombinedShaderDefines.clear(); + int size = m_ConditionalDefines.GetSize(); + + // Loop over all 2^n combinations of flags + for (int i = 0; i < (1 << size); i++) + { + CShaderDefines defs = m_ShaderDefines; + for (int j = 0; j < size; j++) + { + if (i & (1 << j)) + { + CShaderConditionalDefines::CondDefine& def = m_ConditionalDefines.GetItem(j); + defs.Add(def.m_DefName.c_str(), def.m_DefValue.c_str()); + } + } + m_CombinedShaderDefines.push_back(defs); + } +} diff --git a/source/graphics/Material.h b/source/graphics/Material.h index eebe5d2dd2..a144a24c5d 100644 --- a/source/graphics/Material.h +++ b/source/graphics/Material.h @@ -53,7 +53,11 @@ public: CStrIntern GetShaderEffect() const { return m_ShaderEffect; } void AddShaderDefine(const char* key, const char* value); - const CShaderDefines& GetShaderDefines() const { return m_ShaderDefines; } + + // conditionFlags is a bitmask representing which indexes of the + // GetConditionalDefines() list are currently matching. + // Use 0 if you don't care about conditional defines. + const CShaderDefines& GetShaderDefines(uint32_t conditionFlags) const { return m_CombinedShaderDefines.at(conditionFlags); } void AddConditionalDefine(const char* defname, const char* defvalue, int type, std::vector &args); const CShaderConditionalDefines& GetConditionalDefines() const { return m_ConditionalDefines; } @@ -67,6 +71,9 @@ public: void AddRenderQuery(const char* key); const CShaderRenderQueries& GetRenderQueries() const { return m_RenderQueries; } + // Must be called after all AddShaderDefine and AddConditionalDefine + void RecomputeCombinedShaderDefines(); + private: // This pointer is kept to make it easier for the fixed pipeline to @@ -78,6 +85,7 @@ private: CStrIntern m_ShaderEffect; CShaderDefines m_ShaderDefines; CShaderConditionalDefines m_ConditionalDefines; + std::vector m_CombinedShaderDefines; CShaderUniforms m_StaticUniforms; CShaderRenderQueries m_RenderQueries; diff --git a/source/graphics/MaterialManager.cpp b/source/graphics/MaterialManager.cpp index 5cebd6ce5c..68b6923388 100644 --- a/source/graphics/MaterialManager.cpp +++ b/source/graphics/MaterialManager.cpp @@ -182,6 +182,8 @@ CMaterial CMaterialManager::LoadMaterial(const VfsPath& pathname) } } + material.RecomputeCombinedShaderDefines(); + m_Materials[pathname] = material; return material; } diff --git a/source/renderer/DecalRData.cpp b/source/renderer/DecalRData.cpp index 87202b3573..ead1f5e61f 100644 --- a/source/renderer/DecalRData.cpp +++ b/source/renderer/DecalRData.cpp @@ -94,7 +94,7 @@ void CDecalRData::RenderDecals(std::vector& decals, const CShaderD if (!isDummyShader) { techBase = g_Renderer.GetShaderManager().LoadEffect( - material.GetShaderEffect(), contextDecal, material.GetShaderDefines()); + material.GetShaderEffect(), contextDecal, material.GetShaderDefines(0)); if (!techBase) { diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp index 8741e1eb44..e705a7503f 100644 --- a/source/renderer/ModelRenderer.cpp +++ b/source/renderer/ModelRenderer.cpp @@ -426,10 +426,10 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade for (size_t i = 0; i < m->submissions.size(); ++i) { CModel* model = m->submissions[i]; - - CShaderDefines defs = model->GetMaterial().GetShaderDefines(); + + uint32_t condFlags = 0; + CShaderConditionalDefines condefs = model->GetMaterial().GetConditionalDefines(); - for (size_t j = 0; j < condefs.GetSize(); ++j) { CShaderConditionalDefines::CondDefine &item = condefs.GetItem(j); @@ -445,13 +445,14 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade float dmax = item.m_CondArgs[1]; if ((dmin < 0 || dist >= dmin) && (dmax < 0 || dist < dmax)) - defs.Add(item.m_DefName.c_str(), item.m_DefValue.c_str()); + condFlags |= (1 << j); break; } } } + CShaderDefines defs = model->GetMaterial().GetShaderDefines(condFlags); SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defs); std::vector& bucketItems = materialBuckets[key]; bucketItems.push_back(model); diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index 6ddd0bfd9c..af376dab15 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -770,7 +770,7 @@ void CPatchRData::RenderBases(const std::vector& patches, const CS } techBase = g_Renderer.GetShaderManager().LoadEffect(itt->first->GetMaterial().GetShaderEffect(), - context, itt->first->GetMaterial().GetShaderDefines()); + context, itt->first->GetMaterial().GetShaderDefines(0)); numPasses = techBase->GetNumPasses(); } @@ -996,7 +996,7 @@ void CPatchRData::RenderBlends(const std::vector& patches, const C if (!isDummyShader) { - techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines()); + techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines(0)); numPasses = techBase->GetNumPasses(); }