Merge pull request #7613 from aws-lumberyard-dev/Atom/guthadam/centralize_shader_management_console_operations_in_document_class

Atom Tools: Moving shader management console file operations into document class
monroegm-disable-blank-issue-2
Guthrie Adams 4 years ago committed by GitHub
commit ba43efd3d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -43,7 +43,7 @@ namespace AtomToolsFramework
using Base = AzFramework::Application;
AtomToolsApplication(const AZStd::string& targetName, int* argc, char*** argv);
AtomToolsApplication(const char* targetName, int* argc, char*** argv);
~AtomToolsApplication();
virtual bool LaunchLocalServer();

@ -22,7 +22,7 @@ namespace AtomToolsFramework
using Base = AtomToolsApplication;
AtomToolsDocumentApplication(const AZStd::string& targetName, int* argc, char*** argv);
AtomToolsDocumentApplication(const char* targetName, int* argc, char*** argv);
protected:
// AtomToolsApplication overrides...

@ -45,7 +45,7 @@ AZ_POP_DISABLE_WARNING
namespace AtomToolsFramework
{
AtomToolsApplication::AtomToolsApplication(const AZStd::string& targetName, int* argc, char*** argv)
AtomToolsApplication::AtomToolsApplication(const char* targetName, int* argc, char*** argv)
: Application(argc, argv)
, AzQtApplication(*argc, *argv)
, m_targetName(targetName)

@ -11,7 +11,7 @@
namespace AtomToolsFramework
{
AtomToolsDocumentApplication::AtomToolsDocumentApplication(const AZStd::string& targetName, int* argc, char*** argv)
AtomToolsDocumentApplication::AtomToolsDocumentApplication(const char* targetName, int* argc, char*** argv)
: Base(targetName, argc, argv)
{
}

@ -6,10 +6,18 @@
*
*/
#include <AssetDatabase/AssetDatabaseConnection.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Document/ShaderManagementConsoleDocument.h>
namespace ShaderManagementConsole
@ -25,17 +33,15 @@ namespace ShaderManagementConsole
ShaderManagementConsoleDocumentRequestBus::Handler::BusDisconnect();
}
void ShaderManagementConsoleDocument::SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& sourceData)
void ShaderManagementConsoleDocument::SetShaderVariantListSourceData(
const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData)
{
m_shaderVariantListSourceData = sourceData;
m_shaderVariantListSourceData = shaderVariantListSourceData;
AZStd::string shaderPath = m_shaderVariantListSourceData.m_shaderFilePath;
AzFramework::StringFunc::Path::ReplaceExtension(shaderPath, AZ::RPI::ShaderAsset::Extension);
m_shaderAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::ShaderAsset>(shaderPath.c_str());
if (!m_shaderAsset)
{
AZ_Error("ShaderManagementConsoleDocument", false, "Could not load shader asset: %s.", shaderPath.c_str());
}
AZ_Error("ShaderManagementConsoleDocument", m_shaderAsset.IsReady(), "Could not load shader asset: %s.", shaderPath.c_str());
AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
@ -107,22 +113,18 @@ namespace ShaderManagementConsole
return false;
}
if (!AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderSourceData::Extension))
{
AZ_Error("ShaderManagementConsoleDocument", false, "Document extension is not supported: '%s.'", m_absolutePath.c_str());
return OpenFailed();
return LoadShaderSourceData();
}
// Load the shader config data and create a shader config asset from it
AZ::RPI::ShaderVariantListSourceData sourceData;
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_absolutePath, sourceData))
if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
{
AZ_Error("ShaderManagementConsoleDocument", false, "Failed loading shader variant list data: '%s.'", m_absolutePath.c_str());
return OpenFailed();
return LoadShaderVariantListSourceData();
}
SetShaderVariantListSourceData(sourceData);
return IsOpen() ? OpenSucceeded() : OpenFailed();
AZ_Error("ShaderManagementConsoleDocument", false, "Document extension is not supported: '%s.'", m_absolutePath.c_str());
return OpenFailed();
}
bool ShaderManagementConsoleDocument::Save()
@ -173,7 +175,8 @@ namespace ShaderManagementConsole
bool ShaderManagementConsoleDocument::IsSavable() const
{
return true;
return IsOpen() &&
AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension);
}
void ShaderManagementConsoleDocument::Clear()
@ -195,4 +198,178 @@ namespace ShaderManagementConsole
m_absolutePath = m_savePathNormalized;
return SaveSucceeded();
}
bool ShaderManagementConsoleDocument::LoadShaderSourceData()
{
// Get info such as relative path of the file and asset id
bool result = false;
AZ::Data::AssetInfo shaderAssetInfo;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, m_absolutePath.c_str(), shaderAssetInfo,
watchFolder);
if (!result)
{
AZ_Error("ShaderManagementConsoleDocument", false, "Failed to get the asset info for the file: %s.", m_absolutePath.c_str());
return OpenFailed();
}
// retrieves a list of all material source files that use the shader. Note that materials inherit from materialtype files, which
// are actual files that refer to shader files.
const auto& materialAssetIds = FindMaterialAssetsUsingShader(shaderAssetInfo.m_relativePath);
// This loop collects all uniquely-identified shader items used by the materials based on its shader variant id.
AZStd::set<AZ::RPI::ShaderVariantId> shaderVariantIds;
AZStd::vector<AZ::RPI::ShaderOptionGroup> shaderVariantListShaderOptionGroups;
for (const auto& materialAssetId : materialAssetIds)
{
const auto& materialInstanceShaderItems = GetMaterialInstanceShaderItems(materialAssetId);
for (const auto& shaderItem : materialInstanceShaderItems)
{
const auto& shaderAssetId = shaderItem.GetShaderAsset().GetId();
if (shaderAssetInfo.m_assetId == shaderAssetId)
{
const auto& shaderVariantId = shaderItem.GetShaderVariantId();
if (!shaderVariantId.IsEmpty() && shaderVariantIds.insert(shaderVariantId).second)
{
shaderVariantListShaderOptionGroups.push_back(shaderItem.GetShaderOptionGroup());
}
}
}
}
// Generate the shader variant list data by collecting shader option name-value pairs.s
AZ::RPI::ShaderVariantListSourceData shaderVariantListSourceData;
shaderVariantListSourceData.m_shaderFilePath = shaderAssetInfo.m_relativePath;
int stableId = 1;
for (const auto& shaderOptionGroup : shaderVariantListShaderOptionGroups)
{
AZ::RPI::ShaderVariantListSourceData::VariantInfo variantInfo;
variantInfo.m_stableId = stableId;
const auto& shaderOptionDescriptors = shaderOptionGroup.GetShaderOptionDescriptors();
for (const auto& shaderOptionDescriptor : shaderOptionDescriptors)
{
const auto& optionName = shaderOptionDescriptor.GetName();
const auto& optionValue = shaderOptionGroup.GetValue(optionName);
if (!optionValue.IsValid())
{
continue;
}
const auto& valueName = shaderOptionDescriptor.GetValueName(optionValue);
variantInfo.m_options[optionName.GetStringView()] = valueName.GetStringView();
}
if (!variantInfo.m_options.empty())
{
shaderVariantListSourceData.m_shaderVariants.push_back(variantInfo);
stableId++;
}
}
SetShaderVariantListSourceData(shaderVariantListSourceData);
return IsOpen() ? OpenSucceeded() : OpenFailed();
}
bool ShaderManagementConsoleDocument::LoadShaderVariantListSourceData()
{
// Load previously generated shader variant list source data
AZ::RPI::ShaderVariantListSourceData shaderVariantListSourceData;
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_absolutePath, shaderVariantListSourceData))
{
AZ_Error("ShaderManagementConsoleDocument", false, "Failed loading shader variant list data: '%s.'", m_absolutePath.c_str());
return OpenFailed();
}
SetShaderVariantListSourceData(shaderVariantListSourceData);
return IsOpen() ? OpenSucceeded() : OpenFailed();
}
AZStd::vector<AZ::Data::AssetId> ShaderManagementConsoleDocument::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath)
{
AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection;
assetDatabaseConnection.OpenDatabase();
// Find all material types that reference shaderFilePath
AZStd::vector<AZStd::string> materialTypeSources;
assetDatabaseConnection.QuerySourceDependencyByDependsOnSource(
shaderFilePath.c_str(), nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any,
[&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry)
{
if (AzFramework::StringFunc::Path::IsExtension(
sourceFileDependencyEntry.m_source.c_str(), AZ::RPI::MaterialTypeSourceData::Extension))
{
materialTypeSources.push_back(sourceFileDependencyEntry.m_source);
}
return true;
});
// Find all materials that reference any of the material types using this shader
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies;
for (const auto& materialTypeSource : materialTypeSources)
{
bool result = false;
AZ::Data::AssetInfo materialTypeSourceAssetInfo;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, materialTypeSource.c_str(),
materialTypeSourceAssetInfo, watchFolder);
assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId(
materialTypeSourceAssetInfo.m_assetId.m_guid, materialTypeSourceAssetInfo.m_assetId.m_subId,
[&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry)
{
if (AzFramework::StringFunc::Path::IsExtension(entry.m_productName.c_str(), AZ::RPI::MaterialAsset::Extension))
{
productDependencies.push_back(entry);
}
return true;
});
}
AZStd::vector<AZ::Data::AssetId> results;
results.reserve(productDependencies.size());
for (auto product : productDependencies)
{
assetDatabaseConnection.QueryCombinedByProductID(
product.m_productID,
[&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined)
{
results.push_back({ combined.m_sourceGuid, combined.m_subID });
return false;
},
nullptr);
}
return results;
}
AZStd::vector<AZ::RPI::ShaderCollection::Item> ShaderManagementConsoleDocument::GetMaterialInstanceShaderItems(
const AZ::Data::AssetId& materialAssetId)
{
auto materialAsset =
AZ::RPI::AssetUtils::LoadAssetById<AZ::RPI::MaterialAsset>(materialAssetId, AZ::RPI::AssetUtils::TraceLevel::Error);
if (!materialAsset.IsReady())
{
AZ_Error(
"ShaderManagementConsoleDocument", false, "Failed to load material asset from asset id: %s",
materialAssetId.ToString<AZStd::string>().c_str());
return AZStd::vector<AZ::RPI::ShaderCollection::Item>();
}
auto materialInstance = AZ::RPI::Material::Create(materialAsset);
if (!materialInstance)
{
AZ_Error(
"ShaderManagementConsoleDocument", false, "Failed to create material instance from asset: %s",
materialAsset.ToString<AZStd::string>().c_str());
return AZStd::vector<AZ::RPI::ShaderCollection::Item>();
}
return AZStd::vector<AZ::RPI::ShaderCollection::Item>(
materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end());
}
} // namespace ShaderManagementConsole

