Moves all rendering to material passes

Now we should use material passes instead of context and defines to
select a needed shader. It allows us to reduce the number of
shader combinations we use and reduce memory consumption.
This commit is contained in:
Vladislav Belov 2026-05-10 23:57:20 +02:00
parent aced56c336
commit 18a16aa135
No known key found for this signature in database
GPG key ID: 353545E45DB9CCB3
11 changed files with 193 additions and 132 deletions

View file

@ -49,14 +49,6 @@ X(DISABLE_RECEIVE_SHADOWS)
X(IGNORE_LOS)
X(MINIMAP_BASE)
X(MINIMAP_POINT)
X(MODE_SHADOWCAST)
X(MODE_SILHOUETTEDISPLAY)
X(MODE_SILHOUETTEOCCLUDER)
X(MODE_WIREFRAME)
X(MODE_WIREFRAME_SOLID)
X(PASS_REFLECTIONS)
X(PASS_REFRACTIONS)
X(PASS_SHADOWS)
X(RENDER_DEBUG_MODE)
X(RENDER_DEBUG_MODE_AO)
X(RENDER_DEBUG_MODE_ALPHA)
@ -140,6 +132,7 @@ X(particle_multiply)
X(particle_overlay)
X(particle_solid)
X(particle_subtract)
X(particle_wireframe)
X(playerColor)
X(projInvTransform)
X(qualityLevel)
@ -169,11 +162,18 @@ X(spaceTransform)
X(sunColor)
X(sunDir)
X(terrain_base)
X(terrain_base_reflections)
X(terrain_base_wireframe)
X(terrain_blend)
X(terrain_blend_reflections)
X(terrain_blend_wireframe)
X(terrain_decal)
X(terrain_decal_reflections)
X(terrain_decal_wireframe)
X(terrain_shadow_caster)
X(terrain_silhouette_occluder)
X(terrain_solid)
X(terrain_wireframe)
X(tex)
X(texSize)
X(textureTransform)
@ -187,6 +187,7 @@ X(vertexCount)
X(viewInvTransform)
X(water_high)
X(water_simple)
X(water_simple_wireframe)
X(water_waves)
X(waterEffectsTex)
X(waterTex)

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -138,7 +138,8 @@ void CDecalRData::Update(CSimulation2* simulation)
void CDecalRData::RenderDecals(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,
const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow)
const std::vector<CDecalRData*>& decals, const CShaderDefines& context,
ShadowMap* shadow, const CMaterial::Pass materialPass)
{
PROFILE3("render terrain decals");
GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain decals");
@ -156,7 +157,7 @@ void CDecalRData::RenderDecals(
{
CMaterial& material = decal->m_Decal->m_Decal.m_Material;
if (material.GetShaderEffect().empty())
if (material.GetShaderEffect(materialPass).empty())
{
LOGERROR("Terrain renderer failed to load shader effect.\n");
continue;
@ -167,7 +168,7 @@ void CDecalRData::RenderDecals(
SDecalBatch batch;
batch.decal = decal;
batch.shaderEffect = material.GetShaderEffect();
batch.shaderEffect = material.GetShaderEffect(materialPass);
batch.shaderDefines = material.GetShaderDefines();
batch.vertices = decal->m_VBDecals.Get();
batch.indices = decal->m_VBDecalsIndices.Get();
@ -193,8 +194,16 @@ void CDecalRData::RenderDecals(
CShaderDefines defines = contextDecal;
defines.SetMany(itTechBegin->shaderDefines);
// TODO: move enabling blend to XML.
CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
itTechBegin->shaderEffect == str_terrain_base ? str_terrain_decal : itTechBegin->shaderEffect, defines);
CShaderTechniquePtr techBase{g_Renderer.GetShaderManager().LoadEffect([](const CStrIntern shaderEffect)
{
if (shaderEffect == str_terrain_base)
return str_terrain_decal;
if (shaderEffect == str_terrain_base_reflections)
return str_terrain_decal_reflections;
if (shaderEffect == str_terrain_base_wireframe)
return str_terrain_decal_wireframe;
return shaderEffect;
}(itTechBegin->shaderEffect), defines)};
if (!techBase)
{
LOGERROR("Terrain renderer failed to load shader effect (%s)\n",

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,6 +18,7 @@
#ifndef INCLUDED_DECALRDATA
#define INCLUDED_DECALRDATA
#include "graphics/Material.h"
#include "graphics/RenderableObject.h"
#include "lib/code_annotation.h"
#include "maths/Vector2D.h"
@ -45,7 +46,8 @@ public:
static void RenderDecals(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,
const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow);
const std::vector<CDecalRData*>& decals, const CShaderDefines& context,
ShadowMap* shadow, const CMaterial::Pass materialPass);
CModelDecal* GetDecal() { return m_Decal; }

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -64,8 +64,43 @@
#include <unordered_map>
#include <utility>
///////////////////////////////////////////////////////////////////////////////////////////////
// ModelRenderer implementation
namespace
{
CMaterial::Pass GetMaterialPassFromCullGroup(const int cullGroup, const ERenderMode renderMode)
{
switch (renderMode)
{
case ERenderMode::WIREFRAME:
return CMaterial::Pass::WIREFRAME;
case ERenderMode::EDGED_FACES:
return CMaterial::Pass::WIREFRAME_SOLID;
case ERenderMode::SOLID:
break;
}
switch (cullGroup)
{
case CSceneRenderer::CULL_SHADOWS_CASCADE_0: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_1: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_2: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_3:
return CMaterial::Pass::SHADOW_CASTER;
case CSceneRenderer::CULL_SILHOUETTE_OCCLUDER:
return CMaterial::Pass::SILHOUETTE_OCCLUDER;
case CSceneRenderer::CULL_SILHOUETTE_CASTER:
return CMaterial::Pass::SILHOUETTE_CASTER;
case CSceneRenderer::CULL_DEFAULT:
return CMaterial::Pass::MAIN;
default:
break;
}
return CMaterial::Pass::MAIN;
}
}
void ModelRenderer::Init()
{
@ -380,7 +415,8 @@ struct SMRCompareTechBucket
void ShaderModelRenderer::Render(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags)
const RenderModifierPtr& modifier, const CShaderDefines& context,
int cullGroup, int flags, const ERenderMode renderMode)
{
if (m->submissions[cullGroup].empty())
return;
@ -388,6 +424,8 @@ void ShaderModelRenderer::Render(
CMatrix3D worldToCam;
g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse(worldToCam);
const CMaterial::Pass materialPass{GetMaterialPassFromCullGroup(cullGroup, renderMode)};
/*
* Rendering approach:
*
@ -462,8 +500,10 @@ void ShaderModelRenderer::Render(
for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
{
CModel* model = m->submissions[cullGroup][i];
const CShaderDefines& defines = model->GetMaterial().GetShaderDefines();
SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defines);
const CMaterial material{model->GetMaterial()};
const CShaderDefines& defines{material.GetShaderDefines()};
const CStrIntern shaderEffect{material.GetShaderEffect(materialPass)};
SMRMaterialBucketKey key(shaderEffect, defines);
MaterialBuckets_t::iterator it = materialBuckets.find(key);
if (it == materialBuckets.end())

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -26,6 +26,7 @@
#include "graphics/MeshManager.h"
#include "graphics/RenderableObject.h"
#include "renderer/SceneRenderer.h"
#include "lib/types.h"
#include <memory>
@ -173,7 +174,8 @@ public:
*/
virtual void Render(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags) = 0;
const RenderModifierPtr& modifier, const CShaderDefines& context,
int cullGroup, int flags, const ERenderMode renderMode) = 0;
/**
* CopyPositionAndNormals: Copy unanimated object-space vertices and
@ -282,7 +284,8 @@ public:
void EndFrame() override;
void Render(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags) override;
const RenderModifierPtr& modifier, const CShaderDefines& context,
int cullGroup, int flags, const ERenderMode renderMode) override;
private:
struct ShaderModelRendererInternals;

View file

@ -883,7 +883,8 @@ using ShaderTechniqueBatches = PooledBatchMap<std::pair<CStrIntern, CShaderDefin
void CPatchRData::RenderBases(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,
const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
ShadowMap* shadow, const CMaterial::Pass materialPass)
{
PROFILE3("render terrain bases");
GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain bases");
@ -902,7 +903,7 @@ void CPatchRData::RenderBases(
{
SSplat& splat = patch->m_Splats[j];
const CMaterial& material = splat.m_Texture->GetMaterial();
if (material.GetShaderEffect().empty())
if (material.GetShaderEffect(materialPass).empty())
{
LOGERROR("Terrain renderer failed to load shader effect.\n");
continue;
@ -911,7 +912,9 @@ void CPatchRData::RenderBases(
BatchElements& batch = PooledPairGet(
PooledMapGet(
PooledMapGet(
PooledMapGet(batches, std::make_pair(material.GetShaderEffect(), material.GetShaderDefines()), scopedLinearAllocator),
PooledMapGet(
batches, std::make_pair(material.GetShaderEffect(materialPass), material.GetShaderDefines()),
scopedLinearAllocator),
splat.m_Texture, scopedLinearAllocator
),
patch->m_VBBase->m_Owner, scopedLinearAllocator
@ -1043,7 +1046,8 @@ struct SBlendStackItem
void CPatchRData::RenderBlends(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,
const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
ShadowMap* shadow, const CMaterial::Pass materialPass)
{
PROFILE3("render terrain blends");
GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain blends");
@ -1130,11 +1134,19 @@ void CPatchRData::RenderBlends(
CShaderDefines defines = contextBlend;
defines.SetMany(bestTex->GetMaterial().GetShaderDefines());
// TODO: move enabling blend to XML.
const CStrIntern shaderEffect = bestTex->GetMaterial().GetShaderEffect();
if (shaderEffect != str_terrain_base)
const CStrIntern shaderEffect = bestTex->GetMaterial().GetShaderEffect(materialPass);
if (shaderEffect != str_terrain_base && shaderEffect != str_terrain_base_reflections && shaderEffect != str_terrain_base_wireframe)
ONCE(LOGWARNING("Shader effect '%s' doesn't support semi-transparent terrain rendering.", shaderEffect.c_str()));
layer.m_ShaderTech = g_Renderer.GetShaderManager().LoadEffect(
shaderEffect == str_terrain_base ? str_terrain_blend : shaderEffect, defines);
layer.m_ShaderTech = g_Renderer.GetShaderManager().LoadEffect([](const CStrIntern shaderEffect)
{
if (shaderEffect == str_terrain_base)
return str_terrain_blend;
if (shaderEffect == str_terrain_base_reflections)
return str_terrain_blend_reflections;
if (shaderEffect == str_terrain_base_wireframe)
return str_terrain_blend_wireframe;
return shaderEffect;
}(shaderEffect), defines);
}
batches.push_back(layer);
}

View file

@ -18,6 +18,7 @@
#ifndef INCLUDED_PATCHRDATA
#define INCLUDED_PATCHRDATA
#include "graphics/Material.h"
#include "graphics/Patch.h"
#include "graphics/RenderableObject.h"
#include "lib/code_annotation.h"
@ -76,11 +77,13 @@ public:
static void RenderBases(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,
const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow);
const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
ShadowMap* shadow, const CMaterial::Pass materialPass);
static void RenderBlends(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,
const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow);
const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
ShadowMap* shadow, const CMaterial::Pass materialPass);
static void RenderStreams(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IVertexInputLayout* vertexInputLayout,

View file

@ -154,18 +154,18 @@ public:
*/
void CallModelRenderers(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, int flags)
const CShaderDefines& context, int cullGroup, int flags, const ERenderMode renderMode)
{
CShaderDefines contextSkinned = context;
if (g_RenderingOptions.GetGPUSkinning())
contextSkinned.Add(str_USE_INSTANCING, str_1);
Model.NormalSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags);
Model.NormalSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags, renderMode);
if (Model.NormalUnskinned != Model.NormalSkinned)
{
CShaderDefines contextUnskinned = context;
contextUnskinned.Add(str_USE_INSTANCING, str_1);
Model.NormalUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags);
Model.NormalUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode);
}
}
@ -174,18 +174,18 @@ public:
*/
void CallTranspModelRenderers(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, int flags)
const CShaderDefines& context, int cullGroup, int flags, const ERenderMode renderMode)
{
CShaderDefines contextSkinned = context;
if (g_RenderingOptions.GetGPUSkinning())
contextSkinned.Add(str_USE_INSTANCING, str_1);
Model.TranspSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags);
Model.TranspSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags, renderMode);
if (Model.TranspUnskinned != Model.TranspSkinned)
{
CShaderDefines contextUnskinned = context;
contextUnskinned.Add(str_USE_INSTANCING, str_1);
Model.TranspUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags);
Model.TranspUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode);
}
}
};
@ -294,18 +294,11 @@ void CSceneRenderer::SetSimulation(CSimulation2* simulation)
}
void CSceneRenderer::RenderShadowMap(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context)
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
PROFILE3_GPU(deviceCommandContext, "shadow map");
GPU_SCOPED_LABEL(deviceCommandContext, "Render shadow map");
CShaderDefines shadowsContext = context;
shadowsContext.Add(str_PASS_SHADOWS, str_1);
CShaderDefines contextCast = shadowsContext;
contextCast.Add(str_MODE_SHADOWCAST, str_1);
m->shadow.BeginRender(deviceCommandContext);
const int cascadeCount = m->shadow.GetCascadeCount();
@ -317,17 +310,18 @@ void CSceneRenderer::RenderShadowMap(
const int cullGroup = CULL_SHADOWS_CASCADE_0 + cascade;
{
PROFILE("render patches");
m->terrainRenderer.RenderPatches(deviceCommandContext, cullGroup, {});
m->terrainRenderer.RenderPatches(
deviceCommandContext, cullGroup, {}, CColor(0.0f, 0.0f, 0.0f, 1.0f), false);
}
{
PROFILE("render models");
m->CallModelRenderers(deviceCommandContext, contextCast, cullGroup, ModelFlag::CAST_SHADOWS);
m->CallModelRenderers(deviceCommandContext, {}, cullGroup, ModelFlag::CAST_SHADOWS, ERenderMode::SOLID);
}
{
PROFILE("render transparent models");
m->CallTranspModelRenderers(deviceCommandContext, contextCast, cullGroup, ModelFlag::CAST_SHADOWS);
m->CallTranspModelRenderers(deviceCommandContext, {}, cullGroup, ModelFlag::CAST_SHADOWS, ERenderMode::SOLID);
}
}
@ -341,23 +335,17 @@ void CSceneRenderer::RenderPatches(
PROFILE3("patches");
GPU_SCOPED_LABEL(deviceCommandContext, "Render patches");
// Switch on wireframe if we need it.
CShaderDefines localContext = context;
if (m_TerrainRenderMode == WIREFRAME)
localContext.Add(str_MODE_WIREFRAME, str_1);
// Render all the patches, including blend pass.
m->terrainRenderer.RenderTerrainShader(deviceCommandContext, localContext, cullGroup,
g_RenderingOptions.GetShadows() ? &m->shadow : nullptr);
m->terrainRenderer.RenderTerrainShader(deviceCommandContext, context, cullGroup,
g_RenderingOptions.GetShadows() ? &m->shadow : nullptr, m_TerrainRenderMode == WIREFRAME);
if (m_TerrainRenderMode == EDGED_FACES)
{
localContext.Add(str_MODE_WIREFRAME, str_1);
// Edged faces: need to make a second pass over the data.
// Render tiles edges.
m->terrainRenderer.RenderPatches(
deviceCommandContext, cullGroup, localContext, CColor(0.5f, 0.5f, 1.0f, 1.0f));
deviceCommandContext, cullGroup, context, CColor(0.5f, 0.5f, 1.0f, 1.0f), true);
// Render outline of each patch.
m->terrainRenderer.RenderOutlines(deviceCommandContext, cullGroup);
@ -373,18 +361,13 @@ void CSceneRenderer::RenderModels(
int flags = 0;
CShaderDefines localContext = context;
const ERenderMode modelRenderMode{
m_ModelRenderMode == WIREFRAME ? WIREFRAME : SOLID};
if (m_ModelRenderMode == WIREFRAME)
localContext.Add(str_MODE_WIREFRAME, str_1);
m->CallModelRenderers(deviceCommandContext, localContext, cullGroup, flags);
m->CallModelRenderers(deviceCommandContext, context, cullGroup, flags, modelRenderMode);
if (m_ModelRenderMode == EDGED_FACES)
{
localContext.Add(str_MODE_WIREFRAME_SOLID, str_1);
m->CallModelRenderers(deviceCommandContext, localContext, cullGroup, flags);
}
m->CallModelRenderers(deviceCommandContext, {}, cullGroup, flags, EDGED_FACES);
}
void CSceneRenderer::RenderTransparentModels(
@ -402,25 +385,17 @@ void CSceneRenderer::RenderTransparentModels(
CShaderDefines contextBlend = context;
contextBlend.Add(str_ALPHABLEND_PASS_BLEND, str_1);
if (m_ModelRenderMode == WIREFRAME)
{
contextOpaque.Add(str_MODE_WIREFRAME, str_1);
contextBlend.Add(str_MODE_WIREFRAME, str_1);
}
const ERenderMode modelRenderMode{
m_ModelRenderMode == WIREFRAME ? WIREFRAME : SOLID};
if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_OPAQUE)
m->CallTranspModelRenderers(deviceCommandContext, contextOpaque, cullGroup, flags);
m->CallTranspModelRenderers(deviceCommandContext, contextOpaque, cullGroup, flags, modelRenderMode);
if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_BLEND)
m->CallTranspModelRenderers(deviceCommandContext, contextBlend, cullGroup, flags);
m->CallTranspModelRenderers(deviceCommandContext, contextBlend, cullGroup, flags, modelRenderMode);
if (m_ModelRenderMode == EDGED_FACES)
{
CShaderDefines contextWireframe = contextOpaque;
contextWireframe.Add(str_MODE_WIREFRAME, str_1);
m->CallTranspModelRenderers(deviceCommandContext, contextWireframe, cullGroup, flags);
}
m->CallTranspModelRenderers(deviceCommandContext, {}, cullGroup, flags, EDGED_FACES);
}
// SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
@ -602,13 +577,10 @@ void CSceneRenderer::RenderReflections(
scissorRect.height = screenScissor.y2 - screenScissor.y1;
deviceCommandContext->SetScissors(1, &scissorRect);
CShaderDefines reflectionsContext = context;
reflectionsContext.Add(str_PASS_REFLECTIONS, str_1);
// Render terrain and models
RenderPatches(deviceCommandContext, reflectionsContext, CULL_REFLECTIONS);
RenderModels(deviceCommandContext, reflectionsContext, CULL_REFLECTIONS);
RenderTransparentModels(deviceCommandContext, reflectionsContext, CULL_REFLECTIONS, TRANSPARENT);
RenderPatches(deviceCommandContext, context, CULL_REFLECTIONS);
RenderModels(deviceCommandContext, context, CULL_REFLECTIONS);
RenderTransparentModels(deviceCommandContext, context, CULL_REFLECTIONS, TRANSPARENT);
// Particles are always oriented to face the camera in the vertex shader,
// so they don't need the inverted cull face.
@ -713,18 +685,11 @@ void CSceneRenderer::RenderRefractions(
}
void CSceneRenderer::RenderSilhouettes(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context)
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
PROFILE3("silhouettes");
GPU_SCOPED_LABEL(deviceCommandContext, "Render silhouettes");
CShaderDefines contextOccluder = context;
contextOccluder.Add(str_MODE_SILHOUETTEOCCLUDER, str_1);
CShaderDefines contextDisplay = context;
contextDisplay.Add(str_MODE_SILHOUETTEDISPLAY, str_1);
// Render silhouettes of units hidden behind terrain or occluders.
// To avoid breaking the standard rendering of alpha-blended objects, this
// has to be done in a separate pass.
@ -739,29 +704,30 @@ void CSceneRenderer::RenderSilhouettes(
{
PROFILE("render patches");
m->terrainRenderer.RenderPatches(deviceCommandContext, CULL_SILHOUETTE_OCCLUDER, {});
m->terrainRenderer.RenderPatches(
deviceCommandContext, CULL_SILHOUETTE_OCCLUDER, {}, CColor(0.0f, 0.0f, 0.0f, 1.0f), false);
}
{
PROFILE("render model occluders");
m->CallModelRenderers(deviceCommandContext, contextOccluder, CULL_SILHOUETTE_OCCLUDER, 0);
m->CallModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_OCCLUDER, 0, ERenderMode::SOLID);
}
{
PROFILE("render transparent occluders");
m->CallTranspModelRenderers(deviceCommandContext, contextOccluder, CULL_SILHOUETTE_OCCLUDER, 0);
m->CallTranspModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_OCCLUDER, 0, ERenderMode::SOLID);
}
// Since we can't sort, we'll use the stencil buffer to ensure we only draw
// a pixel once (using the color of whatever model happens to be drawn first).
{
PROFILE("render model casters");
m->CallModelRenderers(deviceCommandContext, contextDisplay, CULL_SILHOUETTE_CASTER, 0);
m->CallModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_CASTER, 0, ERenderMode::SOLID);
}
{
PROFILE("render transparent casters");
m->CallTranspModelRenderers(deviceCommandContext, contextDisplay, CULL_SILHOUETTE_CASTER, 0);
m->CallTranspModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_CASTER, 0, ERenderMode::SOLID);
}
}
@ -832,7 +798,7 @@ void CSceneRenderer::PrepareSubmissions(
if (g_RenderingOptions.GetShadows())
{
RenderShadowMap(deviceCommandContext, context);
RenderShadowMap(deviceCommandContext);
}
if (m->waterManager.m_RenderWater)
@ -1164,7 +1130,7 @@ void CSceneRenderer::RenderSceneOverlays(
if (!g_RenderingOptions.GetCutsceneMode())
{
if (g_RenderingOptions.GetSilhouettes())
RenderSilhouettes(deviceCommandContext, m->globalContext);
RenderSilhouettes(deviceCommandContext);
m->silhouetteRenderer.RenderDebugOverlays(deviceCommandContext);

View file

@ -230,8 +230,7 @@ protected:
const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode);
void RenderSilhouettes(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context);
Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
void RenderParticles(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
@ -239,8 +238,7 @@ protected:
// shadow rendering stuff
void RenderShadowMap(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context);
Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
// render water reflection and refraction textures
void RenderReflections(

View file

@ -65,6 +65,9 @@
#include <string>
#include <vector>
namespace
{
/**
* TerrainRenderer keeps track of which phase it is in, to detect
* when Submit, PrepareForRendering etc. are called in the wrong order.
@ -75,6 +78,17 @@ enum Phase
Phase_Render
};
CMaterial::Pass GetMaterialPassFromCullGroup(const int cullGroup, const bool wireframe)
{
if (wireframe)
return CMaterial::Pass::WIREFRAME;
return cullGroup == CSceneRenderer::CULL_REFLECTIONS
? CMaterial::Pass::REFLECTIONS
: CMaterial::Pass::MAIN;
}
}
/**
* Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
@ -93,7 +107,7 @@ struct TerrainRendererInternals
/// Fancy water shader
CShaderTechniquePtr fancyWaterTech;
CShaderTechniquePtr shadowCasterTech, silhouettteOccluderTech;
CShaderTechniquePtr shadowCasterTech, silhouettteOccluderTech, wireframeTech;
CShaderTechniquePtr shaderTechniqueSolid, shaderTechniqueSolidDepthTest;
@ -156,6 +170,7 @@ void TerrainRenderer::Initialize()
CShaderManager& shaderManager{g_Renderer.GetShaderManager()};
m->shadowCasterTech = shaderManager.LoadEffect(str_terrain_shadow_caster);
m->silhouettteOccluderTech = shaderManager.LoadEffect(str_terrain_silhouette_occluder);
m->wireframeTech = shaderManager.LoadEffect(str_terrain_wireframe);
}
void TerrainRenderer::SetSimulation(CSimulation2* simulation)
@ -316,7 +331,8 @@ void TerrainRenderer::PrepareShader(
void TerrainRenderer::RenderTerrainShader(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
const CShaderDefines& context, int cullGroup, ShadowMap* shadow,
const bool wireframe)
{
ENSURE(m->phase == Phase_Render);
@ -353,22 +369,25 @@ void TerrainRenderer::RenderTerrainShader(
deviceCommandContext->EndPass();
const CMaterial::Pass materialPass{GetMaterialPassFromCullGroup(cullGroup, wireframe)};
CPatchRData::RenderBases(
deviceCommandContext, m->baseVertexInputLayout, visiblePatches, context, shadow);
deviceCommandContext, m->baseVertexInputLayout, visiblePatches, context, shadow, materialPass);
// render blend passes for each patch
CPatchRData::RenderBlends(
deviceCommandContext, m->blendVertexInputLayout, visiblePatches, context, shadow);
deviceCommandContext, m->blendVertexInputLayout, visiblePatches, context, shadow, materialPass);
CDecalRData::RenderDecals(
deviceCommandContext, m->decalsVertexInputLayout, visibleDecals, context, shadow);
deviceCommandContext, m->decalsVertexInputLayout, visibleDecals, context, shadow, materialPass);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup, const CShaderDefines& defines, const CColor& color)
int cullGroup, const CShaderDefines& defines, const CColor& color,
const bool wireframe)
{
ENSURE(m->phase == Phase_Render);
@ -379,22 +398,29 @@ void TerrainRenderer::RenderPatches(
GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain patches");
CShaderTechniquePtr solidTech;
switch (cullGroup)
if (wireframe)
{
case CSceneRenderer::CULL_SHADOWS_CASCADE_0: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_1: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_2: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_3:
ENSURE(defines.GetMap().empty());
solidTech = m->shadowCasterTech;
break;
case CSceneRenderer::CULL_SILHOUETTE_OCCLUDER:
ENSURE(defines.GetMap().empty());
solidTech = m->silhouettteOccluderTech;
break;
default:
solidTech = g_Renderer.GetShaderManager().LoadEffect(str_terrain_solid, defines);
break;
solidTech = m->wireframeTech;
}
else
{
switch (cullGroup)
{
case CSceneRenderer::CULL_SHADOWS_CASCADE_0: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_1: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_2: [[fallthrough]];
case CSceneRenderer::CULL_SHADOWS_CASCADE_3:
ENSURE(defines.GetMap().empty());
solidTech = m->shadowCasterTech;
break;
case CSceneRenderer::CULL_SILHOUETTE_OCCLUDER:
ENSURE(defines.GetMap().empty());
solidTech = m->silhouettteOccluderTech;
break;
default:
solidTech = g_Renderer.GetShaderManager().LoadEffect(str_terrain_solid, defines);
break;
}
}
deviceCommandContext->SetGraphicsPipelineState(
solidTech->GetGraphicsPipelineState());

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -104,7 +104,8 @@ public:
*/
void RenderTerrainShader(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, ShadowMap* shadow);
const CShaderDefines& context, int cullGroup, ShadowMap* shadow,
const bool wireframe);
/**
* RenderPatches: Render all patches un-textured as polygons.
@ -118,7 +119,7 @@ public:
void RenderPatches(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup, const CShaderDefines& defines,
const CColor& color = CColor(0.0f, 0.0f, 0.0f, 1.0f));
const CColor& color, const bool wireframe);
/**
* RenderOutlines: Render the outline of patches as lines.