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);
300 expressionContext->add(r.name +
".width", 0.0);
301 expressionContext->add(r.name +
".height", 0.0);
303 renderTexture->width = { expressionContext, r.name +
".width", std::to_string(renderTexture->width.get(0)), 0 };
304 renderTexture->height = { expressionContext, r.name +
".height", std::to_string(renderTexture->height.get(0)), 0 };
306 for (
auto& p : r.parameters) {
307 const size_t code = StringView(p.key).hash();
313 case ParsedDataType::Int4:
314 for (glm::length_t i = 0; i < 4; i++) { renderTexture->clearColor[i] =
static_cast<float>(p.int4Value[i]); }
315 renderTexture->clearColorSet =
true;
317 case ParsedDataType::UInt4:
318 for (glm::length_t i = 0; i < 4; i++) { renderTexture->clearColor[i] =
static_cast<float>(p.uint4Value[i]); }
319 renderTexture->clearColorSet =
true;
321 case ParsedDataType::Float4:
322 for (glm::length_t i = 0; i < 4; i++) { renderTexture->clearColor[i] = p.float4Value[i]; }
323 renderTexture->clearColorSet =
true;
326 LOG_ERROR(logger,
"Texture clear color was not a 4-component vector of int nor floats");
334 std::string name = r.name +
"." + p.key;
335 addVariable(expressionContext, name, p);
338 readSize_t(renderTexture->width, expressionContext, r.name, p);
341 readSize_t(renderTexture->height, expressionContext, r.name, p);
344 readSize_t(renderTexture->levels, expressionContext, r.name, p);
347 readSize_t(renderTexture->layers, expressionContext, r.name, p);
350 readSize_t(renderTexture->faces, expressionContext, r.name, p);
353 readSize_t(renderTexture->samples, expressionContext, r.name, p);
356 renderTexture->sizeSource = pipelineResources.get(prefix + p.value)->resource;
365 return renderTexture;
368 bool handleResourceAlias(
const RenderResourceDefinition & r,
const std::string & resourceName,
const std::string & prefix, RenderTaskResources & pipelineResources)
370 if (r.alias.size()) {
371 auto aliasedResource = pipelineResources.get(prefix + r.alias);
373 if (aliasedResource) {
374 pipelineResources.add(aliasedResource, resourceName);
376 if (r.type != RenderResourceType::RenderTarget) {
377 LOG_ERROR(logger,
"Error aliasing resource %s to %s.", r.name.c_str(), resourceName.c_str());
391 const Cogs::Core::PipelineOptions & options,
393 const std::vector<ParsedValue>& attributes,
394 const std::string & prefix,
395 const std::string & prefixOuter)
397 pipelineResources.resources.reserve(100);
399 auto globalResources = pipelineResources;
401 for (
auto & option : pipelineDefinition.options) {
402 pipeline.dependsVariables.push_back(option.setting);
406 if (!pipelineDefinition.useVariables.empty()
407 || !pipelineDefinition.setVariables.empty()
408 || !pipelineDefinition.useComponentFields.empty()) {
409 pipeline.subContexts.push_back(std::make_unique<SubContext>(expressionContext));
410 expressionContext = &pipeline.subContexts.back()->expressionContext;
412 for (
auto & r : pipelineDefinition.inputs) {
413 auto resource = pipelineResources.get(prefix + r.key);
415 if (resource->type == RenderResourceType::RenderTexture || resource->type == RenderResourceType::RenderTarget) {
417 const auto preStrip = prefixOuter.length();
418 StringView nameOuter = resource->name;
419 if (preStrip <= nameOuter.length() && nameOuter.substr(0, preStrip).compare(prefixOuter) == 0) {
420 nameOuter = nameOuter.substr(preStrip);
423 if (resource->type == RenderResourceType::RenderTexture || nameOuter ==
"Cogs.BackBuffer") {
424 expressionContext->link(expressionContext, nameOuter.to_string() +
".width", r.key +
".width");
425 expressionContext->link(expressionContext, nameOuter.to_string() +
".height", r.key +
".height");
430 pipeline.subContexts.back()->useVariables(renderContext->context, pipelineDefinition.useVariables);
431 pipeline.subContexts.back()->setVariables(renderContext->context, pipelineDefinition.setVariables);
432 pipeline.subContexts.back()->useComponentFields(renderContext, pipelineDefinition.useComponentFields);
435 for (
auto & r : pipelineDefinition.resources) {
436 auto resourceName = prefix + r.name;
438 if (!evaluateCondition(renderContext->context, r.condition, pipelineDefinition, attributes))
continue;
440 if (handleResourceAlias(r, resourceName, prefix, pipelineResources))
continue;
443 case RenderResourceType::RenderBuffer:
445 auto renderBuffer = createRenderBuffer(renderContext->resources, resourceName, r, expressionContext);
447 pipelineResources.add(renderBuffer, resourceName);
448 pipeline.resources.add(renderBuffer, resourceName);
451 case RenderResourceType::RenderList:
453 auto renderList = renderContext->resources->createRenderList();
454 renderList->setName(resourceName);
456 pipelineResources.add(renderList, resourceName);
457 pipeline.resources.add(renderList, resourceName);
460 case RenderResourceType::RenderTexture:
462 auto renderTexture = createRenderTexture(renderContext, pipeline, pipelineResources, resourceName, prefix, r, expressionContext);
464 pipelineResources.add(renderTexture, resourceName);
465 pipeline.resources.add(renderTexture, resourceName);
473 for (
auto & r : pipelineDefinition.resources) {
474 auto resourceName = prefix + r.name;
476 if (!evaluateCondition(renderContext->context, r.condition, pipelineDefinition, attributes))
continue;
478 if (handleResourceAlias(r, resourceName, prefix, pipelineResources))
continue;
480 if (r.type == RenderResourceType::RenderTarget) {
481 auto renderTarget = renderContext->resources->createRenderTarget();
482 renderTarget->setName(resourceName);
484 auto depth = r.getParameter(
"depth",
"");
486 auto depthTexture = pipelineResources.get(prefix + depth->value);
489 auto format = parseTextureFormat(depth->value);
491 if (format != TextureFormat::Unknown) {
492 auto depthName = resourceName +
":DepthTexture";
493 auto depth_rtex = renderContext->resources->createRenderTexture();
494 depth_rtex->setName(depthName);
495 depth_rtex->description.flags = TextureFlags::DepthBuffer | TextureFlags::Texture;
496 depth_rtex->description.format = format;
498 renderTarget->depth = depth_rtex;
500 pipelineResources.add(depth_rtex, depthName);
501 pipeline.resources.add(depth_rtex, depthName);
504 renderTarget->depth = depthTexture->renderTexure;
508 auto clearColor = r.getParameter(
"clearColor",
"");
509 if (clearColor && clearColor->type == ParsedDataType::Float4) {
510 renderTarget->clearColors = { clearColor->float4Value };
513 auto levels = r.getParameter(
"levels",
"");
515 readSize_t(renderTarget->levels, expressionContext, r.name + std::to_string((
size_t)renderTarget), *levels);
518 size_t textureIndex = 0;
519 for (
auto & t : r.textures) {
520 auto name = prefix + t;
521 auto textureResource = pipelineResources.get(name);
523 if (textureResource) {
524 renderTarget->textures.push_back(textureResource->renderTexure);
526 auto format = parseTextureFormat(t);
528 if (format != TextureFormat::Unknown) {
529 auto textureName = resourceName +
":Texture" + std::to_string(textureIndex++);
531 auto texture = renderContext->resources->createRenderTexture();
532 texture->setName(textureName);
533 texture->description.format = format;
534 texture->description.flags = TextureFlags::Texture | TextureFlags::RenderTarget;
536 renderTarget->textures.push_back(texture);
538 pipelineResources.add(texture, textureName);
539 pipeline.resources.add(texture, textureName);
541 LOG_ERROR(logger,
"Could not resolve resource %s.", name.c_str());
546 pipelineResources.add(renderTarget, resourceName);
547 pipeline.resources.add(renderTarget, resourceName);
551 std::list<RenderTask *> tasks;
553 for (
auto & t : pipelineDefinition.tasks) {
554 if (!evaluateCondition(renderContext->context, t.condition, pipelineDefinition, attributes))
continue;
556 if (t.type[0] ==
'$') {
559 for (
auto & p : renderContext->renderer->getPipelineManager()->nameMap) {
560 if (p.first == t.type.substr(1)) {
561 auto & g = renderContext->renderer->getPipelineManager()->definitionByKey(renderContext->context, p.second);
563 auto gPrefix = prefix + t.name;
565 PipelineOptions gOptions;
567 for (
auto & o : t.options) {
568 gOptions.push_back({ o.key, o.value });
571 RenderTaskResources gResources;
572 for (
size_t i = 0; i < 2; ++i) {
573 gResources.add(&pipelineResources.resources[i]);
576 for (
auto & input : g.inputs) {
577 bool handled =
false;
578 for (
auto & ii : t.inputs) {
579 if (ii.key == input.key) {
580 const auto resourceName = prefix + ii.value;
581 if (
auto * resource = pipelineResources.get(resourceName); resource) {
582 gResources.add(resource, gPrefix + input.key);
585 LOG_ERROR(logger,
"In task %s.%s, failed to find input resource '%s'.", pipelineDefinition.name.c_str(), t.name.c_str(), resourceName.c_str());
591 LOG_ERROR(logger,
"In task %s.%s, un-matched input key '%s'", pipelineDefinition.name.c_str(), t.name.c_str(), input.key.c_str());
596 for (
auto & output : g.outputs) {
597 bool handled =
false;
598 for (
auto & oo : t.outputs) {
600 if ((g.outputs.size() == 1 && t.outputs.size() == 1 && oo.key.empty())
601 || (oo.key == output.key)) {
602 const auto resourceName = prefix + oo.value;
603 if (
auto * resource = pipelineResources.get(resourceName); resource) {
604 gResources.add(resource, gPrefix + output.key);
607 LOG_ERROR(logger,
"In task %s.%s, failed to find output resource '%s'.", pipelineDefinition.name.c_str(), t.name.c_str(), resourceName.c_str());
611 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());
615 LOG_ERROR(logger,
"In task %s.%s, un-matched output key '%s'", pipelineDefinition.name.c_str(), t.name.c_str(), output.key.c_str());
621 createPipelineRecursive(renderContext, g, gResources, pipeline, gOptions, expressionContext, attributes, gPrefix, prefix);
623 for (
auto tt : pipeline.tasks) {
628 pipeline.tasks.clear();
635 LOG_ERROR(logger,
"Failed to resolve pipeline type '%s'", t.type.c_str());
640 tt.name = prefix + tt.name;
642 auto task = createRenderTask(renderContext, tt, pipeline.subContexts, expressionContext, options);
645 LOG_ERROR(logger,
"Pipeline task chain incomplete.");
649 task->name = tt.name;
650 for (
auto & d : t.dependencies) {
651 auto dependencyName = prefix + d;
653 auto r = pipelineResources.get(dependencyName);
656 task->dependencies.push_back(r->name);
658 task->dependencies.push_back(prefix + d);
662 for (
auto & input : t.inputs) {
663 const std::string resourceName = prefix + input.value;
664 if (RenderTaskResource* resource = pipelineResources.get(resourceName); resource) {
665 if (!input.key.empty()) {
666 task->input.add(pipelineResources.get(prefix + input.value), input.key);
669 task->input.add(pipelineResources.get(prefix + input.value), input.value);
673 LOG_ERROR(logger,
"Unknown pipeline resource '%s'", resourceName.c_str());
678 for (
auto & output : t.outputs) {
679 if (output.key.size()) {
680 task->output.add(pipelineResources.get(prefix + output.value), output.key);
682 task->output.add(pipelineResources.get(prefix + output.value), output.value);
686 tasks.push_back(task);
690 for (
auto t : tasks) {
691 pipeline.tasks.push_back(t);
697 evaluateTaskDependencies(tasks, globalResources, pipeline);
703 size_t pipelineHash(RenderTaskContext * renderContext,
const RenderPipeline & pipeline)
707 for (
const std::string& vars : pipeline.dependsVariables) {
708 hashValue = hash(renderContext->context->variables->get(vars, StringView()), hashValue);
714bool Cogs::Core::createPipeline(RenderTaskContext * renderContext,
715 const RenderPipelineDefinition & pipelineDefinition,
716 RenderTaskResources & pipelineResources,
717 RenderPipeline & pipeline,
718 const StringView & path,
719 const CameraData* cameraData,
720 const PipelineOptions & options,
721 const std::string & prefix)
724 pipeline.expressionContext.clear();
725 pipeline.expressionContext.inherit(renderContext->expressionContext);
727 std::vector<ParsedValue> attributes;
728 auto queryLoc = path.find(
"?");
729 if (queryLoc != StringView::NoPosition) {
730 parseQueryString(attributes, path.substr(queryLoc + 1));
733 const CameraData* cameraDataSaved = renderContext->cameraData;
735 renderContext->cameraData = cameraData;
738 ExpressionContext* expressionContextSave = renderContext->expressionContext;
739 renderContext->expressionContext = &pipeline.expressionContext;
742 if (createPipelineRecursive(renderContext, pipelineDefinition, pipelineResources, pipeline, options, &pipeline.expressionContext, attributes, prefix,
"")) {
743 pipeline.dependsVariables.sort();
744 pipeline.dependsVariables.unique();
745 pipeline.hash = pipelineHash(renderContext, pipeline);
749 renderContext->expressionContext = expressionContextSave;
750 renderContext->cameraData = cameraDataSaved;
754bool Cogs::Core::isPipelineFresh(RenderTaskContext * renderContext,
const RenderPipeline & pipeline)
756 return pipeline.hash == pipelineHash(renderContext, pipeline);
759void Cogs::Core::releasePipeline(RenderTaskContext * renderContext, RenderPipeline & pipeline)
761 for (
auto & r : pipeline.resources.resources) {
762 if (!r.resource->isOwned()) {
763 renderContext->resources->releaseResource(r.resource);
764 renderContext->resources->destroyResource(r.resource);
766 renderContext->resources->releaseResource(r.resource);
770 pipeline.resources.resources.clear();
771 pipeline.resourceHandles.clear();
773 freeTasks(pipeline.tasks, renderContext);
774 pipeline.tasks.clear();
776 pipeline.subContexts.clear();
777 pipeline.expressionContext.clear();
778 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.