Removes smart pointers from ModelRenderer

We don't need to use pointers because we know lifetimes of all
renderers. And its list statically defined. A new renderer should follow
DOD and use a separate submission list with a separate inplace renderer.
This commit is contained in:
Vladislav Belov 2026-08-04 23:06:10 +02:00
parent fa90ddc8b6
commit 4a7b7e7df1
No known key found for this signature in database
GPG key ID: 353545E45DB9CCB3
6 changed files with 78 additions and 63 deletions

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
@ -33,7 +33,7 @@ namespace Renderer::Backend { class IDeviceCommandContext; }
* This computes and binds per-vertex data; the modifier is responsible
* for setting any shader uniforms etc.
*/
class CPUSkinnedModelVertexRenderer : public ModelVertexRenderer
class CPUSkinnedModelVertexRenderer final : public ModelVertexRenderer
{
public:
CPUSkinnedModelVertexRenderer();

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
@ -34,7 +34,7 @@ namespace Renderer::Backend { class IShaderProgram; }
* This computes and binds per-vertex data; the modifier is responsible
* for setting any shader uniforms etc.
*/
class GPUSkinnedModelModelRenderer : public ModelVertexRenderer
class GPUSkinnedModelModelRenderer final : public ModelVertexRenderer
{
public:
GPUSkinnedModelModelRenderer();

View file

@ -34,7 +34,7 @@ struct InstancingModelRendererInternals;
* This computes and binds per-vertex data; the modifier is responsible
* for setting any shader uniforms etc (including the instancing transform).
*/
class InstancingModelRenderer : public ModelVertexRenderer
class InstancingModelRenderer final : public ModelVertexRenderer
{
public:
InstancingModelRenderer();

View file

@ -306,7 +306,7 @@ struct SMRCompareTechBucket
void ModelRenderer::Render(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
ModelVertexRenderer& modelVertexRenderer, const RenderModifierPtr& modifier, const CShaderDefines& context,
ModelVertexRenderer& modelVertexRenderer, RenderModifier& modifier, const CShaderDefines& context,
int cullGroup, int flags, const ERenderMode renderMode, std::span<CModel*> submissions)
{
if (submissions.empty())
@ -573,7 +573,7 @@ void ModelRenderer::Render(
Renderer::Backend::IShaderProgram* shader = currentTech->GetShader(pass);
modifier->BeginPass(deviceCommandContext, shader);
modifier.BeginPass(deviceCommandContext, shader);
// TODO: Use a more generic approach to handle bound queries.
bool boundTime = false;
@ -703,7 +703,7 @@ void ModelRenderer::Render(
}
}
modifier->PrepareModel(deviceCommandContext, model);
modifier.PrepareModel(deviceCommandContext, model);
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
ENSURE(rdata->GetKey() == &modelVertexRenderer);

View file

@ -40,14 +40,8 @@ namespace Renderer::Backend { class IDeviceCommandContext; }
struct SColor4ub;
template <typename T> class VertexArrayIterator;
class RenderModifier;
typedef std::shared_ptr<RenderModifier> RenderModifierPtr;
class LitRenderModifier;
typedef std::shared_ptr<LitRenderModifier> LitRenderModifierPtr;
class ModelVertexRenderer;
typedef std::shared_ptr<ModelVertexRenderer> ModelVertexRendererPtr;
class RenderModifier;
/**
* Class CModelRData: Render data that is maintained per CModel.
@ -108,7 +102,7 @@ public:
*/
void Render(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
ModelVertexRenderer& modelVertexRenderer, const RenderModifierPtr& modifier, const CShaderDefines& context,
ModelVertexRenderer& modelVertexRenderer, RenderModifier& modifier, const CShaderDefines& context,
int cullGroup, int flags, const ERenderMode renderMode, std::span<CModel*> submissions);
/**

View file

@ -68,6 +68,7 @@
#include <algorithm>
#include <cmath>
#include <optional>
struct SScreenRect
{
@ -115,7 +116,7 @@ public:
SilhouetteRenderer silhouetteRenderer;
/// Various model renderers
// Various model renderers
struct Models
{
// Submitted models are split on two axes:
@ -127,10 +128,12 @@ public:
struct Submissions
{
ModelVertexRenderer* modelVertexRenderer{nullptr};
std::vector<CModel*> submissions[CSceneRenderer::CULL_MAX];
};
// Unskinned submissions should be prepared and rendered with
// VertexInstancingShader. Skinned - with Vertex*SkinningShader
// depending on whether GPU skinning is enabled.
Submissions OpaqueSkinned;
Submissions OpaqueUnskinned;
Submissions TransparentSkinned;
@ -138,11 +141,15 @@ public:
ModelRenderer modelRenderer;
ModelVertexRendererPtr VertexInstancingShader;
ModelVertexRendererPtr VertexCPUSkinningShader;
ModelVertexRendererPtr VertexGPUSkinningShader;
InstancingModelRenderer VertexInstancingShader;
CPUSkinnedModelVertexRenderer VertexCPUSkinningShader;
// We can't create GPU skinning renderer for renderer devices without
// its support.
std::optional<GPUSkinnedModelModelRenderer> VertexGPUSkinningShader;
LitRenderModifierPtr ModShader;
ShaderRenderModifier ModShader;
bool GPUSkinningEnabled{false};
} Model;
CShaderDefines globalContext;
@ -157,12 +164,21 @@ public:
{
PROFILE3("upload models");
ModelVertexRenderer& modelVertexSkinningRenderer{
Model.GPUSkinningEnabled
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
{
Model.OpaqueSkinned.modelVertexRenderer->UploadModelsData(deviceCommandContext, Model.OpaqueSkinned.submissions[cullGroup]);
Model.TransparentSkinned.modelVertexRenderer->UploadModelsData(deviceCommandContext, Model.TransparentSkinned.submissions[cullGroup]);
Model.OpaqueUnskinned.modelVertexRenderer->UploadModelsData(deviceCommandContext, Model.OpaqueUnskinned.submissions[cullGroup]);
Model.TransparentUnskinned.modelVertexRenderer->UploadModelsData(deviceCommandContext, Model.TransparentUnskinned.submissions[cullGroup]);
modelVertexSkinningRenderer.UploadModelsData(deviceCommandContext, Model.OpaqueSkinned.submissions[cullGroup]);
modelVertexSkinningRenderer.UploadModelsData(deviceCommandContext, Model.TransparentSkinned.submissions[cullGroup]);
}
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
{
Model.VertexInstancingShader.UploadModelsData(deviceCommandContext, Model.OpaqueUnskinned.submissions[cullGroup]);
Model.VertexInstancingShader.UploadModelsData(deviceCommandContext, Model.TransparentUnskinned.submissions[cullGroup]);
}
}
@ -178,12 +194,21 @@ public:
{
PROFILE3("prepare models");
ModelVertexRenderer& modelVertexSkinningRenderer{
Model.GPUSkinningEnabled
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
{
PrepareModels(deviceCommandContext, *Model.OpaqueSkinned.modelVertexRenderer, Model.OpaqueSkinned.submissions[cullGroup]);
PrepareModels(deviceCommandContext, *Model.TransparentSkinned.modelVertexRenderer, Model.TransparentSkinned.submissions[cullGroup]);
PrepareModels(deviceCommandContext, *Model.OpaqueUnskinned.modelVertexRenderer, Model.OpaqueUnskinned.submissions[cullGroup]);
PrepareModels(deviceCommandContext, *Model.TransparentUnskinned.modelVertexRenderer, Model.TransparentUnskinned.submissions[cullGroup]);
PrepareModels(deviceCommandContext, modelVertexSkinningRenderer, Model.OpaqueSkinned.submissions[cullGroup]);
PrepareModels(deviceCommandContext, modelVertexSkinningRenderer, Model.TransparentSkinned.submissions[cullGroup]);
}
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
{
PrepareModels(deviceCommandContext, Model.VertexInstancingShader, Model.OpaqueUnskinned.submissions[cullGroup]);
PrepareModels(deviceCommandContext, Model.VertexInstancingShader, Model.TransparentUnskinned.submissions[cullGroup]);
}
}
@ -242,14 +267,19 @@ public:
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, int flags, const ERenderMode renderMode)
{
ModelVertexRenderer& modelVertexSkinningRenderer{
Model.GPUSkinningEnabled
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
CShaderDefines contextSkinned = context;
if (g_RenderingOptions.GetGPUSkinning())
if (Model.GPUSkinningEnabled)
contextSkinned.Add(str_USE_INSTANCING, str_1);
Model.modelRenderer.Render(deviceCommandContext, *Model.OpaqueSkinned.modelVertexRenderer, Model.ModShader, contextSkinned, cullGroup, flags, renderMode, Model.OpaqueSkinned.submissions[cullGroup]);
Model.modelRenderer.Render(deviceCommandContext, modelVertexSkinningRenderer, Model.ModShader, contextSkinned, cullGroup, flags, renderMode, Model.OpaqueSkinned.submissions[cullGroup]);
CShaderDefines contextUnskinned = context;
contextUnskinned.Add(str_USE_INSTANCING, str_1);
Model.modelRenderer.Render(deviceCommandContext, *Model.OpaqueUnskinned.modelVertexRenderer, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode, Model.OpaqueUnskinned.submissions[cullGroup]);
Model.modelRenderer.Render(deviceCommandContext, Model.VertexInstancingShader, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode, Model.OpaqueUnskinned.submissions[cullGroup]);
}
/**
@ -259,14 +289,19 @@ public:
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, int flags, const ERenderMode renderMode)
{
ModelVertexRenderer& modelVertexSkinningRenderer{
Model.GPUSkinningEnabled
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
CShaderDefines contextSkinned = context;
if (g_RenderingOptions.GetGPUSkinning())
if (Model.GPUSkinningEnabled)
contextSkinned.Add(str_USE_INSTANCING, str_1);
Model.modelRenderer.Render(deviceCommandContext, *Model.TransparentSkinned.modelVertexRenderer, Model.ModShader, contextSkinned, cullGroup, flags, renderMode, Model.TransparentSkinned.submissions[cullGroup]);
Model.modelRenderer.Render(deviceCommandContext, modelVertexSkinningRenderer, Model.ModShader, contextSkinned, cullGroup, flags, renderMode, Model.TransparentSkinned.submissions[cullGroup]);
CShaderDefines contextUnskinned = context;
contextUnskinned.Add(str_USE_INSTANCING, str_1);
Model.modelRenderer.Render(deviceCommandContext, *Model.TransparentUnskinned.modelVertexRenderer, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode, Model.TransparentUnskinned.submissions[cullGroup]);
Model.modelRenderer.Render(deviceCommandContext, Model.VertexInstancingShader, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode, Model.TransparentUnskinned.submissions[cullGroup]);
}
};
@ -319,26 +354,12 @@ void CSceneRenderer::ReloadShaders([[maybe_unused]] Renderer::Backend::IDevice*
m->globalContext.Add(str_RENDER_DEBUG_MODE,
RenderDebugModeEnum::ToString(g_RenderingOptions.GetRenderDebugMode()));
m->Model.ModShader = LitRenderModifierPtr(new ShaderRenderModifier());
m->Model.VertexCPUSkinningShader = ModelVertexRendererPtr(new CPUSkinnedModelVertexRenderer());
m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer());
if (g_RenderingOptions.GetGPUSkinning())
m->Model.GPUSkinningEnabled = g_RenderingOptions.GetGPUSkinning();
if (m->Model.GPUSkinningEnabled)
{
m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new GPUSkinnedModelModelRenderer());
m->Model.OpaqueSkinned.modelVertexRenderer = m->Model.VertexGPUSkinningShader.get();
m->Model.TransparentSkinned.modelVertexRenderer = m->Model.VertexGPUSkinningShader.get();
if (!m->Model.VertexGPUSkinningShader.has_value())
m->Model.VertexGPUSkinningShader.emplace();
}
else
{
m->Model.VertexGPUSkinningShader.reset();
m->Model.OpaqueSkinned.modelVertexRenderer = m->Model.VertexCPUSkinningShader.get();
m->Model.TransparentSkinned.modelVertexRenderer = m->Model.VertexCPUSkinningShader.get();
}
m->Model.OpaqueUnskinned.modelVertexRenderer = m->Model.VertexInstancingShader.get();
m->Model.TransparentUnskinned.modelVertexRenderer = m->Model.VertexInstancingShader.get();
}
void CSceneRenderer::Initialize()
@ -362,8 +383,8 @@ void CSceneRenderer::Resize(int /*width*/, int /*height*/)
void CSceneRenderer::BeginFrame()
{
// choose model renderers for this frame
m->Model.ModShader->SetShadowMap(&m->shadow);
m->Model.ModShader->SetLightEnv(m_LightEnv);
m->Model.ModShader.SetShadowMap(&m->shadow);
m->Model.ModShader.SetLightEnv(m_LightEnv);
}
void CSceneRenderer::SetSimulation(CSimulation2* simulation)
@ -1095,23 +1116,23 @@ void CSceneRenderer::SubmitNonRecursive(CModel* model)
const bool requiresSkinning{model->GetModelDef()->GetNumBones() != 0};
ModelVertexRenderer& modelVertexSkinningRenderer{
g_RenderingOptions.GetGPUSkinning() ? *m->Model.VertexGPUSkinningShader : *m->Model.VertexCPUSkinningShader};
ModelVertexRenderer& modelVertexRenderer{
requiresSkinning ? modelVertexSkinningRenderer : *m->Model.VertexInstancingShader};
m->Model.GPUSkinningEnabled
? static_cast<ModelVertexRenderer&>(*m->Model.VertexGPUSkinningShader)
: static_cast<ModelVertexRenderer&>(m->Model.VertexCPUSkinningShader)};
if (model->GetMaterial().UsesAlphaBlending())
{
if (requiresSkinning)
m->Submit(m_CurrentCullGroup, modelVertexRenderer, m->Model.TransparentSkinned, model);
m->Submit(m_CurrentCullGroup, modelVertexSkinningRenderer, m->Model.TransparentSkinned, model);
else
m->Submit(m_CurrentCullGroup, modelVertexRenderer, m->Model.TransparentUnskinned, model);
m->Submit(m_CurrentCullGroup, m->Model.VertexInstancingShader, m->Model.TransparentUnskinned, model);
}
else
{
if (requiresSkinning)
m->Submit(m_CurrentCullGroup, modelVertexRenderer, m->Model.OpaqueSkinned, model);
m->Submit(m_CurrentCullGroup, modelVertexSkinningRenderer, m->Model.OpaqueSkinned, model);
else
m->Submit(m_CurrentCullGroup, modelVertexRenderer, m->Model.OpaqueUnskinned, model);
m->Submit(m_CurrentCullGroup, m->Model.VertexInstancingShader, m->Model.OpaqueUnskinned, model);
}
}