Cogs.Core
RenderMaterialInstance.cpp
1#include "RenderMaterialInstance.h"
2
3#include "Rendering/IEffects.h"
4#include "Rendering/IBuffers.h"
5#include "Rendering/IContext.h"
6#include "Rendering/ITextures.h"
7#include "Rendering/ICapabilities.h"
8
9#include "Context.h"
10
11#include "RenderStates.h"
12#include "RenderResources.h"
13#include "Resources/MaterialManager.h"
14
15#include "Renderer.h"
16#include "Engine.h"
17
18#include "Foundation/HashSequence.h"
19#include "Foundation/Logging/Logger.h"
20
21namespace
22{
23 using namespace Cogs::Core;
24
25 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RenderMaterialInstance");
26
27 void clearBuffers(RenderMaterialInstance* that, Cogs::IBuffers* deviceBuffers)
28 {
29 for (auto& buffer : that->ownedBuffers) {
30 if (HandleIsValid(buffer)) {
31 deviceBuffers->releaseBuffer(buffer);
32 }
33 }
34
35 for (auto& buffer : that->buffers) {
36 buffer.handle = Cogs::BufferHandle::NoHandle;
37 buffer.generation = 0;
38 }
39
40 that->ownedBuffers.clear();
41 }
42
43 size_t getBindingCode(const MeshStreamsLayout& streamsLayout,
44 const EnginePermutation* permutation,
45 const RenderPassOptions& passOptions,
46 ClipShapeType clipShape)
47 {
48 if (streamsLayout.numStreams && streamsLayout.hash == 0) {
49 LOG_ERROR(logger, "MeshStreamsLayout has streams but hash is zero.");
50 }
51 return Cogs::hashSequence(streamsLayout.hash, permutation->getCode(), passOptions.hash, clipShape);
52 }
53
54 void updateBindings(RenderMaterialInstance* that,
55 MaterialInstance* materialInstance,
56 const Material* material,
58 EffectBinding* /*effectBinding*/)
59 {
60 Cogs::IBuffers* deviceBuffers = device->getBuffers();
61 Cogs::IContext* immediateContext = device->getImmediateContext();
62
63 if (that->context->variables->get("renderer.disableMaterialUpdates", false)) {
64 return;
65 }
66
67 if (that->buffersGeneration != materialInstance->buffersGeneration) {
68 that->buffersGeneration = materialInstance->buffersGeneration;
69
70 // If a binding permutation has been encountered earlier with valid bindings for only a
71 // subset of buffers, we may have both owned buffers and missing buffers.
72 clearBuffers(that, deviceBuffers);
73
74 that->buffers.resize(material->constantBuffers.buffers.size());
75
76 for (auto& buffer : material->constantBuffers.buffers) {
77
78 if (buffer.isPerInstance && buffer.size) {
79 that->buffers[buffer.index].handle = deviceBuffers->loadBuffer(nullptr, buffer.size, Cogs::Usage::Dynamic, Cogs::AccessMode::Write, Cogs::BindFlags::ConstantBuffer);
80 deviceBuffers->annotate(that->buffers[buffer.index].handle, material->definition.name + "_matinst_" + std::to_string(buffer.index));
81
82 that->ownedBuffers.push_back(that->buffers[buffer.index].handle);
83 that->buffers[buffer.index].bound = true;
84 }
85 }
86 }
87
88 for (auto& buffer : material->constantBuffers.buffers) {
89 if (!buffer.isPerInstance) continue;
90
91 auto& instanceBuffer = materialInstance->buffers[buffer.index];
92 RenderMaterialInstance::UpdateBuffer& updateBuffer = that->buffers[buffer.index];
93
94 bool dirty = updateBuffer.generation != instanceBuffer.generation;
95
96 if (dirty && HandleIsValid(updateBuffer.handle)) {
97 immediateContext->updateBuffer(updateBuffer.handle, instanceBuffer.content.data(), buffer.size);
98 updateBuffer.generation = instanceBuffer.generation;
99 }
100 }
101 }
102
103
104 void updateTextureProperties(RenderMaterialInstance* that,
105 MaterialInstance* materialInstance,
106 Cogs::IGraphicsDevice* /*device*/,
107 RenderStates* renderStates)
108 {
109 auto& settings = that->context->renderer->getSettings();
110
111 // Allocate enough room to store a sampler state per texture.
112 that->samplerStates.resize(materialInstance->textureVariables.size());
113
114 for (auto& t : materialInstance->textureVariables) {
115 if (t.dirty || !HandleIsValid(that->samplerStates[t.key])) {
116 unsigned int anisotropy = 1;
117 if (t.texture.filterMode == Cogs::SamplerState::FilterMode::MinMagMipLinear) {
118 anisotropy = static_cast<unsigned int>(settings.anisotropicFiltering->getInt());
119 }
120 const Cogs::SamplerState state = {
121 t.texture.sMode,
122 t.texture.tMode,
123 t.texture.uMode,
124 t.texture.filterMode,
125 Cogs::SamplerState::Never,
126 anisotropy,
127 { 0, 0, 0, 1 }
128 };
129
130 that->samplerStates[t.key] = renderStates->getSamplerState(state);
131
132 debug_assert(HandleIsValid(that->samplerStates[t.key]));
133
134 t.dirty = false;
135 }
136 }
137 }
138
139 ActivationResult checkMaterial(RenderMaterialInstance* instance, RenderMaterial* renderMaterial)
140 {
141 if (!renderMaterial) {
142 return ActivationResult::Postponed;
143 }
144
145 if (renderMaterial->hasFailed()) {
146 instance->setFailed();
147 return ActivationResult::Failure;
148 }
149 else if (renderMaterial->isReleased()) {
150 LOG_ERROR(logger, "Could not initialize material instance for released material.");
151 instance->setFailed();
152 return ActivationResult::Failure;
153 }
154 else if (renderMaterial->isDelayed()) {
155 instance->setDelayed();
156 return ActivationResult::Delayed;
157 }
158 else if (!renderMaterial->isActive()) {
159 return ActivationResult::Postponed;
160 }
161
162 return ActivationResult::Success;
163 }
164}
165
166Cogs::Core::ActivationResult Cogs::Core::RenderMaterialInstance::update(MaterialInstance* materialInstance,
167 IGraphicsDevice* device,
168 RenderResources* resources,
169 RenderStates* renderStates)
170{
171 context = resources->getContext();
172
173 Material* material = materialInstance->material;
174 assert(material);
175
176 renderMaterial = resources->getRenderMaterial(materialInstance->material);
177
178 auto checkResult = checkMaterial(this, renderMaterial);
179
180 if (checkResult != ActivationResult::Success) return checkResult;
181
182 updateTextureProperties(this, materialInstance, device, renderStates);
183
184 if ((permutationIndex != materialInstance->permutationIndex) ||
185 (materialVariantGeneration != material->variantGeneration) ||
186 (instanceVariantGeneration != materialInstance->variantGeneration))
187 {
188 loadedBindings.clear();
189 pendingBindings.clear();
190
191 permutationIndex = materialInstance->permutationIndex;
192 materialVariantGeneration = material->variantGeneration;
193 instanceVariantGeneration = materialInstance->variantGeneration;
194 }
195
196 std::vector<RenderEffectBinding> stillPending;
197
198 for (RenderEffectBinding& pending : pendingBindings) {
199
200 EffectBinding* binding = renderMaterial->getBinding(permutationIndex,
201 materialInstance,
202 &pending.streamsLayout,
203 pending.enginePermutation,
204 pending.passOptions,
205 pending.clipShape);
206
207 if (binding->renderEffect &&
208 HandleIsValid(binding->renderEffect->effectHandle) &&
209 HandleIsValid(binding->renderEffect->inputHandle) &&
210 !binding->renderEffect->isDelayed())
211 {
212 loadedBindings.push_back(std::move(pending));
213 }
214 else {
215 stillPending.push_back(std::move(pending));
216 materialInstance->setChanged();
217 }
218 }
219
220 pendingBindings = std::move(stillPending);
221
222 for (auto & loaded : loadedBindings) {
223 updateBindings(this, materialInstance, materialInstance->material, device, loaded.binding);
224 }
225
226 setActive();
227
229}
230
231
232void Cogs::Core::RenderMaterialInstance::release(Renderer * renderer)
233{
234 assert(!isReleased());
235
236 clearBuffers(this, renderer->getDevice()->getBuffers());
237
238 setReleased();
239}
240
242 const EnginePermutation* permutation,
243 const RenderPassOptions& passOptions,
244 ClipShapeType clipShape)
245{
246 MaterialInstance* materialInstance = getResource();
247
248 // We already have an binding
249 if (const EffectBinding* bindings = getBindings(streamsLayout, permutation, passOptions, clipShape); bindings) {
250
251 if (!bindings->renderEffect ||
252 bindings->renderEffect->isDelayed() ||
253 !HandleIsValid(bindings->renderEffect->effectHandle) ||
254 !HandleIsValid(bindings->renderEffect->inputHandle) ||
255 bindings->buffersGeneration != materialInstance->material->constantBuffers.buffersGeneration ||
256 buffersGeneration != materialInstance->buffersGeneration)
257 {
258 Material* material = materialInstance->material;
259
260 if (bindings->renderEffect && bindings->renderEffect->isDelayed()) {
261 bindings->renderEffect->requestLoad();
262 context->engine->setDirty();
263 }
264
265 else if (renderMaterial->isDelayed() || renderMaterial->pendingBindings.size()) {
266 // Ensure the material is queued for processing if it is either delayed or has pending bindings to set up.
267 material->setChanged();
268 }
269
270 if (materialInstance->hasFailedActivation()) {
271 materialInstance->setChanged();
272 materialInstance->material->setChanged();
273 }
274
275 return nullptr;
276 }
277
278 return bindings;
279 }
280
281 // We do not have any bindings, try to get one
282 else {
283
284 size_t permuationCode = getBindingCode(streamsLayout, permutation, passOptions, clipShape);
285
286 Material* material = materialInstance->material;
287
288 EffectBinding* binding = nullptr;
289 if (renderMaterial) {
290 binding = renderMaterial->getBinding(permutationIndex,
291 materialInstance,
292 &streamsLayout,
293 permutation,
294 passOptions,
295 clipShape);
296 }
297
298 if (binding && binding->renderEffect && binding->buffersGeneration == material->constantBuffers.buffersGeneration) {
299 auto device = context->renderer->getDevice();
300 updateBindings(this, materialInstance, material, device, binding);
301 updateTextureProperties(this, materialInstance, device, &context->renderer->getRenderStates());
302
303 loadedBindings.push_back(RenderEffectBinding{ permuationCode, streamsLayout, permutation, passOptions, clipShape, binding });
304
305 return checkReady(streamsLayout, permutation, passOptions, clipShape);
306 }
307
308 else {
309 pendingBindings.emplace_back(RenderEffectBinding{ permuationCode, streamsLayout, permutation, passOptions, clipShape, binding });
310
311 materialInstance->setChanged();
312 context->engine->setDirty();
313 }
314
315
316 if (materialInstance->hasFailedActivation()) {
317 materialInstance->setChanged();
318 materialInstance->material->setChanged();
319 }
320
321 return nullptr;
322 }
323}
324
326 const EnginePermutation * permutation,
327 const RenderPassOptions& passOptions,
328 ClipShapeType clipShape) const
329{
330 permutation = context->renderer->getEnginePermutations().get(permutation->getIndex());
331
332 size_t code = getBindingCode(streamsLayout, permutation, passOptions, clipShape);
333
334 for (auto & binding : loadedBindings) {
335 if (binding.permutationCode == code) return binding.binding;
336 }
337
338 for (auto & binding : pendingBindings) {
339 if (binding.permutationCode == code) return binding.binding;
340 }
341
342 return nullptr;
343}
class IRenderer * renderer
Renderer.
Definition: Context.h:228
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
virtual const RenderSettings & getSettings() const =0
Get the settings of the renderer.
Contains render resources used by the renderer.
Core renderer system.
Definition: Renderer.h:28
IGraphicsDevice * getDevice() override
Get the graphics device used by the renderer.
Definition: Renderer.h:45
Represents a graphics device used to manage graphics resources and issue drawing commands.
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
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.
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 hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
Definition: HashSequence.h:8
@ 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.
Material instances represent a specialized Material combined with state for all its buffers and prope...
std::vector< TextureValue > textureVariables
Texture property values for this instance.
uint16_t buffersGeneration
If the material buffer bindings need updates.
std::vector< MaterialPropertyBufferInstance > buffers
Buffer instances matching the buffers and layout of the parent material.
size_t variantGeneration
If the variant or definitions need updates.
size_t permutationIndex
Index of material permutation to use.
Material * material
Material resource this MaterialInstance is created from.
Material resources define the how of geometry rendering (the what is defined by Mesh and Texture reso...
Definition: Material.h:82
const EffectBinding * getBindings(const MeshStreamsLayout &streamsLayout, const EnginePermutation *permutation, const RenderPassOptions &passOptions, ClipShapeType clipShape) const
const EffectBinding * checkReady(const MeshStreamsLayout &streamsLayout, const EnginePermutation *permutation, const RenderPassOptions &passOptions, ClipShapeType clipShape)
EffectBinding * getBinding(const size_t permutationIndex, const MaterialInstance *materialInstance, const MeshStreamsLayout *streamsLayout, const EnginePermutation *enginePermutation, const RenderPassOptions &passOptions, const ClipShapeType clipShape)
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.
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:78
Provides buffer management functionality.
Definition: IBuffers.h:13
virtual void annotate(BufferHandle handle, const StringView &name)
Associate a name with an object for use in graphics debugging.
Definition: IBuffers.h:17
virtual BufferHandle loadBuffer(const void *data, const size_t size, Usage::EUsage usage, uint32_t accessMode, uint32_t bindFlags, uint32_t stride=0)=0
Loads a new buffer using the given data to populate the buffer.
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.
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