diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h index 546089ab1e..6516407265 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPIUtils.h @@ -60,6 +60,11 @@ namespace AZ //! Same as above. Provided as a convenience when all arguments of the 'numthreads' attributes should be assigned to RHI::DispatchDirect::m_threadsPerGroup* variables. AZ::Outcome GetComputeShaderNumThreads(const Data::Asset& shaderAsset, RHI::DispatchDirect& dispatchDirect); + //! Get single image pixel value from raw image data + //! This assumes the imageData is not empty + template + T GetImageDataPixelValue(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex = 0); + //! Get single image pixel value for specified mip and slice template T GetSubImagePixelValue(const AZ::Data::Asset& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex = 0, uint32_t mip = 0, uint32_t slice = 0); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp index 9ff26f0d3a..8a6cc80cfe 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp @@ -232,16 +232,12 @@ namespace AZ } } - template - T GetSubImagePixelValueInternal(const AZ::Data::Asset& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice) + size_t GetImageDataIndex(const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex) { - AZStd::array values{ aznumeric_cast(0) }; + auto width = imageDescriptor.m_size.m_width; + const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); - auto topLeft = AZStd::make_pair(x, y); - auto bottomRight = AZStd::make_pair(x + 1, y + 1); - GetSubImagePixelValues(imageAsset, topLeft, bottomRight, AZStd::span(values), componentIndex, mip, slice); - - return values[0]; + return (y * width + x) * numComponents + componentIndex; } } @@ -447,22 +443,60 @@ namespace AZ return GetComputeShaderNumThreads(shaderAsset, &dispatchDirect.m_threadsPerGroupX, &dispatchDirect.m_threadsPerGroupY, &dispatchDirect.m_threadsPerGroupZ); } + template<> + float GetImageDataPixelValue(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex) + { + size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex); + return Internal::RetrieveFloatValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); + } + + template<> + AZ::u32 GetImageDataPixelValue(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex) + { + size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex); + return Internal::RetrieveUintValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); + } + + template<> + AZ::s32 GetImageDataPixelValue(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex) + { + size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex); + return Internal::RetrieveIntValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); + } + + template + T GetSubImagePixelValueInternal(const AZ::Data::Asset& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice) + { + if (!imageAsset.IsReady()) + { + return aznumeric_cast(0); + } + + auto imageData = imageAsset->GetSubImageData(mip, slice); + if (imageData.empty()) + { + return aznumeric_cast(0); + } + + return GetImageDataPixelValue(imageData, imageAsset->GetImageDescriptor(), x, y, componentIndex); + } + template<> float GetSubImagePixelValue(const AZ::Data::Asset& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice) { - return Internal::GetSubImagePixelValueInternal(imageAsset, x, y, componentIndex, mip, slice); + return GetSubImagePixelValueInternal(imageAsset, x, y, componentIndex, mip, slice); } template<> AZ::u32 GetSubImagePixelValue(const AZ::Data::Asset& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice) { - return Internal::GetSubImagePixelValueInternal(imageAsset, x, y, componentIndex, mip, slice); + return GetSubImagePixelValueInternal(imageAsset, x, y, componentIndex, mip, slice); } template<> AZ::s32 GetSubImagePixelValue(const AZ::Data::Asset& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice) { - return Internal::GetSubImagePixelValueInternal(imageAsset, x, y, componentIndex, mip, slice); + return GetSubImagePixelValueInternal(imageAsset, x, y, componentIndex, mip, slice); } bool GetSubImagePixelValues(const AZ::Data::Asset& imageAsset, AZStd::pair topLeft, AZStd::pair bottomRight, AZStd::span outValues, uint32_t componentIndex, uint32_t mip, uint32_t slice) @@ -478,16 +512,14 @@ namespace AZ return false; } - const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor(); - auto width = imageDescriptor.m_size.m_width; - const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); + const AZ::RHI::ImageDescriptor& imageDescriptor = imageAsset->GetImageDescriptor(); size_t outValuesIndex = 0; for (uint32_t y = topLeft.second; y < bottomRight.second; ++y) { for (uint32_t x = topLeft.first; x < bottomRight.first; ++x) { - size_t imageDataIndex = (y * width + x) * numComponents + componentIndex; + size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex); auto& outValue = outValues[outValuesIndex++]; outValue = Internal::RetrieveFloatValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); @@ -510,16 +542,14 @@ namespace AZ return false; } - const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor(); - auto width = imageDescriptor.m_size.m_width; - const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); + const AZ::RHI::ImageDescriptor& imageDescriptor = imageAsset->GetImageDescriptor(); size_t outValuesIndex = 0; for (uint32_t y = topLeft.second; y < bottomRight.second; ++y) { for (uint32_t x = topLeft.first; x < bottomRight.first; ++x) { - size_t imageDataIndex = (y * width + x) * numComponents + componentIndex; + size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex); auto& outValue = outValues[outValuesIndex++]; outValue = Internal::RetrieveUintValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); @@ -542,16 +572,14 @@ namespace AZ return false; } - const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor(); - auto width = imageDescriptor.m_size.m_width; - const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); + const AZ::RHI::ImageDescriptor& imageDescriptor = imageAsset->GetImageDescriptor(); size_t outValuesIndex = 0; for (uint32_t y = topLeft.second; y < bottomRight.second; ++y) { for (uint32_t x = topLeft.first; x < bottomRight.first; ++x) { - size_t imageDataIndex = (y * width + x) * numComponents + componentIndex; + size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex); auto& outValue = outValues[outValuesIndex++]; outValue = Internal::RetrieveIntValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h index 0cbd8bd4c7..7ec1c785e5 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h @@ -98,6 +98,8 @@ namespace GradientSignal void SetupDependencies(); + void GetSubImageData(); + // ImageGradientRequestBus overrides... AZStd::string GetImageAssetPath() const override; void SetImageAssetPath(const AZStd::string& assetPath) override; @@ -113,5 +115,6 @@ namespace GradientSignal LmbrCentral::DependencyMonitor m_dependencyMonitor; mutable AZStd::shared_mutex m_imageMutex; GradientTransform m_gradientTransform; + AZStd::span m_imageData; }; } diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h b/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h index 811d74082d..500251d924 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h @@ -62,6 +62,6 @@ namespace GradientSignal } }; - float GetValueFromImageAsset(const AZ::Data::Asset& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue); + float GetValueFromImageAsset(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue); } // namespace GradientSignal diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp index 468b96e5ad..0bf914fea3 100644 --- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp @@ -215,6 +215,16 @@ namespace GradientSignal m_dependencyMonitor.ConnectDependency(m_configuration.m_imageAsset.GetId()); } + void ImageGradientComponent::GetSubImageData() + { + if (!m_configuration.m_imageAsset || !m_configuration.m_imageAsset.IsReady()) + { + return; + } + + m_imageData = m_configuration.m_imageAsset->GetSubImageData(0, 0); + } + void ImageGradientComponent::Activate() { // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform. @@ -224,10 +234,13 @@ namespace GradientSignal ImageGradientRequestBus::Handler::BusConnect(GetEntityId()); GradientRequestBus::Handler::BusConnect(GetEntityId()); - AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_imageAsset.GetId()); - AZStd::unique_lock imageLock(m_imageMutex); + // Invoke the QueueLoad before connecting to the AssetBus, so that + // if the asset is already ready, then OnAssetReady will be triggered immediately + m_imageData = AZStd::span(); m_configuration.m_imageAsset.QueueLoad(); + + AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_imageAsset.GetId()); } void ImageGradientComponent::Deactivate() @@ -267,18 +280,24 @@ namespace GradientSignal { AZStd::unique_lock imageLock(m_imageMutex); m_configuration.m_imageAsset = asset; + + GetSubImageData(); } void ImageGradientComponent::OnAssetMoved(AZ::Data::Asset asset, [[maybe_unused]] void* oldDataPointer) { AZStd::unique_lock imageLock(m_imageMutex); m_configuration.m_imageAsset = asset; + + GetSubImageData(); } void ImageGradientComponent::OnAssetReloaded(AZ::Data::Asset asset) { AZStd::unique_lock imageLock(m_imageMutex); m_configuration.m_imageAsset = asset; + + GetSubImageData(); } void ImageGradientComponent::OnGradientTransformChanged(const GradientTransform& newTransform) @@ -292,6 +311,12 @@ namespace GradientSignal AZ::Vector3 uvw = sampleParams.m_position; bool wasPointRejected = false; + // Return immediately if our cached image data hasn't been retrieved yet + if (m_imageData.empty()) + { + return 0.0f; + } + { AZStd::shared_lock imageLock(m_imageMutex); @@ -300,7 +325,7 @@ namespace GradientSignal if (!wasPointRejected) { return GetValueFromImageAsset( - m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); + m_imageData, m_configuration.m_imageAsset->GetImageDescriptor(), uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); } } @@ -315,6 +340,12 @@ namespace GradientSignal return; } + // Return immediately if our cached image data hasn't been retrieved yet + if (m_imageData.empty()) + { + return; + } + AZ::Vector3 uvw; bool wasPointRejected = false; @@ -327,7 +358,7 @@ namespace GradientSignal if (!wasPointRejected) { outValues[index] = GetValueFromImageAsset( - m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); + m_imageData, m_configuration.m_imageAsset->GetImageDescriptor(), uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); } else { @@ -353,6 +384,10 @@ namespace GradientSignal { AZStd::unique_lock imageLock(m_imageMutex); + + // Clear our cached image data + m_imageData = AZStd::span(); + m_configuration.m_imageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetId, azrtti_typeid(), m_configuration.m_imageAsset.GetAutoLoadBehavior()); } diff --git a/Gems/GradientSignal/Code/Source/ImageAsset.cpp b/Gems/GradientSignal/Code/Source/ImageAsset.cpp index 0f91a3ea08..05ad13d193 100644 --- a/Gems/GradientSignal/Code/Source/ImageAsset.cpp +++ b/Gems/GradientSignal/Code/Source/ImageAsset.cpp @@ -78,11 +78,10 @@ namespace GradientSignal return true; } - float GetValueFromImageAsset(const AZ::Data::Asset& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue) + float GetValueFromImageAsset(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue) { - if (imageAsset.IsReady()) + if (!imageData.empty()) { - auto imageDescriptor = imageAsset->GetImageDescriptor(); auto width = imageDescriptor.m_size.m_width; auto height = imageDescriptor.m_size.m_height; @@ -125,7 +124,7 @@ namespace GradientSignal // Flip the y because images are stored in reverse of our world axes y = (height - 1) - y; - return AZ::RPI::GetSubImagePixelValue(imageAsset, x, y); + return AZ::RPI::GetImageDataPixelValue(imageData, imageDescriptor, x, y); } }