diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h index 3f79392403..4232658980 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -140,6 +141,9 @@ namespace AZ //! Get the memory requirements for allocating a buffer resource. virtual ResourceMemoryRequirements GetResourceMemoryRequirements(const BufferDescriptor& descriptor) = 0; + //! Notifies after all objects currently in the platform release queue are released + virtual void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) = 0; + protected: DeviceFeatures m_features; DeviceLimits m_limits; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h index cb66225286..cd8a85a385 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h @@ -34,6 +34,8 @@ namespace AZ using MutexType = NullMutex; }; + using ObjectCollectorNotifyFunction = AZStd::function; + /** * Deferred-releases reference-counted objects at a specific latency. Example: Use to batch-release * objects that exist on the GPU timeline at the end of the frame after syncing the oldest GPU frame. @@ -85,6 +87,9 @@ namespace AZ /// Must not be called at collection time. size_t GetObjectCount() const; + /// Notifies after the current set of pending objects is released. + void Notify(ObjectCollectorNotifyFunction notifyFunction); + private: void QueueForCollectInternal(ObjectPtrType object); @@ -92,6 +97,7 @@ namespace AZ { AZStd::vector m_objects; uint64_t m_collectIteration; + AZStd::vector m_notifies; }; inline bool IsGarbageReady(size_t collectIteration) @@ -106,6 +112,7 @@ namespace AZ mutable typename Traits::MutexType m_mutex; AZStd::vector m_pendingObjects; AZStd::vector m_pendingGarbage; + AZStd::vector m_pendingNotifies; }; template @@ -172,6 +179,39 @@ namespace AZ { m_pendingGarbage.push_back({ AZStd::move(m_pendingObjects), m_currentIteration }); } + + if (!m_pendingNotifies.empty()) + { + if (!m_pendingGarbage.empty()) + { + // find the newest garbage entry and add any pending notifies + Garbage& latestGarbage = m_pendingGarbage.front(); + size_t latestGarbageAge = m_currentIteration - latestGarbage.m_collectIteration; + + // check the rest of the entries to see if they are newer + for (size_t i = 1; i < m_pendingGarbage.size(); ++i) + { + size_t age = m_currentIteration - m_pendingGarbage[i].m_collectIteration; + if (age < latestGarbageAge) + { + latestGarbage = m_pendingGarbage[i]; + latestGarbageAge = age; + } + } + + latestGarbage.m_notifies.insert(latestGarbage.m_notifies.end(), m_pendingNotifies.begin(), m_pendingNotifies.end()); + } + else + { + // garbage queue is empty, notify now + for (auto& notifyFunction : m_pendingNotifies) + { + notifyFunction(); + } + } + + m_pendingNotifies.clear(); + } m_mutex.unlock(); size_t objectCount = 0; @@ -189,6 +229,12 @@ namespace AZ } } objectCount += garbage.m_objects.size(); + + for (auto& notifyFunction : garbage.m_notifies) + { + notifyFunction(); + } + garbage = AZStd::move(m_pendingGarbage.back()); m_pendingGarbage.pop_back(); } @@ -215,5 +261,13 @@ namespace AZ return objectCount; } + + template + void ObjectCollector::Notify(ObjectCollectorNotifyFunction notifyFunction) + { + m_mutex.lock(); + m_pendingNotifies.push_back(notifyFunction); + m_mutex.unlock(); + } } } diff --git a/Gems/Atom/RHI/Code/Tests/Device.h b/Gems/Atom/RHI/Code/Tests/Device.h index 3a7c513c8b..e11bd75983 100644 --- a/Gems/Atom/RHI/Code/Tests/Device.h +++ b/Gems/Atom/RHI/Code/Tests/Device.h @@ -60,6 +60,8 @@ namespace UnitTest AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::ImageDescriptor& descriptor) { return AZ::RHI::ResourceMemoryRequirements{}; }; AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::BufferDescriptor& descriptor) { return AZ::RHI::ResourceMemoryRequirements{}; }; + + void ObjectCollectionNotify(AZ::RHI::ObjectCollectorNotifyFunction notifyFunction) override {} }; AZ::RHI::Ptr MakeTestDevice(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp index 88c9098952..9d23dfa43d 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp @@ -292,6 +292,11 @@ namespace AZ return memoryRequirements; } + void Device::ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) + { + m_releaseQueue.Notify(notifyFunction); + } + //AZStd::vector Device::GetValidSwapChainImageFormats(const RHI::WindowHandle& windowHandle) const //{ // AZStd::vector formatsList; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h index 149508307a..9119545342 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h @@ -162,6 +162,7 @@ namespace AZ void PreShutdown() override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override; + void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; ////////////////////////////////////////////////////////////////////////// RHI::ResultCode InitSubPlatform(RHI::PhysicalDevice& physicalDevice); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp index f0771d7c45..dbd2204e93 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp @@ -315,7 +315,12 @@ namespace AZ memoryRequirements.m_sizeInBytes = bufferSizeAndAlign.size; return memoryRequirements; } - + + void Device::ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) + { + m_releaseQueue.Notify(notifyFunction); + } + void Device::InitFeatures() { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h index d8c3092c59..9cdeee7eae 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.h @@ -151,7 +151,8 @@ namespace AZ NullDescriptorManager& GetNullDescriptorManager(); RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override; - + void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; + private: Device() = default; diff --git a/Gems/Atom/RHI/Null/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Null/Code/Source/RHI/Device.cpp index 7b94efb69f..08a2c4f411 100644 --- a/Gems/Atom/RHI/Null/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Null/Code/Source/RHI/Device.cpp @@ -20,5 +20,10 @@ namespace AZ { formatsCapabilities.fill(static_cast(~0)); } + + void Device::ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) + { + notifyFunction(); + } } } diff --git a/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h index 3aa045212b..cd44135e62 100644 --- a/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Null/Code/Source/RHI/Device.h @@ -42,6 +42,7 @@ namespace AZ void PreShutdown() override {} RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const RHI::ImageDescriptor& descriptor) override { return RHI::ResourceMemoryRequirements();} RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const RHI::BufferDescriptor& descriptor) override { return RHI::ResourceMemoryRequirements();} + void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; ////////////////////////////////////////////////////////////////////////// }; } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp index 6d3080dce8..05ff8bb2a6 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp @@ -652,6 +652,11 @@ namespace AZ return RHI::ResourceMemoryRequirements{ vkRequirements.alignment, vkRequirements.size }; } + void Device::ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) + { + m_releaseQueue.Notify(notifyFunction); + } + void Device::InitFeaturesAndLimits(const PhysicalDevice& physicalDevice) { m_features.m_tessellationShader = (m_enabledDeviceFeatures.tessellationShader == VK_TRUE); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h index d5f055f4cd..28e56d1fa9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h @@ -134,6 +134,7 @@ namespace AZ void PreShutdown() override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor& descriptor) override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor& descriptor) override; + void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; ////////////////////////////////////////////////////////////////////////// void InitFeaturesAndLimits(const PhysicalDevice& physicalDevice); diff --git a/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h b/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h index 1de06802b1..be362c60d6 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h +++ b/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h @@ -70,6 +70,7 @@ namespace UnitTest void PreShutdown() override {} AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::ImageDescriptor& descriptor) { return AZ::RHI::ResourceMemoryRequirements{}; }; AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::BufferDescriptor& descriptor) { return AZ::RHI::ResourceMemoryRequirements{}; }; + void ObjectCollectionNotify(AZ::RHI::ObjectCollectorNotifyFunction notifyFunction) override {} }; class ImageView diff --git a/scripts/build/Platform/Windows/deploy_cdk_applications.cmd b/scripts/build/Platform/Windows/deploy_cdk_applications.cmd new file mode 100644 index 0000000000..2845707923 --- /dev/null +++ b/scripts/build/Platform/Windows/deploy_cdk_applications.cmd @@ -0,0 +1,105 @@ +@ECHO OFF +REM +REM Copyright (c) Contributors to the Open 3D Engine Project. +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. +REM +REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM +REM + +REM Deploy the CDK applcations for AWS gems (Windows only) +REM Prerequisites: +REM 1) Node.js is installed +REM 2) Node.js version >= 10.13.0, except for versions 13.0.0 - 13.6.0. A version in active long-term support is recommended. +SETLOCAL EnableDelayedExpansion + +SET SOURCE_DIRECTORY=%CD% +SET PATH=%SOURCE_DIRECTORY%\python;%PATH% +SET GEM_DIRECTORY=%SOURCE_DIRECTORY%\Gems + +REM Create and activate a virtualenv for the CDK deployment +CALL python -m venv .env +IF ERRORLEVEL 1 ( + ECHO [cdk_bootstrap] Failed to create a virtualenv for the CDK deployment + exit /b 1 +) +CALL .env\Scripts\activate.bat +IF ERRORLEVEL 1 ( + ECHO [cdk_bootstrap] Failed to activate the virtualenv for the CDK deployment + exit /b 1 +) + +ECHO [cdk_installation] Install the latest version of CDK +CALL npm uninstall -g aws-cdk +IF ERRORLEVEL 1 ( + ECHO [cdk_bootstrap] Failed to uninstall the current version of CDK + exit /b 1 +) +CALL npm install -g aws-cdk@latest +IF ERRORLEVEL 1 ( + ECHO [cdk_bootstrap] Failed to install the latest version of CDK + exit /b 1 +) + +REM Set temporary AWS credentials from the assume role +FOR /f "tokens=1,2,3" %%a IN ('CALL aws sts assume-role --query Credentials.[SecretAccessKey^,SessionToken^,AccessKeyId] --output text --role-arn %ASSUME_ROLE_ARN% --role-session-name o3de-Automation-session') DO ( + SET AWS_SECRET_ACCESS_KEY=%%a + SET AWS_SESSION_TOKEN=%%b + SET AWS_ACCESS_KEY_ID=%%c +) +FOR /F "tokens=4 delims=:" %%a IN ("%ASSUME_ROLE_ARN%") DO SET O3DE_AWS_DEPLOY_ACCOUNT=%%a + +REM Bootstrap and deploy the CDK applications +ECHO [cdk_bootstrap] Bootstrap CDK +CALL cdk bootstrap aws://%O3DE_AWS_DEPLOY_ACCOUNT%/%O3DE_AWS_DEPLOY_REGION% +IF ERRORLEVEL 1 ( + ECHO [cdk_bootstrap] Failed to bootstrap CDK + exit /b 1 +) + +CALL :DeployCDKApplication AWSCore --all +IF ERRORLEVEL 1 ( + exit /b 1 +) +CALL :DeployCDKApplication AWSClientAuth +IF ERRORLEVEL 1 ( + exit /b 1 +) +CALL :DeployCDKApplication AWSMetrics "-c batch_processing=true" +IF ERRORLEVEL 1 ( + exit /b 1 +) + +EXIT /b 0 + +:DeployCDKApplication +REM Deploy the CDK application for a specific AWS gem +SET GEM_NAME=%~1 +SET ADDITIONAL_ARGUMENTS=%~2 +ECHO [cdk_deployment] Deploy the CDK application for the %GEM_NAME% gem +PUSHD %GEM_DIRECTORY%\%GEM_NAME%\cdk + +REM Revert the CDK application code to a stable state using the provided commit ID +CALL git checkout %COMMIT_ID% -- . +IF ERRORLEVEL 1 ( + ECHO [git_checkout] Failed to checkout the CDK application for the %GEM_NAME% gem using commit ID %COMMIT_ID% + POPD + exit /b 1 +) + +REM Install required packages for the CDK application +CALL python -m pip install -r requirements.txt +IF ERRORLEVEL 1 ( + ECHO [cdk_deployment] Failed to install required packages for the %GEM_NAME% gem + POPD + exit /b 1 +) + +REM Deploy the CDK application +CALL cdk deploy %ADDITIONAL_ARGUMENTS% --require-approval never +IF ERRORLEVEL 1 ( + ECHO [cdk_deployment] Failed to deploy the CDK application for the %GEM_NAME% gem + POPD + exit /b 1 +) +POPD