Cogs.Core
RenderTaskFactory.cpp
1#include "RenderTaskFactory.h"
2
3#include "Serialization/RenderPipelineReader.h"
4
5#include "Systems/Core/CameraSystem.h"
6
7#include "Services/Variables.h"
8#include "Context.h"
9
10#include "GenerateListTask.h"
11#include "FilterListTask.h"
12#include "RenderListTask.h"
13
14#include "DeferredLightingTask.h"
15#include "TransparencyRenderTask.h"
16#include "TransparencyMergeTask.h"
17#include "TransparencyTemporalUpscaleTask.h"
18#include "TransparencyUpscaleTask.h"
19
20#include "PostProcessTask.h"
21#include "ComputeTask.h"
22#include "MipLevelsTask.h"
23
24#include "ClearResourceTask.h"
25#include "CopyResourceTask.h"
26#include "ResolveResourceTask.h"
27#include "ReadbackTask.h"
28
29#include "Renderer/Renderer.h"
30#include "Renderer/RenderPipelineManager.h"
31
32#include "Foundation/Logging/Logger.h"
33#include "Foundation/Collections/Pool.h"
34
35namespace
36{
37 Cogs::Logging::Log logger = Cogs::Logging::getLogger("TaskFactory");
38}
39
40namespace Cogs
41{
42 namespace Core
43 {
44 std::unordered_map<std::string, TaskCreator> creators;
45 std::unordered_map<std::string, TaskDestroyer> destroyers;
46
47 template<typename ResourceType>
48 Collections::Pool<ResourceType> & getPool()
49 {
50 static Collections::Pool<ResourceType> pool(128, 128, MemBlockType::RenderResourceStorage);
51
52 return pool;
53 }
54
55 template<typename TaskType>
56 TaskType * createTask(RenderTaskContext * renderContext, bool initialize = true)
57 {
58 auto pool = &getPool<TaskType>();
59 auto task = pool->create();
60
61 task->deleter = [=]()
62 {
63 if (renderContext) {
64 task->cleanup(renderContext);
65 }
66
67 pool->destroy(task);
68 };
69
70 if (renderContext && initialize) {
71 task->initialize(renderContext);
72 }
73
74 return task;
75 }
76
77 void freeTask(RenderTask * task, RenderTaskContext * renderContext)
78 {
79 for (auto & d : destroyers) {
80 d.second(&task, renderContext);
81 if (!task) return;
82 }
83
84 if (task->deleter) {
85 task->deleter();
86 return;
87 }
88
89 LOG_ERROR(logger, "Render task could not be deleted.");
90 }
91
92 void setProcessTaskEffectParameter(RenderTaskContext* renderContext,
93 std::list<std::unique_ptr<SubContext>>& subContexts,
94 ExpressionContext * expressionContext,
95 ProcessTask* task,
96 const ParsedValue& parameter)
97 {
98 task->effectParameter = parameter;
99
100 task->scope = expressionContext;
101 for (const auto & ep : task->effectParameter.values) {
102 if (ep.key == "useVariables") {
103 if (task->scope == expressionContext) {
104 subContexts.push_back(std::make_unique<SubContext>(expressionContext));
105 task->scope = &subContexts.back()->expressionContext;
106 }
107 subContexts.back()->useVariables(renderContext->context, ep.values);
108 }
109 else if (ep.key == "setVariables") {
110 if (task->scope == expressionContext) {
111 subContexts.push_back(std::make_unique<SubContext>(expressionContext));
112 task->scope = &subContexts.back()->expressionContext;
113 }
114 subContexts.back()->setVariables(renderContext->context, ep.values);
115 }
116 else if (ep.key == "useComponentFields") {
117 if (task->scope == expressionContext) {
118 subContexts.push_back(std::make_unique<SubContext>(expressionContext));
119 task->scope = &subContexts.back()->expressionContext;
120 }
121 subContexts.back()->useComponentFields(renderContext, ep.values);
122 }
123 }
124
125 for (const auto & ep : task->effectParameter.values) {
126 if (ep.key == "properties") {
127 for (const auto & p : ep.values) {
128 if (p.key == "source") continue;
129
130 ProcessTaskProperty prop;
131 prop.definition = &p; // is pointer valid for lifetime of task?
132 prop.float4x4Value = p.float4x4Value;
133
134 for (const auto & q : p.expressions) {
135 //auto name = prefix + "." + p.key + "." + std::to_string(q.first);
136 prop.expressions.push_back({ q.first, task->scope->compile(q.second, "") });
137 }
138 switch (p.type) {
139 case ParsedDataType::Texture2D: {
140 SamplerState samplerState {
144 (p.texture.flags & ParsedValueTextureFlags::LinearSampler) != 0 ? SamplerState::MinMagMipLinear : SamplerState::MinMagMipPoint,
145 SamplerState::Never,
146 0,
147 { 0, 0, 0, 1 }
148 };
149 prop.texture.samplerName = Strings::get(Strings::add(p.key + "Sampler"));
150 prop.texture.samplerState = renderContext->states->getSamplerState(samplerState);
151 break;
152 }
153 default:
154 break;
155 }
156 task->properties.push_back(prop);
157 }
158 }
159 else if (ep.key == "groups" && ep.values.size() == 3) {
160 task->groups.expressions.clear();
161 for (size_t i = 0; i < 3; i++) {
162 auto * e = task->scope->compile(ep.values[i].value, "");
163 if (e) {
164 task->groups.expressions.push_back({ i, e });
165 }
166 }
167 }
168 else if (ep.key == "options") {
169 task->options.clear();
170 for (const auto& p : ep.values) {
171 if (p.type == ParsedDataType::String) {
172 task->options.emplace_back(p.key, p.value);
173 } else {
174 LOG_ERROR(logger, "Process task options should be of type string, key=\"%s\".", p.key.c_str());
175 }
176 }
177 }
178 }
179 }
180
182 {
183 uint32_t flags = 0;
184 BucketMask bucketMask = BucketMask::All;
185 StateChangeFlags stateChangeMask = StateChangeFlags::ChangeAll;
186 size_t permutationIndex = 0;
187 RenderLayers renderMask = RenderLayers::All;
188 SortFlags sortFlags = SortFlags::Default;
189 BlendMode blendMode = BlendMode::None;
190 DepthMode depthMode = DepthMode::Default;
191 DepthFunc depthFunc = DepthFunc::Less;
192
193 bool viewportFromTarget = true; // Use render target size as viewport instead of camera viewport.
194 bool temporalOffsets = false;
195 bool clearColor = true;
196 bool clearDepth = true;
197 bool discardColor = false;
198 bool discardDepth = false;
199 bool allowSelfDependency = false;
200 bool drawCallID = false;
201 bool blendModeSet = false;
202 };
203
204 RenderTaskParameters parseParameters(RenderTaskContext * renderContext,
205 const RenderTaskDefinition & taskDefinition,
206 const PipelineOptions& options)
207 {
208 RenderTaskParameters parameters;
209
210 { // Override renderTaskParameters from pipeline options
211 ParsedValue val;
212 for (const PreprocessorDefinition& option : options) {
213 switch (StringView(option.first).hash()) {
214 case Cogs::hash("clearColor"):
215 parseStringValue(option.second, val);
216 val.asBool(parameters.clearColor);
217 break;
218 case Cogs::hash("clearDepth"):
219 parseStringValue(option.second, val);
220 val.asBool(parameters.clearDepth);
221 break;
222 default:
223 break;
224 }
225 }
226 }
227
228 for (auto & p : taskDefinition.parameters) {
229
230 switch (StringView(p.key).hash()) {
231 case Cogs::hash("bucketMask"):
232 parameters.bucketMask = parseEnumFlags(p.value, BucketMask::All);
233 break;
234 case Cogs::hash("stateChangeMask"):
235 parameters.stateChangeMask = parseEnumFlags<StateChangeFlags>(p.value, StateChangeFlags::ChangeAll);
236 break;
237 case Cogs::hash("permutation"):
238 parameters.permutationIndex = renderContext->renderer->getEnginePermutations().getIndex(p.value);
239 break;
240 case Cogs::hash("blendMode"):
241 parameters.blendMode = parseEnum(p.value, parameters.blendMode);
242 parameters.blendModeSet = true;
243 break;
244 case Cogs::hash("depthMode"):
245 parameters.depthMode = parseEnum(p.value, parameters.depthMode);
246 break;
247 case Cogs::hash("depthFunc"):
248 parameters.depthFunc = parseEnum(p.value, parameters.depthFunc);
249 break;
250 case Cogs::hash("clear"):
251 p.asBool(parameters.clearColor);
252 parameters.clearDepth = parameters.clearColor;
253 break;
254 case Cogs::hash("clearDepth"):
255 p.asBool(parameters.clearDepth);
256 break;
257 case Cogs::hash("clearColor"):
258 p.asBool(parameters.clearColor);
259 break;
260 case Cogs::hash("discardDepth"):
261 p.asBool(parameters.discardDepth);
262 break;
263 case Cogs::hash("discardColor"):
264 p.asBool(parameters.discardColor);
265 break;
266 case Cogs::hash("viewportFromTarget"):
267 p.asBool(parameters.viewportFromTarget);
268 break;
269 case Cogs::hash("temporalOffsets"):
270 p.asBool(parameters.temporalOffsets);
271 break;
272 case Cogs::hash("allowSelfDependency"):
273 p.asBool(parameters.allowSelfDependency);
274 break;
275 case Cogs::hash("flags"):
276 parameters.flags = parseEnumFlags(p.value, RenderTaskFlags::None);
277 break;
278 case Cogs::hash("drawCallID"):
279 p.asBool(parameters.drawCallID);
280 break;
281 case Cogs::hash("renderMask"): {
282 parameters.renderMask = parseEnumFlags(p.value, RenderLayers::None);
283 break;
284 }
285 case Cogs::hash("sortFlags"):
286 parameters.sortFlags = parseEnumFlags<SortFlags>(p.value, parameters.sortFlags);
287 break;
288
289 default:
290 break;
291 }
292 }
293
294 return parameters;
295 }
296 }
297}
298
299Cogs::Core::RenderTask * Cogs::Core::createRenderTask(RenderTaskContext * renderContext,
300 const RenderTaskDefinition & taskDefinition,
301 std::list<std::unique_ptr<SubContext>>& subContexts,
302 ExpressionContext * expressionContext,
303 const PipelineOptions & options)
304{
305 RenderTask * task = nullptr;
306
307 auto parameters = parseParameters(renderContext, taskDefinition, options);
308
309 if (taskDefinition.type == "GenerateList") {
310 task = createTask<GenerateListTask>(renderContext);
311 task->initialize(renderContext);
312 } else if (taskDefinition.type == "Filter") {
313 FilterListTask* filterTask = createTask<FilterListTask>(renderContext);
314 filterTask->bucketMask = parameters.bucketMask;
315 filterTask->permutation = parameters.permutationIndex;
316 filterTask->drawCallID = parameters.drawCallID;
317 filterTask->renderMask = parameters.renderMask;
318 filterTask->sortFlags = parameters.sortFlags;
319 filterTask->blendMode = parameters.blendMode;
320 filterTask->blendModeSet = parameters.blendModeSet;
321
322 const CameraData* cameraData = &renderContext->context->cameraSystem->getMainCameraData();
323
324 if (renderContext->cameraData != cameraData) {
325 filterTask->viewportData = renderContext->cameraData;
326 } else {
327 // Main camera data should not be taken a direct dependency on.
328 filterTask->viewportData = nullptr;
329 }
330
331 task = filterTask;
332 } else if (taskDefinition.type == "RenderList") {
333 auto renderTask = createTask<RenderListTask>(renderContext, false);
334 renderTask->bucketMask = parameters.bucketMask;
335 renderTask->stateChangeMask = parameters.stateChangeMask;
336 renderTask->permutationIndex = parameters.permutationIndex;
337 renderTask->colorClear = parameters.clearColor;
338 renderTask->depthClear = parameters.clearDepth;
339 renderTask->discardColor = parameters.discardColor;
340 renderTask->discardDepth = parameters.discardDepth;
341 renderTask->viewportFromTarget = parameters.viewportFromTarget;
342 renderTask->temporalOffsets = parameters.temporalOffsets;
343 renderTask->blendMode = parameters.blendMode;
344 renderTask->depthMode = parameters.depthMode;
345 renderTask->depthFunc = parameters.depthFunc;
346 renderTask->initialize(renderContext);
347 task = renderTask;
348 } else if (taskDefinition.type == "DeferredLighting") {
349 auto defTask = createTask<DeferredLightingTask>(renderContext);
350 defTask->clear = parameters.clearColor;
351 defTask->clearToDefault = true;
352 task = defTask;
353 } else if (taskDefinition.type == "TransparencyRender") {
354 auto tTask = createTask<TransparencyRenderTask>(renderContext);
355 tTask->bucketMask = parameters.bucketMask;
356 tTask->viewportFromTarget = parameters.viewportFromTarget;
357 tTask->temporalOffsets = parameters.temporalOffsets;
358 task = tTask;
359 } else if (taskDefinition.type == "TransparencyMerge") {
360 task = createTask<TransparencyMergeTask>(renderContext);
361 } else if (taskDefinition.type == "TransparencyTemporalUpscale") {
362 task = createTask<TransparencyTemporalUpscaleTask>(renderContext);
363 } else if (taskDefinition.type == "TransparencyUpscale") {
364 task = createTask<TransparencyUpscaleTask>(renderContext);
365 } else if (taskDefinition.type == "PostProcess") {
366 PostProcessTask* pTask = createTask<PostProcessTask>(renderContext, false);
367 pTask->viewportFromTarget = parameters.viewportFromTarget;
368 pTask->clearColor = parameters.clearColor;
369 pTask->clearDepth = parameters.clearDepth;
370 for (auto & p : taskDefinition.parameters) {
371 if (p.key == "effect") {
372 setProcessTaskEffectParameter(renderContext, subContexts, expressionContext, pTask, p);
373 } else if (p.key == "blendMode") {
374 pTask->blendMode = parseEnum<BlendMode>(p.value, pTask->blendMode);
375 } else if (p.key == "writeColor") {
376 p.asBool(pTask->writeColor);
377 } else if (p.key == "writeDepth") {
378 p.asBool(pTask->writeDepth);
379 }
380 }
381
382 pTask->initialize(renderContext, taskDefinition);
383
384 task = pTask;
385 } else if (taskDefinition.type == "ClearResource") {
386 auto pTask = createTask<ClearResourceTask>(renderContext);
387
388 for (auto & p : taskDefinition.parameters) {
389 if (p.key == "destinationIndex") {
390 readSize_t(pTask->destinationIndex, expressionContext, taskDefinition.name, p);
391 }
392 else if (p.key == "fill") {
393 if (p.type == ParsedDataType::UInt) {
394 pTask->fill_float = false;
395 for(uint32_t i=0; i<4; i++)
396 pTask->fill.UValues[i] = p.uintValue;
397 }
398 else if (p.type == ParsedDataType::Float) {
399 pTask->fill_float = true;
400 for(uint32_t i=0; i<4; i++)
401 pTask->fill.FValues[i] = p.floatValue;
402 }
403 }
404 }
405
406 task = pTask;
407 } else if (taskDefinition.type == "CopyResource") {
408 auto pTask = createTask<CopyResourceTask>(renderContext);
409
410 for (auto & p : taskDefinition.parameters) {
411 if (p.key == "sourceIndex") {
412 readSize_t(pTask->sourceIndex, expressionContext, taskDefinition.name, p);
413 } else if (p.key == "destinationIndex") {
414 readSize_t(pTask->destinationIndex, expressionContext, taskDefinition.name, p);
415 }
416 }
417
418 task = pTask;
419 } else if (taskDefinition.type == "ResolveResource") {
420 task = createTask<ResolveResourceTask>(renderContext);
421 } else if (taskDefinition.type == "Readback") {
422 auto pTask = createTask<ReadbackTask>(renderContext);
423
424 for (auto & p : taskDefinition.parameters) {
425 if (p.key == "bufferIndex") {
426 readSize_t(pTask->bufferIndex, expressionContext, taskDefinition.name, p);
427 } else if (p.key == "size") {
428 readSize_t(pTask->size, expressionContext, taskDefinition.name, p);
429 } else if (p.key == "readbackKey") {
430 pTask->key = p.value;
431 }
432 }
433
434 task = pTask;
435 } else if (taskDefinition.type == "Compute") {
436 auto pTask = createTask<ComputeTask>(renderContext, false);
437
438 for (auto & p : taskDefinition.parameters) {
439 if (p.key == "effect") {
440 setProcessTaskEffectParameter(renderContext, subContexts, expressionContext, pTask, p);
441 }
442 }
443
444 pTask->initialize(renderContext, taskDefinition);
445
446 task = pTask;
447 } else if (taskDefinition.type == "MipLevels") {
448 auto pTask = createTask<MipLevelsTask>(renderContext, false);
449
450 for (auto & p : taskDefinition.parameters) {
451 if (p.key == "effect") {
452 setProcessTaskEffectParameter(renderContext, subContexts, expressionContext, pTask, p);
453 } else if (p.key == "firstLevel") {
454 readSize_t(pTask->firstLevel, expressionContext, std::to_string((size_t)(pTask)) + ".firstLevel", p);
455 } else if (p.key == "lastLevel") {
456 readSize_t(pTask->lastLevel, expressionContext, std::to_string((size_t)(pTask)) + ".lastLevel", p);
457 } else if (p.key == "blendMode") {
458 pTask->blendMode = parseEnum<BlendMode>(p.value, pTask->blendMode);
459 }
460 }
461 pTask->initialize(renderContext, taskDefinition);
462 task = pTask;
463 } else {
464 auto fIt = creators.find(taskDefinition.type);
465
466 if (fIt != creators.end()) {
467 task = fIt->second(renderContext, taskDefinition, options);
468 task->initialize(renderContext);
469 }
470 }
471
472 if (!task) {
473 LOG_ERROR(logger, "Could not create task of type %s.", taskDefinition.type.c_str());
474 return nullptr;
475 }
476
477 task->name = taskDefinition.name;
478 task->options = options;
479 task->flags = (RenderTaskFlags::ERenderTaskFlags)parameters.flags;
480 task->allowSelfDependency = parameters.allowSelfDependency;
481
482 return task;
483}
484
485void Cogs::Core::destroyRenderTask(RenderTaskContext* renderContext, RenderTask* task)
486{
487 freeTask(task, renderContext);
488}
489
490void Cogs::Core::addTaskType(const StringView & key, TaskCreator creator, TaskDestroyer destroyer)
491{
492 creators[key.to_string()] = creator;
493 destroyers[key.to_string()] = destroyer;
494}
495
496void Cogs::Core::freeTasks(std::vector<RenderTask *> & tasks, RenderTaskContext * renderContext)
497{
498 for (auto & task : tasks) {
499 if (!(task->flags & RenderTaskFlags::Persistent)) {
500 freeTask(task, renderContext);
501 }
502 }
503}
Log implementation class.
Definition: LogManager.h:140
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr size_t hash() const noexcept
Get the hash code of the string.
Definition: StringView.h:200
BlendMode
Defines blending modes for rendering.
@ None
No blending enabled for opaque shapes, defaults to Blend for transparent shapes.
DepthMode
Defines common depth stencil modes.
@ Default
Default, depth test & write enabled.
DepthFunc
Defines common depth functions.
RenderLayers
Contains common render layers.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
std::pair< std::string, std::string > PreprocessorDefinition
Preprocessor definition.
Definition: IEffects.h:10
Stores the parsed output of a key/value pair.
Definition: Parsing.h:40
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17
@ MinMagMipPoint
Point sampling for both minification and magnification.
Definition: SamplerState.h:33
@ MinMagMipLinear
Linear sampling for both minification and magnification.
Definition: SamplerState.h:35