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 const Material* material = materialInstance->material;
39 assert(material);
40
41 size_t instanceCode = Cogs::hash(permutationIndex);
42 for (const std::string& str : materialInstance->variantStrings) {
43 instanceCode = Cogs::hash(str, instanceCode);
44 }
45
46 for (const ShaderVariantDefinition& definition : material->definition.variants) {
47 if (definition.isShared) {
48 instanceCode = Cogs::hash(definition.defaultValue, instanceCode);
49 }
50 else {
51 instanceCode = Cogs::hash(materialInstance->variantSelectors[definition.index].value, instanceCode);
52 }
53 }
54
55 assert(streamsLayout);
56 if (streamsLayout->numStreams && streamsLayout->hash == 0) {
57 LOG_ERROR(logger, "Non-empty MeshStreamsLayout with zero hash");
58 }
59 instanceCode = Cogs::hash(streamsLayout->hash, instanceCode);
60 instanceCode = Cogs::hash(enginePermutation->getCode(), instanceCode);
61 instanceCode = Cogs::hash(passOptions.hash, instanceCode);
62 instanceCode = Cogs::hash(clipShape, instanceCode);
63
64 return instanceCode;
65 }
66
67 ActivationResult checkEffect(RenderMaterial * renderMaterial, EffectBinding & binding)
68 {
69 if (!binding.renderEffect) {
70 // Render effect has not been created yet.
71 return ActivationResult::Postponed;
72 }
73
74 if (binding.renderEffect->hasFailed()) {
75 if (!renderMaterial->hasFailed()) {
76
77 auto * material = renderMaterial->getResource();
78 LOG_ERROR(logger, "Could not initialize bindings for effect %s, failing activation.", material ? material->getName().to_string().c_str() : "");
79
80 renderMaterial->setFailed();
81 }
82
83 return ActivationResult::Failure;
84 } if (binding.renderEffect->isReleased()) {
85 LOG_ERROR(logger, "Could not initialize bindings for released effect.");
86
87 renderMaterial->setFailed();
88
89 return ActivationResult::Failure;
90 } else if (binding.renderEffect->isDelayed()) {
91 renderMaterial->setDelayed();
92
93 return ActivationResult::Delayed;
94 } else if (!binding.renderEffect->isActive()) {
95 LOG_DEBUG(logger, "Could not initialize render material due to uninitialized effect. Re-queuing.");
96
97 return ActivationResult::Postponed;
98 }
99
100 return ActivationResult::Success;
101 }
102
103 void updateBufferContents(Cogs::IGraphicsDevice * device,
104 const Material * material, EffectBinding & binding)
105 {
106 for (const MaterialPropertyBuffer& buffer : material->constantBuffers.buffers) {
107 if (buffer.isPerInstance) continue;
108
109 const MaterialPropertyBuffer& materialBuffer = material->constantBuffers.buffers[buffer.index];
110 EffectBinding::Buffer& gpubuf = binding.buffers[buffer.index];
111 if ((gpubuf.generation != materialBuffer.generation) && HandleIsValid(binding.bufferBindings[buffer.index])) {
112 Cogs::IContext* immediateContext = device->getImmediateContext();
113 immediateContext->updateBuffer(gpubuf.handle, materialBuffer.content.data(), buffer.size);
114 gpubuf.generation = materialBuffer.generation;
115 }
116 }
117 }
118
119 void updateTextureBinding(const Material* material, Cogs::IGraphicsDevice* device, RenderStates* /*renderStates*/, EffectBinding& binding)
120 {
121 auto b = &binding;
122
123 if (b->renderEffect && HandleIsValid(b->renderEffect->effectHandle) && !b->textureBindings.size()) {
124 b->textureBindings.resize(material->textureProperties.size());
125 b->samplerBindings.resize(material->textureProperties.size());
126
127 int unit = 0;
128
129 for (auto& tp : material->textureProperties) {
130 b->textureBindings[tp.key] = device->getEffects()->getTextureBinding(b->renderEffect->effectHandle, tp.name, unit);
131
132 if (tp.isArray) {
133 b->samplerBindings[tp.key] = device->getEffects()->getSamplerStateBinding(b->renderEffect->effectHandle, tp.name.substr(0, tp.name.find("[")) + "Sampler", unit);
134 }
135 else {
136 b->samplerBindings[tp.key] = device->getEffects()->getSamplerStateBinding(b->renderEffect->effectHandle, tp.name + "Sampler", unit);
137 }
138 ++unit;
139 }
140 }
141 }
142
143 void updateTextureProperties(RenderMaterial* renderMaterial, RenderResources* resources, RenderStates* renderStates, const Material* material)
144 {
145 const RenderSettings& settings = resources->getRenderer()->getSettings();
146
147 size_t N = material->textureProperties.size();
148 renderMaterial->texturePropertyStates.resize(N);
149 for (size_t i = 0; i < N; i++) {
150 const TextureProperty& t = material->textureProperties[i];
151 if (t.isPerInstance) continue;
152
153 RenderMaterial::TexturePropertyState& state = renderMaterial->texturePropertyStates[i];
154 if (size_t hash = t.texture.hash(); !HandleIsValid(state.samplerState) || state.hash != hash) {
155 state.hash = hash;
156
157 unsigned int anisotropy = 1;
158 if (t.texture.filterMode == Cogs::SamplerState::FilterMode::MinMagMipLinear) {
159 anisotropy = static_cast<unsigned int>(settings.anisotropicFiltering->getInt());
160 }
161 const Cogs::SamplerState samplerStateDesc = {
162 t.texture.sMode,
163 t.texture.tMode,
164 t.texture.uMode,
165 t.texture.filterMode,
166 Cogs::SamplerState::Never,
167 anisotropy,
168 { 0, 0, 0, 1 }
169 };
170 state.samplerState = renderStates->getSamplerState(samplerStateDesc);
171 debug_assert(state.samplerState);
172 }
173 }
174 }
175
176 ActivationResult setupBinding(Cogs::IGraphicsDevice* device,
177 RenderStates* renderStates,
178 RenderMaterial* renderMaterial,
179 const Material* material,
180 Effect* /*effect*/,
181 RenderEffect* renderEffect,
182 EffectBinding& binding)
183 {
184 binding.renderEffect = renderEffect;
185
186 auto effectResult = checkEffect(renderMaterial, binding);
187
188 if (effectResult != ActivationResult::Success) return effectResult;
189
190 if (binding.buffersGeneration != material->constantBuffers.buffersGeneration) {
191 binding.buffersGeneration = material->constantBuffers.buffersGeneration;
192
193 auto deviceBuffers = device->getBuffers();
194 auto effects = device->getEffects();
195
196 binding.bufferBindings.resize(material->constantBuffers.buffers.size());
197 binding.buffers.resize(material->constantBuffers.buffers.size());
198
199 for (auto& buffer : material->constantBuffers.buffers) {
200 if (!buffer.size) continue;
201
202 binding.bufferBindings[buffer.index] = effects->getConstantBufferBinding(binding.renderEffect->effectHandle, buffer.name);
203
204 if (HandleIsValid(binding.bufferBindings[buffer.index]) && !buffer.isPerInstance) {
205 binding.buffers[buffer.index].handle = deviceBuffers->loadBuffer(nullptr, buffer.size, Cogs::Usage::Dynamic, Cogs::AccessMode::Write, Cogs::BindFlags::ConstantBuffer);
206 deviceBuffers->annotate(binding.buffers[buffer.index].handle, material->definition.name + "_mat_" + std::to_string(buffer.index));
207 }
208 }
209
210 auto& effectHandle = binding.renderEffect->effectHandle;
211
212 binding.sceneBufferBinding = effects->getConstantBufferBinding(effectHandle, "SceneBuffer");
213 binding.viewBufferBinding = effects->getConstantBufferBinding(effectHandle, "ViewBuffer");
214 binding.animationBufferBinding = effects->getConstantBufferBinding(effectHandle, "AnimationBuffer");
215
216 binding.shadowArrayBinding = effects->getTextureBinding(effectHandle, "cascadedShadowMap", 1);
217 binding.shadowArrayBinding_1 = effects->getTextureBinding(effectHandle, "cascadedShadowMap_1", 1);
218 binding.shadowArraySamplerBinding = effects->getSamplerStateBinding(effectHandle, "cascadedShadowMapSampler", 1);
219 binding.shadowSamplerBinding = effects->getSamplerStateBinding(effectHandle, "cascadedShadowSampler", 1);
220
221 binding.shadowCubeArrayBinding = effects->getTextureBinding(effectHandle, "cubeShadowMap", 2);
222 binding.shadowCubeArrayBinding_1 = effects->getTextureBinding(effectHandle, "cubeShadowMap_1", 2);
223 binding.shadowCubeArraySamplerBinding = effects->getSamplerStateBinding(effectHandle, "cubeShadowMapSampler", 2);
224
225 binding.skyBinding = effects->getTextureBinding(effectHandle, "environmentSky", 0);
226 binding.radianceBinding = effects->getTextureBinding(effectHandle, "environmentRadiance", 0);
227 binding.irradianceBinding = effects->getTextureBinding(effectHandle, "environmentIrradiance", 0);
228 binding.ambientIrradianceBinding = effects->getTextureBinding(effectHandle, "ambientIrradiance", 0);
229 binding.brdfLUTBinding = effects->getTextureBinding(effectHandle, "brdfLUT", 0);
230
231 binding.skySamplerBinding = effects->getSamplerStateBinding(effectHandle, "environmentSkySampler", 0);
232 binding.radianceSamplerBinding = effects->getSamplerStateBinding(effectHandle, "environmentRadianceSampler", 0);
233 binding.irradianceSamplerBinding = effects->getSamplerStateBinding(effectHandle, "environmentIrradianceSampler", 0);
234 binding.ambientIrradianceSamplerBinding = effects->getSamplerStateBinding(effectHandle, "ambientIrradianceSampler", 0);
235 binding.brdfLUTSamplerBinding = effects->getSamplerStateBinding(effectHandle, "brdfLUTSampler", 0);
236
237 binding.objectBufferBinding = effects->getConstantBufferBinding(effectHandle, "ObjectBuffer");
238
239 binding.blueNoise = effects->getTextureBinding(effectHandle, "blueNoise_LDR_RGBA", 0);
240 binding.blueNoiseStable = effects->getTextureBinding(effectHandle, "blueNoiseStable_LDR_RGBA", 0);
241 }
242
243 updateTextureBinding(material, device, renderStates, binding);
244
245 binding.effectGeneration = renderEffect->getGeneration();
246 ++binding.generation;
247
248 return ActivationResult::Success;
249 }
250
251}
252
253Cogs::Core::ActivationResult Cogs::Core::RenderMaterial::update(Material * material, IGraphicsDevice * device, RenderResources * resources, RenderStates * renderStates)
254{
255 auto context = resources->getContext();
256 this->renderer = context->renderer;
257
258 std::vector<EffectBindingInstance> stillPending;
259
260 uint32_t results = 0;
261 for (auto & pending : pendingBindings) {
262 auto & effect = pending.effect;
263 auto binding = pending.binding;
264
265 auto renderEffect = resources->getRenderEffect(effect);
266
268 if (renderEffect && binding->effectGeneration != renderEffect->generation) {
269 result = setupBinding(device, renderStates, this, material, effect.resolve(), renderEffect, *binding);
270
271 results |= static_cast<uint32_t>(result);
272
273 if (result == ActivationResult::Success) {
274 updateBufferContents(device, material, *binding);
275 bindings.emplace_back(pending);
276 continue;
277 }
278 }
279
280 stillPending.emplace_back(pending);
281 }
282
283 for (auto & binding : bindings) {
284 if (!HandleIsValid(binding.binding->renderEffect->effectHandle)) {
285 results |= static_cast<uint32_t>(ActivationResult::Failure);
286 } else {
287 updateBufferContents(device, material, *binding.binding);
288 }
289 }
290
291 pendingBindings = std::move(stillPending);
292
293 const bool haveActive = (results & static_cast<uint32_t>(ActivationResult::Success)) != 0;
294 const bool haveFailed = (results & static_cast<uint32_t>(ActivationResult::Failure)) != 0;
295 const bool havePostponed = (results & static_cast<uint32_t>(ActivationResult::Postponed)) != 0;
296 const bool haveDelayed = (results & static_cast<uint32_t>(ActivationResult::Delayed)) != 0;
297
298 if (haveFailed) {
299 setFailed();
300
302 } else if (havePostponed && !haveActive) {
304 } else if (haveDelayed && !haveActive) {
305 setDelayed();
306
308 }
309
310 auto instances = context->materialInstanceManager->getAllocatedResources();
311
312 if (uint32_t gen = material->getGeneration(); updatedGeneration != gen) {
313 updatedGeneration = gen;
314 updateTextureProperties(this, resources, renderStates, material);
315 }
316
317 // Let material instances using any updated permutations know they need to update.
318 for (auto & resource : instances) {
319 auto materialInstance = static_cast<MaterialInstance *>(resource);
320
321 //TODO: Update permutation/variant equality logic
322 if (materialInstance->material == material &&
323 materialInstance->referenceCount() != 0) {
324 materialInstance->setChanged();
325 }
326 }
327
328 setActive();
329
331}
332
333void Cogs::Core::RenderMaterial::release(Renderer * renderer)
334{
335 IBuffers* buffers = renderer->getDevice()->getBuffers();
336
337 for (auto & b : bindings) {
338 auto binding = b.binding;
339
340 if (!binding) continue;
341
342 for (auto & buffer : binding->buffers) {
343 if (HandleIsValid(buffer.handle)) {
344 buffers->releaseBuffer(buffer.handle);
345 }
346 }
347
348 binding->buffers.clear();
349 binding->bufferBindings.clear();
350
351 renderer->getEffectBindings().deallocateBinding(binding);
352 }
353
354 for (auto & b : pendingBindings) {
355 renderer->getEffectBindings().deallocateBinding(b.binding);
356 }
357
358 bindings.clear();
359 pendingBindings.clear();
360
361 setReleased();
362}
363
365 const MaterialInstance * materialInstance,
366 const MeshStreamsLayout* streamsLayout,
367 const EnginePermutation * enginePermutation,
368 const RenderPassOptions& passOptions,
369 const ClipShapeType clipShape)
370{
371 const size_t code = calculateHash(permutationIndex, materialInstance, streamsLayout, enginePermutation, passOptions, clipShape);
372
373 for (EffectBindingInstance& b : bindings) {
374 if (b.code == code) return b.binding;
375 }
376
377 for (EffectBindingInstance& b : pendingBindings) {
378 if (b.code == code) return b.binding;
379 }
380
381 EffectBinding* binding = renderer->getEffectBindings().allocateBinding();
382 Material* material = getResource();
383 EffectHandle effect = material->getEffect(code, materialInstance, streamsLayout, enginePermutation, passOptions, clipShape);
384
385 pendingBindings.emplace_back(EffectBindingInstance{ binding, code, effect });
386
387 // Perhaps update inline (?)
388
389 material->setChanged();
390
391 return binding;
392}
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:140
std::string to_string() const
String conversion method.
Definition: StringView.cpp:9
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:181
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
StringView getName() const
Get the name of the resource.
Definition: ResourceBase.h:307
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
@ MinMagMipLinear
Linear sampling for both minification and magnification.
Definition: SamplerState.h:35
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30