@ -5,20 +5,21 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
#include <Atom/RPI.Reflect/Material/ShaderCollection.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
#include <AtomToolsFramework/Document/AtomToolsDocument.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/vector.h>
#include <Document/ShaderManagementConsoleDocumentRequestBus.h>
namespace ShaderManagementConsole
{
/**
* ShaderManagementConsoleDocument provides an API for modifying and saving document properties.
*/
//! ShaderManagementConsoleDocument provides an API for modifying and saving document properties.
class ShaderManagementConsoleDocument
: public AtomToolsFramework::AtomToolsDocument
, public ShaderManagementConsoleDocumentRequestBus::Handler
@ -42,7 +43,7 @@ namespace ShaderManagementConsole
bool IsSavable() const override;
// ShaderManagementConsoleDocumentRequestBus::Handler overridfes...
void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& sourceData) override;
void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData) override;
const AZ::RPI::ShaderVariantListSourceData& GetShaderVariantListSourceData() const override;
size_t GetShaderVariantCount() const override;
const AZ::RPI::ShaderVariantListSourceData::VariantInfo& GetShaderVariantInfo(size_t index) const override;
@ -53,8 +54,21 @@ namespace ShaderManagementConsole
// AtomToolsFramework::AtomToolsDocument overrides...
void Clear() override;
// Write shader variant list source data to JSON
bool SaveSourceData();
// Read shader variant list source data from JSON and initialize the document
bool LoadShaderSourceData();
// Read shader source data from JSON then find all references to to populate the shader variant list and initialize the document
bool LoadShaderVariantListSourceData();
// Find all material assets that reference material types using shaderFilePath
AZStd::vector<AZ::Data::AssetId> FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath);
// Retrieve all of the shader collection items from a material instance created from materialAssetId
AZStd::vector<AZ::RPI::ShaderCollection::Item> GetMaterialInstanceShaderItems(const AZ::Data::AssetId& materialAssetId);
// Source data for shader variant list
AZ::RPI::ShaderVariantListSourceData m_shaderVariantListSourceData;

