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->description.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
229 // Set up shadow map render pipline
230 // --------------------------------
231
232 if (wasCreated || instance->dataHash != dataHash || !isPipelineFresh(renderContext, instance->pipeline)) {
233 releasePipelineInstance(renderContext, instance);
234 instance->dataHash = dataHash;
235
236 for (size_t viewportIndex = 0; viewportIndex < lightData.maxViewports; viewportIndex++) {
237 const size_t arrayIndex = viewportIndex + lightData.arrayOffset;
238
239 instance->pipeline.updateResources.add(renderTexture->renderTargets[arrayIndex]);
240
241 RenderTaskResources resources;
242 resources.add(renderTexture->renderTargets[arrayIndex], "Cogs.BackBuffer");
243 resources.add(renderList, "Cogs.RenderList");
244
245 createPipeline(renderContext, manager->definitionByPath(renderContext->context, shadowPath), resources, instance->pipeline, shadowPath, &lightData.lightCameraData[viewportIndex], instance->options);
246
247 for (size_t taskIndex = viewportIndex * 2; taskIndex < viewportIndex * 2 + 2; ++taskIndex) {
248 instance->pipeline.tasks[taskIndex]->frameMod = 0;// lightData.frameMod[viewportIndex];
249 instance->pipeline.tasks[taskIndex]->frameOffset = 0;// lightData.frameOffset[viewportIndex];
250 if (lightData.shadowUpdate == ShadowUpdate::Static || lightData.shadowUpdate == ShadowUpdate::StaticPartial) {
251 instance->pipeline.tasks[taskIndex]->flags = (RenderTaskFlags::ERenderTaskFlags)((int)instance->pipeline.tasks[taskIndex]->flags | (int)RenderTaskFlags::Static);
252 }
253 }
254 }
255 if (!wasCreated) LOG_DEBUG(logger, "Updated light pipeline");
256 manager->dirty = true;
257 }
258 }
259
260 void setupAuxPipelines(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
261 {
262 std::vector<const Variable*> vars;
263 renderContext->context->variables->getMatchingVariables(vars, "renderer.auxiliaryPipelines.");
264
265 for (const auto * var : vars) {
266 if (var->getValue().empty()) continue;
267
268 std::vector<Cogs::StringView> tokens;
269 split(var->getValue(), ":", tokens);
270 if (tokens.empty() || 2 < tokens.size()) {
271 LOG_ERROR_ONCE(logger, "Failed to tokenize %.*s: '%.*s'", StringViewFormat(var->getName()), StringViewFormat(var->getValue()));
272 continue;
273 }
274
275 size_t dataHash = 0;
276 size_t instanceKey = Cogs::hashSequence(Cogs::hash("AuxPipeline"), var->getName().hash());
277 Cogs::StringView pipeline = tokens[0];
278 int priority = 0;
279 if (1 < tokens.size()) {
280 priority = std::stoi(tokens[1].to_string());
281 }
282
283 PipeInitFunc initFunc = [var, &pipeline](PipelineInstance* instance, RenderTaskResources& /*resources*/, bool wasCreated)
284 {
285 instance->name = var->getName().to_string();
286 instance->options.push_back( { "Pass", "Offscreen" });
287 LOG_DEBUG(logger, "%s aux pipeline run for %s: %.*s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), StringViewFormat(pipeline));
288 };
289
290 setupCameraPipeline(renderContext, manager,
291 instanceKey, dataHash, pipeline, priority,
292 initFunc, nullptr, nullptr, nullptr, renderList);
293 }
294 }
295
296 void setupExtraPipelineRuns(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
297 {
298 PipelineService* pipelineService = renderContext->context->services->getService<PipelineService>();
299 for (PipelineRun* run : pipelineService->runs) {
300 if (run->enabled && !run->pipeline.empty()) {
301
302 size_t dataHash = 0;
303 size_t instanceKey = Cogs::hashSequence(run->name.hash(), reinterpret_cast<intptr_t>(run->cameraData));
304 RenderTexture* renderTexture = nullptr;
305 if (run->renderTexture) {
306 renderTexture = getRenderTexture(renderContext, dataHash, run->renderTexture, run->name, true);
307 }
308
309 PipeInitFunc initFunc = [run, renderTexture, renderContext](PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)
310 {
311 instance->name = run->name.to_string();
312 instance->options.push_back( { "Pass", "Offscreen" });
313 if (!renderTexture) {
314 resources.add(renderContext->defaultRenderTarget, "Cogs.BackBuffer");
315 instance->options.push_back({ "Pass", "Onscreen" });
316 }
317 LOG_DEBUG(logger, "%s extra pipeline run for %s: %s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), run->pipeline.c_str());
318 };
319
320 setupCameraPipeline(renderContext, manager,
321 instanceKey, dataHash, run->pipeline, run->priority,
322 initFunc, nullptr, run->cameraData, renderTexture, renderList);
323 }
324 }
325 }
326
327 void setupLightPipelines(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
328 {
329 for (auto & light : renderContext->context->lightSystem->pool) {
330 auto & lightData = renderContext->context->lightSystem->getData(&light);
331
332 if (!lightData.enabled || !lightData.castShadows) continue;
333
334 setupLightPipeline(renderContext, manager, light, lightData, lightData.shadowTexture, renderList);
335 }
336 }
337
338 void setupCameraArrayRenderTarget(RenderTaskContext* renderContext,
339 size_t& dataHash, RenderTexture* renderTexture, size_t targetIndex,
340 uint32_t multiViewCount, uint32_t multiViewSamples,
341 const CameraArrayComponent& camArrComp, CameraArrayData& camArrData,
342 const Cogs::StringView name)
343 {
344 if (1 < multiViewCount) {
345 camArrData.passOptions = camArrData.camData.passOptions ? *camArrData.camData.passOptions : RenderPassOptions{};
346 camArrData.camData.passOptions = &camArrData.passOptions;
347 camArrData.passOptions.multiViews = multiViewCount;
348 }
349
350 if (!renderTexture->renderTargets[targetIndex]) {
351 RenderTarget* renderTarget = renderContext->resources->createRenderTarget();
352 renderTarget->setName(name.to_string() + " MV-RT[" + std::to_string(targetIndex) + ":" + std::to_string(multiViewCount) + "]");
353 renderTarget->textures.push_back(renderTexture);
354 renderTarget->layerIndex = uint16_t(targetIndex);
355 renderTarget->depth = renderTexture->depthTexture;
356 renderTarget->depthLayerIndex = uint16_t(targetIndex);
357 if (1 < multiViewCount) {
358 renderTarget->multiViewBaseIndex = 0;
359 renderTarget->multiViewCount = static_cast<uint8_t>(multiViewCount);
360 renderTarget->multiViewSamples = static_cast<uint8_t>(std::min(255u, multiViewSamples));
361 }
362 renderTarget->width = renderTexture->description.width;
363 renderTarget->height = renderTexture->description.height;
364 renderTarget->samples = renderTexture->description.samples;
365 renderTarget->expectsSRGB = camArrComp.expectsSRGB;
366 renderTexture->renderTargets[targetIndex] = renderTarget;
367 }
368 dataHash = Cogs::hashSequence(dataHash, reinterpret_cast<intptr_t>(renderTexture->renderTarget));
369 }
370
371 void setupCameraArrayDepthTarget(RenderTaskContext* renderContext,
372 size_t& dataHash, RenderTexture* renderTexture, size_t targetIndex,
373 const CameraArrayData& camArrData, const Cogs::StringView name)
374 {
375 switch (camArrData.depthMode) {
376
377 case CameraArrayData::DepthMode::None:
378 break;
379
380 case CameraArrayData::DepthMode::Create:
381 if (!renderTexture->depthTexture) {
382 RenderTexture* depthTexture = renderContext->resources->createRenderTexture();
383 depthTexture->setName(name.to_string() + " DT");
384 depthTexture->description.target = renderTexture->description.target;
385 depthTexture->description.flags = Cogs::TextureFlags::DepthBuffer;
386 depthTexture->description.format = Cogs::TextureFormat::R32_TYPELESS;
387 depthTexture->layers = renderTexture->description.layers;
388 depthTexture->samples = renderTexture->description.samples;
389 depthTexture->sizeSource = renderTexture;
390 renderTexture->depthTexture = depthTexture;
391 }
392 dataHash = Cogs::hashSequence(dataHash, reinterpret_cast<intptr_t>(renderTexture->depthTexture));
393 break;
394
395 case CameraArrayData::DepthMode::Provided:
396 if (!renderTexture->depthTexture) {
397 renderTexture->depthTexture = getRenderTexture(renderContext, dataHash, camArrData.depthTextures[targetIndex], name, false);
398 }
399 dataHash = Cogs::hashSequence(dataHash, reinterpret_cast<intptr_t>(renderTexture->depthTexture));
400 break;
401
402 default:
403 assert(false && "Illegal enum");
404 break;
405 }
406 }
407
408 void setupCameraArrayDraw(RenderTaskContext* renderContext, RenderPipelineManager* manager, RenderList* renderList,
409 const size_t dataHash, RenderTexture* renderTexture, size_t targetIndex,
410 const CameraArrayData& camArrData, const CameraComponent& camComp,
411 const Cogs::StringView name, size_t instanceKey)
412 {
413 Cogs::StringView pipeline = camArrData.pipeline.empty() ? forwardPath : camArrData.pipeline;
414
415 PipeInitFunc initFunc = [targetIndex, &name, &pipeline, renderTexture, updateDepth = camArrData.depthMode == CameraArrayData::DepthMode::Create](PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)
416 {
417 instance->name = name.to_string();
418 // Update the resources owned by this pipeline
419 if (updateDepth) {
420 instance->pipeline.updateResources.add(renderTexture->depthTexture);
421 }
422 instance->pipeline.updateResources.add(renderTexture->renderTargets[targetIndex]);
423 resources.add(renderTexture->renderTargets[targetIndex], "Cogs.BackBuffer");
424
425 ExpressionContext& expressionContext = instance->pipeline.expressionContext;
426
427 expressionContext.add("Cogs.BackBuffer.width", renderTexture->description.width);
428 expressionContext.add("Cogs.BackBuffer.height", renderTexture->description.height);
429
430 //renderTexture->width = { &expressionContext, "Cogs.BackBuffer.width", std::to_string(renderTexture->description.width), 0 };
431 //renderTexture->height = { &expressionContext, "Cogs.BackBuffer.height", std::to_string(renderTexture->description.height), 0 };
432
433 LOG_DEBUG(logger, "%s offscreen camera pipeline for %s: %.*s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), StringViewFormat(pipeline));
434 };
435
436 setupCameraPipeline(renderContext, manager,
437 Cogs::hashSequence(instanceKey, targetIndex), dataHash, pipeline, 300 + camComp.stackOrder,
438 initFunc, &camComp, &camArrData.camData, nullptr, renderList);
439
440 }
441
442
443 void setupCameraArrayPipelines(RenderTaskContext* renderContext, RenderPipelineManager* manager, RenderList* renderList)
444 {
445 bool multiView = renderContext->device->getCapabilities()->getDeviceCapabilities().MultiView;
446
447 std::string name_;
448
449 for (const CameraArrayComponent& camArrComp : renderContext->context->cameraArraySystem->pool) {
450
451 CameraArrayData& camArrData = renderContext->context->cameraArraySystem->getData(&camArrComp);
452 if (camArrData.textureMode == CameraArrayData::TextureMode::None) {
453 continue;
454 }
455
456 Cogs::StringView name = camArrComp.getContainer()->getName();
457 if (name.empty()) {
458 const Entity* entity = camArrComp.getContainer();
459 name_ = "CameraArray@" + std::to_string(entity->getId());
460 name = name_;
461 }
462
463 assert(camArrData.camData.camera);
464 const CameraComponent& camComp = renderContext->context->cameraSystem->pool[camArrData.camData.camera.index];
465 size_t instanceKey = Cogs::hashSequence(Cogs::hash("CameraArrays"), reinterpret_cast<intptr_t>(&camArrData.camData));
466
467 switch (camArrData.textureMode) {
468
469 case CameraArrayData::TextureMode::TextureArray: {
470 size_t dataHash = 0;
471 if (RenderTexture* renderTexture = getRenderTexture(renderContext, dataHash, camArrData.colorTextures[0], name, false); renderTexture) {
472
473
474 setupCameraArrayDepthTarget(renderContext, dataHash, renderTexture, 0, camArrData, name);
475
476
477 if ((1 < renderTexture->description.layers) && multiView) {
478 setupCameraArrayRenderTarget(renderContext, dataHash, renderTexture, 0, renderTexture->description.layers, camArrComp.samples, camArrComp, camArrData, name);
479 setupCameraArrayDraw(renderContext, manager, renderList, dataHash, renderTexture, 0, camArrData, camComp, name, instanceKey);
480 }
481
482 else {
483 for (size_t l = 0; l < renderTexture->description.layers; l++) {
484 setupCameraArrayRenderTarget(renderContext, dataHash, renderTexture, l, 1, 1, camArrComp, camArrData, name);
485 }
486 for (size_t l = 0; l < renderTexture->description.layers; l++) {
487 setupCameraArrayDraw(renderContext, manager, renderList, dataHash, renderTexture, l, camArrData, camComp, name, Cogs::hashSequence(instanceKey, static_cast<intptr_t>(l)));
488 }
489 }
490 }
491 break;
492 }
493
494 case CameraArrayData::TextureMode::ArrayOfTextures:
495 for (size_t l = 0; l < camArrData.numViews; l++) {
496 size_t dataHash = 0;
497 if (RenderTexture* renderTexture = getRenderTexture(renderContext, dataHash, camArrData.colorTextures[l], name, false); renderTexture) {
498 setupCameraArrayDepthTarget(renderContext, dataHash, renderTexture, l, camArrData, name);
499 setupCameraArrayRenderTarget(renderContext, dataHash, renderTexture, l, 1, 1, camArrComp, camArrData, name);
500 setupCameraArrayDraw(renderContext, manager, renderList, dataHash, renderTexture, l, camArrData, camComp, name, Cogs::hashSequence(instanceKey, static_cast<intptr_t>(l)));
501 }
502 }
503 break;
504
505 case CameraArrayData::TextureMode::None: [[fallthrough]];
506 default:
507 assert(false && "Illegal enum");
508 break;
509 }
510
511 }
512 }
513
514 size_t cameraInstanceKey(const CameraData& camData, bool isMainCamera)
515 {
516 return Cogs::hashSequence(Cogs::hash("Camera"), isMainCamera ? 1 : 0, reinterpret_cast<intptr_t>(&camData));
517 }
518
519 void setupCameraPipelines(RenderTaskContext * renderContext, RenderPipelineManager* manager, RenderList* renderList)
520 {
521 std::string nameTemp;
522 const bool defaultTargetRender = renderContext->context->variables->get("renderer.defaultTarget.render", true);
523
524 const CameraComponent* mainCamComp = renderContext->context->cameraSystem->getMainCamera();
525 for(const CameraComponent& camComp : renderContext->context->cameraSystem->pool) {
526
527 if ((camComp.flags & CameraFlags::EnableRender) == 0) { continue; }
528
529
530 const Entity* entity = camComp.getContainer();
531 Cogs::StringView name = entity->getName();
532 if (name.empty()) {
533 nameTemp = "Camera, entity=" + std::to_string(entity->getId());
534 name = nameTemp;
535 }
536
537 // If offscreen, grab offscreen texture
538 size_t dataHash = 0;
539 Cogs::StringView pipeline = camComp.renderPipeline.empty() ? forwardPath : camComp.renderPipeline;
540 RenderTexture* renderTexture = nullptr;
541
542 bool isMainCamera = &camComp == mainCamComp;
543 if (isMainCamera) {
544
545 // The debug gui use this to override the current pipeline without actually changing it when setting visualization mode
546 if (const Variable* var1 = renderContext->context->variables->get("renderer.pipeline.override"); var1 && !var1->isEmpty() && !var1->getValue().empty()) {
547 pipeline = var1->getCString();
548 }
549
550 // Main camera pipeline can be overriden with renderer.pipeline. Should be removed.
551 else if (const Variable* var2 = renderContext->context->variables->get("renderer.pipeline"); var2 && !var2->isEmpty() && !var2->getValue().empty()) {
552 pipeline = var2->getCString();
553 }
554
555 }
556 else {
557 if (camComp.renderTexture) {
558 renderTexture = getRenderTexture(renderContext, dataHash, camComp.renderTexture, name, true);
559 }
560 }
561
562 if (renderTexture == nullptr && defaultTargetRender == false) {
563 // Rendering to default render target disabled, skip setting up a pipeline.
564 continue;
565 }
566
567 PipeInitFunc initFunc = [&name, &pipeline, renderTexture, renderContext](PipelineInstance* instance, RenderTaskResources& resources, bool wasCreated)
568 {
569 instance->name = name.to_string();
570 if (!renderTexture) {
571 resources.add(renderContext->defaultRenderTarget, "Cogs.BackBuffer");
572 }
573 LOG_DEBUG(logger, "%s main camera pipeline for %s: %.*s", wasCreated ? "Creating" : "Updating", instance->name.c_str(), StringViewFormat(pipeline));
574 };
575
576 const CameraData& camData = renderContext->context->cameraSystem->getData(&camComp);
577 setupCameraPipeline(renderContext, manager,
578 cameraInstanceKey(camData, isMainCamera), dataHash, pipeline, (renderTexture ? 300 : 400) + camComp.stackOrder,
579 initFunc, &camComp, &camData, renderTexture, renderList);
580 }
581 }
582
583}
584
585
586
587
588void Cogs::Core::RenderPipelineManager::initialize(RenderTaskContext* renderContext)
589{
590 renderList = renderContext->resources->createRenderList();
591 renderList->setName("Cogs.RenderList");
592
593 std::list<std::unique_ptr<SubContext>> subContexts; // dummy for call to createRenderTask, not actually used when creating a GenerateList task.
594
595 RenderTaskDefinition taskDefinition = {};
596 taskDefinition.name = "Cogs.GenerateList";
597 taskDefinition.type = "GenerateList";
598 generateListTask = createRenderTask(renderContext, taskDefinition, subContexts, &expressionContextRoot);
599 generateListTask->output.add(renderList);
600 generateListTask->flags = RenderTaskFlags::Persistent;
601}
602
603void Cogs::Core::RenderPipelineManager::cleanup(RenderTaskContext* renderContext)
604{
605 if (!renderList) return;
606
607 assert(generateListTask);
608 destroyRenderTask(renderContext, generateListTask);
609 generateListTask = nullptr;
610
611 assert(!renderList->isOwned());
612 renderContext->renderer->getRenderResources().releaseResource(renderList);
613 renderContext->renderer->getRenderResources().destroyResource(renderList);
614 renderList = nullptr;
615
616 for (auto& pipeline : pipelineInstances) {
617 releasePipelineInstance(renderContext, pipeline.second.get());
618 }
619}
620
622Cogs::Core::RenderPipelineManager::definitionByPath(Context* context, const StringView& path)
623{
624 auto insert = [&](size_t hash, StringView myPath) -> RenderPipelineDefinition&
625 {
626 auto p1 = definitions.emplace(hash, std::make_unique<RenderPipelineDefinition>(parsePipeline(context, myPath)));
627 assert(p1.first->second.get());
628 auto& definition = *(p1.first->second.get());
629 if (!definition.name.empty()) {
630 /*auto p2 = */nameMap.insert({ definition.name, hash });
631 if (!p1.second) LOG_WARNING(logger, "Collision in nameMap when inserting %s, hash=%zd.", definition.name.c_str(), hash);
632 }
633
634 for (auto& generator : definition.generators) {
635 if (!definition.name.empty()) {
636 size_t genHash = StringView(generator.name).hash();
637
638 auto pp1 = definitions.emplace(genHash, std::make_unique<RenderPipelineDefinition>(generator));
639 assert(pp1.first->second.get());
640 if (!pp1.second) LOG_WARNING(logger, "Collision in hashes when inserting %s, hash=%zd.", generator.name.c_str(), genHash);
641
642 auto pp2 = nameMap.emplace(generator.name, genHash);
643 if (!pp2.second) LOG_WARNING(logger, "Collision in nameMap when inserting %s, hash=%zd.", generator.name.c_str(), genHash);
644 }
645 }
646
647 return definition;
648 };
649
650 auto queryLoc = path.find("?");
651 const auto trimmedPath = queryLoc == StringView::NoPosition ? path : path.substr(0, queryLoc);
652
653 size_t hash1 = trimmedPath.hash();
654 if (auto it = definitions.find(hash1); it != definitions.end()) {
655 return *(it->second.get());
656 }
657
658 auto& def1 = insert(hash1, trimmedPath);
659
660 // Import dependencies. Start with copy of import.
661 // Particular processing order doesn't matter here, so we don't care to reverse stack fill.
662 std::vector<StringParameter> importStack = def1.imports;
663
664 while (!importStack.empty())
665 {
666 const StringParameter item = importStack.back();
667 importStack.pop_back();
668
669 size_t hash2 = StringView(item.value).hash();
670 if (!definitions.contains(hash2)) {
671 const auto& def2 = insert(hash2, item.value);
672 for (auto& importValue : def2.imports) {
673 importStack.push_back(importValue);
674 }
675 }
676
677 nameMap.insert({ item.key, hash2 });
678 }
679
680 for (const auto& it : definitions) {
681 assert(it.second.get());
682 }
683
684 return def1;
685}
686
687std::pair< Cogs::Core::PipelineInstance*, bool> Cogs::Core::RenderPipelineManager::instanceByKey(Context* context, size_t key)
688{
689 auto it = pipelineInstances.find(key);
690 if (it == pipelineInstances.end()) {
691 auto instance = pipelineInstances.emplace(key, std::make_unique<PipelineInstance>()).first->second.get();
692 instance->touched = context->time->getFrame();
693 return std::make_pair(instance, true);
694 }
695 auto* instance = it->second.get();
696 instance->touched = context->time->getFrame();
697 return std::make_pair(instance, false);
698}
699
700void Cogs::Core::RenderPipelineManager::setupPipeline(RenderTaskContext * renderContext)
701{
702 Context* context = renderContext->context;
703 uint32_t frame = context->time->getFrame();
704
705 renderList->viewportData = renderContext->cameraData;
706
707 expressionContextRoot.add("Cogs.Frame", frame);
708 expressionContextRoot.add("Cogs.Time", renderContext->context->time->getAnimationTime());
709 expressionContextRoot.add("Cogs.TimeDelta", renderContext->context->time->getAnimationTimeDelta());
710 expressionContextRoot.add("Cogs.BackBuffer.width", renderContext->renderer->getSize().x);
711 expressionContextRoot.add("Cogs.BackBuffer.height", renderContext->renderer->getSize().y);
712 expressionContextRoot.add("Cogs.Mouse.x", (float)context->getDefaultView()->refMouse().getState().position.x);
713 expressionContextRoot.add("Cogs.Mouse.y", (float)context->getDefaultView()->refMouse().getState().position.y);
714
715 renderContext->expressionContext = &expressionContextRoot;
716
717 setupAuxPipelines(renderContext, this, renderList);
718 setupExtraPipelineRuns(renderContext, this, renderList);
719 setupLightPipelines(renderContext, this, renderList);
720 setupCameraArrayPipelines(renderContext, this, renderList);
721 setupCameraPipelines(renderContext, this, renderList);
722}
723
724void Cogs::Core::RenderPipelineManager::initializeFrame(RenderTaskContext * renderContext)
725{
726 Context* context = renderContext->context;
727 const uint32_t frame = context->time->getFrame();
728
729 // Remove unused instances.
730 // Note that pipelines doesn't necessarily own the resources it is updating,
731 // so we don't want to update stale resources. Live pipelines get their
732 // 'touched' property set each frame, so if a pipeline is not touched, it is
733 // assumed to be dead.
734 SmallVector<size_t,50> killList;
735 for (const auto& it : pipelineInstances) {
736 if (it.second->touched != frame) {
737 releasePipelineInstance(renderContext, it.second.get());
738 LOG_DEBUG(logger, "Killing pipeline %s.", it.second->name.c_str());
739 killList.push_back(it.first);
740 }
741 }
742 for (const auto& it : killList) {
743 pipelineInstances.erase(it);
744 }
745
746 assert(currentPipeline.empty());
747
748 currentPipeline.push_back(generateListTask);
749
750 // Generate sorted list of pipeline instances to generate global draw list.
751 static std::vector<PipelineInstance*> instances;
752 instances.reserve(pipelineInstances.size());
753 for (auto& it : pipelineInstances) {
754 instances.push_back(it.second.get());
755 }
756 std::sort(instances.begin(), instances.end(), [](const PipelineInstance* a, const PipelineInstance* b) { return a->priority < b->priority; });
757
758 for (PipelineInstance* instance : instances) {
759 for (auto & sc : instance->pipeline.subContexts) {
760 sc->pullVariables(context);
761 }
762 for (RenderTask* task : instance->pipeline.tasks) {
763 currentPipeline.push_back(task);
764 }
765 }
766
767 // Update resources of mainCamera first.
768 for (PipelineInstance* instance : instances) {
769 if (instance->renderTarget == nullptr) {
770 for (RenderTaskResource& r : instance->pipeline.resources.resources) {
771 renderContext->resources->updateResource(r.resource);
772 }
773 }
774 }
775 for (PipelineInstance* instance : instances) {
776 for (RenderTaskResource& r : instance->pipeline.updateResources.resources) {
777 renderContext->resources->updateResource(r.resource);
778 }
779 if (instance->renderTarget != nullptr) {
780 for (RenderTaskResource& r : instance->pipeline.resources.resources) {
781 renderContext->resources->updateResource(r.resource);
782 }
783 }
784 }
785 instances.clear();
786
787}
788
789void Cogs::Core::RenderPipelineManager::applyPipeline(RenderTaskContext * renderContext)
790{
791 auto frame = renderContext->context->time->getFrame();
792
793 for (auto & task : currentPipeline) {
794 if (task->frameMod != 0) {
795 if ((frame % task->frameMod) != task->frameOffset) {
796 continue;
797 }
798 }
799 task->apply(renderContext);
800 }
801}
802
803void Cogs::Core::RenderPipelineManager::cleanupFrame(RenderTaskContext * /*renderContext*/)
804{
805 currentPipeline.clear();
806}
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:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr bool empty() const noexcept
Check if the string is empty.
Definition: StringView.h:122
static constexpr size_t NoPosition
No position.
Definition: StringView.h:43
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:200
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
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