123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <Atom/RPI.Reflect/Shader/ShaderOptionGroupLayout.h>
- #include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
- #include <Atom/RHI.Reflect/Bits.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/std/string/conversions.h>
- #include <AzCore/std/sort.h>
- #include <AzCore/Utils/TypeHash.h>
- #include <AzFramework/StringFunc/StringFunc.h>
- namespace AZ
- {
- namespace RPI
- {
- const char* ShaderOptionDescriptor::DebugCategory = "ShaderOption";
- const char* ShaderOptionGroupLayout::DebugCategory = "ShaderOption";
- const char* ToString(ShaderOptionType shaderOptionType)
- {
- switch (shaderOptionType)
- {
- case ShaderOptionType::Boolean: return "Boolean";
- case ShaderOptionType::Enumeration: return "Enumeration";
- case ShaderOptionType::IntegerRange: return "IntegerRange";
- default: return "<Unknown>";
- }
- }
-
- ShaderOptionValues CreateEnumShaderOptionValues(AZStd::span<const AZStd::string_view> enumNames)
- {
- ShaderOptionValues values;
- values.reserve(enumNames.size());
- for (size_t i = 0; i < enumNames.size(); ++i)
- {
- values.emplace_back(Name{enumNames[i]}, i);
- }
- return values;
- }
-
- ShaderOptionValues CreateEnumShaderOptionValues(AZStd::initializer_list<AZStd::string_view> enumNames)
- {
- return CreateEnumShaderOptionValues(AZStd::span(enumNames.begin(), enumNames.end()));
- }
- ShaderOptionValues CreateBoolShaderOptionValues()
- {
- return CreateEnumShaderOptionValues({"False", "True"});
- }
-
- ShaderOptionValues CreateIntRangeShaderOptionValues(uint32_t min, uint32_t max)
- {
- AZStd::vector<RPI::ShaderOptionValuePair> intOptionRange;
- intOptionRange.push_back({Name{AZStd::string::format("%u", min)}, RPI::ShaderOptionValue{min}});
- intOptionRange.push_back({Name{AZStd::string::format("%u", max)}, RPI::ShaderOptionValue{max}});
- return intOptionRange;
- }
- void ShaderOptionGroupHints::Reflect(ReflectContext* context)
- {
- if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
- {
- serializeContext->Class<ShaderOptionGroupHints>()
- ->Version(4)
- ->Field("BakePrecedingVariants", &ShaderOptionGroupHints::m_bakePrecedingVariants)
- ->Field("BakeEmptyAsDefault", &ShaderOptionGroupHints::m_bakeEmptyAsDefault)
- ;
- }
- }
- void ShaderOptionDescriptor::Reflect(AZ::ReflectContext* context)
- {
- ShaderOptionValue::Reflect(context);
- NameReflectionMapForValues::Reflect(context);
- if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
- {
- serializeContext->Class<ShaderOptionDescriptor>()
- ->Version(4)
- ->Field("m_name", &ShaderOptionDescriptor::m_name)
- ->Field("m_type", &ShaderOptionDescriptor::m_type)
- ->Field("m_defaultValue", &ShaderOptionDescriptor::m_defaultValue)
- ->Field("m_minValue", &ShaderOptionDescriptor::m_minValue)
- ->Field("m_maxValue", &ShaderOptionDescriptor::m_maxValue)
- ->Field("m_bitOffset", &ShaderOptionDescriptor::m_bitOffset)
- ->Field("m_bitCount", &ShaderOptionDescriptor::m_bitCount)
- ->Field("m_bitMask", &ShaderOptionDescriptor::m_bitMask)
- ->Field("m_bitMaskNot", &ShaderOptionDescriptor::m_bitMaskNot)
- ->Field("m_hash", &ShaderOptionDescriptor::m_hash)
- ->Field("m_nameReflectionForValues", &ShaderOptionDescriptor::m_nameReflectionForValues)
- ;
- }
- if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
- {
- behaviorContext->Class<ShaderOptionDescriptor>()
- ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Shader")
- ->Attribute(AZ::Script::Attributes::Module, "shader")
- ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
- ->Method("GetName", &ShaderOptionDescriptor::GetName)
- ->Method("GetDefaultValue", &ShaderOptionDescriptor::GetDefaultValue)
- ->Method("GetValueName", &ShaderOptionDescriptor::GetValueName)
- ->Method("FindValue", &ShaderOptionDescriptor::FindValue)
- ;
- }
- }
- ShaderOptionDescriptor::ShaderOptionDescriptor(const Name& name,
- const ShaderOptionType& optionType,
- uint32_t bitOffset,
- uint32_t order,
- const ShaderOptionValues& nameIndexList,
- const Name& defaultValue)
- : m_name{name}
- , m_type{optionType}
- , m_bitOffset{bitOffset}
- , m_order{order}
- , m_defaultValue{defaultValue}
- {
- for (auto pair : nameIndexList)
- { // Registers the pair in the lookup table
- AddValue(pair.first, pair.second);
- if (m_defaultValue.IsEmpty())
- {
- m_defaultValue = pair.first;
- }
- }
- uint32_t numValues = (m_type == ShaderOptionType::IntegerRange) ? (m_maxValue.GetIndex() - m_minValue.GetIndex() + 1) : (uint32_t) nameIndexList.size();
- numValues = RHI::NextPowerOfTwo(numValues) - 1;
- m_bitCount = RHI::CountBitsSet(numValues);
- ShaderVariantKey bitMask;
- bitMask = AZ_BIT_MASK(m_bitCount);
- bitMask <<= m_bitOffset;
- m_bitMask = bitMask;
- m_bitMaskNot = ~bitMask;
- m_hash = TypeHash64(m_bitMask, static_cast<HashValue64>(m_name.GetHash()));
- }
- const Name& ShaderOptionDescriptor::GetName() const
- {
- return m_name;
- }
- uint32_t ShaderOptionDescriptor::GetBitOffset() const
- {
- return m_bitOffset;
- }
- uint32_t ShaderOptionDescriptor::GetBitCount() const
- {
- return m_bitCount;
- }
- uint32_t ShaderOptionDescriptor::GetOrder() const
- {
- return m_order;
- }
- ShaderVariantKey ShaderOptionDescriptor::GetBitMask() const
- {
- return m_bitMask;
- }
- ShaderVariantKey ShaderOptionDescriptor::GetBitMaskNot() const
- {
- return m_bitMaskNot;
- }
- HashValue64 ShaderOptionDescriptor::GetHash() const
- {
- return m_hash;
- }
- bool ShaderOptionDescriptor::Set(ShaderOptionGroup& group, const Name& valueName) const
- {
- auto valueIndex = FindValue(valueName);
- if (valueIndex.IsValid())
- {
- return Set(group, valueIndex);
- }
- else
- {
- AZ_Error(DebugCategory, false, "ShaderOption value '%s' does not exist", valueName.GetCStr());
- return false;
- }
- }
- bool ShaderOptionDescriptor::Set(ShaderOptionGroup& group, const ShaderOptionValue valueIndex) const
- {
- if (valueIndex.IsNull())
- {
- AZ_Error(DebugCategory, false, "Invalid ShaderOption value");
- return false;
- }
-
- if (m_type == ShaderOptionType::Unknown)
- {
- group.GetShaderVariantMask() &= m_bitMaskNot;
- }
- else
- {
- if (!(m_minValue.GetIndex() <= valueIndex.GetIndex() && valueIndex.GetIndex() <= m_maxValue.GetIndex()))
- {
- AZ_Error(DebugCategory, false, "%s ShaderOption value [%d] is out of range [%d,%d].",
- ToString(m_type), valueIndex.GetIndex(), m_minValue.GetIndex(), m_maxValue.GetIndex());
- return false;
- }
- EncodeBits(group.GetShaderVariantKey(), valueIndex.GetIndex() - m_minValue.GetIndex());
- group.GetShaderVariantMask() |= m_bitMask;
- }
- return true;
- }
- bool ShaderOptionDescriptor::Set(ShaderVariantKey& key, const ShaderOptionValue valueIndex) const
- {
- if (valueIndex.IsNull())
- {
- AZ_Error(DebugCategory, false, "Invalid ShaderOption value");
- return false;
- }
-
- if (m_type != ShaderOptionType::Unknown)
- {
- if (!(m_minValue.GetIndex() <= valueIndex.GetIndex() && valueIndex.GetIndex() <= m_maxValue.GetIndex()))
- {
- AZ_Error(DebugCategory, false, "%s ShaderOption value [%d] is out of range [%d,%d].",
- ToString(m_type), valueIndex.GetIndex(), m_minValue.GetIndex(), m_maxValue.GetIndex());
- return false;
- }
- EncodeBits(key, valueIndex.GetIndex() - m_minValue.GetIndex());
- }
- return true;
-
- }
- ShaderOptionValue ShaderOptionDescriptor::Get(const ShaderOptionGroup& group) const
- {
- if (group.GetShaderVariantMask().test(m_bitOffset))
- {
- return ShaderOptionValue(DecodeBits(group.GetShaderVariantKey()) + m_minValue.GetIndex());
- }
- return ShaderOptionValue();
- }
- void ShaderOptionDescriptor::Clear(ShaderOptionGroup& group) const
- {
- group.GetShaderVariantMask() &= m_bitMaskNot;
- }
- void ShaderOptionDescriptor::AddValue(const Name& valueName, const ShaderOptionValue valueIndex)
- {
- 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!");
- m_nameReflectionForValues.Insert(valueName, valueIndex);
- if (m_minValue.IsNull() || m_minValue.GetIndex() > valueIndex.GetIndex())
- {
- m_minValue = valueIndex;
- }
- if (m_maxValue.IsNull() || m_maxValue.GetIndex() < valueIndex.GetIndex())
- {
- m_maxValue = valueIndex;
- }
- }
- void ShaderOptionDescriptor::SetDefaultValue(const Name& valueName)
- {
- AZ_Assert(!valueName.IsEmpty(), "The default value cannot be empty!");
- auto valueIter = m_nameReflectionForValues.Find(valueName);
- if (valueIter.IsNull())
- {
- AZ_Assert(false, "ShaderOption [%s] has no member value [%s] so this cannot be the default!", m_name.GetCStr(), valueName.GetCStr());
- return;
- }
- m_defaultValue = valueName;
- }
- const Name& ShaderOptionDescriptor::GetDefaultValue() const
- {
- return m_defaultValue;
- }
- uint32_t ShaderOptionDescriptor::GetValuesCount() const
- {
- return static_cast<uint32_t>(m_maxValue.GetIndex() - m_minValue.GetIndex() + 1);
- }
- /// Sets the hint type for the shader option
- void ShaderOptionDescriptor::SetType(ShaderOptionType optionType)
- {
- m_type = optionType;
- }
- /// Gets the hint type for the shader option
- const ShaderOptionType& ShaderOptionDescriptor::GetType() const
- {
- return m_type;
- }
- ShaderOptionValue ShaderOptionDescriptor::GetMinValue() const
- {
- return m_minValue;
- }
- ShaderOptionValue ShaderOptionDescriptor::GetMaxValue() const
- {
- return m_maxValue;
- }
- ShaderOptionValue ShaderOptionDescriptor::FindValue(const Name& valueName) const
- {
- switch (m_type)
- {
- case ShaderOptionType::Boolean:
- // This is better than hardcoding True, or On, or Enabled:
- return m_nameReflectionForValues.Find(valueName);
- case ShaderOptionType::Enumeration:
- return m_nameReflectionForValues.Find(valueName);
- case ShaderOptionType::IntegerRange:
- {
- int asInt;
- if (AzFramework::StringFunc::LooksLikeInt(valueName.GetCStr(), &asInt))
- {
- if (aznumeric_cast<int64_t>(m_minValue.GetIndex()) <= asInt && asInt <= aznumeric_cast<int64_t>(m_maxValue.GetIndex()))
- {
- return ShaderOptionValue(asInt);
- }
- }
- }
- return ShaderOptionValue();
- default:
- AZ_Assert(false, "Unhandled case for ShaderOptionType! We should not break here!");
- }
- // Unreachable code
- return ShaderOptionValue();
- }
- Name ShaderOptionDescriptor::GetValueName(ShaderOptionValue value) const
- {
- auto name = m_nameReflectionForValues.Find(value);
- return name;
- }
- void ShaderOptionDescriptor::EncodeBits(ShaderVariantKey& shaderVariantKey, uint32_t value) const
- {
- if (value < AZ_BIT(m_bitCount))
- {
- ShaderVariantKey valueBits = (value & AZ_BIT_MASK(m_bitCount));
- valueBits <<= m_bitOffset;
- shaderVariantKey &= m_bitMaskNot;
- shaderVariantKey |= valueBits;
- }
- else
- {
- AZ_Assert(false, "Exceeded maximum number of bits allocated for option.");
- }
- }
- uint32_t ShaderOptionDescriptor::DecodeBits(ShaderVariantKey shaderVariantKey) const
- {
- shaderVariantKey >>= m_bitOffset;
- shaderVariantKey &= AZ_BIT_MASK(m_bitCount);
- uint32_t value = static_cast<uint32_t>(shaderVariantKey.to_ulong());
- return value;
- }
-
- bool ShaderOptionDescriptor::operator==(const ShaderOptionDescriptor& rhs) const
- {
- return m_hash == rhs.m_hash;
- }
- bool ShaderOptionDescriptor::operator!=(const ShaderOptionDescriptor& rhs) const
- {
- return m_hash != rhs.m_hash;
- }
- bool ShaderOptionDescriptor::CompareOrder(const ShaderOptionDescriptor& first, const ShaderOptionDescriptor& second)
- {
- return first.GetOrder() < second.GetOrder();
- }
- bool ShaderOptionDescriptor::SameOrder(const ShaderOptionDescriptor& first, const ShaderOptionDescriptor& second)
- {
- return first.GetOrder() == second.GetOrder();
- }
- void ShaderOptionGroupLayout::Reflect(AZ::ReflectContext* context)
- {
- ShaderOptionIndex::Reflect(context);
- NameReflectionMapForOptions::Reflect(context);
- if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
- {
- serializeContext->Class<ShaderOptionGroupLayout>()
- ->Version(2)
- ->Field("m_bitMask", &ShaderOptionGroupLayout::m_bitMask)
- ->Field("m_options", &ShaderOptionGroupLayout::m_options)
- ->Field("m_nameReflectionForOptions", &ShaderOptionGroupLayout::m_nameReflectionForOptions)
- ->Field("m_hash", &ShaderOptionGroupLayout::m_hash)
- ;
- }
- if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
- {
- behaviorContext->Class<ShaderOptionGroupLayout>()
- ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Shader")
- ->Attribute(AZ::Script::Attributes::Module, "shader")
- ->Method("GetShaderOptions", &ShaderOptionGroupLayout::GetShaderOptions)
- ;
- }
- }
- Ptr<ShaderOptionGroupLayout> ShaderOptionGroupLayout::Create()
- {
- return aznew ShaderOptionGroupLayout;
- }
- bool ShaderOptionGroupLayout::IsFinalized() const
- {
- return m_hash != HashValue64{ 0 };
- }
- HashValue64 ShaderOptionGroupLayout::GetHash() const
- {
- return m_hash;
- }
- void ShaderOptionGroupLayout::Clear()
- {
- m_options.clear();
- m_nameReflectionForOptions.Clear();
- m_bitMask = {};
- m_hash = HashValue64{ 0 };
- }
- void ShaderOptionGroupLayout::Finalize()
- {
- AZStd::sort(m_options.begin(), m_options.end(), ShaderOptionDescriptor::CompareOrder);
- // 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.
- HashValue64 hash = TypeHash64(m_options.size());
- for (const ShaderOptionDescriptor& option : m_options)
- {
- hash = TypeHash64(option.GetHash(), hash);
- }
- m_hash = hash;
- }
- bool ShaderOptionGroupLayout::ValidateIsFinalized() const
- {
- if (!IsFinalized())
- {
- AZ_Assert(false, "ShaderOptionGroupLayout is not finalized! This operation is only permitted on a finalized layout.");
- return false;
- }
- return true;
- }
- bool ShaderOptionGroupLayout::ValidateIsNotFinalized() const
- {
- if (IsFinalized())
- {
- AZ_Assert(false, "ShaderOptionGroupLayout is finalized! This operation is only permitted on a non-finalized layout.");
- return false;
- }
- return true;
- }
- bool ShaderOptionGroupLayout::AddShaderOption(const ShaderOptionDescriptor& option)
- {
- if (!ValidateIsNotFinalized())
- {
- return false;
- }
- const Name& optionName = option.GetName();
- const ShaderVariantKey bitMask = option.GetBitMask();
- if ((m_bitMask & bitMask).any())
- {
- AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s': mask overlaps with previously added masks.", optionName.GetCStr());
- return false;
- }
- if (option.GetName().IsEmpty())
- {
- AZ_Error(DebugCategory, false, "ShaderOptionBinding added with empty name.");
- return false;
- }
- if (option.GetBitCount() == 0)
- {
- AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s' has zero bits.", optionName.GetCStr());
- return false;
- }
- if (option.GetBitOffset() + option.GetBitCount() > bitMask.size())
- {
- AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s' exceeds size of mask.", optionName.GetCStr());
- return false;
- }
- if (AZStd::any_of(m_options.begin(), m_options.end(),
- [&option](const ShaderOptionDescriptor& other) { return ShaderOptionDescriptor::SameOrder(option, other); }))
- {
- AZ_Error(DebugCategory, false, "ShaderOption '%s' has the same order (%d) as another shader option.", optionName.GetCStr(), option.GetOrder());
- return false;
- }
- if (!option.FindValue(option.GetDefaultValue()).IsValid())
- {
- AZ_Error(DebugCategory, false, "ShaderOption '%s' has invalid default value '%s'.", optionName.GetCStr(), option.GetDefaultValue().GetCStr());
- return false;
- }
- const ShaderOptionIndex optionIndex(m_options.size());
- if (!m_nameReflectionForOptions.Insert(optionName, optionIndex))
- {
- AZ_Error(DebugCategory, false, "ShaderOptionBinding '%s': name already exists.", optionName.GetCStr());
- return false;
- }
- m_bitMask |= bitMask;
- m_options.push_back(option);
- return true;
- }
- ShaderOptionIndex ShaderOptionGroupLayout::FindShaderOptionIndex(const Name& optionName) const
- {
- if (ValidateIsFinalized())
- {
- return m_nameReflectionForOptions.Find(optionName);
- }
- return {};
- }
- ShaderOptionValue ShaderOptionGroupLayout::FindValue(const Name& optionName, const Name& valueName) const
- {
- return FindValue(FindShaderOptionIndex(optionName), valueName);
- }
- ShaderOptionValue ShaderOptionGroupLayout::FindValue(const ShaderOptionIndex& optionIndex, const Name& valueName) const
- {
- if (optionIndex.IsValid() && optionIndex.GetIndex() < m_options.size())
- {
- return m_options[optionIndex.GetIndex()].FindValue(valueName);
- }
- else
- {
- return ShaderOptionValue{};
- }
- }
- uint32_t ShaderOptionGroupLayout::GetBitSize() const
- {
- if (m_options.empty())
- {
- return 0;
- }
- else
- {
- return m_options.back().GetBitOffset() + m_options.back().GetBitCount();
- }
- }
- const AZStd::vector<ShaderOptionDescriptor>& ShaderOptionGroupLayout::GetShaderOptions() const
- {
- return m_options;
- }
- const ShaderOptionDescriptor& ShaderOptionGroupLayout::GetShaderOption(ShaderOptionIndex optionIndex) const
- {
- return m_options[optionIndex.GetIndex()];
- }
- size_t ShaderOptionGroupLayout::GetShaderOptionCount() const
- {
- return m_options.size();
- }
- ShaderVariantKey ShaderOptionGroupLayout::GetBitMask() const
- {
- return m_bitMask;
- }
- bool ShaderOptionGroupLayout::IsValidShaderVariantKey(const ShaderVariantKey& shaderVariantKey) const
- {
- if (ValidateIsFinalized())
- {
- return (m_bitMask & shaderVariantKey) == shaderVariantKey;
- }
- return false;
- }
- } // namespace RPI
- } // namespace AZ
|