1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "render_data_store_default_material.h"
17
18 #include <algorithm>
19 #include <cstdint>
20
21 #include <3d/render/intf_render_data_store_default_material.h>
22 #include <base/containers/array_view.h>
23 #include <base/math/float_packer.h>
24 #include <core/log.h>
25 #include <render/device/intf_gpu_resource_manager.h>
26 #include <render/device/intf_shader_manager.h>
27 #include <render/intf_render_context.h>
28 #include <render/resource_handle.h>
29
30 #include "render_data_store_default_material.h"
31
32 CORE3D_BEGIN_NAMESPACE()
33 using namespace BASE_NS;
34 using namespace RENDER_NS;
35
36 using RENDER_NS::IShaderManager;
37 using RENDER_NS::RenderHandleReference;
38
39 namespace {
40 // for packing defines
41 #include <3d/shaders/common/3d_dm_structures_common.h>
42 } // namespace
43 namespace {
44 constexpr uint32_t SHADER_DEFAULT_RENDER_SLOT_COUNT { 3u };
45
46 constexpr size_t MEMORY_ALIGNMENT { 64 };
47
48 static constexpr uint32_t MATERIAL_TYPE_SHIFT { 28u };
49 static constexpr uint32_t MATERIAL_TYPE_MASK { 0xF0000000u };
50 static constexpr uint32_t MATERIAL_FLAGS_SHIFT { 8u };
51 static constexpr uint32_t MATERIAL_FLAGS_MASK { 0x0FFFff00u };
52 static constexpr uint32_t SUBMESH_FLAGS_MASK { 0x00000ffu };
53
54 static constexpr uint32_t COMBINED_GPU_INSTANCING_REMOVAL { ~(
55 RenderMaterialFlagBits::RENDER_MATERIAL_GPU_INSTANCING_BIT |
56 RenderMaterialFlagBits::RENDER_MATERIAL_GPU_INSTANCING_MATERIAL_BIT) };
57
58 static constexpr RenderDataDefaultMaterial::AllMaterialUniforms INIT_ALL_MATERIAL_UNIFORMS {};
59
HashCombine32Bit(uint32_t & seed,const uint32_t v)60 inline void HashCombine32Bit(uint32_t& seed, const uint32_t v)
61 {
62 constexpr const uint32_t goldenRatio = 0x9e3779b9;
63 constexpr const uint32_t rotl = 6U;
64 constexpr const uint32_t rotr = 2U;
65 seed ^= hash(v) + goldenRatio + (seed << rotl) + (seed >> rotr);
66 }
67
ConvertMaterialHandleReferences(const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & inputHandles,vector<RenderHandleReference> & references)68 inline constexpr RenderDataDefaultMaterial::MaterialHandles ConvertMaterialHandleReferences(
69 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& inputHandles,
70 vector<RenderHandleReference>& references)
71 {
72 RenderDataDefaultMaterial::MaterialHandles mh;
73 for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
74 if (inputHandles.images[idx]) {
75 mh.images[idx] = inputHandles.images[idx].GetHandle();
76 references.push_back(inputHandles.images[idx]);
77 }
78 if (inputHandles.samplers[idx]) {
79 mh.samplers[idx] = inputHandles.samplers[idx].GetHandle();
80 references.push_back(inputHandles.samplers[idx]);
81 }
82 }
83 return mh;
84 }
85
GetRenderSortLayerHash(const RenderSubmesh & submesh)86 inline constexpr uint16_t GetRenderSortLayerHash(const RenderSubmesh& submesh)
87 {
88 // if submesh layers are defaults -> use material sort values (the defaults are the same)
89 if ((submesh.layers.meshRenderSortLayer == RenderSceneDataConstants::DEFAULT_RENDER_SORT_LAYER_ID) &&
90 (submesh.layers.meshRenderSortLayerOrder == 0)) {
91 return (static_cast<uint16_t>(submesh.layers.materialRenderSortLayer) << 8u) |
92 (static_cast<uint16_t>(submesh.layers.materialRenderSortLayerOrder) & 0xffu);
93 } else {
94 return (static_cast<uint16_t>(submesh.layers.meshRenderSortLayer) << 8u) |
95 (static_cast<uint16_t>(submesh.layers.meshRenderSortLayerOrder) & 0xffu);
96 }
97 }
98
ConvertRenderSubmeshInput(const RenderSubmeshWithHandleReference & input,vector<RenderHandleReference> & references)99 RenderSubmesh ConvertRenderSubmeshInput(
100 const RenderSubmeshWithHandleReference& input, vector<RenderHandleReference>& references)
101 {
102 RenderSubmeshBuffers rsb;
103 const auto& ib = input.buffers.indexBuffer;
104 if (ib.bufferHandle) {
105 references.push_back(ib.bufferHandle);
106 rsb.indexBuffer = { ib.bufferHandle.GetHandle(), ib.bufferOffset, ib.byteSize, ib.indexType };
107 }
108 const auto& iargs = input.buffers.indirectArgsBuffer;
109 if (iargs.bufferHandle) {
110 references.push_back(iargs.bufferHandle);
111 rsb.indirectArgsBuffer = { iargs.bufferHandle.GetHandle(), iargs.bufferOffset, iargs.byteSize };
112 }
113 const RenderHandle safetyBuffer = input.buffers.vertexBuffers[0U].bufferHandle.GetHandle();
114 RenderHandle prevHandle {};
115 for (uint32_t idx = 0; idx < RENDER_NS::PipelineStateConstants::MAX_VERTEX_BUFFER_COUNT; ++idx) {
116 const auto& vb = input.buffers.vertexBuffers[idx];
117 // add safety handles to invalid buffers
118 if (vb.bufferHandle) {
119 rsb.vertexBuffers[idx] = { vb.bufferHandle.GetHandle(), vb.bufferOffset, vb.byteSize };
120 // often we have the same buffer (GPU resource)
121 if (rsb.vertexBuffers[idx].bufferHandle != prevHandle) {
122 references.push_back(vb.bufferHandle);
123 }
124 } else {
125 rsb.vertexBuffers[idx] = { safetyBuffer, 0U, 0U };
126 }
127 prevHandle = rsb.vertexBuffers[idx].bufferHandle;
128 }
129 // NOTE: we will get max amount of vertex buffers if there is at least one
130 rsb.vertexBufferCount = RenderHandleUtil::IsValid(rsb.vertexBuffers[0U].bufferHandle)
131 ? RENDER_NS::PipelineStateConstants::MAX_VERTEX_BUFFER_COUNT
132 : 0U;
133 rsb.inputAssembly = input.buffers.inputAssembly;
134
135 return RenderSubmesh { input.submeshFlags, input.renderSubmeshMaterialFlags, input.indices, input.layers,
136 input.bounds, input.drawCommand, move(rsb) };
137 }
138
139 #if (CORE3D_VALIDATION_ENABLED == 1)
ValidateSubmesh(const RenderSubmeshWithHandleReference & submesh)140 void ValidateSubmesh(const RenderSubmeshWithHandleReference& submesh)
141 {
142 if (((submesh.submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT) == 0) &&
143 submesh.indices.skinJointIndex != RenderSceneDataConstants::INVALID_INDEX) {
144 CORE_LOG_W("CORE3D_VALIDATION: skin bit is not set for submesh flags");
145 }
146 }
147 #endif
148
PackMaterialUVec(const Math::Vec4 & up0,const Math::Vec4 & up1)149 inline Math::UVec4 PackMaterialUVec(const Math::Vec4& up0, const Math::Vec4& up1)
150 {
151 return Math::UVec4 {
152 Math::PackHalf2X16({ up0.x, up0.y }),
153 Math::PackHalf2X16({ up0.z, up0.w }),
154 Math::PackHalf2X16({ up1.x, up1.y }),
155 Math::PackHalf2X16({ up1.z, up1.w }),
156 };
157 }
158
ExtentRenderMaterialFlagsFromSubmeshValues(const RenderSubmeshFlags & submeshFlags,RenderMaterialFlags & rmf)159 inline void ExtentRenderMaterialFlagsFromSubmeshValues(const RenderSubmeshFlags& submeshFlags, RenderMaterialFlags& rmf)
160 {
161 // if there are normal maps and there are tangets, we allow the normal map flag
162 if ((rmf & RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT) &&
163 (submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_TANGENTS_BIT)) {
164 rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT;
165 } else {
166 // remove flag if there were only normal maps but no tangents
167 rmf &= (~RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT);
168 }
169 }
170
ExtentRenderMaterialFlagsForComplexity(const RenderMaterialType materialType,RenderMaterialFlags & rmf)171 void ExtentRenderMaterialFlagsForComplexity(const RenderMaterialType materialType, RenderMaterialFlags& rmf)
172 {
173 constexpr RenderMaterialFlags complexMask { RENDER_MATERIAL_CLEAR_COAT_BIT | RENDER_MATERIAL_TRANSMISSION_BIT |
174 RENDER_MATERIAL_SHEEN_BIT | RENDER_MATERIAL_SPECULAR_BIT };
175 if ((materialType == RenderMaterialType::CUSTOM) || (materialType == RenderMaterialType::CUSTOM_COMPLEX)) {
176 rmf |= (materialType == RenderMaterialType::CUSTOM_COMPLEX) ? RENDER_MATERIAL_COMPLEX_BIT
177 : RENDER_MATERIAL_BASIC_BIT;
178 if (materialType < RenderMaterialType::CUSTOM) {
179 rmf |= ((rmf & complexMask) || (materialType == RenderMaterialType::SPECULAR_GLOSSINESS))
180 ? RENDER_MATERIAL_COMPLEX_BIT
181 : RENDER_MATERIAL_BASIC_BIT;
182 }
183 } else {
184 rmf |= ((rmf & complexMask) > 0) ? RENDER_MATERIAL_COMPLEX_BIT : RENDER_MATERIAL_BASIC_BIT;
185 }
186 }
187
HashSubmeshMaterials(const RenderMaterialType materialType,const RenderMaterialFlags materialFlags,const RenderSubmeshFlags submeshFlags)188 inline constexpr uint32_t HashSubmeshMaterials(const RenderMaterialType materialType,
189 const RenderMaterialFlags materialFlags, const RenderSubmeshFlags submeshFlags)
190 {
191 return (((uint32_t)materialType << MATERIAL_TYPE_SHIFT) & MATERIAL_TYPE_MASK) |
192 ((materialFlags << MATERIAL_FLAGS_SHIFT) & MATERIAL_FLAGS_MASK) | (submeshFlags & SUBMESH_FLAGS_MASK);
193 }
194
PatchRenderMaterialSortLayers(const RenderDataDefaultMaterial::MaterialData & matData,RenderSubmeshLayers & layers)195 inline constexpr void PatchRenderMaterialSortLayers(
196 const RenderDataDefaultMaterial::MaterialData& matData, RenderSubmeshLayers& layers)
197 {
198 // batch render material sort layers if default
199 if (layers.materialRenderSortLayer == RenderSceneDataConstants::DEFAULT_RENDER_SORT_LAYER_ID) {
200 layers.materialRenderSortLayer = matData.renderSortLayer;
201 }
202 if (layers.materialRenderSortLayerOrder == 0U) {
203 layers.materialRenderSortLayerOrder = matData.renderSortLayerOrder;
204 }
205 }
206
HashMaterialId(const uint64_t id,const uint32_t instanceCount)207 inline uint64_t HashMaterialId(const uint64_t id, const uint32_t instanceCount)
208 {
209 return Hash(id, static_cast<uint64_t>(instanceCount));
210 }
211
AllocateMatrixMemory(RenderDataStoreDefaultMaterial::LinearAllocatorStruct & allocator,const size_t byteSize)212 void* AllocateMatrixMemory(RenderDataStoreDefaultMaterial::LinearAllocatorStruct& allocator, const size_t byteSize)
213 {
214 void* data = nullptr;
215 if (!allocator.allocators.empty()) {
216 data = allocator.allocators[allocator.currentIndex]->Allocate(byteSize);
217 }
218
219 if (data) {
220 return data;
221 } else { // current allocator is out of memory
222 allocator.allocators.push_back(make_unique<LinearAllocator>(byteSize, MEMORY_ALIGNMENT));
223 allocator.currentIndex = (uint32_t)(allocator.allocators.size() - 1);
224 data = allocator.allocators[allocator.currentIndex]->Allocate(byteSize);
225 CORE_ASSERT_MSG(data, "render data store default material allocation : out of memory");
226 return data;
227 }
228 }
229
AllocateMatrices(RenderDataStoreDefaultMaterial::LinearAllocatorStruct & allocator,const size_t count)230 Math::Mat4X4* AllocateMatrices(RenderDataStoreDefaultMaterial::LinearAllocatorStruct& allocator, const size_t count)
231 {
232 const size_t byteSize = count * sizeof(Math::Mat4X4);
233 return static_cast<Math::Mat4X4*>(AllocateMatrixMemory(allocator, byteSize));
234 }
235
MaterialUniformsPackedFromInput(const RenderDataDefaultMaterial::InputMaterialUniforms & input)236 RenderDataDefaultMaterial::AllMaterialUniforms MaterialUniformsPackedFromInput(
237 const RenderDataDefaultMaterial::InputMaterialUniforms& input)
238 {
239 RenderDataDefaultMaterial::AllMaterialUniforms amu = INIT_ALL_MATERIAL_UNIFORMS;
240 // premultiplication needs to be handled already with some factors like baseColor
241 for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
242 amu.factors.factors[idx] = input.textureData[idx].factor;
243 }
244 uint32_t transformBits = 0;
245 {
246 constexpr auto supportedMaterials = RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT;
247 for (uint32_t i = 0; i < supportedMaterials; ++i) {
248 const auto& tex = input.textureData[i];
249
250 /* TRS matrix for 2D transformations:
251 * { scaleX * cos(rot), scaleY * sin(rot), transX }
252 * { scaleX * -sin(rot), scaleY * cos(rot), transY }
253 * { 0, 0, 1 }
254 */
255 const Math::Vec4 rotateScale =
256 [](const RenderDataDefaultMaterial::InputMaterialUniforms::TextureData& data) {
257 if (data.rotation == 0.f) {
258 return Math::Vec4 { data.scale.x, 0.f, 0.f, data.scale.y };
259 }
260 const float sinR = Math::sin(data.rotation);
261 const float cosR = Math::cos(data.rotation);
262 return Math::Vec4 { data.scale.x * cosR, data.scale.y * sinR, data.scale.x * -sinR,
263 data.scale.y * cosR };
264 }(tex);
265
266 // set transform bit for texture index
267 static constexpr const auto identityRs = Math::Vec4(1.f, 0.f, 0.f, 1.f);
268 const bool hasTransform =
269 (tex.translation.x != 0.f) || (tex.translation.y != 0.f) || (rotateScale != identityRs);
270 if (!hasTransform) {
271 // this matches packing identityRs.xy, identityRs.zw, translation.xy, and 0,0
272 static constexpr const auto identity = Math::UVec4(0x3c000000, 0x00003c00, 0x0, 0x0);
273 amu.transforms.packed[CORE_MATERIAL_PACK_TEX_BASE_COLOR_UV_IDX + i] = identity;
274 } else {
275 // using PackMaterialUVec costs packing the last unused zeros
276 amu.transforms.packed[CORE_MATERIAL_PACK_TEX_BASE_COLOR_UV_IDX + i] =
277 Math::UVec4 { Math::PackHalf2X16({ rotateScale.x, rotateScale.y }),
278 Math::PackHalf2X16({ rotateScale.z, rotateScale.w }),
279 Math::PackHalf2X16({ tex.translation.x, tex.translation.y }), 0 };
280 transformBits |= static_cast<uint32_t>(hasTransform) << i;
281 }
282 }
283 }
284 const uint32_t transformInfoBits = (input.texCoordSetBits << 16u) | transformBits; // 16: left remove 16 bits
285 const Math::UVec4 indices = { uint32_t(input.id >> 32u), uint32_t(input.id & 0xFFFFffff), 0u, 0u };
286
287 // finalize factors
288 amu.factors.factors[RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT] = { input.alphaCutoff,
289 *reinterpret_cast<const float*>(&transformInfoBits), 0.0f, 0.0f };
290 amu.factors.indices = indices;
291 // finalize transforms
292 amu.transforms.packed[CORE_MATERIAL_PACK_ADDITIONAL_IDX] = { Math::PackHalf2X16({ input.alphaCutoff, 0.f }),
293 transformInfoBits, 0, 0 };
294 amu.transforms.indices = indices;
295 return amu;
296 }
297
DestroyMaterialByIndex(const uint32_t index,RenderDataStoreDefaultMaterial::AllMaterialData & matData)298 void DestroyMaterialByIndex(const uint32_t index, RenderDataStoreDefaultMaterial::AllMaterialData& matData)
299 {
300 // explicit clear
301 if (index < matData.data.size()) {
302 matData.data[index] = {};
303 matData.allUniforms[index] = {};
304 matData.handles[index] = {};
305 matData.customPropertyData[index] = {};
306 matData.customResourceData[index] = {};
307
308 // add for re-use
309 matData.availableIndices.push_back(index);
310 }
311 }
312
GetCertainMaterialIndex(const uint32_t index,const RenderDataStoreDefaultMaterial::AllMaterialData & matData)313 uint32_t GetCertainMaterialIndex(const uint32_t index, const RenderDataStoreDefaultMaterial::AllMaterialData& matData)
314 {
315 if (index >= static_cast<uint32_t>(matData.data.size())) {
316 // built-in material in 0
317 CORE_ASSERT(!matData.data.empty());
318 if (!matData.data.empty()) {
319 return 0;
320 }
321 }
322 return index;
323 }
324
GetDefaultInputMaterialUniforms()325 constexpr RenderDataDefaultMaterial::InputMaterialUniforms GetDefaultInputMaterialUniforms()
326 {
327 CORE_STATIC_ASSERT(RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT == 11U);
328
329 // needs to match material component for default data
330 RenderDataDefaultMaterial::InputMaterialUniforms imu;
331 imu.textureData[0U].factor = { 1.f, 1.f, 1.f, 1.f }; // base color opaque white
332 imu.textureData[1U].factor = { 1.f, 0.f, 0.f, 0.f }; // normal scale 1
333 imu.textureData[2U].factor = { 0.f, 1.f, 1.f, 0.04f }; // material (empty, roughness, metallic, reflectance)
334 imu.textureData[3U].factor = { 0.f, 0.f, 0.f, 1.f }; // emissive 0
335 imu.textureData[4U].factor = { 1.f, 0.f, 0.f, 0.f }; // ambient occlusion 1
336 imu.textureData[5U].factor = { 0.f, 0.f, 0.f, 0.f }; // clearcoat intensity 0
337 imu.textureData[6U].factor = { 0.f, 0.f, 0.f, 0.f }; // clearcoat roughness 0
338 imu.textureData[7U].factor = { 1.f, 0.f, 0.f, 0.f }; // clearcoat normal scale 1
339 imu.textureData[8U].factor = { 0.f, 0.f, 0.f, 0.f }; // sheen color black, roughness 0
340 imu.textureData[9U].factor = { 0.f, 0.f, 0.f, 0.f }; // transmission 0
341 imu.textureData[10U].factor = { 1.f, 1.f, 1.f, 1.f }; // specular white
342
343 imu.id = 0xFFFFffffU;
344
345 return imu;
346 }
347 } // namespace
348
RenderDataStoreDefaultMaterial(RENDER_NS::IRenderContext & renderContext,const string_view name)349 RenderDataStoreDefaultMaterial::RenderDataStoreDefaultMaterial(
350 RENDER_NS::IRenderContext& renderContext, const string_view name)
351 : name_(name), gpuResourceMgr_(renderContext.GetDevice().GetGpuResourceManager()),
352 shaderMgr_(renderContext.GetDevice().GetShaderManager())
353 {
354 GetDefaultRenderSlots();
355
356 // add default materials (~0U and max ~0ULL)
357 const uint32_t materialIndex = AddMaterialDataImpl(~0U, GetDefaultInputMaterialUniforms(), {}, {}, {}, {});
358 CORE_ASSERT(materialIndex == 0);
359 matData_.materialIdToIndex.insert_or_assign(0xFFFFffff, materialIndex);
360 matData_.materialIdToIndex.insert_or_assign(0xFFFFFFFFffffffff, materialIndex);
361 }
362
PostRender()363 void RenderDataStoreDefaultMaterial::PostRender()
364 {
365 Clear();
366 }
367
Clear()368 void RenderDataStoreDefaultMaterial::Clear()
369 {
370 // NOTE: clear is at the moment called typically two times
371 // this could be further optimized to know if clear has already been called
372
373 // release references
374 handleReferences_.clear();
375
376 submeshes_.clear();
377 meshData_.clear();
378 submeshJointMatrixIndices_.clear();
379
380 {
381 // NOTE: material data is not cleared automatically anymore
382 // we keep the data but update the resource references if data is used
383 // separate destroy
384
385 matData_.frameIndices.clear();
386 matData_.idHashToFrameIndex.clear();
387
388 #if (CORE3D_VALIDATION_ENABLED == 1)
389 vector<uint32_t> noIdRemoval;
390 #endif
391 // check that we have id of material (if not, then it's a single frame (usually) debug material
392 for (size_t idx = 0; idx < matData_.data.size(); ++idx) {
393 if (matData_.data[idx].noId) {
394 DestroyMaterialByIndex(static_cast<uint32_t>(idx), matData_);
395 #if (CORE3D_VALIDATION_ENABLED == 1)
396 // should not have id
397 noIdRemoval.push_back(static_cast<uint32_t>(idx));
398 #endif
399 }
400 }
401 #if (CORE3D_VALIDATION_ENABLED == 1)
402 for (const auto& ref : matData_.materialIdToIndex) {
403 for (const auto& noIdRef : noIdRemoval) {
404 if (ref.second == noIdRef) {
405 CORE_LOG_E("CORE3D_VALIDATION: Material removal issue");
406 }
407 }
408 }
409 noIdRemoval.clear();
410 #endif
411 }
412
413 submeshMaterialFlags_.clear();
414
415 for (auto& slotRef : slotToSubmeshIndices_) { // does not remove slots from use
416 slotRef.second.indices.clear();
417 slotRef.second.materialData.clear();
418 slotRef.second.objectCounts = {};
419 }
420
421 if (!submeshJointMatricesAllocator_.allocators.empty()) {
422 submeshJointMatricesAllocator_.currentIndex = 0;
423 if (submeshJointMatricesAllocator_.allocators.size() == 1) { // size is good for this frame
424 submeshJointMatricesAllocator_.allocators[submeshJointMatricesAllocator_.currentIndex]->Reset();
425 } else if (submeshJointMatricesAllocator_.allocators.size() > 1) {
426 size_t fullByteSize = 0;
427 for (auto& ref : submeshJointMatricesAllocator_.allocators) {
428 fullByteSize += ref->GetCurrentByteSize();
429 ref.reset();
430 }
431 submeshJointMatricesAllocator_.allocators.clear();
432 // create new single allocation for combined previous size and some extra bytes
433 submeshJointMatricesAllocator_.allocators.push_back(
434 make_unique<LinearAllocator>(fullByteSize, MEMORY_ALIGNMENT));
435 }
436 }
437
438 // NOTE: re-fetch if default slots are invalid
439 if (materialRenderSlots_.opaqueMask != 0) {
440 GetDefaultRenderSlots();
441 }
442 }
443
Ref()444 void RenderDataStoreDefaultMaterial::Ref()
445 {
446 refcnt_.fetch_add(1, std::memory_order_relaxed);
447 }
448
Unref()449 void RenderDataStoreDefaultMaterial::Unref()
450 {
451 if (std::atomic_fetch_sub_explicit(&refcnt_, 1, std::memory_order_release) == 1) {
452 std::atomic_thread_fence(std::memory_order_acquire);
453 delete this;
454 }
455 }
456
GetRefCount()457 int32_t RenderDataStoreDefaultMaterial::GetRefCount()
458 {
459 return refcnt_;
460 }
461
DestroyMaterialData(const uint64_t id)462 void RenderDataStoreDefaultMaterial::DestroyMaterialData(const uint64_t id)
463 {
464 // explicit material destruction
465 // NOTE: does not clear resource handle references for this frame
466
467 CORE_ASSERT(matData_.allUniforms.size() == matData_.data.size());
468 if (auto iter = matData_.materialIdToIndex.find(id); iter != matData_.materialIdToIndex.cend()) {
469 CORE_ASSERT(iter->second < matData_.allUniforms.size());
470 DestroyMaterialByIndex(iter->second, matData_);
471
472 // destroy from material map
473 matData_.materialIdToIndex.erase(iter);
474 }
475 }
476
DestroyMaterialData(const BASE_NS::array_view<uint64_t> ids)477 void RenderDataStoreDefaultMaterial::DestroyMaterialData(const BASE_NS::array_view<uint64_t> ids)
478 {
479 for (const auto& idRef : ids) {
480 DestroyMaterialData(idRef);
481 }
482 }
483
GetDefaultRenderSlots()484 void RenderDataStoreDefaultMaterial::GetDefaultRenderSlots()
485 {
486 materialRenderSlots_.defaultOpaqueRenderSlot =
487 shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
488 materialRenderSlots_.opaqueMask = (1ull << uint64_t(materialRenderSlots_.defaultOpaqueRenderSlot));
489 materialRenderSlots_.opaqueMask |=
490 (1ull << uint64_t(shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEFERRED_OPAQUE)));
491
492 materialRenderSlots_.defaultTranslucentRenderSlot =
493 shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT);
494 materialRenderSlots_.translucentMask = (1ull << uint64_t(materialRenderSlots_.defaultTranslucentRenderSlot));
495
496 materialRenderSlots_.defaultDepthRenderSlot =
497 shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH);
498 materialRenderSlots_.depthMask = (1ull << uint64_t(materialRenderSlots_.defaultDepthRenderSlot));
499 materialRenderSlots_.depthMask |=
500 (1ull << uint64_t(shaderMgr_.GetRenderSlotId(DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH_VSM)));
501 }
502
AddMaterialDataImpl(const uint32_t matIndex,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData,const array_view<const RenderHandleReference> customResourceData)503 uint32_t RenderDataStoreDefaultMaterial::AddMaterialDataImpl(const uint32_t matIndex,
504 const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
505 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
506 const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData,
507 const array_view<const RenderHandleReference> customResourceData)
508 {
509 uint32_t materialIndex = matIndex;
510 // matData_.frameIndices can have higher counts)
511 CORE_ASSERT(matData_.allUniforms.size() == matData_.data.size());
512 CORE_ASSERT(matData_.handles.size() == matData_.customPropertyData.size());
513 if ((materialIndex == ~0U) && (matData_.availableIndices.empty())) {
514 // totally new indices and material
515 materialIndex = static_cast<uint32_t>(matData_.allUniforms.size());
516 matData_.handles.push_back(ConvertMaterialHandleReferences(materialHandles, handleReferences_));
517 matData_.allUniforms.push_back(MaterialUniformsPackedFromInput(materialUniforms));
518 matData_.customPropertyData.push_back({});
519 matData_.customResourceData.push_back({});
520 matData_.data.push_back({});
521 } else {
522 if ((materialIndex == ~0U) && (!matData_.availableIndices.empty())) {
523 materialIndex = matData_.availableIndices.back();
524 matData_.availableIndices.pop_back();
525 }
526 if (materialIndex >= matData_.allUniforms.size()) {
527 CORE_ASSERT(true);
528 return ~0U;
529 }
530 matData_.handles[materialIndex] = ConvertMaterialHandleReferences(materialHandles, handleReferences_);
531 matData_.allUniforms[materialIndex] = MaterialUniformsPackedFromInput(materialUniforms);
532 matData_.customPropertyData[materialIndex].data.clear();
533 matData_.customResourceData[materialIndex] = {};
534 }
535
536 // NOTE: when material is added/updated, we need to keep the handle reference
537 // the user might not have local references
538
539 // not referenced yet this frame for rendering (false)
540 matData_.data[materialIndex].frameReferenced = false;
541 matData_.data[materialIndex].noId = false;
542 auto& currMaterialData = matData_.data[materialIndex].md;
543 currMaterialData = materialData;
544 ExtentRenderMaterialFlagsForComplexity(currMaterialData.materialType, currMaterialData.renderMaterialFlags);
545
546 if (!customData.empty()) {
547 const auto maxByteSize = Math::min(static_cast<uint32_t>(customData.size_bytes()),
548 RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_PROPERTY_BYTE_SIZE);
549 auto& cpdRef = matData_.customPropertyData[materialIndex];
550 cpdRef.data.resize(maxByteSize);
551 CloneData(cpdRef.data.data(), maxByteSize, customData.data(), maxByteSize);
552 }
553
554 if (!customResourceData.empty()) {
555 auto& dataRef = matData_.customResourceData[materialIndex];
556 dataRef.resourceHandleCount = 0U;
557 dataRef.shaderHandle = materialData.materialShader.shader.GetHandle();
558 const uint32_t maxCount = Math::min(static_cast<uint32_t>(customResourceData.size()),
559 RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT);
560 for (uint32_t idx = 0; idx < maxCount; ++idx) {
561 if (customResourceData[idx]) {
562 // NOTE: when material is added/updated, we need to keep the handle reference
563 // the user might not have local references
564 handleReferences_.push_back(customResourceData[idx]);
565 dataRef.resourceHandles[dataRef.resourceHandleCount++] = customResourceData[idx].GetHandle();
566 }
567 }
568 }
569
570 return materialIndex;
571 }
572
UpdateMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData,const array_view<const RenderHandleReference> customBindings)573 uint32_t RenderDataStoreDefaultMaterial::UpdateMaterialData(const uint64_t id,
574 const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
575 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
576 const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData,
577 const array_view<const RenderHandleReference> customBindings)
578 {
579 CORE_STATIC_ASSERT(RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT == MaterialComponent::TEXTURE_COUNT);
580
581 CORE_ASSERT(matData_.allUniforms.size() == matData_.data.size());
582 if (const auto iter = matData_.materialIdToIndex.find(id); iter != matData_.materialIdToIndex.cend()) {
583 CORE_ASSERT(iter->second < matData_.allUniforms.size());
584 const uint32_t materialIndex = AddMaterialDataImpl(
585 iter->second, materialUniforms, materialHandles, materialData, customData, customBindings);
586 CORE_UNUSED(materialIndex);
587 CORE_ASSERT(materialIndex == iter->second);
588 return iter->second;
589 } else {
590 // create new
591 const uint32_t materialIndex =
592 AddMaterialDataImpl(~0U, materialUniforms, materialHandles, materialData, customData, customBindings);
593 matData_.materialIdToIndex.insert_or_assign(id, materialIndex);
594 return materialIndex;
595 }
596 }
597
UpdateMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customData)598 uint32_t RenderDataStoreDefaultMaterial::UpdateMaterialData(const uint64_t id,
599 const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
600 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
601 const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customData)
602 {
603 return UpdateMaterialData(id, materialUniforms, materialHandles, materialData, customData, {});
604 }
605
UpdateMaterialData(const uint64_t id,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData)606 uint32_t RenderDataStoreDefaultMaterial::UpdateMaterialData(const uint64_t id,
607 const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
608 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
609 const RenderDataDefaultMaterial::MaterialData& materialData)
610 {
611 return UpdateMaterialData(id, materialUniforms, materialHandles, materialData, {}, {});
612 }
613
AddFrameMaterialData(const uint64_t id,const uint32_t instanceCount)614 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(
615 const uint64_t id, const uint32_t instanceCount)
616 {
617 // we expect the material to be in data
618 #if (CORE3D_VALIDATION_ENABLED == 1)
619 if (const auto iter = matData_.materialIdToIndex.find(id); iter == matData_.materialIdToIndex.cend()) {
620 const string name = string("AddFrameMaterialData" + BASE_NS::to_hex(id));
621 CORE_LOG_ONCE_W(name, "AddFrameMaterialData id not updated prior add");
622 }
623 #endif
624 // NOTE: we need to update the reference counts for rendering time
625 // material resource references are not kept alive when just updating the materials
626 // with this approach the resources can be destroyed during the application time
627
628 const uint64_t searchId = HashMaterialId(id, static_cast<uint32_t>(instanceCount));
629 if (const auto iter = matData_.idHashToFrameIndex.find(searchId); iter != matData_.idHashToFrameIndex.cend()) {
630 if (iter->second < static_cast<uint32_t>(matData_.frameIndices.size())) {
631 // these have been already updated with reference counts, just return the indices
632 return { matData_.frameIndices[iter->second], iter->second };
633 }
634 } else if (const auto matIter = matData_.materialIdToIndex.find(id); matIter != matData_.materialIdToIndex.cend()) {
635 // append instance count amount
636 const uint32_t frameMaterialOffset = static_cast<uint32_t>(matData_.frameIndices.size());
637 matData_.idHashToFrameIndex.insert_or_assign(searchId, frameMaterialOffset);
638 RenderFrameMaterialIndices rfmi { matIter->second, frameMaterialOffset };
639 matData_.frameIndices.append(instanceCount, matIter->second); // add material index
640
641 // update reference counts for this frame if needed
642 UpdateFrameMaterialResourceReferences(matIter->second);
643
644 return rfmi;
645 }
646 return {};
647 }
648
AddFrameMaterialData(const uint64_t id)649 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(const uint64_t id)
650 {
651 return AddFrameMaterialData(id, 1U);
652 }
653
AddFrameMaterialData(const BASE_NS::array_view<const uint64_t> ids)654 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(
655 const BASE_NS::array_view<const uint64_t> ids)
656 {
657 if (!ids.empty()) {
658 RenderFrameMaterialIndices rfmi = AddFrameMaterialData(ids[0U], 1U);
659 for (size_t idx = 1; idx < ids.size(); ++idx) {
660 AddFrameMaterialData(ids[idx], 1U);
661 }
662 return rfmi;
663 } else {
664 return {};
665 }
666 }
667
AddFrameMaterialData(const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const BASE_NS::array_view<const uint8_t> customPropertyData,const BASE_NS::array_view<const RENDER_NS::RenderHandleReference> customBindings)668 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialData(
669 const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
670 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
671 const RenderDataDefaultMaterial::MaterialData& materialData,
672 const BASE_NS::array_view<const uint8_t> customPropertyData,
673 const BASE_NS::array_view<const RENDER_NS::RenderHandleReference> customBindings)
674 {
675 // NOTE: this data is added as is
676 // cannot be retrieved with id or anything
677 // mostly used for some debug meshes etc.
678 const uint32_t materialIndex =
679 AddMaterialDataImpl(~0U, materialUniforms, materialHandles, materialData, customPropertyData, customBindings);
680 // add for automatic destruction after rendering
681 if (materialIndex < matData_.data.size()) {
682 matData_.data[materialIndex].noId = true;
683 }
684
685 const uint32_t materialOffset = static_cast<uint32_t>(matData_.frameIndices.size());
686 matData_.frameIndices.push_back(materialIndex); // material index
687 return { materialIndex, materialOffset };
688 }
689
AddFrameMaterialInstanceData(uint32_t materialIndex,uint32_t frameOffset,uint32_t instanceIndex)690 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddFrameMaterialInstanceData(
691 uint32_t materialIndex, uint32_t frameOffset, uint32_t instanceIndex)
692 {
693 const uint32_t frameFullOffset = frameOffset + instanceIndex;
694 if (frameFullOffset < static_cast<uint32_t>(matData_.frameIndices.size())) {
695 matData_.frameIndices[frameFullOffset] = materialIndex;
696 return { materialIndex, frameFullOffset };
697 }
698 return {};
699 }
700
UpdateFrameMaterialResourceReferences(const uint32_t materialIndex)701 void RenderDataStoreDefaultMaterial::UpdateFrameMaterialResourceReferences(const uint32_t materialIndex)
702 {
703 CORE_ASSERT(matData_.data.size() == matData_.handles.size());
704 CORE_ASSERT(matData_.data.size() == matData_.customResourceData.size());
705 if (materialIndex < static_cast<uint32_t>(matData_.handles.size())) {
706 auto& matDataRef = matData_.data[materialIndex];
707 if (!matDataRef.frameReferenced) {
708 // add references
709 matDataRef.frameReferenced = true;
710 const auto& handles = matData_.handles[materialIndex];
711 // NOTE: access to GPU resource manager is locking behaviour
712 // this can be evaluated further and possibly add array_view for get to get all with a one lock
713 for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT; ++idx) {
714 if (RenderHandleUtil::IsValid(handles.images[idx])) {
715 handleReferences_.push_back(gpuResourceMgr_.Get(handles.images[idx]));
716 }
717 if (RenderHandleUtil::IsValid(handles.samplers[idx])) {
718 handleReferences_.push_back(gpuResourceMgr_.Get(handles.samplers[idx]));
719 }
720 }
721 const auto& customHandles = matData_.customResourceData[materialIndex];
722 for (uint32_t idx = 0; idx < RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT; ++idx) {
723 if (RenderHandleUtil::IsValid(customHandles.resourceHandles[idx])) {
724 handleReferences_.push_back(gpuResourceMgr_.Get(customHandles.resourceHandles[idx]));
725 }
726 }
727 }
728 }
729 }
730
AddMeshData(const RenderMeshData & meshData)731 uint32_t RenderDataStoreDefaultMaterial::AddMeshData(const RenderMeshData& meshData)
732 {
733 const uint32_t meshIndex = static_cast<uint32_t>(meshData_.size());
734 meshData_.push_back(meshData);
735 return meshIndex;
736 }
737
AddSkinJointMatrices(const array_view<const Math::Mat4X4> skinJointMatrices,const array_view<const Math::Mat4X4> prevSkinJointMatrices)738 uint32_t RenderDataStoreDefaultMaterial::AddSkinJointMatrices(
739 const array_view<const Math::Mat4X4> skinJointMatrices, const array_view<const Math::Mat4X4> prevSkinJointMatrices)
740 {
741 // check max supported joint count
742 const uint32_t jointCount =
743 std::min(RenderDataDefaultMaterial::MAX_SKIN_MATRIX_COUNT, static_cast<uint32_t>(skinJointMatrices.size()));
744 uint32_t skinJointIndex = RenderSceneDataConstants::INVALID_INDEX;
745 if (jointCount > 0) {
746 const uint32_t byteSize = sizeof(Math::Mat4X4) * jointCount;
747 const uint32_t fullJointCount = jointCount * 2u;
748 Math::Mat4X4* jointMatrixData = AllocateMatrices(submeshJointMatricesAllocator_, fullJointCount);
749 if (jointMatrixData) {
750 CloneData(jointMatrixData, byteSize, skinJointMatrices.data(), byteSize);
751 if (skinJointMatrices.size() == prevSkinJointMatrices.size()) {
752 CloneData(jointMatrixData + jointCount, byteSize, prevSkinJointMatrices.data(), byteSize);
753 } else {
754 // copy current to previous if given prevSkinJointMatrices is not valid
755 CloneData(jointMatrixData + jointCount, byteSize, skinJointMatrices.data(), byteSize);
756 }
757 }
758 skinJointIndex = static_cast<uint32_t>(submeshJointMatrixIndices_.size());
759 submeshJointMatrixIndices_.push_back(
760 RenderDataDefaultMaterial::JointMatrixData { jointMatrixData, fullJointCount });
761 }
762 return skinJointIndex;
763 }
764
AddSubmesh(const RenderSubmeshWithHandleReference & submesh)765 void RenderDataStoreDefaultMaterial::AddSubmesh(const RenderSubmeshWithHandleReference& submesh)
766 {
767 const uint32_t materialIndex = GetCertainMaterialIndex(submesh.indices.materialIndex, matData_);
768 if (materialIndex < static_cast<uint32_t>(matData_.data.size())) {
769 uint32_t renderSlotCount = 0u;
770
771 // default support for 3 render slots
772 IShaderManager::RenderSlotData renderSlotData[SHADER_DEFAULT_RENDER_SLOT_COUNT] {};
773
774 const auto& matData = matData_.data[materialIndex].md;
775 renderSlotData[0u].shader = matData.materialShader.shader;
776 renderSlotData[0u].graphicsState = matData.materialShader.graphicsState;
777 constexpr uint32_t INVALID_RENDER_SLOT_ID = ~0u;
778 uint32_t renderSlotId = matData.customRenderSlotId;
779 if ((renderSlotId == INVALID_RENDER_SLOT_ID) && renderSlotData[0].graphicsState) {
780 renderSlotId = shaderMgr_.GetRenderSlotId(renderSlotData[0u].graphicsState);
781 }
782 if ((renderSlotId == INVALID_RENDER_SLOT_ID) && renderSlotData[0].shader) {
783 renderSlotId = shaderMgr_.GetRenderSlotId(renderSlotData[0u].shader);
784 }
785 // if all fails, render as opaque
786 if (renderSlotId == INVALID_RENDER_SLOT_ID) {
787 renderSlotId = materialRenderSlots_.defaultOpaqueRenderSlot;
788 }
789 renderSlotData[renderSlotCount].renderSlotId = renderSlotId;
790 renderSlotCount++;
791
792 if (matData.renderMaterialFlags & RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_CASTER_BIT) {
793 renderSlotData[renderSlotCount].renderSlotId = materialRenderSlots_.defaultDepthRenderSlot;
794 renderSlotData[renderSlotCount].shader = matData.depthShader.shader;
795 renderSlotData[renderSlotCount].graphicsState = matData.depthShader.graphicsState;
796 renderSlotCount++;
797 }
798 AddSubmesh(submesh, { renderSlotData, renderSlotCount });
799 }
800 }
801
AddSubmesh(const RenderSubmeshWithHandleReference & submesh,const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders)802 void RenderDataStoreDefaultMaterial::AddSubmesh(const RenderSubmeshWithHandleReference& submesh,
803 const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders)
804 {
805 #if (CORE3D_VALIDATION_ENABLED == 1)
806 ValidateSubmesh(submesh);
807 #endif
808 const uint32_t submeshIndex = static_cast<uint32_t>(submeshes_.size());
809 submeshes_.push_back(ConvertRenderSubmeshInput(submesh, handleReferences_));
810 auto& currSubmesh = submeshes_.back();
811
812 FillSubmeshImpl(renderSlotAndShaders, submeshIndex, currSubmesh);
813 }
814
FillSubmeshImpl(const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders,const uint32_t submeshIndex,RenderSubmesh & submesh)815 void RenderDataStoreDefaultMaterial::FillSubmeshImpl(
816 const array_view<const IShaderManager::RenderSlotData> renderSlotAndShaders, const uint32_t submeshIndex,
817 RenderSubmesh& submesh)
818 {
819 const uint32_t materialIndex = GetCertainMaterialIndex(submesh.indices.materialIndex, matData_);
820 submesh.indices.materialIndex = materialIndex; // if invalid -> store the default material
821 if (submesh.indices.meshIndex >= static_cast<uint32_t>(meshData_.size())) {
822 CORE_LOG_W("invalid mesh index (%u) given", submesh.indices.meshIndex);
823 submesh.indices.meshIndex = 0;
824 }
825 if ((submesh.submeshFlags & RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT) &&
826 (submesh.indices.skinJointIndex >= static_cast<uint32_t>(submeshJointMatrixIndices_.size()))) {
827 CORE_LOG_W("invalid skin joint index (%u) given", submesh.indices.skinJointIndex);
828 submesh.indices.skinJointIndex = RenderSceneDataConstants::INVALID_INDEX;
829 }
830
831 if (submesh.indices.materialIndex >= static_cast<uint32_t>(matData_.allUniforms.size())) {
832 // NOTE: shouldn't come here with basic usage
833 RenderFrameMaterialIndices rfmi = AddFrameMaterialData({}, {}, {}, {}, {});
834 submesh.indices.materialIndex = rfmi.index;
835 submesh.indices.materialFrameOffset = rfmi.frameOffset;
836 } else {
837 CORE_ASSERT(matData_.data.size() == matData_.allUniforms.size());
838 }
839 const RenderDataDefaultMaterial::MaterialData& perMatData = matData_.data[submesh.indices.materialIndex].md;
840 // batch render material sort layers if default values
841 PatchRenderMaterialSortLayers(perMatData, submesh.layers);
842
843 // combine with submesh specific flags
844 RenderMaterialFlags submeshRenderMaterialFlags =
845 perMatData.renderMaterialFlags | submesh.renderSubmeshMaterialFlags;
846 // remove instancing related things if not available
847 if (submesh.drawCommand.instanceCount) {
848 submeshRenderMaterialFlags |= RenderMaterialFlagBits::RENDER_MATERIAL_GPU_INSTANCING_BIT;
849 } else {
850 submeshRenderMaterialFlags &= COMBINED_GPU_INSTANCING_REMOVAL;
851 }
852 ExtentRenderMaterialFlagsFromSubmeshValues(submesh.submeshFlags, submeshRenderMaterialFlags);
853
854 const uint32_t renderHash =
855 HashSubmeshMaterials(perMatData.materialType, submeshRenderMaterialFlags, submesh.submeshFlags);
856 // depth optimized flags
857 const uint32_t renderDepthHash = HashSubmeshMaterials(perMatData.materialType,
858 submeshRenderMaterialFlags & RenderDataDefaultMaterial::RENDER_MATERIAL_DEPTH_FLAGS,
859 submesh.submeshFlags & RenderDataDefaultMaterial::RENDER_SUBMESH_DEPTH_FLAGS);
860 submeshMaterialFlags_.push_back(
861 RenderDataDefaultMaterial::SubmeshMaterialFlags { perMatData.materialType, submesh.submeshFlags,
862 perMatData.extraMaterialRenderingFlags, submeshRenderMaterialFlags, renderHash, renderDepthHash });
863
864 const uint16_t renderSortLayerHash = GetRenderSortLayerHash(submesh);
865 // add submeshs to slots
866 for (const auto& slotRef : renderSlotAndShaders) {
867 SlotSubmeshData& dataRef = slotToSubmeshIndices_[slotRef.renderSlotId];
868 dataRef.indices.push_back(submeshIndex);
869 // hash for sorting (material index is certain for the same material)
870 // shader and gfx state is not needed, because it's already in the material, or they are defaults
871 // inverse winding can affect the final graphics state but it's in renderHash
872 // we hash with material index, and render hash
873 // id generation does not matter to us, because this is per frame
874 uint32_t renderSortHash = submesh.indices.materialIndex;
875 HashCombine32Bit(renderSortHash, renderHash);
876 dataRef.materialData.push_back({ renderSortLayerHash, renderSortHash, submeshRenderMaterialFlags,
877 slotRef.shader.GetHandle(), slotRef.graphicsState.GetHandle() });
878
879 dataRef.objectCounts.submeshCount++;
880 if (submesh.indices.skinJointIndex != RenderSceneDataConstants::INVALID_INDEX) {
881 dataRef.objectCounts.skinCount++;
882 }
883 }
884 }
885
SetRenderSlots(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType,const BASE_NS::array_view<const uint32_t> renderSlotIds)886 void RenderDataStoreDefaultMaterial::SetRenderSlots(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType,
887 const BASE_NS::array_view<const uint32_t> renderSlotIds)
888 {
889 uint64_t mask = 0;
890 for (const auto renderSlotId : renderSlotIds) {
891 mask |= 1ULL << uint64_t(renderSlotId);
892 }
893 if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_OPAQUE) {
894 materialRenderSlots_.opaqueMask = mask;
895 } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_TRANSLUCENT) {
896 materialRenderSlots_.translucentMask = mask;
897 } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_DEPTH) {
898 materialRenderSlots_.depthMask = mask;
899 }
900 }
901
GetRenderSlotMask(const RenderDataDefaultMaterial::MaterialSlotType materialSlotType) const902 uint64_t RenderDataStoreDefaultMaterial::GetRenderSlotMask(
903 const RenderDataDefaultMaterial::MaterialSlotType materialSlotType) const
904 {
905 uint64_t mask = 0;
906 if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_OPAQUE) {
907 mask = materialRenderSlots_.opaqueMask;
908 } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_TRANSLUCENT) {
909 mask = materialRenderSlots_.translucentMask;
910 } else if (materialSlotType == RenderDataDefaultMaterial::MaterialSlotType::SLOT_TYPE_DEPTH) {
911 mask = materialRenderSlots_.depthMask;
912 }
913 return mask;
914 }
915
GetRenderSlotIdFromMasks(const uint32_t renderSlotId) const916 uint32_t RenderDataStoreDefaultMaterial::GetRenderSlotIdFromMasks(const uint32_t renderSlotId) const
917 {
918 // compare to masks
919 const uint64_t renderSlotMask = 1ULL << uint64_t(renderSlotId & 0x3F); // 63
920 uint32_t newRenderSlotId = renderSlotId;
921 if (renderSlotMask & materialRenderSlots_.opaqueMask) {
922 newRenderSlotId = materialRenderSlots_.defaultOpaqueRenderSlot;
923 } else if (renderSlotMask & materialRenderSlots_.translucentMask) {
924 newRenderSlotId = materialRenderSlots_.defaultTranslucentRenderSlot;
925 } else if (renderSlotMask & materialRenderSlots_.depthMask) {
926 newRenderSlotId = materialRenderSlots_.defaultDepthRenderSlot;
927 }
928 return newRenderSlotId;
929 }
930
GetSlotSubmeshIndices(const uint32_t renderSlotId) const931 array_view<const uint32_t> RenderDataStoreDefaultMaterial::GetSlotSubmeshIndices(const uint32_t renderSlotId) const
932 {
933 const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
934 if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
935 const auto& slotRef = iter->second;
936 return slotRef.indices;
937 } else {
938 return {};
939 }
940 }
941
942 array_view<const RenderDataDefaultMaterial::SlotMaterialData>
GetSlotSubmeshMaterialData(const uint32_t renderSlotId) const943 RenderDataStoreDefaultMaterial::GetSlotSubmeshMaterialData(const uint32_t renderSlotId) const
944 {
945 const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
946 if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
947 const auto& slotRef = iter->second;
948 return slotRef.materialData;
949 } else {
950 return {};
951 }
952 }
953
GetSlotObjectCounts(const uint32_t renderSlotId) const954 RenderDataDefaultMaterial::ObjectCounts RenderDataStoreDefaultMaterial::GetSlotObjectCounts(
955 const uint32_t renderSlotId) const
956 {
957 const uint32_t rsId = GetRenderSlotIdFromMasks(renderSlotId);
958 if (const auto iter = slotToSubmeshIndices_.find(rsId); iter != slotToSubmeshIndices_.cend()) {
959 return {
960 static_cast<uint32_t>(meshData_.size()),
961 iter->second.objectCounts.submeshCount,
962 iter->second.objectCounts.skinCount,
963 static_cast<uint32_t>(matData_.frameIndices.size()),
964 static_cast<uint32_t>(matData_.allUniforms.size()),
965 };
966 } else {
967 return {};
968 }
969 }
970
GetObjectCounts() const971 RenderDataDefaultMaterial::ObjectCounts RenderDataStoreDefaultMaterial::GetObjectCounts() const
972 {
973 return {
974 static_cast<uint32_t>(meshData_.size()),
975 static_cast<uint32_t>(submeshes_.size()),
976 static_cast<uint32_t>(submeshJointMatrixIndices_.size()),
977 static_cast<uint32_t>(matData_.frameIndices.size()),
978 static_cast<uint32_t>(matData_.allUniforms.size()),
979 };
980 }
981
GetSubmeshes() const982 array_view<const RenderSubmesh> RenderDataStoreDefaultMaterial::GetSubmeshes() const
983 {
984 return submeshes_;
985 }
986
GetMeshData() const987 array_view<const RenderMeshData> RenderDataStoreDefaultMaterial::GetMeshData() const
988 {
989 return meshData_;
990 }
991
992 array_view<const RenderDataDefaultMaterial::JointMatrixData>
GetMeshJointMatrices() const993 RenderDataStoreDefaultMaterial::GetMeshJointMatrices() const
994 {
995 return submeshJointMatrixIndices_;
996 }
997
GetSubmeshJointMatrixData(const uint32_t skinJointIndex) const998 array_view<const Math::Mat4X4> RenderDataStoreDefaultMaterial::GetSubmeshJointMatrixData(
999 const uint32_t skinJointIndex) const
1000 {
1001 if (skinJointIndex < static_cast<uint32_t>(submeshJointMatrixIndices_.size())) {
1002 const RenderDataDefaultMaterial::JointMatrixData& jm = submeshJointMatrixIndices_[skinJointIndex];
1003 return array_view<const Math::Mat4X4>(jm.data, static_cast<size_t>(jm.count));
1004 } else {
1005 return {};
1006 }
1007 }
1008
1009 array_view<const RenderDataDefaultMaterial::AllMaterialUniforms>
GetMaterialUniforms() const1010 RenderDataStoreDefaultMaterial::GetMaterialUniforms() const
1011 {
1012 return matData_.allUniforms;
1013 }
1014
GetMaterialFrameIndices() const1015 array_view<const uint32_t> RenderDataStoreDefaultMaterial::GetMaterialFrameIndices() const
1016 {
1017 return matData_.frameIndices;
1018 }
1019
GetMaterialHandles() const1020 array_view<const RenderDataDefaultMaterial::MaterialHandles> RenderDataStoreDefaultMaterial::GetMaterialHandles() const
1021 {
1022 return matData_.handles;
1023 }
1024
GetMaterialCustomPropertyData(const uint32_t materialIndex) const1025 array_view<const uint8_t> RenderDataStoreDefaultMaterial::GetMaterialCustomPropertyData(
1026 const uint32_t materialIndex) const
1027 {
1028 if (materialIndex < static_cast<uint32_t>(matData_.customPropertyData.size())) {
1029 return matData_.customPropertyData[materialIndex].data;
1030 } else {
1031 return {};
1032 }
1033 }
1034
1035 array_view<const RenderDataDefaultMaterial::SubmeshMaterialFlags>
GetSubmeshMaterialFlags() const1036 RenderDataStoreDefaultMaterial::GetSubmeshMaterialFlags() const
1037 {
1038 return submeshMaterialFlags_;
1039 }
1040
1041 array_view<const RenderDataDefaultMaterial::CustomResourceData>
GetCustomResourceHandles() const1042 RenderDataStoreDefaultMaterial::GetCustomResourceHandles() const
1043 {
1044 return matData_.customResourceData;
1045 }
1046
GenerateRenderHash(const RenderDataDefaultMaterial::SubmeshMaterialFlags & flags) const1047 uint32_t RenderDataStoreDefaultMaterial::GenerateRenderHash(
1048 const RenderDataDefaultMaterial::SubmeshMaterialFlags& flags) const
1049 {
1050 return HashSubmeshMaterials(flags.materialType, flags.renderMaterialFlags, flags.submeshFlags);
1051 }
1052
GetMaterialIndex(const uint64_t id) const1053 uint32_t RenderDataStoreDefaultMaterial::GetMaterialIndex(const uint64_t id) const
1054 {
1055 if (const auto iter = matData_.materialIdToIndex.find(id); iter != matData_.materialIdToIndex.cend()) {
1056 return iter->second;
1057 } else {
1058 return RenderSceneDataConstants::INVALID_INDEX;
1059 }
1060 }
1061
AddRenderSlotSubmeshesFrameMaterialData(const uint32_t toSlotId,const array_view<const uint32_t> fromSlotIds,const RenderDataDefaultMaterial::InputMaterialUniforms & materialUniforms,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & materialHandles,const RenderDataDefaultMaterial::MaterialData & materialData,const array_view<const uint8_t> customPropertyData,const array_view<const RenderHandleReference> customBindings)1062 RenderFrameMaterialIndices RenderDataStoreDefaultMaterial::AddRenderSlotSubmeshesFrameMaterialData(
1063 const uint32_t toSlotId, const array_view<const uint32_t> fromSlotIds,
1064 const RenderDataDefaultMaterial::InputMaterialUniforms& materialUniforms,
1065 const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& materialHandles,
1066 const RenderDataDefaultMaterial::MaterialData& materialData, const array_view<const uint8_t> customPropertyData,
1067 const array_view<const RenderHandleReference> customBindings)
1068 {
1069 const RenderFrameMaterialIndices rfmi =
1070 AddFrameMaterialData(materialUniforms, materialHandles, materialData, customPropertyData, customBindings);
1071
1072 const IShaderManager::RenderSlotData rds { toSlotId, {}, {}, {}, {} };
1073 const array_view<const IShaderManager::RenderSlotData> rdsView = { &rds, 1U };
1074 for (uint32_t slotIdx = 0U; slotIdx < fromSlotIds.size(); ++slotIdx) {
1075 const uint32_t currSlotIdx = fromSlotIds[slotIdx];
1076 const auto slotSubmeshIndices = GetSlotSubmeshIndices(currSlotIdx);
1077 submeshes_.reserve(submeshes_.size() + slotSubmeshIndices.size());
1078 for (uint32_t ssIdx = 0U; ssIdx < static_cast<uint32_t>(slotSubmeshIndices.size()); ++ssIdx) {
1079 const uint32_t currSubmeshIdx = slotSubmeshIndices[ssIdx];
1080 if (currSubmeshIdx < static_cast<uint32_t>(submeshes_.size())) {
1081 // duplicate
1082 RenderSubmesh newSubmesh = submeshes_[currSubmeshIdx];
1083 newSubmesh.indices.materialIndex = rfmi.index;
1084 newSubmesh.indices.materialFrameOffset = rfmi.frameOffset;
1085
1086 const uint32_t submeshIndex = static_cast<uint32_t>(submeshes_.size());
1087 submeshes_.push_back(newSubmesh);
1088
1089 auto& currSubmesh = submeshes_.back();
1090 FillSubmeshImpl(rdsView, submeshIndex, currSubmesh);
1091 }
1092 }
1093 }
1094 return rfmi;
1095 }
1096
1097 // for plugin / factory interface
Create(RENDER_NS::IRenderContext & renderContext,char const * name)1098 refcnt_ptr<IRenderDataStore> RenderDataStoreDefaultMaterial::Create(
1099 RENDER_NS::IRenderContext& renderContext, char const* name)
1100 {
1101 return refcnt_ptr<IRenderDataStore>(new RenderDataStoreDefaultMaterial(renderContext, name));
1102 }
1103 CORE3D_END_NAMESPACE()
1104