Cogs.Core
RenderPipelineManager.cpp
1#include <string>
2
3#include "RenderPipelineManager.h"
4
5#include "Systems/Core/CameraSystem.h"
6#include "Systems/Core/LightSystem.h"
7#include "Systems/Core/CameraArraySystem.h"
8
9#include "Services/Time.h"
10#include "Services/Services.h"
11#include "Services/PipelineService.h"
12
13#include "Context.h"
14#include "ViewContext.h"
15
16#include "Renderer.h"
17#include "RenderPipelineFactory.h"
18#include "RenderTexture.h"
19#include "RenderTarget.h"
20#include "RenderList.h"
21
22#include "Serialization/RenderPipelineReader.h"
23
24#include "Tasks/RenderTaskFactory.h"
25#include "Tasks/RenderListTask.h"
26#include "Tasks/ReadbackTask.h"
27
28#include "Rendering/ICapabilities.h"
29
30#include "Foundation/HashSequence.h"
31#include "Foundation/Collections/SmallVector.h"
32#include "Foundation/Logging/Logger.h"
33#include "Foundation/ComponentModel/Entity.h"
34#include "Foundation/Platform/Mouse.h"
35#include "Foundation/Reflection/TypeDatabase.h"
36
37namespace
38{
39 using namespace Cogs::Core;
41 using PipeInitFunc = std::function<void(PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)>;
42
43 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("RenderPipelineManager");
44
45 const std::string forwardPath = "Pipelines/Forward.pipeline";
46 const std::string shadowPath = "Pipelines/Shadow.pipeline";
47
48 std::string toHexString(size_t value)
49 {
50 constexpr size_t maxDigits = 2 * sizeof(size_t);
51 static const char digits[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
52 std::string rv;
53
54 rv.reserve(2 + maxDigits);
55 rv.push_back('0');
56 rv.push_back('x');
57 size_t o = maxDigits;
58 while (o) {
59 if (size_t nibble = (value >> (4 * (o - 1)) & 0xF); nibble) {
60 break;
61 }
62 o--;
63 }
64 while (o) {
65 size_t nibble = (value >> (4 * (o - 1)) & 0xF);
66 rv.push_back(digits[nibble]);
67 o--;
68 }
69 return rv;
70 }
71
72 RenderTexture* getRenderTexture(RenderTaskContext* renderContext, size_t& dataHash, const TextureHandle& texture, const Cogs::StringView& name, bool useAsTarget)
73 {
74 if (!texture) return nullptr;
75
76 RenderTexture* renderTexture = renderContext->resources->getRenderTexture(texture);
77 if (renderTexture) {
78 if (useAsTarget && !renderTexture->renderTarget) {
79
80 assert(!renderTexture->depthTexture);
81 RenderTexture* depthTexture = renderContext->resources->createRenderTexture();
82 depthTexture->setName(name.to_string() + " DT");
83 depthTexture->description.target = (renderTexture->description.samples > 1) ? Cogs::ResourceDimensions::Texture2DMS : Cogs::ResourceDimensions::Texture2D;
84 depthTexture->description.flags = Cogs::TextureFlags::DepthBuffer;
85 depthTexture->description.format = Cogs::TextureFormat::R32_TYPELESS;
86 depthTexture->samples = renderTexture->description.samples;
87 depthTexture->sizeSource = renderTexture;
88 renderTexture->depthTexture = depthTexture;
89
90 assert(!renderTexture->renderTarget);
91 RenderTarget* renderTarget = renderContext->resources->createRenderTarget();
92 renderTarget->setName(name.to_string() + " RT");
93 renderTarget->textures.push_back(renderTexture);
94 renderTarget->depth = depthTexture;
95 renderTarget->width = renderTexture->description.width;
96 renderTarget->height = renderTexture->description.height;
97 renderTarget->samples = renderTexture->description.samples;
98 renderTexture->renderTarget = renderTarget;
99 }
100 dataHash = Cogs::hashSequence(dataHash,
101 reinterpret_cast<intptr_t>(renderTexture),
102 reinterpret_cast<intptr_t>(renderTexture->renderTarget),
103 reinterpret_cast<intptr_t>(renderTexture->depthTexture));
104 }
105
106 return renderTexture;
107 }
108
109 void releasePipelineInstance(RenderTaskContext* renderContext, PipelineInstance* instance)
110 {
111 releasePipeline(renderContext, instance->pipeline);
112 instance->options.clear();
113 instance->dataHash = 0;
114 instance->priority = 0;
115 instance->pipeline.updateResources.resources.clear();
116 }
117
118 bool setupCameraPipeline(RenderTaskContext* renderContext, RenderPipelineManager* manager,
119 size_t instanceKey, size_t dataHash, const Cogs::StringView& pipeline, int priority,
120 PipeInitFunc const& initFunc, const CameraComponent* camComp, const CameraData* camData, RenderTexture* renderTexture, RenderList* renderList)
121 {
122 auto [instance, wasCreated] = manager->instanceByKey(renderContext->context, instanceKey);
123 if (wasCreated) {
124 instance->priority = priority;
125 instance->sanityCheck = camData;
126 }
127 assert(instance->sanityCheck == camData);
128
129 dataHash = Cogs::hash(pipeline, dataHash);
130 if (camComp) {
131 for (const std::string& value : camComp->renderPipelineOptions) {
132 dataHash = Cogs::hash(value, dataHash);
133 }
134 }
135
136 if (wasCreated || instance->dataHash != dataHash || instance->priority != priority || !isPipelineFresh(renderContext, instance->pipeline)) {
137 releasePipelineInstance(renderContext, instance);
138 instance->dataHash = dataHash;
139 instance->priority = priority;
140
141 RenderTaskResources resources;
142 for (RenderTaskResource& resource : manager->globalResources.resources) {
143 resources.add(&resource);
144 }
145 if (renderList) {
146 resources.add(renderList, "Cogs.RenderList");
147 }
148 if (camComp) {
149 for (size_t i = 0, n = camComp->renderPipelineOptions.size() / 2; i < n; i++) {
150 instance->options.push_back({
151 camComp->renderPipelineOptions[2 * i + 0],
152 camComp->renderPipelineOptions[2 * i + 1]
153 });
154 }
155 }
156
157 if (renderTexture) {
158 instance->pipeline.updateResources.add(renderTexture->depthTexture);
159 instance->pipeline.updateResources.add(renderTexture->renderTarget);
160 resources.add(renderTexture->renderTarget, "Cogs.BackBuffer");
161
162 ExpressionContext& expressionContext = instance->pipeline.expressionContext;
163
164 expressionContext.add("Cogs.BackBuffer.width", renderTexture->description.width);
165 expressionContext.add("Cogs.BackBuffer.height", renderTexture->description.height);
166
167 renderTexture->width = { &expressionContext, "Cogs.BackBuffer.width", std::to_string(renderTexture->description.width), 0 };
168 renderTexture->height = { &expressionContext, "Cogs.BackBuffer.height", std::to_string(renderTexture->description.height), 0 };
169
170 instance->options.push_back({ "Pass", "Offscreen" });
171 }
172 instance->renderTarget = renderTexture;
173
174 if (initFunc) {
175 initFunc(instance, resources, wasCreated);
176 }
177
178 const RenderPipelineDefinition& pipeDefinition = manager->definitionByPath(renderContext->context, pipeline);
179 createPipeline(renderContext, pipeDefinition, resources, instance->pipeline, pipeline, camData, instance->options);
180
181 return true;
182 }
183 return true;
184 }
185
186
187 void setupLightPipeline(RenderTaskContext * renderContext,
189 LightComponent& light,
190 const LightData& lightData,
191 TextureHandle shadowTexture,
192 RenderList * renderList)
193 {
194 auto [instance, wasCreated] = manager->instanceByKey(renderContext->context, Cogs::hashSequence(Cogs::hash("Light"), light.hash()));
195 if (wasCreated) {
196 instance->name = "Light@" + toHexString(size_t(&light));
197 instance->priority = 100;
198 instance->sanityCheck = &light;
199 LOG_DEBUG(logger, "Added light pipeline");
200 }
201 assert(instance->sanityCheck == &light);
202
203 auto renderTexture = renderContext->resources->getRenderTexture(shadowTexture);
204 if (!renderTexture) return;
205
206 size_t dataHash = Cogs::hash(reinterpret_cast<intptr_t>(renderTexture));
207
208 // Set up shadow map render target
209 // -------------------------------
210
211 // Light texture is either array texture (directional light) or cube map texture (point light)
212 for (uint32_t i = 0; i < lightData.maxViewports; ++i) {
213 const uint32_t arrayIndex = i + lightData.arrayOffset;
214 if (!(arrayIndex < renderTexture->renderTargets.size())) {
215 LOG_ERROR(logger, "Light shadow arrayIndex larger than renderTarget size");
216 return;
217 }
218 if (!renderTexture->renderTargets[arrayIndex]) {
219 auto renderTarget = renderContext->resources->createRenderTarget();
220 renderTarget->setName("Light 0x" + toHexString(size_t(&light)) + " RT arrayIndex=" + std::to_string(arrayIndex));
221 renderTarget->depth = renderTexture;
222 renderTarget->depthLayerIndex = arrayIndex;
223 renderTexture->renderTargets[arrayIndex] = renderTarget;
224 }
225 dataHash = Cogs::hashSequence(reinterpret_cast<intptr_t>(renderTexture->renderTargets[arrayIndex]), dataHash);
226 }
227 dataHash = Cogs::hashSequence(lightData.shadowUpdate, dataHash);
228 dataHash = Cogs::hashSequence(lightData.textureSize, dataHash);
229 dataHash = Cogs::hashSequence(renderTexture->description.format , dataHash);
230
231 // Set up shadow map render pipline
232 // --------------------------------
233
234 if (wasCreated || instance->dataHash != dataHash || !isPipelineFresh(renderContext, instance->pipeline)) {
235 releasePipelineInstance(renderContext, instance);
236 instance->dataHash = dataHash;
237
238 for (size_t viewportIndex = 0; viewportIndex < lightData.maxViewports; viewportIndex++) {
239 const size_t arrayIndex = viewportIndex + lightData.arrayOffset;
240
241 instance->pipeline.updateResources.add(renderTexture->renderTargets[arrayIndex]);
242
243 RenderTaskResources resources;
244 resources.add(renderTexture->renderTargets[arrayIndex], "Cogs.BackBuffer");
245 resources.add(renderList, "Cogs.RenderList");
246
247 createPipeline(renderContext, manager->definitionByPath(renderContext->context, shadowPath), resources, instance->pipeline, shadowPath, &lightData.lightCameraData[viewportIndex], instance->options);
248
249 for (size_t taskIndex = viewportIndex * 2; taskIndex < viewportIndex * 2 + 2; ++taskIndex) {
250 instance->pipeline.tasks[taskIndex]->frameMod = 0;// lightData.frameMod[viewportIndex];
251 instance->pipeline.tasks[taskIndex]->frameOffset = 0;// lightData.frameOffset[viewportIndex];
252 if (lightData.shadowUpdate == ShadowUpdate::Static || lightData.shadowUpdate == ShadowUpdate::StaticPartial) {
253 instance->pipeline.tasks[taskIndex]->flags = (RenderTaskFlags::ERenderTaskFlags)((int)instance->pipeline.tasks[taskIndex]->flags | (int)RenderTaskFlags::Static);
254 }
255 }
256 }
257 if (!wasCreated) LOG_DEBUG(logger, "Updated light pipeline");
258 manager->dirty = true;
259 }
260 }
261
262 void setupAuxPipelines(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
263 {
264 std::vector<const Variable*> vars;
265 renderContext->context->variables->getMatchingVariables(vars, "renderer.auxiliaryPipelines.");
266
267 for (const auto * var : vars) {
268 if (var->getValue().empty()) continue;
269
270 std::vector<Cogs::StringView> tokens;
271 split(var->getValue(), ":", tokens);
272 if (tokens.empty() || 2 < tokens.size()) {
273 LOG_ERROR_ONCE(logger, "Failed to tokenize %.*s: '%.*s'", StringViewFormat(var->getName()), StringViewFormat(var->getValue()));
274 continue;
275 }
276
277 size_t dataHash = 0;
278 size_t instanceKey = Cogs::hashSequence(Cogs::hash("AuxPipeline"), var->getName().hash());
279 Cogs::StringView pipeline = tokens[0];
280 int priority = 0;
281 if (1 < tokens.size()) {
282 priority = std::stoi(tokens[1].to_string());
283 }
284
285 PipeInitFunc initFunc = [var, &pipeline](PipelineInstance* instance, RenderTaskResources& /*resources*/, bool wasCreated)
286 {
287 instance->name = var->getName().to_string();
288 instance->options.push_back( { "Pass", "Offscreen" });
289 LOG_DEBUG(logger, "%s aux pipeline run for %s: %.*s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), StringViewFormat(pipeline));
290 };
291
292 setupCameraPipeline(renderContext, manager,
293 instanceKey, dataHash, pipeline, priority,
294 initFunc, nullptr, nullptr, nullptr, renderList);
295 }
296 }
297
298 void setupExtraPipelineRuns(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
299 {
300 PipelineService* pipelineService = renderContext->context->services->getService<PipelineService>();
301 for (PipelineRun* run : pipelineService->runs) {
302 if (run->enabled && !run->pipeline.empty()) {
303
304 size_t dataHash = 0;
305 size_t instanceKey = Cogs::hashSequence(run->name.hash(), reinterpret_cast<intptr_t>(run->cameraData));
306 RenderTexture* renderTexture = nullptr;
307 if (run->renderTexture) {
308 renderTexture = getRenderTexture(renderContext, dataHash, run->renderTexture, run->name, true);
309 }
310
311 PipeInitFunc initFunc = [run, renderTexture, renderContext](PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)
312 {
313 instance->name = run->name.to_string();
314 instance->options.push_back( { "Pass", "Offscreen" });
315 if (!renderTexture) {
316 resources.add(renderContext->defaultRenderTarget, "Cogs.BackBuffer");
317 instance->options.push_back({ "Pass", "Onscreen" });
318 }
319 LOG_DEBUG(logger, "%s extra pipeline run for %s: %s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), run->pipeline.c_str());
320 };
321
322 setupCameraPipeline(renderContext, manager,
323 instanceKey, dataHash, run->pipeline, run->priority,
324 initFunc, nullptr, run->cameraData, renderTexture, renderList);
325 }
326 }
327 }
328
329 void setupLightPipelines(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
330 {
331 for (auto & light : renderContext->context->lightSystem->pool) {
332 auto & lightData = renderContext->context->lightSystem->getData(&light);
333
334 if (!lightData.enabled || !lightData.castShadows) continue;
335
336 setupLightPipeline(renderContext, manager, light, lightData, lightData.shadowTexture, renderList);
337 }
338 }
339
340 void setupCameraArrayRenderTarget(RenderTaskContext* renderContext,
341 size_t& dataHash, RenderTexture* renderTexture, size_t targetIndex,
342 uint32_t multiViewCount, uint32_t multiViewSamples,
343 const CameraArrayComponent& camArrComp, CameraArrayData& camArrData,
344 const Cogs::StringView name)
345 {
346 if (1 < multiViewCount) {
347 camArrData.passOptions = camArrData.camData.passOptions ? *camArrData.camData.passOptions : RenderPassOptions{};
348 camArrData.camData.passOptions = &camArrData.passOptions;
349 camArrData.passOptions.multiViews = multiViewCount;
350 }
351
352 if (!renderTexture->renderTargets[targetIndex]) {
353 RenderTarget* renderTarget = renderContext->resources->createRenderTarget();
354 renderTarget->setName(name.to_string() + " MV-RT[" + std::to_string(targetIndex) + ":" + std::to_string(multiViewCount) + "]");
355 renderTarget->textures.push_back(renderTexture);
356 renderTarget->layerIndex = uint16_t(targetIndex);
357 renderTarget->depth = renderTexture->depthTexture;
358 renderTarget->depthLayerIndex = uint16_t(targetIndex);
359 if (1 < multiViewCount) {
360 renderTarget->multiViewBaseIndex = 0;
361 renderTarget->multiViewCount = static_cast<uint8_t>(multiViewCount);
362 renderTarget->multiViewSamples = static_cast<uint8_t>(std::min(255u, multiViewSamples));
363 }
364 renderTarget->width = renderTexture->description.width;
365 renderTarget->height = renderTexture->description.height;
366 renderTarget->samples = renderTexture->description.samples;
367 renderTarget->expectsSRGB = camArrComp.expectsSRGB;
368 renderTexture->renderTargets[targetIndex] = renderTarget;
369 }
370 dataHash = Cogs::hashSequence(dataHash, reinterpret_cast<intptr_t>(renderTexture->renderTarget));
371 }
372
373 void setupCameraArrayDepthTarget(RenderTaskContext* renderContext,
374 size_t& dataHash, RenderTexture* renderTexture, size_t targetIndex,
375 const CameraArrayData& camArrData, const Cogs::StringView name)
376 {
377 switch (camArrData.depthMode) {
378
379 case CameraArrayData::DepthMode::None:
380 break;
381
382 case CameraArrayData::DepthMode::Create:
383 if (!renderTexture->depthTexture) {
384 RenderTexture* depthTexture = renderContext->resources->createRenderTexture();
385 depthTexture->setName(name.to_string() + " DT");
386 depthTexture->description.target = renderTexture->description.target;
387 depthTexture->description.flags = Cogs::TextureFlags::DepthBuffer;
388 depthTexture->description.format = Cogs::TextureFormat::R32_TYPELESS;
389 depthTexture->layers = renderTexture->description.layers;
390 depthTexture->samples = renderTexture->description.samples;
391 depthTexture->sizeSource = renderTexture;
392 renderTexture->depthTexture = depthTexture;
393 }
394 dataHash = Cogs::hashSequence(dataHash, reinterpret_cast<intptr_t>(renderTexture->depthTexture));
395 break;
396
397 case CameraArrayData::DepthMode::Provided:
398 if (!renderTexture->depthTexture) {
399 renderTexture->depthTexture = getRenderTexture(renderContext, dataHash, camArrData.depthTextures[targetIndex], name, false);
400 }
401 dataHash = Cogs::hashSequence(dataHash, reinterpret_cast<intptr_t>(renderTexture->depthTexture));
402 break;
403
404 default:
405 assert(false && "Illegal enum");
406 break;
407 }
408 }
409
410 void setupCameraArrayDraw(RenderTaskContext* renderContext, RenderPipelineManager* manager, RenderList* renderList,
411 const size_t dataHash, RenderTexture* renderTexture, size_t targetIndex,
412 const CameraArrayData& camArrData, const CameraComponent& camComp,
413 const Cogs::StringView name, size_t instanceKey)
414 {
415 Cogs::StringView pipeline = camArrData.pipeline.empty() ? forwardPath : camArrData.pipeline;
416
417 PipeInitFunc initFunc = [targetIndex, &name, &pipeline, renderTexture, updateDepth = camArrData.depthMode == CameraArrayData::DepthMode::Create](PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)
418 {
419 instance->name = name.to_string();
420 // Update the resources owned by this pipeline
421 if (updateDepth) {
422 instance->pipeline.updateResources.add(renderTexture->depthTexture);
423 }
424 instance->pipeline.updateResources.add(renderTexture->renderTargets[targetIndex]);
425 resources.add(renderTexture->renderTargets[targetIndex], "Cogs.BackBuffer");
426
427 ExpressionContext& expressionContext = instance->pipeline.expressionContext;
428
429 expressionContext.add("Cogs.BackBuffer.width", renderTexture->description.width);
430 expressionContext.add("Cogs.BackBuffer.height", renderTexture->description.height);
431
432 //renderTexture->width = { &expressionContext, "Cogs.BackBuffer.width", std::to_string(renderTexture->description.width), 0 };
433 //renderTexture->height = { &expressionContext, "Cogs.BackBuffer.height", std::to_string(renderTexture->description.height), 0 };
434
435 LOG_DEBUG(logger, "%s offscreen camera pipeline for %s: %.*s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), StringViewFormat(pipeline));
436 };
437
438 setupCameraPipeline(renderContext, manager,
439 Cogs::hashSequence(instanceKey, targetIndex), dataHash, pipeline, 300 + camComp.stackOrder,
440 initFunc, &camComp, &camArrData.camData, nullptr, renderList);
441
442 }
443
444
445 void setupCameraArrayPipelines(RenderTaskContext* renderContext, RenderPipelineManager* manager, RenderList* renderList)
446 {
447 bool multiView = renderContext->device->getCapabilities()->getDeviceCapabilities().MultiView;
448
449 std::string name_;
450
451 for (const CameraArrayComponent& camArrComp : renderContext->context->cameraArraySystem->pool) {
452
453 CameraArrayData& camArrData = renderContext->context->cameraArraySystem->getData(&camArrComp);
454 if (camArrData.textureMode == CameraArrayData::TextureMode::None) {
455 continue;
456 }
457
458 Cogs::StringView name = camArrComp.getContainer()->getName();
459 if (name.empty()) {
460 const Entity* entity = camArrComp.getContainer();
461 name_ = "CameraArray@" + std::to_string(entity->getId());
462 name = name_;
463 }
464
465 assert(camArrData.camData.camera);
466 const CameraComponent& camComp = renderContext->context->cameraSystem->pool[camArrData.camData.camera.index];
467 size_t instanceKey = Cogs::hashSequence(Cogs::hash("CameraArrays"), reinterpret_cast<intptr_t>(&camArrData.camData));
468
469 switch (camArrData.textureMode) {
470
471 case CameraArrayData::TextureMode::TextureArray: {
472 size_t dataHash = 0;
473 if (RenderTexture* renderTexture = getRenderTexture(renderContext, dataHash, camArrData.colorTextures[0], name, false); renderTexture) {
474
475
476 setupCameraArrayDepthTarget(renderContext, dataHash, renderTexture, 0, camArrData, name);
477
478
479 if ((1 < renderTexture->description.layers) && multiView) {
480 setupCameraArrayRenderTarget(renderContext, dataHash, renderTexture, 0, renderTexture->description.layers, camArrComp.samples, camArrComp, camArrData, name);
481 setupCameraArrayDraw(renderContext, manager, renderList, dataHash, renderTexture, 0, camArrData, camComp, name, instanceKey);
482 }
483
484 else {
485 for (size_t l = 0; l < renderTexture->description.layers; l++) {
486 setupCameraArrayRenderTarget(renderContext, dataHash, renderTexture, l, 1, 1, camArrComp, camArrData, name);
487 }
488 for (size_t l = 0; l < renderTexture->description.layers; l++) {
489 setupCameraArrayDraw(renderContext, manager, renderList, dataHash, renderTexture, l, camArrData, camComp, name, Cogs::hashSequence(instanceKey, static_cast<intptr_t>(l)));
490 }
491 }
492 }
493 break;
494 }
495
496 case CameraArrayData::TextureMode::ArrayOfTextures:
497 for (size_t l = 0; l < camArrData.numViews; l++) {
498 size_t dataHash = 0;
499 if (RenderTexture* renderTexture = getRenderTexture(renderContext, dataHash, camArrData.colorTextures[l], name, false); renderTexture) {
500 setupCameraArrayDepthTarget(renderContext, dataHash, renderTexture, l, camArrData, name);
501 setupCameraArrayRenderTarget(renderContext, dataHash, renderTexture, l, 1, 1, camArrComp, camArrData, name);
502 setupCameraArrayDraw(renderContext, manager, renderList, dataHash, renderTexture, l, camArrData, camComp, name, Cogs::hashSequence(instanceKey, static_cast<intptr_t>(l)));
503 }
504 }
505 break;
506
507 case CameraArrayData::TextureMode::None: [[fallthrough]];
508 default:
509 assert(false && "Illegal enum");
510 break;
511 }
512
513 }
514 }
515
516 size_t cameraInstanceKey(const CameraData& camData, bool isMainCamera)
517 {
518 return Cogs::hashSequence(Cogs::hash("Camera"), isMainCamera ? 1 : 0, reinterpret_cast<intptr_t>(&camData));
519 }
520
521 void setupCameraPipelines(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
522 {
523 std::string nameTemp;
524 const bool defaultTargetRender = renderContext->context->variables->get("renderer.defaultTarget.render", true);
525
526 const CameraComponent* mainCamComp = renderContext->context->cameraSystem->getMainCamera();
527 for(const CameraComponent& camComp : renderContext->context->cameraSystem->pool) {
528
529 if ((camComp.flags & CameraFlags::EnableRender) == 0) { continue; }
530
531
532 const Entity* entity = camComp.getContainer();
533 Cogs::StringView name = entity->getName();
534 if (name.empty()) {
535 nameTemp = "Camera, entity=" + std::to_string(entity->getId());
536 name = nameTemp;
537 }
538
539 // If offscreen, grab offscreen texture
540 size_t dataHash = 0;
541 Cogs::StringView pipeline = camComp.renderPipeline.empty() ? forwardPath : camComp.renderPipeline;
542 RenderTexture* renderTexture = nullptr;
543
544 bool isMainCamera = &camComp == mainCamComp;
545 if (isMainCamera) {
546
547 // The debug gui use this to override the current pipeline without actually changing it when setting visualization mode
548 if (const Variable* var1 = renderContext->context->variables->get("renderer.pipeline.override"); var1 && !var1->isEmpty() && !var1->getValue().empty()) {
549 pipeline = var1->getCString();
550 }
551
552 // Main camera pipeline can be overriden with renderer.pipeline. Should be removed.
553 else if (const Variable* var2 = renderContext->context->variables->get("renderer.pipeline"); var2 && !var2->isEmpty() && !var2->getValue().empty()) {
554 pipeline = var2->getCString();
555 }
556
557 }
558 else {
559 if (camComp.renderTexture) {
560 renderTexture = getRenderTexture(renderContext, dataHash, camComp.renderTexture, name, true);
561 }
562 }
563
564 if (renderTexture == nullptr && defaultTargetRender == false) {
565 // Rendering to default render target disabled, skip setting up a pipeline.
566 continue;
567 }
568
569 PipeInitFunc initFunc = [&name, &pipeline, renderTexture, renderContext](PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)
570 {
571 instance->name = name.to_string();
572 if (!renderTexture) {
573 resources.add(renderContext->defaultRenderTarget, "Cogs.BackBuffer");
574 }
575 if (wasCreated) {
576 LOG_DEBUG(logger, "Creating main camera pipeline for %s: %.*s", instance->name.c_str(), StringViewFormat(pipeline));
577 }
578 };
579
580 const CameraData& camData = renderContext->context->cameraSystem->getData(&camComp);
581 setupCameraPipeline(renderContext, manager,
582 cameraInstanceKey(camData, isMainCamera), dataHash, pipeline, (renderTexture ? 300 : 400) + camComp.stackOrder,
583 initFunc, &camComp, &camData, renderTexture, renderList);
584 }
585 }
586
587}
588
589
590
591
592void Cogs::Core::RenderPipelineManager::initialize(RenderTaskContext* renderContext)
593{
594 renderList = renderContext->resources->createRenderList();
595 renderList->setName("Cogs.RenderList");
596
597 std::list<std::unique_ptr<SubContext>> subContexts; // dummy for call to createRenderTask, not actually used when creating a GenerateList task.
598
599 RenderTaskDefinition taskDefinition = {};
600 taskDefinition.name = "Cogs.GenerateList";
601 taskDefinition.type = "GenerateList";
602 generateListTask = createRenderTask(renderContext, taskDefinition, subContexts, &expressionContextRoot);
603 generateListTask->output.add(renderList);
604 generateListTask->flags = RenderTaskFlags::Persistent;
605}
606
607void Cogs::Core::RenderPipelineManager::cleanup(RenderTaskContext* renderContext)
608{
609 if (!renderList) return;
610
611 assert(generateListTask);
612 destroyRenderTask(renderContext, generateListTask);
613 generateListTask = nullptr;
614
615 assert(!renderList->isOwned());
616 renderContext->renderer->getRenderResources().releaseResource(renderList);
617 renderContext->renderer->getRenderResources().destroyResource(renderList);
618 renderList = nullptr;
619
620 for (auto& pipeline : pipelineInstances) {
621 releasePipelineInstance(renderContext, pipeline.second.get());
622 }
623}
624
626Cogs::Core::RenderPipelineManager::definitionByPath(Context* context, const StringView& path)
627{
628 auto insert = [&](size_t hash, StringView myPath) -> RenderPipelineDefinition&
629 {
630 auto p1 = definitions.emplace(hash, std::make_unique<RenderPipelineDefinition>(parsePipeline(context, myPath)));
631 assert(p1.first->second.get());
632 auto& definition = *(p1.first->second.get());
633 if (!definition.name.empty()) {
634 /*auto p2 = */nameMap.insert({ definition.name, hash });
635 if (!p1.second) LOG_WARNING(logger, "Collision in nameMap when inserting %s, hash=%zd.", definition.name.c_str(), hash);
636 }
637
638 for (auto& generator : definition.generators) {
639 if (!definition.name.empty()) {
640 size_t genHash = StringView(generator.name).hash();
641
642 auto pp1 = definitions.emplace(genHash, std::make_unique<RenderPipelineDefinition>(generator));
643 assert(pp1.first->second.get());
644 if (!pp1.second) LOG_WARNING(logger, "Collision in hashes when inserting %s, hash=%zd.", generator.name.c_str(), genHash);
645
646 auto pp2 = nameMap.emplace(generator.name, genHash);
647 if (!pp2.second) LOG_WARNING(logger, "Collision in nameMap when inserting %s, hash=%zd.", generator.name.c_str(), genHash);
648 }
649 }
650
651 return definition;
652 };
653
654 auto queryLoc = path.find("?");
655 const auto trimmedPath = queryLoc == StringView::NoPosition ? path : path.substr(0, queryLoc);
656
657 size_t hash1 = trimmedPath.hash();
658 if (auto it = definitions.find(hash1); it != definitions.end()) {
659 return *(it->second.get());
660 }
661
662 auto& def1 = insert(hash1, trimmedPath);
663
664 // Import dependencies. Start with copy of import.
665 // Particular processing order doesn't matter here, so we don't care to reverse stack fill.
666 std::vector<StringParameter> importStack = def1.imports;
667
668 while (!importStack.empty())
669 {
670 const StringParameter item = importStack.back();
671 importStack.pop_back();
672
673 size_t hash2 = StringView(item.value).hash();
674 if (!definitions.contains(hash2)) {
675 const auto& def2 = insert(hash2, item.value);
676 for (auto& importValue : def2.imports) {
677 importStack.push_back(importValue);
678 }
679 }
680
681 nameMap.insert({ item.key, hash2 });
682 }
683
684 for (const auto& it : definitions) {
685 assert(it.second.get());
686 }
687
688 return def1;
689}
690
691std::pair< Cogs::Core::PipelineInstance*, bool> Cogs::Core::RenderPipelineManager::instanceByKey(Context* context, size_t key)
692{
693 auto it = pipelineInstances.find(key);
694 if (it == pipelineInstances.end()) {
695 auto instance = pipelineInstances.emplace(key, std::make_unique<PipelineInstance>()).first->second.get();
696 instance->touched = context->time->getFrame();
697 return std::make_pair(instance, true);
698 }
699 auto* instance = it->second.get();
700 instance->touched = context->time->getFrame();
701 return std::make_pair(instance, false);
702}
703
704void Cogs::Core::RenderPipelineManager::setupPipeline(RenderTaskContext * renderContext)
705{
706 Context* context = renderContext->context;
707 uint32_t frame = context->time->getFrame();
708
709 renderList->viewportData = renderContext->cameraData;
710
711 expressionContextRoot.add("Cogs.Frame", frame);
712 expressionContextRoot.add("Cogs.Time", renderContext->context->time->getAnimationTime());
713 expressionContextRoot.add("Cogs.TimeDelta", renderContext->context->time->getAnimationTimeDelta());
714 expressionContextRoot.add("Cogs.BackBuffer.width", renderContext->renderer->getSize().x);
715 expressionContextRoot.add("Cogs.BackBuffer.height", renderContext->renderer->getSize().y);
716 expressionContextRoot.add("Cogs.Mouse.x", (float)context->getDefaultView()->refMouse().getState().position.x);
717 expressionContextRoot.add("Cogs.Mouse.y", (float)context->getDefaultView()->refMouse().getState().position.y);
718
719 renderContext->expressionContext = &expressionContextRoot;
720
721 setupAuxPipelines(renderContext, this, renderList);
722 setupExtraPipelineRuns(renderContext, this, renderList);
723 setupLightPipelines(renderContext, this, renderList);
724 setupCameraArrayPipelines(renderContext, this, renderList);
725 setupCameraPipelines(renderContext, this, renderList);
726}
727
728void Cogs::Core::RenderPipelineManager::initializeFrame(RenderTaskContext * renderContext)
729{
730 Context* context = renderContext->context;
731 const uint32_t frame = context->time->getFrame();
732
733 // Remove unused instances.
734 // Note that pipelines doesn't necessarily own the resources it is updating,
735 // so we don't want to update stale resources. Live pipelines get their
736 // 'touched' property set each frame, so if a pipeline is not touched, it is
737 // assumed to be dead.
738 SmallVector<size_t,50> killList;
739 for (const auto& it : pipelineInstances) {
740 if (it.second->touched != frame) {
741 releasePipelineInstance(renderContext, it.second.get());
742 LOG_DEBUG(logger, "Killing pipeline %s.", it.second->name.c_str());
743 killList.push_back(it.first);
744 }
745 }
746 for (const auto& it : killList) {
747 pipelineInstances.erase(it);
748 }
749
750 assert(currentPipeline.empty());
751
752 currentPipeline.push_back(generateListTask);
753
754 // Generate sorted list of pipeline instances to generate global draw list.
755 static std::vector<PipelineInstance*> instances;
756 instances.reserve(pipelineInstances.size());
757 for (auto& it : pipelineInstances) {
758 instances.push_back(it.second.get());
759 }
760 std::sort(instances.begin(), instances.end(), [](const PipelineInstance* a, const PipelineInstance* b) { return a->priority < b->priority; });
761
762 for (PipelineInstance* instance : instances) {
763 for (auto & sc : instance->pipeline.subContexts) {
764 sc->pullVariables(context);
765 }
766 for (RenderTask* task : instance->pipeline.tasks) {
767 currentPipeline.push_back(task);
768 }
769 }
770
771 // Update resources of mainCamera first.
772 for (PipelineInstance* instance : instances) {
773 if (instance->renderTarget == nullptr) {
774 for (RenderTaskResource& r : instance->pipeline.resources.resources) {
775 renderContext->resources->updateResource(r.resource);
776 }
777 }
778 }
779 for (PipelineInstance* instance : instances) {
780 for (RenderTaskResource& r : instance->pipeline.updateResources.resources) {
781 renderContext->resources->updateResource(r.resource);
782 }
783 if (instance->renderTarget != nullptr) {
784 for (RenderTaskResource& r : instance->pipeline.resources.resources) {
785 renderContext->resources->updateResource(r.resource);
786 }
787 }
788 }
789 instances.clear();
790
791}
792
793void Cogs::Core::RenderPipelineManager::applyPipeline(RenderTaskContext * renderContext)
794{
795 auto frame = renderContext->context->time->getFrame();
796
797 for (auto & task : currentPipeline) {
798 if (task->frameMod != 0) {
799 if ((frame % task->frameMod) != task->frameOffset) {
800 continue;
801 }
802 }
803 task->apply(renderContext);
804 }
805}
806
807void Cogs::Core::RenderPipelineManager::cleanupFrame(RenderTaskContext * /*renderContext*/)
808{
809 currentPipeline.clear();
810}
Vector with inline storage for up to Size elements, avoiding heap allocation for small collections.
Definition: SmallVector.h:460
class Entity * getContainer() const
Get the container currently owning this component instance.
Definition: Component.h:151
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
constexpr size_t getId() const noexcept
Get the unique identifier of this entity.
Definition: Entity.h:113
const std::string & getName() const noexcept
Get the name of this entity.
Definition: Entity.h:120
Multi-view: Render a set of related views into array texture layers.
uint32_t samples
Number of multi-sampling samples, a value of 0 or 1 implies no multi-sampling.
bool expectsSRGB
Does the colorTexture expect values in sRGB color space.
CameraFlags flags
Camera behavior flags.
std::string renderPipeline
Render pipeline to apply when rendering to texture. Defaults to the built-in forward rendering pipeli...
TextureHandle renderTexture
The render texture to output the rendered scene from the camera to.
std::vector< std::string > renderPipelineOptions
Extra options passed when creating the render pipeline,.
int32_t stackOrder
Specifies the ordering of cameras when rendering to the same render target, lower numbers render befo...
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Services > services
Services.
Definition: Context.h:174
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
std::unique_ptr< class Time > time
Time service instance.
Definition: Context.h:198
Defines a single light source and its behavior.
glm::vec2 getSize() const override
Get the output surface size of the renderer.
Definition: Renderer.h:44
virtual ICapabilities * getCapabilities()=0
Get a pointer to the capability management interface used to query the graphics device capability fla...
Log implementation class.
Definition: LogManager.h:140
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:50
constexpr bool empty() const noexcept
Check if the string is empty.
Definition: StringView.h:148
static constexpr size_t NoPosition
No position.
Definition: StringView.h:69
std::string to_string() const
String conversion method.
Definition: StringView.cpp:9
constexpr size_t hash() const noexcept
Get the hash code of the string.
Definition: StringView.h:226
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
constexpr size_t hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
Definition: HashSequence.h:8
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
Provides a context for evaluation of expressions.
Definition: Expressions.h:54
Defines calculated light data.
Definition: LightSystem.h:34
std::string name
Friendly name used for debugging.
RenderTexture * renderTarget
Render-target that this pipeline outputs to, nullptr for default rendertarget.
Runtime control variable.
Definition: Variables.h:27
virtual const GraphicsDeviceCapabilities & getDeviceCapabilities() const
Gets the device capabilities in a structure.
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
Definition: Flags.h:122