@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
@ -14,9 +15,7 @@
namespace ShaderManagementConsole
{
class ShaderManagementConsoleDocumentRequests
: public AZ::EBusTraits
class ShaderManagementConsoleDocumentRequests : public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
@ -24,7 +23,7 @@ namespace ShaderManagementConsole
typedef AZ::Uuid BusIdType;
//! Set the shader variant list
virtual void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& sourceData) = 0;
virtual void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData) = 0;
//! Get the shader variant list
virtual const AZ::RPI::ShaderVariantListSourceData& GetShaderVariantListSourceData() const = 0;

@ -6,27 +6,17 @@
*
*/
#include <AssetDatabase/AssetDatabaseConnection.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <Document/ShaderManagementConsoleDocument.h>
#include <Document/ShaderManagementConsoleDocumentRequestBus.h>
#include <ShaderManagementConsoleApplication.h>
#include <ShaderManagementConsoleRequestBus.h>
#include <ShaderManagementConsole_Traits_Platform.h>
#include <QDesktopServices>
@ -62,13 +52,11 @@ namespace ShaderManagementConsole
QApplication::setApplicationName("O3DE Shader Management Console");
ShaderManagementConsoleRequestBus::Handler::BusConnect();
AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect();
}
ShaderManagementConsoleApplication::~ShaderManagementConsoleApplication()
{
ShaderManagementConsoleRequestBus::Handler::BusDisconnect();
AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect();
m_window.reset();
}
@ -79,15 +67,6 @@ namespace ShaderManagementConsole
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<ShaderManagementConsoleRequestBus>("ShaderManagementConsoleRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole")
->Event("GetSourceAssetInfo", &ShaderManagementConsoleRequestBus::Events::GetSourceAssetInfo)
->Event("FindMaterialAssetsUsingShader", &ShaderManagementConsoleRequestBus::Events::FindMaterialAssetsUsingShader )
->Event("GetMaterialInstanceShaderItems", &ShaderManagementConsoleRequestBus::Events::GetMaterialInstanceShaderItems)
;
behaviorContext->EBus<ShaderManagementConsoleDocumentRequestBus>("ShaderManagementConsoleDocumentRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Editor")
@ -145,8 +124,11 @@ namespace ShaderManagementConsole
pythonArgs);
});
}
else if (AzFramework::StringFunc::Path::IsExtension(
entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
if (AzFramework::StringFunc::Path::IsExtension(
entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderSourceData::Extension) ||
AzFramework::StringFunc::Path::IsExtension(
entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
{
menu->addAction(QObject::tr("Open"), [entries, this]()
{
@ -180,100 +162,4 @@ namespace ShaderManagementConsole
{
return m_window.get();
}
AZ::Data::AssetInfo ShaderManagementConsoleApplication::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName)
{
bool result = false;
AZ::Data::AssetInfo assetInfo;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourceAssetFileName.c_str(), assetInfo,
watchFolder);
AZ_Error(nullptr, result, "Failed to get the asset info for the file: %s.", sourceAssetFileName.c_str());
return assetInfo;
}
AZStd::vector<AZ::Data::AssetId> ShaderManagementConsoleApplication::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath)
{
// Collect the material types referencing the shader
AZStd::vector<AZStd::string> materialTypeSources;
AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection;
assetDatabaseConnection.OpenDatabase();
assetDatabaseConnection.QuerySourceDependencyByDependsOnSource(
shaderFilePath.c_str(), nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any,
[&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry)
{
AZStd::string assetExtension;
if (AzFramework::StringFunc::Path::GetExtension(sourceFileDependencyEntry.m_source.c_str(), assetExtension, false))
{
if (assetExtension == "materialtype")
{
materialTypeSources.push_back(sourceFileDependencyEntry.m_source);
}
}
return true;
});
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies;
for (const auto& materialTypeSource : materialTypeSources)
{
bool result = false;
AZ::Data::AssetInfo materialTypeSourceAssetInfo;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, materialTypeSource.c_str(),
materialTypeSourceAssetInfo, watchFolder);
assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId(
materialTypeSourceAssetInfo.m_assetId.m_guid, materialTypeSourceAssetInfo.m_assetId.m_subId,
[&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry)
{
AZStd::string assetExtension;
if (AzFramework::StringFunc::Path::GetExtension(entry.m_productName.c_str(), assetExtension, false))
{
if (assetExtension == "azmaterial")
{
productDependencies.push_back(entry);
}
}
return true;
});
}
AZStd::vector<AZ::Data::AssetId> results;
results.reserve(productDependencies.size());
for (auto product : productDependencies)
{
assetDatabaseConnection.QueryCombinedByProductID(
product.m_productID,
[&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined)
{
results.push_back({ combined.m_sourceGuid, combined.m_subID });
return false;
},
nullptr);
}
return results;
}
AZStd::vector<AZ::RPI::ShaderCollection::Item> ShaderManagementConsoleApplication::GetMaterialInstanceShaderItems(
const AZ::Data::AssetId& assetId)
{
auto materialAsset = AZ::RPI::AssetUtils::LoadAssetById<AZ::RPI::MaterialAsset>(assetId, AZ::RPI::AssetUtils::TraceLevel::Error);
auto materialInstance = AZ::RPI::Material::Create(materialAsset);
AZ_Error(
nullptr, materialAsset, "Failed to get a material instance from product asset id: %s",
assetId.ToString<AZStd::string>().c_str());
if (materialInstance != nullptr)
{
return AZStd::vector<AZ::RPI::ShaderCollection::Item>(
materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end());
}
return AZStd::vector<AZ::RPI::ShaderCollection::Item>();
}
} // namespace ShaderManagementConsole

@ -12,14 +12,12 @@
#include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentApplication.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <ShaderManagementConsoleRequestBus.h>
#include <Window/ShaderManagementConsoleWindow.h>
namespace ShaderManagementConsole
{
class ShaderManagementConsoleApplication
: public AtomToolsFramework::AtomToolsDocumentApplication
, private ShaderManagementConsoleRequestBus::Handler
, private AzToolsFramework::EditorWindowRequestBus::Handler
{
public:
@ -42,11 +40,6 @@ namespace ShaderManagementConsole
// AzToolsFramework::EditorWindowRequests::Bus::Handler
QWidget* GetAppMainWindow() override;
// ShaderManagementConsoleRequestBus::Handler overrides...
AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override;
AZStd::vector<AZ::Data::AssetId> FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override;
AZStd::vector<AZ::RPI::ShaderCollection::Item> GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) override;
private:
AZStd::unique_ptr<ShaderManagementConsoleWindow> m_window;
AZStd::unique_ptr<AtomToolsFramework::AtomToolsAssetBrowserInteractions> m_assetBrowserInteractions;

@ -1,38 +0,0 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
#include <Atom/RPI.Reflect/Material/ShaderCollection.h>
namespace ShaderManagementConsole
{
//! ShaderManagementConsoleRequestBus provides
class ShaderManagementConsoleRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//! Returns a shader file's asset id and relative filepath
virtual AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) = 0;
// [GFX TODO][ATOM-14857] Generalize this API
//! Returns a list of material AssetIds that use the shader file.
virtual AZStd::vector<AZ::Data::AssetId> FindMaterialAssetsUsingShader (const AZStd::string& shaderFilePath) = 0;
//! Returns a list of shader items contained within an instantiated material source's shader collection.
virtual AZStd::vector<AZ::RPI::ShaderCollection::Item> GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) = 0;
};
using ShaderManagementConsoleRequestBus = AZ::EBus<ShaderManagementConsoleRequests>;
} // namespace ShaderManagementConsole

