RPIUtils.cpp 51 KB

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