Cogs.Core
RenderListTask.cpp
1#include "RenderListTask.h"
2#include "GenerateListTask.h"
3#include "FilterListTask.h"
4
5#include "Renderer/Renderer.h"
6#include "Renderer/RenderTexture.h"
7#include "Renderer/RenderMaterialInstance.h"
8#include "Renderer/RenderResources.h"
9#include "Renderer/RenderStateUpdater.h"
10#include "Renderer/RenderTexture.h"
11#include "Renderer/RenderTarget.h"
12#include "Renderer/EnginePermutations.h"
13
14#include "Platform/Instrumentation.h"
15
16#include "Context.h"
17
18#include "Services/Time.h"
19#include "Services/Variables.h"
20
21#include "Rendering/ITextures.h"
22#include "Rendering/IRenderTargets.h"
23#include "Rendering/ICapabilities.h"
24
25#include "Foundation/Logging/Logger.h"
26#include "Foundation/Collections/SmallVector.h"
27
28#include <glm/glm.hpp>
29#include <glm/gtc/color_space.hpp>
30
31#include <sstream>
32
33namespace
34{
35 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RenderListTask");
36}
37
38Cogs::Core::RenderListTask::RenderListTask()
39{
40}
41
42Cogs::Core::RenderListTask::~RenderListTask()
43{
44}
45
46void Cogs::Core::RenderListTask::initialize(RenderTaskContext * context)
47{
48 RenderTask::initialize(context);
49
50 auto renderTargets = context->device->getRenderTargets();
51
52 blendState = renderTargets->loadBlendState(&context->states->blendStates[size_t(blendMode)].color,
53 &context->states->blendStates[size_t(blendMode)].alpha, 1, BlendFlags::IndependentBlend);
54 depthState = context->states->commonDepthStates[(int)DepthMode::Count*(int)depthFunc+(int)depthMode];
55
56 scopeName = context->renderer->getEnginePermutations().get(permutationIndex)->getName();
57}
58
59void Cogs::Core::RenderListTask::apply(RenderTaskContext * context)
60{
61 DynamicRenderInstrumentationScope(context->device->getImmediateContext(), SCOPE_RENDERING, "RenderListTask", scopeName.c_str());
62
63 if (!validate(context, 1, 1)) return;
64
65 const auto renderList = input.get(RenderResourceType::RenderList)->renderList;
66 auto renderTarget = output.get(RenderResourceType::RenderTarget)->renderTarget;
67
68 if (!renderList) {
69 LOG_ERROR(logger, "Missing input render list for %s.", scopeName.c_str());
70 return;
71 }
72
73 if (!renderTarget) {
74 LOG_ERROR(logger, "Missing output target for %s.", scopeName.c_str());
75 return;
76 }
77
78 if (isStatic()) {
79 if (renderList->hash == lastHash) return;
80 lastHash = renderList->hash;
81 }
82
83 if (renderList->viewportData->viewportSize.x == 0 || renderList->viewportData->viewportSize.y == 0) return;
84
85 auto deviceContext = context->device->getImmediateContext();
86
87 setupRenderTarget(context, renderTarget, renderList->viewportData);
88
89 updateEngineBuffers(context, renderTarget, renderList->viewportData, nullptr);
90
91 renderItems(context,
92 renderTarget,
93 renderList,
94 bucketMask,
95 stateChangeMask);
96
97 if (context->device->getCapabilities()->getDeviceCapabilities().RenderPass &&
98 !renderList->viewportData->bindRenderTargetCallback) {
99 deviceContext->endRenderPass();
100 }
101
102 for (auto t : renderTarget->textures) {
103 if (t->description.flags & TextureFlags::GenerateMipMaps) {
104 context->device->getTextures()->generateMipmaps(t->textureHandle);
105 }
106 }
107}
108
109bool Cogs::Core::RenderListTask::validate(RenderTaskContext * /*context*/, size_t expectedInputs, size_t expectedOutputs)
110{
111 const bool validInputs = input.resources.size() >= expectedInputs;
112 const bool validOutputs = output.resources.size() >= expectedOutputs;
113
114 if (validInputs && validOutputs) {
115 return true;
116 } else {
117 LOG_ERROR(logger, "Failed validating connections for %s.", scopeName.c_str());
118 return false;
119 }
120}
121
122void Cogs::Core::RenderListTask::applyMaterial(const DrawContext & drawContext, const RenderItem & item)
123{
124 updateEnvironmentBindings(&drawContext, item.binding);
125
126 auto permutation = drawContext.permutation;
127
128 for (auto & buffer : drawContext.permutation->constantBuffers.buffers) {
129 permutation->bufferHandles.resize(permutation->constantBuffers.buffers.size());
130
131 if (!HandleIsValid(permutation->bufferHandles[buffer.index])) {
132 IBuffers* buffers = drawContext.device->getBuffers();
133 permutation->bufferHandles[buffer.index] = buffers->loadBuffer(nullptr, buffer.size, Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
134 buffers->annotate(permutation->bufferHandles[buffer.index], "RenderListTask::applyMaterial");
135 }
136
137 drawContext.deviceContext->updateBuffer(permutation->bufferHandles[buffer.index], buffer.content.data(), buffer.size);
138 drawContext.deviceContext->setConstantBuffer(buffer.name, permutation->bufferHandles[buffer.index]);
139 }
140}
141
142void Cogs::Core::RenderListTask::renderBatched(RenderTaskContext * taskContext,
143 DrawContext & drawContext,
144 const RenderList* renderList,
145 const RenderItems & items,
146 BucketMask /*bucketMask*/,
147 StateChangeFlags stateChangeMask,
148 bool batched)
149{
150 auto renderer = taskContext->renderer;
151 auto device = renderer->getDevice();
152
153 auto context = device->getImmediateContext();
154
155 auto setViewport = [&](const CameraData & c) {
156 context->setViewport(
157 c.viewportOrigin.x,
158 c.viewportOrigin.y,
159 (!viewportFromTarget && (c.viewportSize.x != 0)) ? c.viewportSize.x : static_cast<float>(defaultViewportSize.x),
160 (!viewportFromTarget && (c.viewportSize.y != 0)) ? c.viewportSize.y : static_cast<float>(defaultViewportSize.y));
161 };
162
163 size_t o = 0;
164 size_t n = items.size();
165 while (o < n) {
166
167 size_t m = batched ? populateObjectBuffer(&drawContext, items.data(), o, n) : o + 1;
168
169 for (size_t i = o; i < m; i++) {
170 const RenderItem & currentItem = items[i];
171
172 if (currentItem.isCustom1()) {
173 (*currentItem.callback)(taskContext, &drawContext, &currentItem);
174 continue;
175 }
176 if (currentItem.isCustom2()) {
177 (*currentItem.callback2)(taskContext, &drawContext, this, &currentItem);
178 continue;
179 }
180
181 const StateChangeFlags stateChanges = currentItem.stateChanges & stateChangeMask;
182
183 if ((stateChanges & StateChangeFlags::ChangeViewport) != 0) {
184 setViewport(*drawContext.cameraData);
185 }
186
187 if ((stateChanges & StateChangeFlags::ChangeDepthStencilState) != 0) {
188 context->setDepthStencilState(taskContext->states->commonDepthStates[currentItem.depthState]);
189 }
190
191 if ((stateChanges & StateChangeFlags::ChangeBlendState) != 0) {
192 context->setBlendState(taskContext->states->blendStates[currentItem.blendState].handle);
193 }
194
195 if ((stateChanges & StateChangeFlags::ChangeRasterizerState) != 0) {
196 context->setRasterizerState(taskContext->states->rasterizerStateHandles[currentItem.rasterizerState]);
197 }
198
199 if ((stateChanges & StateChangeFlags::ChangeViewportData) != 0) {
200 const ClipShapeCache::Item& clipShape = renderList->clipShapeCache->data[currentItem.clipShapeIx];
201 updateViewportBuffer(taskContext, taskContext->engineBuffers->sceneBufferHandle, taskContext->engineBuffers->viewBufferHandle, drawContext.renderTarget, currentItem.viewportData ? currentItem.viewportData : drawContext.cameraData, &clipShape.clipEquations);
202 }
203
204 if ((stateChanges & StateChangeFlags::ChangeMaterialVariant) != 0) {
205 applyMaterialPermutation(taskContext, &drawContext, currentItem.binding, currentItem.renderMaterialInstance);
206
207 applyMaterial(drawContext, currentItem);
208 }
209
210 if ((stateChanges & StateChangeFlags::ChangeMaterialInstance) != 0) {
211 applyMaterialInstance(&drawContext, currentItem.binding, currentItem.renderMaterialInstance);
212 }
213
214 if ((stateChanges & StateChangeFlags::ChangeMesh) != 0) {
215
216 if (HandleIsValid(currentItem.vertexArrayObject)) {
217 context->setVertexArrayObject(currentItem.vertexArrayObject);
218 }
219 else {
220 context->setVertexBuffers(currentItem.meshData->vertexBuffers, currentItem.meshData->streamsLayout.numStreams, currentItem.meshData->vertexStrides, currentItem.meshData->vertexOffsets);
221
222 if (currentItem.meshData->indexBuffer != IndexBufferHandle::NoHandle) {
223 context->setIndexBuffer(currentItem.meshData->indexBuffer, currentItem.meshData->indexStride, currentItem.meshData->indexOffset);
224 }
225
226 }
227 }
228 if (batched) {
229 applyMaterialPerObjectBatched(&drawContext, currentItem.renderMaterialInstance, currentItem, i - o);
230 }
231 else {
232 applyMaterialPerObject(&drawContext, currentItem.renderMaterialInstance, currentItem);
233 }
234
235 if (currentItem.isInstanced()) {
236
237 if (currentItem.instanceData) {
238 auto renderMesh = currentItem.meshData;
239 auto instanceMesh = currentItem.instanceData;
240
241 assert(renderMesh && instanceMesh && "Invalid instanced rendering data.");
242 assert(renderMesh->streamsLayout.numStreams && instanceMesh->streamsLayout.numStreams && "Missing instanced render buffers.");
243 assert(renderMesh->streamsLayout.numStreams + instanceMesh->streamsLayout.numStreams <= MeshStreamsLayout::maxStreams);
244
246 uint32_t strides[MeshStreamsLayout::maxStreams];
247 uint32_t offsets[MeshStreamsLayout::maxStreams];
248
249 // Add vertex streams
250 size_t numStreams = 0;
251 for (size_t j = 0; j < renderMesh->streamsLayout.numStreams; j++) {
252 vertexBuffers[numStreams] = renderMesh->vertexBuffers[j];
253 strides[numStreams] = renderMesh->vertexStrides[j];
254 offsets[numStreams] = renderMesh->vertexOffsets[j];
255 numStreams++;
256 }
257
258 // Add instance streams
259 for (size_t j = 0; j < instanceMesh->streamsLayout.numStreams; j++) {
260 vertexBuffers[numStreams] = instanceMesh->vertexBuffers[j];
261 strides[numStreams] = instanceMesh->vertexStrides[j];
262 offsets[numStreams] = instanceMesh->vertexOffsets[j];
263 numStreams++;
264 }
265
266 context->setVertexBuffers(vertexBuffers, numStreams, strides, offsets);
267 }
268
269 if (currentItem.meshData->indexBuffer) {
270 context->setIndexBuffer(currentItem.meshData->indexBuffer);
271 context->drawInstancedIndexed(currentItem.primitiveType, currentItem.startInstance, currentItem.numInstances, currentItem.startIndex, currentItem.numIndexes);
272 } else {
273 context->drawInstanced(currentItem.primitiveType, currentItem.startIndex, currentItem.numIndexes, currentItem.startInstance, currentItem.numInstances);
274 }
275
276 } else {
277 if (currentItem.meshData->indexBuffer != IndexBufferHandle::NoHandle) {
278 context->drawIndexed(currentItem.primitiveType, currentItem.startIndex, currentItem.numIndexes);
279 } else {
280 context->draw(currentItem.primitiveType, currentItem.startIndex, currentItem.numIndexes);
281 }
282 }
283 }
284
285 o = m;
286 }
287
288
289}
290
291void Cogs::Core::RenderListTask::setupRenderTarget(RenderTaskContext * context, RenderTarget * renderTarget, const CameraData * viewportData)
292{
293 defaultViewportSize.x = renderTarget->width;
294 defaultViewportSize.y = renderTarget->height;
295
296 auto deviceContext = context->device->getImmediateContext();
297
298 const RenderSettings& renderSettings = context->renderer->getSettings();
299 MaterialOptions defaultMaterialOptions;
300 RenderPassOptions defaultPassOptions;
301
302 const bool isBackBuffer =
303 !HandleIsValid(renderTarget->renderTargetHandle) &&
304 !HandleIsValid(renderTarget->depthTargetHandle);
305 const bool doScissor = viewportData->passOptions &&
306 viewportData->passOptions->getFlag(RenderPassOptions::Flags::ScissorTest);
307 bool doColorClear = colorClear &&
308 (HandleIsValid(renderTarget->renderTargetHandle) || !HandleIsValid(renderTarget->depthTargetHandle));
309 bool doDepthClear = depthClear &&
310 (HandleIsValid(renderTarget->depthTargetHandle) || isBackBuffer);
311 if (isBackBuffer && (renderSettings.defaultRenderTargetClear == false)) {
312 doColorClear = false;
313 doDepthClear = false;
314 }
315 bool isCleared = false;
316
317 size_t nt = renderTarget->textures.size();
318 size_t no = std::max(size_t(1), nt);
319 glm::vec4 clearColorSRGB;
320 Collections::SmallVector<const float*, 8> clearColors(no);
321 {
322 glm::vec4 black(0.f);
323
324 size_t nc = renderTarget->clearColors.size();
325
326 for (size_t i = 0; i < no; i++) {
327
328 clearColors[i] = i == 0 ? glm::value_ptr(viewportData->useClearColor ? viewportData->clearColor : context->renderer->getBackgroundColor()) : glm::value_ptr(black);
329
330 if (i < nt && renderTarget->textures[i]->clearColorSet) {
331 clearColors[i] = renderTarget->textures[i]->clearColor;
332 }
333
334 else if (i < nc) {
335 clearColors[i] = glm::value_ptr(renderTarget->clearColors[i]);
336 }
337 }
338
339 if (no == 1) {
340 if ((isBackBuffer && renderSettings.defaultRenderTargetExpectsSRGB) || renderTarget->expectsSRGB) {
341 clearColorSRGB = glm::vec4(glm::convertLinearToSRGB(glm::make_vec3(clearColors[0])), clearColors[0][3]);
342 clearColors[0] = glm::value_ptr(clearColorSRGB);
343 }
344 }
345 }
346
347 if (viewportData->bindRenderTargetCallback) {
348 viewportData->bindRenderTargetCallback(viewportData->bindRenderTargetCallbackData);
349 }
350 else {
351 if(context->device->getCapabilities()->getDeviceCapabilities().RenderPass){
352 RenderPassInfo info;
353 info.renderTargetHandle = renderTarget->renderTargetHandle;
354 info.depthStencilHandle = renderTarget->depthTargetHandle;
355 for (size_t i = 0; i < no; i++) {
356 info.loadOp[i] = doColorClear ? LoadOp::Clear : LoadOp::Load;
357 info.storeOp[i] = discardColor ? StoreOp::Discard : StoreOp::Store;
358 info.clearValue[i][0] = clearColors[i][0];
359 info.clearValue[i][1] = clearColors[i][1];
360 info.clearValue[i][2] = clearColors[i][2];
361 info.clearValue[i][3] = clearColors[i][3];
362 }
363 info.depthLoadOp = doDepthClear ? LoadOp::Clear : LoadOp::Load;
364 info.depthStoreOp = discardDepth ? StoreOp::Discard : StoreOp::Store;
365 info.depthClearValue = context->renderer->getClearDepth();
366 info.depthReadOnly = !depthWrite;
367 deviceContext->beginRenderPass(info);
368 isCleared = true;
369 }
370 else{
371 deviceContext->setRenderTarget(renderTarget->renderTargetHandle, renderTarget->depthTargetHandle);
372 }
373 }
374
375 if (doScissor || doColorClear || doDepthClear || viewportData->bindRenderTargetCallback) {
376 // E.g. scissor test affects clears.'
377 // The bindRenderTargetCallback might change rasterization state.
378 uint16_t rasterizerStateIx = context->states->getRasterizerState(viewportData->passOptions ? *viewportData->passOptions : defaultPassOptions,
380 false, // RenderMode::Normal,
381 true);
382 RasterizerStateHandle& rasterizerStateHandle = context->states->rasterizerStateHandles[rasterizerStateIx];
383 deviceContext->setRasterizerState(rasterizerStateHandle);
384 }
385
386 if (doScissor) {
387 auto & rect = viewportData->passOptions->scissorRectangle;
388 deviceContext->setScissor(rect.x, rect.y, rect.z, rect.w);
389 }
390
391 if(!context->device->getCapabilities()->getDeviceCapabilities().RenderPass){
392 if (doColorClear && !isCleared) {
393 if (no == 1) {
394 deviceContext->clearRenderTarget(clearColors[0]);
395 }
396 else {
397 deviceContext->clearRenderTarget((const float**)clearColors.data(), static_cast<int>(nt));
398 }
399 }
400
401 if (doDepthClear && !isCleared) {
402 deviceContext->clearDepth(context->renderer->getClearDepth());
403 }
404 }
405
406 deviceContext->setBlendState(blendState);
407 deviceContext->setDepthStencilState(depthState);
408}
409
410void Cogs::Core::RenderListTask::renderItems(RenderTaskContext * taskContext,
411 RenderTarget* renderTarget,
412 const RenderList* renderList,
413 BucketMask bucketMask,
414 StateChangeFlags stateChangeMask)
415{
416 auto renderer = taskContext->renderer;
417 auto device = taskContext->device;
418 char buffer[ 128 ];
419
420 std::sprintf(buffer, "mask=0x%08X", static_cast<int>(bucketMask));
421 DynamicRenderInstrumentationScope(taskContext->device->getImmediateContext(), SCOPE_RENDERING, "RenderListTask::renderItems", buffer);
422
423 auto enginePermutation = renderer->getEnginePermutations().get(permutationIndex);
424
425 if(temporalOffsets){
426 uint32_t frame = taskContext->context->time->getFrame();
427 uint32_t widthDivisor = taskContext->context->variables->get("renderer.oit.TemporalUpscaleWidth", 1);
428 uint32_t heightDivisor = taskContext->context->variables->get("renderer.oit.TemporalUpscaleHeight", 1);
429 uint32_t i = frame % (widthDivisor * heightDivisor);
430 uint32_t w = i%widthDivisor;
431 uint32_t h = (i/widthDivisor)%heightDivisor;
432
433 CameraData &viewportData(*(CameraData*)renderList->viewportData);
434 viewportData.projectionMatrix = taskContext->renderer->getProjectionMatrix(viewportData.rawProjectionMatrix);
435 viewportData.projectionMatrix[2][0] += (float)w*(2.0f/widthDivisor)/(float)renderTarget->width;
436 viewportData.projectionMatrix[2][1] -= (float)h*(2.0f/heightDivisor)/(float)renderTarget->height;
437 viewportData.viewProjection = viewportData.projectionMatrix * viewportData.viewMatrix;
438 viewportData.inverseViewProjectionMatrix = glm::inverse(viewportData.viewProjection);
439 viewportData.inverseProjectionMatrix = glm::inverse(viewportData.projectionMatrix);
440 }
441
442 DrawContext drawContext;
443 drawContext.context = taskContext->context;
444 drawContext.renderer = renderer;
445 drawContext.device = device;
446 drawContext.deviceContext = device->getImmediateContext();
447 drawContext.cameraData = renderList->viewportData;
448 drawContext.permutationIndex = enginePermutation->getIndex();
449 drawContext.permutation = enginePermutation;
450 drawContext.engineBuffers = &renderer->getEngineBuffers();
451 drawContext.renderTarget = renderTarget;
452 drawContext.taskContext = taskContext;
453 drawContext.task = this;
454 drawContext.listObjectBuffer = renderList->listObjectBuffer;
455 drawContext.clipShapeCache = renderList->clipShapeCache;
456
457 bool batchObjectBuffer = drawContext.engineBuffers->objectBatch.count != 0;
458 const StringView batchObjectBufferKey = "renderer.batchObjectBuffer";
459 if (Variable* var = drawContext.context->variables->get(batchObjectBufferKey); !var->isEmpty()) {
460 batchObjectBuffer = var->getBool();
461 }
462 else {
463 drawContext.context->variables->set(batchObjectBufferKey, batchObjectBuffer);
464 }
465
466 // list object buffer has presendence
467 if (drawContext.listObjectBuffer && drawContext.listObjectBuffer->isInUse()) {
468 objectBufferMode = ObjectBufferMode::List;
469 batchObjectBuffer = false;
470 }
471 else if (drawContext.engineBuffers->objectBatch.count && batchObjectBuffer) {
472 objectBufferMode = ObjectBufferMode::Batched;
473 }
474 else {
475 objectBufferMode = ObjectBufferMode::Single;
476 batchObjectBuffer = false;
477 }
478
479 for (size_t b = 0; b < size_t(BucketType::Count); b++) {
480 if ((BucketMask(1 << b) & bucketMask) == 0) continue;
481
482 const RenderItems & items = renderList->buckets[b];
483 renderBatched(taskContext, drawContext, renderList, items, bucketMask, stateChangeMask, batchObjectBuffer);
484 }
485
486 if(temporalOffsets){
487 CameraData &viewportData(*(CameraData*)renderList->viewportData);
488 viewportData.projectionMatrix = taskContext->renderer->getProjectionMatrix(viewportData.rawProjectionMatrix);
489 viewportData.viewProjection = viewportData.projectionMatrix * viewportData.viewMatrix;
490 viewportData.inverseViewProjectionMatrix = glm::inverse(viewportData.viewProjection);
491 viewportData.inverseProjectionMatrix = glm::inverse(viewportData.projectionMatrix);
492 }
493}
Log implementation class.
Definition: LogManager.h:140
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
@ Write
The buffer can be mapped and written to by the CPU after creation.
Definition: Flags.h:50
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
Definition: Flags.h:72
static constexpr size_t maxStreams
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:78
@ Back
Cull back facing primitives.
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
Definition: Flags.h:124
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30