ModelLod.cpp 21 KB

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