Cogs.Core
RenderEffect.cpp
1#include "RenderEffect.h"
2
3#include "Rendering/IGraphicsDevice.h"
4#include "Rendering/IEffects.h"
5#include "Rendering/IBuffers.h"
6
7#include "Foundation/Logging/Logger.h"
8
9#include "Context.h"
10
11#include "Services/Variables.h"
12#include "Services/TaskManager.h"
13
14#include "Resources/Material.h"
15
16#include "Renderer.h"
17#include "RenderResources.h"
18
19namespace
20{
21 using namespace Cogs::Core;
22
23 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RenderEffect");
24
25 bool checkAndLogEffectLoad(Effect* effect, Cogs::EffectHandle effectHandle, Cogs::InputLayoutHandle inputHandle)
26 {
27 bool success = true;
28 if (!HandleIsValid(effectHandle)) {
29 LOG_ERROR(logger, "Error loading effect %s, definition:", effect->getName().data());
30 success = false;
31 }
32
33 if (success && !HandleIsValid(inputHandle)) { // Input layout will always fail if effect is invalid, no point in reporting
34 LOG_WARNING(logger, "Could not create input layout for %.*s, definition:", StringViewFormat(effect->getName()));
35 success = false;
36 }
37
38 if(!success) {
39 effect->definition.streamsLayout.logLayout(logger);
40 for (const Cogs::PreprocessorDefinition& d : effect->definition.definitions) {
41 LOG_DEBUG(logger, "+- #define %s %s", d.first.c_str(), d.second.c_str());
42 }
43 if (effect->definition.definitions.empty()) {
44 LOG_DEBUG(logger, "+- No #define's");
45 }
46 const ShaderDefinition& vs = effect->definition.vertexShader;
47 for (const ShaderInterfaceMemberDefinition& i : vs.shaderInterface.members) {
48 LOG_DEBUG(logger, " +- Vertex shader input semantic %.*s", StringViewFormat(ShaderInterfaceMemberDefinition::semanticNameString(i.semantic.name)));
49 }
50 if (effect->definition.vertexShader.shaderInterface.members.empty()) {
51 LOG_DEBUG(logger, "+- No vertex shader input semantics");
52 }
53 }
54
55 return success;
56 }
57
58
59}
60
61Cogs::Core::ActivationResult Cogs::Core::RenderEffect::update(Effect * effect, IGraphicsDevice * device, RenderResources * resources)
62{
63 if (effect->delayLoad) {
64 setDelayed();
65
67 }
68
69 Context* context = resources->getContext();
70 Renderer* renderer = resources->getRenderer();
71
72 const bool updateAsync = context->variables->get("resources.effects.updateAsync", false);
73
74 IEffects* effects = device->getEffects();
75 IBuffers* buffers = device->getBuffers();
76
77 if (haveQueued) {
78 effectHandle = queuedEffectHandle;
79 queuedEffectHandle = Cogs::EffectHandle::NoHandle;
80
81 inputHandle = queuedInputHandle;
82 queuedInputHandle = Cogs::InputLayoutHandle::NoHandle;
83
84 task = NoTask;
85 haveQueued = false;
86
87 if (!checkAndLogEffectLoad(effect, effectHandle, inputHandle)) {
88
89 if (effect->material) {
90 effect->material->setChanged();
91 }
92
93 setFailed();
94
96 }
97
98 if (effect->material) {
99 effect->material->setChanged();
100 }
101
102 incrementGeneration();
103 setActive();
104
106 }
107
108 if (updateAsync) {
109 if (!task.isValid()) {
110 Cogs::EffectDescription description = {};
111 description.flags = renderer->getEffectFlags();
112 description.vertexShader = effect->definition.vertexShader.loadPath;
113 description.hullShader = effect->definition.hullShader.loadPath;
114 description.domainShader = effect->definition.domainShader.loadPath;
115 description.geometryShader = effect->definition.geometryShader.loadPath;
116 description.pixelShader = effect->definition.pixelShader.loadPath;
117 description.definitions = effect->definition.definitions;
118
119 task = context->taskManager->enqueue(TaskManager::ResourceQueue, [this, effects, description, buffers, effect]()
120 {
121 queuedEffectHandle = effects->loadEffect(description);
122 queuedInputHandle = queuedEffectHandle
123 ? buffers->loadInputLayout(effect->definition.streamsLayout.vertexFormats,
124 effect->definition.streamsLayout.numStreams,
125 queuedEffectHandle)
127
128 haveQueued = true;
129 });
130
131 setDelayed();
132 }
133
135 }
136 else {
137 if (!HandleIsValid(effectHandle)) {
138 // First time through: issue the load. This is a cheap call even on backends that compile
139 // shaders asynchronously internally (e.g. WebGL, WebGPU) - the returned handle may not be
140 // usable yet, its readiness is tracked via checkEffect() below.
141 Cogs::EffectDescription description = {};
142 description.flags = renderer->getEffectFlags();
143 description.name = effect->getName();
144 description.vertexShader = effect->definition.vertexShader.loadPath;
145 description.hullShader = effect->definition.hullShader.loadPath;
146 description.domainShader = effect->definition.domainShader.loadPath;
147 description.geometryShader = effect->definition.geometryShader.loadPath;
148 description.pixelShader = effect->definition.pixelShader.loadPath;
149 description.definitions = effect->definition.definitions;
150 description.bindGroupLayoutDesc = effect->definition.bindGroupLayouts;
151
152 if (description.vertexShader.find("glsl") != StringView::NoPosition) {
153 description.flags = static_cast<EffectFlags::EEffectFlags>(description.flags | EffectFlags::GLSL);
154 }
155
156 effectHandle = effects->loadEffect(description);
157
158 if (!HandleIsValid(effectHandle)) {
159 LOG_ERROR(logger, "Error loading effect %s.", effect->getName().data());
160
161 if (effect->material) {
162 effect->material->setChanged();
163 }
164
165 setFailed();
166
168 }
169
170 setDelayed();
171 }
172
173 switch (effects->checkEffect(effectHandle)) {
175 // Still compiling on the backend side, retry next frame.
177
179 LOG_ERROR(logger, "Error loading effect %s.", effect->getName().data());
180
181 if (effect->material) {
182 effect->material->setChanged();
183 }
184
185 setFailed();
186
188
190 default:
191 break;
192 }
193
194 // The effect is ready, its shader reflection can now be used to build the matching input layout.
195 inputHandle = buffers->loadInputLayout(effect->definition.streamsLayout.vertexFormats,
196 effect->definition.streamsLayout.numStreams,
197 effectHandle);
198
199 if (!checkAndLogEffectLoad(effect, effectHandle, inputHandle)) {
200 LOG_ERROR(logger, "Error loading effect %s.", effect->getName().data());
201
202 if (effect->material) {
203 effect->material->setChanged();
204 }
205
206 setFailed();
207
209 }
210
211 if (effect->material) {
212 effect->material->setChanged();
213 }
214
215 // Let dependent resources know the effect has been (re)loaded.
216 ++generation;
217
218 setActive();
219
221 }
222}
223
224void Cogs::Core::RenderEffect::release(Renderer * renderer)
225{
226 IGraphicsDevice* device = renderer->getDevice();
227 IEffects* effects = device->getEffects();
228 IBuffers* buffers = device->getBuffers();
229
230 if (HandleIsValid(effectHandle)) {
231 effects->releaseEffect(effectHandle);
232 effectHandle = Cogs::EffectHandle::NoHandle;
233 }
234 if (HandleIsValid(inputHandle)) {
235 buffers->releaseInputLayout(inputHandle);
237 }
238
239 setReleased();
240}
241
242void Cogs::Core::RenderEffect::requestLoad()
243{
244 getResource()->delayLoad = false;
245 getResource()->setChanged();
246}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
Contains render resources used by the renderer.
Core renderer system.
Definition: Renderer.h:29
IGraphicsDevice * getDevice() override
Get the graphics device used by the renderer.
Definition: Renderer.h:46
EffectFlags::EEffectFlags getEffectFlags() const override
Get the EffectFlags.
Definition: Renderer.h:71
static constexpr TaskQueueId ResourceQueue
Resource task queue.
Definition: TaskManager.h:232
Represents a graphics device used to manage graphics resources and issue drawing commands.
virtual IEffects * getEffects()=0
Get a pointer to the effect management interface.
virtual IBuffers * getBuffers()=0
Get a pointer to the buffer management interface.
Log implementation class.
Definition: LogManager.h:140
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:197
static constexpr size_t NoPosition
No position.
Definition: StringView.h:69
size_t find(const StringView &other, size_t offset=0) const noexcept
Find the given string segment inside the string.
Definition: StringView.cpp:18
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
ActivationResult
Defines results for resource activation.
Definition: ResourceBase.h:14
@ Success
Resource activated successfully.
@ Delayed
Delayed activation.
@ Postponed
Resource activation postponed, retry later.
@ Failure
Resource activation failed.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
@ Pending
The resource is still loading.
@ Error
The resource failed to load.
@ Ready
The resource has loaded successfully and is ready for use.
std::pair< std::string, std::string > PreprocessorDefinition
Preprocessor definition.
Definition: IEffects.h:17
MeshStreamsLayout streamsLayout
The vertex layout this effect expects.
PreprocessorDefinitions definitions
Preprocessor definitions.
BindGroupSetDescription bindGroupLayouts
Optional backend-neutral bind-group layout metadata, typically emitted by shader builders.
Effect resources contain data to control the shader stages of the GPU pipeline.
Definition: Effect.h:24
bool delayLoad
If the effect should be loaded or delayed.
Definition: Effect.h:40
struct Material * material
Owning material resource.
Definition: Effect.h:37
VertexFormatHandle vertexFormats[maxStreams]
void logLayout(Cogs::Logging::Log &logger) const
StringView getName() const
Get the name of the resource.
Definition: ResourceBase.h:307
bool isValid() const
Check if the task id is valid.
Definition: TaskManager.h:29
Contains an effect description used to load a single effect.
Definition: IEffects.h:62
EffectFlags::EEffectFlags flags
Effect loading flags.
Definition: IEffects.h:106
StringView geometryShader
Geometry shader file name or source.
Definition: IEffects.h:73
BindGroupSetDescription bindGroupLayoutDesc
Optional bind-group layout metadata. Backends may use this instead of source introspection.
Definition: IEffects.h:103
StringView domainShader
Domain shader file name or source.
Definition: IEffects.h:70
PreprocessorDefinitions definitions
Definitions.
Definition: IEffects.h:100
StringView vertexShader
Vertex shader file name or source.
Definition: IEffects.h:64
StringView name
Name of the effect. Used for tracking purposes, like naming shader dumps.
Definition: IEffects.h:97
StringView pixelShader
Pixel shader file name or source.
Definition: IEffects.h:76
StringView hullShader
Hull shader file name or source.
Definition: IEffects.h:67
EEffectFlags
Effect source flags.
Definition: IEffects.h:27
@ GLSL
Effect source is GLSL.
Definition: IEffects.h:33
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:78
Provides buffer management functionality.
Definition: IBuffers.h:13
virtual InputLayoutHandle loadInputLayout(const VertexFormatHandle *vertexFormats, const size_t count, EffectHandle effectHandle)=0
Loads a new input layout to map vertex flow between vertex buffers with the given vertexFormats to ef...
virtual void releaseInputLayout(InputLayoutHandle inputLayoutHandle)=0
Releases the input layout with the given inputLayoutHandle.
Provides effects and shader management functionality.
Definition: IEffects.h:158
virtual void releaseEffect(EffectHandle effectHandle)=0
Release the effect with the given handle, freeing all resources generated during program loading.
virtual EffectHandle loadEffect(const EffectDescription &description)=0
Load an effect from the given description.
virtual ResourceStatus checkEffect(EffectHandle effectHandle)=0
Check the load status of the effect with the given effectHandle.