3
0

ModelLod.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/RPI.Public/Model/ModelLod.h>
  9. #include <Atom/RPI.Public/Material/Material.h>
  10. #include <Atom/RHI/Factory.h>
  11. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  12. #include <AtomCore/Instance/InstanceDatabase.h>
  13. namespace AZ
  14. {
  15. namespace RPI
  16. {
  17. Data::Instance<ModelLod> ModelLod::FindOrCreate(const Data::Asset<ModelLodAsset>& lodAsset, const Data::Asset<ModelAsset>& modelAsset)
  18. {
  19. AZStd::any modelAssetAny{&modelAsset};
  20. return Data::InstanceDatabase<ModelLod>::Instance().FindOrCreate(
  21. Data::InstanceId::CreateFromAsset(lodAsset),
  22. lodAsset,
  23. &modelAssetAny);
  24. }
  25. AZStd::span<const ModelLod::Mesh> ModelLod::GetMeshes() const
  26. {
  27. return m_meshes;
  28. }
  29. Data::Instance<ModelLod> ModelLod::CreateInternal(const Data::Asset<ModelLodAsset>& lodAsset, const AZStd::any* modelAssetAny)
  30. {
  31. AZ_Assert(modelAssetAny != nullptr, "Invalid model asset param");
  32. auto modelAsset = AZStd::any_cast<Data::Asset<ModelAsset>*>(*modelAssetAny);
  33. Data::Instance<ModelLod> lod = aznew ModelLod();
  34. const RHI::ResultCode resultCode = lod->Init(lodAsset, *modelAsset);
  35. if (resultCode == RHI::ResultCode::Success)
  36. {
  37. return lod;
  38. }
  39. return nullptr;
  40. }
  41. RHI::ResultCode ModelLod::Init(const Data::Asset<ModelLodAsset>& lodAsset, const Data::Asset<ModelAsset>& modelAsset)
  42. {
  43. AZ_PROFILE_FUNCTION(RPI);
  44. for (const ModelLodAsset::Mesh& mesh : lodAsset->GetMeshes())
  45. {
  46. Mesh meshInstance;
  47. const BufferAssetView& indexBufferAssetView = mesh.GetIndexBufferAssetView();
  48. const Data::Asset<BufferAsset>& indexBufferAsset = indexBufferAssetView.GetBufferAsset();
  49. if (indexBufferAsset.GetId().IsValid())
  50. {
  51. Data::Instance<Buffer> indexBuffer = Buffer::FindOrCreate(indexBufferAsset);
  52. if (!indexBuffer)
  53. {
  54. return RHI::ResultCode::Fail;
  55. }
  56. const RHI::BufferViewDescriptor& bufferViewDescriptor = indexBufferAssetView.GetBufferViewDescriptor();
  57. RHI::IndexFormat indexFormat = RHI::IndexFormat::Uint32;
  58. if (bufferViewDescriptor.m_elementSize == sizeof(uint16_t))
  59. {
  60. indexFormat = RHI::IndexFormat::Uint16;
  61. }
  62. else if (bufferViewDescriptor.m_elementSize != sizeof(uint32_t))
  63. {
  64. AZ_Error("ModelLod", false, "Index buffer format is invalid. Only 16 or 32 bit indices are supported.");
  65. return RHI::ResultCode::InvalidOperation;
  66. }
  67. meshInstance.m_indexBufferView = RHI::IndexBufferView(
  68. *indexBuffer->GetRHIBuffer(),
  69. bufferViewDescriptor.m_elementOffset * bufferViewDescriptor.m_elementSize,
  70. bufferViewDescriptor.m_elementCount * bufferViewDescriptor.m_elementSize,
  71. indexFormat);
  72. RHI::DrawIndexed drawIndexed;
  73. drawIndexed.m_indexCount = bufferViewDescriptor.m_elementCount;
  74. drawIndexed.m_instanceCount = 1;
  75. meshInstance.m_drawArguments = drawIndexed;
  76. TrackBuffer(indexBuffer);
  77. }
  78. // [GFX TODO][ATOM-838]: We need to figure out how to load only the required streams from disk rather than all available streams.
  79. for (const auto& streamBufferInfo : mesh.GetStreamBufferInfoList())
  80. {
  81. if (!SetMeshInstanceData(streamBufferInfo, meshInstance))
  82. {
  83. return RHI::ResultCode::InvalidOperation;
  84. }
  85. }
  86. const ModelMaterialSlot& materialSlot = modelAsset->FindMaterialSlot(mesh.GetMaterialSlotId());
  87. meshInstance.m_materialSlotStableId = materialSlot.m_stableId;
  88. meshInstance.m_materialSlotName = materialSlot.m_displayName;
  89. if (materialSlot.m_defaultMaterialAsset.IsReady())
  90. {
  91. meshInstance.m_material = Material::FindOrCreate(materialSlot.m_defaultMaterialAsset);
  92. }
  93. m_meshes.emplace_back(AZStd::move(meshInstance));
  94. }
  95. m_isUploadPending = true;
  96. return RHI::ResultCode::Success;
  97. }
  98. ModelLod::StreamInfoList::const_iterator ModelLod::FindFirstUvStreamFromMesh(size_t meshIndex) const
  99. {
  100. const Mesh& mesh = m_meshes[meshIndex];
  101. auto firstUv = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [](const StreamBufferInfo& info) {
  102. return info.m_semantic.m_name.GetStringView().starts_with(RHI::ShaderSemantic::UvStreamSemantic);
  103. });
  104. return firstUv;
  105. }
  106. ModelLod::StreamInfoList::const_iterator ModelLod::FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const
  107. {
  108. const Mesh& mesh = m_meshes[meshIndex];
  109. // The default UV is used for cases that there are more UVs defined in the material than in the model.
  110. // The unmatched UV slots will be filled with the default UV.
  111. // The default UV is the first one matched in the shader input contract.
  112. auto defaultUv = mesh.m_streamInfo.end();
  113. for (const auto& materialUvNamePair : materialUvNameMap)
  114. {
  115. const AZ::Name& uvCustomName = materialUvNamePair.m_uvName;
  116. const RHI::ShaderSemantic& shaderInput = materialUvNamePair.m_shaderInput;
  117. // Use name matching first. Empty name can't be used because it will match other non-UV streams.
  118. if (!uvCustomName.IsEmpty())
  119. {
  120. defaultUv = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [&uvCustomName](const StreamBufferInfo& info)
  121. {
  122. return info.m_customName == uvCustomName;
  123. });
  124. }
  125. // Use semantic matching second if previous matching failed.
  126. if (defaultUv == mesh.m_streamInfo.end())
  127. {
  128. defaultUv = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [&shaderInput](const StreamBufferInfo& info)
  129. {
  130. return info.m_semantic == shaderInput;
  131. });
  132. }
  133. // Select the first matching
  134. if (defaultUv != mesh.m_streamInfo.end())
  135. {
  136. break;
  137. }
  138. }
  139. return defaultUv;
  140. }
  141. ModelLod::StreamInfoList::const_iterator ModelLod::FindMatchingStream(
  142. size_t meshIndex,
  143. const MaterialModelUvOverrideMap& materialModelUvMap,
  144. const MaterialUvNameMap& materialUvNameMap,
  145. const ShaderInputContract::StreamChannelInfo& contractStreamChannel,
  146. StreamInfoList::const_iterator defaultUv,
  147. StreamInfoList::const_iterator firstUv,
  148. UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const
  149. {
  150. const Mesh& mesh = m_meshes[meshIndex];
  151. auto iter = mesh.m_streamInfo.end();
  152. // Special matching for UV sets, we will match each UV shader input by following steps:
  153. // 1. The custom mapping from the name in material to the name in model (modelUvMap)
  154. // 2. The exact name matching between material and model (uvCustomNames <=> mesh.m_streamInfo.m_customName)
  155. // 3. The exact semantic matching between material and model (uvDefaultNames <=> mesh.m_streamInfo.m_semantic)
  156. // 4. If no matching found from the model, then the first applied model UV fills the slot.
  157. // e.g. (In practice, custom mapping should have the same size as material's UV, or empty if in places like material editor)
  158. // Material Model model UV map Final Mapping
  159. // UV0: Unwrapped UV0: Packed Unwrapped = Packed UV0: Unwrapped = UV0: Packed (rule 1: custom mapping)
  160. // UV1: Packed UV1: Unwrapped UV1: Packed = UV0: Packed (rule 2: default name mapping)
  161. // UV2: Tiled UV2: Repeated UV2: Tiled = UV2: Repeated (rule 3: semantic name mapping)
  162. // UV3: Extra UV3: Extra = UV0: Packed (rule 4: first filling)
  163. // ensure the semantic is a UV, otherwise skip name matching
  164. auto materialUvIter = AZStd::find_if(materialUvNameMap.begin(), materialUvNameMap.end(),
  165. [&contractStreamChannel](const UvNamePair& uvNamePair)
  166. {
  167. // Cost of linear search UV names is low because the size is extremely limited.
  168. return uvNamePair.m_shaderInput == contractStreamChannel.m_semantic;
  169. });
  170. const bool isUv = materialUvIter != materialUvNameMap.end();
  171. if (isUv)
  172. {
  173. const AZ::Name& materialUvName = materialUvIter->m_uvName;
  174. auto modelUvMapIter = materialModelUvMap.find(materialUvIter->m_shaderInput);
  175. if (modelUvMapIter != materialModelUvMap.end())
  176. {
  177. const AZ::Name& modelUvName = modelUvMapIter->second;
  178. // Empty name can't be used because it will match other non-UV streams.
  179. if (!modelUvName.IsEmpty())
  180. {
  181. iter = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [&modelUvName](const StreamBufferInfo& info)
  182. {
  183. return info.m_customName == modelUvName
  184. || info.m_semantic.ToString() == modelUvName.GetStringView(); // For unnamed UVs, use the semantic instead.
  185. });
  186. }
  187. }
  188. if (iter == mesh.m_streamInfo.end())
  189. {
  190. // Empty name can't be used because it will match other non-UV streams.
  191. if (!materialUvName.IsEmpty())
  192. {
  193. iter = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [&materialUvName](const StreamBufferInfo& info)
  194. {
  195. return info.m_customName == materialUvName;
  196. });
  197. }
  198. }
  199. }
  200. if (iter == mesh.m_streamInfo.end())
  201. {
  202. iter = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [&contractStreamChannel](const StreamBufferInfo& info)
  203. {
  204. return info.m_semantic == contractStreamChannel.m_semantic;
  205. });
  206. }
  207. if (iter == mesh.m_streamInfo.end() && isUv)
  208. {
  209. iter = defaultUv;
  210. }
  211. if (isUv && uvStreamTangentBitmaskOut)
  212. {
  213. uvStreamTangentBitmaskOut->ApplyTangent(iter == firstUv ? 0 : UvStreamTangentBitmask::UnassignedTangent);
  214. }
  215. return iter;
  216. }
  217. bool ModelLod::GetStreamsForMesh(
  218. RHI::InputStreamLayout& layoutOut,
  219. StreamBufferViewList& streamBufferViewsOut,
  220. UvStreamTangentBitmask* uvStreamTangentBitmaskOut,
  221. const ShaderInputContract& contract,
  222. size_t meshIndex,
  223. const MaterialModelUvOverrideMap& materialModelUvMap,
  224. const MaterialUvNameMap& materialUvNameMap) const
  225. {
  226. streamBufferViewsOut.clear();
  227. RHI::InputStreamLayoutBuilder layoutBuilder;
  228. const Mesh& mesh = m_meshes[meshIndex];
  229. bool success = true;
  230. // Searching for the first UV in the mesh, so it can be used to paired with tangent/bitangent stream
  231. auto firstUv = FindFirstUvStreamFromMesh(meshIndex);
  232. auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap);
  233. if (uvStreamTangentBitmaskOut)
  234. {
  235. uvStreamTangentBitmaskOut->Reset();
  236. }
  237. for (auto& contractStreamChannel : contract.m_streamChannels)
  238. {
  239. auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentBitmaskOut);
  240. if (iter == mesh.m_streamInfo.end())
  241. {
  242. if (contractStreamChannel.m_isOptional)
  243. {
  244. // The configuration of the dummy input stream is a bit touchy, and could result in crashes or validation failures that are platform-specific.
  245. // If you modify this code, be sure to test with "-rhi-device-validation=enable" on every platform.
  246. // Here are some criteria that we've noticed before, even for empty buffers:
  247. // Metal: Mesh stream formats need to be at least 4 byte aligned.
  248. // Vulkan: Mesh stream data type (float vs uint) must match the shader, or validation errors are reported
  249. // ("does not match vertex shader input type")
  250. // Vulkan: We can't just use a null buffer pointer because vulkan will occasionally crash. So we bind some valid non-null buffer and view it with length 0.
  251. // Dx12: Mesh stream data type (float vs uint) must match the shader, or validation warnings are reported
  252. // ("the matching entry in the Input Layout declaration ... specifies mismatched format").
  253. //
  254. // The stride value does not seem to matter, just the Format type. Still, we use GetFormatSize to set an accurate stride.
  255. RHI::Format dummyStreamFormat = RHI::Format::R32G32B32A32_FLOAT;
  256. layoutBuilder.AddBuffer()->Channel(contractStreamChannel.m_semantic, dummyStreamFormat);
  257. RHI::StreamBufferView dummyBuffer{*mesh.m_indexBufferView.GetBuffer(), 0, 0, RHI::GetFormatSize(dummyStreamFormat)};
  258. streamBufferViewsOut.push_back(dummyBuffer);
  259. }
  260. else
  261. {
  262. AZ_Warning("Mesh", false, "Mesh does not have all the required input streams. Missing '%s'.", contractStreamChannel.m_semantic.ToString().c_str());
  263. success = false;
  264. }
  265. }
  266. else
  267. {
  268. // Note, we may need to iterate on the details of this validation. It might not be correct for all use cases.
  269. if (RHI::GetFormatComponentCount(iter->m_format) < contractStreamChannel.m_componentCount)
  270. {
  271. AZ_Error("Mesh", false, "Mesh format (%s) for stream '%s' provides %d components but the shader requires %d.",
  272. RHI::ToString(iter->m_format),
  273. contractStreamChannel.m_semantic.ToString().c_str(),
  274. RHI::GetFormatComponentCount(iter->m_format),
  275. contractStreamChannel.m_componentCount);
  276. success = false;
  277. }
  278. else
  279. {
  280. // Note, don't use iter->m_semantic as it can be a UV name matching.
  281. layoutBuilder.AddBuffer()->Channel(contractStreamChannel.m_semantic, iter->m_format);
  282. RHI::StreamBufferView bufferView(*m_buffers[iter->m_bufferIndex]->GetRHIBuffer(), iter->m_byteOffset, iter->m_byteCount, iter->m_stride);
  283. streamBufferViewsOut.push_back(bufferView);
  284. }
  285. }
  286. }
  287. if (success)
  288. {
  289. layoutOut = layoutBuilder.End();
  290. success &= RHI::ValidateStreamBufferViews(layoutOut, streamBufferViewsOut);
  291. }
  292. return success;
  293. }
  294. void ModelLod::CheckOptionalStreams(
  295. ShaderOptionGroup& shaderOptions,
  296. const ShaderInputContract& contract,
  297. size_t meshIndex,
  298. const MaterialModelUvOverrideMap& materialModelUvMap,
  299. const MaterialUvNameMap& materialUvNameMap) const
  300. {
  301. const Mesh& mesh = m_meshes[meshIndex];
  302. auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap);
  303. auto firstUv = FindFirstUvStreamFromMesh(meshIndex);
  304. for (auto& contractStreamChannel : contract.m_streamChannels)
  305. {
  306. if (!contractStreamChannel.m_isOptional)
  307. {
  308. continue;
  309. }
  310. AZ_Assert(contractStreamChannel.m_streamBoundIndicatorIndex.IsValid(), "m_streamBoundIndicatorIndex was invalid for an optional shader input stream");
  311. auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, nullptr);
  312. ShaderOptionValue isStreamBound = (iter == mesh.m_streamInfo.end()) ? ShaderOptionValue{0} : ShaderOptionValue{1};
  313. shaderOptions.SetValue(contractStreamChannel.m_streamBoundIndicatorIndex, isStreamBound);
  314. }
  315. }
  316. bool ModelLod::SetMeshInstanceData(
  317. const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo,
  318. Mesh& meshInstance)
  319. {
  320. AZ_PROFILE_FUNCTION(RPI);
  321. const Data::Asset<BufferAsset>& streamBufferAsset = streamBufferInfo.m_bufferAssetView.GetBufferAsset();
  322. const Data::Instance<Buffer>& streamBuffer = Buffer::FindOrCreate(streamBufferAsset);
  323. if (streamBuffer == nullptr)
  324. {
  325. AZ_Error("ModelLod", false, "Failed to create stream buffer! Possibly out of memory!");
  326. return false;
  327. }
  328. const RHI::BufferViewDescriptor& bufferViewDescriptor = streamBufferInfo.m_bufferAssetView.GetBufferViewDescriptor();
  329. StreamBufferInfo info;
  330. info.m_semantic = streamBufferInfo.m_semantic;
  331. info.m_customName = streamBufferInfo.m_customName;
  332. info.m_format = bufferViewDescriptor.m_elementFormat;
  333. info.m_byteOffset = bufferViewDescriptor.m_elementOffset * bufferViewDescriptor.m_elementSize;
  334. info.m_byteCount = bufferViewDescriptor.m_elementCount * bufferViewDescriptor.m_elementSize;
  335. info.m_stride = bufferViewDescriptor.m_elementSize;
  336. info.m_bufferIndex = TrackBuffer(streamBuffer);
  337. meshInstance.m_streamInfo.push_back(info);
  338. return true;
  339. }
  340. void ModelLod::WaitForUpload()
  341. {
  342. if (m_isUploadPending)
  343. {
  344. for (const Data::Instance<Buffer>& buffer : m_buffers)
  345. {
  346. buffer->WaitForUpload();
  347. }
  348. m_isUploadPending = false;
  349. }
  350. }
  351. uint32_t ModelLod::TrackBuffer(const Data::Instance<Buffer>& buffer)
  352. {
  353. for (uint32_t i = 0; i < m_buffers.size(); ++i)
  354. {
  355. auto& existingBuffer = m_buffers[i];
  356. if (existingBuffer.get() == buffer)
  357. {
  358. return i;
  359. }
  360. }
  361. m_buffers.emplace_back(buffer);
  362. return static_cast<uint32_t>(m_buffers.size() - 1);
  363. }
  364. } // namespace RPI
  365. } // namespace AZ