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