3
0

ShaderManagementConsoleDocument.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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 <AssetDatabase/AssetDatabaseConnection.h>
  9. #include <Atom/RPI.Edit/Common/AssetUtils.h>
  10. #include <Atom/RPI.Edit/Common/JsonUtils.h>
  11. #include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
  12. #include <Atom/RPI.Edit/Shader/ShaderOptionValuesSourceData.h>
  13. #include <Atom/RPI.Public/Material/Material.h>
  14. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  15. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  16. #include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. #include <AzCore/Serialization/EditContext.h>
  19. #include <AzCore/Serialization/SerializeContext.h>
  20. #include <AzCore/Utils/Utils.h>
  21. #include <AzFramework/StringFunc/StringFunc.h>
  22. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  23. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  24. #include <Document/ShaderManagementConsoleDocument.h>
  25. namespace ShaderManagementConsole
  26. {
  27. void ShaderManagementConsoleDocument::Reflect(AZ::ReflectContext* context)
  28. {
  29. if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
  30. {
  31. serialize->Class<ShaderManagementConsoleDocument, AtomToolsFramework::AtomToolsDocument>()
  32. ->Version(0);
  33. }
  34. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  35. {
  36. behaviorContext->EBus<ShaderManagementConsoleDocumentRequestBus>("ShaderManagementConsoleDocumentRequestBus")
  37. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  38. ->Attribute(AZ::Script::Attributes::Category, "Editor")
  39. ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole")
  40. ->Event("SetShaderVariantListSourceData", &ShaderManagementConsoleDocumentRequestBus::Events::SetShaderVariantListSourceData)
  41. ->Event("GetShaderVariantListSourceData", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantListSourceData)
  42. ->Event("GetShaderOptionDescriptorCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptorCount)
  43. ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor)
  44. ;
  45. }
  46. }
  47. ShaderManagementConsoleDocument::ShaderManagementConsoleDocument(
  48. const AZ::Crc32& toolId, const AtomToolsFramework::DocumentTypeInfo& documentTypeInfo)
  49. : AtomToolsFramework::AtomToolsDocument(toolId, documentTypeInfo)
  50. {
  51. ShaderManagementConsoleDocumentRequestBus::Handler::BusConnect(m_id);
  52. }
  53. ShaderManagementConsoleDocument::~ShaderManagementConsoleDocument()
  54. {
  55. ShaderManagementConsoleDocumentRequestBus::Handler::BusDisconnect();
  56. }
  57. void ShaderManagementConsoleDocument::SetShaderVariantListSourceData(
  58. const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData)
  59. {
  60. m_shaderVariantListSourceData = shaderVariantListSourceData;
  61. AZStd::string shaderPath = m_shaderVariantListSourceData.m_shaderFilePath;
  62. auto shaderAssetResult = AZ::RPI::AssetUtils::LoadAsset<AZ::RPI::ShaderAsset>(m_absolutePath, shaderPath);
  63. if (shaderAssetResult)
  64. {
  65. m_shaderAsset = shaderAssetResult.GetValue();
  66. // No material is using this shader, check for system option settings
  67. if (m_shaderVariantListSourceData.m_shaderVariants.empty())
  68. {
  69. // Read system option file
  70. AZ::IO::Path fullPath = AZ::IO::Path(AZ::RPI::AssetUtils::ResolvePathReference(m_absolutePath, shaderPath));
  71. fullPath.ReplaceExtension("systemoptions");
  72. AZ::RPI::ShaderOptionValuesSourceData systemOptionSetting;
  73. if (!AZ::RPI::JsonUtils::LoadObjectFromFile(fullPath.String(), systemOptionSetting))
  74. {
  75. AZ_Warning("ShaderManagementConsoleDocument", false, "System option setting not found : '%s.'", fullPath.c_str());
  76. return;
  77. }
  78. if (systemOptionSetting.size() > 0)
  79. {
  80. AZ::u32 stableId = 1;
  81. AZStd::vector<AZ::RPI::ShaderOptionDescriptor> unsetOption;
  82. const auto& shaderOptionDescriptors = m_shaderAsset->GetShaderOptionGroupLayout()->GetShaderOptions();
  83. // Check user input with descriptor from shader asset
  84. for (auto& shaderOptionDescriptor : shaderOptionDescriptors)
  85. {
  86. AZ::Name optionName = shaderOptionDescriptor.GetName();
  87. const auto optionIt = systemOptionSetting.find(optionName);
  88. if (optionIt != systemOptionSetting.end())
  89. {
  90. AZ::Name valueName = AZ::Name(optionIt->second);
  91. if (strcmp(valueName.GetCStr(), "") == 0)
  92. {
  93. // Option with unset value, expend later
  94. unsetOption.push_back(shaderOptionDescriptor);
  95. systemOptionSetting[optionName] = shaderOptionDescriptor.GetDefaultValue();
  96. }
  97. }
  98. }
  99. // Get total number of variants
  100. size_t totalVariantSize = 1;
  101. for (auto& shaderOptionDescriptor : unsetOption)
  102. {
  103. uint32_t minValue = shaderOptionDescriptor.GetMinValue().GetIndex();
  104. uint32_t maxValue = shaderOptionDescriptor.GetMaxValue().GetIndex();
  105. totalVariantSize = totalVariantSize * (maxValue - minValue + 1);
  106. }
  107. m_shaderVariantListSourceData.m_shaderVariants.reserve(totalVariantSize);
  108. m_shaderVariantListSourceData.m_shaderVariants.emplace_back(stableId, systemOptionSetting);
  109. stableId++;
  110. // Expand unset option
  111. for (auto& shaderOptionDescriptor : unsetOption)
  112. {
  113. AZStd::vector<AZ::RPI::ShaderVariantListSourceData::VariantInfo> shaderVariants;
  114. AZStd::vector<AZ::RPI::ShaderVariantListSourceData::VariantInfo> expandShaderVariants;
  115. uint32_t minValue = shaderOptionDescriptor.GetMinValue().GetIndex();
  116. uint32_t maxValue = shaderOptionDescriptor.GetMaxValue().GetIndex();
  117. size_t listSize = m_shaderVariantListSourceData.m_shaderVariants.size();
  118. size_t expandSize = listSize * (maxValue - minValue);
  119. shaderVariants.reserve(listSize);
  120. expandShaderVariants.reserve(expandSize);
  121. for (uint32_t index = minValue; index <= maxValue; ++index)
  122. {
  123. AZ::Name optionValue = shaderOptionDescriptor.GetValueName(index);
  124. if (optionValue != shaderOptionDescriptor.GetDefaultValue())
  125. {
  126. stableId = UpdateOptionValue(
  127. m_shaderVariantListSourceData.m_shaderVariants,
  128. shaderVariants,
  129. shaderOptionDescriptor.GetName(),
  130. optionValue,
  131. stableId);
  132. expandShaderVariants.insert(
  133. expandShaderVariants.end(),
  134. AZStd::make_move_iterator(shaderVariants.begin()),
  135. AZStd::make_move_iterator(shaderVariants.end()));
  136. }
  137. }
  138. m_shaderVariantListSourceData.m_shaderVariants.insert(
  139. m_shaderVariantListSourceData.m_shaderVariants.end(),
  140. AZStd::make_move_iterator(expandShaderVariants.begin()),
  141. AZStd::make_move_iterator(expandShaderVariants.end()));
  142. }
  143. }
  144. }
  145. AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
  146. m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoInvalidated, m_id);
  147. AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
  148. m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
  149. m_modified = true;
  150. }
  151. else
  152. {
  153. AZ_Error("ShaderManagementConsoleDocument", false, "Could not load shader asset: %s.", shaderPath.c_str());
  154. }
  155. }
  156. const AZ::RPI::ShaderVariantListSourceData& ShaderManagementConsoleDocument::GetShaderVariantListSourceData() const
  157. {
  158. return m_shaderVariantListSourceData;
  159. }
  160. size_t ShaderManagementConsoleDocument::GetShaderOptionDescriptorCount() const
  161. {
  162. if (m_shaderAsset.IsReady())
  163. {
  164. const auto& layout = m_shaderAsset->GetShaderOptionGroupLayout();
  165. const auto& shaderOptionDescriptors = layout->GetShaderOptions();
  166. return shaderOptionDescriptors.size();
  167. }
  168. return 0;
  169. }
  170. const AZ::RPI::ShaderOptionDescriptor& ShaderManagementConsoleDocument::GetShaderOptionDescriptor(size_t index) const
  171. {
  172. if (m_shaderAsset.IsReady())
  173. {
  174. const auto& layout = m_shaderAsset->GetShaderOptionGroupLayout();
  175. const auto& shaderOptionDescriptors = layout->GetShaderOptions();
  176. return shaderOptionDescriptors.at(index);
  177. }
  178. return m_invalidDescriptor;
  179. }
  180. AtomToolsFramework::DocumentTypeInfo ShaderManagementConsoleDocument::BuildDocumentTypeInfo()
  181. {
  182. AtomToolsFramework::DocumentTypeInfo documentType;
  183. documentType.m_documentTypeName = "Shader Variant List";
  184. documentType.m_documentFactoryCallback = [](const AZ::Crc32& toolId, const AtomToolsFramework::DocumentTypeInfo& documentTypeInfo) {
  185. return aznew ShaderManagementConsoleDocument(toolId, documentTypeInfo); };
  186. documentType.m_supportedExtensionsToOpen.push_back({ "Shader Variant List", AZ::RPI::ShaderVariantListSourceData::Extension });
  187. documentType.m_supportedExtensionsToSave.push_back({ "Shader Variant List", AZ::RPI::ShaderVariantListSourceData::Extension });
  188. return documentType;
  189. }
  190. AtomToolsFramework::DocumentObjectInfoVector ShaderManagementConsoleDocument::GetObjectInfo() const
  191. {
  192. AtomToolsFramework::DocumentObjectInfoVector objects = AtomToolsDocument::GetObjectInfo();
  193. AtomToolsFramework::DocumentObjectInfo objectInfo;
  194. objectInfo.m_visible = true;
  195. objectInfo.m_name = "Shader Variant List";
  196. objectInfo.m_displayName = "Shader Variant List";
  197. objectInfo.m_description = "Shader Variant List";
  198. objectInfo.m_objectType = azrtti_typeid<AZ::RPI::ShaderVariantListSourceData>();
  199. objectInfo.m_objectPtr = const_cast<AZ::RPI::ShaderVariantListSourceData*>(&m_shaderVariantListSourceData);
  200. objects.push_back(AZStd::move(objectInfo));
  201. return objects;
  202. }
  203. bool ShaderManagementConsoleDocument::Open(const AZStd::string& loadPath)
  204. {
  205. if (!AtomToolsDocument::Open(loadPath))
  206. {
  207. // SaveFailed has already been called so just forward the result without additional notifications.
  208. // TODO Replace bool return value with enum for open and save states.
  209. return false;
  210. }
  211. if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderSourceData::Extension))
  212. {
  213. return LoadShaderSourceData();
  214. }
  215. if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
  216. {
  217. return LoadShaderVariantListSourceData();
  218. }
  219. AZ_Error("ShaderManagementConsoleDocument", false, "Document extension is not supported: '%s.'", m_absolutePath.c_str());
  220. return OpenFailed();
  221. }
  222. bool ShaderManagementConsoleDocument::Save()
  223. {
  224. if (!AtomToolsDocument::Save())
  225. {
  226. // SaveFailed has already been called so just forward the result without additional notifications.
  227. // TODO Replace bool return value with enum for open and save states.
  228. return false;
  229. }
  230. return SaveSourceData();
  231. }
  232. bool ShaderManagementConsoleDocument::SaveAsCopy(const AZStd::string& savePath)
  233. {
  234. if (!AtomToolsDocument::SaveAsCopy(savePath))
  235. {
  236. // SaveFailed has already been called so just forward the result without additional notifications.
  237. // TODO Replace bool return value with enum for open and save states.
  238. return false;
  239. }
  240. return SaveSourceData();
  241. }
  242. bool ShaderManagementConsoleDocument::SaveAsChild(const AZStd::string& savePath)
  243. {
  244. if (!AtomToolsDocument::SaveAsChild(savePath))
  245. {
  246. // SaveFailed has already been called so just forward the result without additional notifications.
  247. // TODO Replace bool return value with enum for open and save states.
  248. return false;
  249. }
  250. return SaveSourceData();
  251. }
  252. bool ShaderManagementConsoleDocument::IsModified() const
  253. {
  254. return m_modified;
  255. }
  256. bool ShaderManagementConsoleDocument::BeginEdit()
  257. {
  258. // Save the current properties as a momento for undo before any changes are applied
  259. m_shaderVariantListSourceDataBeforeEdit = m_shaderVariantListSourceData;
  260. return true;
  261. }
  262. bool ShaderManagementConsoleDocument::EndEdit()
  263. {
  264. bool modified = false;
  265. // Lazy evaluation, comparing the current and previous shader variant list source data state to determine if we need to record undo/redo history.
  266. // TODO Refine this so that only the deltas are stored.
  267. const auto& undoState = m_shaderVariantListSourceDataBeforeEdit;
  268. const auto& redoState = m_shaderVariantListSourceData;
  269. if (undoState.m_shaderFilePath != redoState.m_shaderFilePath ||
  270. undoState.m_shaderVariants.size() != redoState.m_shaderVariants.size())
  271. {
  272. modified = true;
  273. }
  274. else
  275. {
  276. for (size_t i = 0; i < redoState.m_shaderVariants.size(); ++i)
  277. {
  278. if (undoState.m_shaderVariants[i].m_stableId != redoState.m_shaderVariants[i].m_stableId ||
  279. undoState.m_shaderVariants[i].m_options != redoState.m_shaderVariants[i].m_options)
  280. {
  281. modified = true;
  282. break;
  283. }
  284. }
  285. }
  286. if (modified)
  287. {
  288. AddUndoRedoHistory(
  289. [this, undoState]() { SetShaderVariantListSourceData(undoState); },
  290. [this, redoState]() { SetShaderVariantListSourceData(redoState); });
  291. }
  292. m_shaderVariantListSourceDataBeforeEdit = {};
  293. return true;
  294. }
  295. void ShaderManagementConsoleDocument::Clear()
  296. {
  297. AtomToolsFramework::AtomToolsDocument::Clear();
  298. m_shaderVariantListSourceData = {};
  299. m_shaderVariantListSourceDataBeforeEdit = {};
  300. m_shaderAsset = {};
  301. m_modified = {};
  302. }
  303. bool ShaderManagementConsoleDocument::SaveSourceData()
  304. {
  305. if (!AZ::RPI::JsonUtils::SaveObjectToFile(m_savePathNormalized, m_shaderVariantListSourceData))
  306. {
  307. AZ_Error("ShaderManagementConsoleDocument", false, "Document could not be saved: '%s'.", m_savePathNormalized.c_str());
  308. return SaveFailed();
  309. }
  310. m_absolutePath = m_savePathNormalized;
  311. m_modified = {};
  312. return SaveSucceeded();
  313. }
  314. bool ShaderManagementConsoleDocument::LoadShaderSourceData()
  315. {
  316. return OpenFailed();
  317. }
  318. bool ShaderManagementConsoleDocument::LoadShaderVariantListSourceData()
  319. {
  320. // Load previously generated shader variant list source data
  321. AZ::RPI::ShaderVariantListSourceData shaderVariantListSourceData;
  322. if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_absolutePath, shaderVariantListSourceData))
  323. {
  324. AZ_Error("ShaderManagementConsoleDocument", false, "Failed loading shader variant list data: '%s.'", m_absolutePath.c_str());
  325. return OpenFailed();
  326. }
  327. SetShaderVariantListSourceData(shaderVariantListSourceData);
  328. m_modified = {};
  329. return OpenSucceeded();
  330. }
  331. AZ::u32 ShaderManagementConsoleDocument::UpdateOptionValue(
  332. AZStd::vector<AZ::RPI::ShaderVariantListSourceData::VariantInfo>& shaderVariantIN,
  333. AZStd::vector<AZ::RPI::ShaderVariantListSourceData::VariantInfo>& shaderVariantOUT,
  334. AZ::Name targetOption,
  335. AZ::Name targetValue,
  336. AZ::u32 stableId)
  337. {
  338. shaderVariantOUT.clear();
  339. for (auto& variantInfo : shaderVariantIN)
  340. {
  341. AZ::RPI::ShaderOptionValuesSourceData optionList;
  342. optionList = variantInfo.m_options;
  343. if (optionList.count(targetOption) > 0)
  344. {
  345. optionList[targetOption] = targetValue;
  346. }
  347. shaderVariantOUT.emplace_back(stableId, optionList);
  348. stableId += 1;
  349. }
  350. return stableId;
  351. }
  352. } // namespace ShaderManagementConsole