3
0

ShaderOptionGroupLayout.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/RPI.Reflect/Shader/ShaderOptionGroupLayout.h>
  9. #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
  10. #include <Atom/RHI.Reflect/Bits.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/std/string/conversions.h>
  13. #include <AzCore/std/sort.h>
  14. #include <AzCore/Utils/TypeHash.h>
  15. #include <AzFramework/StringFunc/StringFunc.h>
  16. namespace AZ
  17. {
  18. namespace RPI
  19. {
  20. const char* ShaderOptionDescriptor::DebugCategory = "ShaderOption";
  21. const char* ShaderOptionGroupLayout::DebugCategory = "ShaderOption";
  22. const char* ToString(ShaderOptionType shaderOptionType)
  23. {
  24. switch (shaderOptionType)
  25. {
  26. case ShaderOptionType::Boolean: return "Boolean";
  27. case ShaderOptionType::Enumeration: return "Enumeration";
  28. case ShaderOptionType::IntegerRange: return "IntegerRange";
  29. default: return "<Unknown>";
  30. }
  31. }
  32. ShaderOptionValues CreateEnumShaderOptionValues(AZStd::span<const AZStd::string_view> enumNames)
  33. {
  34. ShaderOptionValues values;
  35. values.reserve(enumNames.size());
  36. for (size_t i = 0; i < enumNames.size(); ++i)
  37. {
  38. values.emplace_back(Name{enumNames[i]}, i);
  39. }
  40. return values;
  41. }
  42. ShaderOptionValues CreateEnumShaderOptionValues(AZStd::initializer_list<AZStd::string_view> enumNames)
  43. {
  44. return CreateEnumShaderOptionValues(AZStd::span(enumNames.begin(), enumNames.end()));
  45. }
  46. ShaderOptionValues CreateBoolShaderOptionValues()
  47. {
  48. return CreateEnumShaderOptionValues({"False", "True"});
  49. }
  50. ShaderOptionValues CreateIntRangeShaderOptionValues(uint32_t min, uint32_t max)
  51. {
  52. AZStd::vector<RPI::ShaderOptionValuePair> intOptionRange;
  53. intOptionRange.push_back({Name{AZStd::string::format("%u", min)}, RPI::ShaderOptionValue{min}});
  54. intOptionRange.push_back({Name{AZStd::string::format("%u", max)}, RPI::ShaderOptionValue{max}});
  55. return intOptionRange;
  56. }
  57. void ShaderOptionGroupHints::Reflect(ReflectContext* context)
  58. {
  59. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  60. {
  61. serializeContext->Class<ShaderOptionGroupHints>()
  62. ->Version(4)
  63. ->Field("BakePrecedingVariants", &ShaderOptionGroupHints::m_bakePrecedingVariants)
  64. ->Field("BakeEmptyAsDefault", &ShaderOptionGroupHints::m_bakeEmptyAsDefault)
  65. ;
  66. }
  67. }
  68. void ShaderOptionDescriptor::Reflect(AZ::ReflectContext* context)
  69. {
  70. ShaderOptionValue::Reflect(context);
  71. NameReflectionMapForValues::Reflect(context);
  72. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  73. {
  74. serializeContext->Class<ShaderOptionDescriptor>()
  75. ->Version(6) // 6: addition of m_specializationId field
  76. ->Field("m_name", &ShaderOptionDescriptor::m_name)
  77. ->Field("m_type", &ShaderOptionDescriptor::m_type)
  78. ->Field("m_defaultValue", &ShaderOptionDescriptor::m_defaultValue)
  79. ->Field("m_minValue", &ShaderOptionDescriptor::m_minValue)
  80. ->Field("m_maxValue", &ShaderOptionDescriptor::m_maxValue)
  81. ->Field("m_bitOffset", &ShaderOptionDescriptor::m_bitOffset)
  82. ->Field("m_bitCount", &ShaderOptionDescriptor::m_bitCount)
  83. ->Field("m_order", &ShaderOptionDescriptor::m_order)
  84. ->Field("m_costEstimate", &ShaderOptionDescriptor::m_costEstimate)
  85. ->Field("m_bitMask", &ShaderOptionDescriptor::m_bitMask)
  86. ->Field("m_bitMaskNot", &ShaderOptionDescriptor::m_bitMaskNot)
  87. ->Field("m_hash", &ShaderOptionDescriptor::m_hash)
  88. ->Field("m_nameReflectionForValues", &ShaderOptionDescriptor::m_nameReflectionForValues)
  89. ->Field("m_specializationId", &ShaderOptionDescriptor::m_specializationId)
  90. ;
  91. }
  92. if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
  93. {
  94. behaviorContext->Class<ShaderOptionDescriptor>()
  95. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  96. ->Attribute(AZ::Script::Attributes::Category, "Shader")
  97. ->Attribute(AZ::Script::Attributes::Module, "shader")
  98. ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
  99. ->Method("GetName", &ShaderOptionDescriptor::GetName)
  100. ->Method("GetDefaultValue", &ShaderOptionDescriptor::GetDefaultValue)
  101. ->Method("GetValueName", static_cast<Name (ShaderOptionDescriptor::*)(ShaderOptionValue) const>(&ShaderOptionDescriptor::GetValueName))
  102. ->Method("FindValue", &ShaderOptionDescriptor::FindValue)
  103. ->Method("GetMinValue", &ShaderOptionDescriptor::GetMinValue)
  104. ->Method("GetMaxValue", &ShaderOptionDescriptor::GetMaxValue)
  105. ->Method("GetValuesCount", &ShaderOptionDescriptor::GetValuesCount)
  106. ->Method("GetType", &ShaderOptionDescriptor::GetType)
  107. ->Method("GetValueNameByIndex", static_cast<Name (ShaderOptionDescriptor::*)(uint32_t) const>(&ShaderOptionDescriptor::GetValueName))
  108. ->Method("GetOrder", &ShaderOptionDescriptor::GetOrder)
  109. ->Method("GetCostEstimate", &ShaderOptionDescriptor::GetCostEstimate)
  110. ;
  111. }
  112. }
  113. ShaderOptionDescriptor::ShaderOptionDescriptor(const Name& name,
  114. const ShaderOptionType& optionType,
  115. uint32_t bitOffset,
  116. uint32_t order,
  117. const ShaderOptionValues& nameIndexList,
  118. const Name& defaultValue,
  119. uint32_t cost,
  120. int specializationId)
  121. : m_name{name}
  122. , m_type{optionType}
  123. , m_bitOffset{bitOffset}
  124. , m_order{order}
  125. , m_costEstimate{cost}
  126. , m_defaultValue{defaultValue}
  127. , m_specializationId{specializationId}
  128. {
  129. for (auto pair : nameIndexList)
  130. { // Registers the pair in the lookup table
  131. AddValue(pair.first, pair.second);
  132. if (m_defaultValue.IsEmpty())
  133. {
  134. m_defaultValue = pair.first;
  135. }
  136. }
  137. uint32_t numValues = (m_type == ShaderOptionType::IntegerRange) ? (m_maxValue.GetIndex() - m_minValue.GetIndex() + 1) : (uint32_t) nameIndexList.size();
  138. numValues = RHI::NextPowerOfTwo(numValues) - 1;
  139. m_bitCount = RHI::CountBitsSet(numValues);
  140. ShaderVariantKey bitMask;
  141. bitMask = AZ_BIT_MASK(m_bitCount);
  142. bitMask <<= m_bitOffset;
  143. m_bitMask = bitMask;
  144. m_bitMaskNot = ~bitMask;
  145. m_hash = TypeHash64(m_bitMask, static_cast<HashValue64>(m_name.GetHash()));
  146. }
  147. const Name& ShaderOptionDescriptor::GetName() const
  148. {
  149. return m_name;
  150. }
  151. uint32_t ShaderOptionDescriptor::GetBitOffset() const
  152. {
  153. return m_bitOffset;
  154. }
  155. uint32_t ShaderOptionDescriptor::GetBitCount() const
  156. {
  157. return m_bitCount;
  158. }
  159. uint32_t ShaderOptionDescriptor::GetOrder() const
  160. {
  161. return m_order;
  162. }
  163. uint32_t ShaderOptionDescriptor::GetCostEstimate() const
  164. {
  165. return m_costEstimate;
  166. }
  167. int ShaderOptionDescriptor::GetSpecializationId() const
  168. {
  169. return m_specializationId;
  170. }
  171. ShaderVariantKey ShaderOptionDescriptor::GetBitMask() const
  172. {
  173. return m_bitMask;
  174. }
  175. ShaderVariantKey ShaderOptionDescriptor::GetBitMaskNot() const
  176. {
  177. return m_bitMaskNot;
  178. }
  179. HashValue64 ShaderOptionDescriptor::GetHash() const
  180. {
  181. return m_hash;
  182. }
  183. bool ShaderOptionDescriptor::Set(ShaderOptionGroup& group, const Name& valueName) const
  184. {
  185. auto valueIndex = FindValue(valueName);
  186. if (valueIndex.IsValid())
  187. {
  188. return Set(group, valueIndex);
  189. }
  190. else
  191. {
  192. AZ_Error(DebugCategory, false, "ShaderOption value '%s' does not exist", valueName.GetCStr());
  193. return false;
  194. }
  195. }
  196. bool ShaderOptionDescriptor::Set(ShaderOptionGroup& group, const ShaderOptionValue valueIndex) const
  197. {
  198. if (valueIndex.IsNull())
  199. {
  200. AZ_Error(DebugCategory, false, "Invalid ShaderOption value");
  201. return false;
  202. }
  203. if (m_type == ShaderOptionType::Unknown)
  204. {
  205. group.GetShaderVariantMask() &= m_bitMaskNot;
  206. }
  207. else
  208. {
  209. if (!(m_minValue.GetIndex() <= valueIndex.GetIndex() && valueIndex.GetIndex() <= m_maxValue.GetIndex()))
  210. {
  211. AZ_Error(DebugCategory, false, "%s ShaderOption value [%d] is out of range [%d,%d].",
  212. ToString(m_type), valueIndex.GetIndex(), m_minValue.GetIndex(), m_maxValue.GetIndex());
  213. return false;
  214. }
  215. EncodeBits(group.GetShaderVariantKey(), valueIndex.GetIndex() - m_minValue.GetIndex());
  216. group.GetShaderVariantMask() |= m_bitMask;
  217. }
  218. return true;
  219. }
  220. bool ShaderOptionDescriptor::Set(ShaderVariantKey& key, const ShaderOptionValue valueIndex) const
  221. {
  222. if (valueIndex.IsNull())
  223. {
  224. AZ_Error(DebugCategory, false, "Invalid ShaderOption value");
  225. return false;
  226. }
  227. if (m_type != ShaderOptionType::Unknown)
  228. {
  229. if (!(m_minValue.GetIndex() <= valueIndex.GetIndex() && valueIndex.GetIndex() <= m_maxValue.GetIndex()))
  230. {
  231. AZ_Error(DebugCategory, false, "%s ShaderOption value [%d] is out of range [%d,%d].",
  232. ToString(m_type), valueIndex.GetIndex(), m_minValue.GetIndex(), m_maxValue.GetIndex());
  233. return false;
  234. }
  235. EncodeBits(key, valueIndex.GetIndex() - m_minValue.GetIndex());
  236. }
  237. return true;
  238. }
  239. ShaderOptionValue ShaderOptionDescriptor::Get(const ShaderOptionGroup& group) const
  240. {
  241. if (group.GetShaderVariantMask().test(m_bitOffset))
  242. {
  243. return ShaderOptionValue(DecodeBits(group.GetShaderVariantKey()) + m_minValue.GetIndex());
  244. }
  245. return ShaderOptionValue();
  246. }
  247. void ShaderOptionDescriptor::Clear(ShaderOptionGroup& group) const
  248. {
  249. group.GetShaderVariantMask() &= m_bitMaskNot;
  250. }
  251. void ShaderOptionDescriptor::AddValue(const Name& valueName, const ShaderOptionValue valueIndex)
  252. {
  253. AZ_Assert(m_type != ShaderOptionType::IntegerRange || valueIndex.GetIndex() == ShaderOptionValue((uint32_t)AZStd::stoll(AZStd::string{valueName.GetCStr()})).GetIndex(), "By convention, IntegerRange's values' ids must be equal to their numerical value!");
  254. m_nameReflectionForValues.Insert(valueName, valueIndex);
  255. if (m_minValue.IsNull() || m_minValue.GetIndex() > valueIndex.GetIndex())
  256. {
  257. m_minValue = valueIndex;
  258. }
  259. if (m_maxValue.IsNull() || m_maxValue.GetIndex() < valueIndex.GetIndex())
  260. {
  261. m_maxValue = valueIndex;
  262. }
  263. }
  264. void ShaderOptionDescriptor::SetDefaultValue(const Name& valueName)
  265. {
  266. AZ_Assert(!valueName.IsEmpty(), "The default value cannot be empty!");
  267. auto valueIter = m_nameReflectionForValues.Find(valueName);
  268. if (valueIter.IsNull())
  269. {
  270. AZ_Assert(false, "ShaderOption [%s] has no member value [%s] so this cannot be the default!", m_name.GetCStr(), valueName.GetCStr());
  271. return;
  272. }
  273. m_defaultValue = valueName;
  274. }
  275. const Name& ShaderOptionDescriptor::GetDefaultValue() const
  276. {
  277. return m_defaultValue;
  278. }
  279. uint32_t ShaderOptionDescriptor::GetValuesCount() const
  280. {
  281. return static_cast<uint32_t>(m_maxValue.GetIndex() - m_minValue.GetIndex() + 1);
  282. }
  283. /// Sets the hint type for the shader option
  284. void ShaderOptionDescriptor::SetType(ShaderOptionType optionType)
  285. {
  286. m_type = optionType;
  287. }
  288. /// Gets the hint type for the shader option
  289. const ShaderOptionType& ShaderOptionDescriptor::GetType() const
  290. {
  291. return m_type;
  292. }
  293. ShaderOptionValue ShaderOptionDescriptor::GetMinValue() const
  294. {
  295. return m_minValue;
  296. }
  297. ShaderOptionValue ShaderOptionDescriptor::GetMaxValue() const
  298. {
  299. return m_maxValue;
  300. }
  301. ShaderOptionValue ShaderOptionDescriptor::FindValue(const Name& valueName) const
  302. {
  303. switch (m_type)
  304. {
  305. case ShaderOptionType::Boolean:
  306. // This is better than hardcoding True, or On, or Enabled:
  307. return m_nameReflectionForValues.Find(valueName);
  308. case ShaderOptionType::Enumeration:
  309. return m_nameReflectionForValues.Find(valueName);
  310. case ShaderOptionType::IntegerRange:
  311. {
  312. int asInt;
  313. if (AzFramework::StringFunc::LooksLikeInt(valueName.GetCStr(), &asInt))
  314. {
  315. if (aznumeric_cast<int64_t>(m_minValue.GetIndex()) <= asInt && asInt <= aznumeric_cast<int64_t>(m_maxValue.GetIndex()))
  316. {
  317. return ShaderOptionValue(asInt);
  318. }
  319. }
  320. }
  321. return ShaderOptionValue();
  322. default:
  323. AZ_Assert(false, "Unhandled case for ShaderOptionType! We should not break here!");
  324. }
  325. // Unreachable code
  326. return ShaderOptionValue();
  327. }
  328. Name ShaderOptionDescriptor::GetValueName(ShaderOptionValue value) const
  329. {
  330. if (m_type == ShaderOptionType::IntegerRange)
  331. {
  332. // We can just return the value here, as IntegerRange's values' ids must be equal to their numerical value, this had been checked in AddValue()
  333. // We can't use m_nameReflectionForValues, since it only contains min and max value
  334. uint32_t value_uint = value.GetIndex();
  335. if (m_minValue.GetIndex() <= value_uint && value_uint <= m_maxValue.GetIndex())
  336. {
  337. return Name(AZStd::to_string(value_uint));
  338. }
  339. else
  340. {
  341. // mimic the behavior of RHI::NameIdReflectionMap's Find function
  342. return {};
  343. }
  344. }
  345. auto name = m_nameReflectionForValues.Find(value);
  346. return name;
  347. }
  348. Name ShaderOptionDescriptor::GetValueName(uint32_t valueIndex) const
  349. {
  350. return GetValueName(ShaderOptionValue{ valueIndex });
  351. }
  352. void ShaderOptionDescriptor::EncodeBits(ShaderVariantKey& shaderVariantKey, uint32_t value) const
  353. {
  354. if (value < AZ_BIT(m_bitCount))
  355. {
  356. ShaderVariantKey valueBits = (value & AZ_BIT_MASK(m_bitCount));
  357. valueBits <<= m_bitOffset;
  358. shaderVariantKey &= m_bitMaskNot;
  359. shaderVariantKey |= valueBits;
  360. }
  361. else
  362. {
  363. AZ_Assert(false, "Exceeded maximum number of bits allocated for option.");
  364. }
  365. }
  366. uint32_t ShaderOptionDescriptor::DecodeBits(ShaderVariantKey shaderVariantKey) const
  367. {
  368. shaderVariantKey >>= m_bitOffset;
  369. shaderVariantKey &= AZ_BIT_MASK(m_bitCount);
  370. uint32_t value = static_cast<uint32_t>(shaderVariantKey.to_ulong());
  371. return value;
  372. }
  373. bool ShaderOptionDescriptor::operator==(const ShaderOptionDescriptor& rhs) const
  374. {
  375. return m_hash == rhs.m_hash;
  376. }
  377. bool ShaderOptionDescriptor::operator!=(const ShaderOptionDescriptor& rhs) const
  378. {
  379. return m_hash != rhs.m_hash;
  380. }
  381. bool ShaderOptionDescriptor::CompareOrder(const ShaderOptionDescriptor& first, const ShaderOptionDescriptor& second)
  382. {
  383. return first.GetOrder() < second.GetOrder();
  384. }
  385. bool ShaderOptionDescriptor::SameOrder(const ShaderOptionDescriptor& first, const ShaderOptionDescriptor& second)
  386. {
  387. return first.GetOrder() == second.GetOrder();
  388. }
  389. void ShaderOptionGroupLayout::Reflect(AZ::ReflectContext* context)
  390. {
  391. ShaderOptionIndex::Reflect(context);
  392. NameReflectionMapForOptions::Reflect(context);
  393. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  394. {
  395. serializeContext->Class<ShaderOptionGroupLayout>()
  396. ->Version(3)
  397. ->Field("m_bitMask", &ShaderOptionGroupLayout::m_bitMask)
  398. ->Field("m_options", &ShaderOptionGroupLayout::m_options)
  399. ->Field("m_nameReflectionForOptions", &ShaderOptionGroupLayout::m_nameReflectionForOptions)
  400. ->Field("m_hash", &ShaderOptionGroupLayout::m_hash)
  401. ->Field("m_isFullySpecialized", &ShaderOptionGroupLayout::m_isFullySpecialized)
  402. ->Field("m_useSpecializationConstants", &ShaderOptionGroupLayout::m_useSpecializationConstants)
  403. ;
  404. }
  405. if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
  406. {
  407. behaviorContext->Class<ShaderOptionGroupLayout>()
  408. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  409. ->Attribute(AZ::Script::Attributes::Category, "Shader")
  410. ->Attribute(AZ::Script::Attributes::Module, "shader")
  411. ->Method("GetShaderOptions", &ShaderOptionGroupLayout::GetShaderOptions)
  412. ;
  413. }
  414. }
  415. Ptr<ShaderOptionGroupLayout> ShaderOptionGroupLayout::Create()
  416. {
  417. return aznew ShaderOptionGroupLayout;
  418. }
  419. bool ShaderOptionGroupLayout::IsFinalized() const
  420. {
  421. return m_hash != HashValue64{ 0 };
  422. }
  423. HashValue64 ShaderOptionGroupLayout::GetHash() const
  424. {
  425. return m_hash;
  426. }
  427. bool ShaderOptionGroupLayout::IsFullySpecialized() const
  428. {
  429. return m_isFullySpecialized;
  430. }
  431. bool ShaderOptionGroupLayout::UseSpecializationConstants() const
  432. {
  433. return m_useSpecializationConstants;
  434. }
  435. void ShaderOptionGroupLayout::Clear()
  436. {
  437. m_options.clear();
  438. m_nameReflectionForOptions.Clear();
  439. m_bitMask = {};
  440. m_hash = HashValue64{ 0 };
  441. }
  442. void ShaderOptionGroupLayout::Finalize()
  443. {
  444. AZStd::sort(m_options.begin(), m_options.end(), ShaderOptionDescriptor::CompareOrder);
  445. // Start with a hash of the size so that m_hash!=0 will mean the group is finalized, even if the options list is empty.
  446. HashValue64 hash = TypeHash64(m_options.size());
  447. for (const ShaderOptionDescriptor& option : m_options)
  448. {
  449. hash = TypeHash64(option.GetHash(), hash);
  450. }
  451. m_hash = hash;
  452. m_isFullySpecialized = !AZStd::any_of(
  453. m_options.begin(),
  454. m_options.end(),
  455. [](const ShaderOptionDescriptor& elem)
  456. {
  457. return elem.GetSpecializationId() < 0;
  458. });
  459. m_useSpecializationConstants = AZStd::any_of(
  460. m_options.begin(),
  461. m_options.end(),
  462. [](const ShaderOptionDescriptor& elem)
  463. {
  464. return elem.GetSpecializationId() >= 0;
  465. });
  466. }
  467. bool ShaderOptionGroupLayout::ValidateIsFinalized() const
  468. {
  469. if (!IsFinalized())
  470. {
  471. AZ_Assert(false, "ShaderOptionGroupLayout is not finalized! This operation is only permitted on a finalized layout.");
  472. return false;
  473. }
  474. return true;
  475. }
  476. bool ShaderOptionGroupLayout::ValidateIsNotFinalized() const
  477. {
  478. if (IsFinalized())
  479. {
  480. AZ_Assert(false, "ShaderOptionGroupLayout is finalized! This operation is only permitted on a non-finalized layout.");
  481. return false;
  482. }
  483. return true;
  484. }
  485. bool ShaderOptionGroupLayout::AddShaderOption(const ShaderOptionDescriptor& option)
  486. {
  487. if (!ValidateIsNotFinalized())
  488. {
  489. return false;
  490. }
  491. const Name& optionName = option.GetName();
  492. const ShaderVariantKey bitMask = option.GetBitMask();
  493. if ((m_bitMask & bitMask).any())
  494. {
  495. AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s': mask overlaps with previously added masks.", optionName.GetCStr());
  496. return false;
  497. }
  498. if (option.GetName().IsEmpty())
  499. {
  500. AZ_Error(DebugCategory, false, "ShaderOptionBinding added with empty name.");
  501. return false;
  502. }
  503. if (option.GetBitCount() == 0)
  504. {
  505. AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s' has zero bits.", optionName.GetCStr());
  506. return false;
  507. }
  508. if (option.GetBitOffset() + option.GetBitCount() > bitMask.size())
  509. {
  510. AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s' exceeds size of mask.", optionName.GetCStr());
  511. return false;
  512. }
  513. if (AZStd::any_of(m_options.begin(), m_options.end(),
  514. [&option](const ShaderOptionDescriptor& other) { return ShaderOptionDescriptor::SameOrder(option, other); }))
  515. {
  516. AZ_Error(DebugCategory, false, "ShaderOption '%s' has the same order (%d) as another shader option.", optionName.GetCStr(), option.GetOrder());
  517. return false;
  518. }
  519. if (!option.FindValue(option.GetDefaultValue()).IsValid())
  520. {
  521. AZ_Error(DebugCategory, false, "ShaderOption '%s' has invalid default value '%s'.", optionName.GetCStr(), option.GetDefaultValue().GetCStr());
  522. return false;
  523. }
  524. const ShaderOptionIndex optionIndex(m_options.size());
  525. if (!m_nameReflectionForOptions.Insert(optionName, optionIndex))
  526. {
  527. AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s': name already exists.", optionName.GetCStr());
  528. return false;
  529. }
  530. m_bitMask |= bitMask;
  531. m_options.push_back(option);
  532. return true;
  533. }
  534. ShaderOptionIndex ShaderOptionGroupLayout::FindShaderOptionIndex(const Name& optionName) const
  535. {
  536. if (ValidateIsFinalized())
  537. {
  538. return m_nameReflectionForOptions.Find(optionName);
  539. }
  540. return {};
  541. }
  542. ShaderOptionValue ShaderOptionGroupLayout::FindValue(const Name& optionName, const Name& valueName) const
  543. {
  544. return FindValue(FindShaderOptionIndex(optionName), valueName);
  545. }
  546. ShaderOptionValue ShaderOptionGroupLayout::FindValue(const ShaderOptionIndex& optionIndex, const Name& valueName) const
  547. {
  548. if (optionIndex.IsValid() && optionIndex.GetIndex() < m_options.size())
  549. {
  550. return m_options[optionIndex.GetIndex()].FindValue(valueName);
  551. }
  552. else
  553. {
  554. return ShaderOptionValue{};
  555. }
  556. }
  557. uint32_t ShaderOptionGroupLayout::GetBitSize() const
  558. {
  559. if (m_options.empty())
  560. {
  561. return 0;
  562. }
  563. else
  564. {
  565. return m_options.back().GetBitOffset() + m_options.back().GetBitCount();
  566. }
  567. }
  568. const AZStd::vector<ShaderOptionDescriptor>& ShaderOptionGroupLayout::GetShaderOptions() const
  569. {
  570. return m_options;
  571. }
  572. const ShaderOptionDescriptor& ShaderOptionGroupLayout::GetShaderOption(ShaderOptionIndex optionIndex) const
  573. {
  574. return m_options[optionIndex.GetIndex()];
  575. }
  576. size_t ShaderOptionGroupLayout::GetShaderOptionCount() const
  577. {
  578. return m_options.size();
  579. }
  580. ShaderVariantKey ShaderOptionGroupLayout::GetBitMask() const
  581. {
  582. return m_bitMask;
  583. }
  584. bool ShaderOptionGroupLayout::IsValidShaderVariantKey(const ShaderVariantKey& shaderVariantKey) const
  585. {
  586. if (ValidateIsFinalized())
  587. {
  588. return (m_bitMask & shaderVariantKey) == shaderVariantKey;
  589. }
  590. return false;
  591. }
  592. } // namespace RPI
  593. } // namespace AZ