Merge branch 'development' into Atom/santorac/OrderOnceDependencyForPassBuilder

This commit is contained in:
santorac
2022-01-12 11:26:11 -08:00
6060 changed files with 481813 additions and 192037 deletions
@@ -63,11 +63,21 @@ namespace AZ
{
BusDisconnect();
}
bool MaterialBuilder::ReportMaterialAssetWarningsAsErrors() const
{
bool warningsAsErrors = false;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Get(warningsAsErrors, "/O3DE/Atom/RPI/MaterialBuilder/WarningsAsErrors");
}
return warningsAsErrors;
}
//! Adds all relevant dependencies for a referenced source file, considering that the path might be relative to the original file location or a full asset path.
//! This will usually include multiple source dependencies and a single job dependency, but will include only source dependencies if the file is not found.
//! Note the AssetBuilderSDK::JobDependency::m_platformIdentifier will not be set by this function. The calling code must set this value before passing back
//! to the AssetBuilderSDK::CreateJobsResponse. If isOrderedOnceForMaterialTypes is true and the dependency is a materialtype file, the job dependency type
//! to the AssetBuilderSDK::CreateJobsResponse. If isOrderedOnceForMaterialTypes is true and the dependency is a .materialtype file, the job dependency type
//! will be set to JobDependencyType::OrderOnce.
void AddPossibleDependencies(AZStd::string_view currentFilePath,
AZStd::string_view referencedParentPath,
@@ -277,8 +287,8 @@ namespace AZ
return materialTypeAssetOutcome.GetValue();
}
AZ::Data::Asset<MaterialAsset> CreateMaterialAsset(AZStd::string_view materialSourceFilePath, const rapidjson::Value& json)
AZ::Data::Asset<MaterialAsset> MaterialBuilder::CreateMaterialAsset(AZStd::string_view materialSourceFilePath, const rapidjson::Value& json) const
{
auto material = LoadSourceData<MaterialSourceData>(json, materialSourceFilePath);
@@ -292,7 +302,7 @@ namespace AZ
return {};
}
auto materialAssetOutcome = material.GetValue().CreateMaterialAsset(Uuid::CreateRandom(), materialSourceFilePath, true);
auto materialAssetOutcome = material.GetValue().CreateMaterialAsset(Uuid::CreateRandom(), materialSourceFilePath, ReportMaterialAssetWarningsAsErrors());
if (!materialAssetOutcome.IsSuccess())
{
return {};
@@ -9,6 +9,8 @@
#pragma once
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AzCore/JSON/document.h>
namespace AZ
{
@@ -37,6 +39,9 @@ namespace AZ
private:
AZ::Data::Asset<MaterialAsset> CreateMaterialAsset(AZStd::string_view materialSourceFilePath, const rapidjson::Value& json) const;
bool ReportMaterialAssetWarningsAsErrors() const;
bool m_isShuttingDown = false;
};
@@ -948,19 +948,17 @@ namespace AZ
{
AZStd::vector<uint16_t>& skinJointIndices = productMesh.m_skinJointIndices;
AZStd::vector<float>& skinWeights = productMesh.m_skinWeights;
const auto& sourceMeshData = sourceMesh.m_meshData;
size_t numInfluencesAdded = 0;
for (const auto& skinData : sourceMesh.m_skinData)
{
const AZ::u32 controlPointIndex = sourceMeshData->GetControlPointIndex(static_cast<int>(vertexIndex));
const size_t numSkinInfluences = skinData->GetLinkCount(controlPointIndex);
const size_t numSkinInfluences = skinData->GetLinkCount(vertexIndex);
size_t numInfluencesExcess = 0;
for (size_t influenceIndex = 0; influenceIndex < numSkinInfluences; ++influenceIndex)
{
const AZ::SceneAPI::DataTypes::ISkinWeightData::Link& link = skinData->GetLink(controlPointIndex, influenceIndex);
const AZ::SceneAPI::DataTypes::ISkinWeightData::Link& link = skinData->GetLink(vertexIndex, influenceIndex);
const float weight = link.weight;
const AZStd::string& boneName = skinData->GetBoneName(link.boneId);
@@ -2088,7 +2086,7 @@ namespace AZ
AZ::Vector3 vpos; //note: it seems to be fastest to reuse a local Vector3 rather than constructing new ones each loop iteration
for (uint32_t i = 0; i < elementCount; ++i)
{
vpos.Set(const_cast<float*>(reinterpret_cast<const float*>(&buffer[i])));
vpos.Set(reinterpret_cast<const float*>(&buffer[i]));
aabb.AddPoint(vpos);
}
}
@@ -25,7 +25,7 @@ namespace AZ
namespace RPI
{
/**
* This is the central component that drive the process of exporting a scene to Model
* This is the central component that drive the process of exporting a scene to Model
* and Material assets. It delegates asset-build duties to other components like
* ModelAssetBuilderComponent and MaterialAssetBuilderComponent via export events.
*/
@@ -55,7 +55,7 @@ namespace AZ
AZStd::string_view m_relativeFileName;
AZStd::string_view m_extension;
const Uuid m_sourceUuid;
const Uuid m_sourceUuid = Uuid::CreateNull();
const DataStream::StreamType m_dataStreamType = DataStream::ST_BINARY;
};
@@ -10,6 +10,7 @@
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/IO/IOUtils.h>
#include <AzCore/IO/Path/Path.h>
namespace AZ
{
@@ -46,6 +47,12 @@ namespace AZ
AZStd::string ResolvePathReference(const AZStd::string& originatingSourceFilePath, const AZStd::string& referencedSourceFilePath)
{
// The IsAbsolute part prevents "second join parameter is an absolute path" warnings in StringFunc::Path::Join below
if (referencedSourceFilePath.empty() || AZ::IO::PathView{referencedSourceFilePath}.IsAbsolute())
{
return referencedSourceFilePath;
}
AZStd::string normalizedReferencedPath = referencedSourceFilePath;
AzFramework::StringFunc::Path::Normalize(normalizedReferencedPath);
@@ -113,7 +120,7 @@ namespace AZ
return results;
}
Outcome<Data::AssetId> MakeAssetId(const AZStd::string& sourcePath, uint32_t productSubId)
Outcome<Data::AssetId> MakeAssetId(const AZStd::string& sourcePath, uint32_t productSubId, TraceLevel reporting)
{
bool assetFound = false;
AZ::Data::AssetInfo sourceInfo;
@@ -122,7 +129,7 @@ namespace AZ
if (!assetFound)
{
AZ_Error("AssetUtils", false, "Could not find asset [%s]", sourcePath.c_str());
AssetUtilsInternal::ReportIssue(reporting, AZStd::string::format("Could not find asset [%s]", sourcePath.c_str()).c_str());
return AZ::Failure();
}
else
@@ -131,10 +138,10 @@ namespace AZ
}
}
Outcome<Data::AssetId> MakeAssetId(const AZStd::string& originatingSourcePath, const AZStd::string& referencedSourceFilePath, uint32_t productSubId)
Outcome<Data::AssetId> MakeAssetId(const AZStd::string& originatingSourcePath, const AZStd::string& referencedSourceFilePath, uint32_t productSubId, TraceLevel reporting)
{
AZStd::string resolvedPath = ResolvePathReference(originatingSourcePath, referencedSourceFilePath);
return MakeAssetId(resolvedPath, productSubId);
return MakeAssetId(resolvedPath, productSubId, reporting);
}
} // namespace AssetUtils
} // namespace RPI
@@ -14,14 +14,14 @@ namespace AZ
{
namespace ColorUtils
{
enum ColorSpace : uint32_t
{
LinearSRGB,
SRGB
};
AzToolsFramework::ColorEditorConfiguration GetLinearRgbEditorConfig()
{
enum ColorSpace : uint32_t
{
LinearSRGB,
SRGB
};
AzToolsFramework::ColorEditorConfiguration configuration;
configuration.m_colorPickerDialogConfiguration = AzQtComponents::ColorPicker::Configuration::RGB;
@@ -59,6 +59,15 @@ namespace AZ
return configuration;
}
AzToolsFramework::ColorEditorConfiguration GetRgbEditorConfig()
{
AzToolsFramework::ColorEditorConfiguration configuration = GetLinearRgbEditorConfig();
configuration.m_propertyColorSpaceId = ColorSpace::SRGB;
return configuration;
}
} // namespace ColorPropertyEditorConfigurations
} // namespace RPI
} // namespace AZ
@@ -137,7 +137,10 @@ namespace AZ
}
else if (!m_luaSourceFile.empty())
{
auto loadOutcome = RPI::AssetUtils::LoadAsset<ScriptAsset>(materialTypeSourceFilePath, m_luaSourceFile);
// The sub ID for script assets must be explicit.
// LUA source files output a compiled as well as an uncompiled asset, sub Ids of 1 and 2.
auto loadOutcome =
RPI::AssetUtils::LoadAsset<ScriptAsset>(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId);
if (!loadOutcome)
{
AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str());
@@ -17,6 +17,7 @@
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonFileLoadContext.h>
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
@@ -115,10 +116,11 @@ namespace AZ
{
m_properties = AZStd::move(newPropertyGroups);
AZ_Warning("MaterialSourceData", false,
AZ_Warning(
"MaterialSourceData", false,
"This material is based on version '%u' of '%s', but the material type is now at version '%u'. "
"Automatic updates are available. Consider updating the .material source file.",
m_materialTypeVersion, m_materialType.c_str(), materialTypeSourceData.m_version);
"Automatic updates are available. Consider updating the .material source file: '%s'.",
m_materialTypeVersion, materialTypeFullPath.c_str(), materialTypeSourceData.m_version, materialSourceFilePath.data());
}
m_materialTypeVersion = materialTypeSourceData.m_version;
@@ -126,7 +128,8 @@ namespace AZ
return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates;
}
Outcome<Data::Asset<MaterialAsset> > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
Outcome<Data::Asset<MaterialAsset>> MaterialSourceData::CreateMaterialAsset(
Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
{
MaterialAssetCreator materialAssetCreator;
materialAssetCreator.SetElevateWarnings(elevateWarnings);
@@ -172,66 +175,7 @@ namespace AZ
materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames);
}
for (auto& group : m_properties)
{
for (auto& property : group.second)
{
MaterialPropertyId propertyId{ group.first, property.first };
if (!property.second.m_value.IsValid())
{
AZ_Warning("Material source data", false, "Source data for material property value is invalid.");
}
else
{
MaterialPropertyIndex propertyIndex = materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
if (propertyIndex.IsValid())
{
const MaterialPropertyDescriptor* propertyDescriptor = materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
switch (propertyDescriptor->GetDataType())
{
case MaterialPropertyDataType::Image:
{
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(materialSourceFilePath, property.second.m_value.GetValue<AZStd::string>());
if (imageAssetResult.IsSuccess())
{
auto& imageAsset = imageAssetResult.GetValue();
// Load referenced images when load material
imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
}
else
{
materialAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.second.m_value.GetValue<AZStd::string>().data());
}
}
break;
case MaterialPropertyDataType::Enum:
{
AZ::Name enumName = AZ::Name(property.second.m_value.GetValue<AZStd::string>());
uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName);
if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue)
{
materialAssetCreator.ReportError("Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr());
}
else
{
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
}
}
break;
default:
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
break;
}
}
else
{
materialAssetCreator.ReportWarning("Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data());
}
}
}
}
ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
Data::Asset<MaterialAsset> material;
if (materialAssetCreator.End(material))
@@ -244,5 +188,181 @@ namespace AZ
}
}
Outcome<Data::Asset<MaterialAsset>> MaterialSourceData::CreateMaterialAssetFromSourceData(
Data::AssetId assetId,
AZStd::string_view materialSourceFilePath,
bool elevateWarnings,
bool includeMaterialPropertyNames,
AZStd::unordered_set<AZStd::string>* sourceDependencies) const
{
const auto materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType);
const auto materialTypeAssetId = AssetUtils::MakeAssetId(materialTypeSourcePath, 0);
if (!materialTypeAssetId.IsSuccess())
{
AZ_Error("MaterialSourceData", false, "Failed to create material type asset ID: '%s'.", materialTypeSourcePath.c_str());
return Failure();
}
MaterialTypeSourceData materialTypeSourceData;
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData))
{
AZ_Error("MaterialSourceData", false, "Failed to load MaterialTypeSourceData: '%s'.", materialTypeSourcePath.c_str());
return Failure();
}
materialTypeSourceData.ResolveUvEnums();
const auto materialTypeAsset =
materialTypeSourceData.CreateMaterialTypeAsset(materialTypeAssetId.GetValue(), materialTypeSourcePath, elevateWarnings);
if (!materialTypeAsset.IsSuccess())
{
AZ_Error("MaterialSourceData", false, "Failed to create material type asset from source data: '%s'.", materialTypeSourcePath.c_str());
return Failure();
}
// Track all of the material and material type assets loaded while trying to create a material asset from source data. This will
// be used for evaluating circular dependencies and returned for external monitoring or other use.
AZStd::unordered_set<AZStd::string> dependencies;
dependencies.insert(materialSourceFilePath);
dependencies.insert(materialTypeSourcePath);
// Load and build a stack of MaterialSourceData from all of the parent materials in the hierarchy. Properties from the source
// data will be applied in reverse to the asset creator.
AZStd::vector<MaterialSourceData> parentSourceDataStack;
AZStd::string parentSourceRelPath = m_parentMaterial;
AZStd::string parentSourceAbsPath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentSourceRelPath);
while (!parentSourceRelPath.empty())
{
if (!dependencies.insert(parentSourceAbsPath).second)
{
AZ_Error("MaterialSourceData", false, "Detected circular dependency between materials: '%s' and '%s'.", materialSourceFilePath.data(), parentSourceAbsPath.c_str());
return Failure();
}
MaterialSourceData parentSourceData;
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentSourceAbsPath, parentSourceData))
{
AZ_Error("MaterialSourceData", false, "Failed to load MaterialSourceData for parent material: '%s'.", parentSourceAbsPath.c_str());
return Failure();
}
// Make sure that all materials in the hierarchy share the same material type
const auto parentTypeAssetId = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0);
if (!parentTypeAssetId)
{
AZ_Error("MaterialSourceData", false, "Parent material asset ID wasn't found: '%s'.", parentSourceAbsPath.c_str());
return Failure();
}
if (parentTypeAssetId.GetValue() != materialTypeAssetId.GetValue())
{
AZ_Error("MaterialSourceData", false, "This material and its parent material do not share the same material type.");
return Failure();
}
// Get the location of the next parent material and push the source data onto the stack
parentSourceRelPath = parentSourceData.m_parentMaterial;
parentSourceAbsPath = AssetUtils::ResolvePathReference(parentSourceAbsPath, parentSourceRelPath);
parentSourceDataStack.emplace_back(AZStd::move(parentSourceData));
}
// Create the material asset from all the previously loaded source data
MaterialAssetCreator materialAssetCreator;
materialAssetCreator.SetElevateWarnings(elevateWarnings);
materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames);
while (!parentSourceDataStack.empty())
{
parentSourceDataStack.back().ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
parentSourceDataStack.pop_back();
}
ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
Data::Asset<MaterialAsset> material;
if (materialAssetCreator.End(material))
{
if (sourceDependencies)
{
sourceDependencies->insert(dependencies.begin(), dependencies.end());
}
return Success(material);
}
return Failure();
}
void MaterialSourceData::ApplyPropertiesToAssetCreator(
AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const
{
for (auto& group : m_properties)
{
for (auto& property : group.second)
{
MaterialPropertyId propertyId{ group.first, property.first };
if (!property.second.m_value.IsValid())
{
materialAssetCreator.ReportWarning("Source data for material property value is invalid.");
}
else
{
MaterialPropertyIndex propertyIndex =
materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
if (propertyIndex.IsValid())
{
const MaterialPropertyDescriptor* propertyDescriptor =
materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
switch (propertyDescriptor->GetDataType())
{
case MaterialPropertyDataType::Image:
{
Data::Asset<ImageAsset> imageAsset;
MaterialUtils::GetImageAssetResult result = MaterialUtils::GetImageAssetReference(
imageAsset, materialSourceFilePath, property.second.m_value.GetValue<AZStd::string>());
if (result == MaterialUtils::GetImageAssetResult::Missing)
{
materialAssetCreator.ReportWarning(
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
property.second.m_value.GetValue<AZStd::string>().data());
}
imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
}
break;
case MaterialPropertyDataType::Enum:
{
AZ::Name enumName = AZ::Name(property.second.m_value.GetValue<AZStd::string>());
uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName);
if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue)
{
materialAssetCreator.ReportError(
"Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr());
}
else
{
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
}
}
break;
default:
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
break;
}
}
else
{
materialAssetCreator.ReportWarning(
"Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data());
}
}
}
}
}
} // namespace RPI
} // namespace AZ
@@ -300,48 +300,6 @@ namespace AZ
}
}
bool MaterialTypeSourceData::ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const
{
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is<uint32_t>())
{
const uint32_t index = propertyValue.GetValue<uint32_t>();
if (index >= propertyDefinition.m_enumValues.size())
{
AZ_Error("Material source data", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_name.c_str());
return false;
}
propertyValue = propertyDefinition.m_enumValues[index];
return true;
}
// Image asset references must be converted from asset IDs to a relative source file path
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Image && propertyValue.Is<Data::Asset<ImageAsset>>())
{
const Data::Asset<ImageAsset>& imageAsset = propertyValue.GetValue<Data::Asset<ImageAsset>>();
Data::AssetInfo imageAssetInfo;
if (imageAsset.GetId().IsValid())
{
bool result = false;
AZStd::string rootFilePath;
const AZStd::string platformName = ""; // Empty for default
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAssetInfoById,
imageAsset.GetId(), imageAsset.GetType(), platformName, imageAssetInfo, rootFilePath);
if (!result)
{
AZ_Error("Material source data", false, "Image asset could not be found for property: '%s'.", propertyDefinition.m_name.c_str());
return false;
}
}
propertyValue = imageAssetInfo.m_relativePath;
return true;
}
return true;
}
Outcome<Data::Asset<MaterialTypeAsset>> MaterialTypeSourceData::CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath, bool elevateWarnings) const
{
MaterialTypeAssetCreator materialTypeAssetCreator;
@@ -393,11 +351,12 @@ namespace AZ
for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection)
{
const auto& shaderFile = shaderRef.m_shaderFilePath;
const auto& shaderAsset = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0);
auto shaderAssetResult = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0);
if (shaderAsset)
if (shaderAssetResult)
{
auto optionsLayout = shaderAsset.GetValue()->GetShaderOptionGroupLayout();
auto shaderAsset = shaderAssetResult.GetValue();
auto optionsLayout = shaderAsset->GetShaderOptionGroupLayout();
ShaderOptionGroup options{ optionsLayout };
for (auto& iter : shaderRef.m_shaderOptionValues)
{
@@ -408,12 +367,11 @@ namespace AZ
}
materialTypeAssetCreator.AddShader(
shaderAsset.GetValue(), options.GetShaderVariantId(),
shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString<AZ::Name>() : shaderRef.m_shaderTag
);
shaderAsset, options.GetShaderVariantId(),
shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString<AZ::Name>() : shaderRef.m_shaderTag);
// Gather UV names
const ShaderInputContract& shaderInputContract = shaderAsset.GetValue()->GetInputContract();
const ShaderInputContract& shaderInputContract = shaderAsset->GetInputContract();
for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels)
{
const RHI::ShaderSemantic& semantic = channel.m_semantic;
@@ -493,15 +451,20 @@ namespace AZ
{
case MaterialPropertyDataType::Image:
{
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(materialTypeSourceFilePath, property.m_value.GetValue<AZStd::string>());
Data::Asset<ImageAsset> imageAsset;
if (imageAssetResult.IsSuccess())
MaterialUtils::GetImageAssetResult result = MaterialUtils::GetImageAssetReference(
imageAsset, materialTypeSourceFilePath, property.m_value.GetValue<AZStd::string>());
if (result == MaterialUtils::GetImageAssetResult::Missing)
{
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAssetResult.GetValue());
materialTypeAssetCreator.ReportError(
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
property.m_value.GetValue<AZStd::string>().data());
}
else
{
materialTypeAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.m_value.GetValue<AZStd::string>().data());
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
}
}
break;
@@ -28,25 +28,36 @@ namespace AZ
{
namespace MaterialUtils
{
Outcome<Data::Asset<ImageAsset>> GetImageAssetReference(AZStd::string_view materialSourceFilePath, const AZStd::string imageFilePath)
GetImageAssetResult GetImageAssetReference(Data::Asset<ImageAsset>& imageAsset, AZStd::string_view materialSourceFilePath, const AZStd::string imageFilePath)
{
imageAsset = {};
if (imageFilePath.empty())
{
// The image value was present but specified an empty string, meaning the texture asset should be explicitly cleared.
return AZ::Success(Data::Asset<ImageAsset>());
return GetImageAssetResult::Empty;
}
else
{
Outcome<Data::AssetId> imageAssetId = AssetUtils::MakeAssetId(materialSourceFilePath, imageFilePath, StreamingImageAsset::GetImageAssetSubId());
// We use TraceLevel::None because fallback textures are available and we'll return GetImageAssetResult::Missing below in that case.
// Callers of GetImageAssetReference will be responsible for logging warnings or errors as needed.
Outcome<Data::AssetId> imageAssetId = AssetUtils::MakeAssetId(materialSourceFilePath, imageFilePath, StreamingImageAsset::GetImageAssetSubId(), AssetUtils::TraceLevel::None);
if (!imageAssetId.IsSuccess())
{
return AZ::Failure();
}
else
{
Data::Asset<ImageAsset> unloadedImageAssetReference(imageAssetId.GetValue(), azrtti_typeid<StreamingImageAsset>(), imageFilePath);
return AZ::Success(unloadedImageAssetReference);
// When the AssetId cannot be found, we don't want to outright fail, because the runtime has mechanisms for displaying fallback textures which gives the
// user a better recovery workflow. On the other hand we can't just provide an empty/invalid Asset<ImageAsset> because that would be interpreted as simply
// no value was present and result in using no texture, and this would amount to a silent failure.
// So we use a randomly generated (well except for the "BADA55E7" bit ;) UUID which the runtime and tools will interpret as a missing asset and represent
// it as such.
static const Uuid InvalidAssetPlaceholderId = "{BADA55E7-1A1D-4940-B655-9D08679BD62F}";
imageAsset = Data::Asset<ImageAsset>{InvalidAssetPlaceholderId, azrtti_typeid<StreamingImageAsset>(), imageFilePath};
return GetImageAssetResult::Missing;
}
imageAsset = Data::Asset<ImageAsset>{imageAssetId.GetValue(), azrtti_typeid<StreamingImageAsset>(), imageFilePath};
return GetImageAssetResult::Found;
}
}
@@ -92,7 +92,7 @@ namespace AZ
/////////////////////////////////////////////////////////////////////
// Methods for all shader variant types
void ShaderVariantAssetCreator::SetBuildTimestamp(AZStd::sys_time_t buildTimestamp)
void ShaderVariantAssetCreator::SetBuildTimestamp(AZ::u64 buildTimestamp)
{
if (ValidateIsReady())
{
@@ -17,7 +17,8 @@
#include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/EventTrace.h>
AZ_DECLARE_BUDGET(RPI);
namespace AZ
{
@@ -90,7 +91,7 @@ namespace AZ
RHI::ResultCode Buffer::Init(BufferAsset& bufferAsset)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
RHI::ResultCode resultCode = RHI::ResultCode::Fail;
@@ -140,7 +141,7 @@ namespace AZ
if (bufferAsset.GetBuffer().size() > 0 && !initWithData)
{
AZ_TRACE_METHOD_NAME("Stream Upload");
AZ_PROFILE_SCOPE(RPI, "Stream Upload");
m_streamFence = RHI::Factory::Get().CreateFence();
if (m_streamFence)
{
@@ -16,7 +16,6 @@
#include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
#include <Atom/RPI.Reflect/Buffer/BufferAssetView.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Interface/Interface.h>
+356 -222
View File
@@ -19,10 +19,10 @@
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Debug/Timer.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/Job.h>
#include <AzCore/Task/TaskGraph.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <Atom_RPI_Traits_Platform.h>
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
@@ -34,7 +34,7 @@
//Enables more detailed profiling descriptions within the culling system, but adds some performance overhead.
//Enable this to more easily see which jobs are associated with which view.
//#define AZ_CULL_PROFILE_VERBOSE
//#define AZ_CULL_PROFILE_VERBOSE
namespace AZ
{
@@ -43,6 +43,7 @@ namespace AZ
AZ_CVAR(bool, r_CullInParallel, true, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(uint32_t, r_CullWorkPerBatch, 500, nullptr, ConsoleFunctorFlags::Null, "");
#ifdef AZ_CULL_DEBUG_ENABLED
void DebugDrawWorldCoordinateAxes(AuxGeomDraw* auxGeom)
{
auxGeom->DrawCylinder(Vector3(.5, .0, .0), Vector3(1, 0, 0), 0.02f, 1.0f, Colors::Red, AuxGeomDraw::DrawStyle::Solid, AuxGeomDraw::DepthTest::Off);
@@ -198,6 +199,7 @@ namespace AZ
AZ_Assert(false, "invalid frustum, cannot draw");
}
}
#endif //AZ_CULL_DEBUG_ENABLED
CullingDebugContext::~CullingDebugContext()
{
@@ -265,89 +267,73 @@ namespace AZ
return m_visScene->GetEntryCount();
}
class AddObjectsToViewJob final
: public Job
struct WorklistData
{
public:
AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0);
struct JobData
{
CullingDebugContext* m_debugCtx = nullptr;
const Scene* m_scene = nullptr;
View* m_view = nullptr;
Frustum m_frustum;
CullingDebugContext* m_debugCtx = nullptr;
const Scene* m_scene = nullptr;
View* m_view = nullptr;
Frustum m_frustum;
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
#endif
};
};
private:
const AZStd::shared_ptr<JobData> m_jobData;
CullingScene::WorkListType m_worklist;
static AZStd::shared_ptr<WorklistData> MakeWorklistData(
CullingDebugContext& debugCtx,
const Scene& scene,
View& view,
Frustum& frustum,
[[maybe_unused]] void* maskedOcclusionCulling)
{
AZStd::shared_ptr<WorklistData> worklistData = AZStd::make_shared<WorklistData>();
worklistData->m_debugCtx = &debugCtx;
worklistData->m_scene = &scene;
worklistData->m_view = &view;
worklistData->m_frustum = frustum;
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
worklistData->m_maskedOcclusionCulling = static_cast<MaskedOcclusionCulling*>(maskedOcclusionCulling);
#endif
return worklistData;
}
constexpr size_t WorkListCapacity = 5;
using WorkListType = AZStd::fixed_vector<AzFramework::IVisibilityScene::NodeData, WorkListCapacity>;
public:
AddObjectsToViewJob(const AZStd::shared_ptr<AddObjectsToViewJob::JobData>& jobData, CullingScene::WorkListType& worklist)
: Job(true, nullptr) //auto-deletes, no JobContext
, m_jobData(jobData)
, m_worklist(worklist)
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
static MaskedOcclusionCulling::CullingResult TestOcclusionCulling(
const AZStd::shared_ptr<WorklistData>& worklistData,
AzFramework::VisibilityEntry* visibleEntry);
#endif
static void ProcessWorklist(const AZStd::shared_ptr<WorklistData>& worklistData, const WorkListType& worklist)
{
AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process");
const View::UsageFlags viewFlags = worklistData->m_view->GetUsageFlags();
const RHI::DrawListMask drawListMask = worklistData->m_view->GetDrawListMask();
uint32_t numDrawPackets = 0;
uint32_t numVisibleCullables = 0;
AZ_Assert(worklist.size() > 0, "Received empty worklist in ProcessWorklist");
for (const AzFramework::IVisibilityScene::NodeData& nodeData : worklist)
{
}
//work function
void Process() override
{
AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process");
const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags();
const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask();
uint32_t numDrawPackets = 0;
uint32_t numVisibleCullables = 0;
for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist)
{
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds);
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
bool nodeIsContainedInFrustum =
!worklistData->m_debugCtx->m_enableFrustumCulling ||
ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds);
#ifdef AZ_CULL_PROFILE_VERBOSE
AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %d",
m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0);
AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %ds",
worklistData->m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? "true" : "false");
#endif
if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling)
if (nodeIsContainedInFrustum)
{
//Add all objects within this node to the view, without any extra culling
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
//Add all objects within this node to the view, without any extra culling
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
{
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
{
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
c->m_cullData.m_hideFlags & viewFlags ||
c->m_cullData.m_scene != m_jobData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
c->m_isHidden)
{
continue;
}
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
#endif
{
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
++numVisibleCullables;
c->m_isVisible = true;
}
}
}
}
}
else
{
//Do fine-grained culling before adding objects to the view
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
{
@@ -355,160 +341,200 @@ namespace AZ
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
c->m_cullData.m_hideFlags & viewFlags ||
c->m_cullData.m_scene != m_jobData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
c->m_cullData.m_scene != worklistData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
c->m_isHidden)
{
continue;
}
IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere);
if (res == IntersectResult::Exterior)
{
continue;
}
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb))
{
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
#endif
{
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
++numVisibleCullables;
c->m_isVisible = true;
}
}
}
}
}
if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName))
{
AZ_PROFILE_SCOPE(RPI, "debug draw culling");
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene);
if (auxGeomPtr)
{
//Draw the node bounds
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
// we have a few debug booleans to toggle which ones to draw.
if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes)
{
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
}
else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes)
{
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
}
//Draw bounds on individual objects
if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii)
{
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view);
++numVisibleCullables;
c->m_isVisible = true;
}
}
}
}
}
else
{
//Do fine-grained culling before adding objects to the view
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
{
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
c->m_cullData.m_hideFlags & viewFlags ||
c->m_cullData.m_scene != worklistData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
c->m_isHidden)
{
continue;
}
IntersectResult res = ShapeIntersection::Classify(worklistData->m_frustum, c->m_cullData.m_boundingSphere);
if (res == IntersectResult::Exterior)
{
continue;
}
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(worklistData->m_frustum, c->m_cullData.m_boundingObb))
{
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
#endif
{
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view);
++numVisibleCullables;
c->m_isVisible = true;
}
}
}
}
}
#ifdef AZ_CULL_DEBUG_ENABLED
if (worklistData->m_debugCtx->m_debugDraw && (worklistData->m_view->GetName() == worklistData->m_debugCtx->m_currentViewSelectionName))
{
AZ_PROFILE_SCOPE(RPI, "debug draw culling");
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(worklistData->m_scene);
if (auxGeomPtr)
{
//Draw the node bounds
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
// we have a few debug booleans to toggle which ones to draw.
if (nodeIsContainedInFrustum && worklistData->m_debugCtx->m_drawFullyVisibleNodes)
{
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
}
else if (!nodeIsContainedInFrustum && worklistData->m_debugCtx->m_drawPartiallyVisibleNodes)
{
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
}
//Draw bounds on individual objects
if (worklistData->m_debugCtx->m_drawBoundingBoxes || worklistData->m_debugCtx->m_drawBoundingSpheres || worklistData->m_debugCtx->m_drawLodRadii)
{
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
{
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
if (worklistData->m_debugCtx->m_drawBoundingBoxes)
{
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
if (m_jobData->m_debugCtx->m_drawBoundingBoxes)
{
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
}
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
}
if (m_jobData->m_debugCtx->m_drawBoundingSpheres)
{
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
}
if (worklistData->m_debugCtx->m_drawBoundingSpheres)
{
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
}
if (m_jobData->m_debugCtx->m_drawLodRadii)
{
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
c->m_lodData.m_lodSelectionRadius,
Color(1.0f, 0.5f, 0.0f, 0.3f), RPI::AuxGeomDraw::DrawStyle::Shaded);
}
if (worklistData->m_debugCtx->m_drawLodRadii)
{
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
c->m_lodData.m_lodSelectionRadius,
Color(1.0f, 0.5f, 0.0f, 0.3f), RPI::AuxGeomDraw::DrawStyle::Shaded);
}
}
}
}
}
}
if (m_jobData->m_debugCtx->m_enableStats)
{
CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view);
//no need for mutex here since these are all atomics
cullStats.m_numVisibleDrawPackets += numDrawPackets;
cullStats.m_numVisibleCullables += numVisibleCullables;
++cullStats.m_numJobs;
}
#endif
}
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry)
#ifdef AZ_CULL_DEBUG_ENABLED
if (worklistData->m_debugCtx->m_enableStats)
{
if (!m_jobData->m_maskedOcclusionCulling)
{
return MaskedOcclusionCulling::CullingResult::VISIBLE;
}
CullingDebugContext::CullStats& cullStats = worklistData->m_debugCtx->GetCullStatsForView(worklistData->m_view);
if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation()))
{
// camera is inside bounding volume
return MaskedOcclusionCulling::CullingResult::VISIBLE;
}
//no need for mutex here since these are all atomics
cullStats.m_numVisibleDrawPackets += numDrawPackets;
cullStats.m_numVisibleCullables += numVisibleCullables;
++cullStats.m_numJobs;
}
#endif //AZ_CULL_DEBUG_ENABLED
}
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
static MaskedOcclusionCulling::CullingResult TestOcclusionCulling(
const AZStd::shared_ptr<WorklistData>& worklistData,
AzFramework::VisibilityEntry* visibleEntry)
{
if (!worklistData->m_maskedOcclusionCulling)
{
return MaskedOcclusionCulling::CullingResult::VISIBLE;
}
// compute bounding volume corners
Vector4 corners[8];
corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
#ifdef AZ_CULL_PROFILE_VERBOSE
AZ_PROFILE_SCOPE(RPI, "TestOcclusionCulling");
#endif
// find min clip-space depth and NDC min/max
float minDepth = FLT_MAX;
float ndcMinX = FLT_MAX;
float ndcMinY = FLT_MAX;
float ndcMaxX = -FLT_MAX;
float ndcMaxY = -FLT_MAX;
for (uint32_t index = 0; index < 8; ++index)
{
minDepth = AZStd::min(minDepth, corners[index].GetW());
if (visibleEntry->m_boundingVolume.Contains(worklistData->m_view->GetCameraTransform().GetTranslation()))
{
// camera is inside bounding volume
return MaskedOcclusionCulling::CullingResult::VISIBLE;
}
// convert to NDC
corners[index] /= corners[index].GetW();
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
}
// compute bounding volume corners
Vector4 corners[8];
corners[0] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
corners[1] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
corners[2] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
corners[3] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
corners[4] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
corners[5] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
corners[6] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
corners[7] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
// find min clip-space depth and NDC min/max
float minDepth = FLT_MAX;
float ndcMinX = FLT_MAX;
float ndcMinY = FLT_MAX;
float ndcMaxX = -FLT_MAX;
float ndcMaxY = -FLT_MAX;
for (uint32_t index = 0; index < 8; ++index)
{
minDepth = AZStd::min(minDepth, corners[index].GetW());
if (minDepth < 0.00000001f)
{
return MaskedOcclusionCulling::VISIBLE;
return MaskedOcclusionCulling::CullingResult::VISIBLE;
}
// test against the occlusion buffer, which contains only the manually placed occlusion planes
return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
// convert to NDC
corners[index] /= corners[index].GetW();
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
}
// test against the occlusion buffer, which contains only the manually placed occlusion planes
return worklistData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
}
#endif
};
void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob)
void CullingScene::ProcessCullablesCommon(
const Scene& scene [[maybe_unused]],
View& view,
AZ::Frustum& frustum [[maybe_unused]],
void*& maskedOcclusionCulling [[maybe_unused]])
{
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullables() - %s", view.GetName().GetCStr());
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesCommon() - %s", view.GetName().GetCStr());
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
#ifdef AZ_CULL_DEBUG_ENABLED
if (m_debugCtx.m_freezeFrustums)
{
AZStd::lock_guard<AZStd::mutex> lock(m_debugCtx.m_frozenFrustumsMutex);
@@ -533,10 +559,10 @@ namespace AZ
CullingDebugContext::CullStats& cullStats = m_debugCtx.GetCullStatsForView(&view);
cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix();
}
#endif //AZ_CULL_DEBUG_ENABLED
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
// setup occlusion culling, if necessary
MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
if (maskedOcclusionCulling)
{
// frustum cull occlusion planes
@@ -578,23 +604,27 @@ namespace AZ
static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 };
// render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder
maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
static_cast<MaskedOcclusionCulling*>(maskedOcclusionCulling)->RenderTriangles(verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
}
}
#endif
}
void CullingScene::ProcessCullablesJobs(const Scene& scene, View& view, AZ::Job& parentJob)
{
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesJobs() - %s", view.GetName().GetCStr());
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
AZ::Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
void* maskedOcclusionCulling = nullptr;
ProcessCullablesCommon(scene, view, frustum, maskedOcclusionCulling);
WorkListType worklist;
AZStd::shared_ptr<AddObjectsToViewJob::JobData> jobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
jobData->m_debugCtx = &m_debugCtx;
jobData->m_scene = &scene;
jobData->m_view = &view;
jobData->m_frustum = frustum;
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
jobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
#endif
AZStd::shared_ptr<WorklistData> worklistData = MakeWorklistData(m_debugCtx, scene, view, frustum, maskedOcclusionCulling);
auto nodeVisitorLambda = [jobData, &parentJob, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
auto nodeVisitorLambda = [worklistData, &parentJob, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
{
AZ_PROFILE_SCOPE(RPI, "nodeVisitorLambda()");
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
@@ -606,8 +636,13 @@ namespace AZ
if (worklist.size() == worklist.capacity())
{
// capture worklistData & worklist by value
auto processWorklist = [worklistData, worklist]()
{
ProcessWorklist(worklistData, worklist);
};
//Kick off a job to process the (full) worklist
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
AZ::Job* job = AZ::CreateJobFunction(processWorklist, true);
worklist.clear();
parentJob.SetContinuation(job);
job->Start();
@@ -616,7 +651,7 @@ namespace AZ
if (m_debugCtx.m_enableFrustumCulling)
{
m_visScene->Enumerate(frustum, nodeVisitorLambda);
m_visScene->Enumerate(frustum, nodeVisitorLambda);
}
else
{
@@ -625,21 +660,76 @@ namespace AZ
if (worklist.size() > 0)
{
AZStd::shared_ptr<AddObjectsToViewJob::JobData> remainingJobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
remainingJobData->m_debugCtx = &m_debugCtx;
remainingJobData->m_scene = &scene;
remainingJobData->m_view = &view;
remainingJobData->m_frustum = frustum;
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
#endif
//Kick off a job to process any remaining workitems
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
// capture worklistData & worklist by value
auto processWorklist = [worklistData, worklist]()
{
ProcessWorklist(worklistData, worklist);
};
//Kick off a job to process the (full) worklist
AZ::Job* job = AZ::CreateJobFunction(processWorklist, true);
parentJob.SetContinuation(job);
job->Start();
}
}
void CullingScene::ProcessCullablesTG(const Scene& scene, View& view, AZ::TaskGraph& taskGraph)
{
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesTG() - %s", view.GetName().GetCStr());
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
AZ::Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
void* maskedOcclusionCulling = nullptr;
ProcessCullablesCommon(scene, view, frustum, maskedOcclusionCulling);
AZStd::unique_ptr<WorkListType> worklist = AZStd::make_unique<WorkListType>();
AZStd::shared_ptr<WorklistData> worklistData = MakeWorklistData(m_debugCtx, scene, view, frustum, maskedOcclusionCulling);
static const AZ::TaskDescriptor descriptor{ "AZ::RPI::ProcessWorklist", "Graphics" };
auto nodeVisitorLambda = [worklistData, &taskGraph, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
{
AZ_PROFILE_SCOPE(RPI, "nodeVisitorLambda()");
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
AZ_Assert(worklist->size() < worklist->capacity(), "we should always have room to push a node on the queue");
//Queue up a small list of work items (NodeData*) which will be pushed to a worker task once the queue is full.
//This reduces the number of tasks in flight, reducing task-system overhead.
worklist->emplace_back(AZStd::move(nodeData));
if (worklist->size() == worklist->capacity())
{
//Task takes ownership of the worklist unique ptr
taskGraph.AddTask( descriptor, [worklistData, worklist = AZStd::move(worklist)]()
{
ProcessWorklist(worklistData, *worklist.get());
// allow worklist to go out of scope and be deleted
});
worklist = AZStd::make_unique<WorkListType>();
}
};
if (m_debugCtx.m_enableFrustumCulling)
{
m_visScene->Enumerate(frustum, nodeVisitorLambda);
}
else
{
m_visScene->EnumerateNoCull(nodeVisitorLambda);
}
if (worklist->size() > 0)
{
//Task takes ownership of the worklist unique ptr
taskGraph.AddTask( descriptor, [worklistData, worklist = AZStd::move(worklist)]()
{
ProcessWorklist(worklistData, *worklist.get());
// allow worklist to go out of scope and be deleted
});
}
}
uint32_t AddLodDataToView(const Vector3& pos, const Cullable::LodData& lodData, RPI::View& view)
{
#ifdef AZ_CULL_PROFILE_DETAILED
@@ -702,6 +792,8 @@ namespace AZ
AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", m_parentScene->GetName().GetCStr()));
m_visScene = AZ::Interface<AzFramework::IVisibilitySystem>::Get()->CreateVisibilityScene(visSceneName);
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
#ifdef AZ_CULL_DEBUG_ENABLED
AZ_Assert(CountObjectsInScene() == 0, "The culling system should start with 0 entries in this scene.");
#endif
@@ -719,19 +811,35 @@ namespace AZ
}
}
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
void CullingScene::BeginCullingTaskGraph(const AZStd::vector<ViewPtr>& views)
{
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling");
m_cullDataConcurrencyCheck.soft_lock();
AZ::TaskGraph taskGraph;
AZ::TaskDescriptor beginCullingDescriptor{"RPI_CullingScene_BeginCullingView", "Graphics"};
for (auto& view : views)
{
taskGraph.AddTask(
beginCullingDescriptor,
[&view]()
{
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCullingTaskGraph");
view->BeginCulling();
});
}
m_debugCtx.ResetCullStats();
m_debugCtx.m_numCullablesInScene = GetNumCullables();
AZ::TaskGraphEvent waitForCompletion;
taskGraph.Submit(&waitForCompletion);
waitForCompletion.Wait();
}
void CullingScene::BeginCullingJobs(const AZStd::vector<ViewPtr>& views)
{
AZ::JobCompletion beginCullingCompletion;
for (auto& view : views)
{
const auto cullingLambda = [&view]()
{
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCullingJob");
view->BeginCulling();
};
@@ -741,7 +849,32 @@ namespace AZ
}
beginCullingCompletion.StartAndWaitForCompletion();
}
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
{
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling");
m_cullDataConcurrencyCheck.soft_lock();
m_debugCtx.ResetCullStats();
m_debugCtx.m_numCullablesInScene = GetNumCullables();
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
if(views.size() == 1) // avoid job overhead when only 1 job
{
views[0]->BeginCulling();
}
else if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive())
{
BeginCullingTaskGraph(views);
}
else
{
BeginCullingJobs(views);
}
#ifdef AZ_CULL_DEBUG_ENABLED
AuxGeomDrawPtr auxGeom;
if (m_debugCtx.m_debugDraw)
{
@@ -774,6 +907,7 @@ namespace AZ
m_debugCtx.m_frozenFrustums.clear();
}
}
#endif
}
void CullingScene::EndCulling()
@@ -141,6 +141,7 @@ namespace AZ
void DynamicDrawContext::InitVertexFormat(const AZStd::vector<VertexChannel>& vertexChannels)
{
AZ_Assert(!m_initialized, "Can't call InitVertexFormat after context was initialized (EndInit was called)");
AZ_Assert(m_pipelineState, "Can't call InitVertexFormat before InitShader is called with a valid shader");
m_perVertexDataSize = 0;
RHI::InputStreamLayoutBuilder layoutBuilder;
@@ -150,7 +151,10 @@ namespace AZ
bufferBuilder->Channel(channel.m_channel, channel.m_format);
m_perVertexDataSize += RHI::GetFormatSize(channel.m_format);
}
m_pipelineState->InputStreamLayout() = layoutBuilder.End();
if (m_pipelineState)
{
m_pipelineState->InputStreamLayout() = layoutBuilder.End();
}
}
void DynamicDrawContext::InitDrawListTag(RHI::DrawListTag drawListTag)
@@ -27,7 +27,6 @@
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Asset/AssetManager.h>
@@ -16,12 +16,13 @@
#include <Atom/RHI/Factory.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
// Enable this define to debug output streaming image initialization and expanding process.
//#define AZ_RPI_STREAMING_IMAGE_DEBUG_LOG
AZ_DECLARE_BUDGET(RPI);
namespace AZ
{
namespace RPI
@@ -111,7 +112,7 @@ namespace AZ
RHI::ResultCode StreamingImage::Init(StreamingImageAsset& imageAsset)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
Data::Instance<StreamingImagePool> pool;
if (imageAsset.GetPoolAssetId().IsValid())
@@ -12,9 +12,10 @@
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Jobs/Job.h>
AZ_DECLARE_BUDGET(RPI);
namespace AZ
{
namespace RPI
@@ -34,7 +35,7 @@ namespace AZ
void StreamingImageController::AttachImage(StreamingImage* image)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
AZ_Assert(image, "Image must not be null");
@@ -69,7 +70,7 @@ namespace AZ
void StreamingImageController::Update()
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
UpdateInternal(m_timestamp, m_contexts);
@@ -14,7 +14,6 @@
#include <Atom/RHI/Factory.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
namespace AZ
@@ -43,7 +42,7 @@ namespace AZ
RHI::ResultCode StreamingImagePool::Init(RHI::Device& device, StreamingImagePoolAsset& poolAsset)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
if (Validation::IsEnabled())
{
@@ -18,7 +18,6 @@
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AtomCore/Utils/ScopedValue.h>
@@ -57,7 +56,7 @@ namespace AZ
RHI::ResultCode Material::Init(MaterialAsset& materialAsset)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
ScopedValue isInitializing(&m_isInitializing, true, false);
@@ -234,7 +233,7 @@ namespace AZ
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnAssetReloaded %s", this, asset.GetHint().c_str());
Data::Asset<MaterialAsset> newMaterialAsset = { asset.GetAs<MaterialAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
Data::Asset<MaterialAsset> newMaterialAsset = Data::static_pointer_cast<MaterialAsset>(asset);
if (newMaterialAsset)
{
@@ -320,7 +319,7 @@ namespace AZ
bool Material::Compile()
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
if (NeedsCompile() && CanCompile())
{
@@ -610,7 +609,7 @@ namespace AZ
}
}
if (Data::Asset<StreamingImageAsset> streamingImageAsset = { imageAsset.GetAs<StreamingImageAsset>(), AZ::Data::AssetLoadBehavior::PreLoad })
if (Data::Asset<StreamingImageAsset> streamingImageAsset = Data::static_pointer_cast<StreamingImageAsset>(imageAsset))
{
Data::Instance<Image> image = StreamingImage::FindOrCreate(streamingImageAsset);
if (!image)
@@ -11,7 +11,6 @@
#include <Atom/RHI/Factory.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Debug/Timer.h>
#include <AzCore/Jobs/JobFunction.h>
@@ -12,7 +12,6 @@
#include <Atom/RHI/Factory.h>
#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
namespace AZ
@@ -52,7 +51,7 @@ namespace AZ
RHI::ResultCode ModelLod::Init(const Data::Asset<ModelLodAsset>& lodAsset, const Data::Asset<ModelAsset>& modelAsset)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
for (const ModelLodAsset::Mesh& mesh : lodAsset->GetMeshes())
{
@@ -389,7 +388,7 @@ namespace AZ
const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo,
Mesh& meshInstance)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
const Data::Asset<BufferAsset>& streamBufferAsset = streamBufferInfo.m_bufferAssetView.GetBufferAsset();
const Data::Instance<Buffer>& streamBuffer = Buffer::FindOrCreate(streamBufferAsset);
@@ -9,6 +9,7 @@
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
#include <Atom/RPI.Reflect/Pass/FullscreenTrianglePassData.h>
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
@@ -46,16 +47,19 @@ namespace AZ
void FullscreenTrianglePass::OnShaderReinitialized(const Shader&)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderReinitialized", this);
LoadShader();
}
void FullscreenTrianglePass::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>&)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderAssetReinitialized", this);
LoadShader();
}
void FullscreenTrianglePass::OnShaderVariantReinitialized(const ShaderVariant&)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderVariantReinitialized", this);
LoadShader();
}
@@ -129,6 +133,8 @@ namespace AZ
void FullscreenTrianglePass::InitializeInternal()
{
RenderPass::InitializeInternal();
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::InitializeInternal", this);
// This draw item purposefully does not reference any geometry buffers.
// Instead it's expected that the extended class uses a vertex shader
@@ -136,6 +142,12 @@ namespace AZ
RHI::DrawLinear draw = RHI::DrawLinear();
draw.m_vertexCount = 3;
if (m_shader == nullptr)
{
AZ_Error("PassSystem", false, "[FullscreenTrianglePass]: Shader not loaded!");
return;
}
RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
// [GFX TODO][ATOM-872] The pass should be able to drive the shader variant
@@ -9,11 +9,15 @@
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AtomCore/std/containers/vector_set.h>
#include <Atom/RPI.Public/Pass/SlowClearPass.h>
#include <Atom/RPI.Public/Pass/ParentPass.h>
#include <Atom/RPI.Public/Pass/PassAttachment.h>
#include <Atom/RPI.Public/Pass/PassDefines.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Reflect/Pass/SlowClearPassData.h>
#include <Atom/RPI.Reflect/Pass/PassName.h>
#include <Atom/RPI.Reflect/Pass/PassRequest.h>
namespace AZ
@@ -196,7 +200,7 @@ namespace AZ
}
}
// --- PassTemplate related functions ---
// --- Child creation ---
void ParentPass::CreatePassesFromTemplate()
{
@@ -217,6 +221,49 @@ namespace AZ
}
}
void ParentPass::CreateClearPassFromBinding(PassAttachmentBinding& binding, PassRequest& clearRequest)
{
if (binding.m_unifiedScopeDesc.m_loadStoreAction.m_loadAction == RHI::AttachmentLoadAction::Clear ||
binding.m_unifiedScopeDesc.m_loadStoreAction.m_loadActionStencil == RHI::AttachmentLoadAction::Clear)
{
// Set the name of the child clear pass as well as the binding it's connected to
clearRequest.m_passName = ConcatPassName(Name("Clear"), binding.m_name);
clearRequest.m_connections[0].m_attachmentRef.m_attachment = binding.m_name;
// Set the pass clear value to the clear value of the attachment binding
SlowClearPassData* clearData = static_cast<SlowClearPassData*>(clearRequest.m_passData.get());
clearData->m_clearValue = binding.m_unifiedScopeDesc.m_loadStoreAction.m_clearValue;
// Create and add the pass
Ptr<Pass> clearPass = PassSystemInterface::Get()->CreatePassFromRequest(&clearRequest);
if (clearPass)
{
AddChild(clearPass);
}
}
}
void ParentPass::CreateClearPassesFromBindings()
{
PassRequest clearRequest;
clearRequest.m_templateName = Name("SlowClearPassTemplate");
clearRequest.m_passData = AZStd::make_shared<SlowClearPassData>();
clearRequest.m_connections.push_back();
clearRequest.m_connections[0].m_localSlot = Name("ClearInputOutput");
clearRequest.m_connections[0].m_attachmentRef.m_pass = Name("Parent");
for (uint32_t idx = 0; idx < GetInputCount(); ++idx)
{
CreateClearPassFromBinding(GetInputBinding(idx), clearRequest);
}
for (uint32_t idx = 0; idx < GetInputOutputCount(); ++idx)
{
CreateClearPassFromBinding(GetInputOutputBinding(idx), clearRequest);
}
}
// --- Pass behavior functions ---
void ParentPass::CreateChildPasses()
@@ -229,6 +276,7 @@ namespace AZ
m_flags.m_alreadyCreatedChildren = true;
RemoveChildren();
CreateClearPassesFromBindings();
CreatePassesFromTemplate();
CreateChildPassesInternal();
@@ -147,6 +147,11 @@ namespace AZ
m_treeDepth = m_parent->m_treeDepth + 1;
m_path = ConcatPassName(m_parent->m_path, m_name);
m_flags.m_partOfHierarchy = m_parent->m_flags.m_partOfHierarchy;
if (m_state == PassState::Orphaned)
{
QueueForBuildAndInitialization();
}
}
void Pass::RemoveFromParent()
@@ -154,7 +159,7 @@ namespace AZ
AZ_RPI_PASS_ASSERT(m_parent != nullptr, "Trying to remove pass from parent but pointer to the parent pass is null.");
m_parent->RemoveChild(Ptr<Pass>(this));
m_queueState = PassQueueState::NoQueue;
m_state = PassState::Idle;
m_state = PassState::Orphaned;
}
void Pass::OnOrphan()
@@ -162,6 +167,8 @@ namespace AZ
m_parent = nullptr;
m_flags.m_partOfHierarchy = false;
m_treeDepth = 0;
m_queueState = PassQueueState::NoQueue;
m_state = PassState::Orphaned;
}
// --- Getters & Setters ---
@@ -1281,6 +1288,7 @@ namespace AZ
void Pass::FrameBegin(FramePrepareParams params)
{
AZ_PROFILE_SCOPE(RPI, "Pass::FrameBegin() - %s", m_path.GetCStr());
AZ_RPI_BREAK_ON_TARGET_PASS;
if (!IsEnabled())
@@ -1303,7 +1311,10 @@ namespace AZ
// FrameBeginInternal needs to be the last function be called in FrameBegin because its implementation expects
// all the attachments are imported to database (for example, ImageAttachmentPreview)
FrameBeginInternal(params);
{
AZ_PROFILE_SCOPE(RPI, "Pass::FrameBeginInternal()");
FrameBeginInternal(params);
}
// readback attachment with output state
UpdateReadbackAttachment(params, false);
@@ -19,6 +19,7 @@
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/RasterPass.h>
#include <Atom/RPI.Public/Pass/MSAAResolvePass.h>
#include <Atom/RPI.Public/Pass/SlowClearPass.h>
#include <Atom/RPI.Public/Pass/Specific/MSAAResolveFullScreenPass.h>
#include <Atom/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.h>
#include <Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h>
@@ -60,6 +61,7 @@ namespace AZ
{
AddPassCreator(Name("ParentPass"), &ParentPass::Create);
AddPassCreator(Name("RasterPass"), &RasterPass::Create);
AddPassCreator(Name("SlowClearPass"), &SlowClearPass::Create);
AddPassCreator(Name("CopyPass"), &CopyPass::Create);
AddPassCreator(Name("FullScreenTriangle"), &FullscreenTrianglePass::Create);
AddPassCreator(Name("ComputePass"), &ComputePass::Create);
@@ -90,13 +90,13 @@ namespace AZ
return filter;
}
void PassFilter::SetOwenrScene(const Scene* scene)
void PassFilter::SetOwnerScene(const Scene* scene)
{
m_ownerScene = scene;
UpdateFilterOptions();
}
void PassFilter::SetOwenrRenderPipeline(const RenderPipeline* renderPipeline)
void PassFilter::SetOwnerRenderPipeline(const RenderPipeline* renderPipeline)
{
m_ownerRenderPipeline = renderPipeline;
UpdateFilterOptions();
@@ -294,7 +294,7 @@ namespace AZ
void PassLibrary::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
// Handle pass asset reload
Data::Asset<PassAsset> passAsset = { asset.GetAs<PassAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
Data::Asset<PassAsset> passAsset = Data::static_pointer_cast<PassAsset>(asset);
if (passAsset && passAsset->GetPassTemplate())
{
LoadPassAsset(passAsset->GetPassTemplate()->m_name, passAsset, true);
@@ -9,7 +9,6 @@
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/Utils.h>
@@ -40,6 +39,7 @@
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Pass/RasterPassData.h>
#include <Atom/RPI.Reflect/Pass/RenderPassData.h>
#include <Atom/RPI.Reflect/Pass/SlowClearPassData.h>
namespace AZ
{
@@ -68,6 +68,7 @@ namespace AZ
PassSlot::Reflect(context);
PassData::Reflect(context);
SlowClearPassData::Reflect(context);
CopyPassData::Reflect(context);
RenderPassData::Reflect(context);
ComputePassData::Reflect(context);
@@ -312,7 +313,6 @@ namespace AZ
Pass::FramePrepareParams params{ &frameGraphBuilder };
{
AZ_PROFILE_SCOPE(RPI, "Pass: FrameBegin");
m_rootPass->FrameBegin(params);
}
}
@@ -146,6 +146,7 @@ namespace AZ
void RasterPass::UpdateDrawList()
{
AZ_PROFILE_SCOPE(RPI, "RasterPass::UpdateDrawList");
// DrawLists from dynamic draw
AZStd::vector<RHI::DrawListView> drawLists = DynamicDrawInterface::Get()->GetDrawListsForPass(this);
@@ -216,8 +217,6 @@ namespace AZ
void RasterPass::CompileResources(const RHI::FrameGraphCompileContext& context)
{
AZ_PROFILE_SCOPE(RPI, "RasterPass: CompileResources");
if (m_shaderResourceGroup == nullptr)
{
return;
@@ -276,7 +276,8 @@ namespace AZ
{
inputIndex = imageIndex;
}
const RHI::ImageView* imageView = context.GetImageView(attachment->GetAttachmentId(), binding.m_attachmentUsageIndex);
const RHI::ImageView* imageView =
context.GetImageView(attachment->GetAttachmentId(), binding.m_unifiedScopeDesc.GetImageViewDescriptor(), binding.m_scopeAttachmentUsage);
if (binding.m_shaderImageDimensionsNameIndex.HasName())
{
@@ -315,7 +316,7 @@ namespace AZ
{
inputIndex = bufferIndex;
}
const RHI::BufferView* bufferView = context.GetBufferView(attachment->GetAttachmentId(), binding.m_attachmentUsageIndex);
const RHI::BufferView* bufferView = context.GetBufferView(attachment->GetAttachmentId(), binding.m_scopeAttachmentUsage);
m_shaderResourceGroup->SetBufferView(RHI::ShaderInputBufferIndex(inputIndex), bufferView, arrayIndex);
++bufferIndex;
}
@@ -0,0 +1,45 @@
/*
* 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.Public/Pass/SlowClearPass.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Reflect/Pass/SlowClearPassData.h>
namespace AZ
{
namespace RPI
{
Ptr<SlowClearPass> SlowClearPass::Create(const PassDescriptor& descriptor)
{
Ptr<SlowClearPass> pass = aznew SlowClearPass(descriptor);
return pass;
}
SlowClearPass::SlowClearPass(const PassDescriptor& descriptor)
: RenderPass(descriptor)
{
const SlowClearPassData* passData = PassUtils::GetPassData<SlowClearPassData>(descriptor);
if (passData != nullptr)
{
m_clearValue = passData->m_clearValue;
}
}
void SlowClearPass::InitializeInternal()
{
RenderPass::InitializeInternal();
// Set clear value
AZ_Assert(GetInputOutputCount() > 0, "SlowClearPass: Missing InputOutput binding!");
RPI::PassAttachmentBinding& binding = GetInputOutputBinding(0);
binding.m_unifiedScopeDesc.m_loadStoreAction.m_clearValue = m_clearValue;
}
} // namespace RPI
} // namespace AZ
@@ -231,7 +231,7 @@ namespace AZ
void ImageAttachmentPreviewPass::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
Data::Asset<ShaderAsset> shaderAsset = { asset.GetAs<ShaderAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
Data::Asset<ShaderAsset> shaderAsset = Data::static_pointer_cast<ShaderAsset>(asset);
if (shaderAsset)
{
m_needsShaderLoad = true;
@@ -31,8 +31,8 @@
#include <Atom/RHI/Device.h>
#include <Atom/RHI.Reflect/PlatformLimitsDescriptor.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Time/ITime.h>
#include <AzFramework/Asset/AssetSystemBus.h>
@@ -268,21 +268,18 @@ namespace AZ
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
// Update tick time info
FillTickTimeInfo();
m_currentSimulationTime = GetCurrentTime();
for (auto& scene : m_scenes)
{
scene->Simulate(m_tickTime, m_simulationJobPolicy);
scene->Simulate(m_simulationJobPolicy, m_currentSimulationTime);
}
}
void RPISystem::FillTickTimeInfo()
float RPISystem::GetCurrentTime() const
{
AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime);
ScriptTimePoint currentTime;
AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
m_tickTime.m_currentGameTime = static_cast<float>(currentTime.GetSeconds());
const AZ::TimeUs currentSimulationTimeUs = AZ::GetRealElapsedTimeUs();
return AZ::TimeUsToSeconds(currentSimulationTimeUs);
}
void RPISystem::RenderTick()
@@ -301,7 +298,7 @@ namespace AZ
// [GFX TODO] We may parallel scenes' prepare render.
for (auto& scenePtr : m_scenes)
{
scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy);
scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime);
}
m_rhiSystem.FrameUpdate(
@@ -178,6 +178,10 @@ namespace AZ
pipelineViews.m_views.resize(1);
}
ViewPtr previousView = pipelineViews.m_views[0];
if (view)
{
view->OnAddToRenderPipeline();
}
pipelineViews.m_views[0] = view;
if (previousView)
@@ -238,6 +242,7 @@ namespace AZ
pipelineViews.m_type = PipelineViewType::Transient;
}
view->SetPassesByDrawList(&pipelineViews.m_passesByDrawList);
view->OnAddToRenderPipeline();
pipelineViews.m_views.push_back(view);
}
}
@@ -375,7 +380,7 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId);
}
void RenderPipeline::OnStartFrame([[maybe_unused]] const TickTimeInfo& tick)
void RenderPipeline::OnStartFrame([[maybe_unused]] float time)
{
AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");
+78 -60
View File
@@ -18,7 +18,6 @@
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/View.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/JobEmpty.h>
@@ -44,6 +43,9 @@ namespace AZ
{
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName());
// Set value for constants defined in SceneTimeSrg.azsli
scene->m_timeInputIndex = scene->m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
}
scene->m_name = sceneDescriptor.m_nameId;
@@ -111,7 +113,8 @@ namespace AZ
{
if (m_taskGraphActive)
{
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
m_simulationFinishedTGEvent.reset();
}
else
{
@@ -381,12 +384,14 @@ namespace AZ
simulationTGDesc,
[this, featureProcessor]()
{
featureProcessor->Simulate(m_simulatePacket);
FeatureProcessor::SimulatePacket jobPacket = m_simulatePacket;
jobPacket.m_parentJob = nullptr;
featureProcessor->Simulate(jobPacket);
});
}
simulationTG.Detach();
m_simulationFinishedWorkActive = true;
simulationTG.Submit(&m_simulationFinishedTGEvent);
m_simulationFinishedTGEvent = AZStd::make_unique<TaskGraphEvent>();
simulationTG.Submit(m_simulationFinishedTGEvent.get());
}
void Scene::SimulateJobs()
@@ -397,10 +402,11 @@ namespace AZ
for (FeatureProcessorPtr& fp : m_featureProcessors)
{
FeatureProcessor* featureProcessor = fp.get();
const auto jobLambda = [this, featureProcessor]()
const auto jobLambda = [this, featureProcessor](AZ::Job& owner)
{
featureProcessor->Simulate(m_simulatePacket);
FeatureProcessor::SimulatePacket jobPacket = m_simulatePacket;
jobPacket.m_parentJob = &owner;
featureProcessor->Simulate(jobPacket);
};
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
@@ -410,16 +416,17 @@ namespace AZ
//[GFX TODO]: the completion job should start here
}
void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
void Scene::Simulate(RHI::JobPolicy jobPolicy, float simulationTime)
{
AZ_PROFILE_SCOPE(RPI, "Scene: Simulate");
m_simulationTime = tickInfo.m_currentGameTime;
m_simulationTime = simulationTime;
// If previous simulation job wasn't done, wait for it to finish.
if (m_taskGraphActive)
{
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
m_simulationFinishedTGEvent.reset();
}
else
{
@@ -449,17 +456,14 @@ namespace AZ
}
}
void Scene::WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn )
void Scene::WaitAndCleanTGEvent(AZStd::unique_ptr<AZ::TaskGraphEvent>&& completionTGEvent)
{
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob");
if (!workToWaitOn || workToWaitOn->load())
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanTGEvent");
if (completionTGEvent)
{
completionTGEvent.Wait();
}
if (workToWaitOn)
{
workToWaitOn->store(false);
completionTGEvent->Wait();
}
// allow completionTGEvent to go out of scope and be deleted
}
void Scene::WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob)
@@ -483,11 +487,9 @@ namespace AZ
{
if (m_srg)
{
// Set value for constants defined in SceneTimeSrg.azsli
RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
if (timeIndex.IsValid())
if (m_timeInputIndex.IsValid())
{
m_srg->SetConstant(timeIndex, m_simulationTime);
m_srg->SetConstant(m_timeInputIndex, m_simulationTime);
}
// signal any handlers to update values for their partial scene srg
@@ -499,12 +501,12 @@ namespace AZ
void Scene::CollectDrawPacketsTaskGraph()
{
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
AZ_PROFILE_SCOPE(RPI, "CollectDrawPacketsTaskGraph");
AZ::TaskGraphEvent collectDrawPacketsTGEvent;
static const AZ::TaskDescriptor collectDrawPacketsTGDesc{"RPI_Scene_PrepareRender_CollectDrawPackets", "Graphics"};
AZ::TaskGraph collectDrawPacketsTG;
// Launch FeatureProcessor::Render() jobs
// Launch FeatureProcessor::Render() taskgraphs
for (auto& fp : m_featureProcessors)
{
collectDrawPacketsTG.AddTask(
@@ -518,34 +520,50 @@ namespace AZ
collectDrawPacketsTG.Submit(&collectDrawPacketsTGEvent);
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal)
bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
const bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
m_cullingScene->BeginCulling(m_renderPacket.m_views);
AZ::JobCompletion processCullablesCompletion;
for (ViewPtr& viewPtr : m_renderPacket.m_views)
static const AZ::TaskDescriptor processCullablesDescriptor{"AZ::RPI::Scene::ProcessCullables", "Graphics"};
AZ::TaskGraphEvent processCullablesTGEvent;
AZ::TaskGraph processCullablesTG;
if (parallelOctreeTraversal)
{
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
{
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
},
true, nullptr); //auto-deletes
if (parallelOctreeTraversal)
for (ViewPtr& viewPtr : m_renderPacket.m_views)
{
processCullablesJob->SetDependent(&processCullablesCompletion);
processCullablesJob->Start();
}
else
{
processCullablesJob->StartAndWaitForCompletion();
processCullablesTG.AddTask(processCullablesDescriptor, [this, &viewPtr, &processCullablesTGEvent]()
{
AZ::TaskGraph subTaskGraph;
m_cullingScene->ProcessCullablesTG(*this, *viewPtr, subTaskGraph);
if (!subTaskGraph.IsEmpty())
{
subTaskGraph.Detach();
subTaskGraph.Submit(&processCullablesTGEvent);
}
});
}
}
else
{
for (ViewPtr& viewPtr : m_renderPacket.m_views)
{
m_cullingScene->ProcessCullablesTG(*this, *viewPtr, processCullablesTG);
}
}
bool processCullablesHasWork = !processCullablesTG.IsEmpty();
if (processCullablesHasWork)
{
processCullablesTG.Submit(&processCullablesTGEvent);
}
WaitTGEvent(collectDrawPacketsTGEvent);
processCullablesCompletion.StartAndWaitForCompletion();
collectDrawPacketsTGEvent.Wait();
if (processCullablesHasWork) // skip the wait if there is no work to do
{
processCullablesTGEvent.Wait();
}
}
void Scene::CollectDrawPacketsJobs()
{
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
AZ_PROFILE_SCOPE(RPI, "CollectDrawPacketsJobs");
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
// Launch FeatureProcessor::Render() jobs
@@ -562,15 +580,16 @@ namespace AZ
}
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs)
const bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
m_cullingScene->BeginCulling(m_renderPacket.m_views);
for (ViewPtr& viewPtr : m_renderPacket.m_views)
{
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
{
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
m_cullingScene->ProcessCullablesJobs(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
},
true, nullptr); //auto-deletes
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
if (parallelOctreeTraversal)
{
processCullablesJob->SetDependent(collectDrawPacketsCompletion);
processCullablesJob->Start();
@@ -594,13 +613,13 @@ namespace AZ
{
finalizeDrawListsTG.AddTask(
finalizeDrawListsTGDesc,
[view]()
[view, &finalizeDrawListsTGEvent]()
{
view->FinalizeDrawLists();
view->FinalizeDrawListsTG(finalizeDrawListsTGEvent);
});
}
finalizeDrawListsTG.Submit(&finalizeDrawListsTGEvent);
WaitTGEvent(finalizeDrawListsTGEvent);
finalizeDrawListsTGEvent.Wait();
}
void Scene::FinalizeDrawListsJobs()
@@ -608,9 +627,9 @@ namespace AZ
AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion();
for (auto& view : m_renderPacket.m_views)
{
const auto finalizeDrawListsLambda = [view]()
const auto finalizeDrawListsLambda = [view](AZ::Job& job)
{
view->FinalizeDrawLists();
view->FinalizeDrawListsJob(&job);
};
AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes
@@ -620,13 +639,14 @@ namespace AZ
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
}
void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
void Scene::PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime)
{
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
if (m_taskGraphActive)
{
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
m_simulationFinishedTGEvent.reset();
}
else
{
@@ -644,7 +664,7 @@ namespace AZ
if (pipeline->NeedsRender())
{
activePipelines.push_back(pipeline);
pipeline->OnStartFrame(tickInfo);
pipeline->OnStartFrame(simulationTime);
}
}
}
@@ -717,20 +737,19 @@ namespace AZ
// Add dynamic draw data for all the views
if (m_dynamicDrawSystem)
{
AZ_PROFILE_SCOPE(RPI, "DynamicDraw SubmitDrawData");
m_dynamicDrawSystem->SubmitDrawData(this, m_renderPacket.m_views);
}
}
{
AZ_PROFILE_BEGIN(RPI, "FinalizeDrawLists");
if (jobPolicy == RHI::JobPolicy::Serial)
AZ_PROFILE_SCOPE(RPI, "FinalizeDrawLists");
if (jobPolicy == RHI::JobPolicy::Serial ||
m_renderPacket.m_views.size() <= 1) // FinalizeDrawListsX both immediately wait for the job to complete, skip job if only 1 job would be generated
{
for (auto& view : m_renderPacket.m_views)
{
view->FinalizeDrawLists();
view->FinalizeDrawListsJob(nullptr);
}
AZ_PROFILE_END(RPI);
}
else
{
@@ -742,7 +761,6 @@ namespace AZ
{
FinalizeDrawListsJobs();
}
AZ_PROFILE_END(RPI);
}
}
@@ -15,6 +15,8 @@
#include <Atom/RPI.Public/Shader/ShaderSystemInterface.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Component/TickBus.h>
namespace AZ
{
@@ -96,8 +98,7 @@ namespace AZ
RHI::ResultCode Shader::Init(ShaderAsset& shaderAsset)
{
Data::AssetBus::Handler::BusDisconnect();
ShaderReloadNotificationBus::Handler::BusDisconnect();
Data::AssetBus::MultiHandler::BusDisconnect();
ShaderVariantFinderNotificationBus::Handler::BusDisconnect();
RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get();
@@ -112,7 +113,8 @@ namespace AZ
AZStd::unique_lock<decltype(m_variantCacheMutex)> lock(m_variantCacheMutex);
m_shaderVariants.clear();
}
m_rootVariant.Init(Data::Asset<ShaderAsset>{&shaderAsset, AZ::Data::AssetLoadBehavior::PreLoad}, shaderAsset.GetRootVariant(m_supervariantIndex), m_supervariantIndex);
auto rootShaderVariantAsset = shaderAsset.GetRootVariant(m_supervariantIndex);
m_rootVariant.Init(m_asset, rootShaderVariantAsset, m_supervariantIndex);
if (m_pipelineLibraryHandle.IsNull())
{
@@ -146,8 +148,8 @@ namespace AZ
}
ShaderVariantFinderNotificationBus::Handler::BusConnect(m_asset.GetId());
Data::AssetBus::Handler::BusConnect(m_asset.GetId());
ShaderReloadNotificationBus::Handler::BusConnect(m_asset.GetId());
Data::AssetBus::MultiHandler::BusConnect(rootShaderVariantAsset.GetId());
Data::AssetBus::MultiHandler::BusConnect(m_asset.GetId());
return RHI::ResultCode::Success;
}
@@ -155,8 +157,7 @@ namespace AZ
void Shader::Shutdown()
{
ShaderVariantFinderNotificationBus::Handler::BusDisconnect();
Data::AssetBus::Handler::BusDisconnect();
ShaderReloadNotificationBus::Handler::BusDisconnect();
Data::AssetBus::MultiHandler::BusDisconnect();
if (m_pipelineLibraryHandle.IsValid())
{
@@ -181,14 +182,52 @@ namespace AZ
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnAssetReloaded %s", this, asset.GetHint().c_str());
if (asset->GetId() == m_asset->GetId())
if (asset.GetAs<ShaderVariantAsset>())
{
Data::Asset<ShaderAsset> newAsset = { asset.GetAs<ShaderAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
AZ_Assert(newAsset, "Reloaded ShaderAsset is null");
m_reloadedRootShaderVariantAsset = Data::static_pointer_cast<ShaderVariantAsset>(asset);
if (m_asset->m_buildTimestamp == m_reloadedRootShaderVariantAsset->GetBuildTimestamp())
{
Init(*m_asset.Get());
ShaderReloadNotificationBus::Event(asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this);
}
return;
}
Init(*newAsset.Get());
if (asset.GetAs<ShaderAsset>())
{
m_asset = Data::static_pointer_cast<ShaderAsset>(asset);
if (!m_reloadedRootShaderVariantAsset.IsReady())
{
// Do nothing, as We should not re-initilize until the root shader variant asset has been reloaded.
return;
}
AZ_Assert(m_asset->m_buildTimestamp == m_reloadedRootShaderVariantAsset->GetBuildTimestamp(),
"shaderAsset '%s' timeStamp=%lld, but Root ShaderVariantAsset timeStamp=%lld", m_asset.GetHint().c_str(),
m_asset->m_buildTimestamp, m_reloadedRootShaderVariantAsset->GetBuildTimestamp());
m_asset->UpdateRootShaderVariantAsset(m_supervariantIndex, m_reloadedRootShaderVariantAsset);
m_reloadedRootShaderVariantAsset = {}; // Clear the temporary reference.
if (ShaderReloadDebugTracker::IsEnabled())
{
auto makeTimeString = [](AZ::u64 timestamp, AZ::u64 now)
{
AZ::u64 elapsedMillis = now - timestamp;
double elapsedSeconds = aznumeric_cast<double>(elapsedMillis / 1'000);
AZStd::string timeString = AZStd::string::format("%lld (%f seconds ago)", timestamp, elapsedSeconds);
return timeString;
};
AZStd::sys_time_t now = AZStd::GetTimeNowMicroSecond();
const auto shaderVariantAsset = m_asset->GetRootVariant();
ShaderReloadDebugTracker::Printf("{%p}->Shader::OnAssetReloaded for shader '%s' [build time %s] found variant '%s' [build time %s]", this,
m_asset.GetHint().c_str(), makeTimeString(m_asset->m_buildTimestamp, now).c_str(),
shaderVariantAsset.GetHint().c_str(), makeTimeString(shaderVariantAsset->GetBuildTimestamp(), now).c_str());
}
Init(*m_asset.Get());
ShaderReloadNotificationBus::Event(asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this);
}
}
///////////////////////////////////////////////////////////////////////
@@ -253,23 +292,6 @@ namespace AZ
ShaderReloadNotificationBus::Event(m_asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, updatedVariant);
}
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// ShaderReloadNotificationBus overrides...
void Shader::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>& shaderAsset)
{
// When reloads occur, it's possible for old Asset objects to hang around and report reinitialization,
// so we can reduce unnecessary reinitialization in that case.
if (shaderAsset.Get() == m_asset.Get())
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str());
Init(*m_asset.Get());
ShaderReloadNotificationBus::Event(shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this);
}
}
///////////////////////////////////////////////////////////////////
ConstPtr<RHI::PipelineLibraryData> Shader::LoadPipelineLibrary() const
{
@@ -320,6 +342,30 @@ namespace AZ
}
const ShaderVariant& Shader::GetVariant(ShaderVariantStableId shaderVariantStableId)
{
const ShaderVariant& variant = GetVariantInternal(shaderVariantStableId);
if (ShaderReloadDebugTracker::IsEnabled())
{
auto makeTimeString = [](AZStd::sys_time_t timestamp, AZStd::sys_time_t now)
{
AZStd::sys_time_t elapsedMicroseconds = now - timestamp;
double elapsedSeconds = aznumeric_cast<double>(elapsedMicroseconds / 1'000'000);
AZStd::string timeString = AZStd::string::format("%lld (%f seconds ago)", timestamp, elapsedSeconds);
return timeString;
};
AZStd::sys_time_t now = AZStd::GetTimeNowMicroSecond();
ShaderReloadDebugTracker::Printf("{%p}->Shader::GetVariant for shader '%s' [build time %s] found variant '%s' [build time %s]", this,
m_asset.GetHint().c_str(), makeTimeString(m_asset->GetBuildTimestamp(), now).c_str(),
variant.GetShaderVariantAsset().GetHint().c_str(), makeTimeString(variant.GetShaderVariantAsset()->GetBuildTimestamp(), now).c_str());
}
return variant;
}
const ShaderVariant& Shader::GetVariantInternal(ShaderVariantStableId shaderVariantStableId)
{
if (!shaderVariantStableId.IsValid() || shaderVariantStableId == ShaderAsset::RootShaderVariantStableId)
{
@@ -336,7 +382,7 @@ namespace AZ
// reloaded, but some (or all) shader variants haven't been built yet. Since we want to use the latest version of the
// shader code, ignore the old variants and fall back to the newer root variant instead. There's no need to report a
// warning here because m_asset->GetVariant below will report one.
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
if (findIt->second.GetBuildTimestamp() >= m_asset->GetBuildTimestamp())
{
return findIt->second;
}
@@ -359,7 +405,7 @@ namespace AZ
auto findIt = m_shaderVariants.find(shaderVariantStableId);
if (findIt != m_shaderVariants.end())
{
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
if (findIt->second.GetBuildTimestamp() >= m_asset->GetBuildTimestamp())
{
return findIt->second;
}
@@ -7,24 +7,71 @@
*/
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
#include <AzCore/Module/Environment.h>
namespace AZ
{
namespace RPI
{
bool ShaderReloadDebugTracker::s_enabled = false;
int ShaderReloadDebugTracker::s_indent = 0;
namespace ShaderReloadDebugTrackerInternal
{
static constexpr char EnabledVariableName[] = "ShaderReloadDebugTracker enabled";
static constexpr char IndentVariableName[] = "ShaderReloadDebugTracker indent";
static EnvironmentVariable<bool> s_enabled;
static EnvironmentVariable<int> s_indent;
}
void ShaderReloadDebugTracker::Init()
{
MakeReady();
}
void ShaderReloadDebugTracker::Shutdown()
{
ShaderReloadDebugTrackerInternal::s_enabled.Reset();
ShaderReloadDebugTrackerInternal::s_indent.Reset();
}
void ShaderReloadDebugTracker::MakeReady()
{
if (!ShaderReloadDebugTrackerInternal::s_enabled.IsValid())
{
ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::CreateVariable<bool>(AZ::Crc32(ShaderReloadDebugTrackerInternal::EnabledVariableName), false);
ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::CreateVariable<int>(AZ::Crc32(ShaderReloadDebugTrackerInternal::IndentVariableName), 0);
}
}
bool ShaderReloadDebugTracker::IsEnabled()
{
#ifdef AZ_ENABLE_SHADER_RELOAD_DEBUG_TRACKER
MakeReady();
// Set this to true in the debugger to turn on hot reload tracing.
// If needed, we could hook this up to a CVar.
return s_enabled;
return ShaderReloadDebugTrackerInternal::s_enabled.Get();
#else
return false;
#endif
}
void ShaderReloadDebugTracker::AddIndent()
{
MakeReady();
ShaderReloadDebugTrackerInternal::s_indent.Get() += IndentSpaces;
}
void ShaderReloadDebugTracker::RemoveIndent()
{
MakeReady();
ShaderReloadDebugTrackerInternal::s_indent.Get() -= IndentSpaces;
}
int ShaderReloadDebugTracker::GetIndent()
{
MakeReady();
return ShaderReloadDebugTrackerInternal::s_indent.Get();
}
ShaderReloadDebugTracker::ScopedSection::~ScopedSection()
{
@@ -10,7 +10,6 @@
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <AzCore/Debug/EventTrace.h>
#include <AtomCore/Instance/InstanceDatabase.h>
namespace AZ
@@ -75,8 +74,6 @@ namespace AZ
RHI::ResultCode ShaderResourceGroup::Init(ShaderAsset& shaderAsset, const SupervariantIndex& supervariantIndex, const AZ::Name& srgName)
{
AZ_TRACE_METHOD();
const auto& lay = shaderAsset.FindShaderResourceGroupLayout(srgName, supervariantIndex);
m_layout = lay.get();
@@ -10,6 +10,7 @@
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroupPool.h>
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
@@ -86,10 +87,13 @@ namespace AZ
};
Data::InstanceDatabase<ShaderResourceGroupPool>::Create(azrtti_typeid<ShaderResourceGroupPool>(), handler, false);
}
ShaderReloadDebugTracker::Init();
}
void ShaderSystem::Shutdown()
{
ShaderReloadDebugTracker::Shutdown();
Data::InstanceDatabase<Shader>::Destroy();
Data::InstanceDatabase<ShaderResourceGroup>::Destroy();
Data::InstanceDatabase<ShaderResourceGroupPool>::Destroy();
@@ -22,24 +22,20 @@ namespace AZ
const Data::Asset<ShaderAsset>& shaderAsset,
const Data::Asset<ShaderVariantAsset>& shaderVariantAsset,
SupervariantIndex supervariantIndex)
{
{
m_shaderAsset = shaderAsset;
m_shaderVariantAsset = shaderVariantAsset;
m_supervariantIndex = supervariantIndex;
m_pipelineStateType = shaderAsset->GetPipelineStateType();
m_pipelineLayoutDescriptor = shaderAsset->GetPipelineLayoutDescriptor(supervariantIndex);
m_shaderVariantAsset = shaderVariantAsset;
m_renderStates = &shaderAsset->GetRenderStates(supervariantIndex);
m_supervariantIndex = supervariantIndex;
Data::AssetBus::MultiHandler::BusDisconnect();
Data::AssetBus::MultiHandler::BusConnect(shaderAsset.GetId());
Data::AssetBus::MultiHandler::BusConnect(shaderVariantAsset.GetId());
m_shaderAsset = shaderAsset;
return true;
}
ShaderVariant::~ShaderVariant()
{
Data::AssetBus::MultiHandler::BusDisconnect();
}
void ShaderVariant::ConfigurePipelineState(RHI::PipelineStateDescriptor& descriptor) const
@@ -82,25 +78,5 @@ namespace AZ
}
}
void ShaderVariant::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderVariant::OnAssetReloaded %s", this, asset.GetHint().c_str());
if (asset.GetAs<ShaderVariantAsset>())
{
Data::Asset<ShaderVariantAsset> shaderVariantAsset = { asset.GetAs<ShaderVariantAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
Init(m_shaderAsset, shaderVariantAsset, m_supervariantIndex);
ShaderReloadNotificationBus::Event(m_shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, *this);
}
if (asset.GetAs<ShaderAsset>())
{
Data::Asset<ShaderAsset> shaderAsset = { asset.GetAs<ShaderAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
Init(shaderAsset, m_shaderVariantAsset, m_supervariantIndex);
ShaderReloadNotificationBus::Event(m_shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, *this);
}
}
} // namespace RPI
} // namespace AZ
+139 -32
View File
@@ -19,6 +19,9 @@
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Jobs/JobCompletion.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Task/TaskGraph.h>
#include <Atom_RPI_Traits_Platform.h>
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
@@ -47,18 +50,14 @@ namespace AZ
{
AZ_Assert(!name.IsEmpty(), "invalid name");
// Set default matrixes.
// Set default matrices
SetWorldToViewMatrix(AZ::Matrix4x4::CreateIdentity());
AZ::Matrix4x4 viewToClipMatrix;
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, 1, 0.1f, 1000.f, true);
SetViewToClipMatrix(viewToClipMatrix);
Data::Asset<ShaderAsset> viewSrgShaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
TryCreateShaderResourceGroup();
if (viewSrgShaderAsset.IsReady())
{
m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgShaderAsset, RPISystemInterface::Get()->GetViewSrgLayout()->GetName());
}
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
m_maskedOcclusionCulling = MaskedOcclusionCulling::Create();
m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight);
@@ -125,6 +124,7 @@ namespace AZ
m_worldToViewMatrix = worldToView;
m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix;
m_clipToWorldMatrix = m_worldToClipMatrix.GetInverseFull();
m_onWorldToViewMatrixChange.Signal(m_worldToViewMatrix);
m_onWorldToClipMatrixChange.Signal(m_worldToClipMatrix);
@@ -162,6 +162,7 @@ namespace AZ
m_worldToViewMatrix = m_viewToWorldMatrix.GetInverseFast();
m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix;
m_clipToWorldMatrix = m_worldToClipMatrix.GetInverseFull();
// Only signal an update when there is a change, otherwise this might block
// user input from changing the value.
@@ -177,6 +178,7 @@ namespace AZ
m_viewToClipMatrix = viewToClip;
m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix;
m_clipToWorldMatrix = m_worldToClipMatrix.GetInverseFull();
// Update z depth constant simultaneously
// zNear -> n, zFar -> f
@@ -217,6 +219,16 @@ namespace AZ
return m_viewToWorldMatrix;
}
AZ::Matrix3x4 View::GetWorldToViewMatrixAsMatrix3x4() const
{
return AZ::Matrix3x4::UnsafeCreateFromMatrix4x4(m_worldToViewMatrix);
}
AZ::Matrix3x4 View::GetViewToWorldMatrixAsMatrix3x4() const
{
return AZ::Matrix3x4::UnsafeCreateFromMatrix4x4(m_viewToWorldMatrix);
}
const AZ::Matrix4x4& View::GetViewToClipMatrix() const
{
return m_viewToClipMatrix;
@@ -227,6 +239,11 @@ namespace AZ
return m_worldToClipMatrix;
}
const AZ::Matrix4x4& View::GetClipToWorldMatrix() const
{
return m_clipToWorldMatrix;
}
bool View::HasDrawListTag(RHI::DrawListTag drawListTag)
{
return drawListTag.IsValid() && m_drawListMask[drawListTag.GetIndex()];
@@ -237,24 +254,79 @@ namespace AZ
return m_drawListContext.GetList(drawListTag);
}
void View::FinalizeDrawLists()
void View::FinalizeDrawListsTG(AZ::TaskGraphEvent& finalizeDrawListsTGEvent)
{
AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists");
m_drawListContext.FinalizeLists();
SortFinalizedDrawLists();
SortFinalizedDrawListsTG(finalizeDrawListsTGEvent);
}
void View::FinalizeDrawListsJob(AZ::Job* parentJob)
{
AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists");
m_drawListContext.FinalizeLists();
SortFinalizedDrawListsJob(parentJob);
}
void View::SortFinalizedDrawLists()
void View::SortFinalizedDrawListsTG(AZ::TaskGraphEvent& finalizeDrawListsTGEvent)
{
AZ_PROFILE_SCOPE(RPI, "View: SortFinalizedDrawLists");
RHI::DrawListsByTag& drawListsByTag = m_drawListContext.GetMergedDrawListsByTag();
AZ::TaskGraph drawListSortTG;
AZ::TaskDescriptor drawListSortTGDescriptor{"RPI_View_SortFinalizedDrawLists", "Graphics"};
for (size_t idx = 0; idx < drawListsByTag.size(); ++idx)
{
if (drawListsByTag[idx].size() > 1)
{
SortDrawList(drawListsByTag[idx], RHI::DrawListTag(idx));
drawListSortTG.AddTask(drawListSortTGDescriptor, [this, &drawListsByTag, idx]()
{
AZ_PROFILE_SCOPE(RPI, "View: SortDrawList Task");
SortDrawList(drawListsByTag[idx], RHI::DrawListTag(idx));
});
}
}
if (!drawListSortTG.IsEmpty())
{
drawListSortTG.Detach();
drawListSortTG.Submit(&finalizeDrawListsTGEvent);
}
}
void View::SortFinalizedDrawListsJob(AZ::Job* parentJob)
{
AZ_PROFILE_SCOPE(RPI, "View: SortFinalizedDrawLists");
RHI::DrawListsByTag& drawListsByTag = m_drawListContext.GetMergedDrawListsByTag();
AZ::JobCompletion jobCompletion;
for (size_t idx = 0; idx < drawListsByTag.size(); ++idx)
{
if (drawListsByTag[idx].size() > 1)
{
auto jobLambda = [this, &drawListsByTag, idx]()
{
AZ_PROFILE_SCOPE(RPI, "View: SortDrawList Job");
SortDrawList(drawListsByTag[idx], RHI::DrawListTag(idx));
};
Job* jobSortDrawList = aznew JobFunction<decltype(jobLambda)>(jobLambda, true, nullptr); // Auto-deletes
if (parentJob)
{
parentJob->StartAsChild(jobSortDrawList);
}
else
{
jobSortDrawList->SetDependent(&jobCompletion);
jobSortDrawList->Start();
}
}
}
if (parentJob)
{
parentJob->WaitForChildren();
}
else
{
jobCompletion.StartAndWaitForCompletion();
}
}
void View::SortDrawList(RHI::DrawList& drawList, RHI::DrawListTag tag)
@@ -263,12 +335,12 @@ namespace AZ
passWithDrawListTag->SortDrawList(drawList);
}
void View::ConnectWorldToViewMatrixChangedHandler(View::MatrixChangedEvent::Handler& handler)
void View::ConnectWorldToViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler)
{
handler.Connect(m_onWorldToViewMatrixChange);
}
void View::ConnectWorldToClipMatrixChangedHandler(View::MatrixChangedEvent::Handler& handler)
void View::ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler)
{
handler.Connect(m_onWorldToClipMatrixChange);
}
@@ -361,16 +433,19 @@ namespace AZ
{
if (m_clipSpaceOffset.IsZero())
{
Matrix4x4 worldToClipPrevMatrix = m_viewToClipPrevMatrix * m_worldToViewPrevMatrix;
m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, worldToClipPrevMatrix);
m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, m_worldToClipMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, m_viewToClipMatrix);
m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, m_clipToWorldMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, m_viewToClipMatrix.GetInverseFull());
if (m_shaderResourceGroup)
{
Matrix4x4 worldToClipPrevMatrix = m_viewToClipPrevMatrix * m_worldToViewPrevMatrix;
m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, worldToClipPrevMatrix);
m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, m_worldToClipMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, m_viewToClipMatrix);
m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, m_clipToWorldMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, m_viewToClipMatrix.GetInverseFull());
}
}
else
{
// Offset the current and previous frame clip matricies
// Offset the current and previous frame clip matrices
Matrix4x4 offsetViewToClipMatrix = m_viewToClipMatrix;
offsetViewToClipMatrix.SetElement(0, 2, m_clipSpaceOffset.GetX());
offsetViewToClipMatrix.SetElement(1, 2, m_clipSpaceOffset.GetY());
@@ -379,27 +454,33 @@ namespace AZ
offsetViewToClipPrevMatrix.SetElement(0, 2, m_clipSpaceOffset.GetX());
offsetViewToClipPrevMatrix.SetElement(1, 2, m_clipSpaceOffset.GetY());
// Build other matricies dependent on the view to clip matricies
// Build other matrices dependent on the view to clip matrices
Matrix4x4 offsetWorldToClipMatrix = offsetViewToClipMatrix * m_worldToViewMatrix;
Matrix4x4 offsetWorldToClipPrevMatrix = offsetViewToClipPrevMatrix * m_worldToViewPrevMatrix;
Matrix4x4 offsetClipToViewMatrix = offsetViewToClipMatrix.GetInverseFull();
Matrix4x4 offsetClipToWorldMatrix = m_viewToWorldMatrix * offsetClipToViewMatrix;
m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, offsetWorldToClipPrevMatrix);
m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, offsetWorldToClipMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, offsetViewToClipMatrix);
m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, offsetClipToWorldMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, offsetViewToClipMatrix.GetInverseFull());
if (m_shaderResourceGroup)
{
m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, offsetWorldToClipPrevMatrix);
m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, offsetWorldToClipMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, offsetViewToClipMatrix);
m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, offsetClipToWorldMatrix);
m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, offsetViewToClipMatrix.GetInverseFull());
}
}
m_shaderResourceGroup->SetConstant(m_worldPositionConstantIndex, m_position);
m_shaderResourceGroup->SetConstant(m_viewMatrixConstantIndex, m_worldToViewMatrix);
m_shaderResourceGroup->SetConstant(m_viewMatrixInverseConstantIndex, m_worldToViewMatrix.GetInverseFull());
m_shaderResourceGroup->SetConstant(m_zConstantsConstantIndex, m_nearZ_farZ_farZTimesNearZ_farZMinusNearZ);
m_shaderResourceGroup->SetConstant(m_unprojectionConstantsIndex, m_unprojectionConstants);
if (m_shaderResourceGroup)
{
m_shaderResourceGroup->SetConstant(m_worldPositionConstantIndex, m_position);
m_shaderResourceGroup->SetConstant(m_viewMatrixConstantIndex, m_worldToViewMatrix);
m_shaderResourceGroup->SetConstant(m_viewMatrixInverseConstantIndex, m_worldToViewMatrix.GetInverseFull());
m_shaderResourceGroup->SetConstant(m_zConstantsConstantIndex, m_nearZ_farZ_farZTimesNearZ_farZMinusNearZ);
m_shaderResourceGroup->SetConstant(m_unprojectionConstantsIndex, m_unprojectionConstants);
m_shaderResourceGroup->Compile();
m_shaderResourceGroup->Compile();
}
m_viewToClipPrevMatrix = m_viewToClipMatrix;
m_worldToViewPrevMatrix = m_worldToViewMatrix;
@@ -410,6 +491,7 @@ namespace AZ
void View::BeginCulling()
{
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
AZ_PROFILE_SCOPE(RPI, "View: ClearMaskedOcclusionBuffer");
m_maskedOcclusionCulling->ClearBuffer();
#endif
}
@@ -418,5 +500,30 @@ namespace AZ
{
return m_maskedOcclusionCulling;
}
void View::TryCreateShaderResourceGroup()
{
if (!m_shaderResourceGroup)
{
if (auto rpiSystemInterface = RPISystemInterface::Get())
{
if (Data::Asset<ShaderAsset> viewSrgShaderAsset = rpiSystemInterface->GetCommonShaderAssetForSrgs();
viewSrgShaderAsset.IsReady())
{
m_shaderResourceGroup =
ShaderResourceGroup::Create(viewSrgShaderAsset, rpiSystemInterface->GetViewSrgLayout()->GetName());
}
}
}
}
void View::OnAddToRenderPipeline()
{
TryCreateShaderResourceGroup();
if (!m_shaderResourceGroup)
{
AZ_Warning("RPI::View", false, "Shader Resource Group failed to initialize");
}
}
} // namespace RPI
} // namespace AZ
@@ -36,12 +36,12 @@ namespace AZ
AzFramework::WindowNotificationBus::Handler::BusConnect(nativeWindow);
AzFramework::ViewportRequestBus::Handler::BusConnect(id);
m_onProjectionMatrixChangedHandler = ViewportContext::MatrixChangedEvent::Handler([this](const AZ::Matrix4x4& matrix)
m_onProjectionMatrixChangedHandler = MatrixChangedEvent::Handler([this](const AZ::Matrix4x4& matrix)
{
m_projectionMatrixChangedEvent.Signal(matrix);
});
m_onViewMatrixChangedHandler = ViewportContext::MatrixChangedEvent::Handler([this](const AZ::Matrix4x4& matrix)
m_onViewMatrixChangedHandler = MatrixChangedEvent::Handler([this](const AZ::Matrix4x4& matrix)
{
m_viewMatrixChangedEvent.Signal(matrix);
});
@@ -203,6 +203,11 @@ namespace AZ
return GetDefaultView()->GetWorldToViewMatrix();
}
AZ::Matrix3x4 ViewportContext::GetCameraViewMatrixAsMatrix3x4() const
{
return GetDefaultView()->GetWorldToViewMatrixAsMatrix3x4();
}
void ViewportContext::SetCameraViewMatrix(const AZ::Matrix4x4& matrix)
{
GetDefaultView()->SetWorldToViewMatrix(matrix);
@@ -176,6 +176,20 @@ namespace AZ
return {};
}
ViewportContextPtr ViewportContextManager::GetViewportContextByScene(const Scene* scene) const
{
AZStd::lock_guard lock(m_containerMutex);
for (const auto& viewportData : m_viewportContexts)
{
ViewportContextPtr viewportContext = viewportData.second.context.lock();
if (viewportContext && viewportContext->GetRenderScene().get() == scene)
{
return viewportContext;
}
}
return {};
}
void ViewportContextManager::RenameViewportContext(ViewportContextPtr viewportContext, const Name& newContextName)
{
auto currentAssignedViewportContext = GetViewportContextByName(newContextName);
@@ -7,6 +7,9 @@
*/
#include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzFramework/Asset/AssetSystemBus.h>
namespace AZ
{
@@ -40,5 +43,56 @@ namespace AZ
return loadResult;
}
}
}
Data::AssetId StreamingImageAssetHandler::AssetMissingInCatalog(const Data::Asset<Data::AssetData>& asset)
{
// Find out if the asset is missing completely, or just still processing
// and escalate the asset to the top of the list
AzFramework::AssetSystem::AssetStatus missingAssetStatus;
AzFramework::AssetSystemRequestBus::BroadcastResult(
missingAssetStatus, &AzFramework::AssetSystem::AssetSystemRequests::GetAssetStatusById, asset.GetId().m_guid);
// Determine which fallback image to use
const char* relativePath = DefaultImageAssetPaths::DefaultFallback;
bool useDebugFallbackImages = true;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->GetObject(useDebugFallbackImages, "/O3DE/Atom/RPI/UseDebugFallbackImages");
}
if (useDebugFallbackImages)
{
switch (missingAssetStatus)
{
case AzFramework::AssetSystem::AssetStatus::AssetStatus_Queued:
case AzFramework::AssetSystem::AssetStatus::AssetStatus_Compiling:
relativePath = DefaultImageAssetPaths::Processing;
break;
case AzFramework::AssetSystem::AssetStatus::AssetStatus_Failed:
relativePath = DefaultImageAssetPaths::ProcessingFailed;
break;
case AzFramework::AssetSystem::AssetStatus::AssetStatus_Missing:
case AzFramework::AssetSystem::AssetStatus::AssetStatus_Unknown:
case AzFramework::AssetSystem::AssetStatus::AssetStatus_Compiled:
relativePath = DefaultImageAssetPaths::Missing;
break;
}
}
// Make sure the fallback image has been processed
AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
AzFramework::AssetSystemRequestBus::BroadcastResult(
status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, relativePath);
// Return the asset id of the fallback image
Data::AssetId assetId{};
bool autoRegisterIfNotFound = false;
Data::AssetCatalogRequestBus::BroadcastResult(
assetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, relativePath,
azrtti_typeid<AZ::RPI::StreamingImageAsset>(), autoRegisterIfNotFound);
return assetId;
}
} // namespace RPI
} // namespace AZ
@@ -18,8 +18,6 @@
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Debug/EventTrace.h>
namespace AZ
{
namespace RPI
@@ -123,7 +121,7 @@ namespace AZ
void LuaMaterialFunctor::Process(RuntimeContext& context)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
InitScriptContext();
@@ -141,7 +139,7 @@ namespace AZ
void LuaMaterialFunctor::Process(EditorContext& context)
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
InitScriptContext();
@@ -226,10 +226,12 @@ namespace AZ
if (changesWereApplied)
{
AZ_Warning("MaterialAsset", false,
AZ_Warning(
"MaterialAsset", false,
"This material is based on version '%u' of %s, but the material type is now at version '%u'. "
"Automatic updates are available. Consider updating the .material source file.",
originalVersion, m_materialTypeAsset.ToString<AZStd::string>().c_str(), m_materialTypeAsset->GetVersion());
"Automatic updates are available. Consider updating the .material source file for '%s'.",
originalVersion, m_materialTypeAsset.ToString<AZStd::string>().c_str(), m_materialTypeAsset->GetVersion(),
GetId().ToString<AZStd::string>().c_str());
}
m_materialTypeVersion = m_materialTypeAsset->GetVersion();
@@ -237,7 +239,7 @@ namespace AZ
void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset<Data::AssetData> asset)
{
Data::Asset<MaterialTypeAsset> newMaterialTypeAsset = { asset.GetAs<MaterialTypeAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
Data::Asset<MaterialTypeAsset> newMaterialTypeAsset = Data::static_pointer_cast<MaterialTypeAsset>(asset);
if (newMaterialTypeAsset)
{
@@ -118,12 +118,16 @@ namespace AZ
else if (value.is<Data::Asset<Data::AssetData>>())
{
result.m_value = Data::Asset<RPI::ImageAsset>(
AZStd::any_cast<Data::Asset<Data::AssetData>>(value).GetId(), azrtti_typeid<RPI::StreamingImageAsset>());
AZStd::any_cast<Data::Asset<Data::AssetData>>(value).GetId(),
azrtti_typeid<RPI::StreamingImageAsset>(),
AZStd::any_cast<Data::Asset<Data::AssetData>>(value).GetHint());
}
else if (value.is<Data::Asset<StreamingImageAsset>>())
{
result.m_value = Data::Asset<RPI::ImageAsset>(
AZStd::any_cast<Data::Asset<StreamingImageAsset>>(value).GetId(), azrtti_typeid<RPI::StreamingImageAsset>());
AZStd::any_cast<Data::Asset<StreamingImageAsset>>(value).GetId(),
azrtti_typeid<RPI::StreamingImageAsset>(),
AZStd::any_cast<Data::Asset<StreamingImageAsset>>(value).GetHint());
}
else if (value.is<Data::Asset<ImageAsset>>())
{
@@ -188,6 +188,10 @@ namespace AZ
void MaterialTypeAsset::SetReady()
{
m_status = AssetStatus::Ready;
// If this was created dynamically using MaterialTypeAssetCreator (which is what calls SetReady()),
// we need to connect to the AssetBus for reloads.
PostLoadInit();
}
bool MaterialTypeAsset::PostLoadInit()
@@ -126,8 +126,8 @@ namespace AZ
}
ShaderCollection::Item::Item()
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
{
m_renderStatesOverlay = RHI::GetInvalidRenderStates();
}
ShaderCollection::Item& ShaderCollection::operator[](size_t i)
@@ -156,7 +156,8 @@ namespace AZ
}
ShaderCollection::Item::Item(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
: m_shaderAsset(shaderAsset)
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
, m_shaderAsset(shaderAsset)
, m_shaderVariantId(variantId)
, m_shaderTag(shaderTag)
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
@@ -164,7 +165,8 @@ namespace AZ
}
ShaderCollection::Item::Item(Data::Asset<ShaderAsset>&& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
: m_shaderAsset(AZStd::move(shaderAsset))
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
, m_shaderAsset(AZStd::move(shaderAsset))
, m_shaderVariantId(variantId)
, m_shaderTag(shaderTag)
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
@@ -9,7 +9,6 @@
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/Model/ModelKdTree.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/std/limits.h>
@@ -137,7 +136,7 @@ namespace AZ
// For runtime approach is to do this during asset processing and serialized spatial information alongside with mesh model assets
const auto jobLambda = [&]() -> void
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION(RPI);
AZStd::unique_ptr<ModelKdTree> tree = AZStd::make_unique<ModelKdTree>();
tree->Build(this);
@@ -201,23 +200,11 @@ namespace AZ
AZ::Vector3& normal) const
{
const BufferAssetView& indexBufferView = mesh.GetIndexBufferAssetView();
const AZStd::array_view<ModelLodAsset::Mesh::StreamBufferInfo>& streamBufferList = mesh.GetStreamBufferInfoList();
const BufferAssetView* positionBufferView = mesh.GetSemanticBufferAssetView(m_positionName);
// find position semantic
const ModelLodAsset::Mesh::StreamBufferInfo* positionBuffer = nullptr;
for (const ModelLodAsset::Mesh::StreamBufferInfo& bufferInfo : streamBufferList)
if (positionBufferView && positionBufferView->GetBufferAsset().Get())
{
if (bufferInfo.m_semantic.m_name == m_positionName)
{
positionBuffer = &bufferInfo;
break;
}
}
if (positionBuffer && positionBuffer->m_bufferAssetView.GetBufferAsset().Get())
{
BufferAsset* bufferAssetViewPtr = positionBuffer->m_bufferAssetView.GetBufferAsset().Get();
BufferAsset* bufferAssetViewPtr = positionBufferView->GetBufferAsset().Get();
BufferAsset* indexAssetViewPtr = indexBufferView.GetBufferAsset().Get();
if (!bufferAssetViewPtr || !indexAssetViewPtr)
@@ -225,7 +212,7 @@ namespace AZ
return false;
}
RHI::BufferViewDescriptor positionBufferViewDesc = bufferAssetViewPtr->GetBufferViewDescriptor();
RHI::BufferViewDescriptor positionBufferViewDesc = positionBufferView->GetBufferViewDescriptor();
AZStd::array_view<uint8_t> positionRawBuffer = bufferAssetViewPtr->GetBuffer();
const uint32_t positionElementSize = positionBufferViewDesc.m_elementSize;
@@ -234,22 +221,28 @@ namespace AZ
// Position is 3 floats
if (positionElementSize != sizeof(float) * 3)
{
AZ_Warning("ModelAsset", false, "unsupported mesh posiiton format, only full 3 floats per vertex are supported at the moment");
AZ_Warning(
"ModelAsset", false, "unsupported mesh posiiton format, only full 3 floats per vertex are supported at the moment");
return false;
}
RHI::BufferViewDescriptor indexBufferViewDesc = indexBufferView.GetBufferViewDescriptor();
AZStd::array_view<uint8_t> indexRawBuffer = indexAssetViewPtr->GetBuffer();
RHI::BufferViewDescriptor indexRawDesc = indexAssetViewPtr->GetBufferViewDescriptor();
bool anyHit = false;
const AZ::Vector3 rayEnd = rayStart + rayDir;
AZ::Vector3 a, b, c;
AZ::Vector3 intersectionNormal;
bool anyHit = false;
float shortestDistanceNormalized = AZStd::numeric_limits<float>::max();
const AZ::u32* indexPtr = reinterpret_cast<const AZ::u32*>(indexRawBuffer.data());
for (uint32_t indexIter = 0; indexIter <= indexRawDesc.m_elementCount - 3; indexIter += 3, indexPtr += 3)
const AZ::u32* indexPtr = reinterpret_cast<const AZ::u32*>(
indexRawBuffer.data() + (indexBufferViewDesc.m_elementOffset * indexBufferViewDesc.m_elementSize));
const float* positionPtr = reinterpret_cast<const float*>(
positionRawBuffer.data() + (positionBufferViewDesc.m_elementOffset * positionBufferViewDesc.m_elementSize));
constexpr int StepSize = 3; // number of values per vertex (x, y, z)
for (uint32_t indexIter = 0; indexIter < indexBufferViewDesc.m_elementCount; indexIter += StepSize, indexPtr += StepSize)
{
AZ::u32 index0 = indexPtr[0];
AZ::u32 index1 = indexPtr[1];
@@ -261,17 +254,17 @@ namespace AZ
return false;
}
const float* p = reinterpret_cast<const float*>(&positionRawBuffer[index0 * positionElementSize]);
a.Set(const_cast<float*>(p)); // faster than AZ::Vector3 c-tor
p = reinterpret_cast<const float*>(&positionRawBuffer[index1 * positionElementSize]);
b.Set(const_cast<float*>(p));
p = reinterpret_cast<const float*>(&positionRawBuffer[index2 * positionElementSize]);
c.Set(const_cast<float*>(p));
// faster than AZ::Vector3 c-tor
const float* aRef = &positionPtr[index0 * StepSize];
a.Set(aRef);
const float* bRef = &positionPtr[index1 * StepSize];
b.Set(bRef);
const float* cRef = &positionPtr[index2 * StepSize];
c.Set(cRef);
float currentDistanceNormalized;
if (AZ::Intersect::IntersectSegmentTriangleCCW(rayStart, rayEnd, a, b, c, intersectionNormal, currentDistanceNormalized))
if (AZ::Intersect::IntersectSegmentTriangleCCW(
rayStart, rayEnd, a, b, c, intersectionNormal, currentDistanceNormalized))
{
anyHit = true;
@@ -100,7 +100,7 @@ namespace AZ
->Field("pipelineStateType", &ShaderAsset::m_pipelineStateType)
->Field("shaderOptionGroupLayout", &ShaderAsset::m_shaderOptionGroupLayout)
->Field("drawListName", &ShaderAsset::m_drawListName)
->Field("shaderAssetBuildTimestamp", &ShaderAsset::m_shaderAssetBuildTimestamp)
->Field("shaderAssetBuildTimestamp", &ShaderAsset::m_buildTimestamp)
->Field("perAPIShaderData", &ShaderAsset::m_perAPIShaderData)
;
}
@@ -108,7 +108,6 @@ namespace AZ
ShaderAsset::~ShaderAsset()
{
Data::AssetBus::Handler::BusDisconnect();
ShaderVariantFinderNotificationBus::Handler::BusDisconnect();
AssetInitBus::Handler::BusDisconnect();
}
@@ -134,11 +133,11 @@ namespace AZ
return m_drawListName;
}
AZStd::sys_time_t ShaderAsset::GetShaderAssetBuildTimestamp() const
AZStd::sys_time_t ShaderAsset::GetBuildTimestamp() const
{
return m_shaderAssetBuildTimestamp;
return m_buildTimestamp;
}
void ShaderAsset::SetReady()
{
m_status = AssetStatus::Ready;
@@ -256,7 +255,7 @@ namespace AZ
}
return GetRootVariant(supervariantIndex);
}
else if (variant->GetBuildTimestamp() >= m_shaderAssetBuildTimestamp)
else if (variant->GetBuildTimestamp() >= m_buildTimestamp)
{
return variant;
}
@@ -570,46 +569,16 @@ namespace AZ
bool ShaderAsset::PostLoadInit()
{
// Once the ShaderAsset is loaded, it is necessary to listen for changes in the Root Variant Asset.
Data::AssetBus::Handler::BusConnect(GetRootVariant().GetId());
ShaderVariantFinderNotificationBus::Handler::BusConnect(GetId());
AssetInitBus::Handler::BusDisconnect();
return true;
}
void ShaderAsset::ReinitializeRootShaderVariant(Data::Asset<Data::AssetData> asset)
{
Data::Asset<ShaderVariantAsset> shaderVariantAsset = { asset.GetAs<ShaderVariantAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId, "Was expecting to update the root variant");
SupervariantIndex supervariantIndex = GetSupervariantIndexFromAssetId(asset.GetId());
GetCurrentShaderApiData().m_supervariants[supervariantIndex.GetIndex()].m_rootShaderVariantAsset = asset;
ShaderReloadNotificationBus::Event(GetId(), &ShaderReloadNotificationBus::Events::OnShaderAssetReinitialized, Data::Asset<ShaderAsset>{ this, AZ::Data::AssetLoadBehavior::PreLoad } );
}
///////////////////////////////////////////////////////////////////////
// AssetBus overrides...
void ShaderAsset::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
void ShaderAsset::UpdateRootShaderVariantAsset(SupervariantIndex supervariantIndex, Data::Asset<ShaderVariantAsset> newRootVariant)
{
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReloaded %s", this, asset.GetHint().c_str());
ReinitializeRootShaderVariant(asset);
GetCurrentShaderApiData().m_supervariants[supervariantIndex.GetIndex()].m_rootShaderVariantAsset = newRootVariant;
}
void ShaderAsset::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
// We have to listen to OnAssetReady, OnAssetReloaded isn't enough, because of the following scenario:
// The user changes a .shader file, which causes the AP to rebuild the ShaderAsset and root ShaderVariantAsset.
// 1) Thread A creates the new ShaderAsset, loads it, and gets the old ShaderVariantAsset.
// 2) Thread B creates the new ShaderVariantAsset, loads it, and calls OnAssetReloaded.
// 3) Main thread calls ShaderAsset::PostLoadInit which connects to the AssetBus but it's too late to receive OnAssetReloaded,
// so it continues using the old ShaderVariantAsset instead of the new one.
// The OnAssetReady bus function is called automatically whenever a connection to AssetBus is made, so listening to this gives
// us the opportunity to assign the appropriate ShaderVariantAsset.
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReady %s", this, asset.GetHint().c_str());
ReinitializeRootShaderVariant(asset);
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
/// ShaderVariantFinderNotificationBus overrides
@@ -628,7 +597,6 @@ namespace AZ
m_shaderVariantTree = shaderVariantTreeAsset;
}
lock.unlock();
ShaderReloadNotificationBus::Event(GetId(), &ShaderReloadNotificationBus::Events::OnShaderAssetReinitialized, Data::Asset<ShaderAsset>{ this, AZ::Data::AssetLoadBehavior::PreLoad });
}
///////////////////////////////////////////////////////////////////
@@ -21,7 +21,7 @@ namespace AZ
{
if (ValidateIsReady())
{
m_asset->m_shaderAssetBuildTimestamp = shaderAssetBuildTimestamp;
m_asset->m_buildTimestamp = shaderAssetBuildTimestamp;
}
}
@@ -390,7 +390,7 @@ namespace AZ
m_asset->m_pipelineStateType = sourceShaderAsset.m_pipelineStateType;
m_asset->m_drawListName = sourceShaderAsset.m_drawListName;
m_asset->m_shaderOptionGroupLayout = sourceShaderAsset.m_shaderOptionGroupLayout;
m_asset->m_shaderAssetBuildTimestamp = sourceShaderAsset.m_shaderAssetBuildTimestamp;
m_asset->m_buildTimestamp = sourceShaderAsset.m_buildTimestamp;
// copy root variant assets
for (auto& perAPIShaderData : sourceShaderAsset.m_perAPIShaderData)
@@ -60,7 +60,7 @@ namespace AZ
}
}
AZStd::sys_time_t ShaderVariantAsset::GetBuildTimestamp() const
AZ::u64 ShaderVariantAsset::GetBuildTimestamp() const
{
return m_buildTimestamp;
}