1#include "RenderPipelineFactory.h"
5#include "Renderer/Renderer.h"
6#include "Renderer/RenderPipelineManager.h"
8#include "Resources/Texture.h"
9#include "Resources/TextureManager.h"
11#include "Tasks/RenderTask.h"
13#include "Utilities/Parsing.h"
15#include "RenderResources.h"
17#include "RenderTexture.h"
18#include "RenderTarget.h"
19#include "RenderBuffer.h"
20#include "RenderList.h"
22#include "Foundation/HashSequence.h"
23#include "Foundation/Logging/Logger.h"
32 bool evaluateCondition(Context * context,
33 const StringView & condition,
34 const RenderPipelineDefinition & pipelineDefinition,
35 const std::vector<ParsedValue>& attributes)
37 thread_local static std::vector<StringView> tokens;
40 if (!condition.size())
return true;
43 split(condition,
"$=", tokens);
45 assert((tokens.size() % 2 == 0) &&
"Token array must be multiple of two");
47 bool totalConditionMet =
true;
49 for (
size_t i = 0; i < tokens.size(); i += 2) {
50 bool conditionMet =
false;
52 const auto key = tokens[i];
53 const auto expected = tokens[i + 1];
55 for (
auto & o : pipelineDefinition.options) {
60 for (
const auto & attribute : attributes) {
61 if (key != StringView(attribute.key))
continue;
64 if (StringView(attribute.value) == expected) {
71 if (!o.values.empty()) {
72 def = o.values.front();
75 if (!handled && !o.setting.empty()) {
77 const auto setting = context->variables->get(o.setting, def);
78 if (!setting.empty()) {
79 if (setting == expected) {
86 if (!handled && !def.empty()) {
88 if (def == expected) {
96 totalConditionMet = totalConditionMet && conditionMet;
98 return totalConditionMet;
101 void evaluateTaskDependencies(std::list<RenderTask *> tasks,
const RenderTaskResources & globalResources, RenderPipeline & pipeline)
103 auto findResource = [&](
const RenderTask * task,
const RenderTaskResource & input,
bool allow_self_dependency) ->
bool
105 auto globalResource = globalResources.get(input.name);
107 if (globalResource) {
111 if (input.resource->isPersistent()) {
115 if(allow_self_dependency){
117 for (
auto & output : t->output.resources) {
118 if (output.resource->getName() == input.resource->getName()) {
120 }
else if (input.type == RenderResourceType::RenderTexture && output.type == RenderResourceType::RenderTarget) {
121 for (
auto & texture : output.renderTarget->textures) {
122 if (texture->getName() == input.resource->getName()) {
126 if (output.renderTarget->depth) {
127 if (output.renderTarget->depth->getName() == input.resource->getName()) {
134 for (
auto & t : pipeline.tasks) {
135 for (
auto & output : t->output.resources) {
136 if (output.resource->getName() == input.resource->getName()) {
138 }
else if (input.type == RenderResourceType::RenderTexture && output.type == RenderResourceType::RenderTarget) {
139 for (
auto & texture : output.renderTarget->textures) {
140 if (texture->getName() == input.resource->getName()) {
144 if (output.renderTarget->depth) {
145 if (output.renderTarget->depth->getName() == input.resource->getName()) {
156 auto findDependency = [&](
const RenderTask * task,
const StringView & d,
bool allow_self_dependency) ->
bool
158 if(allow_self_dependency){
159 for(
auto & o : task->output.resources) {
165 for (
auto & t : pipeline.tasks) {
170 for (
auto & o : t->output.resources) {
180 auto findPrerequisites = [&](
const RenderTask * task) ->
bool
182 bool foundResources =
true;
183 bool foundDependencies =
true;
184 bool allowSelfDependency = task->allowSelfDependency;
186 for (
auto & input : task->input.resources) {
187 foundResources &= findResource(task, input, allowSelfDependency);
190 for (
auto & d : task->dependencies) {
191 foundDependencies &= findDependency(task, d, allowSelfDependency);
194 return foundResources && foundDependencies;
197 while (tasks.size()) {
198 auto nextTaskIt = std::find_if(tasks.begin(), tasks.end(), findPrerequisites);
200 if (nextTaskIt != tasks.end()) {
201 pipeline.tasks.push_back(*nextTaskIt);
202 tasks.erase(nextTaskIt);
204 LOG_ERROR(logger,
"Unable to determine processing order for the following tasks:");
205 for (
auto task : tasks) {
206 LOG_ERROR(logger,
" %s", task->name.c_str());
213 bool addVariable(ExpressionContext * expressionContext, StringView name,
const ParsedValue & p)
215 if (p.type == ParsedDataType::Float) {
216 expressionContext->add(name, p.floatValue);
217 }
else if (p.type == ParsedDataType::Int) {
218 expressionContext->add(name, p.intValue);
219 }
else if (p.type == ParsedDataType::UInt) {
220 expressionContext->add(name, p.uintValue);
221 }
else if (p.type == ParsedDataType::Bool) {
222 expressionContext->add(name, p.boolValue ? 1 : 0);
223 }
else if (p.type == ParsedDataType::String) {
224 expressionContext->add(name, parseDouble(p.value, 0));
226 LOG_ERROR(logger,
"Invalid variable type for %.*s", StringViewFormat(name));
233 RenderBuffer * createRenderBuffer(RenderResources * renderResources,
const std::string & resourceName,
const RenderResourceDefinition & r, ExpressionContext * expressionContext)
235 auto renderBuffer = renderResources->createRenderBuffer();
236 renderBuffer->setName(resourceName);
237 if (r.getParameter(
"persistent",
false)) {
238 renderBuffer->setPersistent();
241 renderBuffer->parameters = r.parameters;
243 for (
auto & p : r.parameters) {
244 auto name = r.name +
"." + p.key;
245 addVariable(expressionContext, name, p);
247 const size_t code = StringView(p.key).hash();
251 readSize_t(renderBuffer->elementSize, expressionContext, r.name, p);
254 readSize_t(renderBuffer->elementCount, expressionContext, r.name, p);
257 readSize_t(renderBuffer->bufferSize, expressionContext, r.name, p);
260 readSize_t(renderBuffer->bufferCount, expressionContext, r.name, p);
270 RenderTexture * createRenderTexture(RenderTaskContext * renderContext,
271 RenderPipeline & pipeline,
272 RenderTaskResources & pipelineResources,
273 const std::string & resourceName,
274 const std::string & prefix,
275 const RenderResourceDefinition & r,
276 ExpressionContext * expressionContext)
278 auto texture = renderContext->context->textureManager->create();
279 texture->setName(resourceName);
280 texture->setResident();
282 pipeline.resourceHandles.push_back(texture);
284 auto renderTexture = renderContext->resources->createRenderTexture();
285 texture->attachResource(renderTexture);
287 renderTexture->setName(resourceName);
288 renderTexture->setResource(texture.resolve());
290 if (r.getParameter(
"persistent",
false)) {
291 renderTexture->setPersistent();
293 renderTexture->setOwned();
295 auto format = r.getParameter(
"format",
"R8G8B8A8_UNORM_SRGB");
296 renderTexture->description.format = parseTextureFormat(format ? format->value :
"R8G8B8A8_UNORM_SRGB");
298 renderTexture->description.samples = r.getParameter(
"samples", 1);
301 expressionContext->add(r.name +
".width", 0.0);
302 expressionContext->add(r.name +
".height", 0.0);
304 renderTexture->width = { expressionContext, r.name +
".width", std::to_string(renderTexture->width.get(0)), 0 };
305 renderTexture->height = { expressionContext, r.name +
".height", std::to_string(renderTexture->height.get(0)), 0 };
307 for (
auto& p : r.parameters) {
308 const size_t code = StringView(p.key).hash();
314 renderTexture->sizeSource = pipelineResources.get(prefix + p.value)->resource;
319 case ParsedDataType::Int4:
320 for (glm::length_t i = 0; i < 4; i++) { renderTexture->clearColor[i] =
static_cast<float>(p.int4Value[i]); }
321 renderTexture->clearColorSet =
true;
323 case ParsedDataType::UInt4:
324 for (glm::length_t i = 0; i < 4; i++) { renderTexture->clearColor[i] =
static_cast<float>(p.uint4Value[i]); }
325 renderTexture->clearColorSet =
true;
327 case ParsedDataType::Float4:
328 for (glm::length_t i = 0; i < 4; i++) { renderTexture->clearColor[i] = p.float4Value[i]; }
329 renderTexture->clearColorSet =
true;
332 LOG_ERROR(logger,
"Texture clear color was not a 4-component vector of int nor floats");
340 std::string name = r.name +
"." + p.key;
341 addVariable(expressionContext, name, p);
344 readSize_t(renderTexture->width, expressionContext, r.name, p);
347 readSize_t(renderTexture->height, expressionContext, r.name, p);
350 readSize_t(renderTexture->levels, expressionContext, r.name, p);
353 readSize_t(renderTexture->layers, expressionContext, r.name, p);
356 readSize_t(renderTexture->faces, expressionContext, r.name, p);
359 readSize_t(renderTexture->samples, expressionContext, r.name, p);
368 return renderTexture;
371 bool handleResourceAlias(
const RenderResourceDefinition & r,
const std::string & resourceName,
const std::string & prefix, RenderTaskResources & pipelineResources)
373 if (r.alias.size()) {
374 auto aliasedResource = pipelineResources.get(prefix + r.alias);
376 if (aliasedResource) {
377 pipelineResources.add(aliasedResource, resourceName);
379 if (r.type != RenderResourceType::RenderTarget) {
380 LOG_ERROR(logger,
"Error aliasing resource %s to %s.", r.name.c_str(), resourceName.c_str());
394 const Cogs::Core::PipelineOptions & options,
396 const std::vector<ParsedValue>& attributes,
397 const std::string & prefix,
398 const std::string & prefixOuter)
400 pipelineResources.resources.reserve(100);
402 auto globalResources = pipelineResources;
404 for (
auto & option : pipelineDefinition.options) {
405 pipeline.dependsVariables.push_back(option.setting);
408 if (!pipelineDefinition.useVariables.empty() ||
409 !pipelineDefinition.setVariables.empty() ||
410 !pipelineDefinition.useComponentFields.empty()) {
411 pipeline.subContexts.push_back(std::make_unique<SubContext>(expressionContext));
412 expressionContext = &pipeline.subContexts.back()->expressionContext;
413 pipeline.subContexts.back()->useVariables(renderContext->context, pipelineDefinition.useVariables);
414 pipeline.subContexts.back()->setVariables(renderContext->context, pipelineDefinition.setVariables);
415 pipeline.subContexts.back()->useComponentFields(renderContext, pipelineDefinition.useComponentFields);
418 for (
auto& r : pipelineDefinition.inputs) {
419 auto resource = pipelineResources.get(prefix + r.key);
421 if (resource->type == RenderResourceType::RenderTexture || resource->type == RenderResourceType::RenderTarget) {
423 const auto preStrip = prefixOuter.length();
424 StringView nameOuter = resource->name;
425 if (preStrip <= nameOuter.length() && nameOuter.substr(0, preStrip).compare(prefixOuter) == 0) {
426 nameOuter = nameOuter.substr(preStrip);
429 if (resource->type == RenderResourceType::RenderTexture || nameOuter ==
"Cogs.BackBuffer") {
430 expressionContext->link(expressionContext, nameOuter.to_string() +
".width", r.key +
".width");
431 expressionContext->link(expressionContext, nameOuter.to_string() +
".height", r.key +
".height");
435 for (
auto& r : pipelineDefinition.outputs) {
436 auto resource = pipelineResources.get(prefix + r.key);
438 if (resource->type == RenderResourceType::RenderTexture || resource->type == RenderResourceType::RenderTarget) {
440 const auto preStrip = prefixOuter.length();
441 StringView nameOuter = resource->name;
442 if (preStrip <= nameOuter.length() && nameOuter.substr(0, preStrip).compare(prefixOuter) == 0) {
443 nameOuter = nameOuter.substr(preStrip);
446 expressionContext->link(expressionContext, nameOuter.to_string() +
".width", r.key +
".width");
447 expressionContext->link(expressionContext, nameOuter.to_string() +
".height", r.key +
".height");
451 for (
auto & r : pipelineDefinition.resources) {
452 auto resourceName = prefix + r.name;
454 if (!evaluateCondition(renderContext->context, r.condition, pipelineDefinition, attributes))
continue;
456 if (handleResourceAlias(r, resourceName, prefix, pipelineResources))
continue;
459 case RenderResourceType::RenderBuffer:
461 auto renderBuffer = createRenderBuffer(renderContext->resources, resourceName, r, expressionContext);
463 pipelineResources.add(renderBuffer, resourceName);
464 pipeline.resources.add(renderBuffer, resourceName);
467 case RenderResourceType::RenderList:
469 auto renderList = renderContext->resources->createRenderList();
470 renderList->setName(resourceName);
472 pipelineResources.add(renderList, resourceName);
473 pipeline.resources.add(renderList, resourceName);
476 case RenderResourceType::RenderTexture:
478 auto renderTexture = createRenderTexture(renderContext, pipeline, pipelineResources, resourceName, prefix, r, expressionContext);
480 pipelineResources.add(renderTexture, resourceName);
481 pipeline.resources.add(renderTexture, resourceName);
489 for (
auto & r : pipelineDefinition.resources) {
490 auto resourceName = prefix + r.name;
492 if (!evaluateCondition(renderContext->context, r.condition, pipelineDefinition, attributes))
continue;
494 if (handleResourceAlias(r, resourceName, prefix, pipelineResources))
continue;
496 if (r.type == RenderResourceType::RenderTarget) {
497 auto renderTarget = renderContext->resources->createRenderTarget();
498 renderTarget->setName(resourceName);
500 bool has_expression =
false;
501 auto depth = r.getParameter(
"depth",
"");
503 auto depthTexture = pipelineResources.get(prefix + depth->value);
506 auto format = parseTextureFormat(depth->value);
508 if (format != TextureFormat::Unknown) {
509 auto depthName = resourceName +
":DepthTexture";
510 auto depth_rtex = renderContext->resources->createRenderTexture();
511 depth_rtex->setName(depthName);
512 depth_rtex->description.flags = TextureFlags::DepthBuffer | TextureFlags::Texture;
513 depth_rtex->description.format = format;
515 renderTarget->depth = depth_rtex;
517 pipelineResources.add(depth_rtex, depthName);
518 pipeline.resources.add(depth_rtex, depthName);
521 renderTarget->depth = depthTexture->renderTexure;
522 expressionContext->link(expressionContext, depth->value +
".width", r.name +
".width");
523 expressionContext->link(expressionContext, depth->value +
".height", r.name +
".height");
524 has_expression =
true;
528 auto clearColor = r.getParameter(
"clearColor",
"");
529 if (clearColor && clearColor->type == ParsedDataType::Float4) {
530 renderTarget->clearColors = { clearColor->float4Value };
533 auto levels = r.getParameter(
"levels",
"");
535 readSize_t(renderTarget->levels, expressionContext, r.name + std::to_string((
size_t)renderTarget), *levels);
538 size_t textureIndex = 0;
539 for (
auto & t : r.textures) {
540 auto name = prefix + t;
541 auto textureResource = pipelineResources.get(name);
543 if (textureResource) {
544 renderTarget->textures.push_back(textureResource->renderTexure);
545 if (!has_expression) {
546 expressionContext->link(expressionContext, t +
".width", r.name +
".width");
547 expressionContext->link(expressionContext, t +
".height", r.name +
".height");
548 has_expression =
true;
551 auto format = parseTextureFormat(t);
553 if (format != TextureFormat::Unknown) {
554 auto textureName = resourceName +
":Texture" + std::to_string(textureIndex++);
556 auto texture = renderContext->resources->createRenderTexture();
557 texture->setName(textureName);
558 texture->description.format = format;
559 texture->description.flags = TextureFlags::Texture | TextureFlags::RenderTarget;
561 renderTarget->textures.push_back(texture);
563 pipelineResources.add(texture, textureName);
564 pipeline.resources.add(texture, textureName);
566 LOG_ERROR(logger,
"Could not resolve resource %s.", name.c_str());
571 pipelineResources.add(renderTarget, resourceName);
572 pipeline.resources.add(renderTarget, resourceName);
576 std::list<RenderTask *> tasks;
578 for (
auto & t : pipelineDefinition.tasks) {
579 if (!evaluateCondition(renderContext->context, t.condition, pipelineDefinition, attributes))
continue;
581 if (t.type[0] ==
'$') {
584 for (
auto & p : renderContext->renderer->getPipelineManager()->nameMap) {
585 if (p.first == t.type.substr(1)) {
586 auto & g = renderContext->renderer->getPipelineManager()->definitionByKey(renderContext->context, p.second);
588 auto gPrefix = prefix + t.name;
590 PipelineOptions gOptions;
592 for (
auto & o : t.options) {
593 gOptions.push_back({ o.key, o.value });
596 RenderTaskResources gResources;
597 for (
size_t i = 0; i < 2; ++i) {
598 gResources.add(&pipelineResources.resources[i]);
601 for (
auto & input : g.inputs) {
602 bool handled =
false;
603 for (
auto & ii : t.inputs) {
604 if (ii.key == input.key) {
605 const auto resourceName = prefix + ii.value;
606 if (
auto * resource = pipelineResources.get(resourceName); resource) {
607 gResources.add(resource, gPrefix + input.key);
610 LOG_ERROR(logger,
"In task %s.%s, failed to find input resource '%s'.", pipelineDefinition.name.c_str(), t.name.c_str(), resourceName.c_str());
616 LOG_ERROR(logger,
"In task %s.%s, un-matched input key '%s'", pipelineDefinition.name.c_str(), t.name.c_str(), input.key.c_str());
621 for (
auto & output : g.outputs) {
622 bool handled =
false;
623 for (
auto & oo : t.outputs) {
625 if ((g.outputs.size() == 1 && t.outputs.size() == 1 && oo.key.empty())
626 || (oo.key == output.key)) {
627 const auto resourceName = prefix + oo.value;
628 if (
auto * resource = pipelineResources.get(resourceName); resource) {
629 gResources.add(resource, gPrefix + output.key);
632 LOG_ERROR(logger,
"In task %s.%s, failed to find output resource '%s'.", pipelineDefinition.name.c_str(), t.name.c_str(), resourceName.c_str());
636 LOG_WARNING(logger,
"In task %s.%s, unhandled output key='%s', value='%s'", pipelineDefinition.name.c_str(), t.name.c_str(), oo.key.c_str(), oo.value.c_str());
640 LOG_ERROR(logger,
"In task %s.%s, un-matched output key '%s'", pipelineDefinition.name.c_str(), t.name.c_str(), output.key.c_str());
646 createPipelineRecursive(renderContext, g, gResources, pipeline, gOptions, expressionContext, attributes, gPrefix, prefix);
648 for (
auto tt : pipeline.tasks) {
653 pipeline.tasks.clear();
660 LOG_ERROR(logger,
"Failed to resolve pipeline type '%s'", t.type.c_str());
665 tt.name = prefix + tt.name;
667 auto task = createRenderTask(renderContext, tt, pipeline.subContexts, expressionContext, options);
670 LOG_ERROR(logger,
"Pipeline task chain incomplete.");
674 task->name = tt.name;
675 for (
auto & d : t.dependencies) {
676 auto dependencyName = prefix + d;
678 auto r = pipelineResources.get(dependencyName);
681 task->dependencies.push_back(r->name);
683 task->dependencies.push_back(prefix + d);
687 for (
auto & input : t.inputs) {
688 const std::string resourceName = prefix + input.value;
689 if (RenderTaskResource* resource = pipelineResources.get(resourceName); resource) {
690 if (!input.key.empty()) {
691 task->input.add(pipelineResources.get(prefix + input.value), input.key);
694 task->input.add(pipelineResources.get(prefix + input.value), input.value);
698 LOG_ERROR(logger,
"Unknown pipeline resource '%s'", resourceName.c_str());
703 for (
auto & output : t.outputs) {
704 if (output.key.size()) {
705 task->output.add(pipelineResources.get(prefix + output.value), output.key);
707 task->output.add(pipelineResources.get(prefix + output.value), output.value);
711 tasks.push_back(task);
715 for (
auto t : tasks) {
716 pipeline.tasks.push_back(t);
722 evaluateTaskDependencies(tasks, globalResources, pipeline);
728 size_t pipelineHash(RenderTaskContext * renderContext,
const RenderPipeline & pipeline)
732 for (
const std::string& vars : pipeline.dependsVariables) {
733 hashValue = hash(renderContext->context->variables->get(vars, StringView()), hashValue);
739bool Cogs::Core::createPipeline(RenderTaskContext * renderContext,
740 const RenderPipelineDefinition & pipelineDefinition,
741 RenderTaskResources & pipelineResources,
742 RenderPipeline & pipeline,
743 const StringView & path,
744 const CameraData* cameraData,
745 const PipelineOptions & options,
746 const std::string & prefix)
749 pipeline.expressionContext.clear();
750 pipeline.expressionContext.inherit(renderContext->expressionContext);
752 std::vector<ParsedValue> attributes;
753 auto queryLoc = path.find(
"?");
754 if (queryLoc != StringView::NoPosition) {
755 parseQueryString(attributes, path.substr(queryLoc + 1));
758 const CameraData* cameraDataSaved = renderContext->cameraData;
760 renderContext->cameraData = cameraData;
763 ExpressionContext* expressionContextSave = renderContext->expressionContext;
764 renderContext->expressionContext = &pipeline.expressionContext;
767 if (createPipelineRecursive(renderContext, pipelineDefinition, pipelineResources, pipeline, options, &pipeline.expressionContext, attributes, prefix,
"")) {
768 pipeline.dependsVariables.sort();
769 pipeline.dependsVariables.unique();
770 pipeline.hash = pipelineHash(renderContext, pipeline);
774 renderContext->expressionContext = expressionContextSave;
775 renderContext->cameraData = cameraDataSaved;
779bool Cogs::Core::isPipelineFresh(RenderTaskContext * renderContext,
const RenderPipeline & pipeline)
781 return pipeline.hash == pipelineHash(renderContext, pipeline);
784void Cogs::Core::releasePipeline(RenderTaskContext * renderContext, RenderPipeline & pipeline)
786 for (
auto & r : pipeline.resources.resources) {
787 if (!r.resource->isOwned()) {
788 renderContext->resources->releaseResource(r.resource);
789 renderContext->resources->destroyResource(r.resource);
791 renderContext->resources->releaseResource(r.resource);
795 pipeline.resources.resources.clear();
796 pipeline.resourceHandles.clear();
798 freeTasks(pipeline.tasks, renderContext);
799 pipeline.tasks.clear();
801 pipeline.subContexts.clear();
802 pipeline.expressionContext.clear();
803 pipeline.dependsVariables.clear();
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Provides a context for evaluation of expressions.
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
@ RenderTarget
The texture can be used as a render target and drawn into.
@ Texture
Texture usage, see Default.