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_node_default_material_objects.h"
17
18 #include <3d/render/default_material_constants.h>
19 #include <3d/render/intf_render_data_store_default_material.h>
20 #include <base/containers/allocator.h>
21 #include <base/math/matrix.h>
22 #include <base/math/vector.h>
23 #include <core/log.h>
24 #include <core/namespace.h>
25 #include <render/datastore/intf_render_data_store.h>
26 #include <render/datastore/intf_render_data_store_manager.h>
27 #include <render/device/intf_gpu_resource_manager.h>
28 #include <render/device/pipeline_layout_desc.h>
29 #include <render/nodecontext/intf_render_node_context_manager.h>
30 #include <render/resource_handle.h>
31
32 namespace {
33 #include <3d/shaders/common/3d_dm_structures_common.h>
34 } // namespace
35
36 CORE3D_BEGIN_NAMESPACE()
37 using namespace BASE_NS;
38 using namespace RENDER_NS;
39
40 namespace {
41 constexpr uint32_t UBO_BIND_OFFSET_ALIGNMENT { PipelineLayoutConstants::MIN_UBO_BIND_OFFSET_ALIGNMENT_BYTE_SIZE };
42 constexpr uint32_t MIN_UBO_OBJECT_COUNT { CORE_UNIFORM_BUFFER_MAX_BIND_SIZE / UBO_BIND_OFFSET_ALIGNMENT };
43
Align(size_t value,size_t align)44 constexpr size_t Align(size_t value, size_t align)
45 {
46 if (value == 0) {
47 return 0;
48 }
49 return ((value + align) / align) * align;
50 }
51 } // namespace
52
InitNode(IRenderNodeContextManager & renderNodeContextMgr)53 void RenderNodeDefaultMaterialObjects::InitNode(IRenderNodeContextManager& renderNodeContextMgr)
54 {
55 renderNodeContextMgr_ = &renderNodeContextMgr;
56
57 const auto& renderNodeGraphData = renderNodeContextMgr_->GetRenderNodeGraphData();
58 stores_ = RenderNodeSceneUtil::GetSceneRenderDataStores(
59 renderNodeContextMgr, renderNodeGraphData.renderNodeGraphDataStoreName);
60 ProcessBuffers({ 1u, 1u, 1u, 1u });
61 }
62
PreExecuteFrame()63 void RenderNodeDefaultMaterialObjects::PreExecuteFrame()
64 {
65 // re-create needed gpu resources
66 const auto& renderDataStoreMgr = renderNodeContextMgr_->GetRenderDataStoreManager();
67 const IRenderDataStoreDefaultMaterial* dataStoreMaterial = static_cast<IRenderDataStoreDefaultMaterial*>(
68 renderDataStoreMgr.GetRenderDataStore(stores_.dataStoreNameMaterial));
69 // we need to have all buffers created (reason to have at least 1u)
70 if (dataStoreMaterial) {
71 const auto dsOc = dataStoreMaterial->GetObjectCounts();
72 const ObjectCounts oc {
73 Math::max(dsOc.meshCount, 1u),
74 Math::max(dsOc.submeshCount, 1u),
75 Math::max(dsOc.skinCount, 1u),
76 Math::max(dsOc.materialCount, 1u),
77 };
78 ProcessBuffers(oc);
79 } else {
80 ProcessBuffers({ 1u, 1u, 1u, 1u });
81 }
82 }
83
ExecuteFrame(IRenderCommandList & cmdList)84 void RenderNodeDefaultMaterialObjects::ExecuteFrame(IRenderCommandList& cmdList)
85 {
86 const auto& renderDataStoreMgr = renderNodeContextMgr_->GetRenderDataStoreManager();
87 const auto* dataStoreMaterial = static_cast<IRenderDataStoreDefaultMaterial*>(
88 renderDataStoreMgr.GetRenderDataStore(stores_.dataStoreNameMaterial));
89
90 if (dataStoreMaterial) {
91 UpdateBuffers(*dataStoreMaterial);
92 } else {
93 CORE_LOG_E("invalid render data store in RenderNodeDefaultMaterialObjects");
94 }
95 }
96
UpdateBuffers(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)97 void RenderNodeDefaultMaterialObjects::UpdateBuffers(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
98 {
99 UpdateMeshBuffer(dataStoreMaterial);
100 UpdateSkinBuffer(dataStoreMaterial);
101 UpdateMaterialBuffers(dataStoreMaterial);
102 }
103
UpdateMeshBuffer(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)104 void RenderNodeDefaultMaterialObjects::UpdateMeshBuffer(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
105 {
106 IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
107 // mesh data is copied to single buffer with UBO_BIND_OFFSET_ALIGNMENT alignments
108 if (auto meshDataPtr = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.mesh.GetHandle())); meshDataPtr) {
109 constexpr uint32_t meshByteSize = sizeof(DefaultMaterialSingleMeshStruct);
110 CORE_STATIC_ASSERT(meshByteSize >= UBO_BIND_OFFSET_ALIGNMENT);
111 CORE_STATIC_ASSERT(sizeof(RenderMeshData) == sizeof(DefaultMaterialSingleMeshStruct));
112 const auto* meshDataPtrEnd = meshDataPtr + meshByteSize * objectCounts_.maxMeshCount;
113 if (const auto meshData = dataStoreMaterial.GetMeshData(); !meshData.empty()) {
114 // clone all at once, they are in order
115 const size_t cloneByteSize = meshData.size_bytes();
116 if (!CloneData(meshDataPtr, size_t(meshDataPtrEnd - meshDataPtr), meshData.data(), cloneByteSize)) {
117 CORE_LOG_I("meshData ubo copying failed");
118 }
119 }
120
121 gpuResourceMgr.UnmapBuffer(ubos_.mesh.GetHandle());
122 }
123 }
124
UpdateSkinBuffer(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)125 void RenderNodeDefaultMaterialObjects::UpdateSkinBuffer(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
126 {
127 IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
128 // skindata is copied to a single buffer with sizeof(DefaultMaterialSkinStruct) offset
129 // skin offset for submesh is calculated submesh.skinIndex * sizeof(DefaultMaterialSkinStruct)
130 // NOTE: the size could be optimized, but render data store should calculate correct size with alignment
131 if (auto skinData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.submeshSkin.GetHandle())); skinData) {
132 CORE_STATIC_ASSERT(
133 RenderDataDefaultMaterial::MAX_SKIN_MATRIX_COUNT * 2u == CORE_DEFAULT_MATERIAL_MAX_JOINT_COUNT);
134 const auto* skinDataEnd = skinData + sizeof(DefaultMaterialSkinStruct) * objectCounts_.maxSkinCount;
135 const auto meshJointMatrices = dataStoreMaterial.GetMeshJointMatrices();
136 for (const auto& jointRef : meshJointMatrices) {
137 // we copy first current frame matrices, then previous frame
138 const size_t copyCount = static_cast<size_t>(jointRef.count / 2u);
139 const size_t copySize = copyCount * sizeof(Math::Mat4X4);
140 const size_t ptrOffset = CORE_DEFAULT_MATERIAL_PREV_JOINT_OFFSET * sizeof(Math::Mat4X4);
141 if (!CloneData(skinData, size_t(skinDataEnd - skinData), jointRef.data, copySize)) {
142 CORE_LOG_I("skinData ubo copying failed");
143 }
144 if (!CloneData(skinData + ptrOffset, size_t(skinDataEnd - (skinData + ptrOffset)),
145 jointRef.data + copyCount, copySize)) {
146 CORE_LOG_I("skinData ubo copying failed");
147 }
148 skinData = skinData + sizeof(DefaultMaterialSkinStruct);
149 }
150
151 gpuResourceMgr.UnmapBuffer(ubos_.submeshSkin.GetHandle());
152 }
153 }
154
UpdateMaterialBuffers(const IRenderDataStoreDefaultMaterial & dataStoreMaterial)155 void RenderNodeDefaultMaterialObjects::UpdateMaterialBuffers(const IRenderDataStoreDefaultMaterial& dataStoreMaterial)
156 {
157 IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
158 // material data is copied to single buffer with UBO_BIND_OFFSET_ALIGNMENT alignment
159 auto matFactorData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.mat.GetHandle()));
160 auto matTransformData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.matTransform.GetHandle()));
161 auto userMaterialData = reinterpret_cast<uint8_t*>(gpuResourceMgr.MapBuffer(ubos_.userMat.GetHandle()));
162 if (matFactorData && matTransformData && userMaterialData) {
163 const auto* matFactorDataEnd = matFactorData + UBO_BIND_OFFSET_ALIGNMENT * objectCounts_.maxMaterialCount;
164 const auto* matTransformDataEnd = matTransformData + UBO_BIND_OFFSET_ALIGNMENT * objectCounts_.maxMaterialCount;
165 const auto* userMaterialDataEnd = userMaterialData + UBO_BIND_OFFSET_ALIGNMENT * objectCounts_.maxMaterialCount;
166 const auto materialUniforms = dataStoreMaterial.GetMaterialUniforms();
167 for (uint32_t matIdx = 0; matIdx < materialUniforms.size(); ++matIdx) {
168 const RenderDataDefaultMaterial::AllMaterialUniforms& uniforms = materialUniforms[matIdx];
169 if (!CloneData(matFactorData, size_t(matFactorDataEnd - matFactorData), &uniforms.factors,
170 sizeof(RenderDataDefaultMaterial::MaterialUniforms))) {
171 CORE_LOG_I("materialFactorData ubo copying failed");
172 }
173 if (!CloneData(matTransformData, size_t(matTransformDataEnd - matTransformData), &uniforms.transforms,
174 sizeof(RenderDataDefaultMaterial::MaterialPackedUniforms))) {
175 CORE_LOG_I("materialTransformData ubo copying failed");
176 }
177 const auto materialCustomProperties = dataStoreMaterial.GetMaterialCustomPropertyData(matIdx);
178 if (!materialCustomProperties.empty()) {
179 CORE_ASSERT(materialCustomProperties.size_bytes() <= UBO_BIND_OFFSET_ALIGNMENT);
180 if (!CloneData(userMaterialData, size_t(userMaterialDataEnd - userMaterialData),
181 materialCustomProperties.data(), materialCustomProperties.size_bytes())) {
182 CORE_LOG_I("userMaterialData ubo copying failed");
183 }
184 }
185 matFactorData = matFactorData + UBO_BIND_OFFSET_ALIGNMENT;
186 matTransformData = matTransformData + UBO_BIND_OFFSET_ALIGNMENT;
187 userMaterialData = userMaterialData + UBO_BIND_OFFSET_ALIGNMENT;
188 }
189
190 gpuResourceMgr.UnmapBuffer(ubos_.mat.GetHandle());
191 gpuResourceMgr.UnmapBuffer(ubos_.matTransform.GetHandle());
192 gpuResourceMgr.UnmapBuffer(ubos_.userMat.GetHandle());
193 }
194 }
195
ProcessBuffers(const ObjectCounts & objectCounts)196 void RenderNodeDefaultMaterialObjects::ProcessBuffers(const ObjectCounts& objectCounts)
197 {
198 auto& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
199 constexpr uint32_t overEstimate { 16u };
200 constexpr uint32_t baseStructSize = CORE_UNIFORM_BUFFER_MAX_BIND_SIZE;
201 constexpr uint32_t singleComponentStructSize = UBO_BIND_OFFSET_ALIGNMENT;
202 const string_view us = stores_.dataStoreNameScene;
203 // instancing utilization for mesh and materials
204 if (objectCounts_.maxMeshCount < objectCounts.maxMeshCount) {
205 // mesh matrix uses max ubo bind size to utilize gpu instancing
206 CORE_STATIC_ASSERT(sizeof(DefaultMaterialMeshStruct) <= PipelineLayoutConstants::MAX_UBO_BIND_BYTE_SIZE);
207 objectCounts_.maxMeshCount =
208 objectCounts.maxMeshCount + (objectCounts.maxMeshCount / overEstimate) + MIN_UBO_OBJECT_COUNT;
209
210 const uint32_t byteSize =
211 static_cast<uint32_t>(Align(objectCounts_.maxMeshCount * singleComponentStructSize, baseStructSize));
212 objectCounts_.maxMeshCount = (byteSize / singleComponentStructSize) - MIN_UBO_OBJECT_COUNT;
213 CORE_ASSERT((int32_t(byteSize / singleComponentStructSize) - int32_t(MIN_UBO_OBJECT_COUNT)) > 0);
214
215 ubos_.mesh = gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::MESH_DATA_BUFFER_NAME,
216 GpuBufferDesc { CORE_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
217 (CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT | CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT),
218 CORE_ENGINE_BUFFER_CREATION_DYNAMIC_RING_BUFFER, byteSize });
219 }
220 if (objectCounts_.maxSkinCount < objectCounts.maxSkinCount) {
221 objectCounts_.maxSkinCount = objectCounts.maxSkinCount + (objectCounts.maxSkinCount / overEstimate);
222
223 ubos_.submeshSkin = gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::SKIN_DATA_BUFFER_NAME,
224 GpuBufferDesc { CORE_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
225 (CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT | CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT),
226 CORE_ENGINE_BUFFER_CREATION_DYNAMIC_RING_BUFFER,
227 static_cast<uint32_t>(sizeof(DefaultMaterialSkinStruct)) * objectCounts_.maxSkinCount });
228 }
229 if (objectCounts_.maxMaterialCount < objectCounts.maxMaterialCount) {
230 CORE_STATIC_ASSERT(sizeof(RenderDataDefaultMaterial::MaterialUniforms) <= UBO_BIND_OFFSET_ALIGNMENT);
231 objectCounts_.maxMaterialCount =
232 objectCounts.maxMaterialCount + (objectCounts.maxMaterialCount / overEstimate) + MIN_UBO_OBJECT_COUNT;
233
234 const uint32_t byteSize =
235 static_cast<uint32_t>(Align(objectCounts_.maxMaterialCount * singleComponentStructSize, baseStructSize));
236 objectCounts_.maxMaterialCount = (byteSize / singleComponentStructSize) - MIN_UBO_OBJECT_COUNT;
237 CORE_ASSERT((int32_t(byteSize / singleComponentStructSize) - int32_t(MIN_UBO_OBJECT_COUNT)) > 0);
238
239 const GpuBufferDesc bufferDesc { CORE_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
240 (CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT | CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT),
241 CORE_ENGINE_BUFFER_CREATION_DYNAMIC_RING_BUFFER, byteSize };
242
243 ubos_.mat = gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::MATERIAL_DATA_BUFFER_NAME, bufferDesc);
244
245 ubos_.matTransform = gpuResourceMgr.Create(
246 us + DefaultMaterialMaterialConstants::MATERIAL_TRANSFORM_DATA_BUFFER_NAME, bufferDesc);
247
248 ubos_.userMat =
249 gpuResourceMgr.Create(us + DefaultMaterialMaterialConstants::MATERIAL_USER_DATA_BUFFER_NAME, bufferDesc);
250 }
251 }
252
253 // for plugin / factory interface
Create()254 RENDER_NS::IRenderNode* RenderNodeDefaultMaterialObjects::Create()
255 {
256 return new RenderNodeDefaultMaterialObjects();
257 }
258
Destroy(IRenderNode * instance)259 void RenderNodeDefaultMaterialObjects::Destroy(IRenderNode* instance)
260 {
261 delete static_cast<RenderNodeDefaultMaterialObjects*>(instance);
262 }
263 CORE3D_END_NAMESPACE()
264