@ -0,0 +1,106 @@
/*
* 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 <AzCore/Name/Name.h>
#include <Document/ShaderManagementConsoleDocumentRequestBus.h>
#include <Window/ShaderManagementConsoleTableView.h>
#include <QHeaderView>
namespace ShaderManagementConsole
{
ShaderManagementConsoleTableView::ShaderManagementConsoleTableView(
const AZ::Crc32& toolId, const AZ::Uuid& documentId, QWidget* parent)
: QTableView(parent)
, m_toolId(toolId)
, m_documentId(documentId)
, m_model(new QStandardItemModel(this))
{
setSelectionBehavior(QAbstractItemView::SelectRows);
setModel(m_model);
RebuildTable();
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
}
ShaderManagementConsoleTableView::~ShaderManagementConsoleTableView()
{
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
}
void ShaderManagementConsoleTableView::OnDocumentOpened(const AZ::Uuid& documentId)
{
if (m_documentId == documentId)
{
RebuildTable();
}
}
void ShaderManagementConsoleTableView::OnDocumentModified(const AZ::Uuid& documentId)
{
if (m_documentId == documentId)
{
RebuildTable();
}
}
void ShaderManagementConsole::ShaderManagementConsoleTableView::RebuildTable()
{
AZStd::unordered_set<AZStd::string> optionNames;
size_t shaderOptionCount = 0;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderOptionCount, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount);
for (size_t optionIndex = 0; optionIndex < shaderOptionCount; ++optionIndex)
{
AZ::RPI::ShaderOptionDescriptor shaderOptionDesc;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderOptionDesc, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor, optionIndex);
optionNames.insert(shaderOptionDesc.GetName().GetCStr());
}
size_t shaderVariantCount = 0;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderVariantCount, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount);
m_model->clear();
m_model->setRowCount(static_cast<int>(shaderVariantCount));
m_model->setColumnCount(static_cast<int>(optionNames.size()));
int nameIndex = 0;
for (const auto& optionName : optionNames)
{
m_model->setHeaderData(nameIndex++, Qt::Horizontal, optionName.c_str());
}
for (int variantIndex = 0; variantIndex < shaderVariantCount; ++variantIndex)
{
AZ::RPI::ShaderVariantListSourceData::VariantInfo shaderVariantInfo;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderVariantInfo, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo, variantIndex);
m_model->setHeaderData(variantIndex, Qt::Vertical, QString::number(variantIndex));
for (const auto& shaderOption : shaderVariantInfo.m_options)
{
AZ::Name optionName{ shaderOption.first };
AZ::Name optionValue{ shaderOption.second };
auto optionIt = optionNames.find(optionName.GetCStr());
int optionIndex = static_cast<int>(AZStd::distance(optionNames.begin(), optionIt));
QStandardItem* item = new QStandardItem(optionValue.GetCStr());
m_model->setItem(variantIndex, optionIndex, item);
}
}
}
} // namespace ShaderManagementConsole
#include <Window/moc_ShaderManagementConsoleTableView.cpp>
#include "ShaderManagementConsoleTableView.h"

@ -0,0 +1,40 @@
/*
* 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
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <QStandardItemModel>
#include <QTableView>
#endif
namespace ShaderManagementConsole
{
class ShaderManagementConsoleTableView
: public QTableView
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ShaderManagementConsoleTableView, AZ::SystemAllocator, 0);
ShaderManagementConsoleTableView(const AZ::Crc32& toolId, const AZ::Uuid& documentId, QWidget* parent);
~ShaderManagementConsoleTableView();
protected:
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OnDocumentModified(const AZ::Uuid& documentId) override;
void RebuildTable();
const AZ::Crc32 m_toolId = {};
const AZ::Uuid m_documentId = AZ::Uuid::CreateNull();
QStandardItemModel* m_model = {};
};
} // namespace ShaderManagementConsole

@ -6,23 +6,19 @@
*
*/
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/Name/Name.h>
#include <AzCore/Utils/Utils.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <Document/ShaderManagementConsoleDocumentRequestBus.h>
#include <Window/ShaderManagementConsoleTableView.h>
#include <Window/ShaderManagementConsoleWindow.h>
#include <AzCore/Utils/Utils.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QDesktopServices>
#include <QFileDialog>
#include <QHeaderView>
#include <QStandardItemModel>
#include <QTableView>
#include <QUrl>
#include <QWindow>
AZ_POP_DISABLE_WARNING
namespace ShaderManagementConsole
{
@ -40,16 +36,18 @@ namespace ShaderManagementConsole
setObjectName("ShaderManagementConsoleWindow");
m_assetBrowser->SetFilterState("", AZ::RPI::ShaderAsset::Group, true);
m_assetBrowser->SetOpenHandler([this](const AZStd::string& absolutePath) {
if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
m_assetBrowser->SetOpenHandler([this](const AZStd::string& absolutePath)
{
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath);
return;
}
if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderSourceData::Extension) ||
AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
{
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath);
return;
}
QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str()));
});
QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str()));
});
// Restore geometry and show the window
mainWindowWrapper->showFromSettings();
@ -66,68 +64,18 @@ namespace ShaderManagementConsole
bool ShaderManagementConsoleWindow::GetOpenDocumentParams(AZStd::string& openPath)
{
openPath = QFileDialog::getOpenFileName(
this, tr("Open Document"), AZ::Utils::GetProjectPath().c_str(), tr("Files (*.%1)").arg(AZ::RPI::ShaderVariantListSourceData::Extension)).toUtf8().constData();
this, tr("Open Document"), AZ::Utils::GetProjectPath().c_str(),
tr("Shader Files (*.%1);;Shader Variant List Files (*.%2)")
.arg(AZ::RPI::ShaderSourceData::Extension)
.arg(AZ::RPI::ShaderVariantListSourceData::Extension))
.toUtf8()
.constData();
return !openPath.empty();
}
QWidget* ShaderManagementConsoleWindow::CreateDocumentTabView(const AZ::Uuid& documentId)
{
AZStd::unordered_set<AZStd::string> optionNames;
size_t shaderOptionCount = 0;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderOptionCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount);
for (size_t optionIndex = 0; optionIndex < shaderOptionCount; ++optionIndex)
{
AZ::RPI::ShaderOptionDescriptor shaderOptionDesc;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderOptionDesc, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor, optionIndex);
const char* optionName = shaderOptionDesc.GetName().GetCStr();
optionNames.insert(optionName);
}
size_t shaderVariantCount = 0;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderVariantCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount);
auto model = new QStandardItemModel();
model->setRowCount(static_cast<int>(shaderVariantCount));
model->setColumnCount(static_cast<int>(optionNames.size()));
int nameIndex = 0;
for (const auto& optionName : optionNames)
{
model->setHeaderData(nameIndex++, Qt::Horizontal, optionName.c_str());
}
for (int variantIndex = 0; variantIndex < shaderVariantCount; ++variantIndex)
{
AZ::RPI::ShaderVariantListSourceData::VariantInfo shaderVariantInfo;
ShaderManagementConsoleDocumentRequestBus::EventResult(
shaderVariantInfo, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo, variantIndex);
model->setHeaderData(variantIndex, Qt::Vertical, QString::number(variantIndex));
for (const auto& shaderOption : shaderVariantInfo.m_options)
{
AZ::Name optionName{ shaderOption.first };
AZ::Name optionValue{ shaderOption.second };
auto optionIt = optionNames.find(optionName.GetCStr());
int optionIndex = static_cast<int>(AZStd::distance(optionNames.begin(), optionIt));
QStandardItem* item = new QStandardItem(optionValue.GetCStr());
model->setItem(variantIndex, optionIndex, item);
}
}
// The document tab contains a table view.
auto contentWidget = new QTableView(centralWidget());
contentWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
contentWidget->setModel(model);
return contentWidget;
return new ShaderManagementConsoleTableView(m_toolId, documentId, centralWidget());
}
} // namespace ShaderManagementConsole

