SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
RenderCommandQueue.cpp
Go to the documentation of this file.
7#include <Core/Logger.hpp>
8
9namespace Sleak {
10 namespace RenderEngine {
11 RenderCommandQueue* RenderCommandQueue::Instance = nullptr;
12
13
15 List<RefPtr<BufferBase>> constantBuffers,
16 uint32_t vertexCount,
17 uint32_t startVertexLocation) {
18 auto command = RefPtr<RenderCommandBase>(new DrawCommand(vertexBuffer,constantBuffers,vertexCount, startVertexLocation));
19 commands.push(std::move(command));
20 }
21
23 RefPtr<BufferBase> vertexBuffer, RefPtr<BufferBase> indexBuffer,
24 List<RefPtr<BufferBase>> constantBuffers, uint32_t indexCount,
25 uint32_t startIndexLocation, int32_t baseVertexLocation,
26 bool castsShadow) {
28 vertexBuffer, indexBuffer, constantBuffers, indexCount,
29 startIndexLocation, baseVertexLocation));
30 command->SetCastsShadow(castsShadow);
31 commands.push(std::move(command));
32 }
33
35 auto command = RefPtr<RenderCommandBase>(new BindConstantBufferCommand(buffer, slot));
36 commands.push(std::move(command));
37 }
38
40 auto command = RefPtr<RenderCommandBase>(new UpdateConstantBufferCommand(buffer, Data, Size));
41 commands.push(std::move(command));
42 }
43
45 auto command = RefPtr<RenderCommandBase>(new BindMaterialCommand(material));
46 commands.push(std::move(command));
47 }
48
50 auto command = RefPtr<RenderCommandBase>(new SetRenderModeCommand(mode));
51 commands.push(command);
52 }
53
55 auto command = RefPtr<RenderCommandBase>(new SetRenderFaceCommand(face));
56 commands.push(command);
57 }
58
60 auto command =
62 commands.push(std::move(command));
63
64 }
65
67
70
71 // Cache draw commands for shadow pass replay next frame,
72 // pairing each with the last-bound slot-0 (transform) buffer.
73 m_retiredShadowDraws[m_retireIndex].clear();
74 m_retiredShadowDraws[m_retireIndex] = std::move(cachedShadowDraws);
75 m_retireIndex = (m_retireIndex + 1) % RETIRE_FRAMES;
76 cachedShadowDraws.clear();
77 {
78 RefPtr<BufferBase> lastSlot0Buffer;
79 RefPtr<BufferBase> lastSlot3Buffer;
80 for (auto& cmd : commands) {
81 auto type = cmd->GetType();
83 auto* bindCmd = static_cast<BindConstantBufferCommand*>(cmd.get());
84 if (bindCmd->GetSlot() == 0) {
85 lastSlot0Buffer = bindCmd->GetBuffer();
86 } else if (bindCmd->GetSlot() == 3) {
87 lastSlot3Buffer = bindCmd->GetBuffer();
88 }
89 }
90 if ((type == CommandType::Draw || type == CommandType::DrawIndexed)
91 && lastSlot0Buffer && cmd->CastsShadow()) {
92 ShadowDrawEntry entry;
93 entry.command = cmd;
94 entry.transformBuffer = lastSlot0Buffer;
95 // Pair bone buffer only with skinned draws so non-skinned
96 // casters don't accidentally inherit a stale bone UBO.
97 if (cmd->IsSkinned())
98 entry.boneBuffer = lastSlot3Buffer;
99 cachedShadowDraws.add(entry);
100 }
101 }
102 }
103
104 // ---- Deferred rendering path ----
105 // Split commands into:
106 // - Opaque group → geometry pass (writes to GBuffer)
107 // - Forward group → forward transparent pass (after lighting)
108 //
109 // State commands (UpdateCB, BindCB, SetMode, SetFace) are accumulated
110 // per-group and flushed together with the draw command they precede.
111 // CustomCommands (skybox, debug lines) always go to the forward group.
112 if (context->IsDeferredEnabled()) {
113
114 // Per-group accumulator: state commands preceding the current draw.
115 List<RefPtr<RenderCommandBase>> pendingState;
117
118 ::Sleak::Material* lastMaterial = nullptr;
119 bool lastMaterialForward = false;
120
121 auto isStateCmd = [](CommandType t) {
125 || t == CommandType::SetFace;
126 };
127
128 for (auto& cmd : commands) {
129 auto type = cmd->GetType();
130
131 if (type == CommandType::BindMaterial) {
132 auto* bmc = static_cast<BindMaterialCommand*>(cmd.get());
133 lastMaterial = bmc->GetMaterial();
134 lastMaterialForward = lastMaterial && lastMaterial->IsForwardRendered();
135 // Always add material bind to pending state
136 pendingState.add(cmd);
137 continue;
138 }
139
140 if (type == CommandType::CustomCommand) {
141 // Skybox, debug lines — always rendered after the lighting pass.
142 // Flush any pending state to forward group first so state is correct.
143 for (auto& s : pendingState) forwardCmds.add(s);
144 pendingState.clear();
145 forwardCmds.add(cmd);
146 lastMaterial = nullptr;
147 lastMaterialForward = false;
148 continue;
149 }
150
151 if (isStateCmd(type)) {
152 pendingState.add(cmd);
153 continue;
154 }
155
156 if (type == CommandType::Draw || type == CommandType::DrawIndexed) {
157 // Skinned meshes have no GBuffer pipeline variant — the
158 // skinned pipeline is render-pass-compatible only with
159 // the forward pass, so route them there even when opaque.
160 const bool isSkinned = cmd->IsSkinned();
161 if (lastMaterialForward || isSkinned) {
162 // Transparent/forward/skinned draw — defer to forward pass
163 for (auto& s : pendingState) forwardCmds.add(s);
164 forwardCmds.add(cmd);
165 } else {
166 // Opaque draw — execute now in geometry pass
167 for (auto& s : pendingState) s->Execute(context);
168 cmd->Execute(context);
169 }
170 pendingState.clear();
171 continue;
172 }
173
174 // Any other command type: execute immediately (opaque path)
175 cmd->Execute(context);
176 }
177
178 // Transition: geometry pass → lighting pass → forward transparent pass
180
182 for (auto& cmd : forwardCmds)
183 cmd->Execute(context);
184 context->EndForwardTransparentPass();
185
186 commands.clear();
187 return;
188 }
189
190 // ---- Forward rendering path (unchanged behavior) ----
191 for (auto& cmd : commands)
192 cmd->Execute(context);
193 commands.clear();
194 }
195
197 for (size_t i = 0; i < cachedShadowDraws.GetSize(); ++i) {
198 auto& entry = cachedShadowDraws[i];
199 // Bind the transform buffer (slot 0) before drawing — shadow mode
200 // in VulkanRenderer::BindConstantBuffer computes LightVP * World
201 if (entry.transformBuffer) {
202 context->BindConstantBuffer(entry.transformBuffer, 0);
203 }
204 // Skinned casters need their bone matrices bound so the shadow
205 // depth vert can transform verts into the posed skeleton space.
206 if (entry.boneBuffer) {
207 context->BindBoneBuffer(entry.boneBuffer);
208 }
209 entry.command->ExecuteShadow(context);
210 }
211 }
212
214 // Commands are submitted in the correct per-object order:
215 // UpdateCB → BindCB → BindMaterial → Draw for each object,
216 // with custom commands (skybox, debug lines) at their correct position.
217 // Preserve submission order to maintain correct state bindings.
218 // Future optimization: batch by material while preserving per-object groups.
219 }
220
222 // Batching is a technique to call render commands as single pass instead pf multiple draw calls. This significantly improves rendering efficiency
223 // TODO: Implement this method
224 }
225
227 while (!commands.isEmpty())
228 commands.pop();
229 }
230
232 while (!commands.isEmpty())
233 commands.pop();
234 cachedShadowDraws.clear();
235 for (int i = 0; i < RETIRE_FRAMES; ++i)
236 m_retiredShadowDraws[i].clear();
237 m_retireIndex = 0;
238 }
239
241 if (Instance) {
242 Instance->ClearAll();
243 delete Instance;
244 Instance = nullptr;
245 }
246 }
247 }
248}
249
Implements a dynamic array-like list for storing and managing a collection of elements.
Definition List.hpp:20
void clear()
Definition List.hpp:169
void add(const T &value)
Definition List.hpp:113
bool IsForwardRendered() const
True when this material must skip the GBuffer and render in the forward transparent pass.
Definition Material.cpp:102
Binds a constant buffer to a pipeline slot.
Binds a material's shader and textures for subsequent draws.
std::function< void(RenderContext *)> ExecuteFunction
Non-indexed draw command with its vertex and constant buffers.
Indexed draw command with its vertex, index, and constant buffers.
Frame-scoped queue of recorded draw/state commands, replayed into a RenderContext at flush time.
void Clear()
Drops queued commands for the current frame, keeping the cached shadow draw list.
void SubmitUpdateConstantBuffer(RefPtr< BufferBase > buffer, void *Data, uint16_t Size)
Queues a constant buffer write with data captured at submit time.
void SubmitCustomCommand(CustomCommand::ExecuteFunction function)
Queues an arbitrary callback to run inline with other render commands.
void ExecuteCommands(RenderContext *context)
void SubmitBindMaterial(::Sleak::Material *material)
Queues a material bind, switching shader/texture state for subsequent draws.
void ExecuteShadowPass(RenderContext *context)
void SubmitDrawIndexed(RefPtr< BufferBase > vertexBuffer, RefPtr< BufferBase > indexBuffer, List< RefPtr< BufferBase > > constantBuffers, uint32_t indexCount, uint32_t startIndexLocation=0, int32_t baseVertexLocation=0, bool castsShadow=true)
Queues an indexed draw with its vertex/index buffers and constant buffers.
void ClearAll()
Drops queued commands and the cached shadow draw list.
void SubmitDraw(RefPtr< BufferBase > vertexBuffer, List< RefPtr< BufferBase > > constantBuffers, uint32_t vertexCount, uint32_t startVertexLocation=0)
Queues a non-indexed draw with its vertex buffer and constant buffers.
void SubmitBindConstantBuffer(RefPtr< BufferBase > buffer, uint8_t slot)
Queues a constant buffer bind at the given slot.
Abstract interface for high-level graphics command execution and resource management.
virtual void BindBoneBuffer(RefPtr< BufferBase > buffer)
virtual void BindConstantBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0)=0
Switches the rasterizer fill mode.
Constant buffer write, captured by value into inline storage or a heap fallback for larger payloads.
Backend-facing rendering layer shared by the four graphics backends.
CommandType
Discriminator for RenderCommandBase subclasses, used for sorting and batching.
RenderMode
Rasterizer fill style for a draw.
RenderFace
Which triangle winding gets culled.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10
A draw command paired with the transform/bone buffers it needs to replay in the shadow pass.