RPIUtils.cpp 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  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. // Find a format for formats with two planars (DepthStencil) based on its ImageView's aspect flag
  633. RHI::Format FindFormatForAspect(RHI::Format format, RHI::ImageAspect imageAspect)
  634. {
  635. RHI::ImageAspectFlags imageAspectFlags = RHI::GetImageAspectFlags(format);
  636. // only need to convert if the source contains two aspects
  637. if (imageAspectFlags == RHI::ImageAspectFlags::DepthStencil)
  638. {
  639. switch (imageAspect)
  640. {
  641. case RHI::ImageAspect::Stencil:
  642. return RHI::Format::R8_UINT;
  643. case RHI::ImageAspect::Depth:
  644. {
  645. switch (format)
  646. {
  647. case RHI::Format::D32_FLOAT_S8X24_UINT:
  648. return RHI::Format::R32_FLOAT;
  649. case RHI::Format::D24_UNORM_S8_UINT:
  650. return RHI::Format::R32_UINT;
  651. case RHI::Format::D16_UNORM_S8_UINT:
  652. return RHI::Format::R16_UNORM;
  653. default:
  654. AZ_Assert(false, "Unknown DepthStencil format. Please update this function");
  655. return RHI::Format::R32_FLOAT;
  656. }
  657. }
  658. }
  659. }
  660. return format;
  661. }
  662. //! A helper function for GetComputeShaderNumThreads(), to consolidate error messages, etc.
  663. static bool GetAttributeArgumentByIndex(
  664. const Data::Asset<ShaderAsset>& shaderAsset,
  665. const AZ::Name& attributeName,
  666. const RHI::ShaderStageAttributeArguments& args,
  667. const size_t argIndex,
  668. uint16_t* value,
  669. AZStd::string& errorMsg)
  670. {
  671. if (value)
  672. {
  673. const auto numArguments = args.size();
  674. if (numArguments > argIndex)
  675. {
  676. if (args[argIndex].type() == azrtti_typeid<int>())
  677. {
  678. *value = aznumeric_caster(AZStd::any_cast<int>(args[argIndex]));
  679. }
  680. else
  681. {
  682. errorMsg = AZStd::string::format(
  683. "Was expecting argument '%zu' in attribute '%s' to be of type 'int' from shader asset '%s'",
  684. argIndex,
  685. attributeName.GetCStr(),
  686. shaderAsset.GetHint().c_str());
  687. return false;
  688. }
  689. }
  690. else
  691. {
  692. errorMsg = AZStd::string::format(
  693. "Was expecting at least '%zu' arguments in attribute '%s' from shader asset '%s'",
  694. argIndex + 1,
  695. attributeName.GetCStr(),
  696. shaderAsset.GetHint().c_str());
  697. return false;
  698. }
  699. }
  700. return true;
  701. }
  702. AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(
  703. const Data::Asset<ShaderAsset>& shaderAsset,
  704. const AZ::Name& attributeName,
  705. uint16_t* numThreadsX,
  706. uint16_t* numThreadsY,
  707. uint16_t* numThreadsZ)
  708. {
  709. // Set default 1, 1, 1 now. In case of errors later this is what the caller will get.
  710. if (numThreadsX)
  711. {
  712. *numThreadsX = 1;
  713. }
  714. if (numThreadsY)
  715. {
  716. *numThreadsY = 1;
  717. }
  718. if (numThreadsZ)
  719. {
  720. *numThreadsZ = 1;
  721. }
  722. const auto numThreads = shaderAsset->GetAttribute(RHI::ShaderStage::Compute, attributeName);
  723. if (!numThreads)
  724. {
  725. return AZ::Failure(AZStd::string::format(
  726. "Couldn't find attribute '%s' in shader asset '%s'", attributeName.GetCStr(), shaderAsset.GetHint().c_str()));
  727. }
  728. const RHI::ShaderStageAttributeArguments& args = *numThreads;
  729. AZStd::string errorMsg;
  730. if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 0, numThreadsX, errorMsg))
  731. {
  732. return AZ::Failure(errorMsg);
  733. }
  734. if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 1, numThreadsY, errorMsg))
  735. {
  736. return AZ::Failure(errorMsg);
  737. }
  738. if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 2, numThreadsZ, errorMsg))
  739. {
  740. return AZ::Failure(errorMsg);
  741. }
  742. return AZ::Success();
  743. }
  744. AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(
  745. const Data::Asset<ShaderAsset>& shaderAsset, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ)
  746. {
  747. return GetComputeShaderNumThreads(shaderAsset, Name{ "numthreads" }, numThreadsX, numThreadsY, numThreadsZ);
  748. }
  749. AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(
  750. const Data::Asset<ShaderAsset>& shaderAsset, RHI::DispatchDirect& dispatchDirect)
  751. {
  752. return GetComputeShaderNumThreads(
  753. shaderAsset, &dispatchDirect.m_threadsPerGroupX, &dispatchDirect.m_threadsPerGroupY, &dispatchDirect.m_threadsPerGroupZ);
  754. }
  755. bool IsImageDataPixelAPISupported(AZ::RHI::Format format)
  756. {
  757. switch (format)
  758. {
  759. // Float types
  760. case AZ::RHI::Format::R8_UNORM:
  761. case AZ::RHI::Format::A8_UNORM:
  762. case AZ::RHI::Format::R8G8_UNORM:
  763. case AZ::RHI::Format::R8G8B8A8_UNORM:
  764. case AZ::RHI::Format::A8B8G8R8_UNORM:
  765. case AZ::RHI::Format::R8_UNORM_SRGB:
  766. case AZ::RHI::Format::R8G8_UNORM_SRGB:
  767. case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB:
  768. case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB:
  769. case AZ::RHI::Format::R8_SNORM:
  770. case AZ::RHI::Format::R8G8_SNORM:
  771. case AZ::RHI::Format::R8G8B8A8_SNORM:
  772. case AZ::RHI::Format::A8B8G8R8_SNORM:
  773. case AZ::RHI::Format::D16_UNORM:
  774. case AZ::RHI::Format::R16_UNORM:
  775. case AZ::RHI::Format::R16G16_UNORM:
  776. case AZ::RHI::Format::R16G16B16A16_UNORM:
  777. case AZ::RHI::Format::R16_SNORM:
  778. case AZ::RHI::Format::R16G16_SNORM:
  779. case AZ::RHI::Format::R16G16B16A16_SNORM:
  780. case AZ::RHI::Format::R16_FLOAT:
  781. case AZ::RHI::Format::R16G16_FLOAT:
  782. case AZ::RHI::Format::R16G16B16A16_FLOAT:
  783. case AZ::RHI::Format::D32_FLOAT:
  784. case AZ::RHI::Format::R32_FLOAT:
  785. case AZ::RHI::Format::R32G32_FLOAT:
  786. case AZ::RHI::Format::R32G32B32_FLOAT:
  787. case AZ::RHI::Format::R32G32B32A32_FLOAT:
  788. // Unsigned integer types
  789. case AZ::RHI::Format::R8_UINT:
  790. case AZ::RHI::Format::R8G8_UINT:
  791. case AZ::RHI::Format::R8G8B8A8_UINT:
  792. case AZ::RHI::Format::R16_UINT:
  793. case AZ::RHI::Format::R16G16_UINT:
  794. case AZ::RHI::Format::R16G16B16A16_UINT:
  795. case AZ::RHI::Format::R32_UINT:
  796. case AZ::RHI::Format::R32G32_UINT:
  797. case AZ::RHI::Format::R32G32B32_UINT:
  798. case AZ::RHI::Format::R32G32B32A32_UINT:
  799. // Signed integer types
  800. case AZ::RHI::Format::R8_SINT:
  801. case AZ::RHI::Format::R8G8_SINT:
  802. case AZ::RHI::Format::R8G8B8A8_SINT:
  803. case AZ::RHI::Format::R16_SINT:
  804. case AZ::RHI::Format::R16G16_SINT:
  805. case AZ::RHI::Format::R16G16B16A16_SINT:
  806. case AZ::RHI::Format::R32_SINT:
  807. case AZ::RHI::Format::R32G32_SINT:
  808. case AZ::RHI::Format::R32G32B32_SINT:
  809. case AZ::RHI::Format::R32G32B32A32_SINT:
  810. // Compressed types
  811. case AZ::RHI::Format::BC1_UNORM:
  812. case AZ::RHI::Format::BC1_UNORM_SRGB:
  813. case AZ::RHI::Format::BC4_UNORM:
  814. return true;
  815. }
  816. return false;
  817. }
  818. template<>
  819. AZ::Color GetImageDataPixelValue<AZ::Color>(
  820. AZStd::span<const uint8_t> imageData,
  821. const AZ::RHI::ImageDescriptor& imageDescriptor,
  822. uint32_t x,
  823. uint32_t y,
  824. [[maybe_unused]] uint32_t componentIndex)
  825. {
  826. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  827. return Internal::RetrieveColorValue(imageData.data(), imageDataIndices, imageDescriptor.m_format);
  828. }
  829. template<>
  830. float GetImageDataPixelValue<float>(
  831. AZStd::span<const uint8_t> imageData,
  832. const AZ::RHI::ImageDescriptor& imageDescriptor,
  833. uint32_t x,
  834. uint32_t y,
  835. uint32_t componentIndex)
  836. {
  837. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  838. return Internal::RetrieveFloatValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  839. }
  840. template<>
  841. AZ::u32 GetImageDataPixelValue<AZ::u32>(
  842. AZStd::span<const uint8_t> imageData,
  843. const AZ::RHI::ImageDescriptor& imageDescriptor,
  844. uint32_t x,
  845. uint32_t y,
  846. uint32_t componentIndex)
  847. {
  848. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  849. return Internal::RetrieveUintValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  850. }
  851. template<>
  852. AZ::s32 GetImageDataPixelValue<AZ::s32>(
  853. AZStd::span<const uint8_t> imageData,
  854. const AZ::RHI::ImageDescriptor& imageDescriptor,
  855. uint32_t x,
  856. uint32_t y,
  857. uint32_t componentIndex)
  858. {
  859. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  860. return Internal::RetrieveIntValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  861. }
  862. template<typename T>
  863. T GetSubImagePixelValueInternal(
  864. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  865. uint32_t x,
  866. uint32_t y,
  867. uint32_t componentIndex,
  868. uint32_t mip,
  869. uint32_t slice)
  870. {
  871. if (!imageAsset.IsReady())
  872. {
  873. return aznumeric_cast<T>(0);
  874. }
  875. auto imageData = imageAsset->GetSubImageData(mip, slice);
  876. if (imageData.empty())
  877. {
  878. return aznumeric_cast<T>(0);
  879. }
  880. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  881. return GetImageDataPixelValue<T>(imageData, imageDescriptor, x, y, componentIndex);
  882. }
  883. template<>
  884. float GetSubImagePixelValue<float>(
  885. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  886. uint32_t x,
  887. uint32_t y,
  888. uint32_t componentIndex,
  889. uint32_t mip,
  890. uint32_t slice)
  891. {
  892. return GetSubImagePixelValueInternal<float>(imageAsset, x, y, componentIndex, mip, slice);
  893. }
  894. template<>
  895. AZ::u32 GetSubImagePixelValue<AZ::u32>(
  896. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  897. uint32_t x,
  898. uint32_t y,
  899. uint32_t componentIndex,
  900. uint32_t mip,
  901. uint32_t slice)
  902. {
  903. return GetSubImagePixelValueInternal<AZ::u32>(imageAsset, x, y, componentIndex, mip, slice);
  904. }
  905. template<>
  906. AZ::s32 GetSubImagePixelValue<AZ::s32>(
  907. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  908. uint32_t x,
  909. uint32_t y,
  910. uint32_t componentIndex,
  911. uint32_t mip,
  912. uint32_t slice)
  913. {
  914. return GetSubImagePixelValueInternal<AZ::s32>(imageAsset, x, y, componentIndex, mip, slice);
  915. }
  916. bool GetSubImagePixelValues(
  917. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  918. AZStd::pair<uint32_t, uint32_t> topLeft,
  919. AZStd::pair<uint32_t, uint32_t> bottomRight,
  920. AZStd::function<void(const AZ::u32& x, const AZ::u32& y, const float& value)> callback,
  921. uint32_t componentIndex,
  922. uint32_t mip,
  923. uint32_t slice)
  924. {
  925. if (!imageAsset.IsReady())
  926. {
  927. return false;
  928. }
  929. auto imageData = imageAsset->GetSubImageData(mip, slice);
  930. if (imageData.empty())
  931. {
  932. return false;
  933. }
  934. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  935. for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
  936. {
  937. for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
  938. {
  939. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  940. float value =
  941. Internal::RetrieveFloatValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  942. callback(x, y, value);
  943. }
  944. }
  945. return true;
  946. }
  947. bool GetSubImagePixelValues(
  948. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  949. AZStd::pair<uint32_t, uint32_t> topLeft,
  950. AZStd::pair<uint32_t, uint32_t> bottomRight,
  951. AZStd::function<void(const AZ::u32& x, const AZ::u32& y, const AZ::u32& value)> callback,
  952. uint32_t componentIndex,
  953. uint32_t mip,
  954. uint32_t slice)
  955. {
  956. if (!imageAsset.IsReady())
  957. {
  958. return false;
  959. }
  960. auto imageData = imageAsset->GetSubImageData(mip, slice);
  961. if (imageData.empty())
  962. {
  963. return false;
  964. }
  965. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  966. for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
  967. {
  968. for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
  969. {
  970. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  971. AZ::u32 value =
  972. Internal::RetrieveUintValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  973. callback(x, y, value);
  974. }
  975. }
  976. return true;
  977. }
  978. bool GetSubImagePixelValues(
  979. const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset,
  980. AZStd::pair<uint32_t, uint32_t> topLeft,
  981. AZStd::pair<uint32_t, uint32_t> bottomRight,
  982. AZStd::function<void(const AZ::u32& x, const AZ::u32& y, const AZ::s32& value)> callback,
  983. uint32_t componentIndex,
  984. uint32_t mip,
  985. uint32_t slice)
  986. {
  987. if (!imageAsset.IsReady())
  988. {
  989. return false;
  990. }
  991. auto imageData = imageAsset->GetSubImageData(mip, slice);
  992. if (imageData.empty())
  993. {
  994. return false;
  995. }
  996. auto imageDescriptor = imageAsset->GetImageDescriptorForMipLevel(mip);
  997. for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
  998. {
  999. for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
  1000. {
  1001. auto imageDataIndices = Internal::GetImageDataIndex(imageDescriptor, x, y);
  1002. AZ::s32 value =
  1003. Internal::RetrieveIntValue(imageData.data(), imageDataIndices, componentIndex, imageDescriptor.m_format);
  1004. callback(x, y, value);
  1005. }
  1006. }
  1007. return true;
  1008. }
  1009. AZStd::optional<RenderPipelineDescriptor> GetRenderPipelineDescriptorFromAsset(
  1010. const Data::AssetId& pipelineAssetId, AZStd::string_view nameSuffix)
  1011. {
  1012. AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset =
  1013. AssetUtils::LoadAssetById<AZ::RPI::AnyAsset>(pipelineAssetId, AssetUtils::TraceLevel::Error);
  1014. if (!pipelineAsset.IsReady())
  1015. {
  1016. // Error already reported by LoadAssetByProductPath
  1017. return AZStd::nullopt;
  1018. }
  1019. const RenderPipelineDescriptor* assetPipelineDesc = GetDataFromAnyAsset<AZ::RPI::RenderPipelineDescriptor>(pipelineAsset);
  1020. if (!assetPipelineDesc)
  1021. {
  1022. AZ_Error("RPIUtils", false, "Invalid render pipeline descriptor from asset %s", pipelineAssetId.ToString<AZStd::string>().c_str());
  1023. return AZStd::nullopt;
  1024. }
  1025. RenderPipelineDescriptor pipelineDesc = *assetPipelineDesc;
  1026. pipelineDesc.m_name += nameSuffix;
  1027. return {AZStd::move(pipelineDesc)};
  1028. }
  1029. AZStd::optional<RenderPipelineDescriptor> GetRenderPipelineDescriptorFromAsset(
  1030. const AZStd::string& pipelineAssetPath, AZStd::string_view nameSuffix)
  1031. {
  1032. Data::AssetId assetId = AssetUtils::GetAssetIdForProductPath(pipelineAssetPath.c_str(), AssetUtils::TraceLevel::Error);
  1033. if (assetId.IsValid())
  1034. {
  1035. return GetRenderPipelineDescriptorFromAsset(assetId, nameSuffix);
  1036. }
  1037. else
  1038. {
  1039. return AZStd::nullopt;
  1040. }
  1041. }
  1042. void AddPassRequestToRenderPipeline(
  1043. AZ::RPI::RenderPipeline* renderPipeline,
  1044. const char* passRequestAssetFilePath,
  1045. const char* referencePass,
  1046. bool beforeReferencePass)
  1047. {
  1048. auto passRequestAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(
  1049. passRequestAssetFilePath, AZ::RPI::AssetUtils::TraceLevel::Warning);
  1050. const AZ::RPI::PassRequest* passRequest = nullptr;
  1051. if (passRequestAsset->IsReady())
  1052. {
  1053. passRequest = passRequestAsset->GetDataAs<AZ::RPI::PassRequest>();
  1054. }
  1055. if (!passRequest)
  1056. {
  1057. AZ_Error("RPIUtils", false, "Can't load PassRequest from %s", passRequestAssetFilePath);
  1058. return;
  1059. }
  1060. // Return if the pass to be created already exists
  1061. AZ::RPI::PassFilter passFilter = AZ::RPI::PassFilter::CreateWithPassName(passRequest->m_passName, renderPipeline);
  1062. AZ::RPI::Pass* existingPass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
  1063. if (existingPass)
  1064. {
  1065. return;
  1066. }
  1067. // Create the pass
  1068. AZ::RPI::Ptr<AZ::RPI::Pass> newPass = AZ::RPI::PassSystemInterface::Get()->CreatePassFromRequest(passRequest);
  1069. if (!newPass)
  1070. {
  1071. AZ_Error("RPIUtils", false, "Failed to create the pass from pass request [%s].", passRequest->m_passName.GetCStr());
  1072. return;
  1073. }
  1074. // Add the pass to render pipeline
  1075. bool success;
  1076. if (beforeReferencePass)
  1077. {
  1078. success = renderPipeline->AddPassBefore(newPass, AZ::Name(referencePass));
  1079. }
  1080. else
  1081. {
  1082. success = renderPipeline->AddPassAfter(newPass, AZ::Name(referencePass));
  1083. }
  1084. // only create pass resources if it was success
  1085. if (!success)
  1086. {
  1087. AZ_Error(
  1088. "RPIUtils",
  1089. false,
  1090. "Failed to add pass [%s] to render pipeline [%s].",
  1091. newPass->GetName().GetCStr(),
  1092. renderPipeline->GetId().GetCStr());
  1093. }
  1094. }
  1095. } // namespace RPI
  1096. } // namespace AZ