@ -8,16 +8,8 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
#include <Atom/RPI.Public/Shader/Shader.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QStandardItemModel>
AZ_POP_DISABLE_WARNING
#endif
namespace ShaderManagementConsole
{
//! ShaderManagementConsoleWindow is the main class. Its responsibility is limited to initializing and connecting

@ -11,6 +11,8 @@ set(FILES
Source/Document/ShaderManagementConsoleDocument.cpp
Source/Document/ShaderManagementConsoleDocument.h
Source/Window/ShaderManagementConsoleTableView.h
Source/Window/ShaderManagementConsoleTableView.cpp
Source/Window/ShaderManagementConsoleWindow.h
Source/Window/ShaderManagementConsoleWindow.cpp
Source/Window/ShaderManagementConsole.qrc
@ -18,6 +20,5 @@ set(FILES
Source/main.cpp
Source/ShaderManagementConsoleApplication.cpp
Source/ShaderManagementConsoleApplication.h
Source/ShaderManagementConsoleRequestBus.h
../Scripts/GenerateShaderVariantListForMaterials.py
)

@ -39,27 +39,15 @@ def main():
print("The input argument for the script is not a valid .shader file")
return
# Get info such as relative path of the file and asset id
shaderAssetInfo = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(
azlmbr.bus.Broadcast,
'GetSourceAssetInfo',
filename
)
# retrieves a list of all material source files that use the shader. Note that materials inherit from materialtype files, which are actual files that refer to shader files.
materialAssetIds = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(
azlmbr.bus.Broadcast,
'FindMaterialAssetsUsingShader',
shaderAssetInfo.relativePath
)
# prompt the user to save the file in the project folder or same folder as the shader
msgBox = QtWidgets.QMessageBox(
QtWidgets.QMessageBox.Question,
"Choose Save Location for .shadervariantlist File",
"Save .shadervariantlist file in Project folder or in the same folder as shader file?"
)
projectButton = msgBox.addButton("Project Folder", QtWidgets.QMessageBox.AcceptRole)
msgBox.addButton("Same Folder as Shader", QtWidgets.QMessageBox.AcceptRole)
shaderButton = msgBox.addButton("Same Folder as Shader", QtWidgets.QMessageBox.AcceptRole)
cancelButton = msgBox.addButton("Cancel", QtWidgets.QMessageBox.RejectRole)
msgBox.exec()
@ -69,80 +57,36 @@ def main():
elif msgBox.clickedButton() == cancelButton:
return
# This loop collects all uniquely-identified shader items used by the materials based on its shader variant id.
shader_file = os.path.basename(filename)
shaderVariantIds = []
shaderVariantListShaderOptionGroups = []
progressDialog = QtWidgets.QProgressDialog(f"Generating .shadervariantlist file for:\n{shader_file}", "Cancel", 0, len(materialAssetIds))
progressDialog.setMaximumWidth(400)
progressDialog.setMaximumHeight(100)
progressDialog.setModal(True)
progressDialog.setWindowTitle("Generating Shader Variant List")
for i, materialAssetId in enumerate(materialAssetIds):
materialInstanceShaderItems = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(azlmbr.bus.Broadcast, 'GetMaterialInstanceShaderItems', materialAssetId)
for shaderItem in materialInstanceShaderItems:
shaderAssetId = shaderItem.GetShaderAsset().get_id()
if shaderAssetInfo.assetId == shaderAssetId:
shaderVariantId = shaderItem.GetShaderVariantId()
if not shaderVariantId.IsEmpty():
# Check for repeat shader variant ids. We are using a list here
# instead of a set to check for duplicates on shaderVariantIds because
# shaderVariantId is not hashed by the ID like it is in the C++ side.
has_repeat = False
for variantId in shaderVariantIds:
if shaderVariantId == variantId:
has_repeat = True
break
if has_repeat:
continue
shaderVariantIds.append(shaderVariantId)
shaderVariantListShaderOptionGroups.append(shaderItem.GetShaderOptionGroup())
progressDialog.setValue(i)
if progressDialog.wasCanceled():
return
progressDialog.close()
# Generate the shader variant list data by collecting shader option name-value pairs.s
shaderVariantList = azlmbr.shader.ShaderVariantListSourceData()
shaderVariantList.shaderFilePath = shaderAssetInfo.relativePath
shaderVariants = []
stableId = 1
for shaderOptionGroup in shaderVariantListShaderOptionGroups:
variantInfo = azlmbr.shader.ShaderVariantInfo()
variantInfo.stableId = stableId
options = {}
shaderOptionDescriptors = shaderOptionGroup.GetShaderOptionDescriptors()
for shaderOptionDescriptor in shaderOptionDescriptors:
optionName = shaderOptionDescriptor.GetName()
optionValue = shaderOptionGroup.GetValueByOptionName(optionName)
if not optionValue.IsValid():
continue
valueName = shaderOptionDescriptor.GetValueName(optionValue)
options[optionName.ToString()] = valueName.ToString()
if len(options) != 0:
variantInfo.options = options
shaderVariants.append(variantInfo)
stableId += 1
shaderVariantList.shaderVariants = shaderVariants
# open the shader file as a document which will generate all of the shader variant list data
documentId = azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
azlmbr.bus.Broadcast,
'OpenDocument',
filename
)
if documentId.IsNull():
print("The shader source data file could not be opened")
return
# clean previously generated shader variant list file so they don't clash.
pre, ext = os.path.splitext(shaderAssetInfo.relativePath)
projectShaderVariantListFilePath = os.path.join(azlmbr.paths.projectroot, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist')
# get the shader variant list data object which is only needed for the shader file path
shaderVariantList = azlmbr.shadermanagementconsole.ShaderManagementConsoleDocumentRequestBus(
azlmbr.bus.Event,
'GetShaderVariantListSourceData',
documentId
)
# generate the default save file path by replacing the extension of the open shader file
pre, ext = os.path.splitext(filename)
defaultShaderVariantListFilePath = f'{pre}.shadervariantlist'
# clean previously generated shader variant list file so they don't clash.
pre, ext = os.path.splitext(shaderVariantList.shaderFilePath)
projectShaderVariantListFilePath = os.path.join(azlmbr.paths.projectroot, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist')
clean_existing_shadervariantlist_files([
projectShaderVariantListFilePath
])
# Save the shader variant list file
if is_save_in_project_folder:
shaderVariantListFilePath = projectShaderVariantListFilePath
@ -150,16 +94,16 @@ def main():
shaderVariantListFilePath = defaultShaderVariantListFilePath
shaderVariantListFilePath = shaderVariantListFilePath.replace("\\", "/")
azlmbr.shader.SaveShaderVariantListSourceData(shaderVariantListFilePath, shaderVariantList)
# Open the document in shader management console
result = azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
# Save the document in shader management console
saveResult = azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
azlmbr.bus.Broadcast,
'OpenDocument',
'SaveDocumentAsChild',
documentId,
shaderVariantListFilePath
)
if not result.IsNull():
if saveResult:
msgBox = QtWidgets.QMessageBox(
QtWidgets.QMessageBox.Information,
"Shader Variant List File Successfully Generated",

Loading…
Cancel
Save