3
0

RPIUtils.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  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/RHI/RHISystemInterface.h>
  9. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  10. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  11. #include <Atom/RPI.Reflect/System/AnyAsset.h>
  12. #include <Atom/RPI.Public/BlockCompression.h>
  13. #include <Atom/RPI.Public/Pass/PassFilter.h>
  14. #include <Atom/RPI.Public/RenderPipeline.h>
  15. #include <Atom/RPI.Public/RPISystemInterface.h>
  16. #include <Atom/RPI.Public/RPIUtils.h>
  17. #include <Atom/RPI.Public/Shader/Shader.h>
  18. #include <AzCore/Math/Color.h>
  19. #include <AzCore/std/containers/array.h>
  20. #include <AzFramework/Asset/AssetSystemBus.h>
  21. namespace AZ
  22. {
  23. namespace RPI
  24. {
  25. namespace Internal
  26. {
  27. // The original implementation was from cryhalf's CryConvertFloatToHalf and CryConvertHalfToFloat function
  28. // Will be replaced with centralized half float API
  29. struct SHalf
  30. {
  31. explicit SHalf(float floatValue)
  32. {
  33. AZ::u32 Result;
  34. AZ::u32 intValue = ((AZ::u32*)(&floatValue))[0];
  35. AZ::u32 Sign = (intValue & 0x80000000U) >> 16U;
  36. intValue = intValue & 0x7FFFFFFFU;
  37. if (intValue > 0x47FFEFFFU)
  38. {
  39. // The number is too large to be represented as a half. Saturate to infinity.
  40. Result = 0x7FFFU;
  41. }
  42. else
  43. {
  44. if (intValue < 0x38800000U)
  45. {
  46. // The number is too small to be represented as a normalized half.
  47. // Convert it to a denormalized value.
  48. AZ::u32 Shift = 113U - (intValue >> 23U);
  49. intValue = (0x800000U | (intValue & 0x7FFFFFU)) >> Shift;
  50. }
  51. else
  52. {
  53. // Rebias the exponent to represent the value as a normalized half.
  54. intValue += 0xC8000000U;
  55. }
  56. Result = ((intValue + 0x0FFFU + ((intValue >> 13U) & 1U)) >> 13U) & 0x7FFFU;
  57. }
  58. h = static_cast<AZ::u16>(Result | Sign);
  59. }
  60. operator float() const
  61. {
  62. AZ::u32 Mantissa;
  63. AZ::u32 Exponent;
  64. AZ::u32 Result;
  65. Mantissa = h & 0x03FF;
  66. if ((h & 0x7C00) != 0) // The value is normalized
  67. {
  68. Exponent = ((h >> 10) & 0x1F);
  69. }
  70. else if (Mantissa != 0) // The value is denormalized
  71. {
  72. // Normalize the value in the resulting float
  73. Exponent = 1;
  74. do
  75. {
  76. Exponent--;
  77. Mantissa <<= 1;
  78. } while ((Mantissa & 0x0400) == 0);
  79. Mantissa &= 0x03FF;
  80. }
  81. else // The value is zero
  82. {
  83. Exponent = static_cast<AZ::u32>(-112);
  84. }
  85. Result = ((h & 0x8000) << 16) | // Sign
  86. ((Exponent + 112) << 23) | // Exponent
  87. (Mantissa << 13); // Mantissa
  88. return *(float*)&Result;
  89. }
  90. private:
  91. AZ::u16 h;
  92. };
  93. float ScaleValue(float value, float origMin, float origMax, float scaledMin, float scaledMax)
  94. {
  95. // This method assumes that origMin <= value <= origMax. Since this is a private helper method only
  96. // used by ScaleSNorm8Value and ScaleSNorm16Value below, we'll avoid using asserts to verify the
  97. // condition in the interest of performance in assert-enabled builds.
  98. return ((value - origMin) / (origMax - origMin)) * (scaledMax - scaledMin) + scaledMin;
  99. }
  100. float ScaleSNorm8Value(int8_t value)
  101. {
  102. // Scale the value from AZ::s8 min/max to -1 to 1
  103. // We need to treat -128 and -127 the same, so that we get a symmetric
  104. // range of -127 to 127 with complementary scaled values of -1 to 1
  105. constexpr float signedMax = static_cast<float>(AZStd::numeric_limits<int8_t>::max());
  106. constexpr float signedMin = -signedMax;
  107. return ScaleValue(AZStd::max(aznumeric_cast<float>(value), signedMin), signedMin, signedMax, -1.0f, 1.0f);
  108. }
  109. float ScaleSNorm16Value(int16_t value)
  110. {
  111. // Scale the value from AZ::s16 min/max to -1 to 1
  112. // We need to treat -32768 and -32767 the same, so that we get a symmetric
  113. // range of -32767 to 32767 with complementary scaled values of -1 to 1
  114. constexpr float signedMax = static_cast<float>(AZStd::numeric_limits<int16_t>::max());
  115. constexpr float signedMin = -signedMax;
  116. return ScaleValue(AZStd::max(aznumeric_cast<float>(value), signedMin), signedMin, signedMax, -1.0f, 1.0f);
  117. }
  118. // Pre-compute a lookup table for converting SRGB gamma to linear
  119. // by specifying the AZ::u8 so we don't have to do the computation
  120. // when retrieving pixels
  121. using ConversionLookupTable = AZStd::array<float, 256>;
  122. ConversionLookupTable CreateSrgbGammaToLinearLookupTable()
  123. {
  124. ConversionLookupTable lookupTable;
  125. for (size_t i = 0; i < lookupTable.array_size; ++i)
  126. {
  127. float srgbValue = i / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max());
  128. lookupTable[i] = AZ::Color::ConvertSrgbGammaToLinear(srgbValue);
  129. }
  130. return lookupTable;
  131. }
  132. static ConversionLookupTable s_SrgbGammaToLinearLookupTable = CreateSrgbGammaToLinearLookupTable();
  133. float RetrieveFloatValue(
  134. const AZ::u8* mem, AZStd::pair<size_t, size_t> indices, uint32_t componentIndex, AZ::RHI::Format format)
  135. {
  136. switch (format)
  137. {
  138. case AZ::RHI::Format::R8_UNORM:
  139. case AZ::RHI::Format::A8_UNORM:
  140. case AZ::RHI::Format::R8G8_UNORM:
  141. case AZ::RHI::Format::R8G8B8A8_UNORM:
  142. case AZ::RHI::Format::A8B8G8R8_UNORM:
  143. {
  144. return mem[indices.first + componentIndex] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max());
  145. }
  146. case AZ::RHI::Format::R8_UNORM_SRGB:
  147. case AZ::RHI::Format::R8G8_UNORM_SRGB:
  148. case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB:
  149. case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB:
  150. {
  151. // Use a lookup table that takes an AZ::u8 instead of a float
  152. // for better performance
  153. return s_SrgbGammaToLinearLookupTable[mem[indices.first + componentIndex]];
  154. }
  155. case AZ::RHI::Format::R8_SNORM:
  156. case AZ::RHI::Format::R8G8_SNORM:
  157. case AZ::RHI::Format::R8G8B8A8_SNORM:
  158. case AZ::RHI::Format::A8B8G8R8_SNORM:
  159. {
  160. auto actualMem = reinterpret_cast<const AZ::s8*>(mem);
  161. return ScaleSNorm8Value(actualMem[indices.first + componentIndex]);
  162. }
  163. case AZ::RHI::Format::D16_UNORM:
  164. case AZ::RHI::Format::R16_UNORM:
  165. case AZ::RHI::Format::R16G16_UNORM:
  166. case AZ::RHI::Format::R16G16B16A16_UNORM:
  167. {
  168. auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
  169. return actualMem[indices.first + componentIndex] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max());
  170. }
  171. case AZ::RHI::Format::R16_SNORM:
  172. case AZ::RHI::Format::R16G16_SNORM:
  173. case AZ::RHI::Format::R16G16B16A16_SNORM:
  174. {
  175. auto actualMem = reinterpret_cast<const AZ::s16*>(mem);
  176. return ScaleSNorm16Value(actualMem[indices.first + componentIndex]);
  177. }
  178. case AZ::RHI::Format::R16_FLOAT:
  179. case AZ::RHI::Format::R16G16_FLOAT:
  180. case AZ::RHI::Format::R16G16B16A16_FLOAT:
  181. {
  182. auto actualMem = reinterpret_cast<const float*>(mem);
  183. return SHalf(actualMem[indices.first + componentIndex]);
  184. }
  185. case AZ::RHI::Format::D32_FLOAT:
  186. case AZ::RHI::Format::R32_FLOAT:
  187. case AZ::RHI::Format::R32G32_FLOAT:
  188. case AZ::RHI::Format::R32G32B32_FLOAT:
  189. case AZ::RHI::Format::R32G32B32A32_FLOAT:
  190. {
  191. auto actualMem = reinterpret_cast<const float*>(mem);
  192. return actualMem[indices.first + componentIndex];
  193. }
  194. case AZ::RHI::Format::BC1_UNORM:
  195. {
  196. auto actualMem = reinterpret_cast<const BC1Block*>(mem);
  197. return actualMem[indices.first].GetBlockColor(indices.second).GetElement(componentIndex);
  198. }
  199. case AZ::RHI::Format::BC1_UNORM_SRGB:
  200. {
  201. auto actualMem = reinterpret_cast<const BC1Block*>(mem);
  202. float color = actualMem[indices.first].GetBlockColor(indices.second).GetElement(componentIndex);
  203. return s_SrgbGammaToLinearLookupTable[aznumeric_cast<uint8_t>(color * AZStd::numeric_limits<AZ::u8>::max())];
  204. }
  205. case AZ::RHI::Format::BC4_UNORM:
  206. {
  207. auto actualMem = reinterpret_cast<const BC4Block*>(mem);
  208. return actualMem[indices.first].GetBlockColor(indices.second).GetElement(componentIndex);
  209. }
  210. default:
  211. AZ_Assert(false, "Unsupported pixel format: %s", AZ::RHI::ToString(format));
  212. return 0.0f;
  213. }
  214. }
  215. AZ::Color RetrieveColorValue(const AZ::u8* mem, AZStd::pair<size_t, size_t> indices, AZ::RHI::Format format)
  216. {
  217. switch (format)
  218. {
  219. case AZ::RHI::Format::R8_UNORM:
  220. {
  221. return AZ::Color(mem[indices.first] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()), 0.0f, 0.0f, 1.0f);
  222. }
  223. case AZ::RHI::Format::A8_UNORM:
  224. {
  225. return AZ::Color(0.0f, 0.0f, 0.0f, mem[indices.first] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()));
  226. }
  227. case AZ::RHI::Format::R8G8_UNORM:
  228. {
  229. return AZ::Color(
  230. mem[indices.first + 0] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  231. mem[indices.first + 1] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  232. 0.0f,
  233. 1.0f);
  234. }
  235. case AZ::RHI::Format::R8G8B8A8_UNORM:
  236. {
  237. return AZ::Color(
  238. mem[indices.first + 0] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  239. mem[indices.first + 1] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  240. mem[indices.first + 2] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  241. mem[indices.first + 3] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()));
  242. }
  243. case AZ::RHI::Format::A8B8G8R8_UNORM:
  244. {
  245. return AZ::Color(
  246. mem[indices.first + 3] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  247. mem[indices.first + 2] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  248. mem[indices.first + 1] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()),
  249. mem[indices.first + 0] / static_cast<float>(AZStd::numeric_limits<AZ::u8>::max()));
  250. }
  251. case AZ::RHI::Format::R8_UNORM_SRGB:
  252. {
  253. return AZ::Color(s_SrgbGammaToLinearLookupTable[mem[indices.first]], 0.0f, 0.0f, 1.0f);
  254. }
  255. case AZ::RHI::Format::R8G8_UNORM_SRGB:
  256. {
  257. return AZ::Color(
  258. s_SrgbGammaToLinearLookupTable[mem[indices.first + 0]],
  259. s_SrgbGammaToLinearLookupTable[mem[indices.first + 1]],
  260. 0.0f,
  261. 1.0f);
  262. }
  263. case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB:
  264. {
  265. return AZ::Color(
  266. s_SrgbGammaToLinearLookupTable[mem[indices.first + 0]],
  267. s_SrgbGammaToLinearLookupTable[mem[indices.first + 1]],
  268. s_SrgbGammaToLinearLookupTable[mem[indices.first + 2]],
  269. s_SrgbGammaToLinearLookupTable[mem[indices.first + 3]]);
  270. }
  271. case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB:
  272. {
  273. return AZ::Color(
  274. s_SrgbGammaToLinearLookupTable[mem[indices.first + 3]],
  275. s_SrgbGammaToLinearLookupTable[mem[indices.first + 2]],
  276. s_SrgbGammaToLinearLookupTable[mem[indices.first + 1]],
  277. s_SrgbGammaToLinearLookupTable[mem[indices.first + 0]]);
  278. }
  279. case AZ::RHI::Format::R8_SNORM:
  280. {
  281. auto actualMem = reinterpret_cast<const AZ::s8*>(mem);
  282. return AZ::Color(ScaleSNorm8Value(actualMem[indices.first]), 0.0f, 0.0f, 1.0f);
  283. }
  284. case AZ::RHI::Format::R8G8_SNORM:
  285. {
  286. auto actualMem = reinterpret_cast<const AZ::s8*>(mem);
  287. return AZ::Color(
  288. ScaleSNorm8Value(actualMem[indices.first + 0]), ScaleSNorm8Value(actualMem[indices.first + 1]), 0.0f, 1.0f);
  289. }
  290. case AZ::RHI::Format::R8G8B8A8_SNORM:
  291. {
  292. auto actualMem = reinterpret_cast<const AZ::s8*>(mem);
  293. return AZ::Color(
  294. ScaleSNorm8Value(actualMem[indices.first + 0]),
  295. ScaleSNorm8Value(actualMem[indices.first + 1]),
  296. ScaleSNorm8Value(actualMem[indices.first + 2]),
  297. ScaleSNorm8Value(actualMem[indices.first + 3]));
  298. }
  299. case AZ::RHI::Format::A8B8G8R8_SNORM:
  300. {
  301. auto actualMem = reinterpret_cast<const AZ::s8*>(mem);
  302. return AZ::Color(
  303. ScaleSNorm8Value(actualMem[indices.first + 3]),
  304. ScaleSNorm8Value(actualMem[indices.first + 2]),
  305. ScaleSNorm8Value(actualMem[indices.first + 1]),
  306. ScaleSNorm8Value(actualMem[indices.first + 0]));
  307. }
  308. case AZ::RHI::Format::D16_UNORM:
  309. case AZ::RHI::Format::R16_UNORM:
  310. {
  311. auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
  312. return AZ::Color(
  313. actualMem[indices.first] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()), 0.0f, 0.0f, 1.0f);
  314. }
  315. case AZ::RHI::Format::R16G16_UNORM:
  316. {
  317. auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
  318. return AZ::Color(
  319. actualMem[indices.first + 0] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()),
  320. actualMem[indices.first + 1] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()),
  321. 0.0f,
  322. 1.0f);
  323. }
  324. case AZ::RHI::Format::R16G16B16A16_UNORM:
  325. {
  326. auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
  327. return AZ::Color(
  328. actualMem[indices.first + 0] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()),
  329. actualMem[indices.first + 1] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()),
  330. actualMem[indices.first + 2] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()),
  331. actualMem[indices.first + 3] / static_cast<float>(AZStd::numeric_limits<AZ::u16>::max()));
  332. }
  333. case AZ::RHI::Format::R16_SNORM:
  334. {
  335. auto actualMem = reinterpret_cast<const AZ::s16*>(mem);
  336. return AZ::Color(ScaleSNorm16Value(actualMem[indices.first]), 0.0f, 0.0f, 1.0f);
  337. }
  338. case AZ::RHI::Format::R16G16_SNORM:
  339. {
  340. auto actualMem = reinterpret_cast<const AZ::s16*>(mem);
  341. return AZ::Color(
  342. ScaleSNorm16Value(actualMem[indices.first + 0]), ScaleSNorm16Value(actualMem[indices.first + 1]), 0.0f, 1.0f);
  343. }
  344. case AZ::RHI::Format::R16G16B16A16_SNORM:
  345. {
  346. auto actualMem = reinterpret_cast<const AZ::s16*>(mem);
  347. return AZ::Color(
  348. ScaleSNorm16Value(actualMem[indices.first + 0]),
  349. ScaleSNorm16Value(actualMem[indices.first + 1]),
  350. ScaleSNorm16Value(actualMem[indices.first + 2]),
  351. ScaleSNorm16Value(actualMem[indices.first + 3]));
  352. }
  353. case AZ::RHI::Format::R16_FLOAT:
  354. {
  355. auto actualMem = reinterpret_cast<const float*>(mem);
  356. return AZ::Color(SHalf(actualMem[indices.first]), 0.0f, 0.0f, 1.0f);
  357. }
  358. case AZ::RHI::Format::R16G16_FLOAT:
  359. {
  360. auto actualMem = reinterpret_cast<const float*>(mem);
  361. return AZ::Color(SHalf(actualMem[indices.first + 0]), SHalf(actualMem[indices.first + 1]), 0.0f, 1.0f);
  362. }
  363. case AZ::RHI::Format::R16G16B16A16_FLOAT:
  364. {
  365. auto actualMem = reinterpret_cast<const float*>(mem);
  366. return AZ::Color(
  367. SHalf(actualMem[indices.first + 0]),
  368. SHalf(actualMem[indices.first + 1]),
  369. SHalf(actualMem[indices.first + 2]),
  370. SHalf(actualMem[indices.first + 3]));
  371. }
  372. case AZ::RHI::Format::D32_FLOAT:
  373. case AZ::RHI::Format::R32_FLOAT:
  374. {
  375. auto actualMem = reinterpret_cast<const float*>(mem);
  376. return AZ::Color(actualMem[indices.first], 0.0f, 0.0f, 1.0f);
  377. }
  378. case AZ::RHI::Format::R32G32_FLOAT:
  379. {
  380. auto actualMem = reinterpret_cast<const float*>(mem);
  381. return AZ::Color(actualMem[indices.first + 0], actualMem[indices.first + 1], 0.0f, 1.0f);
  382. }
  383. case AZ::RHI::Format::R32G32B32_FLOAT:
  384. {
  385. auto actualMem = reinterpret_cast<const float*>(mem);
  386. return AZ::Color(actualMem[indices.first + 0], actualMem[indices.first + 1], actualMem[indices.first + 2], 1.0f);
  387. }
  388. case AZ::RHI::Format::R32G32B32A32_FLOAT:
  389. {
  390. auto actualMem = reinterpret_cast<const float*>(mem);
  391. return AZ::Color(
  392. actualMem[indices.first + 0],
  393. actualMem[indices.first + 1],
  394. actualMem[indices.first + 2],
  395. actualMem[indices.first + 3]);
  396. }
  397. case AZ::RHI::Format::BC1_UNORM:
  398. {
  399. auto actualMem = reinterpret_cast<const BC1Block*>(mem);
  400. return actualMem[indices.first].GetBlockColor(indices.second);
  401. }
  402. case AZ::RHI::Format::BC1_UNORM_SRGB:
  403. {
  404. auto actualMem = reinterpret_cast<const BC1Block*>(mem);
  405. AZ::Color color = actualMem[indices.first].GetBlockColor(indices.second);
  406. return AZ::Color(
  407. s_SrgbGammaToLinearLookupTable[aznumeric_cast<uint8_t>(color.GetR() * AZStd::numeric_limits<AZ::u8>::max())],
  408. s_SrgbGammaToLinearLookupTable[aznumeric_cast<uint8_t>(color.GetG() * AZStd::numeric_limits<AZ::u8>::max())],
  409. s_SrgbGammaToLinearLookupTable[aznumeric_cast<uint8_t>(color.GetB() * AZStd::numeric_limits<AZ::u8>::max())],
  410. s_SrgbGammaToLinearLookupTable[aznumeric_cast<uint8_t>(color.GetA() * AZStd::numeric_limits<AZ::u8>::max())]);
  411. }
  412. case AZ::RHI::Format::BC4_UNORM:
  413. {
  414. auto actualMem = reinterpret_cast<const BC4Block*>(mem);
  415. return actualMem[indices.first].GetBlockColor(indices.second);
  416. }
  417. default:
  418. AZ_Assert(false, "Unsupported pixel format: %s", AZ::RHI::ToString(format));
  419. return AZ::Color::CreateZero();
  420. }
  421. }
  422. AZ::u32 RetrieveUintValue(
  423. const AZ::u8* mem, AZStd::pair<size_t, size_t> indices, uint32_t componentIndex, AZ::RHI::Format format)
  424. {
  425. switch (format)
  426. {
  427. case AZ::RHI::Format::R8_UINT:
  428. case AZ::RHI::Format::R8G8_UINT:
  429. case AZ::RHI::Format::R8G8B8A8_UINT:
  430. {
  431. return mem[indices.first + componentIndex] / static_cast<AZ::u32>(AZStd::numeric_limits<AZ::u8>::max());
  432. }
  433. case AZ::RHI::Format::R16_UINT:
  434. case AZ::RHI::Format::R16G16_UINT:
  435. case AZ::RHI::Format::R16G16B16A16_UINT:
  436. {
  437. auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
  438. return actualMem[indices.first + componentIndex] / static_cast<AZ::u32>(AZStd::numeric_limits<AZ::u16>::max());
  439. }
  440. case AZ::RHI::Format::R32_UINT:
  441. case AZ::RHI::Format::R32G32_UINT:
  442. case AZ::RHI::Format::R32G32B32_UINT:
  443. case AZ::RHI::Format::R32G32B32A32_UINT:
  444. {
  445. auto actualMem = reinterpret_cast<const AZ::u32*>(mem);
  446. return actualMem[indices.first + componentIndex];
  447. }
  448. default:
  449. AZ_Assert(false, "Unsupported pixel format: %s", AZ::RHI::ToString(format));
  450. return 0;
  451. }
  452. }
  453. AZ::s32 RetrieveIntValue(
  454. const AZ::u8* mem, AZStd::pair<size_t, size_t> indices, uint32_t componentIndex, AZ::RHI::Format format)
  455. {
  456. switch (format)
  457. {
  458. case AZ::RHI::Format::R8_SINT:
  459. case AZ::RHI::Format::R8G8_SINT:
  460. case AZ::RHI::Format::R8G8B8A8_SINT:
  461. {
  462. return mem[indices.first + componentIndex] / static_cast<AZ::s32>(AZStd::numeric_limits<AZ::s8>::max());
  463. }
  464. case AZ::RHI::Format::R16_SINT:
  465. case AZ::RHI::Format::R16G16_SINT:
  466. case AZ::RHI::Format::R16G16B16A16_SINT:
  467. {
  468. auto actualMem = reinterpret_cast<const AZ::s16*>(mem);
  469. return actualMem[indices.first + componentIndex] / static_cast<AZ::s32>(AZStd::numeric_limits<AZ::s16>::max());
  470. }
  471. case AZ::RHI::Format::R32_SINT:
  472. case AZ::RHI::Format::R32G32_SINT:
  473. case AZ::RHI::Format::R32G32B32_SINT:
  474. case AZ::RHI::Format::R32G32B32A32_SINT:
  475. {
  476. auto actualMem = reinterpret_cast<const AZ::s32*>(mem);
  477. return actualMem[indices.first + componentIndex];
  478. }
  479. default:
  480. AZ_Assert(false, "Unsupported pixel format: %s", AZ::RHI::ToString(format));
  481. return 0;
  482. }
  483. }
  484. // Given an XY position, return a pair of indices that can be used to decode an individual pixel.
  485. // For uncompressed formats:
  486. // The first index points to the start of the pixel when indexing by component type
  487. // The second index is 0 (unused)
  488. // Ex: an input XY of (2, 0) for an R16G16B16 format returns (6,0) because the requested pixel starts at
  489. // the 6th 16-bit entry in the pixel data.
  490. // For compressed formats:
  491. // The first index points to the start of the compressed block
  492. // The second index is a relative pixel index within that block
  493. // Ex: an input XY of (6, 0) with a 4x4 compressed format produces an output of (1, 2), which means use block[1] to
  494. // decompress and pixel[2] within that decompressed block.
  495. AZStd::pair<size_t, size_t> GetImageDataIndex(const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y)
  496. {
  497. auto width = imageDescriptor.m_size.m_width;
  498. const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
  499. switch (imageDescriptor.m_format)
  500. {
  501. case AZ::RHI::Format::BC1_UNORM:
  502. case AZ::RHI::Format::BC1_UNORM_SRGB:
  503. return BC1Block::GetBlockIndices(width, x, y);
  504. case AZ::RHI::Format::BC4_UNORM:
  505. return BC4Block::GetBlockIndices(width, x, y);
  506. default:
  507. return AZStd::pair<size_t, size_t>((y * width + x) * numComponents, 0);
  508. }
  509. }
  510. } // namespace Internal
  511. bool IsNullRenderer()
  512. {
  513. return RPI::RPISystemInterface::Get()->IsNullRenderer();
  514. }
  515. Data::AssetId GetShaderAssetId(const AZStd::string& shaderFilePath, bool isCritical)
  516. {
  517. Data::AssetId shaderAssetId;
  518. Data::AssetCatalogRequestBus::BroadcastResult(
  519. shaderAssetId,
  520. &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
  521. shaderFilePath.c_str(),
  522. azrtti_typeid<ShaderAsset>(),
  523. false);
  524. if (!shaderAssetId.IsValid())
  525. {
  526. if (isCritical)
  527. {
  528. Data::Asset<RPI::ShaderAsset> shaderAsset = RPI::AssetUtils::LoadCriticalAsset<RPI::ShaderAsset>(shaderFilePath);
  529. if (shaderAsset.IsReady())
  530. {
  531. return shaderAsset.GetId();
  532. }
  533. else
  534. {
  535. AZ_Error("RPI Utils", false, "Could not load critical shader [%s]", shaderFilePath.c_str());
  536. }
  537. }
  538. AZ_Error("RPI Utils", false, "Failed to get asset id for shader [%s]", shaderFilePath.c_str());
  539. }
  540. return shaderAssetId;
  541. }
  542. Data::Asset<ShaderAsset> FindShaderAsset(Data::AssetId shaderAssetId, [[maybe_unused]] const AZStd::string& shaderFilePath)
  543. {
  544. if (!shaderAssetId.IsValid())
  545. {
  546. return Data::Asset<ShaderAsset>();
  547. }
  548. auto shaderAsset = Data::AssetManager::Instance().GetAsset<ShaderAsset>(shaderAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
  549. shaderAsset.BlockUntilLoadComplete();
  550. if (!shaderAsset.IsReady())
  551. {
  552. AZ_Error(
  553. "RPI Utils",
  554. false,
  555. "Failed to find shader asset [%s] with asset ID [%s]",
  556. shaderFilePath.c_str(),
  557. shaderAssetId.ToString<AZStd::string>().c_str());
  558. return Data::Asset<ShaderAsset>();
  559. }
  560. return shaderAsset;
  561. }
  562. Data::Instance<Shader> LoadShader(
  563. Data::AssetId shaderAssetId, const AZStd::string& shaderFilePath, const AZStd::string& supervariantName)
  564. {
  565. auto shaderAsset = FindShaderAsset(shaderAssetId, shaderFilePath);
  566. if (!shaderAsset.IsReady())
  567. {
  568. return nullptr;
  569. }
  570. Data::Instance<Shader> shader = Shader::FindOrCreate(shaderAsset, AZ::Name(supervariantName));
  571. if (!shader)
  572. {
  573. AZ_Error(
  574. "RPI Utils",
  575. false,
  576. "Failed to find or create a shader instance from shader asset [%s] with asset ID [%s]",
  577. shaderFilePath.c_str(),
  578. shaderAssetId.ToString<AZStd::string>().c_str());
  579. return nullptr;
  580. }
  581. return shader;
  582. }
  583. Data::Asset<ShaderAsset> FindShaderAsset(const AZStd::string& shaderFilePath)
  584. {
  585. return FindShaderAsset(GetShaderAssetId(shaderFilePath), shaderFilePath);
  586. }
  587. Data::Asset<ShaderAsset> FindCriticalShaderAsset(const AZStd::string& shaderFilePath)
  588. {
  589. const bool isCritical = true;
  590. return FindShaderAsset(GetShaderAssetId(shaderFilePath, isCritical), shaderFilePath);
  591. }
  592. Data::Instance<Shader> LoadShader(const AZStd::string& shaderFilePath, const AZStd::string& supervariantName)
  593. {
  594. return LoadShader(GetShaderAssetId(shaderFilePath), shaderFilePath, supervariantName);
  595. }
  596. Data::Instance<Shader> LoadCriticalShader(const AZStd::string& shaderFilePath, const AZStd::string& supervariantName)
  597. {
  598. const bool isCritical = true;
  599. return LoadShader(GetShaderAssetId(shaderFilePath, isCritical), shaderFilePath, supervariantName);
  600. }
  601. AZ::Data::Instance<RPI::StreamingImage> LoadStreamingTexture(AZStd::string_view path)
  602. {
  603. auto streamingImageAsset = RPI::AssetUtils::LoadCriticalAsset<RPI::StreamingImageAsset>(path);
  604. if (!streamingImageAsset.IsReady())
  605. {
  606. AZ_Error("RPI Utils", false, "Failed to get streaming image asset: " AZ_STRING_FORMAT, AZ_STRING_ARG(path));
  607. return AZ::Data::Instance<RPI::StreamingImage>();
  608. }
  609. return RPI::StreamingImage::FindOrCreate(streamingImageAsset);
  610. }
  611. //! A helper function for GetComputeShaderNumThreads(), to consolidate error messages, etc.
  612. static bool GetAttributeArgumentByIndex(
  613. const Data::Asset<ShaderAsset>& shaderAsset,
  614. const AZ::Name& attributeName,
  615. const RHI::ShaderStageAttributeArguments& args,
  616. const size_t argIndex,
  617. uint16_t* value,
  618. AZStd::string& errorMsg)
  619. {
  620. if (value)
  621. {
  622. const auto numArguments = args.size();
  623. if (numArguments > argIndex)
  624. {
  625. if (args[argIndex].type() == azrtti_typeid<int>())
  626. {
  627. *value = aznumeric_caster(AZStd::any_cast<int>(args[argIndex]));
  628. }
  629. else
  630. {
  631. errorMsg = AZStd::string::format(
  632. "Was expecting argument '%zu' in attribute '%s' to be of type 'int' from shader asset '%s'",
  633. argIndex,
  634. attributeName.GetCStr(),
  635. shaderAsset.GetHint().c_str());
  636. return false;
  637. }
  638. }
  639. else
  640. {
  641. errorMsg = AZStd::string::format(
  642. "Was expecting at least '%zu' arguments in attribute '%s' from shader asset '%s'",
  643. argIndex + 1,
  644. attributeName.GetCStr(),
  645. shaderAsset.GetHint().c_str());
  646. return false;
  647. }
  648. }
  649. return true;
  650. }
  651. AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(
  652. const Data::Asset<ShaderAsset>& shaderAsset,
  653. const AZ::Name& attributeName,
  654. uint16_t* numThreadsX,
  655. uint16_t* numThreadsY,
  656. uint16_t* numThreadsZ)
  657. {
  658. // Set default 1, 1, 1 now. In case of errors later this is what the caller will get.
  659. if (numThreadsX)
  660. {
  661. *numThreadsX = 1;
  662. }
  663. if (numThreadsY)
  664. {
  665. *numThreadsY = 1;
  666. }
  667. if (numThreadsZ)
  668. {
  669. *numThreadsZ = 1;
  670. }
  671. const auto numThreads = shaderAsset->GetAttribute(RHI::ShaderStage::Compute, attributeName);
  672. if (!numThreads)
  673. {
  674. return AZ::Failure(AZStd::string::format(
  675. "Couldn't find attribute '%s' in shader asset '%s'", attributeName.GetCStr(), shaderAsset.GetHint().c_str()));
  676. }
  677. const RHI::ShaderStageAttributeArguments& args = *numThreads;
  678. AZStd::string errorMsg;
  679. if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 0, numThreadsX, errorMsg))
  680. {
  681. return AZ::Failure(errorMsg);
  682. }
  683. if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 1, numThreadsY, errorMsg))
  684. {
  685. return AZ::Failure(errorMsg);
  686. }
  687. if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 2, numThreadsZ, errorMsg))
  688. {
  689. return AZ::Failure(errorMsg);
  690. }
  691. return AZ::Success();
  692. }
  693. AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(
  694. const Data::Asset<ShaderAsset>& shaderAsset, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ)
  695. {
  696. return GetComputeShaderNumThreads(shaderAsset, Name{ "numthreads" }, numThreadsX, numThreadsY, numThreadsZ);
  697. }
  698. AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(
  699. const Data::Asset<ShaderAsset>& shaderAsset, RHI::DispatchDirect& dispatchDirect)
  700. {
  701. return GetComputeShaderNumThreads(
  702. shaderAsset, &dispatchDirect.m_threadsPerGroupX, &dispatchDirect.m_threadsPerGroupY, &dispatchDirect.m_threadsPerGroupZ);
  703. }
  704. bool IsImageDataPixelAPISupported(AZ::RHI::Format format)
  705. {
  706. switch (format)
  707. {
  708. // Float types
  709. case AZ::RHI::Format::R8_UNORM:
  710. case AZ::RHI::Format::A8_UNORM:
  711. case AZ::RHI::Format::R8G8_UNORM:
  712. case AZ::RHI::Format::R8G8B8A8_UNORM:
  713. case AZ::RHI::Format::A8B8G8R8_UNORM:
  714. case AZ::RHI::Format::R8_UNORM_SRGB:
  715. case AZ::RHI::Format::R8G8_UNORM_SRGB:
  716. case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB:
  717. case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB:
  718. case AZ::RHI::Format::R8_SNORM:
  719. case AZ::RHI::Format::R8G8_SNORM:
  720. case AZ::RHI::Format::R8G8B8A8_SNORM:
  721. case AZ::RHI::Format::A8B8G8R8_SNORM:
  722. case AZ::RHI::Format::D16_UNORM:
  723. case AZ::RHI::Format::R16_UNORM:
  724. case AZ::RHI::Format::R16G16_UNORM:
  725. case AZ::RHI::Format::R16G16B16A16_UNORM:
  726. case AZ::RHI::Format::R16_SNORM:
  727. case AZ::RHI::Format::R16G16_SNORM:
  728. case AZ::RHI::Format::R16G16B16A16_SNORM:
  729. case AZ::RHI::Format::R16_FLOAT:
  730. case AZ::RHI::Format::R16G16_FLOAT:
  731. case AZ::RHI::Format::R16G16B16A16_FLOAT:
  732. case AZ::RHI::Format::D32_FLOAT:
  733. case AZ::RHI::Format::R32_FLOAT:
  734. case AZ::RHI::Format::R32G32_FLOAT:
  735. case AZ::RHI::Format::R32G32B32_FLOAT:
  736. case AZ::RHI::Format::R32G32B32A32_FLOAT:
  737. // Unsigned integer types
  738. case AZ::RHI::Format::R8_UINT:
  739. case AZ::RHI::Format::R8G8_UINT:
  740. case AZ::RHI::Format::R8G8B8A8_UINT:
  741. case AZ::RHI::Format::R16_UINT:
  742. case AZ::RHI::Format::R16G16_UINT:
  743. case AZ::RHI::Format::R16G16B16A16_UINT:
  744. case AZ::RHI::Format::R32_UINT:
  745. case AZ::RHI::Format::R32G32_UINT:
  746. case AZ::RHI::Format::R32G32B32_UINT:
  747. case AZ::RHI::Format::R32G32B32A32_UINT:
  748. // Signed integer types
  749. case AZ::RHI::Format::R8_SINT:
  750. case AZ::RHI::Format::R8G8_SINT:
  751. case AZ::RHI::Format::R8G8B8A8_SINT:
  752. case AZ::RHI::Format::R16_SINT:
  753. case AZ::RHI::Format::R16G16_SINT:
  754. case AZ::RHI::Format::R16G16B16A16_SINT:
  755. case AZ::RHI::Format::R32_SINT:
  756. case AZ::RHI::Format::R32G32_SINT:
  757. case AZ::RHI::Format::R32G32B32_SINT:
  758. case AZ::RHI::Format::R32G32B32A32_SINT:
  759. // Compressed types
  760. case AZ::RHI::Format::BC1_UNORM:
  761. case AZ::RHI::Format::BC1_UNORM_SRGB:
  762. case AZ::RHI::Format::BC4_UNORM:
  763. return true;
  764. }
  765. return false;
  766. }
  767. template<>
  768. AZ::Color GetImageDataPixelValue<AZ::Color>(
  769. AZStd::span<const uint8_t> imageData,
  770. const AZ::RHI::ImageDescriptor& imageDescriptor,
  771. uint32_t x,
  772. uint32_t y,
  773. [[maybe_unused]] uint32_t componentIndex)
  774. {
  775. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  776. return Internal::RetrieveColorValue(imageData.data(), imageDataIndices, imageDescriptor.m_format);
  777. }
  778. template<>
  779. float GetImageDataPixelValue<float>(
  780. AZStd::span<const uint8_t> imageData,
  781. const AZ::RHI::ImageDescriptor& imageDescriptor,
  782. uint32_t x,
  783. uint32_t y,
  784. uint32_t componentIndex)
  785. {
  786. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  787. return Internal::RetrieveFloatValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  788. }
  789. template<>
  790. AZ::u32 GetImageDataPixelValue<AZ::u32>(
  791. AZStd::span<const uint8_t> imageData,
  792. const AZ::RHI::ImageDescriptor& imageDescriptor,
  793. uint32_t x,
  794. uint32_t y,
  795. uint32_t componentIndex)
  796. {
  797. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  798. return Internal::RetrieveUintValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  799. }
  800. template<>
  801. AZ::s32 GetImageDataPixelValue<AZ::s32>(
  802. AZStd::span<const uint8_t> imageData,
  803. const AZ::RHI::ImageDescriptor& imageDescriptor,
  804. uint32_t x,
  805. uint32_t y,
  806. uint32_t componentIndex)
  807. {
  808. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  809. return Internal::RetrieveIntValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  810. }
  811. template<typename T>
  812. T GetSubImagePixelValueInternal(
  813. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  814. uint32_t x,
  815. uint32_t y,
  816. uint32_t componentIndex,
  817. uint32_t mip,
  818. uint32_t slice)
  819. {
  820. if (!imageAsset.IsReady())
  821. {
  822. return aznumeric_cast<T>(0);
  823. }
  824. auto imageData = imageAsset->GetSubImageData(mip, slice);
  825. if (imageData.empty())
  826. {
  827. return aznumeric_cast<T>(0);
  828. }
  829. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  830. return GetImageDataPixelValue<T>(imageData, imageDescriptor, x, y, componentIndex);
  831. }
  832. template<>
  833. float GetSubImagePixelValue<float>(
  834. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  835. uint32_t x,
  836. uint32_t y,
  837. uint32_t componentIndex,
  838. uint32_t mip,
  839. uint32_t slice)
  840. {
  841. return GetSubImagePixelValueInternal<float>(imageAsset, x, y, componentIndex, mip, slice);
  842. }
  843. template<>
  844. AZ::u32 GetSubImagePixelValue<AZ::u32>(
  845. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  846. uint32_t x,
  847. uint32_t y,
  848. uint32_t componentIndex,
  849. uint32_t mip,
  850. uint32_t slice)
  851. {
  852. return GetSubImagePixelValueInternal<AZ::u32>(imageAsset, x, y, componentIndex, mip, slice);
  853. }
  854. template<>
  855. AZ::s32 GetSubImagePixelValue<AZ::s32>(
  856. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  857. uint32_t x,
  858. uint32_t y,
  859. uint32_t componentIndex,
  860. uint32_t mip,
  861. uint32_t slice)
  862. {
  863. return GetSubImagePixelValueInternal<AZ::s32>(imageAsset, x, y, componentIndex, mip, slice);
  864. }
  865. bool GetSubImagePixelValues(
  866. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  867. AZStd::pair<uint32_t, uint32_t> topLeft,
  868. AZStd::pair<uint32_t, uint32_t> bottomRight,
  869. AZStd::function<void(const AZ::u32& x, const AZ::u32& y, const float& value)> callback,
  870. uint32_t componentIndex,
  871. uint32_t mip,
  872. uint32_t slice)
  873. {
  874. if (!imageAsset.IsReady())
  875. {
  876. return false;
  877. }
  878. auto imageData = imageAsset->GetSubImageData(mip, slice);
  879. if (imageData.empty())
  880. {
  881. return false;
  882. }
  883. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  884. for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
  885. {
  886. for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
  887. {
  888. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  889. float value =
  890. Internal::RetrieveFloatValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  891. callback(x, y, value);
  892. }
  893. }
  894. return true;
  895. }
  896. bool GetSubImagePixelValues(
  897. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  898. AZStd::pair<uint32_t, uint32_t> topLeft,
  899. AZStd::pair<uint32_t, uint32_t> bottomRight,
  900. AZStd::function<void(const AZ::u32& x, const AZ::u32& y, const AZ::u32& value)> callback,
  901. uint32_t componentIndex,
  902. uint32_t mip,
  903. uint32_t slice)
  904. {
  905. if (!imageAsset.IsReady())
  906. {
  907. return false;
  908. }
  909. auto imageData = imageAsset->GetSubImageData(mip, slice);
  910. if (imageData.empty())
  911. {
  912. return false;
  913. }
  914. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  915. for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
  916. {
  917. for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
  918. {
  919. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  920. AZ::u32 value =
  921. Internal::RetrieveUintValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  922. callback(x, y, value);
  923. }
  924. }
  925. return true;
  926. }
  927. bool GetSubImagePixelValues(
  928. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  929. AZStd::pair<uint32_t, uint32_t> topLeft,
  930. AZStd::pair<uint32_t, uint32_t> bottomRight,
  931. AZStd::function<void(const AZ::u32& x, const AZ::u32& y, const AZ::s32& value)> callback,
  932. uint32_t componentIndex,
  933. uint32_t mip,
  934. uint32_t slice)
  935. {
  936. if (!imageAsset.IsReady())
  937. {
  938. return false;
  939. }
  940. auto imageData = imageAsset->GetSubImageData(mip, slice);
  941. if (imageData.empty())
  942. {
  943. return false;
  944. }
  945. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  946. for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
  947. {
  948. for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
  949. {
  950. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  951. AZ::s32 value =
  952. Internal::RetrieveIntValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  953. callback(x, y, value);
  954. }
  955. }
  956. return true;
  957. }
  958. AZStd::optional<RenderPipelineDescriptor> GetRenderPipelineDescriptorFromAsset(
  959. const Data::AssetId& pipelineAssetId, AZStd::string_view nameSuffix)
  960. {
  961. AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset =
  962. AssetUtils::LoadAssetById<AZ::RPI::AnyAsset>(pipelineAssetId, AssetUtils::TraceLevel::Error);
  963. if (!pipelineAsset.IsReady())
  964. {
  965. // Error already reported by LoadAssetByProductPath
  966. return AZStd::nullopt;
  967. }
  968. const RenderPipelineDescriptor* assetPipelineDesc = GetDataFromAnyAsset<AZ::RPI::RenderPipelineDescriptor>(pipelineAsset);
  969. if (!assetPipelineDesc)
  970. {
  971. AZ_Error("RPIUtils", false, "Invalid render pipeline descriptor from asset %s", pipelineAssetId.ToString<AZStd::string>().c_str());
  972. return AZStd::nullopt;
  973. }
  974. RenderPipelineDescriptor pipelineDesc = *assetPipelineDesc;
  975. pipelineDesc.m_name += nameSuffix;
  976. return {AZStd::move(pipelineDesc)};
  977. }
  978. AZStd::optional<RenderPipelineDescriptor> GetRenderPipelineDescriptorFromAsset(
  979. const AZStd::string& pipelineAssetPath, AZStd::string_view nameSuffix)
  980. {
  981. Data::AssetId assetId = AssetUtils::GetAssetIdForProductPath(pipelineAssetPath.c_str(), AssetUtils::TraceLevel::Error);
  982. if (assetId.IsValid())
  983. {
  984. return GetRenderPipelineDescriptorFromAsset(assetId, nameSuffix);
  985. }
  986. else
  987. {
  988. return AZStd::nullopt;
  989. }
  990. }
  991. void AddPassRequestToRenderPipeline(
  992. AZ::RPI::RenderPipeline* renderPipeline,
  993. const char* passRequestAssetFilePath,
  994. const char* referencePass,
  995. bool beforeReferencePass)
  996. {
  997. auto passRequestAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(
  998. passRequestAssetFilePath, AZ::RPI::AssetUtils::TraceLevel::Warning);
  999. const AZ::RPI::PassRequest* passRequest = nullptr;
  1000. if (passRequestAsset->IsReady())
  1001. {
  1002. passRequest = passRequestAsset->GetDataAs<AZ::RPI::PassRequest>();
  1003. }
  1004. if (!passRequest)
  1005. {
  1006. AZ_Error("RPIUtils", false, "Can't load PassRequest from %s", passRequestAssetFilePath);
  1007. return;
  1008. }
  1009. // Return if the pass to be created already exists
  1010. AZ::RPI::PassFilter passFilter = AZ::RPI::PassFilter::CreateWithPassName(passRequest->m_passName, renderPipeline);
  1011. AZ::RPI::Pass* existingPass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
  1012. if (existingPass)
  1013. {
  1014. return;
  1015. }
  1016. // Create the pass
  1017. AZ::RPI::Ptr<AZ::RPI::Pass> newPass = AZ::RPI::PassSystemInterface::Get()->CreatePassFromRequest(passRequest);
  1018. if (!newPass)
  1019. {
  1020. AZ_Error("RPIUtils", false, "Failed to create the pass from pass request [%s].", passRequest->m_passName.GetCStr());
  1021. return;
  1022. }
  1023. // Add the pass to render pipeline
  1024. bool success;
  1025. if (beforeReferencePass)
  1026. {
  1027. success = renderPipeline->AddPassBefore(newPass, AZ::Name(referencePass));
  1028. }
  1029. else
  1030. {
  1031. success = renderPipeline->AddPassAfter(newPass, AZ::Name(referencePass));
  1032. }
  1033. // only create pass resources if it was success
  1034. if (!success)
  1035. {
  1036. AZ_Error(
  1037. "RPIUtils",
  1038. false,
  1039. "Failed to add pass [%s] to render pipeline [%s].",
  1040. newPass->GetName().GetCStr(),
  1041. renderPipeline->GetId().GetCStr());
  1042. }
  1043. }
  1044. } // namespace RPI
  1045. } // namespace AZ