Added DiffuseProbeGrid texture baking

This commit is contained in:
dmcdiar
2021-05-21 03:26:29 -07:00
parent d0daab4fe0
commit d6e25bbb33
22 changed files with 1538 additions and 124 deletions
@@ -0,0 +1,45 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}",
"Name": "LUT_R32F",
"FileMasks": ["_lutr32f"],
"SourceColor": "Linear",
"DestColor": "Linear",
"PixelFormat": "R32F"
},
"PlatformsPresets": {
"es3": {
"UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}",
"Name": "LUT_R32F",
"SourceColor": "Linear",
"DestColor": "Linear",
"PixelFormat": "R32F"
},
"ios": {
"UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}",
"Name": "LUT_R32F",
"SourceColor": "Linear",
"DestColor": "Linear",
"PixelFormat": "R32F"
},
"osx_gl": {
"UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}",
"Name": "LUT_R32F",
"SourceColor": "Linear",
"DestColor": "Linear",
"PixelFormat": "R32F"
},
"provo": {
"UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}",
"Name": "LUT_R32F",
"SourceColor": "Linear",
"DestColor": "Linear",
"PixelFormat": "R32F"
}
}
}
}
@@ -0,0 +1,59 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}",
"Name": "LUT_RGBA16",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16"
],
"PixelFormat": "R16G16B16A16"
},
"PlatformsPresets": {
"es3": {
"UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}",
"Name": "LUT_RGBA16",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16"
],
"PixelFormat": "R16G16B16A16"
},
"ios": {
"UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}",
"Name": "LUT_RGBA16",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16"
],
"PixelFormat": "R16G16B16A16"
},
"osx_gl": {
"UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}",
"Name": "LUT_RGBA16",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16"
],
"PixelFormat": "R16G16B16A16"
},
"provo": {
"UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}",
"Name": "LUT_RGBA16",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16"
],
"PixelFormat": "R16G16B16A16"
}
}
}
}
@@ -0,0 +1,59 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "MultiplatformPresetSettings",
"ClassData": {
"DefaultPreset": {
"UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}",
"Name": "LUT_RGBA16F",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16f"
],
"PixelFormat": "R16G16B16A16F"
},
"PlatformsPresets": {
"es3": {
"UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}",
"Name": "LUT_RGBA16F",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16f"
],
"PixelFormat": "R16G16B16A16F"
},
"ios": {
"UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}",
"Name": "LUT_RGBA16F",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16f"
],
"PixelFormat": "R16G16B16A16F"
},
"osx_gl": {
"UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}",
"Name": "LUT_RGBA16F",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16f"
],
"PixelFormat": "R16G16B16A16F"
},
"provo": {
"UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}",
"Name": "LUT_RGBA16F",
"SourceColor": "Linear",
"DestColor": "Linear",
"FileMasks": [
"_lutrgba16f"
],
"PixelFormat": "R16G16B16A16F"
}
}
}
}
@@ -14,6 +14,9 @@
#include <AzCore/base.h>
#include <Atom/RPI.Public/FeatureProcessor.h>
#include <Atom/RHI.Reflect/Size.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/RPI.Reflect/Image/Image.h>
namespace AZ
{
@@ -23,6 +26,57 @@ namespace AZ
using DiffuseProbeGridHandle = AZStd::shared_ptr<DiffuseProbeGrid>;
enum class DiffuseProbeGridMode : uint8_t
{
RealTime,
Baked,
AutoSelect
};
enum class DiffuseProbeGridTextureNotificationType
{
None,
Ready,
Error
};
struct DiffuseProbeGridTexture
{
const AZStd::shared_ptr<AZStd::vector<uint8_t>> m_data;
RHI::Format m_format;
RHI::Size m_size;
};
static const char* DiffuseProbeGridIrradianceFileName = "Irradiance_lutrgba16.dds";
static const char* DiffuseProbeGridDistanceFileName = "Distance_lutrg32f.dds";
static const char* DiffuseProbeGridRelocationFileName = "Relocation_lutrgba16f.dds";
static const char* DiffuseProbeGridClassificationFileName = "Classification_lutr32f.dds";
using DiffuseProbeGridBakeTexturesCallback = AZStd::function<void(
DiffuseProbeGridTexture irradianceTexture,
DiffuseProbeGridTexture distanceTexture,
DiffuseProbeGridTexture relocationTexture,
DiffuseProbeGridTexture classificationTexture)>;
struct DiffuseProbeGridBakedTextures
{
// irradiance and distance images can be used directly
Data::Instance<RPI::Image> m_irradianceImage;
AZStd::string m_irradianceImageRelativePath;
Data::Instance<RPI::Image> m_distanceImage;
AZStd::string m_distanceImageRelativePath;
// relocation and classification images need to be recreated as RW textures
RHI::ImageDescriptor m_relocationImageDescriptor;
AZStd::array_view<uint8_t> m_relocationImageData;
AZStd::string m_relocationImageRelativePath;
RHI::ImageDescriptor m_classificationImageDescriptor;
AZStd::array_view<uint8_t> m_classificationImageData;
AZStd::string m_classificationImageRelativePath;
};
// DiffuseProbeGridFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom
class DiffuseProbeGridFeatureProcessorInterface
: public RPI::FeatureProcessor
@@ -44,6 +98,28 @@ namespace AZ
virtual void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) = 0;
virtual void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) = 0;
virtual void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) = 0;
virtual void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) = 0;
virtual void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) = 0;
virtual void BakeTextures(
const DiffuseProbeGridHandle& probeGrid,
DiffuseProbeGridBakeTexturesCallback callback,
const AZStd::string& irradianceTextureRelativePath,
const AZStd::string& distanceTextureRelativePath,
const AZStd::string& relocationTextureRelativePath,
const AZStd::string& classificationTextureRelativePath) = 0;
virtual bool CheckTextureAssetNotification(
const AZStd::string& relativePath,
Data::Asset<RPI::StreamingImageAsset>& outTextureAsset,
DiffuseProbeGridTextureNotificationType& outNotificationType) = 0;
virtual bool AreBakedTexturesReferenced(
const AZStd::string& irradianceTextureRelativePath,
const AZStd::string& distanceTextureRelativePath,
const AZStd::string& relocationTextureRelativePath,
const AZStd::string& classificationTextureRelativePath) = 0;
};
} // namespace Render
} // namespace AZ
@@ -13,6 +13,7 @@
#include <Atom/RHI.Reflect/ShaderResourceGroupLayoutDescriptor.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <DiffuseProbeGrid/DiffuseProbeGrid.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RHI/Factory.h>
@@ -23,6 +24,11 @@ namespace AZ
{
namespace Render
{
DiffuseProbeGrid::DiffuseProbeGrid()
: m_textureReadback(this)
{
}
DiffuseProbeGrid::~DiffuseProbeGrid()
{
m_scene->GetCullingScene()->UnregisterCullable(m_cullable);
@@ -166,6 +172,84 @@ namespace AZ
m_updateRenderObjectSrg = true;
}
void DiffuseProbeGrid::SetMode(DiffuseProbeGridMode mode)
{
// handle auto-select
if (mode == DiffuseProbeGridMode::AutoSelect)
{
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
m_mode = (device->GetFeatures().m_rayTracing) ? DiffuseProbeGridMode::RealTime : DiffuseProbeGridMode::Baked;
}
else
{
m_mode = mode;
}
m_updateTextures = true;
}
void DiffuseProbeGrid::SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures)
{
AZ_Assert(bakedTextures.m_irradianceImage.get(), "Invalid Irradiance image passed to SetBakedTextures");
AZ_Assert(bakedTextures.m_distanceImage.get(), "Invalid Distance image passed to SetBakedTextures");
AZ_Assert(bakedTextures.m_relocationImageData.size() > 0, "Invalid Relocation image data passed to SetBakedTextures");
AZ_Assert(bakedTextures.m_classificationImageData.size() > 0, "Invalid Classification image data passed to SetBakedTextures");
m_bakedIrradianceImage = bakedTextures.m_irradianceImage;
m_bakedDistanceImage = bakedTextures.m_distanceImage;
m_bakedIrradianceRelativePath = bakedTextures.m_irradianceImageRelativePath;
m_bakedDistanceRelativePath = bakedTextures.m_distanceImageRelativePath;
m_bakedRelocationRelativePath = bakedTextures.m_relocationImageRelativePath;
m_bakedClassificationRelativePath = bakedTextures.m_classificationImageRelativePath;
m_bakedRelocationImageData.resize(bakedTextures.m_relocationImageData.size());
memcpy(m_bakedRelocationImageData.data(), bakedTextures.m_relocationImageData.data(), bakedTextures.m_relocationImageData.size());
m_bakedClassificationImageData.resize(bakedTextures.m_classificationImageData.size());
memcpy(m_bakedClassificationImageData.data(), bakedTextures.m_classificationImageData.data(), bakedTextures.m_classificationImageData.size());
// create the relocation and distance RW textures now, these are needed for shader compatibility
// (image data is copied in UpdateTextures)
{
m_bakedRelocationImage = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest initRequest;
initRequest.m_image = m_bakedRelocationImage.get();
initRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead,
bakedTextures.m_relocationImageDescriptor.m_size.m_width,
bakedTextures.m_relocationImageDescriptor.m_size.m_height,
bakedTextures.m_relocationImageDescriptor.m_format);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Relocation image");
}
{
m_bakedClassificationImage = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest initRequest;
initRequest.m_image = m_bakedClassificationImage.get();
initRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead,
bakedTextures.m_classificationImageDescriptor.m_size.m_width,
bakedTextures.m_classificationImageDescriptor.m_size.m_height,
bakedTextures.m_classificationImageDescriptor.m_format);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Classification image");
}
m_updateTextures = true;
}
bool DiffuseProbeGrid::HasValidBakedTextures() const
{
return m_bakedIrradianceImage.get() &&
m_bakedDistanceImage.get() &&
m_bakedRelocationImage.get() &&
m_bakedClassificationImage.get();
}
uint32_t DiffuseProbeGrid::GetTotalProbeCount() const
{
return m_probeCountX * m_probeCountY * m_probeCountZ;
@@ -188,83 +272,117 @@ namespace AZ
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
// advance to the next image in the frame image array
m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount;
// probe raytrace
{
uint32_t width = m_numRaysPerProbe;
uint32_t height = GetTotalProbeCount();
m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_rayTraceImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image");
}
uint32_t probeCountX;
uint32_t probeCountY;
GetTexture2DProbeCount(probeCountX, probeCountY);
// probe irradiance
if (m_mode == DiffuseProbeGridMode::RealTime)
{
uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2);
uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2);
// advance to the next image in the frame image array
m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount;
m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
// probe raytrace
{
uint32_t width = m_numRaysPerProbe;
uint32_t height = GetTotalProbeCount();
RHI::ImageInitRequest request;
request.m_image = m_irradianceImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat);
RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
request.m_optimizedClearValue = &clearValue;
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image");
m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_rayTraceImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image");
}
// probe irradiance
{
uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2);
uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2);
m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_irradianceImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat);
RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
request.m_optimizedClearValue = &clearValue;
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image");
}
// probe distance
{
uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2);
uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2);
m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_distanceImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::DistanceImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image");
}
// probe relocation
{
uint32_t width = probeCountX;
uint32_t height = probeCountY;
m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_relocationImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RelocationImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image");
}
// probe classification
{
uint32_t width = probeCountX;
uint32_t height = probeCountY;
m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_classificationImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image");
}
}
// probe distance
else if (m_mode == DiffuseProbeGridMode::Baked && HasValidBakedTextures())
{
uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2);
uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2);
// copy the baked relocation and classification texture data to the RW textures
// (these need to be RW for shader compatibility)
RHI::ImageSubresourceRange range{ 0, 0, 0 ,0 };
RHI::ImageSubresourceLayoutPlaced layout;
m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
// relocation
{
m_bakedRelocationImage->GetSubresourceLayouts(range, &layout, nullptr);
RHI::ImageInitRequest request;
request.m_image = m_distanceImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::DistanceImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image");
}
RHI::ImageUpdateRequest updateRequest;
updateRequest.m_image = m_bakedRelocationImage.get();
updateRequest.m_sourceSubresourceLayout = layout;
updateRequest.m_sourceData = m_bakedRelocationImageData.data();
updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0);
m_renderData->m_imagePool->UpdateImageContents(updateRequest);
}
// probe relocation
{
uint32_t width = probeCountX;
uint32_t height = probeCountY;
// classification
{
m_bakedClassificationImage->GetSubresourceLayouts(range, &layout, nullptr);
m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_relocationImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RelocationImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image");
}
// probe classification
{
uint32_t width = probeCountX;
uint32_t height = probeCountY;
m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage();
RHI::ImageInitRequest request;
request.m_image = m_classificationImage[m_currentImageIndex].get();
request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat);
[[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image");
RHI::ImageUpdateRequest updateRequest;
updateRequest.m_image = m_bakedClassificationImage.get();
updateRequest.m_sourceSubresourceLayout = layout;
updateRequest.m_sourceData = m_bakedClassificationImageData.data();
updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0);
m_renderData->m_imagePool->UpdateImageContents(updateRequest);
}
}
m_updateTextures = false;
@@ -639,16 +757,16 @@ namespace AZ
m_renderObjectSrg->SetConstant(constantIndex, m_ambientMultiplier);
imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeIrradiance"));
m_renderObjectSrg->SetImageView(imageIndex, m_irradianceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get());
m_renderObjectSrg->SetImageView(imageIndex, GetIrradianceImage()->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get());
imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeDistance"));
m_renderObjectSrg->SetImageView(imageIndex, m_distanceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get());
m_renderObjectSrg->SetImageView(imageIndex, GetDistanceImage()->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get());
imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeOffsets"));
m_renderObjectSrg->SetImageView(imageIndex, m_relocationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get());
m_renderObjectSrg->SetImageView(imageIndex, GetRelocationImage()->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get());
imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeStates"));
m_renderObjectSrg->SetImageView(imageIndex, m_classificationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get());
m_renderObjectSrg->SetImageView(imageIndex, GetClassificationImage()->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get());
SetGridConstants(m_renderObjectSrg);
@@ -17,6 +17,7 @@
#include <Atom/RPI.Public/Scene.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Math/Aabb.h>
#include <DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h>
namespace AZ
{
@@ -30,7 +31,7 @@ namespace AZ
static const RHI::Format IrradianceImageFormat = RHI::Format::R16G16B16A16_UNORM;
static const RHI::Format DistanceImageFormat = RHI::Format::R32G32_FLOAT;
static const RHI::Format RelocationImageFormat = RHI::Format::R16G16B16A16_FLOAT;
static const RHI::Format ClassificationImageFormat = RHI::Format::R8_UINT;
static const RHI::Format ClassificationImageFormat = RHI::Format::R32_FLOAT;
// image pool
RHI::Ptr<RHI::ImagePool> m_imagePool;
@@ -61,7 +62,7 @@ namespace AZ
class DiffuseProbeGrid final
{
public:
DiffuseProbeGrid() = default;
DiffuseProbeGrid();
~DiffuseProbeGrid();
void Init(RPI::Scene* scene, DiffuseProbeGridRenderData* diffuseProbeGridRenderData);
@@ -96,6 +97,9 @@ namespace AZ
bool GetUseDiffuseIbl() const { return m_useDiffuseIbl; }
void SetUseDiffuseIbl(bool useDiffuseIbl) { m_useDiffuseIbl = useDiffuseIbl; }
DiffuseProbeGridMode GetMode() const { return m_mode; }
void SetMode(DiffuseProbeGridMode mode);
uint32_t GetNumRaysPerProbe() const { return m_numRaysPerProbe; }
uint32_t GetRemainingRelocationIterations() const { return aznumeric_cast<uint32_t>(m_remainingRelocationIterations); }
@@ -133,11 +137,16 @@ namespace AZ
void UpdateRenderObjectSrg();
// textures
const RHI::Ptr<RHI::Image>& GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; }
const RHI::Ptr<RHI::Image>& GetIrradianceImage() { return m_irradianceImage[m_currentImageIndex]; }
const RHI::Ptr<RHI::Image>& GetDistanceImage() { return m_distanceImage[m_currentImageIndex]; }
const RHI::Ptr<RHI::Image>& GetRelocationImage() { return m_relocationImage[m_currentImageIndex]; }
const RHI::Ptr<RHI::Image>& GetClassificationImage() { return m_classificationImage[m_currentImageIndex]; }
const RHI::Ptr<RHI::Image> GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; }
const RHI::Ptr<RHI::Image> GetIrradianceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_irradianceImage[m_currentImageIndex] : m_bakedIrradianceImage->GetRHIImage(); }
const RHI::Ptr<RHI::Image> GetDistanceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_distanceImage[m_currentImageIndex] : m_bakedDistanceImage->GetRHIImage(); }
const RHI::Ptr<RHI::Image> GetRelocationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_relocationImage[m_currentImageIndex] : m_bakedRelocationImage; }
const RHI::Ptr<RHI::Image> GetClassificationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_classificationImage[m_currentImageIndex] : m_bakedClassificationImage; }
const AZStd::string& GetBakedIrradianceRelativePath() const { return m_bakedIrradianceRelativePath; }
const AZStd::string& GetBakedDistanceRelativePath() const { return m_bakedDistanceRelativePath; }
const AZStd::string& GetBakedRelocationRelativePath() const { return m_bakedRelocationRelativePath; }
const AZStd::string& GetBakedClassificationRelativePath() const { return m_bakedClassificationRelativePath; }
// attachment Ids
const RHI::AttachmentId GetRayTraceImageAttachmentId() const { return m_rayTraceImageAttachmentId; }
@@ -152,6 +161,12 @@ namespace AZ
bool GetIrradianceClearRequired() const { return m_irradianceClearRequired; }
void ResetIrradianceClearRequired() { m_irradianceClearRequired = false; }
// texture readback
DiffuseProbeGridTextureReadback& GetTextureReadback() { return m_textureReadback; }
void SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures);
bool HasValidBakedTextures() const;
static constexpr uint32_t DefaultNumIrradianceTexels = 6;
static constexpr uint32_t DefaultNumDistanceTexels = 14;
static constexpr int32_t DefaultNumRelocationIterations = 100;
@@ -221,7 +236,10 @@ namespace AZ
// culling
RPI::Cullable m_cullable;
// textures
// grid mode (RealTime or Baked)
DiffuseProbeGridMode m_mode = DiffuseProbeGridMode::RealTime;
// real-time textures
static const uint32_t MaxTextureDimension = 8192;
static const uint32_t ImageFrameCount = 3;
RHI::Ptr<RHI::Image> m_rayTraceImage[ImageFrameCount];
@@ -233,6 +251,25 @@ namespace AZ
bool m_updateTextures = false;
bool m_irradianceClearRequired = true;
// baked textures
Data::Instance<RPI::Image> m_bakedIrradianceImage;
Data::Instance<RPI::Image> m_bakedDistanceImage;
RHI::Ptr<RHI::Image> m_bakedRelocationImage;
RHI::Ptr<RHI::Image> m_bakedClassificationImage;
// baked texture relative paths
AZStd::string m_bakedIrradianceRelativePath;
AZStd::string m_bakedDistanceRelativePath;
AZStd::string m_bakedRelocationRelativePath;
AZStd::string m_bakedClassificationRelativePath;
// baked texture data (only needed for the relocation and classification textures)
AZStd::vector<uint8_t> m_bakedRelocationImageData;
AZStd::vector<uint8_t> m_bakedClassificationImageData;
// texture readback
DiffuseProbeGridTextureReadback m_textureReadback;
// Srgs
Data::Instance<RPI::ShaderResourceGroup> m_rayTraceSrg;
Data::Instance<RPI::ShaderResourceGroup> m_blendIrradianceSrg;
@@ -87,7 +87,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return;
@@ -111,7 +111,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// probe raytrace image
{
@@ -150,7 +150,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs
// (see ValidateSetImageView() in ShaderResourceGroupData.cpp)
@@ -167,7 +167,7 @@ namespace AZ
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
// submit the DispatchItem for each DiffuseProbeGrid
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendDistanceSrg()->GetRHIShaderResourceGroup();
commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup);
@@ -87,7 +87,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return;
@@ -111,7 +111,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// probe raytrace image
{
@@ -150,7 +150,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs
// (see ValidateSetImageView() in ShaderResourceGroupData.cpp)
@@ -167,7 +167,7 @@ namespace AZ
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
// submit the DispatchItem for each DiffuseProbeGrid
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendIrradianceSrg()->GetRHIShaderResourceGroup();
commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup);
@@ -100,7 +100,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return;
@@ -124,7 +124,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// probe irradiance image
{
@@ -153,7 +153,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs
// (see line ValidateSetImageView() in ShaderResourceGroupData.cpp)
@@ -173,7 +173,7 @@ namespace AZ
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
// submit the DispatchItems for each DiffuseProbeGrid
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
uint32_t probeCountX;
uint32_t probeCountY;
@@ -91,7 +91,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return;
@@ -115,7 +115,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// probe raytrace image
{
@@ -143,7 +143,7 @@ namespace AZ
{
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs
// (see ValidateSetImageView() in ShaderResourceGroupData.cpp)
@@ -159,7 +159,7 @@ namespace AZ
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
// submit the DispatchItems for each DiffuseProbeGrid
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetClassificationSrg()->GetRHIShaderResourceGroup();
commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup);
@@ -45,6 +45,7 @@ namespace AZ
RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get();
m_diffuseProbeGrids.reserve(InitialProbeGridAllocationSize);
m_realTimeDiffuseProbeGrids.reserve(InitialProbeGridAllocationSize);
RHI::BufferPoolDescriptor desc;
desc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
@@ -61,7 +62,7 @@ namespace AZ
// image pool
{
RHI::ImagePoolDescriptor imagePoolDesc;
imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite;
imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead;
m_probeGridRenderData.m_imagePool = RHI::Factory::Get().CreateImagePool();
[[maybe_unused]] RHI::ResultCode result = m_probeGridRenderData.m_imagePool->Init(*rhiSystem->GetDevice(), imagePoolDesc);
@@ -123,6 +124,32 @@ namespace AZ
m_needUpdatePipelineStates = false;
}
// check pending textures and connect bus for notifications
for (auto& notificationEntry : m_notifyTextureAssets)
{
if (notificationEntry.m_assetId.IsValid())
{
// asset already has an assetId
continue;
}
// query for the assetId
AZ::Data::AssetId assetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetId,
&AZ::Data::AssetCatalogRequests::GetAssetIdByPath,
notificationEntry.m_relativePath.c_str(),
azrtti_typeid<AZ::RPI::StreamingImageAsset>(),
false);
if (assetId.IsValid())
{
notificationEntry.m_assetId = assetId;
notificationEntry.m_asset.Create(assetId, true);
Data::AssetBus::MultiHandler::BusConnect(assetId);
}
}
// if the volumes changed we need to re-sort the probe list
if (m_probeGridSortRequired)
{
@@ -139,6 +166,7 @@ namespace AZ
};
AZStd::sort(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), sortFn);
AZStd::sort(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), sortFn);
m_probeGridSortRequired = false;
}
@@ -160,6 +188,9 @@ namespace AZ
diffuseProbeGrid->SetExtents(extents);
diffuseProbeGrid->SetProbeSpacing(probeSpacing);
m_diffuseProbeGrids.push_back(diffuseProbeGrid);
UpdateRealTimeList(diffuseProbeGrid);
m_probeGridSortRequired = true;
return diffuseProbeGrid;
@@ -169,6 +200,7 @@ namespace AZ
{
AZ_Assert(probeGrid.get(), "RemoveProbeGrid called with an invalid handle");
// remove from main list
auto itEntry = AZStd::find_if(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), [&](AZStd::shared_ptr<DiffuseProbeGrid> const& entry)
{
return (entry == probeGrid);
@@ -176,6 +208,18 @@ namespace AZ
AZ_Assert(itEntry != m_diffuseProbeGrids.end(), "RemoveProbeGrid called with a probe grid that is not in the probe list");
m_diffuseProbeGrids.erase(itEntry);
// remove from side list of real-time grids
itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr<DiffuseProbeGrid> const& entry)
{
return (entry == probeGrid);
});
if (itEntry != m_realTimeDiffuseProbeGrids.end())
{
m_realTimeDiffuseProbeGrids.erase(itEntry);
}
probeGrid = nullptr;
}
@@ -247,6 +291,133 @@ namespace AZ
probeGrid->SetUseDiffuseIbl(useDiffuseIbl);
}
void DiffuseProbeGridFeatureProcessor::BakeTextures(
const DiffuseProbeGridHandle& probeGrid,
DiffuseProbeGridBakeTexturesCallback callback,
const AZStd::string& irradianceTextureRelativePath,
const AZStd::string& distanceTextureRelativePath,
const AZStd::string& relocationTextureRelativePath,
const AZStd::string& classificationTextureRelativePath)
{
AZ_Assert(probeGrid.get(), "BakeTextures called with an invalid handle");
AddNotificationEntry(irradianceTextureRelativePath);
AddNotificationEntry(distanceTextureRelativePath);
AddNotificationEntry(relocationTextureRelativePath);
AddNotificationEntry(classificationTextureRelativePath);
probeGrid->GetTextureReadback().BeginTextureReadback(callback);
}
void DiffuseProbeGridFeatureProcessor::UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid)
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::RealTime)
{
// add to side list of real-time grids
auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr<DiffuseProbeGrid> const& entry)
{
return (entry == diffuseProbeGrid);
});
if (itEntry == m_realTimeDiffuseProbeGrids.end())
{
m_realTimeDiffuseProbeGrids.push_back(diffuseProbeGrid);
}
}
else
{
// remove from side list of real-time grids
auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr<DiffuseProbeGrid> const& entry)
{
return (entry == diffuseProbeGrid);
});
if (itEntry != m_realTimeDiffuseProbeGrids.end())
{
m_realTimeDiffuseProbeGrids.erase(itEntry);
}
}
}
void DiffuseProbeGridFeatureProcessor::AddNotificationEntry(const AZStd::string& relativePath)
{
AZStd::string assetPath = relativePath + ".streamingimage";
// check to see if this is an existing asset
AZ::Data::AssetId assetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetId,
&AZ::Data::AssetCatalogRequests::GetAssetIdByPath,
assetPath.c_str(),
azrtti_typeid<AZ::RPI::StreamingImageAsset>(),
false);
// we only track notifications for new texture assets, existing assets are automatically reloaded by the RPI
if (!assetId.IsValid())
{
m_notifyTextureAssets.push_back({ assetPath, assetId });
}
}
bool DiffuseProbeGridFeatureProcessor::CheckTextureAssetNotification(
const AZStd::string& relativePath,
Data::Asset<RPI::StreamingImageAsset>& outTextureAsset,
DiffuseProbeGridTextureNotificationType& outNotificationType)
{
for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification)
{
if (itNotification->m_relativePath == relativePath)
{
outNotificationType = itNotification->m_notificationType;
if (outNotificationType != DiffuseProbeGridTextureNotificationType::None)
{
outTextureAsset = itNotification->m_asset;
m_notifyTextureAssets.erase(itNotification);
}
return true;
}
}
return false;
}
bool DiffuseProbeGridFeatureProcessor::AreBakedTexturesReferenced(
const AZStd::string& irradianceTextureRelativePath,
const AZStd::string& distanceTextureRelativePath,
const AZStd::string& relocationTextureRelativePath,
const AZStd::string& classificationTextureRelativePath)
{
for (auto& diffuseProbeGrid : m_diffuseProbeGrids)
{
if ((diffuseProbeGrid->GetBakedIrradianceRelativePath() == irradianceTextureRelativePath) ||
(diffuseProbeGrid->GetBakedDistanceRelativePath() == distanceTextureRelativePath) ||
(diffuseProbeGrid->GetBakedRelocationRelativePath() == relocationTextureRelativePath) ||
(diffuseProbeGrid->GetBakedClassificationRelativePath() == classificationTextureRelativePath))
{
return true;
}
}
return false;
}
void DiffuseProbeGridFeatureProcessor::SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode)
{
AZ_Assert(probeGrid.get(), "SetMode called with an invalid handle");
probeGrid->SetMode(mode);
UpdateRealTimeList(probeGrid);
m_probeGridSortRequired = true;
}
void DiffuseProbeGridFeatureProcessor::SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures)
{
AZ_Assert(probeGrid.get(), "SetBakedTextures called with an invalid handle");
probeGrid->SetBakedTextures(bakedTextures);
}
void DiffuseProbeGridFeatureProcessor::CreateBoxMesh()
{
// vertex positions
@@ -418,5 +589,34 @@ namespace AZ
}
}
void DiffuseProbeGridFeatureProcessor::HandleAssetNotification(Data::Asset<Data::AssetData> asset, DiffuseProbeGridTextureNotificationType notificationType)
{
for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification)
{
if (itNotification->m_assetId == asset.GetId())
{
// store the texture asset
itNotification->m_asset = Data::static_pointer_cast<RPI::StreamingImageAsset>(asset);
itNotification->m_notificationType = notificationType;
// stop notifications on this asset
Data::AssetBus::MultiHandler::BusDisconnect(itNotification->m_assetId);
break;
}
}
}
void DiffuseProbeGridFeatureProcessor::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Ready);
}
void DiffuseProbeGridFeatureProcessor::OnAssetError(Data::Asset<Data::AssetData> asset)
{
AZ_Error("ReflectionProbeFeatureProcessor", false, "Failed to load cubemap [%s]", asset.GetHint().c_str());
HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Error);
}
} // namespace Render
} // namespace AZ
@@ -22,6 +22,7 @@ namespace AZ
//! This class manages DiffuseProbeGrids which generate diffuse global illumination
class DiffuseProbeGridFeatureProcessor final
: public DiffuseProbeGridFeatureProcessorInterface
, private Data::AssetBus::MultiHandler
{
public:
AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessor, "{BCD232F9-1EBF-4D0D-A5F4-84AEC933A93C}", DiffuseProbeGridFeatureProcessorInterface);
@@ -46,6 +47,27 @@ namespace AZ
void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) override;
void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) override;
void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) override;
void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) override;
void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) override;
void BakeTextures(
const DiffuseProbeGridHandle& probeGrid,
DiffuseProbeGridBakeTexturesCallback callback,
const AZStd::string& irradianceTextureRelativePath,
const AZStd::string& distanceTextureRelativePath,
const AZStd::string& relocationTextureRelativePath,
const AZStd::string& classificationTextureRelativePath) override;
bool CheckTextureAssetNotification(
const AZStd::string& relativePath,
Data::Asset<RPI::StreamingImageAsset>& outTextureAsset,
DiffuseProbeGridTextureNotificationType& outNotificationType) override;
bool AreBakedTexturesReferenced(
const AZStd::string& irradianceTextureRelativePath,
const AZStd::string& distanceTextureRelativePath,
const AZStd::string& relocationTextureRelativePath,
const AZStd::string& classificationTextureRelativePath) override;
// FeatureProcessor overrides
void Activate() override;
@@ -56,12 +78,28 @@ namespace AZ
using DiffuseProbeGridVector = AZStd::vector<AZStd::shared_ptr<DiffuseProbeGrid>>;
DiffuseProbeGridVector& GetProbeGrids() { return m_diffuseProbeGrids; }
// retrieve the side list of probe grids that are using real-time (raytraced) mode
DiffuseProbeGridVector& GetRealTimeProbeGrids() { return m_realTimeDiffuseProbeGrids; }
private:
AZ_DISABLE_COPY_MOVE(DiffuseProbeGridFeatureProcessor);
// create the box vertex and index streams, which are used to render the probe volumes
void CreateBoxMesh();
// AssetBus::MultiHandler overrides...
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
void OnAssetError(Data::Asset<Data::AssetData> asset) override;
// updates the real-time list for a specific probe grid
void UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid);
// adds a notification entry for a new asset
void AddNotificationEntry(const AZStd::string& relativePath);
// notifies and removes the notification entry
void HandleAssetNotification(Data::Asset<Data::AssetData> asset, DiffuseProbeGridTextureNotificationType notificationType);
// RPI::SceneNotificationBus::Handler overrides
void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override;
void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override;
@@ -70,10 +108,13 @@ namespace AZ
void UpdatePipelineStates();
void UpdatePasses();
// list of diffuse probe grids
// list of all diffuse probe grids
const size_t InitialProbeGridAllocationSize = 64;
DiffuseProbeGridVector m_diffuseProbeGrids;
// side list of diffuse probe grids that are in real-time mode (subset of m_diffuseProbeGrids)
DiffuseProbeGridVector m_realTimeDiffuseProbeGrids;
// position structure for the box vertices
struct Position
{
@@ -102,6 +143,17 @@ namespace AZ
// indicates the the diffuse probe grid render pipeline state needs to be updated
bool m_needUpdatePipelineStates = false;
// list of texture assets that we need to check during Simulate() to see if they are ready
struct NotifyTextureAssetEntry
{
AZStd::string m_relativePath;
AZ::Data::AssetId m_assetId;
Data::Asset<RPI::StreamingImageAsset> m_asset;
DiffuseProbeGridTextureNotificationType m_notificationType = DiffuseProbeGridTextureNotificationType::None;
};
typedef AZStd::vector<NotifyTextureAssetEntry> NotifyTextureAssetVector;
NotifyTextureAssetVector m_notifyTextureAssets;
};
} // namespace Render
} // namespace AZ
@@ -132,7 +132,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return;
@@ -210,10 +210,10 @@ namespace AZ
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
frameGraph.SetEstimatedItemCount(aznumeric_cast<uint32_t>(diffuseProbeGridFeatureProcessor->GetProbeGrids().size()));
frameGraph.SetEstimatedItemCount(aznumeric_cast<uint32_t>(diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().size()));
frameGraph.ExecuteAfter(m_rayTracingScopeProducerShaderTable->GetScopeId());
for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// TLAS
{
@@ -318,7 +318,7 @@ namespace AZ
rayTracingFeatureProcessor->GetMeshInfoBuffer() &&
rayTracingFeatureProcessor->GetSubMeshCount())
{
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader
// inputs (see line ValidateSetImageView() in ShaderResourceGroupData.cpp)
@@ -341,7 +341,7 @@ namespace AZ
m_rayTracingShaderTable)
{
// submit the DispatchRaysItem for each DiffuseProbeGrid
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
const RHI::ShaderResourceGroup* shaderResourceGroups[] = {
diffuseProbeGrid->GetRayTraceSrg()->GetRHIShaderResourceGroup(),
@@ -91,7 +91,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty())
{
// no diffuse probe grids
return;
@@ -108,7 +108,7 @@ namespace AZ
// create the Relocation Srgs for each DiffuseProbeGrid, and check to see if any grids need relocation
bool needRelocation = false;
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
uint32_t rayTracingDataRevision = rayTracingFeatureProcessor->GetRevision();
if (rayTracingDataRevision != m_rayTracingDataRevision)
@@ -139,7 +139,7 @@ namespace AZ
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// probe raytrace image
{
@@ -167,7 +167,7 @@ namespace AZ
{
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs
// (see ValidateSetImageView() in ShaderResourceGroupData.cpp)
@@ -187,7 +187,7 @@ namespace AZ
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
// submit the DispatchItems for each DiffuseProbeGrid
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetRelocationSrg()->GetRHIShaderResourceGroup();
commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup);
@@ -79,6 +79,12 @@ namespace AZ
params.m_scissorState = scissor;
Base::FrameBeginInternal(params);
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids())
{
// process attachment readback
diffuseProbeGrid->GetTextureReadback().FrameBegin(params);
}
}
void DiffuseProbeGridRenderPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
@@ -88,8 +94,21 @@ namespace AZ
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked &&
!diffuseProbeGrid->HasValidBakedTextures())
{
continue;
}
// probe irradiance image
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked)
{
// import the irradiance image now, since it is baked and therefore was not imported during the raytracing pass
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetIrradianceImageAttachmentId(), diffuseProbeGrid->GetIrradianceImage());
AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeIrradianceImage");
}
RHI::ImageScopeAttachmentDescriptor desc;
desc.m_attachmentId = diffuseProbeGrid->GetIrradianceImageAttachmentId();
desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeIrradianceImageViewDescriptor;
@@ -100,6 +119,13 @@ namespace AZ
// probe distance image
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked)
{
// import the distance image now, since it is baked and therefore was not imported during the raytracing pass
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetDistanceImageAttachmentId(), diffuseProbeGrid->GetDistanceImage());
AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeDistanceImage");
}
RHI::ImageScopeAttachmentDescriptor desc;
desc.m_attachmentId = diffuseProbeGrid->GetDistanceImageAttachmentId();
desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeDistanceImageViewDescriptor;
@@ -110,6 +136,13 @@ namespace AZ
// probe relocation image
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked)
{
// import the relocation image now, since it is baked and therefore was not imported during the raytracing pass
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetRelocationImageAttachmentId(), diffuseProbeGrid->GetRelocationImage());
AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeRelocationImage");
}
RHI::ImageScopeAttachmentDescriptor desc;
desc.m_attachmentId = diffuseProbeGrid->GetRelocationImageAttachmentId();
desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeRelocationImageViewDescriptor;
@@ -120,6 +153,13 @@ namespace AZ
// probe classification image
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked)
{
// import the classification image now, since it is baked and therefore was not imported during the raytracing pass
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetClassificationImageAttachmentId(), diffuseProbeGrid->GetClassificationImage());
AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeClassificationImage");
}
RHI::ImageScopeAttachmentDescriptor desc;
desc.m_attachmentId = diffuseProbeGrid->GetClassificationImageAttachmentId();
desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeClassificationImageViewDescriptor;
@@ -127,6 +167,8 @@ namespace AZ
frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
}
diffuseProbeGrid->GetTextureReadback().Update(GetName());
}
Base::SetupFrameGraphDependencies(frameGraph);
@@ -139,6 +181,12 @@ namespace AZ
for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
{
if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked &&
!diffuseProbeGrid->HasValidBakedTextures())
{
continue;
}
// the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs
// (see ValidateSetImageView() of ShaderResourceGroupData.cpp)
diffuseProbeGrid->UpdateRenderObjectSrg();
@@ -0,0 +1,134 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <DiffuseProbeGrid/DiffuseProbeGridTextureReadBack.h>
#include <DiffuseProbeGrid/DiffuseProbeGrid.h>
namespace AZ
{
namespace Render
{
DiffuseProbeGridTextureReadback::DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid)
: m_diffuseProbeGrid(diffuseProbeGrid)
{
}
void DiffuseProbeGridTextureReadback::BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback)
{
AZ_Assert(m_readbackState == DiffuseProbeGridReadbackState::Idle, "DiffuseProbeGridTextureReadback is already processing a readback request");
m_callback = callback;
m_readbackState = DiffuseProbeGridReadbackState::Irradiance;
}
void DiffuseProbeGridTextureReadback::Update(const AZ::Name& passName)
{
if (m_readbackState == DiffuseProbeGridReadbackState::Idle || m_readbackState == DiffuseProbeGridReadbackState::Complete)
{
return;
}
if (m_attachmentReadback.get() && m_attachmentReadback->GetReadbackState() > RPI::AttachmentReadback::ReadbackState::Idle)
{
// still processing previous request
return;
}
AZStd::string readbackName = AZStd::string::format("DiffuseProbeGridReadback_%s", passName.GetCStr());
RHI::ImageDescriptor descriptor;
RHI::AttachmentId attachmentId;
RPI::AttachmentReadback::CallbackFunction callbackFunction;
switch (m_readbackState)
{
case DiffuseProbeGridReadbackState::Irradiance:
descriptor = m_diffuseProbeGrid->GetIrradianceImage()->GetDescriptor();
attachmentId = m_diffuseProbeGrid->GetIrradianceImageAttachmentId();
callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
m_irradianceReadbackResult = readbackResult;
m_readbackState = DiffuseProbeGridReadbackState::Distance;
};
break;
case DiffuseProbeGridReadbackState::Distance:
descriptor = m_diffuseProbeGrid->GetDistanceImage()->GetDescriptor();
attachmentId = m_diffuseProbeGrid->GetDistanceImageAttachmentId();
callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
m_distanceReadbackResult = readbackResult;
m_readbackState = DiffuseProbeGridReadbackState::Relocation;
};
break;
case DiffuseProbeGridReadbackState::Relocation:
descriptor = m_diffuseProbeGrid->GetRelocationImage()->GetDescriptor();
attachmentId = m_diffuseProbeGrid->GetRelocationImageAttachmentId();
callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
m_relocationReadbackResult = readbackResult;
m_readbackState = DiffuseProbeGridReadbackState::Classification;
};
break;
case DiffuseProbeGridReadbackState::Classification:
descriptor = m_diffuseProbeGrid->GetClassificationImage()->GetDescriptor();
attachmentId = m_diffuseProbeGrid->GetClassificationImageAttachmentId();
callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
m_classificationReadbackResult = readbackResult;
m_readbackState = DiffuseProbeGridReadbackState::Complete;
};
break;
default:
AZ_Assert(false, "Unknown readback state");
}
m_attachmentReadback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "DiffuseProbeGridTextureReadBack" });
m_attachmentReadback->SetCallback(callbackFunction);
AZ::RPI::PassAttachment passAttachment;
passAttachment.m_descriptor = descriptor;
passAttachment.m_path = attachmentId;
passAttachment.m_name = readbackName;
passAttachment.m_lifetime = RHI::AttachmentLifetimeType::Imported;
m_attachmentReadback->ReadPassAttachment(&passAttachment, AZ::Name(readbackName));
}
void DiffuseProbeGridTextureReadback::FrameBegin(AZ::RPI::Pass::FramePrepareParams& params)
{
if (m_readbackState == DiffuseProbeGridReadbackState::Idle)
{
return;
}
if (!m_attachmentReadback.get())
{
return;
}
if (m_readbackState == DiffuseProbeGridReadbackState::Complete)
{
// readback of all textures is complete, invoke callback and return to Idle state
m_callback(
{ m_irradianceReadbackResult.m_dataBuffer, m_irradianceReadbackResult.m_imageDescriptor.m_format, m_irradianceReadbackResult.m_imageDescriptor.m_size },
{ m_distanceReadbackResult.m_dataBuffer, m_distanceReadbackResult.m_imageDescriptor.m_format, m_distanceReadbackResult.m_imageDescriptor.m_size },
{ m_relocationReadbackResult.m_dataBuffer, m_relocationReadbackResult.m_imageDescriptor.m_format, m_relocationReadbackResult.m_imageDescriptor.m_size },
{ m_classificationReadbackResult.m_dataBuffer, m_classificationReadbackResult.m_imageDescriptor.m_format, m_classificationReadbackResult.m_imageDescriptor.m_size });
m_readbackState = DiffuseProbeGridReadbackState::Idle;
m_attachmentReadback.reset();
return;
}
m_attachmentReadback->FrameBegin(params);
}
} // namespace Render
} // namespace AZ
@@ -0,0 +1,60 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/RHI/ScopeProducer.h>
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
#include <Atom/RPI.Public/Pass/Pass.h>
#include <Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h>
namespace AZ
{
namespace Render
{
class DiffuseProbeGrid;
enum class DiffuseProbeGridReadbackState
{
Idle,
Irradiance,
Distance,
Relocation,
Classification,
Complete
};
//! This class contains functionality necessary to read back the DiffuseProbeGrid textures, which
//! allows them to be saved as assets to run the DiffuseProbeGrid in non-realtime mode.
class DiffuseProbeGridTextureReadback final
{
public:
DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid);
~DiffuseProbeGridTextureReadback() = default;
void BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback);
void Update(const AZ::Name& passName);
void FrameBegin(AZ::RPI::Pass::FramePrepareParams& params);
private:
DiffuseProbeGrid* m_diffuseProbeGrid = nullptr;
DiffuseProbeGridReadbackState m_readbackState = DiffuseProbeGridReadbackState::Idle;
AZStd::shared_ptr<AZ::RPI::AttachmentReadback> m_attachmentReadback;
DiffuseProbeGridBakeTexturesCallback m_callback;
AZ::RPI::AttachmentReadback::ReadbackResult m_irradianceReadbackResult;
AZ::RPI::AttachmentReadback::ReadbackResult m_distanceReadbackResult;
AZ::RPI::AttachmentReadback::ReadbackResult m_relocationReadbackResult;
AZ::RPI::AttachmentReadback::ReadbackResult m_classificationReadbackResult;
};
} // namespace Render
} // namespace AZ
@@ -135,6 +135,8 @@ set(FILES
Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h
Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp
Source/DiffuseProbeGrid/DiffuseProbeGrid.h
Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp
Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h
Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h
Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp
Source/DisplayMapper/AcesOutputTransformPass.cpp
@@ -44,7 +44,17 @@ namespace AZ
->Field("AmbientMultiplier", &DiffuseProbeGridComponentConfig::m_ambientMultiplier)
->Field("ViewBias", &DiffuseProbeGridComponentConfig::m_viewBias)
->Field("NormalBias", &DiffuseProbeGridComponentConfig::m_normalBias)
;
->Field("EditorMode", &DiffuseProbeGridComponentConfig::m_editorMode)
->Field("RuntimeMode", &DiffuseProbeGridComponentConfig::m_runtimeMode)
->Field("BakedIrradianceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureRelativePath)
->Field("BakedDistanceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureRelativePath)
->Field("BakedRelocationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureRelativePath)
->Field("BakedClassificationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureRelativePath)
->Field("BakedIrradianceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureAsset)
->Field("BakedDistanceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureAsset)
->Field("BakedRelocationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureAsset)
->Field("BakedClassificationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureAsset)
;
}
}
@@ -110,6 +120,26 @@ namespace AZ
m_boxShapeInterface = LmbrCentral::BoxShapeComponentRequestsBus::FindFirstHandler(m_entityId);
AZ_Assert(m_boxShapeInterface, "DiffuseProbeGridComponentController was unable to find box shape component");
// special handling is required if this component is being cloned in the editor:
// check to see if the baked textures are already referenced by another DiffuseProbeGrid
if (m_featureProcessor->AreBakedTexturesReferenced(
m_configuration.m_bakedIrradianceTextureRelativePath,
m_configuration.m_bakedDistanceTextureRelativePath,
m_configuration.m_bakedRelocationTextureRelativePath,
m_configuration.m_bakedClassificationTextureRelativePath))
{
// clear the baked texture paths and assets
m_configuration.m_bakedIrradianceTextureRelativePath.clear();
m_configuration.m_bakedDistanceTextureRelativePath.clear();
m_configuration.m_bakedRelocationTextureRelativePath.clear();
m_configuration.m_bakedClassificationTextureRelativePath.clear();
m_configuration.m_bakedIrradianceTextureAsset.Reset();
m_configuration.m_bakedDistanceTextureAsset.Reset();
m_configuration.m_bakedRelocationTextureAsset.Reset();
m_configuration.m_bakedClassificationTextureAsset.Reset();
}
// add this diffuse probe grid to the feature processor
const AZ::Transform& transform = m_transformInterface->GetWorldTM();
m_handle = m_featureProcessor->AddProbeGrid(transform, m_configuration.m_extents, m_configuration.m_probeSpacing);
@@ -118,11 +148,61 @@ namespace AZ
m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias);
m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias);
// load the baked texture assets, but only if they are all valid
if (m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() &&
m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() &&
m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() &&
m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid())
{
Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedIrradianceTextureAsset.GetId());
Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedDistanceTextureAsset.GetId());
Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedRelocationTextureAsset.GetId());
Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedClassificationTextureAsset.GetId());
m_configuration.m_bakedIrradianceTextureAsset.QueueLoad();
m_configuration.m_bakedDistanceTextureAsset.QueueLoad();
m_configuration.m_bakedRelocationTextureAsset.QueueLoad();
m_configuration.m_bakedClassificationTextureAsset.QueueLoad();
}
else if (m_configuration.m_runtimeMode == DiffuseProbeGridMode::Baked ||
m_configuration.m_runtimeMode == DiffuseProbeGridMode::AutoSelect ||
m_configuration.m_editorMode == DiffuseProbeGridMode::Baked ||
m_configuration.m_editorMode == DiffuseProbeGridMode::AutoSelect)
{
AZ_Error("DiffuseProbeGrid", false, "DiffuseProbeGrid mdoe is set to Baked or Auto-Select, but it does not have baked texture assets. Please re-bake this DiffuseProbeGrid.");
}
m_featureProcessor->SetMode(m_handle, m_configuration.m_runtimeMode);
// set box shape component dimensions from the configuration
// this will invoke the OnShapeChanged() handler and set the outer extents on the feature processor
m_boxShapeInterface->SetBoxDimensions(m_configuration.m_extents);
}
void DiffuseProbeGridComponentController::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
// if all assets are ready we can set the baked texture images
if (m_configuration.m_bakedIrradianceTextureAsset.IsReady() &&
m_configuration.m_bakedDistanceTextureAsset.IsReady() &&
m_configuration.m_bakedRelocationTextureAsset.IsReady() &&
m_configuration.m_bakedClassificationTextureAsset.IsReady())
{
Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedIrradianceTextureAsset.GetId());
Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedDistanceTextureAsset.GetId());
Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedRelocationTextureAsset.GetId());
Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedClassificationTextureAsset.GetId());
UpdateBakedTextures();
}
}
void DiffuseProbeGridComponentController::OnAssetError(Data::Asset<Data::AssetData> asset)
{
Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
AZ_Error("DiffuseProbeGrid", false, "Failed to load baked texture [%s], please re-bake this DiffuseProbeGrid.", asset.GetId().ToString<AZStd::string>().c_str());
}
void DiffuseProbeGridComponentController::Deactivate()
{
if (m_featureProcessor)
@@ -212,20 +292,96 @@ namespace AZ
void DiffuseProbeGridComponentController::SetAmbientMultiplier(float ambientMultiplier)
{
if (!m_featureProcessor)
{
return;
}
m_configuration.m_ambientMultiplier = ambientMultiplier;
m_featureProcessor->SetAmbientMultiplier(m_handle, m_configuration.m_ambientMultiplier);
}
void DiffuseProbeGridComponentController::SetViewBias(float viewBias)
{
if (!m_featureProcessor)
{
return;
}
m_configuration.m_viewBias = viewBias;
m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias);
}
void DiffuseProbeGridComponentController::SetNormalBias(float normalBias)
{
if (!m_featureProcessor)
{
return;
}
m_configuration.m_normalBias = normalBias;
m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias);
}
void DiffuseProbeGridComponentController::SetEditorMode(DiffuseProbeGridMode editorMode)
{
if (!m_featureProcessor)
{
return;
}
// update the configuration and change the DiffuseProbeGrid mode
m_configuration.m_editorMode = editorMode;
m_featureProcessor->SetMode(m_handle, m_configuration.m_editorMode);
}
void DiffuseProbeGridComponentController::SetRuntimeMode(DiffuseProbeGridMode runtimeMode)
{
if (!m_featureProcessor)
{
return;
}
// only update the configuration
m_configuration.m_runtimeMode = runtimeMode;
}
void DiffuseProbeGridComponentController::BakeTextures(DiffuseProbeGridBakeTexturesCallback callback)
{
if (!m_featureProcessor)
{
return;
}
m_featureProcessor->BakeTextures(
m_handle,
callback,
m_configuration.m_bakedIrradianceTextureRelativePath,
m_configuration.m_bakedDistanceTextureRelativePath,
m_configuration.m_bakedRelocationTextureRelativePath,
m_configuration.m_bakedClassificationTextureRelativePath);
}
void DiffuseProbeGridComponentController::UpdateBakedTextures()
{
if (!m_featureProcessor)
{
return;
}
DiffuseProbeGridBakedTextures bakedTextures;
bakedTextures.m_irradianceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedIrradianceTextureAsset);
bakedTextures.m_irradianceImageRelativePath = m_configuration.m_bakedIrradianceTextureRelativePath;
bakedTextures.m_distanceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedDistanceTextureAsset);
bakedTextures.m_distanceImageRelativePath = m_configuration.m_bakedDistanceTextureRelativePath;
bakedTextures.m_relocationImageDescriptor = m_configuration.m_bakedRelocationTextureAsset->GetImageDescriptor();
bakedTextures.m_relocationImageData = m_configuration.m_bakedRelocationTextureAsset->GetSubImageData(0, 0);
bakedTextures.m_relocationImageRelativePath = m_configuration.m_bakedRelocationTextureRelativePath;
bakedTextures.m_classificationImageDescriptor = m_configuration.m_bakedClassificationTextureAsset->GetImageDescriptor();
bakedTextures.m_classificationImageData = m_configuration.m_bakedClassificationTextureAsset->GetSubImageData(0, 0);
bakedTextures.m_classificationImageRelativePath = m_configuration.m_bakedClassificationTextureRelativePath;
m_featureProcessor->SetBakedTextures(m_handle, bakedTextures);
}
} // namespace Render
} // namespace AZ
@@ -39,6 +39,19 @@ namespace AZ
float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier;
float m_viewBias = DefaultDiffuseProbeGridViewBias;
float m_normalBias = DefaultDiffuseProbeGridNormalBias;
DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime;
DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime;
AZStd::string m_bakedIrradianceTextureRelativePath;
AZStd::string m_bakedDistanceTextureRelativePath;
AZStd::string m_bakedRelocationTextureRelativePath;
AZStd::string m_bakedClassificationTextureRelativePath;
Data::Asset<RPI::StreamingImageAsset> m_bakedIrradianceTextureAsset;
Data::Asset<RPI::StreamingImageAsset> m_bakedDistanceTextureAsset;
Data::Asset<RPI::StreamingImageAsset> m_bakedRelocationTextureAsset;
Data::Asset<RPI::StreamingImageAsset> m_bakedClassificationTextureAsset;
};
class DiffuseProbeGridComponentController final
@@ -79,12 +92,24 @@ namespace AZ
// ShapeComponentNotificationsBus overrides
void OnShapeChanged(ShapeChangeReasons changeReason) override;
// AssetBus overrides
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
void OnAssetError(Data::Asset<Data::AssetData> asset) override;
// Property handlers
bool ValidateProbeSpacing(const AZ::Vector3& newSpacing);
void SetProbeSpacing(const AZ::Vector3& probeSpacing);
void SetAmbientMultiplier(float ambientMultiplier);
void SetViewBias(float viewBias);
void SetNormalBias(float normalBias);
void SetEditorMode(DiffuseProbeGridMode editorMode);
void SetRuntimeMode(DiffuseProbeGridMode runtimeMode);
// Bake the diffuse probe grid textures to assets
void BakeTextures(DiffuseProbeGridBakeTexturesCallback callback);
// Update the baked texture assets from the configuration
void UpdateBakedTextures();
// box shape component, used for defining the outer extents of the probe area
LmbrCentral::BoxShapeComponentRequests* m_boxShapeInterface = nullptr;
@@ -16,6 +16,15 @@
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/IO/SystemFile.h>
#include <Atom/RPI.Reflect/Image/StreamingImagePoolAsset.h>
#include <Atom/Utils/DdsFile.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QApplication>
#include <QMessageBox>
#include <QProgressDialog>
AZ_POP_DISABLE_WARNING
namespace AZ
{
@@ -35,7 +44,9 @@ namespace AZ
->Field("ambientMultiplier", &EditorDiffuseProbeGridComponent::m_ambientMultiplier)
->Field("viewBias", &EditorDiffuseProbeGridComponent::m_viewBias)
->Field("normalBias", &EditorDiffuseProbeGridComponent::m_normalBias)
;
->Field("editorMode", &EditorDiffuseProbeGridComponent::m_editorMode)
->Field("runtimeMode", &EditorDiffuseProbeGridComponent::m_runtimeMode)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
@@ -48,25 +59,26 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing")
->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing (meters between probes)")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis, in meters")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis, in meters")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateY)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis, in meters")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateZ)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged)
->ClassElement(AZ::Edit::ClassElements::Group, "Grid Settings")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_ambientMultiplier, "Ambient Multiplier", "Multiplier for the irradiance intensity")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnAmbientMultiplierChanged)
->Attribute(Edit::Attributes::Decimals, 0)
->Attribute(Edit::Attributes::Step, 1.0f)
->Attribute(Edit::Attributes::Decimals, 1)
->Attribute(Edit::Attributes::Step, 0.1f)
->Attribute(Edit::Attributes::Min, 0.0f)
->Attribute(Edit::Attributes::Max, 10.0f)
->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_viewBias, "View Bias", "View bias adjustment")
@@ -81,6 +93,27 @@ namespace AZ
->Attribute(Edit::Attributes::Step, 0.1f)
->Attribute(Edit::Attributes::Min, 0.0f)
->Attribute(Edit::Attributes::Max, 1.0f)
->ClassElement(AZ::Edit::ClassElements::EditorData, "Grid mode")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_editorMode, "Editor Mode", "Controls whether the editor uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available")
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEditorModeChanged)
->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)")
->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked")
->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select")
->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_runtimeMode, "Runtime Mode", "Controls whether the runtime uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available")
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnRuntimeModeChanged)
->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)")
->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked")
->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select")
->ClassElement(AZ::Edit::ClassElements::Group, "Bake Textures")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->UIElement(AZ::Edit::UIHandlers::Button, "Bake Textures", "Bake the Diffuse Probe Grid textures to static assets that will be used when the mode is set to Baked")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Textures")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting)
;
editContext->Class<DiffuseProbeGridComponentController>(
@@ -90,12 +123,6 @@ namespace AZ
->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseProbeGridComponentController::m_configuration, "Configuration", "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
editContext->Class<DiffuseProbeGridComponentConfig>(
"DiffuseProbeGridComponentConfig", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
@@ -121,15 +148,73 @@ namespace AZ
BaseClass::Activate();
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
AZ::TickBus::Handler::BusConnect();
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect();
}
void EditorDiffuseProbeGridComponent::Deactivate()
{
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
BaseClass::Deactivate();
}
void EditorDiffuseProbeGridComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if (!m_controller.m_featureProcessor)
{
return;
}
DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration;
// set the editor mode, which will override the runtime mode set by the controller
if (!m_editorModeSet)
{
m_controller.m_featureProcessor->SetMode(m_controller.m_handle, configuration.m_editorMode);
m_editorModeSet = true;
}
CheckTextureAssetNotification(configuration.m_bakedIrradianceTextureRelativePath, configuration.m_bakedIrradianceTextureAsset);
CheckTextureAssetNotification(configuration.m_bakedDistanceTextureRelativePath, configuration.m_bakedDistanceTextureAsset);
CheckTextureAssetNotification(configuration.m_bakedRelocationTextureRelativePath, configuration.m_bakedRelocationTextureAsset);
CheckTextureAssetNotification(configuration.m_bakedClassificationTextureRelativePath, configuration.m_bakedClassificationTextureAsset);
}
void EditorDiffuseProbeGridComponent::CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset<RPI::StreamingImageAsset>& configurationAsset)
{
Data::Asset<RPI::StreamingImageAsset> textureAsset;
DiffuseProbeGridTextureNotificationType notificationType = DiffuseProbeGridTextureNotificationType::None;
if (m_controller.m_featureProcessor->CheckTextureAssetNotification(relativePath + ".streamingimage", textureAsset, notificationType))
{
if (notificationType == DiffuseProbeGridTextureNotificationType::Ready)
{
// bake is complete, update configuration with the new baked texture asset
AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid Texture Bake");
configurationAsset = { textureAsset.GetAs<RPI::StreamingImageAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
SetDirty();
if (m_controller.m_configuration.m_bakedIrradianceTextureAsset.IsReady() &&
m_controller.m_configuration.m_bakedDistanceTextureAsset.IsReady() &&
m_controller.m_configuration.m_bakedClassificationTextureAsset.IsReady() &&
m_controller.m_configuration.m_bakedRelocationTextureAsset.IsReady())
{
m_controller.UpdateBakedTextures();
}
}
else if (notificationType == DiffuseProbeGridTextureNotificationType::Error)
{
QMessageBox::information(
QApplication::activeWindow(),
"Diffuse Probe Grid",
"Diffuse Probe Grid texture failed to bake, please check the Asset Processor for more information.",
QMessageBox::Ok);
}
}
}
AZ::Aabb EditorDiffuseProbeGridComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
{
return m_controller.GetAabb();
@@ -140,11 +225,19 @@ namespace AZ
return false;
}
void EditorDiffuseProbeGridComponent::OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible)
{
if ((GetEntityId() == entityId) && !visible)
{
m_editorModeSet = false;
}
}
AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
{
if (!m_controller.m_featureProcessor)
{
return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again"));
return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing."));
}
float newProbeSpacingX = *(reinterpret_cast<float*>(newValue));
@@ -152,7 +245,7 @@ namespace AZ
Vector3 newSpacing(newProbeSpacingX, m_probeSpacingY, m_probeSpacingZ);
if (!m_controller.ValidateProbeSpacing(newSpacing))
{
return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents"));
return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents."));
}
return AZ::Success();
@@ -162,7 +255,7 @@ namespace AZ
{
if (!m_controller.m_featureProcessor)
{
return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again"));
return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing."));
}
float newProbeSpacingY = *(reinterpret_cast<float*>(newValue));
@@ -170,7 +263,7 @@ namespace AZ
Vector3 newSpacing(m_probeSpacingX, newProbeSpacingY, m_probeSpacingZ);
if (!m_controller.ValidateProbeSpacing(newSpacing))
{
return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents"));
return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents."));
}
return AZ::Success();
@@ -180,7 +273,7 @@ namespace AZ
{
if (!m_controller.m_featureProcessor)
{
return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again"));
return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing."));
}
float newProbeSpacingZ = *(reinterpret_cast<float*>(newValue));
@@ -188,7 +281,7 @@ namespace AZ
Vector3 newSpacing(m_probeSpacingX, m_probeSpacingY, newProbeSpacingZ);
if (!m_controller.ValidateProbeSpacing(newSpacing))
{
return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents"));
return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents."));
}
return AZ::Success();
@@ -218,5 +311,226 @@ namespace AZ
m_controller.SetNormalBias(m_normalBias);
return AZ::Edit::PropertyRefreshLevels::None;
}
AZ::u32 EditorDiffuseProbeGridComponent::OnEditorModeChanged()
{
// this will update the configuration and also change the DiffuseProbeGrid mode
m_controller.SetEditorMode(m_editorMode);
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
AZ::u32 EditorDiffuseProbeGridComponent::OnRuntimeModeChanged()
{
// this will only update the configuration
m_controller.SetRuntimeMode(m_runtimeMode);
return AZ::Edit::PropertyRefreshLevels::None;
}
AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnModeChangeValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
{
DiffuseProbeGridMode newMode = (*(reinterpret_cast<DiffuseProbeGridMode*>(newValue)));
if (newMode == DiffuseProbeGridMode::Baked || newMode == DiffuseProbeGridMode::AutoSelect)
{
if (!m_controller.m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() ||
!m_controller.m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() ||
!m_controller.m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() ||
!m_controller.m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid())
{
return AZ::Failure(AZStd::string("Please bake textures before changing the Diffuse Probe Grid to Baked or Auto-Select mode."));
}
}
return AZ::Success();
}
AZ::u32 EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting()
{
// the Bake button is visible only when the editor mode is set to RealTime
return m_editorMode == DiffuseProbeGridMode::RealTime ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::u32 EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid()
{
if (m_bakeInProgress)
{
return AZ::Edit::PropertyRefreshLevels::None;
}
// retrieve entity visibility
bool isHidden = false;
AzToolsFramework::EditorEntityInfoRequestBus::EventResult(
isHidden,
GetEntityId(),
&AzToolsFramework::EditorEntityInfoRequestBus::Events::IsHidden);
// the entity must be visible in order to bake
if (isHidden)
{
QMessageBox::information(
QApplication::activeWindow(),
"Diffuse Probe Grid",
"This Diffuse Probe Grid entity is hidden, it must be visible in order to bake textures.",
QMessageBox::Ok);
return AZ::Edit::PropertyRefreshLevels::None;
}
DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration;
// retrieve the source image paths from the configuration
// Note: we need to make sure to use the same source image for each bake
AZStd::string irradianceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedIrradianceTextureRelativePath, DiffuseProbeGridIrradianceFileName);
AZStd::string distanceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedDistanceTextureRelativePath, DiffuseProbeGridDistanceFileName);
AZStd::string relocationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedRelocationTextureRelativePath, DiffuseProbeGridRelocationFileName);
AZStd::string classificationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedClassificationTextureRelativePath, DiffuseProbeGridClassificationFileName);
// create the full paths
char projectPath[AZ_MAX_PATH_LEN];
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN);
AZStd::string irradianceTextureFullPath;
AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true);
AZStd::string distanceTextureFullPath;
AzFramework::StringFunc::Path::Join(projectPath, distanceTextureRelativePath.c_str(), distanceTextureFullPath, true, true);
AZStd::string relocationTextureFullPath;
AzFramework::StringFunc::Path::Join(projectPath, relocationTextureRelativePath.c_str(), relocationTextureFullPath, true, true);
AZStd::string classificationTextureFullPath;
AzFramework::StringFunc::Path::Join(projectPath, classificationTextureRelativePath.c_str(), classificationTextureFullPath, true, true);
// make sure the folder is created
AZStd::string diffuseProbeGridFolder;
AzFramework::StringFunc::Path::GetFolderPath(irradianceTextureFullPath.data(), diffuseProbeGridFolder);
AZ::IO::SystemFile::CreateDir(diffuseProbeGridFolder.c_str());
// check out the files in source control
CheckoutSourceTextureFile(irradianceTextureFullPath);
CheckoutSourceTextureFile(distanceTextureFullPath);
CheckoutSourceTextureFile(relocationTextureFullPath);
CheckoutSourceTextureFile(classificationTextureFullPath);
// update the configuration
AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid bake");
configuration.m_bakedIrradianceTextureRelativePath = irradianceTextureRelativePath;
configuration.m_bakedDistanceTextureRelativePath = distanceTextureRelativePath;
configuration.m_bakedRelocationTextureRelativePath = relocationTextureRelativePath;
configuration.m_bakedClassificationTextureRelativePath = classificationTextureRelativePath;
SetDirty();
// callback for the texture readback
DiffuseProbeGridBakeTexturesCallback bakeTexturesCallback = [=](
DiffuseProbeGridTexture irradianceTexture,
DiffuseProbeGridTexture distanceTexture,
DiffuseProbeGridTexture relocationTexture,
DiffuseProbeGridTexture classificationTexture)
{
// irradiance
{
AZ::DdsFile::DdsFileData fileData = { irradianceTexture.m_size, irradianceTexture.m_format, irradianceTexture.m_data.get() };
[[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(irradianceTextureFullPath, fileData);
AZ_Assert(outcome.IsSuccess(), "Failed to write Irradiance texture .dds file [%s]", irradianceTextureFullPath.c_str());
}
// distance
{
AZ::DdsFile::DdsFileData fileData = { distanceTexture.m_size, distanceTexture.m_format, distanceTexture.m_data.get() };
[[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(distanceTextureFullPath, fileData);
AZ_Assert(outcome.IsSuccess(), "Failed to write Distance texture .dds file [%s]", distanceTextureFullPath.c_str());
}
// relocation
{
AZ::DdsFile::DdsFileData fileData = { relocationTexture.m_size, relocationTexture.m_format, relocationTexture.m_data.get() };
[[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(relocationTextureFullPath, fileData);
AZ_Assert(outcome.IsSuccess(), "Failed to write Relocation texture .dds file [%s]", relocationTextureFullPath.c_str());
}
// classification
{
AZ::DdsFile::DdsFileData fileData = { classificationTexture.m_size, classificationTexture.m_format, classificationTexture.m_data.get() };
[[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(classificationTextureFullPath, fileData);
AZ_Assert(outcome.IsSuccess(), "Failed to write Classification texture .dds file [%s]", classificationTextureFullPath.c_str());
}
m_bakeInProgress = false;
};
m_bakeInProgress = true;
m_controller.BakeTextures(bakeTexturesCallback);
while (m_bakeInProgress)
{
QApplication::processEvents();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
}
QMessageBox::information(
QApplication::activeWindow(),
"Diffuse Probe Grid",
"Successfully baked Diffuse Probe Grid textures.",
QMessageBox::Ok);
return AZ::Edit::PropertyRefreshLevels::None;
}
AZStd::string EditorDiffuseProbeGridComponent::ValidateOrCreateNewTexturePath(const AZStd::string& configurationRelativePath, const char* fileSuffix)
{
AZStd::string relativePath = configurationRelativePath;
AZStd::string fullPath;
char projectPath[AZ_MAX_PATH_LEN];
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN);
if (!relativePath.empty())
{
// test to see if the texture file is actually there, if it was removed we need to
// generate a new filename, otherwise it will cause an error in the asset system
AzFramework::StringFunc::Path::Join(projectPath, configurationRelativePath.c_str(), fullPath, true, true);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(fullPath.c_str()))
{
// file does not exist, clear the relative path so we generate a new name
relativePath.clear();
}
}
// build a new image path if necessary
if (relativePath.empty())
{
// the file name is a combination of the entity name, a UUID, and the filemask
Entity* entity = GetEntity();
AZ_Assert(entity, "DiffuseProbeGrid entity is null");
AZ::Uuid uuid = AZ::Uuid::CreateRandom();
AZStd::string uuidString;
uuid.ToString(uuidString);
relativePath = "DiffuseProbeGrids/" + entity->GetName() + uuidString + fileSuffix;
// replace any invalid filename characters
auto invalidCharacters = [](char letter)
{
return
letter == ':' || letter == '"' || letter == '\'' ||
letter == '{' || letter == '}' ||
letter == '<' || letter == '>';
};
AZStd::replace_if(relativePath.begin(), relativePath.end(), invalidCharacters, '_');
}
return relativePath;
}
void EditorDiffuseProbeGridComponent::CheckoutSourceTextureFile(const AZStd::string& fullPath)
{
bool checkedOutSuccessfully = false;
using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus;
ApplicationBus::BroadcastResult(
checkedOutSuccessfully,
&ApplicationBus::Events::RequestEditForFileBlocking,
fullPath.c_str(),
"Checking out for edit...",
ApplicationBus::Events::RequestEditProgressCallback());
}
} // namespace Render
} // namespace AZ
@@ -12,8 +12,10 @@
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <DiffuseProbeGrid/DiffuseProbeGridComponent.h>
#include <DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h>
#include <Atom/Feature/Utils/EditorRenderComponentAdapter.h>
@@ -26,6 +28,8 @@ namespace AZ
: public EditorRenderComponentAdapter<DiffuseProbeGridComponentController, DiffuseProbeGridComponent, DiffuseProbeGridComponentConfig>
, private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
, private AzFramework::EntityDebugDisplayEventBus::Handler
, private AZ::TickBus::Handler
, private AzToolsFramework::EditorEntityInfoNotificationBus::Handler
{
public:
using BaseClass = EditorRenderComponentAdapter<DiffuseProbeGridComponentController, DiffuseProbeGridComponent, DiffuseProbeGridComponentConfig>;
@@ -41,10 +45,22 @@ namespace AZ
void Deactivate() override;
private:
// AZ::TickBus overrides
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// EditorComponentSelectionRequestsBus overrides
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
bool SupportsEditorRayIntersect() override;
// EditorEntityInfoNotifications overrides
void OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) override;
// helper functions
AZStd::string ValidateOrCreateNewTexturePath(const AZStd::string& relativePath, const char* fileSuffix);
void CheckoutSourceTextureFile(const AZStd::string& fullPath);
void CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset<RPI::StreamingImageAsset>& configurationAsset);
// property change notifications
AZ::Outcome<void, AZStd::string> OnProbeSpacingValidateX(void* newValue, const AZ::Uuid& valueType);
AZ::Outcome<void, AZStd::string> OnProbeSpacingValidateY(void* newValue, const AZ::Uuid& valueType);
@@ -53,6 +69,13 @@ namespace AZ
AZ::u32 OnAmbientMultiplierChanged();
AZ::u32 OnViewBiasChanged();
AZ::u32 OnNormalBiasChanged();
AZ::u32 OnEditorModeChanged();
AZ::u32 OnRuntimeModeChanged();
AZ::Outcome<void, AZStd::string> OnModeChangeValidate(void* newValue, const AZ::Uuid& valueType);
// Button handler
AZ::u32 BakeDiffuseProbeGrid();
AZ::u32 GetBakeDiffuseProbeGridVisibilitySetting();
// properties
float m_probeSpacingX = DefaultDiffuseProbeGridSpacing;
@@ -61,6 +84,12 @@ namespace AZ
float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier;
float m_viewBias = DefaultDiffuseProbeGridViewBias;
float m_normalBias = DefaultDiffuseProbeGridNormalBias;
DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime;
DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime;
// flags
bool m_editorModeSet = false;
AZStd::atomic_bool m_bakeInProgress = false;
};
} // namespace Render
} // namespace AZ