Cogs.Core
RenderMaterial.cpp
1#include "RenderMaterial.h"
2
3#include "Rendering/IBuffers.h"
4#include "Rendering/ICapabilities.h"
5#include "Rendering/IContext.h"
6#include "Rendering/IEffects.h"
7#include "Rendering/ITextures.h"
8
9#include "Context.h"
10
11#include "Resources/MaterialManager.h"
12#include "Systems/Core/CameraSystem.h"
13
14#include "RenderStates.h"
15#include "RenderResources.h"
16
17#include "Renderer.h"
18#include "Context.h"
19
20#include "EffectBindings.h"
21
22#include "Foundation/HashSequence.h"
23#include "Foundation/Logging/Logger.h"
24
25namespace
26{
27 using namespace Cogs::Core;
28
29 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RenderMaterial");
30
31 size_t calculateHash(const size_t permutationIndex,
32 const MaterialInstance* materialInstance,
33 const MeshStreamsLayout* streamsLayout,
34 const EnginePermutation* enginePermutation,
35 const RenderPassOptions& passOptions,
36 const ClipShapeType clipShape)
37 {
38 size_t instanceCode = Cogs::hash(permutationIndex);
39
40 for (const std::string& str : materialInstance->variantStrings) {
41 instanceCode = Cogs::hash(str, instanceCode);
42 }
43
44 for (const ShaderVariantDefinition& definition : materialInstance->material->definition.variants) {
45 if (definition.isShared) {
46 instanceCode = Cogs::hash(definition.value, instanceCode);
47 }
48 else {
49 instanceCode = Cogs::hash(materialInstance->variantSelectors[definition.index].value, instanceCode);
50 }
51 }
52
53 assert(streamsLayout);
54 if (streamsLayout->numStreams && streamsLayout->hash == 0) {
55 LOG_ERROR(logger, "Non-empty MeshStreamsLayout with zero hash");
56 }
57 instanceCode = Cogs::hash(streamsLayout->hash, instanceCode);
58 instanceCode = Cogs::hash(enginePermutation->getCode(), instanceCode);
59 instanceCode = Cogs::hash(passOptions.hash, instanceCode);
60 instanceCode = Cogs::hash(clipShape, instanceCode);
61
62 return instanceCode;
63 }
64
65 ActivationResult checkEffect(RenderMaterial * renderMaterial, EffectBinding & binding)
66 {
67 if (!binding.renderEffect) {
68 // Render effect has not been created yet.
69 return ActivationResult::Postponed;
70 }
71
72 if (binding.renderEffect->hasFailed()) {
73 if (!renderMaterial->hasFailed()) {
74
75 auto * material = renderMaterial->getResource();
76 LOG_ERROR(logger, "Could not initialize bindings for effect %s, failing activation.", material ? material->getName().to_string().c_str() : "");
77
78 renderMaterial->setFailed();
79 }
80
81 return ActivationResult::Failure;
82 } if (binding.renderEffect->isReleased()) {
83 LOG_ERROR(logger, "Could not initialize bindings for released effect.");
84
85 renderMaterial->setFailed();
86
87 return ActivationResult::Failure;
88 } else if (binding.renderEffect->isDelayed()) {
89 renderMaterial->setDelayed();
90
91 return ActivationResult::Delayed;
92 } else if (!binding.renderEffect->isActive()) {
93 LOG_DEBUG(logger, "Could not initialize render material due to uninitialized effect. Re-queuing.");
94
95 return ActivationResult::Postponed;
96 }
97
98 return ActivationResult::Success;
99 }
100
101 void updateBufferContents(Cogs::IGraphicsDevice * device,
102 const Material * material, EffectBinding & binding)
103 {
104 for (const MaterialPropertyBuffer& buffer : material->constantBuffers.buffers) {
105 if (buffer.isPerInstance) continue;
106
107 const MaterialPropertyBuffer& materialBuffer = material->constantBuffers.buffers[buffer.index];
108 EffectBinding::Buffer& gpubuf = binding.buffers[buffer.index];
109 if ((gpubuf.generation != materialBuffer.generation) && HandleIsValid(binding.bufferBindings[buffer.index])) {
110 Cogs::IContext* immediateContext = device->getImmediateContext();
111 immediateContext->updateBuffer(gpubuf.handle, materialBuffer.content.data(), buffer.size);
112 gpubuf.generation = materialBuffer.generation;
113 }
114 }
115 }
116
117 void updateTextureBinding(const Material* material, Cogs::IGraphicsDevice* device, RenderStates* /*renderStates*/, EffectBinding& binding)
118 {
119 auto b = &binding;
120
121 if (b->renderEffect && HandleIsValid(b->renderEffect->effectHandle) && !b->textureBindings.size()) {
122 b->textureBindings.resize(material->textureProperties.size());
123 b->samplerBindings.resize(material->textureProperties.size());
124
125 int unit = 0;
126
127 for (auto& tp : material->textureProperties) {
128 b->textureBindings[tp.key] = device->getEffects()->getTextureBinding(b->renderEffect->effectHandle, tp.name, unit);
129
130 if (tp.isArray) {
131 b->samplerBindings[tp.key] = device->getEffects()->getSamplerStateBinding(b->renderEffect->effectHandle, tp.name.substr(0, tp.name.find("[")) + "Sampler", unit);
132 }
133 else {
134 b->samplerBindings[tp.key] = device->getEffects()->getSamplerStateBinding(b->renderEffect->effectHandle, tp.name + "Sampler", unit);
135 }
136 ++unit;
137 }
138 }
139 }
140
141 void updateTextureProperties(RenderMaterial* renderMaterial, RenderResources* resources, RenderStates* renderStates, const Material* material)
142 {
143 const RenderSettings& settings = resources->getRenderer()->getSettings();
144
145 size_t N = material->textureProperties.size();
146 renderMaterial->texturePropertyStates.resize(N);
147 for (size_t i = 0; i < N; i++) {
148 const TextureProperty& t = material->textureProperties[i];
149 if (t.isPerInstance) continue;
150
151 RenderMaterial::TexturePropertyState& state = renderMaterial->texturePropertyStates[i];
152 if (size_t hash = t.texture.hash(); !HandleIsValid(state.samplerState) || state.hash != hash) {
153 state.hash = hash;
154
155 const Cogs::SamplerState samplerStateDesc = {
156 t.texture.sMode,
157 t.texture.tMode,
158 t.texture.uMode,
159 t.texture.filterMode,
160 Cogs::SamplerState::Never,
161 static_cast<unsigned int>(settings.anisotropicFiltering->getInt()),
162 { 0, 0, 0, 1 }
163 };
164 state.samplerState = renderStates->getSamplerState(samplerStateDesc);
165 debug_assert(state.samplerState);
166 }
167 }
168 }
169
170 ActivationResult setupBinding(Cogs::IGraphicsDevice* device,
171 RenderStates* renderStates,
172 RenderMaterial* renderMaterial,
173 const Material* material,
174 Effect* /*effect*/,
175 RenderEffect* renderEffect,
176 EffectBinding& binding)
177 {
178 binding.renderEffect = renderEffect;
179
180 auto effectResult = checkEffect(renderMaterial, binding);
181
182 if (effectResult != ActivationResult::Success) return effectResult;
183
184 if (binding.buffersGeneration != material->constantBuffers.buffersGeneration) {
185 binding.buffersGeneration = material->constantBuffers.buffersGeneration;
186
187 auto deviceBuffers = device->getBuffers();
188 auto effects = device->getEffects();
189
190 binding.bufferBindings.resize(material->constantBuffers.buffers.size());
191 binding.buffers.resize(material->constantBuffers.buffers.size());
192
193 for (auto& buffer : material->constantBuffers.buffers) {
194 if (!buffer.size) continue;
195
196 binding.bufferBindings[buffer.index] = effects->getConstantBufferBinding(binding.renderEffect->effectHandle, buffer.name);
197
198 if (HandleIsValid(binding.bufferBindings[buffer.index]) && !buffer.isPerInstance) {
199 binding.buffers[buffer.index].handle = deviceBuffers->loadBuffer(nullptr, buffer.size, Cogs::Usage::Dynamic, Cogs::AccessMode::Write, Cogs::BindFlags::ConstantBuffer);
200 deviceBuffers->annotate(binding.buffers[buffer.index].handle, material->definition.name + "_mat_" + std::to_string(buffer.index));
201 }
202 }
203
204 auto& effectHandle = binding.renderEffect->effectHandle;
205
206 binding.sceneBufferBinding = effects->getConstantBufferBinding(effectHandle, "SceneBuffer");
207 binding.viewBufferBinding = effects->getConstantBufferBinding(effectHandle, "ViewBuffer");
208 binding.lightBufferBinding = effects->getConstantBufferBinding(effectHandle, "LightBuffer");
209 binding.shadowBufferBinding = effects->getConstantBufferBinding(effectHandle, "ShadowBuffer");
210 binding.animationBufferBinding = effects->getConstantBufferBinding(effectHandle, "AnimationBuffer");
211
212 binding.shadowArrayBinding = effects->getTextureBinding(effectHandle, "cascadedShadowMap", 1);
213 binding.shadowArrayBinding_1 = effects->getTextureBinding(effectHandle, "cascadedShadowMap_1", 1);
214 binding.shadowArraySamplerBinding = effects->getSamplerStateBinding(effectHandle, "cascadedShadowMapSampler", 1);
215 binding.shadowSamplerBinding = effects->getSamplerStateBinding(effectHandle, "cascadedShadowSampler", 1);
216
217 binding.shadowCubeArrayBinding = effects->getTextureBinding(effectHandle, "cubeShadowMap", 2);
218 binding.shadowCubeArrayBinding_1 = effects->getTextureBinding(effectHandle, "cubeShadowMap_1", 2);
219 binding.shadowCubeArraySamplerBinding = effects->getSamplerStateBinding(effectHandle, "cubeShadowMapSampler", 2);
220
221 binding.skyBinding = effects->getTextureBinding(effectHandle, "environmentSky", 0);
222 binding.radianceBinding = effects->getTextureBinding(effectHandle, "environmentRadiance", 0);
223 binding.irradianceBinding = effects->getTextureBinding(effectHandle, "environmentIrradiance", 0);
224 binding.ambientIrradianceBinding = effects->getTextureBinding(effectHandle, "ambientIrradiance", 0);
225 binding.brdfLUTBinding = effects->getTextureBinding(effectHandle, "brdfLUT", 0);
226
227 binding.skySamplerBinding = effects->getSamplerStateBinding(effectHandle, "environmentSkySampler", 0);
228 binding.radianceSamplerBinding = effects->getSamplerStateBinding(effectHandle, "environmentRadianceSampler", 0);
229 binding.irradianceSamplerBinding = effects->getSamplerStateBinding(effectHandle, "environmentIrradianceSampler", 0);
230 binding.ambientIrradianceSamplerBinding = effects->getSamplerStateBinding(effectHandle, "ambientIrradianceSampler", 0);
231 binding.brdfLUTSamplerBinding = effects->getSamplerStateBinding(effectHandle, "brdfLUTSampler", 0);
232
233 binding.objectBufferBinding = effects->getConstantBufferBinding(effectHandle, "ObjectBuffer");
234
235 binding.blueNoise = effects->getTextureBinding(effectHandle, "blueNoise_LDR_RGBA", 0);
236 binding.blueNoiseStable = effects->getTextureBinding(effectHandle, "blueNoiseStable_LDR_RGBA", 0);
237 }
238
239 updateTextureBinding(material, device, renderStates, binding);
240
241 binding.effectGeneration = renderEffect->getGeneration();
242 ++binding.generation;
243
244 return ActivationResult::Success;
245 }
246
247}
248
249Cogs::Core::ActivationResult Cogs::Core::RenderMaterial::update(Material * material, IGraphicsDevice * device, RenderResources * resources, RenderStates * renderStates)
250{
251 auto context = resources->getContext();
252 this->renderer = context->renderer;
253
254 std::vector<EffectBindingInstance> stillPending;
255
256 uint32_t results = 0;
257 for (auto & pending : pendingBindings) {
258 auto & effect = pending.effect;
259 auto binding = pending.binding;
260
261 auto renderEffect = resources->getRenderEffect(effect);
262
264 if (renderEffect && binding->effectGeneration != renderEffect->generation) {
265 result = setupBinding(device, renderStates, this, material, effect.resolve(), renderEffect, *binding);
266
267 results |= static_cast<uint32_t>(result);
268
269 if (result == ActivationResult::Success) {
270 updateBufferContents(device, material, *binding);
271 bindings.emplace_back(pending);
272 continue;
273 }
274 }
275
276 stillPending.emplace_back(pending);
277 }
278
279 for (auto & binding : bindings) {
280 if (!HandleIsValid(binding.binding->renderEffect->effectHandle)) {
281 results |= static_cast<uint32_t>(ActivationResult::Failure);
282 } else {
283 updateBufferContents(device, material, *binding.binding);
284 }
285 }
286
287 pendingBindings = std::move(stillPending);
288
289 const bool haveActive = (results & static_cast<uint32_t>(ActivationResult::Success)) != 0;
290 const bool haveFailed = (results & static_cast<uint32_t>(ActivationResult::Failure)) != 0;
291 const bool havePostponed = (results & static_cast<uint32_t>(ActivationResult::Postponed)) != 0;
292 const bool haveDelayed = (results & static_cast<uint32_t>(ActivationResult::Delayed)) != 0;
293
294 if (haveFailed) {
295 setFailed();
296
298 } else if (havePostponed && !haveActive) {
300 } else if (haveDelayed && !haveActive) {
301 setDelayed();
302
304 }
305
306 auto instances = context->materialInstanceManager->getAllocatedResources();
307
308 if (uint32_t gen = material->getGeneration(); updatedGeneration != gen) {
309 updatedGeneration = gen;
310 updateTextureProperties(this, resources, renderStates, material);
311 }
312
313 // Let material instances using any updated permutations know they need to update.
314 for (auto & resource : instances) {
315 auto materialInstance = static_cast<MaterialInstance *>(resource);
316
317 //TODO: Update permutation/variant equality logic
318 if (materialInstance->material == material &&
319 materialInstance->referenceCount() != 0) {
320 materialInstance->setChanged();
321 }
322 }
323
324 setActive();
325
327}
328
329void Cogs::Core::RenderMaterial::release(Renderer * renderer)
330{
331 IBuffers* buffers = renderer->getDevice()->getBuffers();
332
333 for (auto & b : bindings) {
334 auto binding = b.binding;
335
336 if (!binding) continue;
337
338 for (auto & buffer : binding->buffers) {
339 if (HandleIsValid(buffer.handle)) {
340 buffers->releaseBuffer(buffer.handle);
341 }
342 }
343
344 binding->buffers.clear();
345 binding->bufferBindings.clear();
346
347 renderer->getEffectBindings().deallocateBinding(binding);
348 }
349
350 for (auto & b : pendingBindings) {
351 renderer->getEffectBindings().deallocateBinding(b.binding);
352 }
353
354 bindings.clear();
355 pendingBindings.clear();
356
357 setReleased();
358}
359
361 const MaterialInstance * materialInstance,
362 const MeshStreamsLayout* streamsLayout,
363 const EnginePermutation * enginePermutation,
364 const RenderPassOptions& passOptions,
365 const ClipShapeType clipShape)
366{
367 const size_t code = calculateHash(permutationIndex, materialInstance, streamsLayout, enginePermutation, passOptions, clipShape);
368
369 for (EffectBindingInstance& b : bindings) {
370 if (b.code == code) return b.binding;
371 }
372
373 for (EffectBindingInstance& b : pendingBindings) {
374 if (b.code == code) return b.binding;
375 }
376
377 EffectBinding* binding = renderer->getEffectBindings().allocateBinding();
378 Material* material = getResource();
379 EffectHandle effect = material->getEffect(code, materialInstance, streamsLayout, enginePermutation, passOptions, clipShape);
380
381 pendingBindings.emplace_back(EffectBindingInstance{ binding, code, effect });
382
383 // Perhaps update inline (?)
384
385 material->setChanged();
386
387 return binding;
388}
Contains render resources used by the renderer.
Core renderer system.
Definition: Renderer.h:28
EffectBindings & getEffectBindings() override
Get the reference to the EffectBindings structure.
Definition: Renderer.h:71
IGraphicsDevice * getDevice() override
Get the graphics device used by the renderer.
Definition: Renderer.h:45
const RenderSettings & getSettings() const override
Get the settings of the renderer.
Definition: Renderer.h:46
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 IContext * getImmediateContext()=0
Get a pointer to the immediate context used to issue commands to the graphics device.
virtual IBuffers * getBuffers()=0
Get a pointer to the buffer management interface.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
ClipShapeType
Specifices what kind of shape a clip shape has.
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:180
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
@ Write
The buffer can be mapped and written to by the CPU after creation.
Definition: Flags.h:50
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
Definition: Flags.h:72
std::vector< MaterialPropertyBuffer > buffers
Constant buffer instances.
uint16_t buffersGeneration
If the constant buffer bindings need updates.
Effect resources contain data to control the shader stages of the GPU pipeline.
Definition: Effect.h:24
Material instances represent a specialized Material combined with state for all its buffers and prope...
std::vector< std::string > variantStrings
String storage for string variants.
Material * material
Material resource this MaterialInstance is created from.
ShaderVariantSelectors variantSelectors
Variant selectors.
Material property buffers contain properties for a material, such as color, reflectiveness or other v...
bool isPerInstance
If the buffer is updated per instance.
ConstantBufferKey index
Index of the buffer in the set of buffers owned by the same Material.
size_t size
Total size of the buffer in bytes.
PropertyName name
Name used to refer to the buffer in shaders.
std::vector< uint8_t > content
Default content for property buffer instances created from this buffer.
Material resources define the how of geometry rendering (the what is defined by Mesh and Texture reso...
Definition: Material.h:82
EffectHandle getEffect(size_t code, const MaterialInstance *materialInstance, const MeshStreamsLayout *streamsLayout, const EnginePermutation *enginePermutation, const RenderPassOptions &passOptions, const ClipShapeType clipShape)
Definition: Material.cpp:223
EffectBinding * getBinding(const size_t permutationIndex, const MaterialInstance *materialInstance, const MeshStreamsLayout *streamsLayout, const EnginePermutation *enginePermutation, const RenderPassOptions &passOptions, const ClipShapeType clipShape)
Material * resource
Engine resource this render resource represents.
bool isDelayed() const
Get if the render resource is in a delayed state.
bool isActive() const
Get if the render resource is active and can be used for rendering.
Render settings variables.
uint32_t getGeneration() const
Get the generation count.
Definition: ResourceBase.h:380
uint32_t referenceCount() const
Get the current reference count.
Definition: ResourceBase.h:360
Property value for texture samplers.
Provides buffer management functionality.
Definition: IBuffers.h:13
virtual void releaseBuffer(BufferHandle bufferHandle)=0
Releases the buffer with the given bufferHandle.
Represents a graphics device context which can receive rendering commands.
Definition: IContext.h:43
virtual void updateBuffer(BufferHandle bufferHandle, const void *data, size_t size)=0
Replace contents of buffer with new data.
virtual SamplerStateBindingHandle getSamplerStateBinding(EffectHandle effectHandle, const StringView &name, const unsigned int slot)=0
Get a handle to a sampler state object binding, mapping how to bind the sampler state to the given ef...
virtual TextureBindingHandle getTextureBinding(EffectHandle effectHandle, const StringView &name, const unsigned int slot)=0
Get a handle to a texture object binding, mapping how to bind textures to the given effect.
Encapsulates state for texture sampling in a state object.
Definition: SamplerState.h:12
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30