From 9976ee2b8ef6fca6f95a80257e3c54fe38cccf48 Mon Sep 17 00:00:00 2001 From: bosnichd Date: Thu, 9 Sep 2021 12:09:17 -0600 Subject: [PATCH 01/14] Miscellaneous fixes and PAL changes required for restricted platforms. (#4021) * Miscellaneous fixes and PAL changes required for restricted platforms. Signed-off-by: bosnichd * Rename O3DE::ProjectManager::ProjectUtils::ReplaceFile -> ReplaceProjectFile to prevent conflict with Windows ReplaceFile #define Signed-off-by: bosnichd --- .../AzFramework/API/ApplicationAPI.h | 1 + .../ProjectManager/ProjectManager.cpp | 2 +- Code/Legacy/CryCommon/WinBase.cpp | 5 +- Code/Legacy/CrySystem/System.cpp | 1 - Code/Legacy/CrySystem/SystemWin32.cpp | 4 - .../ProjectManager/Source/ProjectUtils.cpp | 2 +- .../ProjectManager/Source/ProjectUtils.h | 2 +- .../Source/UpdateProjectCtrl.cpp | 2 +- .../Tools/ProjectManager/tests/UtilsTests.cpp | 2 +- .../Platform/Windows/RHI/Device_Windows.cpp | 8 ++ .../Platform/Windows/RHI/SwapChain_Platform.h | 10 +++ .../Windows/RHI/SwapChain_Windows.cpp | 56 +++++++++++++- .../Platform/Windows/RHI/SwapChain_Windows.h | 60 +++++++++++++++ .../platform_private_windows_files.cmake | 2 + Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h | 4 + .../Source/RHI/RayTracingPipelineState.cpp | 4 +- .../RHI/DX12/Code/Source/RHI/SwapChain.cpp | 73 ------------------- .../Atom/RHI/DX12/Code/Source/RHI/SwapChain.h | 54 +------------- .../atom_rhi_dx12_private_common_files.cmake | 1 - Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt | 3 + .../ModuleStub_Unimplemented.cpp | 9 ++- Gems/PhysX/Code/physx_unsupported_files.cmake | 10 +++ 22 files changed, 172 insertions(+), 143 deletions(-) create mode 100644 Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Platform.h create mode 100644 Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.h delete mode 100644 Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.cpp create mode 100644 Gems/PhysX/Code/physx_unsupported_files.cmake diff --git a/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h b/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h index dbbf443656..1c5db0a82b 100644 --- a/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h +++ b/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h @@ -18,6 +18,7 @@ #include #include #include +#include #include diff --git a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp index 33fafa2110..6bbb07ea74 100644 --- a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp @@ -83,7 +83,7 @@ namespace AzFramework::ProjectManager return ProjectPathCheckResult::ProjectManagerLaunchFailed; } - bool LaunchProjectManager(const AZStd::string& commandLineArgs) + bool LaunchProjectManager([[maybe_unused]]const AZStd::string& commandLineArgs) { bool launchSuccess = false; #if (AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER) diff --git a/Code/Legacy/CryCommon/WinBase.cpp b/Code/Legacy/CryCommon/WinBase.cpp index 71bec78f69..9f7cdfd609 100644 --- a/Code/Legacy/CryCommon/WinBase.cpp +++ b/Code/Legacy/CryCommon/WinBase.cpp @@ -6,9 +6,10 @@ * */ +#include // Description : Linux/Mac port support for Win32API calls -#if !defined(WIN32) +#if AZ_TRAIT_LEGACY_CRYCOMMON_USE_WINDOWS_STUBS #include "platform.h" // Note: This should be first to get consistent debugging definitions @@ -1391,4 +1392,4 @@ __finddata64_t::~__finddata64_t() } #endif //defined(APPLE) || defined(LINUX) -#endif // !defined(WIN32) +#endif // AZ_TRAIT_LEGACY_CRYCOMMON_USE_WINDOWS_STUBS diff --git a/Code/Legacy/CrySystem/System.cpp b/Code/Legacy/CrySystem/System.cpp index 71775dbb6c..4a9d9ece5f 100644 --- a/Code/Legacy/CrySystem/System.cpp +++ b/Code/Legacy/CrySystem/System.cpp @@ -131,7 +131,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) #include "SystemEventDispatcher.h" #include "HMDBus.h" -#include "zlib.h" #include "RemoteConsole/RemoteConsole.h" #include diff --git a/Code/Legacy/CrySystem/SystemWin32.cpp b/Code/Legacy/CrySystem/SystemWin32.cpp index 20f4d7ef65..4f0a154afe 100644 --- a/Code/Legacy/CrySystem/SystemWin32.cpp +++ b/Code/Legacy/CrySystem/SystemWin32.cpp @@ -273,11 +273,7 @@ static const char* GetLastSystemErrorMessage() return szBuffer; } -#else - return 0; - #endif //WIN32 - return 0; } diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index fb3f7e0270..84d731832e 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -441,7 +441,7 @@ namespace O3DE::ProjectManager return true; } - bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent, bool interactive) + bool ReplaceProjectFile(const QString& origFile, const QString& newFile, QWidget* parent, bool interactive) { QFileInfo original(origFile); if (original.exists()) diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index d06b8c2c8b..f1050531d4 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager bool DeleteProjectFiles(const QString& path, bool force = false); bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool skipRegister = false); - bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true); + bool ReplaceProjectFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true); bool FindSupportedCompiler(QWidget* parent = nullptr); AZ::Outcome FindSupportedCompilerForPlatform(); diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index e1b6d740e2..08ad7f24d9 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -242,7 +242,7 @@ namespace O3DE::ProjectManager if (!newProjectSettings.m_newPreviewImagePath.isEmpty()) { - if (!ProjectUtils::ReplaceFile( + if (!ProjectUtils::ReplaceProjectFile( QDir(newProjectSettings.m_path).filePath(newProjectSettings.m_iconPath), newProjectSettings.m_newPreviewImagePath)) { QMessageBox::critical(this, tr("File replace failed"), tr("Failed to replace project preview image.")); diff --git a/Code/Tools/ProjectManager/tests/UtilsTests.cpp b/Code/Tools/ProjectManager/tests/UtilsTests.cpp index bfe26ba760..760b599ad8 100644 --- a/Code/Tools/ProjectManager/tests/UtilsTests.cpp +++ b/Code/Tools/ProjectManager/tests/UtilsTests.cpp @@ -202,7 +202,7 @@ namespace O3DE::ProjectManager TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds) #endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS { - EXPECT_TRUE(ReplaceFile(m_projectAOrigFilePath, m_projectAReplaceFilePath, nullptr, false)); + EXPECT_TRUE(ReplaceProjectFile(m_projectAOrigFilePath, m_projectAReplaceFilePath, nullptr, false)); QFile origFile(m_projectAOrigFilePath); EXPECT_TRUE(origFile.open(QIODevice::ReadOnly)); diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp index 7cf83e28bc..51272f3488 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp @@ -258,6 +258,14 @@ namespace AZ return RHI::ResultCode::Success; } + RHI::ResultCode Device::CreateSwapChain( + [[maybe_unused]] const DXGI_SWAP_CHAIN_DESCX& swapChainDesc, + [[maybe_unused]] AZStd::array, RHI::Limits::Device::FrameCountMax>& outSwapChainResources) + { + AZ_Assert(false, "Wrong Device::CreateSwapChain function called on Windows."); + return RHI::ResultCode::Fail; + } + AZStd::vector Device::GetValidSwapChainImageFormats(const RHI::WindowHandle& windowHandle) const { AZStd::vector formatsList; diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Platform.h b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Platform.h new file mode 100644 index 0000000000..f611027497 --- /dev/null +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.cpp b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.cpp index 9a6d7e44d1..ab14f9b5d3 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.cpp @@ -5,15 +5,28 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + +#include + #include #include +#include #include namespace AZ { namespace DX12 { + RHI::Ptr SwapChain::Create() + { + return aznew SwapChain(); + } + + Device& SwapChain::GetDevice() const + { + return static_cast(RHI::SwapChain::GetDevice()); + } + RHI::ResultCode SwapChain::InitInternal(RHI::Device& deviceBase, const RHI::SwapChainDescriptor& descriptor, RHI::SwapChainDimensions* nativeDimensions) { // Check whether tearing support is available for full screen borderless windowed mode. @@ -165,6 +178,47 @@ namespace AZ return GetCurrentImageIndex(); } + RHI::ResultCode SwapChain::InitImageInternal(const InitImageRequest& request) + { + Device& device = GetDevice(); + + Microsoft::WRL::ComPtr resource; + DX12::AssertSuccess(m_swapChain->GetBuffer(request.m_imageIndex, IID_GRAPHICS_PPV_ARGS(resource.GetAddressOf()))); + + D3D12_RESOURCE_ALLOCATION_INFO allocationInfo; + device.GetImageAllocationInfo(request.m_descriptor, allocationInfo); + + Name name(AZStd::string::format("SwapChainImage_%d", request.m_imageIndex)); + + Image& image = static_cast(*request.m_image); + image.m_memoryView = MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image); + image.SetName(name); + image.GenerateSubresourceLayouts(); + // Overwrite m_initialAttachmentState because Swapchain images are created with D3D12_RESOURCE_STATE_COMMON state + image.SetAttachmentState(D3D12_RESOURCE_STATE_COMMON); + + RHI::HeapMemoryUsage& memoryUsage = m_memoryUsage.GetHeapMemoryUsage(RHI::HeapMemoryLevel::Device); + memoryUsage.m_reservedInBytes += allocationInfo.SizeInBytes; + memoryUsage.m_residentInBytes += allocationInfo.SizeInBytes; + + return RHI::ResultCode::Success; + } + + void SwapChain::ShutdownResourceInternal(RHI::Resource& resourceBase) + { + Image& image = static_cast(resourceBase); + + const size_t sizeInBytes = image.GetMemoryView().GetSize(); + + RHI::HeapMemoryUsage& memoryUsage = m_memoryUsage.GetHeapMemoryUsage(RHI::HeapMemoryLevel::Device); + memoryUsage.m_reservedInBytes -= sizeInBytes; + memoryUsage.m_residentInBytes -= sizeInBytes; + + GetDevice().QueueForRelease(image.m_memoryView); + + image.m_memoryView = {}; + } + RHI::ResultCode SwapChain::ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) { GetDevice().WaitForIdle(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.h b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.h new file mode 100644 index 0000000000..2e45371c48 --- /dev/null +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/SwapChain_Windows.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include + +namespace AZ +{ + namespace DX12 + { + class Device; + + class SwapChain + : public RHI::SwapChain + { + public: + AZ_RTTI(SwapChain, "{974AC6A9-5009-47BE-BD7E-61348BF623F0}", RHI::SwapChain); + AZ_CLASS_ALLOCATOR(SwapChain, AZ::SystemAllocator, 0); + + static RHI::Ptr Create(); + + Device& GetDevice() const; + + private: + SwapChain() = default; + friend class SwapChainFactory; + + ////////////////////////////////////////////////////////////////////////// + // RHI::SwapChain + RHI::ResultCode InitInternal(RHI::Device& deviceBase, const RHI::SwapChainDescriptor& descriptor, RHI::SwapChainDimensions* nativeDimensions) override; + void ShutdownInternal() override; + uint32_t PresentInternal() override; + RHI::ResultCode InitImageInternal(const InitImageRequest& request) override; + void ShutdownResourceInternal(RHI::Resource& resourceBase) override; + RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override; + bool IsExclusiveFullScreenPreferred() const override; + bool GetExclusiveFullScreenState() const override; + bool SetExclusiveFullScreenState(bool fullScreenState) override; + ////////////////////////////////////////////////////////////////////////// + + void ConfigureDisplayMode(const RHI::SwapChainDimensions& dimensions); + void EnsureColorSpace(const DXGI_COLOR_SPACE_TYPE& colorSpace); + void DisableHdr(); + void SetHDRMetaData(float maxOutputNits, float minOutputNits, float maxContentLightLevel, float maxFrameAverageLightLevel); + + static const uint32_t InvalidColorSpace = 0xFFFFFFFE; + DXGI_COLOR_SPACE_TYPE m_colorSpace = static_cast(InvalidColorSpace); + + RHI::Ptr m_swapChain; + bool m_isInFullScreenExclusiveState = false; //!< Was SetFullscreenState used to enter full screen exclusive state? + bool m_isTearingSupported = false; //!< Is tearing support available for full screen borderless windowed mode? + }; + } +} diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/platform_private_windows_files.cmake b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/platform_private_windows_files.cmake index e3a60c937c..5333b89336 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/platform_private_windows_files.cmake +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/platform_private_windows_files.cmake @@ -22,7 +22,9 @@ set(FILES RHI/DX12_Windows.cpp RHI/DX12_Windows.h RHI/SystemComponent_Windows.cpp + RHI/SwapChain_Platform.h RHI/SwapChain_Windows.cpp + RHI/SwapChain_Windows.h RHI/NsightAftermathGpuCrashTracker_Windows.cpp RHI/NsightAftermathGpuCrashTracker_Windows.h RHI/NsightAftermath_Windows.cpp diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h index 4d1f2c4ac9..7004900ec5 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h @@ -57,6 +57,10 @@ namespace AZ const DXGI_SWAP_CHAIN_DESCX& swapChainDesc, RHI::Ptr& swapChain); + RHI::ResultCode CreateSwapChain( + const DXGI_SWAP_CHAIN_DESCX& swapChainDesc, + AZStd::array, RHI::Limits::Device::FrameCountMax>& outSwapChainResources); + void GetImageAllocationInfo( const RHI::ImageDescriptor& descriptor, D3D12_RESOURCE_ALLOCATION_INFO& info); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingPipelineState.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingPipelineState.cpp index be0c084d95..37f4e6b49a 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingPipelineState.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingPipelineState.cpp @@ -28,11 +28,11 @@ namespace AZ } #endif - RHI::ResultCode RayTracingPipelineState::InitInternal(RHI::Device& deviceBase, [[maybe_unused]]const RHI::RayTracingPipelineStateDescriptor* descriptor) + RHI::ResultCode RayTracingPipelineState::InitInternal([[maybe_unused]]RHI::Device& deviceBase, [[maybe_unused]]const RHI::RayTracingPipelineStateDescriptor* descriptor) { +#ifdef AZ_DX12_DXR_SUPPORT Device& device = static_cast(deviceBase); -#ifdef AZ_DX12_DXR_SUPPORT size_t dxilLibraryCount = descriptor->GetShaderLibraries().size(); size_t hitGroupCount = descriptor->GetHitGroups().size(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.cpp deleted file mode 100644 index bb5acc878a..0000000000 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace AZ -{ - namespace DX12 - { - RHI::Ptr SwapChain::Create() - { - return aznew SwapChain(); - } - - Device& SwapChain::GetDevice() const - { - return static_cast(Base::GetDevice()); - } - - RHI::ResultCode SwapChain::InitImageInternal(const InitImageRequest& request) - { - - Device& device = GetDevice(); - - Microsoft::WRL::ComPtr resource; - DX12::AssertSuccess(m_swapChain->GetBuffer(request.m_imageIndex, IID_GRAPHICS_PPV_ARGS(resource.GetAddressOf()))); - - D3D12_RESOURCE_ALLOCATION_INFO allocationInfo; - device.GetImageAllocationInfo(request.m_descriptor, allocationInfo); - - Name name(AZStd::string::format("SwapChainImage_%d", request.m_imageIndex)); - - Image& image = static_cast(*request.m_image); - image.m_memoryView = MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image); - image.SetName(name); - image.GenerateSubresourceLayouts(); - // Overwrite m_initialAttachmentState because Swapchain images are created with D3D12_RESOURCE_STATE_COMMON state - image.SetAttachmentState(D3D12_RESOURCE_STATE_COMMON); - - RHI::HeapMemoryUsage& memoryUsage = m_memoryUsage.GetHeapMemoryUsage(RHI::HeapMemoryLevel::Device); - memoryUsage.m_reservedInBytes += allocationInfo.SizeInBytes; - memoryUsage.m_residentInBytes += allocationInfo.SizeInBytes; - - return RHI::ResultCode::Success; - } - - void SwapChain::ShutdownResourceInternal(RHI::Resource& resourceBase) - { - Image& image = static_cast(resourceBase); - - const size_t sizeInBytes = image.GetMemoryView().GetSize(); - - RHI::HeapMemoryUsage& memoryUsage = m_memoryUsage.GetHeapMemoryUsage(RHI::HeapMemoryLevel::Device); - memoryUsage.m_reservedInBytes -= sizeInBytes; - memoryUsage.m_residentInBytes -= sizeInBytes; - - GetDevice().QueueForRelease(image.m_memoryView); - - image.m_memoryView = {}; - } - } -} diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.h index 8035ae1e38..bd12b1f316 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/SwapChain.h @@ -7,56 +7,4 @@ */ #pragma once -#include -#include - -namespace AZ -{ - namespace DX12 - { - class Device; - class Image; - class CommandQueue; - - class SwapChain - : public RHI::SwapChain - { - using Base = RHI::SwapChain; - public: - AZ_RTTI(SwapChain, "{974AC6A9-5009-47BE-BD7E-61348BF623F0}", Base); - AZ_CLASS_ALLOCATOR(SwapChain, AZ::SystemAllocator, 0); - - static RHI::Ptr Create(); - - Device& GetDevice() const; - - private: - SwapChain() = default; - - ////////////////////////////////////////////////////////////////////////// - // RHI::SwapChain - RHI::ResultCode InitInternal(RHI::Device& deviceBase, const RHI::SwapChainDescriptor& descriptor, RHI::SwapChainDimensions* nativeDimensions) override; - void ShutdownInternal() override; - uint32_t PresentInternal() override; - RHI::ResultCode InitImageInternal(const InitImageRequest& request) override; - void ShutdownResourceInternal(RHI::Resource& resourceBase) override; - RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override; - bool IsExclusiveFullScreenPreferred() const override; - bool GetExclusiveFullScreenState() const override; - bool SetExclusiveFullScreenState(bool fullScreenState) override; - ////////////////////////////////////////////////////////////////////////// - - void ConfigureDisplayMode(const RHI::SwapChainDimensions& dimensions); - void EnsureColorSpace(const DXGI_COLOR_SPACE_TYPE& colorSpace); - void DisableHdr(); - void SetHDRMetaData(float maxOutputNits, float minOutputNits, float maxContentLightLevel, float maxFrameAverageLightLevel); - - static const uint32_t InvalidColorSpace = 0xFFFFFFFE; - DXGI_COLOR_SPACE_TYPE m_colorSpace = static_cast(InvalidColorSpace); - - RHI::Ptr m_swapChain; - bool m_isInFullScreenExclusiveState = false; //!< Was SetFullscreenState used to enter full screen exclusive state? - bool m_isTearingSupported = false; //!< Is tearing support available for full screen borderless windowed mode? - }; - } -} +#include diff --git a/Gems/Atom/RHI/DX12/Code/atom_rhi_dx12_private_common_files.cmake b/Gems/Atom/RHI/DX12/Code/atom_rhi_dx12_private_common_files.cmake index 3ad79398b4..807a637b0f 100644 --- a/Gems/Atom/RHI/DX12/Code/atom_rhi_dx12_private_common_files.cmake +++ b/Gems/Atom/RHI/DX12/Code/atom_rhi_dx12_private_common_files.cmake @@ -95,7 +95,6 @@ set(FILES Source/RHI/ShaderResourceGroup.h Source/RHI/ShaderResourceGroupPool.cpp Source/RHI/ShaderResourceGroupPool.h - Source/RHI/SwapChain.cpp Source/RHI/SwapChain.h Source/RHI/DX12.cpp Source/RHI/DX12.h diff --git a/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt b/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt index f4148b435e..5c74852f57 100644 --- a/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt +++ b/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt @@ -19,9 +19,12 @@ if(NOT PAL_TRAIT_ATOM_RHI_VULKAN_SUPPORTED) NAMESPACE Gem FILES_CMAKE atom_rhi_vulkan_stub_module.cmake + atom_rhi_vulkan_reflect_common_files.cmake INCLUDE_DIRECTORIES PRIVATE + Include Source + ${pal_include_dir} Include/Atom/RHI.Loader/Glad BUILD_DEPENDENCIES PRIVATE diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp index 8c06700404..44a8a26968 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp @@ -5,6 +5,8 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + +#include #include namespace AZ @@ -17,7 +19,12 @@ namespace AZ public: AZ_RTTI(PlatformModule, "{958CB096-796C-42C7-9B29-17C6FE792C30}", Module); - PlatformModule() = default; + PlatformModule() + { + m_descriptors.insert(m_descriptors.end(), { + ReflectSystemComponent::CreateDescriptor() + }); + } ~PlatformModule() override = default; AZ::ComponentTypeList GetRequiredSystemComponents() const override diff --git a/Gems/PhysX/Code/physx_unsupported_files.cmake b/Gems/PhysX/Code/physx_unsupported_files.cmake new file mode 100644 index 0000000000..c2c5a11c4c --- /dev/null +++ b/Gems/PhysX/Code/physx_unsupported_files.cmake @@ -0,0 +1,10 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES +) From 8dbecb654e43fa52303311c7a83ed1b49d19b404 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Thu, 9 Sep 2021 13:28:52 -0700 Subject: [PATCH 02/14] [SPEC-7868] Update AWS Gems cmake target with correct runtime dependencies (#3991) Signed-off-by: onecent1101 --- Gems/AWSClientAuth/Code/CMakeLists.txt | 45 ++++++++++++++----- Gems/AWSCore/Code/CMakeLists.txt | 34 +++++++++++--- .../Code/AWSGameLiftClient/CMakeLists.txt | 19 ++++++-- Gems/AWSMetrics/Code/CMakeLists.txt | 37 ++++++++++++--- 4 files changed, 110 insertions(+), 25 deletions(-) diff --git a/Gems/AWSClientAuth/Code/CMakeLists.txt b/Gems/AWSClientAuth/Code/CMakeLists.txt index 514b1ab79e..bd8b174b65 100644 --- a/Gems/AWSClientAuth/Code/CMakeLists.txt +++ b/Gems/AWSClientAuth/Code/CMakeLists.txt @@ -26,9 +26,6 @@ ly_add_target( Gem::HttpRequestor 3rdParty::AWSNativeSDK::AWSClientAuth 3rdParty::AWSNativeSDK::Core - RUNTIME_DEPENDENCIES - Gem::AWSCore - Gem::HttpRequestor ) ly_add_target( @@ -48,17 +45,45 @@ ly_add_target( 3rdParty::AWSNativeSDK::Core PUBLIC Gem::AWSClientAuth.Static - RUNTIME_DEPENDENCIES - Gem::AWSCore - Gem::HttpRequestor ) # Load the "Gem::AWSClientAuth" module in all types of applications. -ly_create_alias(NAME AWSClientAuth.Servers NAMESPACE Gem TARGETS Gem::AWSClientAuth) -ly_create_alias(NAME AWSClientAuth.Clients NAMESPACE Gem TARGETS Gem::AWSClientAuth) +ly_create_alias( + NAME AWSClientAuth.Servers + NAMESPACE Gem + TARGETS + Gem::AWSClientAuth + Gem::AWSCore.Servers + Gem::HttpRequestor.Servers +) + +ly_create_alias( + NAME AWSClientAuth.Clients + NAMESPACE Gem + TARGETS + Gem::AWSClientAuth + Gem::AWSCore.Clients + Gem::HttpRequestor.Clients +) + if (PAL_TRAIT_BUILD_HOST_TOOLS) - ly_create_alias(NAME AWSClientAuth.Tools NAMESPACE Gem TARGETS Gem::AWSClientAuth) - ly_create_alias(NAME AWSClientAuth.Builders NAMESPACE Gem TARGETS Gem::AWSClientAuth) + ly_create_alias( + NAME AWSClientAuth.Tools + NAMESPACE Gem + TARGETS + Gem::AWSClientAuth + Gem::AWSCore.Tools + Gem::HttpRequestor.Tools + ) + + ly_create_alias( + NAME AWSClientAuth.Builders + NAMESPACE Gem + TARGETS + Gem::AWSClientAuth + Gem::AWSCore.Builders + Gem::HttpRequestor.Builders + ) endif() ################################################################################ diff --git a/Gems/AWSCore/Code/CMakeLists.txt b/Gems/AWSCore/Code/CMakeLists.txt index 5fdefa9f9e..808436b7fd 100644 --- a/Gems/AWSCore/Code/CMakeLists.txt +++ b/Gems/AWSCore/Code/CMakeLists.txt @@ -45,8 +45,19 @@ ly_add_target( ) # clients and servers will use the above Gem::AWSCore module. -ly_create_alias(NAME AWSCore.Servers NAMESPACE Gem TARGETS Gem::AWSCore) -ly_create_alias(NAME AWSCore.Clients NAMESPACE Gem TARGETS Gem::AWSCore) +ly_create_alias( + NAME AWSCore.Servers + NAMESPACE Gem + TARGETS + Gem::AWSCore +) + +ly_create_alias( + NAME AWSCore.Clients + NAMESPACE Gem + TARGETS + Gem::AWSCore +) if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -84,8 +95,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE AZ::AzCore Gem::AWSCore.Editor.Static - RUNTIME_DEPENDENCIES - Gem::AWSCore ) # This target is not a real gem module @@ -106,8 +115,21 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_dependencies(AWSCore.Editor AWSCore.ResourceMappingTool) # Builders and Tools (such as the Editor use AWSCore.Editor) use the .Editor module above. - ly_create_alias(NAME AWSCore.Tools NAMESPACE Gem TARGETS Gem::AWSCore.Editor) - ly_create_alias(NAME AWSCore.Builders NAMESPACE Gem TARGETS Gem::AWSCore.Editor) + ly_create_alias( + NAME AWSCore.Tools + NAMESPACE Gem + TARGETS + Gem::AWSCore + Gem::AWSCore.Editor + ) + + ly_create_alias( + NAME AWSCore.Builders + NAMESPACE Gem + TARGETS + Gem::AWSCore + Gem::AWSCore.Editor + ) endif() diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt b/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt index a2e7a5b2e1..3eb5eafab0 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt @@ -45,13 +45,26 @@ ly_add_target( PUBLIC Gem::AWSGameLift.Client.Static RUNTIME_DEPENDENCIES - Gem::AWSCore + Gem::AWSCore.Clients ) # Load the "Gem::AWSGameLift" module in all types of applications. if (PAL_TRAIT_BUILD_HOST_TOOLS) - ly_create_alias(NAME AWSGameLift.Tools NAMESPACE Gem TARGETS Gem::AWSGameLift.Clients) - ly_create_alias(NAME AWSGameLift.Builders NAMESPACE Gem TARGETS Gem::AWSGameLift.Clients) + ly_create_alias( + NAME AWSGameLift.Tools + NAMESPACE Gem + TARGETS + Gem::AWSCore.Tools + Gem::AWSGameLift.Clients + ) + + ly_create_alias( + NAME AWSGameLift.Builders + NAMESPACE Gem + TARGETS + Gem::AWSCore.Builders + Gem::AWSGameLift.Clients + ) endif() ################################################################################ diff --git a/Gems/AWSMetrics/Code/CMakeLists.txt b/Gems/AWSMetrics/Code/CMakeLists.txt index 2bba967bb7..2e78ee8c42 100644 --- a/Gems/AWSMetrics/Code/CMakeLists.txt +++ b/Gems/AWSMetrics/Code/CMakeLists.txt @@ -40,16 +40,41 @@ ly_add_target( AZ::AzCore AZ::AzFramework Gem::AWSMetrics.Static - RUNTIME_DEPENDENCIES - Gem::AWSCore ) # Load the "Gem::AWSMetrics" module in all types of applications. -ly_create_alias(NAME AWSMetrics.Servers NAMESPACE Gem TARGETS Gem::AWSMetrics) -ly_create_alias(NAME AWSMetrics.Clients NAMESPACE Gem TARGETS Gem::AWSMetrics) +ly_create_alias( + NAME AWSMetrics.Servers + NAMESPACE Gem + TARGETS + Gem::AWSCore.Servers + Gem::AWSMetrics +) + +ly_create_alias( + NAME AWSMetrics.Clients + NAMESPACE Gem + TARGETS + Gem::AWSCore.Clients + Gem::AWSMetrics +) + if (PAL_TRAIT_BUILD_HOST_TOOLS) - ly_create_alias(NAME AWSMetrics.Tools NAMESPACE Gem TARGETS Gem::AWSMetrics) - ly_create_alias(NAME AWSMetrics.Builders NAMESPACE Gem TARGETS Gem::AWSMetrics) + ly_create_alias( + NAME AWSMetrics.Tools + NAMESPACE Gem + TARGETS + Gem::AWSCore.Tools + Gem::AWSMetrics + ) + + ly_create_alias( + NAME AWSMetrics.Builders + NAMESPACE Gem + TARGETS + Gem::AWSCore.Builders + Gem::AWSMetrics + ) endif() ################################################################################ From 10900fee388651ab0d81ee5d06082ee90d02a08b Mon Sep 17 00:00:00 2001 From: moudgils <47460854+moudgils@users.noreply.github.com> Date: Thu, 9 Sep 2021 14:30:35 -0700 Subject: [PATCH 03/14] =?UTF-8?q?Disable=20PSO=20caching=20if=20Renderdoc?= =?UTF-8?q?=20or=20Pix=20dlls=20are=20loaded=20as=20they=20confl=E2=80=A6?= =?UTF-8?q?=20(#3976)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Disable PSO caching if Renderdoc or Pix dlls are loaded as they conflict with dx12 api * Add support for command line param --enablePixGPU which manually loads the pix gpu capture dll - This will allow pix to attach to a running process if the enablePixGPU is enabled - Support for enabling/disabling gpu markers based on enablePixGPU Signed-off-by: moudgils --- Gems/Atom/RHI/Code/CMakeLists.txt | 1 + Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h | 111 +++++++++--------- .../platform_private_android_files.cmake | 11 ++ .../Unimplemented/Empty_Unimplemented.cpp | 21 ++++ .../Linux/platform_private_linux_files.cmake | 11 ++ .../Mac/platform_private_mac_files.cmake | 11 ++ .../Windows/Atom_RHI_Traits_Windows.h | 1 + .../Platform/Windows/RHI/Factory_windows.cpp | 59 ++++++++++ .../platform_private_windows_files.cmake | 11 ++ .../iOS/platform_private_ios_files.cmake | 11 ++ Gems/Atom/RHI/Code/Source/RHI/Factory.cpp | 64 +++++++++- .../RHI/DX12/Code/Source/RHI/CommandList.cpp | 16 ++- .../DX12/Code/Source/RHI/PipelineLibrary.cpp | 24 ++-- Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp | 11 +- 14 files changed, 291 insertions(+), 72 deletions(-) create mode 100644 Gems/Atom/RHI/Code/Source/Platform/Android/platform_private_android_files.cmake create mode 100644 Gems/Atom/RHI/Code/Source/Platform/Common/Unimplemented/Empty_Unimplemented.cpp create mode 100644 Gems/Atom/RHI/Code/Source/Platform/Linux/platform_private_linux_files.cmake create mode 100644 Gems/Atom/RHI/Code/Source/Platform/Mac/platform_private_mac_files.cmake create mode 100644 Gems/Atom/RHI/Code/Source/Platform/Windows/RHI/Factory_windows.cpp create mode 100644 Gems/Atom/RHI/Code/Source/Platform/Windows/platform_private_windows_files.cmake create mode 100644 Gems/Atom/RHI/Code/Source/Platform/iOS/platform_private_ios_files.cmake diff --git a/Gems/Atom/RHI/Code/CMakeLists.txt b/Gems/Atom/RHI/Code/CMakeLists.txt index 879fc7d2ce..843eeb6364 100644 --- a/Gems/Atom/RHI/Code/CMakeLists.txt +++ b/Gems/Atom/RHI/Code/CMakeLists.txt @@ -42,6 +42,7 @@ ly_add_target( FILES_CMAKE atom_rhi_public_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_source_dir}/platform_private_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake INCLUDE_DIRECTORIES PRIVATE Source diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h index e0f03df9d7..992cc0e79a 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h @@ -50,24 +50,21 @@ namespace AZ class RayTracingPipelineState; class RayTracingShaderTable; - /* Priority of a Factory. The lower the number the higher the priority. - * Used when there's multiple factories available and the user hasn't define - * a priority. - */ + //! Priority of a Factory. The lower the number the higher the priority. + //! Used when there's multiple factories available and the user hasn't define + //! a priority. using APIPriority = uint32_t; static const APIPriority APITopPriority = 1; static const APIPriority APILowPriority = 10; static const APIPriority APIMiddlePriority = (APILowPriority - APITopPriority) / 2; - /** - * The factory is an interface for creating RHI data structures. The platform system should - * register itself with the factory by calling Register, and unregister on shutdown with - * Unregister. - * - * A call to Get will return the active instance. In the event that it's unclear whether - * a platform instance exists, you must call IsReady to determine whether it's safe to - * call Get. Calling Get without a registered platform will result in an assert. - */ + //! The factory is an interface for creating RHI data structures. The platform system should + //! register itself with the factory by calling Register, and unregister on shutdown with + //! Unregister. + //! + //! A call to Get will return the active instance. In the event that it's unclear whether + //! a platform instance exists, you must call IsReady to determine whether it's safe to + //! call Get. Calling Get without a registered platform will result in an assert. class Factory { public: @@ -79,78 +76,78 @@ namespace AZ // Note that you have to delete these for safety reasons, you will trip a static_assert if you do not AZ_DISABLE_COPY_MOVE(Factory); - /// Returns the component service name CRC used by the platform RHI system component. + //! Returns the component service name CRC used by the platform RHI system component. static uint32_t GetComponentService(); - /// Returns the component service name CRC used by the Factory manager component. + //! Returns the component service name CRC used by the Factory manager component. static uint32_t GetManagerComponentService(); - /// Returns the component service name CRC used by the platform RHI system component. + //! Returns the component service name CRC used by the platform RHI system component. static uint32_t GetPlatformService(); - /// Registers the global factory instance. + //! Registers the global factory instance. static void Register(Factory* instance); - /// Unregisters the global factory instance. + //! Unregisters the global factory instance. static void Unregister(Factory* instance); - /// Returns whether the factory is initialized and active in this module. + //! Returns whether the factory is initialized and active in this module. static bool IsReady(); - /// Access the global factory instance. + //! Access the global factory instance. static Factory& Get(); #if defined(USE_RENDERDOC) - /// Access the RenderDoc API pointer if available. - /// The availability of the render doc API at runtime depends on the following: - /// - You must not be building a packaged game/product (LY_MONOLITHIC_GAME not enabled in CMake) - /// - A valid renderdoc installation was found, either by auto-discovery, or by supplying ATOM_RENDERDOC_PATH as an environment variable - /// - The module loaded successfully at runtime, and the API function pointer was retrieved successfully + //! Access the RenderDoc API pointer if available. + //! The availability of the render doc API at runtime depends on the following: + //! - You must not be building a packaged game/product (LY_MONOLITHIC_GAME not enabled in CMake) + //! - A valid renderdoc installation was found, either by auto-discovery, or by supplying ATOM_RENDERDOC_PATH as an environment variable + //! - The module loaded successfully at runtime, and the API function pointer was retrieved successfully static RENDERDOC_API_1_1_2* GetRenderDocAPI(); #endif + + //! Returns true if RenderDoc dll is loaded + static bool IsRenderDocModuleLoaded(); - /// Returns the name of the Factory. + //! Returns true if Pix dll is loaded + static bool IsPixModuleLoaded(); + + //! Returns the name of the Factory. virtual Name GetName() = 0; - /// Returns the APIType of the factory. + //! Returns the APIType of the factory. virtual APIType GetType() = 0; - /// Returns the default priority of the factory in case there's no priorities set in the FactoryManager. + //! Returns the default priority of the factory in case there's no priorities set in the FactoryManager. virtual APIPriority GetDefaultPriority() = 0; - /** - * Purpose: The API Unique Index will be encoded in the 2 Most Significant Bits of a ShaderVariantAsset ProductSubId (a 32bits integer). - * Returns a number in the range [0..3]. - * In theory any given AssetBuilderSdk::PlatformInfo can support several RHI::APITypes. - * In reality "pc" only supports DX12 & Vulkan. - * "ios" supports only Metal. - * "mac" supports only Metal. - * "android" supports only Vulkan. - * So, for all practical purposes, a single PlatformInfo won't support more than 2 ShaderPlatformInterfaces, but for the sake of - * hedging our bets into the future We assume no more than 4 ShaderPlatformInterfaces will ever be supported for any given PlatformInfo. - * REMARK: It is the responsibility of the Factory subclass to return a unique number between 0...3. - * For example DX12 can return 0, while Vulkan should return 1 (Satisfies "pc", "android" and "linux"). - * Metal can return 0 because it is the only ShaderPlatformInterface for "ios", "mac" and "appletv". - * See AZ::RHI::Limits::APIType::PerPlatformApiUniqueIndexMax. - */ + //! Purpose: The API Unique Index will be encoded in the 2 Most Significant Bits of a ShaderVariantAsset ProductSubId (a 32bits integer). + //! Returns a number in the range [0..3]. + //! In theory any given AssetBuilderSdk::PlatformInfo can support several RHI::APITypes. + //! In reality "pc" only supports DX12 & Vulkan. + //! "ios" supports only Metal. + //! "mac" supports only Metal. + //! "android" supports only Vulkan. + //! So, for all practical purposes, a single PlatformInfo won't support more than 2 ShaderPlatformInterfaces, but for the sake of + //! hedging our bets into the future We assume no more than 4 ShaderPlatformInterfaces will ever be supported for any given PlatformInfo. + //! REMARK: It is the responsibility of the Factory subclass to return a unique number between 0...3. + //! For example DX12 can return 0, while Vulkan should return 1 (Satisfies "pc", "android" and "linux"). + //! Metal can return 0 because it is the only ShaderPlatformInterface for "ios", "mac" and "appletv". + //! See AZ::RHI::Limits::APIType::PerPlatformApiUniqueIndexMax. virtual uint32_t GetAPIUniqueIndex() const = 0; - /** - * Collects the set of physical devices on the system and returns a list of them. Physical - * devices represent the hardware attached to the system. Physical devices can be grouped - * into nodes for linked setups (e.g. SLI / Crossfire). They can also represent software - * reference implementations. Check the PhysicalDeviceType on the descriptor to inspect - * this information. - */ + //! Collects the set of physical devices on the system and returns a list of them. Physical + //! devices represent the hardware attached to the system. Physical devices can be grouped + //! into nodes for linked setups (e.g. SLI / Crossfire). They can also represent software + //! reference implementations. Check the PhysicalDeviceType on the descriptor to inspect + //! this information. virtual PhysicalDeviceList EnumeratePhysicalDevices() = 0; - /** - * Factory Creation Methods: - * - * Returns the platform-specific derived variant of the RHI type. All instances are created - * in an uninitialized state; the operation simply allocates the memory for the appropriate - * platform type and returns the pointer. - */ + //! Factory Creation Methods: + //! + //! Returns the platform-specific derived variant of the RHI type. All instances are created + //! in an uninitialized state; the operation simply allocates the memory for the appropriate + //! platform type and returns the pointer. virtual Ptr CreateBuffer() = 0; diff --git a/Gems/Atom/RHI/Code/Source/Platform/Android/platform_private_android_files.cmake b/Gems/Atom/RHI/Code/Source/Platform/Android/platform_private_android_files.cmake new file mode 100644 index 0000000000..6c7f8f46f1 --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/Android/platform_private_android_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + ../Common/Unimplemented/Empty_Unimplemented.cpp +) diff --git a/Gems/Atom/RHI/Code/Source/Platform/Common/Unimplemented/Empty_Unimplemented.cpp b/Gems/Atom/RHI/Code/Source/Platform/Common/Unimplemented/Empty_Unimplemented.cpp new file mode 100644 index 0000000000..4152be0696 --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/Common/Unimplemented/Empty_Unimplemented.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include + +namespace AZ::RHI::Platform +{ + bool IsPixDllInjected([[maybe_unused]] const char* dllName) + { + return false; + } + + AZStd::wstring GetLatestWinPixGpuCapturerPath() + { + return L""; + } +} diff --git a/Gems/Atom/RHI/Code/Source/Platform/Linux/platform_private_linux_files.cmake b/Gems/Atom/RHI/Code/Source/Platform/Linux/platform_private_linux_files.cmake new file mode 100644 index 0000000000..6c7f8f46f1 --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/Linux/platform_private_linux_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + ../Common/Unimplemented/Empty_Unimplemented.cpp +) diff --git a/Gems/Atom/RHI/Code/Source/Platform/Mac/platform_private_mac_files.cmake b/Gems/Atom/RHI/Code/Source/Platform/Mac/platform_private_mac_files.cmake new file mode 100644 index 0000000000..6c7f8f46f1 --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/Mac/platform_private_mac_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + ../Common/Unimplemented/Empty_Unimplemented.cpp +) diff --git a/Gems/Atom/RHI/Code/Source/Platform/Windows/Atom_RHI_Traits_Windows.h b/Gems/Atom/RHI/Code/Source/Platform/Windows/Atom_RHI_Traits_Windows.h index 82358784a3..9ca1afb5c7 100644 --- a/Gems/Atom/RHI/Code/Source/Platform/Windows/Atom_RHI_Traits_Windows.h +++ b/Gems/Atom/RHI/Code/Source/Platform/Windows/Atom_RHI_Traits_Windows.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_RENDERDOC_MODULE "renderdoc.dll" +#define AZ_TRAIT_PIX_MODULE "WinPixGpuCapturer.dll" diff --git a/Gems/Atom/RHI/Code/Source/Platform/Windows/RHI/Factory_windows.cpp b/Gems/Atom/RHI/Code/Source/Platform/Windows/RHI/Factory_windows.cpp new file mode 100644 index 0000000000..808575c7e8 --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/Windows/RHI/Factory_windows.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include + +namespace AZ::RHI::Platform +{ + bool IsPixDllInjected(const char* dllName) + { + bool isDllLoaded = false; + + wchar_t fileNameW[256]; + size_t numCharsConverted; + errno_t wcharResult = mbstowcs_s(&numCharsConverted, fileNameW, dllName, AZ_ARRAY_SIZE(fileNameW) - 1); + if (wcharResult == 0) + { + isDllLoaded = NULL != GetModuleHandleW(fileNameW); + } + return isDllLoaded; + } + + AZStd::wstring GetLatestWinPixGpuCapturerPath() + { + LPWSTR programFilesPath = nullptr; + SHGetKnownFolderPath(FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &programFilesPath); + + std::filesystem::path pixInstallationPath = programFilesPath; + pixInstallationPath /= "Microsoft PIX"; + + std::wstring newestVersionFound; + + for (auto const& directory_entry : std::filesystem::directory_iterator(pixInstallationPath)) + { + if (directory_entry.is_directory()) + { + if (newestVersionFound.empty() || newestVersionFound < directory_entry.path().filename().c_str()) + { + newestVersionFound = directory_entry.path().filename().c_str(); + } + } + } + + if (newestVersionFound.empty()) + { + return L""; + } + + std::wstring finalPath = pixInstallationPath / newestVersionFound / L"WinPixGpuCapturer.dll"; + return AZStd::wstring(finalPath.c_str()); + } +} diff --git a/Gems/Atom/RHI/Code/Source/Platform/Windows/platform_private_windows_files.cmake b/Gems/Atom/RHI/Code/Source/Platform/Windows/platform_private_windows_files.cmake new file mode 100644 index 0000000000..ac0a5526ea --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/Windows/platform_private_windows_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + RHI/Factory_windows.cpp +) diff --git a/Gems/Atom/RHI/Code/Source/Platform/iOS/platform_private_ios_files.cmake b/Gems/Atom/RHI/Code/Source/Platform/iOS/platform_private_ios_files.cmake new file mode 100644 index 0000000000..6c7f8f46f1 --- /dev/null +++ b/Gems/Atom/RHI/Code/Source/Platform/iOS/platform_private_ios_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + ../Common/Unimplemented/Empty_Unimplemented.cpp +) diff --git a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp index 3162a71e34..48b64c17c0 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp @@ -11,20 +11,33 @@ #include #include -#if defined(USE_RENDERDOC) +#if defined(USE_RENDERDOC) || defined(USE_PIX) #include #include #include +#endif +#if defined(USE_RENDERDOC) static AZStd::unique_ptr s_renderDocModule; static RENDERDOC_API_1_1_2* s_renderDocApi = nullptr; +static bool s_isRenderDocDllLoaded = false; #endif +#if defined(USE_PIX) +static AZStd::unique_ptr s_pixModule; +static bool s_isPixGpuCaptureDllLoaded = false; +#endif namespace AZ { namespace RHI { + namespace Platform + { + bool IsPixDllInjected(const char* dllName); + AZStd::wstring GetLatestWinPixGpuCapturerPath(); + } + uint32_t Factory::GetComponentService() { return AZ_CRC("RHIService", 0x45d8e053); @@ -53,6 +66,7 @@ namespace AZ { if (s_renderDocModule->Load(false)) { + s_isRenderDocDllLoaded = true; pRENDERDOC_GetAPI renderDocGetAPI = s_renderDocModule->GetFunction("RENDERDOC_GetAPI"); if (renderDocGetAPI) { @@ -78,8 +92,30 @@ namespace AZ } } } -#endif // defined(USE_RENDERDOC) +#endif + +#if defined(USE_PIX) + // If GPU capture is requested, we need to load the pix library as early as possible (before device queries/factories are made) + bool enablePixGPU = RHI::QueryCommandLineOption("enablePixGPU"); + if (enablePixGPU && AZ_TRAIT_PIX_MODULE && !s_pixModule) + { + //Get the path to the latest pix install directory + AZStd::wstring pixGpuDllPath = Platform::GetLatestWinPixGpuCapturerPath(); + AZStd::string dllPath; + AZStd::to_string(dllPath, pixGpuDllPath); + s_pixModule = DynamicModuleHandle::Create(dllPath.c_str()); + if (s_pixModule) + { + if (!s_pixModule->Load(false)) + { + AZ_Printf("RHISystem", "Pix capture requested but module failed to load.\n"); + } + } + } + //Pix dll can still be injected even if we do not pass in enablePixGPU. This can be done if we launch the app from Pix. + s_isPixGpuCaptureDllLoaded = Platform::IsPixDllInjected(AZ_TRAIT_PIX_MODULE); +#endif } void Factory::Register(Factory* instance) @@ -116,6 +152,12 @@ namespace AZ { s_renderDocModule->Unload(); } +#endif +#if defined(USE_PIX) + if (s_pixModule) + { + s_pixModule->Unload(); + } #endif } @@ -137,5 +179,23 @@ namespace AZ return s_renderDocApi; } #endif + + bool Factory::IsRenderDocModuleLoaded() + { +#if defined(USE_RENDERDOC) + return s_isRenderDocDllLoaded; +#else + return false; +#endif + } + + bool Factory::IsPixModuleLoaded() + { +#if defined(USE_PIX) + return s_isPixGpuCaptureDllLoaded; +#else + return false; +#endif + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.cpp index a38de024d5..1ee35c513a 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.cpp @@ -41,6 +41,8 @@ #define DX12_COMMANDLIST_TIMER_DETAIL(id) #endif +#define PIX_MARKER_CMDLIST_COL 0xFF0000FF + namespace AZ { namespace DX12 @@ -94,16 +96,22 @@ namespace AZ { SetName(name); - PIXBeginEvent(0xFF0000FF, name.GetCStr()); - PIXBeginEvent(GetCommandList(), 0xFF0000FF, name.GetCStr()); + PIXBeginEvent(PIX_MARKER_CMDLIST_COL, name.GetCStr()); + if (RHI::Factory::Get().IsPixModuleLoaded() || RHI::Factory::Get().IsRenderDocModuleLoaded()) + { + PIXBeginEvent(GetCommandList(), PIX_MARKER_CMDLIST_COL, name.GetCStr()); + } } void CommandList::Close() { FlushBarriers(); - - PIXEndEvent(GetCommandList()); PIXEndEvent(); + if (RHI::Factory::Get().IsPixModuleLoaded() || RHI::Factory::Get().IsRenderDocModuleLoaded()) + { + PIXEndEvent(GetCommandList()); + } + CommandListBase::Close(); } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp index d0a9ef0c07..182edb1298 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp @@ -7,6 +7,7 @@ */ #include #include +#include namespace AZ { @@ -46,7 +47,16 @@ namespace AZ #if defined (AZ_DX12_USE_PIPELINE_LIBRARY) AZStd::array_view bytes; - if (serializedData) + + bool shouldCreateLibFromSerializedData = true; + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + { + // CreatePipelineLibrary api does not function properly if Renderdoc or Pix is enabled + shouldCreateLibFromSerializedData = false; + } + + + if (serializedData && shouldCreateLibFromSerializedData) { bytes = serializedData->GetData(); } @@ -205,10 +215,11 @@ namespace AZ RHI::ResultCode PipelineLibrary::MergeIntoInternal([[maybe_unused]] AZStd::array_view pipelineLibraries) { -#if defined(USE_PIX) || defined(USE_RENDERDOC) - // StorePipeline api does not function properly if Pix or RenderDoc is enabled - return RHI::ResultCode::Fail; -#else + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + { + // StorePipeline api does not function properly if RenderDoc or Pix is enabled + return RHI::ResultCode::Fail; + } #if defined (AZ_DX12_USE_PIPELINE_LIBRARY) AZStd::lock_guard lock(m_mutex); @@ -226,8 +237,7 @@ namespace AZ } } #endif - return RHI::ResultCode::Success; -#endif + return RHI::ResultCode::Success; } RHI::ConstPtr PipelineLibrary::GetSerializedDataInternal() const diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp index 80e41434b9..cea072954a 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -309,8 +310,11 @@ namespace AZ commandList.GetValidator().BeginScope(*this); PIXBeginEvent(0xFFFF00FF, GetId().GetCStr()); - PIXBeginEvent(commandList.GetCommandList(), 0xFFFF00FF, GetId().GetCStr()); + if (RHI::Factory::Get().IsPixModuleLoaded() || RHI::Factory::Get().IsRenderDocModuleLoaded()) + { + PIXBeginEvent(commandList.GetCommandList(), 0xFFFF00FF, GetId().GetCStr()); + } commandList.SetAftermathEventMarker(GetId().GetCStr()); @@ -424,7 +428,10 @@ namespace AZ } } - PIXEndEvent(commandList.GetCommandList()); + if (RHI::Factory::Get().IsPixModuleLoaded() || RHI::Factory::Get().IsRenderDocModuleLoaded()) + { + PIXEndEvent(commandList.GetCommandList()); + } PIXEndEvent(); commandList.GetValidator().EndScope(); From 7f7d3d9c1c1e52b685cc4e16e6375da130e98500 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 9 Sep 2021 16:09:55 -0700 Subject: [PATCH 04/14] Creates destination directory if it doesnt exists, `file(ARCHIVE_CREATE` will fail if the directory is not there Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Projects.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index e70c8a4b14..7f2ac6a4fd 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -149,8 +149,9 @@ foreach(project ${LY_PROJECTS}) if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}") message(STATUS "Generating ${install_output_folder}/Engine.pak from @full_directory_path@/Cache") - file(ARCHIVE_CREATE OUTPUT ${install_output_folder}/Engine.pak - PATHS @full_directory_path@/Cache + file(MAKE_DIRECTORY "${install_output_folder}") + file(ARCHIVE_CREATE OUTPUT "${install_output_folder}/Engine.pak" + PATHS "@full_directory_path@/Cache" FORMAT zip ) message(STATUS "${install_output_folder}/Engine.pak generated") From 0445ec395619324e88f56c0c2e02eb5cc08da4d8 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Thu, 9 Sep 2021 16:13:23 -0700 Subject: [PATCH 05/14] Remove references to unsupported "xlib" windowing system support (#4028) Support for X clients is provided by xcb instead. Signed-off-by: Chris Burel --- .../Linux/AzFramework/Application/Application_Linux.cpp | 3 --- .../Linux/AzFramework/Windowing/NativeWindow_Linux.cpp | 3 --- .../AzFramework/Platform/Linux/platform_linux.cmake | 6 +----- .../Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake | 4 ---- .../Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp | 3 --- cmake/Platform/Linux/PAL_linux.cmake | 5 +++-- 6 files changed, 4 insertions(+), 20 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp index 5cc147b038..59602fe788 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp @@ -21,9 +21,6 @@ namespace AzFramework #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND #error "Linux Window Manager Wayland not supported." return nullptr; -#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB - #error "Linux Window Manager XLIB not supported." - return nullptr; #else #error "Linux Window Manager not recognized." return nullptr; diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp index 436be28ee6..2950f8dcfc 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp @@ -17,9 +17,6 @@ namespace AzFramework #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND #error "Linux Window Manager Wayland not supported." return nullptr; -#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB - #error "Linux Window Manager XLIB not supported." - return nullptr; #else #error "Linux Window Manager not recognized." return nullptr; diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake index c79c5f1dff..4480f86c1d 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake @@ -7,7 +7,7 @@ # # Based on the linux window manager trait, perform the appropriate additional build configurations -# Only 'xcb', 'wayland', and 'xlib' are recognized +# Only 'xcb' and 'wayland' are recognized if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") find_library(XCB_LIBRARY xcb) @@ -23,10 +23,6 @@ elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland") set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND) -elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "xlib") - - set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB) - else() message(FATAL_ERROR, "Linux Window Manager ${PAL_TRAIT_LINUX_WINDOW_MANAGER} is not recognized") diff --git a/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake b/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake index 1936c5b911..c89bbcb3f2 100644 --- a/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake +++ b/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake @@ -14,10 +14,6 @@ elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland") set(GLAD_VULKAN_COMPILE_DEFINITIONS VK_USE_PLATFORM_WAYLAND_KHR ) -elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "xlib") - set(GLAD_VULKAN_COMPILE_DEFINITIONS - VK_USE_PLATFORM_XLIB_KHR - ) else() message(FATAL_ERROR, "Linux Window Manager ${PAL_TRAIT_LINUX_WINDOW_MANAGER} is not recognized") endif() diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp index 98e91519ea..a818599b0d 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp @@ -41,9 +41,6 @@ namespace AZ #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND #error "Linux Window Manager Wayland not supported." return RHI::ResultCode::Unimplemented; -#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB - #error "Linux Window Manager XLIB not supported." - return RHI::ResultCode::Unimplemented; #else #error "Linux Window Manager not recognized." return RHI::ResultCode::Unimplemented; diff --git a/cmake/Platform/Linux/PAL_linux.cmake b/cmake/Platform/Linux/PAL_linux.cmake index 528bb5794c..c088163dca 100644 --- a/cmake/Platform/Linux/PAL_linux.cmake +++ b/cmake/Platform/Linux/PAL_linux.cmake @@ -41,5 +41,6 @@ set(LY_ASSET_DEPLOY_ASSET_TYPE "pc" CACHE STRING "Set the asset type for deploym ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) # Set the default window manager that applications should be using on Linux -# Note: Only ("xcb", "wayland", or "xlib" should be considered) -set(PAL_TRAIT_LINUX_WINDOW_MANAGER "xcb" CACHE STRING "Sets the Window Manager type to use when configuring Linux (xcb, wayland, or xlib)") +# Note: Only ("xcb" or "wayland" should be considered) +set(PAL_TRAIT_LINUX_WINDOW_MANAGER "xcb" CACHE STRING "Sets the Window Manager type to use when configuring Linux") +set_property(CACHE PAL_TRAIT_LINUX_WINDOW_MANAGER PROPERTY STRINGS xcb wayland) From cf4cb2750a0e5692fc35aaa4b27df62b064c402b Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 9 Sep 2021 16:27:39 -0700 Subject: [PATCH 06/14] Adding missing pragma header guards to _xcb.h header files (#4029) Signed-off-by: Steve Pham --- .../Linux/AzFramework/Application/Application_Linux_xcb.h | 1 + .../Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h index 55daedb4dd..fbd7f165d3 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#pragma once #include #include diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h index 73e255bab0..40181395e4 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#pragma once #include #include From 3a4937456a507c52a726e32be3057936ad00618b Mon Sep 17 00:00:00 2001 From: Artur K <96597+nemerle@users.noreply.github.com> Date: Fri, 10 Sep 2021 02:08:33 +0200 Subject: [PATCH 07/14] Build time reduction: AzStdOnDemandReflection (#3111) * Move a few specializations from AzStdOnDemandReflection.inl to cpp file This reduces compilation time and produced profile/debug file sizes. The specializations for string and string_view are only implemented for 'char' type, since others are not used anywhere. Extracted `Reflect` method from `ClientAuthAWSCredentials` to a cpp file. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Windows build fixes. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Added missing license. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Fix missing spaces in template argument lists Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Clang format on ClientAuthAWSCredentials.cpp Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Merge upstream development and fix linux build. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- .../AzCore/AzCore/Asset/AssetSerializer.h | 1 + .../AzCore/RTTI/AzStdOnDemandReflection.inl | 225 ++++-------------- ...AzStdOnDemandReflectionSpecializations.cpp | 208 ++++++++++++++++ .../Settings/SettingsRegistryScriptUtils.cpp | 1 + .../AzCore/AzCore/azcore_files.cmake | 1 + .../SettingsRegistryScriptUtilsTests.cpp | 9 +- .../Terrain/TerrainDataRequestBus.cpp | 1 + .../SceneCore/Events/ExportProductList.cpp | 1 + .../Authorization/ClientAuthAWSCredentials.h | 31 +-- .../ClientAuthAWSCredentials.cpp | 50 ++++ .../Code/awsclientauth_files.cmake | 1 + ...adiusWeightModifierComponentController.cpp | 1 + .../Code/Source/Asset/BlastChunksAsset.cpp | 1 + .../Code/Source/PythonReflectionComponent.cpp | 1 + .../Code/Source/PythonUtility.cpp | 5 +- .../Code/Source/Shape/ShapeComponent.cpp | 5 +- .../Code/Source/TerrainSystem/TerrainSystem.h | 3 +- .../Code/Source/WhiteBoxToolApiReflection.cpp | 1 + 18 files changed, 332 insertions(+), 214 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflectionSpecializations.cpp create mode 100644 Gems/AWSClientAuth/Code/Source/Authorization/ClientAuthAWSCredentials.cpp diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.h b/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.h index f2ac6ea459..a9e7f5f02a 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetSerializer.h @@ -10,6 +10,7 @@ #include #include +#include namespace AZ { struct Uuid; diff --git a/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl b/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl index 690bbb0b04..bba79e36f7 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl +++ b/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl @@ -7,18 +7,17 @@ */ #pragma once -#include #include #include #include -#include -#include -#include -#include -#include +#include #include #include -#include + +#ifndef AZ_USE_CUSTOM_SCRIPT_BIND +struct lua_State; +struct lua_Debug; +#endif // AZ_USE_CUSTOM_SCRIPT_BIND // forward declare specialized types namespace AZStd @@ -59,6 +58,26 @@ namespace AZ class BehaviorContext; class ScriptDataContext; + namespace OnDemandLuaFunctions + { + inline void AnyToLua(lua_State* lua, BehaviorValueParameter& param); + } + namespace ScriptCanvasOnDemandReflection + { + template + struct OnDemandPrettyName; + template + struct OnDemandToolTip; + template + struct OnDemandCategoryName; + } + namespace CommonOnDemandReflections + { + void ReflectCommonString(ReflectContext* context); + void ReflectCommonStringView(ReflectContext* context); + void ReflectStdAny(ReflectContext* context); + void ReflectVoidOutcome(ReflectContext* context); + } /// OnDemand reflection for AZStd::basic_string template struct OnDemandReflection< AZStd::basic_string > @@ -66,108 +85,16 @@ namespace AZ using ContainerType = AZStd::basic_string; using SizeType = typename ContainerType::size_type; using ValueType = typename ContainerType::value_type; - + static void Reflect(ReflectContext* context) { - if (BehaviorContext* behaviorContext = azrtti_cast(context)) + constexpr bool is_string = AZStd::is_same_v && AZStd::is_same_v> + && AZStd::is_same_v; + if constexpr(is_string) { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) - ->template Constructor() - ->Attribute(AZ::Script::Attributes::ConstructorOverride, &OnDemandLuaFunctions::ConstructBasicString) - ->Attribute(AZ::Script::Attributes::ReaderWriterOverride, ScriptContext::CustomReaderWriter(&OnDemandLuaFunctions::StringTypeToLua, &OnDemandLuaFunctions::StringTypeFromLua)) - ->template WrappingMember(&ContainerType::c_str) - ->Method("c_str", &ContainerType::c_str) - ->Method("Length", [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->length()); }) - ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) - ->Method("Equal", [](const ContainerType& lhs, const ContainerType& rhs) - { - return lhs == rhs; - }) - ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal) - ->Method("Find", [](ContainerType* thisPtr, const ContainerType& stringToFind, const int& startPos) - { - return aznumeric_cast(thisPtr->find(stringToFind, startPos)); - }) - ->Method("Substring", [](ContainerType* thisPtr, const int& pos, const int& len) - { - return thisPtr->substr(pos, len); - }) - ->Method("Replace", [](ContainerType* thisPtr, const ContainerType& stringToReplace, const ContainerType& replacementString) - { - SizeType startPos = 0; - while ((startPos = thisPtr->find(stringToReplace, startPos)) != ContainerType::npos && !stringToReplace.empty()) - { - thisPtr->replace(startPos, stringToReplace.length(), replacementString); - startPos += replacementString.length(); - } - - return *thisPtr; - }) - ->Method("ReplaceByIndex", [](ContainerType* thisPtr, const int& beginIndex, const int& endIndex, const ContainerType& replacementString) - { - thisPtr->replace(beginIndex, endIndex - beginIndex + 1, replacementString); - return *thisPtr; - }) - ->Method("Add", [](ContainerType* thisPtr, const ContainerType& addend) - { - return *thisPtr + addend; - }) - ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Concat) - ->Method("TrimLeft", [](ContainerType* thisPtr) - { - auto wsfront = AZStd::find_if_not(thisPtr->begin(), thisPtr->end(), [](char c) {return AZStd::is_space(c);}); - thisPtr->erase(thisPtr->begin(), wsfront); - return *thisPtr; - }) - ->Method("TrimRight", [](ContainerType* thisPtr) - { - auto wsend = AZStd::find_if_not(thisPtr->rbegin(), thisPtr->rend(), [](char c) {return AZStd::is_space(c);}); - thisPtr->erase(wsend.base(), thisPtr->end()); - return *thisPtr; - }) - ->Method("ToLower", [](ContainerType* thisPtr) - { - ContainerType toLowerString; - for (auto itr = thisPtr->begin(); itr < thisPtr->end(); itr++) - { - toLowerString.push_back(static_cast(tolower(*itr))); - } - return toLowerString; - }) - ->Method("ToUpper", [](ContainerType* thisPtr) - { - ContainerType toUpperString; - for (auto itr = thisPtr->begin(); itr < thisPtr->end(); itr++) - { - toUpperString.push_back(static_cast(toupper(*itr))); - } - return toUpperString; - }) - ->Method("Join", [](AZStd::vector* stringsToJoinPtr, const ContainerType& joinStr) - { - ContainerType joinString; - for (auto& stringToJoin : *stringsToJoinPtr) - { - joinString.append(stringToJoin).append(joinStr); - } - //Cut off the last join str - if (!stringsToJoinPtr->empty()) - { - joinString = joinString.substr(0, joinString.length() - joinStr.length()); - } - return joinString; - }) - - ->Method("Split", [](ContainerType* thisPtr, const ContainerType& splitter) - { - AZStd::vector splitStringList; - AZStd::tokenize(*thisPtr, splitter, splitStringList); - return splitStringList; - }) - ; + CommonOnDemandReflections::ReflectCommonString(context); } + static_assert (is_string, "Unspecialized basic_string<> template reflection requested."); } }; @@ -181,44 +108,9 @@ namespace AZ static void Reflect(ReflectContext* context) { - if (BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::Category, "Core") - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) - ->template Constructor() - ->Attribute(AZ::Script::Attributes::ConstructorOverride, &OnDemandLuaFunctions::ConstructStringView) - ->Attribute(AZ::Script::Attributes::ReaderWriterOverride, ScriptContext::CustomReaderWriter(&OnDemandLuaFunctions::StringTypeToLua, &OnDemandLuaFunctions::StringTypeFromLua)) - ->Method("ToString", [](const ContainerType& stringView) { return static_cast(stringView).c_str(); }, { { { "Reference", "String view object being converted to string" } } }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Converts string_view to string") - ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) - ->template WrappingMember(&ContainerType::data) - ->Method("data", &ContainerType::data) - ->Attribute(AZ::Script::Attributes::ToolTip, "Returns reference to raw string data") - ->Method("length", [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->length()); }, { { { "This", "Reference to the object the method is being performed on" } } }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Returns length of string view") - ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) - ->Method("size", [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->size()); }, { { { "This", "Reference to the object the method is being performed on" }} }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Returns length of string view") - ->Method("find", [](ContainerType* thisPtr, ContainerType stringToFind, int startPos) - { - return aznumeric_cast(thisPtr->find(stringToFind, startPos)); - }, { { { "This", "Reference to the object the method is being performed on" }, { "View", "View to search " }, { "Position", "Index in view to start search" }} }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Searches for supplied string within this string") - ->Method("substr", [](ContainerType* thisPtr, int pos, int len) - { - return thisPtr->substr(pos, len); - }, { {{"This", "Reference to the object the method is being performed on"}, {"Position", "Index in view that indicates the beginning of the sub string"}, {"Count", "Length of characters that sub string view occupies" }} }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Creates a sub view of this string view. The string data is not actually modified") - ->Method("remove_prefix", [](ContainerType* thisPtr, int n) {thisPtr->remove_prefix(n); }, - { { { "This", "Reference to the object the method is being performed on" }, { "Count", "Number of characters to remove from start of view" }} }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Moves the supplied number of characters from the beginning of this sub view") - ->Method("remove_suffix", [](ContainerType* thisPtr, int n) {thisPtr->remove_suffix(n); }, - { { { "This", "Reference to the object the method is being performed on" } ,{ "Count", "Number of characters to remove from end of view" }} }) - ->Attribute(AZ::Script::Attributes::ToolTip, "Moves the supplied number of characters from the end of this sub view") - ; - } + constexpr bool is_common = AZStd::is_same_v && AZStd::is_same_v>; + static_assert (is_common, "Unspecialized basic_string_view<> template reflection requested."); + CommonOnDemandReflections::ReflectCommonStringView(context); } }; @@ -228,7 +120,7 @@ namespace AZ { using ContainerType = AZStd::intrusive_ptr; - // TODO: Count reflection types for a proper un-reflect + // TODO: Count reflection types for a proper un-reflect static void CustomConstructor(ContainerType* thisPtr, ScriptDataContext& dc) { @@ -276,7 +168,7 @@ namespace AZ { using ContainerType = AZStd::shared_ptr; - // TODO: Count reflection types for a proper un-reflect + // TODO: Count reflection types for a proper un-reflect static void CustomConstructor(ContainerType* thisPtr, ScriptDataContext& dc) { @@ -435,7 +327,7 @@ namespace AZ thisPtr[uindex] = value; } - + static bool EraseCheck_VM(ContainerType& thisPtr, AZ::u64 index) { if (index < thisPtr.size()) @@ -448,7 +340,7 @@ namespace AZ return false; } } - + static ContainerType& ErasePost_VM(ContainerType& thisPtr, AZ::u64 /*index*/) { return thisPtr; @@ -604,7 +496,7 @@ namespace AZ return AZ::Failure(AZStd::string::format("Index out of bounds: %zu (size: %zu)", index, thisContainer.size())); } } - + static AZ::Outcome Replace(ContainerType& thisContainer, size_t index, T& value) { if (index >= 0 && index < thisContainer.size()) @@ -634,7 +526,7 @@ namespace AZ ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) ->Attribute(AZ::Script::Attributes::Deprecated, true) - + ->Method(k_accessElementName, &At, {{ {}, { "Index", "The index to read from", nullptr, BehaviorParameter::Traits::TR_INDEX }}}) ->Method(k_sizeName, [](ContainerType*) { return aznumeric_cast(N); }) ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) @@ -743,27 +635,8 @@ namespace AZ template<> // in case someone has an issue with bool struct OnDemandReflection> { - using OutcomeType = AZ::Outcome; - - static void Reflect(ReflectContext* context) - { - if (BehaviorContext* behaviorContext = azrtti_cast(context)) - { - // note we can reflect iterator types and support iterators, as of know we want to keep it simple - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Attribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, true) - ->Attribute(AZ::ScriptCanvasAttributes::PrettyName, &ScriptCanvasOnDemandReflection::OnDemandPrettyName::Get) - ->Attribute(AZ::Script::Attributes::ToolTip, &ScriptCanvasOnDemandReflection::OnDemandToolTip::Get) - ->Attribute(AZ::Script::Attributes::Category, &ScriptCanvasOnDemandReflection::OnDemandCategoryName::Get) - ->Attribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, AttributeIsValid::IfPresent) - ->Attribute(AZ::ScriptCanvasAttributes::VariableCreationForbidden, AttributeIsValid::IfPresent) - ->Method("Failure", []() -> OutcomeType { return AZ::Failure(); }) - ->Method("Success", []() -> OutcomeType { return AZ::Success(); }) - ->Method("IsSuccess", &OutcomeType::IsSuccess) - ; - } + static void Reflect(ReflectContext* context) { + CommonOnDemandReflections::ReflectVoidOutcome(context); } }; @@ -1179,16 +1052,8 @@ namespace AZ template <> struct OnDemandReflection { - static void Reflect(ReflectContext* context) - { - if (BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Attribute(Script::Attributes::Ignore, true) // Don't reflect any type to script (there should never be an any instance in script) - ->Attribute(Script::Attributes::ReaderWriterOverride, ScriptContext::CustomReaderWriter(&AZ::OnDemandLuaFunctions::AnyToLua, &OnDemandLuaFunctions::AnyFromLua)) - ; - } + static void Reflect(ReflectContext* context) { + CommonOnDemandReflections::ReflectStdAny(context); } }; diff --git a/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflectionSpecializations.cpp b/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflectionSpecializations.cpp new file mode 100644 index 0000000000..c42e32cd42 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflectionSpecializations.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +namespace AZ::CommonOnDemandReflections +{ + void ReflectCommonString(ReflectContext* context) + { + using ContainerType = AZStd::string; + using SizeType = typename ContainerType::size_type; + using ValueType = typename ContainerType::value_type; + if (BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->template Constructor() + ->Attribute(AZ::Script::Attributes::ConstructorOverride, &OnDemandLuaFunctions::ConstructBasicString) + ->Attribute(AZ::Script::Attributes::ReaderWriterOverride, ScriptContext::CustomReaderWriter(&OnDemandLuaFunctions::StringTypeToLua, &OnDemandLuaFunctions::StringTypeFromLua)) + ->template WrappingMember(&ContainerType::c_str) + ->Method("c_str", &ContainerType::c_str) + ->Method("Length", [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->length()); }) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) + ->Method("Equal", [](const ContainerType& lhs, const ContainerType& rhs) + { + return lhs == rhs; + }) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal) + ->Method("Find", [](ContainerType* thisPtr, const ContainerType& stringToFind, const int& startPos) + { + return aznumeric_cast(thisPtr->find(stringToFind, startPos)); + }) + ->Method("Substring", [](ContainerType* thisPtr, const int& pos, const int& len) + { + return thisPtr->substr(pos, len); + }) + ->Method("Replace", [](ContainerType* thisPtr, const ContainerType& stringToReplace, const ContainerType& replacementString) + { + SizeType startPos = 0; + while ((startPos = thisPtr->find(stringToReplace, startPos)) != ContainerType::npos && !stringToReplace.empty()) + { + thisPtr->replace(startPos, stringToReplace.length(), replacementString); + startPos += replacementString.length(); + } + + return *thisPtr; + }) + ->Method("ReplaceByIndex", [](ContainerType* thisPtr, const int& beginIndex, const int& endIndex, const ContainerType& replacementString) + { + thisPtr->replace(beginIndex, endIndex - beginIndex + 1, replacementString); + return *thisPtr; + }) + ->Method("Add", [](ContainerType* thisPtr, const ContainerType& addend) + { + return *thisPtr + addend; + }) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Concat) + ->Method("TrimLeft", [](ContainerType* thisPtr) + { + auto wsfront = AZStd::find_if_not(thisPtr->begin(), thisPtr->end(), [](char c) {return AZStd::is_space(c);}); + thisPtr->erase(thisPtr->begin(), wsfront); + return *thisPtr; + }) + ->Method("TrimRight", [](ContainerType* thisPtr) + { + auto wsend = AZStd::find_if_not(thisPtr->rbegin(), thisPtr->rend(), [](char c) {return AZStd::is_space(c);}); + thisPtr->erase(wsend.base(), thisPtr->end()); + return *thisPtr; + }) + ->Method("ToLower", [](ContainerType* thisPtr) + { + ContainerType toLowerString; + for (auto itr = thisPtr->begin(); itr < thisPtr->end(); itr++) + { + toLowerString.push_back(static_cast(tolower(*itr))); + } + return toLowerString; + }) + ->Method("ToUpper", [](ContainerType* thisPtr) + { + ContainerType toUpperString; + for (auto itr = thisPtr->begin(); itr < thisPtr->end(); itr++) + { + toUpperString.push_back(static_cast(toupper(*itr))); + } + return toUpperString; + }) + ->Method("Join", [](AZStd::vector* stringsToJoinPtr, const ContainerType& joinStr) + { + ContainerType joinString; + for (auto& stringToJoin : *stringsToJoinPtr) + { + joinString.append(stringToJoin).append(joinStr); + } + //Cut off the last join str + if (!stringsToJoinPtr->empty()) + { + joinString = joinString.substr(0, joinString.length() - joinStr.length()); + } + return joinString; + }) + + ->Method("Split", [](ContainerType* thisPtr, const ContainerType& splitter) + { + AZStd::vector splitStringList; + AZStd::tokenize(*thisPtr, splitter, splitStringList); + return splitStringList; + }) + ; + } + } + void ReflectCommonStringView(ReflectContext* context) + { + using ContainerType = AZStd::string_view; + + if (BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Category, "Core") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->template Constructor() + ->Attribute(AZ::Script::Attributes::ConstructorOverride, &OnDemandLuaFunctions::ConstructStringView) + ->Attribute(AZ::Script::Attributes::ReaderWriterOverride, ScriptContext::CustomReaderWriter(&OnDemandLuaFunctions::StringTypeToLua, &OnDemandLuaFunctions::StringTypeFromLua)) + ->Method("ToString", [](const ContainerType& stringView) { return static_cast(stringView).c_str(); }, { { { "Reference", "String view object being converted to string" } } }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Converts string_view to string") + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) + ->template WrappingMember(&ContainerType::data) + ->Method("data", &ContainerType::data) + ->Attribute(AZ::Script::Attributes::ToolTip, "Returns reference to raw string data") + ->Method("length", [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->length()); }, { { { "This", "Reference to the object the method is being performed on" } } }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Returns length of string view") + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length) + ->Method("size", [](ContainerType* thisPtr) { return aznumeric_cast(thisPtr->size()); }, { { { "This", "Reference to the object the method is being performed on" }} }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Returns length of string view") + ->Method("find", [](ContainerType* thisPtr, ContainerType stringToFind, int startPos) + { + return aznumeric_cast(thisPtr->find(stringToFind, startPos)); + }, { { { "This", "Reference to the object the method is being performed on" }, { "View", "View to search " }, { "Position", "Index in view to start search" }} }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Searches for supplied string within this string") + ->Method("substr", [](ContainerType* thisPtr, int pos, int len) + { + return thisPtr->substr(pos, len); + }, { {{"This", "Reference to the object the method is being performed on"}, {"Position", "Index in view that indicates the beginning of the sub string"}, {"Count", "Length of characters that sub string view occupies" }} }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Creates a sub view of this string view. The string data is not actually modified") + ->Method("remove_prefix", [](ContainerType* thisPtr, int n) {thisPtr->remove_prefix(n); }, + { { { "This", "Reference to the object the method is being performed on" }, { "Count", "Number of characters to remove from start of view" }} }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Moves the supplied number of characters from the beginning of this sub view") + ->Method("remove_suffix", [](ContainerType* thisPtr, int n) {thisPtr->remove_suffix(n); }, + { { { "This", "Reference to the object the method is being performed on" } ,{ "Count", "Number of characters to remove from end of view" }} }) + ->Attribute(AZ::Script::Attributes::ToolTip, "Moves the supplied number of characters from the end of this sub view") + ; + } + } + + + void ReflectVoidOutcome(ReflectContext* context) + { + using OutcomeType = AZ::Outcome; + + if (BehaviorContext* behaviorContext = azrtti_cast(context)) + { + // note we can reflect iterator types and support iterators, as of know we want to keep it simple + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, true) + ->Attribute(AZ::ScriptCanvasAttributes::PrettyName, &ScriptCanvasOnDemandReflection::OnDemandPrettyName::Get) + ->Attribute(AZ::Script::Attributes::ToolTip, &ScriptCanvasOnDemandReflection::OnDemandToolTip::Get) + ->Attribute(AZ::Script::Attributes::Category, &ScriptCanvasOnDemandReflection::OnDemandCategoryName::Get) + ->Attribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, AttributeIsValid::IfPresent) + ->Attribute(AZ::ScriptCanvasAttributes::VariableCreationForbidden, AttributeIsValid::IfPresent) + ->Method("Failure", []() -> OutcomeType { return AZ::Failure(); }) + ->Method("Success", []() -> OutcomeType { return AZ::Success(); }) + ->Method("IsSuccess", &OutcomeType::IsSuccess) + ; + } + } + + void ReflectStdAny(ReflectContext* context) + { + if (BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute( + Script::Attributes::Ignore, true) // Don't reflect any type to script (there should never be an any instance in script) + ->Attribute( + Script::Attributes::ReaderWriterOverride, + ScriptContext::CustomReaderWriter(&AZ::OnDemandLuaFunctions::AnyToLua, &OnDemandLuaFunctions::AnyFromLua)); + } + } + +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp index abaab425d3..c32591874b 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index c2ee439645..b2ef586053 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -440,6 +440,7 @@ set(FILES RTTI/AttributeReader.h RTTI/AzStdOnDemandPrettyName.inl RTTI/AzStdOnDemandReflection.inl + RTTI/AzStdOnDemandReflectionSpecializations.cpp RTTI/AzStdOnDemandReflectionLuaFunctions.inl RTTI/BehaviorContext.cpp RTTI/BehaviorContext.h diff --git a/Code/Framework/AzCore/Tests/Settings/SettingsRegistryScriptUtilsTests.cpp b/Code/Framework/AzCore/Tests/Settings/SettingsRegistryScriptUtilsTests.cpp index 111b4a027a..0fc491ac34 100644 --- a/Code/Framework/AzCore/Tests/Settings/SettingsRegistryScriptUtilsTests.cpp +++ b/Code/Framework/AzCore/Tests/Settings/SettingsRegistryScriptUtilsTests.cpp @@ -10,11 +10,12 @@ #include #include #include +#include namespace SettingsRegistryScriptUtilsTests { static constexpr const char* SettingsRegistryScriptClassName = "SettingsRegistryInterface"; - + class SettingsRegistryBehaviorContextFixture : public UnitTest::ScopedAllocatorSetupFixture @@ -77,7 +78,7 @@ namespace SettingsRegistryScriptUtilsTests // so the invoking the getter on global Settings Registry should succeed, but return nullptr EXPECT_TRUE(globalSettingsRegistryGetter->InvokeResult(settingsRegistryObject)); EXPECT_EQ(nullptr, settingsRegistryObject.m_settingsRegistry); - + // Register the Settings Registry stored on the fixture with the SettingsRegistry Interface AZ::SettingsRegistry::Register(m_registry.get()); EXPECT_TRUE(globalSettingsRegistryGetter->InvokeResult(settingsRegistryObject)); @@ -227,7 +228,7 @@ namespace SettingsRegistryScriptUtilsTests R"( "intIndex": -55)" "\n" R"( })" "\n" R"(])"; - + // Populate the settings registry to match the expected json values m_registry->Set("/TestObject/boolValue", false); m_registry->Set("/TestObject/intValue", aznumeric_cast(17)); @@ -562,4 +563,4 @@ namespace SettingsRegistryScriptUtilsTests SettingsRegistryBehaviorContextParams{ "/TestObject/stringValue", AZStd::string_view{"Hello World"}, "GetString", "SetString" } ) ); -} +} diff --git a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp index 59a49a48e1..947d3821ab 100644 --- a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp +++ b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.cpp @@ -7,6 +7,7 @@ */ #include "TerrainDataRequestBus.h" +#include namespace AzFramework { diff --git a/Code/Tools/SceneAPI/SceneCore/Events/ExportProductList.cpp b/Code/Tools/SceneAPI/SceneCore/Events/ExportProductList.cpp index d8a4cf1991..2392ed8219 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/ExportProductList.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Events/ExportProductList.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace AZ diff --git a/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h b/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h index ba08534d91..9a838be556 100644 --- a/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h +++ b/Gems/AWSClientAuth/Code/Include/Public/Authorization/ClientAuthAWSCredentials.h @@ -8,11 +8,15 @@ #pragma once -#include #include #include +namespace AZ +{ + class ReflectContext; +} + namespace AWSClientAuth { //! Client auth AWS Credentials object for serialization. @@ -54,31 +58,8 @@ namespace AWSClientAuth return m_sessionToken; } - static void Reflect(AZ::ReflectContext* context) - { - auto serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class() - ->Field("AWSAccessKeyId", &ClientAuthAWSCredentials::m_accessKeyId) - ->Field("AWSSecretKey", &ClientAuthAWSCredentials::m_secretKey) - ->Field("AWSSessionToken", &ClientAuthAWSCredentials::m_sessionToken); - } + static void Reflect(AZ::ReflectContext* context); - AZ::BehaviorContext* behaviorContext = azrtti_cast(context); - if (behaviorContext) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::Category, "AWSClientAuth") - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Constructor() - ->Constructor() - ->Property("AWSAccessKeyId", BehaviorValueGetter(&ClientAuthAWSCredentials::m_accessKeyId), BehaviorValueSetter(&ClientAuthAWSCredentials::m_accessKeyId)) - ->Property("AWSSecretKey", BehaviorValueGetter(&ClientAuthAWSCredentials::m_secretKey), BehaviorValueSetter(&ClientAuthAWSCredentials::m_secretKey)) - ->Property("AWSSessionToken", BehaviorValueGetter(&ClientAuthAWSCredentials::m_sessionToken), BehaviorValueSetter(&ClientAuthAWSCredentials::m_sessionToken)); - } - } private: AZStd::string m_accessKeyId; diff --git a/Gems/AWSClientAuth/Code/Source/Authorization/ClientAuthAWSCredentials.cpp b/Gems/AWSClientAuth/Code/Source/Authorization/ClientAuthAWSCredentials.cpp new file mode 100644 index 0000000000..764c67a4a6 --- /dev/null +++ b/Gems/AWSClientAuth/Code/Source/Authorization/ClientAuthAWSCredentials.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include +#include + +namespace AWSClientAuth +{ + void ClientAuthAWSCredentials::Reflect(AZ::ReflectContext* context) + { + auto serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Field("AWSAccessKeyId", &ClientAuthAWSCredentials::m_accessKeyId) + ->Field("AWSSecretKey", &ClientAuthAWSCredentials::m_secretKey) + ->Field("AWSSessionToken", &ClientAuthAWSCredentials::m_sessionToken); + } + + AZ::BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Category, "AWSClientAuth") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Constructor() + ->Constructor() + ->Property( + "AWSAccessKeyId", BehaviorValueGetter(&ClientAuthAWSCredentials::m_accessKeyId), + BehaviorValueSetter(&ClientAuthAWSCredentials::m_accessKeyId)) + ->Property( + "AWSSecretKey", BehaviorValueGetter(&ClientAuthAWSCredentials::m_secretKey), + BehaviorValueSetter(&ClientAuthAWSCredentials::m_secretKey)) + ->Property( + "AWSSessionToken", BehaviorValueGetter(&ClientAuthAWSCredentials::m_sessionToken), + BehaviorValueSetter(&ClientAuthAWSCredentials::m_sessionToken)); + } + } +} // namespace AWSClientAuth diff --git a/Gems/AWSClientAuth/Code/awsclientauth_files.cmake b/Gems/AWSClientAuth/Code/awsclientauth_files.cmake index ad679fe13a..bd4c971377 100644 --- a/Gems/AWSClientAuth/Code/awsclientauth_files.cmake +++ b/Gems/AWSClientAuth/Code/awsclientauth_files.cmake @@ -42,6 +42,7 @@ set(FILES Source/Authentication/LWAAuthenticationProvider.cpp Source/Authentication/GoogleAuthenticationProvider.cpp + Source/Authorization/ClientAuthAWSCredentials.cpp Source/Authorization/AWSCognitoAuthorizationController.cpp Source/Authorization/AWSClientAuthPersistentCognitoIdentityProvider.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponentController.cpp index c2d7176436..434fc81e8f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/RadiusWeightModifierComponentController.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace AZ { diff --git a/Gems/Blast/Code/Source/Asset/BlastChunksAsset.cpp b/Gems/Blast/Code/Source/Asset/BlastChunksAsset.cpp index 1d0ce15241..2a1ae5eb04 100644 --- a/Gems/Blast/Code/Source/Asset/BlastChunksAsset.cpp +++ b/Gems/Blast/Code/Source/Asset/BlastChunksAsset.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace Blast { diff --git a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp index 38e00e73ed..4ca40a5bab 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp b/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp index bc1a7d45a3..d324c6c14b 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -276,7 +277,7 @@ namespace EditorPythonBindings } return false; } - + bool AllocateBehaviorValueParameter(const AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorValueParameter& result, Convert::StackVariableAllocator& stackVariableAllocator) { if (const AZ::BehaviorParameter* resultType = behaviorMethod->GetResult()) @@ -390,7 +391,7 @@ namespace EditorPythonBindings cleanUp(); } } - + void StackVariableAllocator::StoreVariableDeleter(VariableDeleter&& deleter) { m_cleanUpItems.emplace_back(deleter); diff --git a/Gems/LmbrCentral/Code/Source/Shape/ShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/ShapeComponent.cpp index 522493e1b4..9599b3f6e7 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/ShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/ShapeComponent.cpp @@ -7,6 +7,7 @@ */ #include +#include #include namespace LmbrCentral @@ -23,7 +24,7 @@ namespace LmbrCentral Call(FN_OnShapeChanged, changeReason); } }; - + void ShapeComponentGeneric::Reflect(AZ::ReflectContext* context) { AZ::BehaviorContext* behaviorContext = azrtti_cast(context); @@ -41,7 +42,7 @@ namespace LmbrCentral behaviorContext->Enum<(int)ShapeComponentNotifications::ShapeChangeReasons::TransformChanged>("ShapeChangeReasons_TransformChanged") ->Enum<(int)LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged>("ShapeChangeReasons_ShapeChanged"); - + behaviorContext->EBus("ShapeComponentNotificationsBus") ->Handler() ; diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 0239170640..a75685fb12 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,7 @@ namespace Terrain /////////////////////////////////////////// // TerrainSystemServiceRequestBus::Handler Impl - + void SetWorldBounds(const AZ::Aabb& worldBounds) override; void SetHeightQueryResolution(AZ::Vector2 queryResolution) override; diff --git a/Gems/WhiteBox/Code/Source/WhiteBoxToolApiReflection.cpp b/Gems/WhiteBox/Code/Source/WhiteBoxToolApiReflection.cpp index f1b015b359..9c954e628c 100644 --- a/Gems/WhiteBox/Code/Source/WhiteBoxToolApiReflection.cpp +++ b/Gems/WhiteBox/Code/Source/WhiteBoxToolApiReflection.cpp @@ -10,6 +10,7 @@ #include "WhiteBoxToolApiReflection.h" #include +#include #include #include #include From 0505f200e55e67175aabced45bc18bd8c170ba07 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Fri, 10 Sep 2021 09:11:06 -0500 Subject: [PATCH 08/14] Remove extra/bad profile markers (#4031) Many of these are just extra noise in the profile, but the one in Archive.cpp could also cause PIX to crash. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Framework/AzFramework/AzFramework/Archive/Archive.cpp | 3 --- .../Visibility/EntityVisibilityBoundsUnionSystem.cpp | 8 -------- .../ViewportSelection/EditorVisibleEntityDataCache.cpp | 2 -- .../Common/Code/Source/Mesh/MeshFeatureProcessor.cpp | 7 ------- Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp | 1 - Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp | 2 -- Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp | 4 ---- Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp | 4 ---- Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp | 2 -- Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp | 4 ---- .../Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp | 2 -- .../RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp | 4 ---- .../Source/RPI.Reflect/Shader/ShaderVariantTreeAsset.cpp | 2 -- .../Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp | 2 -- .../Source/Components/SurfaceDataColliderComponent.cpp | 2 -- .../Code/Source/SurfaceDataSystemComponent.cpp | 2 -- 16 files changed, 51 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index ce0cc23a4e..e125cc1df6 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -689,9 +689,6 @@ namespace AZ::IO return AZ::IO::InvalidHandle; } - AZ_PROFILE_SCOPE(Game, "File: %.*s Archive: %p", - aznumeric_cast(pName.size()), pName.data(), this); - SAutoCollectFileAccessTime accessTime(this); AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; diff --git a/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp b/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp index 4a6b29b95c..826570fee5 100644 --- a/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp +++ b/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp @@ -45,8 +45,6 @@ namespace AzFramework void EntityVisibilityBoundsUnionSystem::OnEntityActivated(AZ::Entity* entity) { - AZ_PROFILE_FUNCTION(AzFramework); - // ignore any entity that might activate which does not have a TransformComponent if (entity->GetTransform() == nullptr) { @@ -68,8 +66,6 @@ namespace AzFramework void EntityVisibilityBoundsUnionSystem::OnEntityDeactivated(AZ::Entity* entity) { - AZ_PROFILE_FUNCTION(AzFramework); - // ignore any entity that might deactivate which does not have a TransformComponent if (entity->GetTransform() == nullptr) { @@ -89,8 +85,6 @@ namespace AzFramework void EntityVisibilityBoundsUnionSystem::UpdateVisibilitySystem(AZ::Entity* entity, EntityVisibilityBoundsUnionInstance& instance) { - AZ_PROFILE_FUNCTION(AzFramework); - if (const auto& localEntityBoundsUnions = instance.m_localEntityBoundsUnion; localEntityBoundsUnions.IsValid()) { // note: worldEntityBounds will not be a 'tight-fit' Aabb but that of a transformed local aabb @@ -155,8 +149,6 @@ namespace AzFramework void EntityVisibilityBoundsUnionSystem::OnTransformUpdated(AZ::Entity* entity) { - AZ_PROFILE_FUNCTION(AzFramework); - // update the world transform of the visibility bounds union if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity); instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp index 5da827244f..babc6f972c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp @@ -312,8 +312,6 @@ namespace AzToolsFramework void EditorVisibleEntityDataCache::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) { - AZ_PROFILE_FUNCTION(AzToolsFramework); - const AZ::EntityId entityId = *AZ::TransformNotificationBus::GetCurrentBusId(); if (AZStd::optional entityIndex = GetVisibleEntityIndexFromId(entityId)) diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 67702a8176..8ab324cdf7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -479,8 +479,6 @@ namespace AZ : m_modelAsset(modelAsset) , m_parent(parent) { - AZ_PROFILE_FUNCTION(AzRender); - if (!m_modelAsset.GetId().IsValid()) { AZ_Error("MeshDataInstance::MeshLoader", false, "Invalid model asset Id."); @@ -508,7 +506,6 @@ namespace AZ //! AssetBus::Handler overrides... void MeshDataInstance::MeshLoader::OnAssetReady(Data::Asset asset) { - AZ_PROFILE_FUNCTION(AzRender); Data::Asset modelAsset = asset; // Assign the fully loaded asset back to the mesh handle to not only hold asset id, but the actual data as well. @@ -580,8 +577,6 @@ namespace AZ void MeshDataInstance::Init(Data::Instance model) { - AZ_PROFILE_FUNCTION(AzRender); - m_model = model; const size_t modelLodCount = m_model->GetLodCount(); m_drawPacketListsByLod.resize(modelLodCount); @@ -612,8 +607,6 @@ namespace AZ void MeshDataInstance::BuildDrawPacketList(size_t modelLodIndex) { - AZ_PROFILE_FUNCTION(AzRender); - RPI::ModelLod& modelLod = *m_model->GetLods()[modelLodIndex]; const size_t meshCount = modelLod.GetMeshes().size(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 0b8c6ad9ce..fe69f56856 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -417,7 +417,6 @@ namespace AZ void FrameScheduler::ExecuteContextInternal(FrameGraphExecuteGroup& group, uint32_t index) { - AZ_PROFILE_FUNCTION(RHI); FrameGraphExecuteContext* executeContext = group.BeginContext(index); { diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index a2a2736106..6e98456933 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -278,8 +278,6 @@ namespace AZ const PipelineState* PipelineStateCache::AcquirePipelineState(PipelineLibraryHandle handle, const PipelineStateDescriptor& descriptor) { - AZ_PROFILE_FUNCTION(RHI); - if (handle.IsNull()) { return nullptr; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp index 0e4c8ec15a..49b093d4be 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp @@ -124,7 +124,6 @@ namespace AZ bool MeshDrawPacket::DoUpdate(const Scene& parentScene) { - AZ_PROFILE_FUNCTION(RPI); const ModelLod::Mesh& mesh = m_modelLod->GetMeshes()[m_modelLodMeshIndex]; if (!m_material) @@ -155,8 +154,6 @@ namespace AZ auto appendShader = [&](const ShaderCollection::Item& shaderItem) { - AZ_PROFILE_SCOPE(RPI, "appendShader()"); - // Skip the shader item without creating the shader instance // if the mesh is not going to be rendered based on the draw tag RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get(); @@ -256,7 +253,6 @@ namespace AZ Data::Instance drawSrg; if (drawSrgLayout) { - AZ_PROFILE_SCOPE(RPI, "create drawSrg"); // If the DrawSrg exists we must create and bind it, otherwise the CommandList will fail validation for SRG being null drawSrg = RPI::ShaderResourceGroup::Create(shader->GetAsset(), shader->GetSupervariantIndex(), drawSrgLayout->GetName()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp index 04fa004228..7850aa6da2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp @@ -264,8 +264,6 @@ namespace AZ const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap) const { - AZ_PROFILE_FUNCTION(RPI); - streamBufferViewsOut.clear(); RHI::InputStreamLayoutBuilder layoutBuilder; @@ -366,8 +364,6 @@ namespace AZ const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap) const { - AZ_PROFILE_FUNCTION(RPI); - const Mesh& mesh = m_meshes[meshIndex]; auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp index 51cbc82f2b..4c923d4a1a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp @@ -230,8 +230,6 @@ namespace AZ void RasterPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) { - AZ_PROFILE_FUNCTION(RPI); - RHI::CommandList* commandList = context.GetCommandList(); const RHI::DrawListView drawListViewPartition = RHI::GetDrawListPartition(m_drawListView, context.GetCommandListIndex(), context.GetCommandListCount()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp index e602e3e91b..51dd9c36d3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp @@ -299,7 +299,6 @@ namespace AZ const ShaderVariant& Shader::GetVariant(const ShaderVariantId& shaderVariantId) { - AZ_PROFILE_FUNCTION(RPI); Data::Asset shaderVariantAsset = m_asset->GetVariant(shaderVariantId, m_supervariantIndex); if (!shaderVariantAsset || shaderVariantAsset->IsRootVariant()) { @@ -316,15 +315,12 @@ namespace AZ ShaderVariantSearchResult Shader::FindVariantStableId(const ShaderVariantId& shaderVariantId) const { - AZ_PROFILE_FUNCTION(RPI); ShaderVariantSearchResult variantSearchResult = m_asset->FindVariantStableId(shaderVariantId); return variantSearchResult; } const ShaderVariant& Shader::GetVariant(ShaderVariantStableId shaderVariantStableId) { - AZ_PROFILE_FUNCTION(RPI); - if (!shaderVariantStableId.IsValid() || shaderVariantStableId == ShaderAsset::RootShaderVariantStableId) { return m_rootVariant; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp index 8230c57e15..8e2d1bdd7e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp @@ -96,8 +96,6 @@ namespace AZ const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, bool allowBruteForce, float& distanceNormalized, AZ::Vector3& normal) const { - AZ_PROFILE_FUNCTION(RPI); - if (!m_modelTriangleCount) { // [GFX TODO][ATOM-4343 Bake mesh spatial information during AP processing] diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 616aa44299..49ef457311 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -172,8 +172,6 @@ namespace AZ Data::Asset ShaderAsset::GetVariant( const ShaderVariantId& shaderVariantId, SupervariantIndex supervariantIndex) { - AZ_PROFILE_FUNCTION(RPI); - auto variantFinder = AZ::Interface::Get(); AZ_Assert(variantFinder, "The IShaderVariantFinder doesn't exist"); @@ -189,8 +187,6 @@ namespace AZ ShaderVariantSearchResult ShaderAsset::FindVariantStableId(const ShaderVariantId& shaderVariantId) { - AZ_PROFILE_FUNCTION(RPI); - uint32_t dynamicOptionCount = aznumeric_cast(GetShaderOptionGroupLayout()->GetShaderOptions().size()); ShaderVariantSearchResult variantSearchResult{RootShaderVariantStableId, dynamicOptionCount }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantTreeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantTreeAsset.cpp index f17a144adb..4565e3ce25 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantTreeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantTreeAsset.cpp @@ -72,8 +72,6 @@ namespace AZ ShaderVariantSearchResult ShaderVariantTreeAsset::FindVariantStableId(const ShaderOptionGroupLayout* shaderOptionGroupLayout, const ShaderVariantId& shaderVariantId) const { - AZ_PROFILE_FUNCTION(RPI); - struct NodeToVisit { uint32_t m_branchCount; // Number of static branches diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp index be4d5186c8..cbb30e6cd8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp @@ -147,8 +147,6 @@ namespace SurfaceData bool SurfaceDataMeshComponent::DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const { - AZ_PROFILE_FUNCTION(Entity); - AZStd::lock_guard lock(m_cacheMutex); // test AABB as first pass to claim the point diff --git a/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp b/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp index eef9179194..31bf4e7028 100644 --- a/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp +++ b/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp @@ -184,8 +184,6 @@ namespace SurfaceData bool SurfaceDataColliderComponent::DoRayTrace(const AZ::Vector3& inPosition, bool queryPointOnly, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const { - AZ_PROFILE_FUNCTION(Entity); - AZStd::lock_guard lock(m_cacheMutex); // test AABB as first pass to claim the point diff --git a/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp b/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp index 7638b6720e..9038d2f082 100644 --- a/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp +++ b/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp @@ -229,8 +229,6 @@ namespace SurfaceData void SurfaceDataSystemComponent::GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, const SurfaceTagVector& desiredTags, SurfacePointListPerPosition& surfacePointListPerPosition) const { - AZ_PROFILE_FUNCTION(Entity); - AZStd::lock_guard registrationLock(m_registrationMutex); surfacePointListPerPosition.clear(); From e638f27572841755335e335ecca03ce4c6da23d7 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 10 Sep 2021 10:02:17 -0500 Subject: [PATCH 09/14] Fixed PathView `MakeRelativeTo` and `Append` functions path segment comparisons (#3628) * Fixed PathView `MakeRelativeTo` and `Append` functions path segment comparisons when using the Windows path separator of '\' The PathSegment comparisons were case-sensitive in both those functions and now use `Internal::ComparePathSegments` function to perform the appropriate case comparison based on the path separator value of the Path class Reverted the LocalFileIO::CheckInvalidWrite function back to not lowercasing the assets alias and input path before invoking `PathView::IsRelativeTo` Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Simplified the LocalFileIO::ConvertToAliasBuffer logic Fix for the ArchiveTest `IResourceList_Add_AbsolutePath_RemovesAndReplacesWithAlias` and `TestArchiveViaFileIO` test Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added a PathIterable structure stores a non-heap container of normalized path segments of an input path. Moved the PathParser logic to a PathParser.inl file Removed dependency of the PathView::IsRelativeTo logic on FixedMaxPath There is no longer a 1024 character limit when determining if a path is relative to a base Added a GetNormalPathParts and AppendNormalPathParts to function and removed LexicallyNormalInplace to share the logic for creating a normalized path between IsRelativeTo and LexicallyNormal Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Clang PathIterable.inl build fix Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed Normalize and Relative Path functions initialize the result paths With the correct path separator for the paths being transformed Ported over the Custom Path Root Separator logic to the PathParser.inl Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated he Shader Preprocessor include path gather. It now uses AZ::IO::Path for the path operations and checks if the path exist before adding it to the list of include paths. Finally the set logic has been removed for a simpler find_if check to see if the include path already since in the project include paths This fixes the Asset Processing issues with shader includes due to the Path.inl changes Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed tail recursion call to AppendNormalPathParts to supply a PathView with the same path separator as the parent call Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding reference qualifier overloads to the Path class Native function Removed the conversion operators from the Path class for converting to a string_type&/const string_type& Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/IO/Path/Path.h | 25 +- Code/Framework/AzCore/AzCore/IO/Path/Path.inl | 902 +++--------------- .../AzCore/AzCore/IO/Path/PathIterable.inl | 162 ++++ .../AzCore/AzCore/IO/Path/PathParser.inl | 706 ++++++++++++++ .../AzCore/AzCore/azcore_files.cmake | 2 + .../AzCore/Tests/IO/Path/PathTests.cpp | 22 +- .../AzFramework/Archive/Archive.cpp | 6 +- .../AzFramework/AzFramework/Archive/Archive.h | 2 +- .../AzFramework/Archive/IArchive.h | 2 +- .../AzFramework/IO/LocalFileIO.cpp | 31 +- .../AzFramework/Tests/ArchiveTests.cpp | 13 +- Code/Legacy/CryCommon/Mocks/ICryPakMock.h | 2 +- .../Editor/CommonFiles/Preprocessor.cpp | 130 ++- 13 files changed, 1128 insertions(+), 877 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl create mode 100644 Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.h b/Code/Framework/AzCore/AzCore/IO/Path/Path.h index 36640e821d..c0c4b1c974 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.h +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.h @@ -256,8 +256,22 @@ namespace AZ::IO template static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base); - template - static constexpr void LexicallyNormalInplace(PathResultType& pathResult, const AZ::IO::PathView& path); + + struct PathIterable; + //! Returns a structure that provides a view of the path parts which can be used for iteration + //! Only the path parts that correspond to creating an normalized path is returned + //! This function is useful for returning a "view" into a normalized path without the need + //! to allocate memory for the heap + static constexpr PathIterable GetNormalPathParts(const AZ::IO::PathView& path) noexcept; + // joins the input path to the Path Iterable structure using similiar logic to Path::Append + // If the input path is absolute it will replace the current PathIterable otherwise + // the input path will be appended to the Path Iterable structure + // For example a PathIterable with parts = ['C:', '/', 'foo'] + // If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar'] + // If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar'] + // If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ] + // If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ] + static constexpr void AppendNormalPathParts(PathIterable& pathIterableResult, const AZ::IO::PathView& path) noexcept; constexpr int compare_string_view(AZStd::string_view other) const; constexpr AZStd::string_view root_name_view() const; @@ -442,14 +456,15 @@ namespace AZ::IO constexpr void swap(BasicPath& rhs) noexcept; // native format observers - constexpr const string_type& Native() const noexcept; + constexpr const string_type& Native() const& noexcept; + constexpr const string_type&& Native() const&& noexcept; constexpr const value_type* c_str() const noexcept; constexpr explicit operator string_type() const; // Adds support for retrieving a modifiable copy of the underlying string // Any modifications to the string invalidates existing PathIterators - constexpr string_type& Native() noexcept; - constexpr explicit operator string_type&() noexcept; + constexpr string_type& Native() & noexcept; + constexpr string_type&& Native() && noexcept; //! The string and wstring functions cannot be constexpr until AZStd::basic_string is made constexpr. //! This cannot occur until C++20 as operator new/delete cannot be used within constexpr functions diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl index 6354324136..40cbf6f46b 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl @@ -8,10 +8,10 @@ #pragma once -#include #include #include -#include + +#include // extern instantiations of Path templates to prevent implicit instantiations namespace AZ::IO @@ -56,702 +56,6 @@ namespace AZ::IO const PathIterator& rhs); } -namespace AZ::IO::Internal -{ - constexpr bool IsSeparator(const char elem) - { - return elem == '/' || elem == '\\'; - } - template >> - static constexpr bool HasDrivePrefix(InputIt first, EndIt last) - { - size_t prefixSize = AZStd::distance(first, last); - if (prefixSize < 2 || *AZStd::next(first, 1) != ':') - { - // Drive prefix must be at least two characters and have a colon for the second character - return false; - } - - constexpr size_t ValidDrivePrefixRange = 26; - // Uppercase the drive letter by bitwise and'ing out the the 2^5 bit - unsigned char driveLetter = static_cast(*first); - - driveLetter &= 0b1101'1111; - // normalize the character value in the range of A-Z -> 0-25 - driveLetter -= 'A'; - return driveLetter < ValidDrivePrefixRange; - } - - static constexpr bool HasDrivePrefix(AZStd::string_view prefix) - { - return HasDrivePrefix(prefix.begin(), prefix.end()); - } - - //! Returns an iterator past the end of the consumed root name - //! Windows root names can have include drive letter within them - template - constexpr auto ConsumeRootName(InputIt entryBeginIter, InputIt entryEndIter, const char preferredSeparator) - -> AZStd::enable_if_t, InputIt> - { - if (preferredSeparator == PosixPathSeparator) - { - // If the preferred separator is forward slash the parser is in posix path - // parsing mode, which doesn't have a root name, - // unless we're on a posix platform that uses a custom path root separator - #if defined(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) - const AZStd::string_view path{ entryBeginIter, entryEndIter }; - const auto positionOfPathSeparator = path.find(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR); - if (positionOfPathSeparator == AZStd::string_view::npos) - { - return entryBeginIter; - } - const AZStd::string_view rootName{ path.substr(0, positionOfPathSeparator + 1) }; - return AZStd::next(entryBeginIter, rootName.size()); - #else - return entryBeginIter; - #endif - } - else - { - // Information for GetRootName has been gathered from Microsoft header - // Below are examples of paths and what there root-name will return - // "/" - returns "" - // "foo/" - returns "" - // "C:DriveRelative" - returns "C:" - // "C:\\DriveAbsolute" - returns "C:" - // "C://DriveAbsolute" - returns "C:" - // "\\server\share" - returns "\\server" - // The following paths are based on the UNC specification to work with paths longer than the 260 character path limit - // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#maximum-path-length-limitation - // \\?\device - returns "\\?" - // \??\device - returns "\??" - // \\.\device - returns "\\." - - - AZStd::string_view path{ entryBeginIter, entryEndIter }; - - if (path.size() < 2) - { - // A root name is either or a network path - // therefore it has a least two characters - return entryBeginIter; - } - - if (HasDrivePrefix(path)) - { - // If the path has a drive prefix, then it has a root name of - return AZStd::next(entryBeginIter, 2); - } - - if (!Internal::IsSeparator(path[0])) - { - // At this point all other root names start with a path separator - return entryBeginIter; - } - - // Check if the path has the form of "\\?\, "\??\" or "\\.\" - const bool pathInUncForm = path.size() >= 4 && Internal::IsSeparator(path[3]) - && (path.size() == 4 || !Internal::IsSeparator(path[4])); - if (pathInUncForm) - { - // \\?\<0 or more> or \\.\$ - const bool slashQuestionMark = Internal::IsSeparator(path[1]) && (path[2] == '?' || path[2] == '.'); - // \??\<0 or more> - const bool questionMarkTwice = path[1] == '?' && path[2] == '?'; - if (slashQuestionMark || questionMarkTwice) - { - // Return the root value root slash - i.e "\\?" - return AZStd::next(entryBeginIter, 3); - } - } - - if (path.size() >= 3 && Internal::IsSeparator(path[1]) && !Internal::IsSeparator(path[2])) - { - // Find the next path separator for network paths that have the form of \\server\share - constexpr AZStd::string_view PathSeparators = { "/\\" }; - size_t nextPathSeparatorOffset = path.find_first_of(PathSeparators, 3); - return AZStd::next(entryBeginIter, nextPathSeparatorOffset != AZStd::string_view::npos ? nextPathSeparatorOffset : path.size()); - } - - return entryBeginIter; - } - } - - //! Returns an iterator past the end of the consumed path separator(s) - template - constexpr InputIt ConsumeSeparator(InputIt entryBeginIter, InputIt entryEndIter) noexcept - { - return AZStd::find_if_not(entryBeginIter, entryEndIter, [](const char elem) { return Internal::IsSeparator(elem); }); - } - - //! Returns an iterator past the end of the consumed filename - template - constexpr InputIt ConsumeName(InputIt entryBeginIter, InputIt entryEndIter) noexcept - { - return AZStd::find_if(entryBeginIter, entryEndIter, [](const char elem) { return Internal::IsSeparator(elem); }); - } - - //! Check if a path is absolute on a OS basis - //! If the preferred separator is '/' just checks if the path starts with a '/ - //! Otherwise a check for a Windows absolute path occurs - //! Windows absolute paths can include a RootName - template >> - static constexpr bool IsAbsolute(InputIt first, EndIt last, const char preferredSeparator) - { - // If the preferred separator is a forward slash - // than an absolute path is simply one that starts with a forward slash, - // unless we're on a posix platform that uses a custom path root separator - if (preferredSeparator == PosixPathSeparator) - { - #if defined(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) - const AZStd::string_view path{ first, last }; - return path.find(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) != AZStd::string_view::npos; - #else - const size_t pathSize = AZStd::distance(first, last); - return pathSize > 0 && IsSeparator(*first); - #endif - } - else - { - if (Internal::HasDrivePrefix(first, last)) - { - // If a windows path ends starts with C:foo it is a root relative path - // A path is absolute root absolute on windows if it starts with - const size_t pathSize = AZStd::distance(first, last); - return pathSize > 2 && Internal::IsSeparator(*AZStd::next(first, 2)); - } - - return first != ConsumeRootName(first, last, preferredSeparator); - } - } - static constexpr bool IsAbsolute(AZStd::string_view pathView, const char preferredSeparator) - { - // Uses the template preferred to branch on the absolute path check - // logic - return IsAbsolute(pathView.begin(), pathView.end(), preferredSeparator); - } - - // Compares path segments using either Posix or Windows path rules based on the path separator in use - // Posix paths perform a case-sensitive comparison, while Windows paths perform a case-insensitive comparison - static int ComparePathSegment(AZStd::string_view left, AZStd::string_view right, char pathSeparator) - { - const size_t maxCharsToCompare = (AZStd::min)(left.size(), right.size()); - - int charCompareResult = pathSeparator == PosixPathSeparator - ? strncmp(left.data(), right.data(), maxCharsToCompare) - : azstrnicmp(left.data(), right.data(), maxCharsToCompare); - return charCompareResult == 0 - ? static_cast(aznumeric_cast(left.size()) - aznumeric_cast(right.size())) - : charCompareResult; - } -} - -//! PathParser implementation -//! For internal use only -namespace AZ::IO::parser -{ - using parser_path_type = PathView; - using string_view_pair = AZStd::pair; - using PosPtr = const typename parser_path_type::value_type*; - - enum ParserState : uint8_t - { - // Zero is a special sentinel value used by default constructed iterators. - PS_BeforeBegin = PathIterator::BeforeBegin, - PS_InRootName = PathIterator::InRootName, - PS_InRootDir = PathIterator::InRootDir, - PS_InFilenames = PathIterator::InFilenames, - PS_AtEnd = PathIterator::AtEnd - }; - - struct PathParser - { - AZStd::string_view m_path_view; - AZStd::string_view m_path_raw_entry; - ParserState m_parser_state{}; - const char m_preferred_separator{ AZ_TRAIT_OS_PATH_SEPARATOR }; - - constexpr PathParser(AZStd::string_view path, ParserState state, const char preferredSeparator) noexcept - : m_path_view(path) - , m_parser_state(state) - , m_preferred_separator(preferredSeparator) - { - } - - constexpr PathParser(AZStd::string_view path, AZStd::string_view entry, ParserState state, const char preferredSeparator) noexcept - : m_path_view(path) - , m_path_raw_entry(entry) - , m_parser_state(static_cast(state)) - , m_preferred_separator(preferredSeparator) - { - } - - constexpr static PathParser CreateBegin(AZStd::string_view path, const char preferredSeparator) noexcept - { - PathParser pathParser(path, PS_BeforeBegin, preferredSeparator); - pathParser.Increment(); - return pathParser; - } - - constexpr static PathParser CreateEnd(AZStd::string_view path, const char preferredSeparator) noexcept - { - PathParser pathParser(path, PS_AtEnd, preferredSeparator); - return pathParser; - } - - constexpr PosPtr Peek() const noexcept - { - auto tokenEnd = getNextTokenStartPos(); - auto End = m_path_view.end(); - return tokenEnd == End ? nullptr : tokenEnd; - } - - constexpr void Increment() noexcept - { - const PosPtr pathEnd = m_path_view.end(); - const PosPtr currentPathEntry = getNextTokenStartPos(); - if (currentPathEntry == pathEnd) - { - return MakeState(PS_AtEnd); - } - - switch (m_parser_state) - { - case PS_BeforeBegin: - { - /* - * First the determine if the path contains only a root-name such as "C:" or is a filename such as "foo" - * root-relative path(Windows only) - C:foo - * root-absolute path - C:\foo - * root-absolute path - /foo - * relative path - foo - * - * Try to consume the root-name then the root directory to determine if path entry - * being parsed is a root-name or filename - * The State transitions from BeforeBegin are - * "C:", "\\server\", "\\?\", "\??\", "\\.\" -> Root Name - * "/", "\" -> Root Directory - * "path/foo", "foo" -> Filename - */ - auto rootNameEnd = Internal::ConsumeRootName(currentPathEntry, pathEnd, m_preferred_separator); - if (currentPathEntry != rootNameEnd) - { - // Transition to the Root Name state - return MakeState(PS_InRootName, currentPathEntry, rootNameEnd); - } - [[fallthrough]]; - } - case PS_InRootName: - { - auto rootDirEnd = Internal::ConsumeSeparator(currentPathEntry, pathEnd); - if (currentPathEntry != rootDirEnd) - { - // Transition to Root Directory state - return MakeState(PS_InRootDir, currentPathEntry, rootDirEnd); - } - [[fallthrough]]; - } - case PS_InRootDir: - { - auto filenameEnd = Internal::ConsumeName(currentPathEntry, pathEnd); - if (currentPathEntry != filenameEnd) - { - return MakeState(PS_InFilenames, currentPathEntry, filenameEnd); - } - [[fallthrough]]; - } - case PS_InFilenames: - { - auto separatorEnd = Internal::ConsumeSeparator(currentPathEntry, pathEnd); - if (separatorEnd != pathEnd) - { - // find the end of the current filename entry - auto filenameEnd = Internal::ConsumeName(separatorEnd, pathEnd); - return MakeState(PS_InFilenames, separatorEnd, filenameEnd); - } - // If after consuming the separator that path entry is at the end iterator - // move the path state to AtEnd - return MakeState(PS_AtEnd); - } - case PS_AtEnd: - AZ_Assert(false, "Path Parser cannot be incremented when it is in the AtEnd state"); - } - } - - constexpr void Decrement() noexcept - { - auto pathStart = m_path_view.begin(); - auto currentPathEntry = getCurrentTokenStartPos(); - - if (currentPathEntry == pathStart) - { - // we're decrementing the begin - return MakeState(PS_BeforeBegin); - } - switch (m_parser_state) - { - case PS_AtEnd: - { - /* - * First the determine if the path contains only a root-name such as "C:" or is a filename such as "foo" - * root-relative path(Windows only) - C:foo - * root-absolute path - C:\foo - * root-absolute path - /foo - * relative path - foo - * Try to consume the root-name then the root directory to determine if path entry - * being parsed is a root-name or filename - * The State transitions from AtEnd are - * "/path/foo/", "foo/", "C:foo\", "C:\foo\" -> Trailing Separator - * "/path/foo", "foo", "C:foo", "C:\foo" -> Filename - * "/", "C:\" or "\\server\" -> Root Directory - * "C:", "\\server", "\\?", "\??", "\\." -> Root Name - */ - auto rootNameEnd = Internal::ConsumeRootName(pathStart, currentPathEntry, m_preferred_separator); - if (pathStart != rootNameEnd && currentPathEntry == rootNameEnd) - { - // Transition to the Root Name state - return MakeState(PS_InRootName, pathStart, currentPathEntry); - } - - auto rootDirEnd = Internal::ConsumeSeparator(rootNameEnd, currentPathEntry); - if (rootNameEnd != rootDirEnd && currentPathEntry == rootDirEnd) - { - // Transition to Root Directory state - return MakeState(PS_InRootDir, rootNameEnd, currentPathEntry); - } - - auto filenameEnd = currentPathEntry; - if (Internal::IsSeparator(*(filenameEnd - 1))) - { - // The last character a path separator that isn't root directory - // consume all the preceding path separators - filenameEnd = Internal::ConsumeSeparator(AZStd::make_reverse_iterator(filenameEnd), - AZStd::make_reverse_iterator(rootDirEnd)).base(); - } - - // The previous state will be Filename, so the beginning of the filename is searched found - auto filenameBegin = Internal::ConsumeName(AZStd::make_reverse_iterator(filenameEnd), - AZStd::make_reverse_iterator(rootDirEnd)).base(); - return MakeState(PS_InFilenames, filenameBegin, filenameEnd); - } - case PS_InFilenames: - { - /* The State transitions from Filename are - * "/path/foo" -> Filename - * ^ - * "C:\foo" -> Root Directory - * ^ - * "C:foo" -> Root Name - * ^ - * "foo" -> This case has been taken care of by the current path entry != path start check - * ^ - */ - auto rootNameEnd = Internal::ConsumeRootName(pathStart, currentPathEntry, m_preferred_separator); - if (pathStart != rootNameEnd && currentPathEntry == rootNameEnd) - { - // Transition to the Root Name state - return MakeState(PS_InRootName, pathStart, rootNameEnd); - } - - auto rootDirEnd = Internal::ConsumeSeparator(rootNameEnd, currentPathEntry); - if (rootNameEnd != rootDirEnd && currentPathEntry == rootDirEnd) - { - // Transition to Root Directory state - return MakeState(PS_InRootDir, rootNameEnd, rootDirEnd); - } - // The previous state will be Filename again, so first the end of that filename is found - // proceeded by finding the beginning of that filename - auto filenameEnd = Internal::ConsumeSeparator(AZStd::make_reverse_iterator(currentPathEntry), - AZStd::make_reverse_iterator(rootDirEnd)).base(); - auto filenameBegin = Internal::ConsumeName(AZStd::make_reverse_iterator(filenameEnd), - AZStd::make_reverse_iterator(rootDirEnd)).base(); - return MakeState(PS_InFilenames, filenameBegin, filenameEnd); - } - case PS_InRootDir: - { - /* The State transitions from Root Directory are - * "C:\" "\\server\", "\\?\", "\??\", "\\.\" -> Root Name - * ^ ^ ^ ^ ^ - * "/" -> This case has been taken care of by the current path entry != path start check - * ^ - */ - return MakeState(PS_InRootName, pathStart, currentPathEntry); - } - case PS_InRootName: - // The only valid state transition from Root Name is BeforeBegin - return MakeState(PS_BeforeBegin); - case PS_BeforeBegin: - AZ_Assert(false, "Path Parser cannot be decremented when it is in the BeforeBegin State"); - } - } - - //! Return a view of the current element in the path processor state - constexpr AZStd::string_view operator*() const noexcept - { - switch (m_parser_state) - { - case PS_BeforeBegin: - [[fallthrough]]; - case PS_AtEnd: - [[fallthrough]]; - case PS_InRootDir: - return m_preferred_separator == '/' ? "/" : "\\"; - case PS_InRootName: - case PS_InFilenames: - return m_path_raw_entry; - default: - AZ_Assert(false, "Path Parser is in an invalid state"); - } - return {}; - } - - constexpr explicit operator bool() const noexcept - { - return m_parser_state != PS_BeforeBegin && m_parser_state != PS_AtEnd; - } - - constexpr PathParser& operator++() noexcept - { - Increment(); - return *this; - } - - constexpr PathParser& operator--() noexcept - { - Decrement(); - return *this; - } - - constexpr bool AtEnd() const noexcept - { - return m_parser_state == PS_AtEnd; - } - - constexpr bool InRootDir() const noexcept - { - return m_parser_state == PS_InRootDir; - } - - constexpr bool InRootName() const noexcept - { - return m_parser_state == PS_InRootName; - } - - constexpr bool InRootPath() const noexcept - { - return InRootName() || InRootDir(); - } - - private: - constexpr void MakeState(ParserState newState, typename AZStd::string_view::iterator start, typename AZStd::string_view::iterator end) noexcept - { - m_parser_state = newState; - m_path_raw_entry = AZStd::string_view(start, end); - } - constexpr void MakeState(ParserState newState) noexcept - { - m_parser_state = newState; - m_path_raw_entry = {}; - } - - //! Return a pointer to the first character after the currently lexed element. - constexpr typename AZStd::string_view::iterator getNextTokenStartPos() const noexcept - { - switch (m_parser_state) - { - case PS_BeforeBegin: - return m_path_view.begin(); - case PS_InRootName: - case PS_InRootDir: - case PS_InFilenames: - return m_path_raw_entry.end(); - case PS_AtEnd: - return m_path_view.end(); - default: - AZ_Assert(false, "Path Parser is in an invalid state"); - } - return m_path_view.end(); - } - - //! Return a pointer to the first character in the currently lexed element. - constexpr typename AZStd::string_view::iterator getCurrentTokenStartPos() const noexcept - { - switch (m_parser_state) - { - case PS_BeforeBegin: - case PS_InRootName: - return m_path_view.begin(); - case PS_InRootDir: - case PS_InFilenames: - return m_path_raw_entry.begin(); - case PS_AtEnd: - return m_path_view.end(); - default: - AZ_Assert(false, "Path Parser is in an invalid state"); - } - return m_path_view.end(); - } - }; - - constexpr string_view_pair SeparateFilename(const AZStd::string_view& srcView) - { - if (srcView == "." || srcView == ".." || srcView.empty()) - { - return string_view_pair{ srcView, "" }; - } - auto pos = srcView.find_last_of('.'); - if (pos == AZStd::string_view::npos || pos == 0) - { - return string_view_pair{ srcView, AZStd::string_view{} }; - } - return string_view_pair{ srcView.substr(0, pos), srcView.substr(pos) }; - } - - - // path part consumption - constexpr bool ConsumeRootName(PathParser* pathParser) - { - static_assert(PS_BeforeBegin == 1 && PS_InRootName == 2, - "PathParser must be in state before begin or in the root name in order to consume the root name"); - while (pathParser->m_parser_state <= PS_InRootName) - { - ++(*pathParser); - } - return pathParser->m_parser_state == PS_AtEnd; - } - constexpr bool ConsumeRootDir(PathParser* pathParser) - { - static_assert(PS_BeforeBegin == 1 && PS_InRootName == 2 && PS_InRootDir == 3, - "PathParser must be in state before begin, in the root name or in the root directory in order to consume the root directory"); - while (pathParser->m_parser_state <= PS_InRootDir) - { - ++(*pathParser); - } - return pathParser->m_parser_state == PS_AtEnd; - } - - // path.comparisons - constexpr int CompareRootName(PathParser* lhsPathParser, PathParser* rhsPathParser) - { - if (!lhsPathParser->InRootName() && !rhsPathParser->InRootName()) - { - return 0; - } - - auto GetRootName = [](PathParser* pathParser) constexpr -> AZStd::string_view - { - return pathParser->InRootName() ? **pathParser : ""; - }; - int res = Internal::ComparePathSegment(GetRootName(lhsPathParser), GetRootName(rhsPathParser), lhsPathParser->m_preferred_separator); - ConsumeRootName(lhsPathParser); - ConsumeRootName(rhsPathParser); - return res; - } - constexpr int CompareRootDir(PathParser* lhsPathParser, PathParser* rhsPathParser) - { - if (!lhsPathParser->InRootDir() && rhsPathParser->InRootDir()) - { - return -1; - } - else if (lhsPathParser->InRootDir() && !rhsPathParser->InRootDir()) - { - return 1; - } - else - { - ConsumeRootDir(lhsPathParser); - ConsumeRootDir(rhsPathParser); - return 0; - } - } - constexpr int CompareRelative(PathParser* lhsPathParserPtr, PathParser* rhsPathParserPtr) - { - auto& lhsPathParser = *lhsPathParserPtr; - auto& rhsPathParser = *rhsPathParserPtr; - - while (lhsPathParser && rhsPathParser) - { - if (int res = Internal::ComparePathSegment(*lhsPathParser, *rhsPathParser, lhsPathParser.m_preferred_separator); - res != 0) - { - return res; - } - ++lhsPathParser; - ++rhsPathParser; - } - return 0; - } - constexpr int CompareEndState(PathParser* lhsPathParser, PathParser* rhsPathParser) - { - if (lhsPathParser->AtEnd() && !rhsPathParser->AtEnd()) - { - return -1; - } - else if (!lhsPathParser->AtEnd() && rhsPathParser->AtEnd()) - { - return 1; - } - return 0; - } - - enum class PathPartKind : uint8_t - { - PK_None, - PK_RootName, - PK_RootSep, - PK_Filename, - PK_Dot, - PK_DotDot, - }; - - constexpr PathPartKind ClassifyPathPart(const PathParser& parser) - { - // Check each parser state to determine the PathPartKind - if (parser.m_parser_state == PS_InRootDir) - { - return PathPartKind::PK_RootSep; - } - if (parser.m_parser_state == PS_InRootName) - { - return PathPartKind::PK_RootName; - } - - // Fallback to checking parser pathEntry view value - // to determine if the special "." or ".." values are being used - AZStd::string_view pathPart = *parser; - if (pathPart == ".") - { - return PathPartKind::PK_Dot; - } - if (pathPart == "..") - { - return PathPartKind::PK_DotDot; - } - - // Return PathPartKind of Filename if the parser state doesn't match - // the states of InRootDir or InRootName and the filename - // isn't made up of the special directory values of "." and ".." - return PathPartKind::PK_Filename; - } - - constexpr int DetermineLexicalElementCount(PathParser pathParser) - { - int count = 0; - for (; pathParser; ++pathParser) - { - auto pathElement = *pathParser; - if (pathElement == "..") - { - --count; - } - else if (pathElement != "." && pathElement != "") - { - ++count; - } - } - return count; - } -} //! PathView implementation namespace AZ::IO @@ -1183,7 +487,8 @@ namespace AZ::IO }; if (pathParser.InRootName() && pathParserBase.InRootName()) { - if (*pathParser != *pathParserBase) + if (int res = Internal::ComparePathSegment(*pathParser, *pathParserBase, pathParser.m_preferred_separator); + res != 0) { pathResult.m_path = AZStd::string_view{}; return; @@ -1213,7 +518,8 @@ namespace AZ::IO // Find the first mismatching element auto pathParser = parser::PathParser::CreateBegin(path.m_path, path.m_preferred_separator); auto pathParserBase = parser::PathParser::CreateBegin(base.m_path, base.m_preferred_separator); - while (pathParser && pathParserBase && pathParser.m_parser_state == pathParserBase.m_parser_state && *pathParser == *pathParserBase) + while (pathParser && pathParserBase && pathParser.m_parser_state == pathParserBase.m_parser_state && + Internal::ComparePathSegment(*pathParser, *pathParserBase, pathParser.m_preferred_separator) == 0) { ++pathParser; ++pathParserBase; @@ -1255,66 +561,92 @@ namespace AZ::IO } } - template - constexpr void PathView::LexicallyNormalInplace(PathResultType& pathResult, const AZ::IO::PathView& path) + constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView& path) noexcept -> void { if (path.m_path.empty()) { - pathResult = path; return; } - using PartKindPair = AZStd::pair; - // Max number of path parts supported when normalizing a path - constexpr size_t MaxPathParts = 64; - AZStd::array pathParts{}; - size_t currentPartSize = 0; - - // Track the total size of the parts as we collect them. This allows the - // resulting path to reserve the correct amount of memory. - size_t newPathSize = 0; - auto AddPart = [&newPathSize, &pathParts, ¤tPartSize](parser::PathPartKind pathKind, AZStd::string_view parserPathPart) constexpr - { - newPathSize += parserPathPart.size(); - pathParts[currentPartSize++] = { parserPathPart, pathKind }; - }; - auto LastPartKind = [&pathParts, ¤tPartSize]() constexpr - { - if (currentPartSize == 0) - { - return parser::PathPartKind::PK_None; - } - return pathParts[currentPartSize - 1].second; - }; - // Build a stack containing the remaining elements of the path, popping off // elements which occur before a '..' entry. for (auto pathParser = parser::PathParser::CreateBegin(path.m_path, path.m_preferred_separator); pathParser; ++pathParser) { - parser::PathPartKind Kind = parser::ClassifyPathPart(pathParser); - switch (Kind) + switch (const parser::PathPartKind Kind = parser::ClassifyPathPart(pathParser); Kind) { case parser::PathPartKind::PK_RootName: - case parser::PathPartKind::PK_Filename: - [[fallthrough]]; + { + // Root Name normalization is a bit tricky. + // A path of C:/foo/C:bar = C:/foo/bar and a path of C:foo/C:bar = C:foo/bar + // A path of C:/foo/C: = C:/foo + // A path of C:/foo/C:/bar = C:/bar + // Also a path of C:/foo/C: = C:/foo, but C:/foo/C:/ = C:/ + // A path of C:foo/D:bar = D:bar + // The pathIterable only stores the Root Name at the front + if (const auto [firstPartView, firstPartKind] = !pathIterable.empty() ? pathIterable.front() : PathIterable::PartKindPair{}; + firstPartKind != parser::PathPartKind::PK_RootName || firstPartView != *pathParser) + { + // The root name has changed or this is the first time a root name has been seen, + // discard the accumulated path parts + pathIterable.clear(); + pathIterable.emplace_back(*pathParser, Kind); + } + break; + } case parser::PathPartKind::PK_RootSep: { - // Add all non-dot and non-dot-dot elements to the stack of elements. - AddPart(Kind, *pathParser); + // If a root directory has been found, discard the accumulated path parts so far + // but not before storing of the first path part in case it is a Root Name + const auto [firstPartView, firstPartKind] = !pathIterable.empty() ? pathIterable.front() : PathIterable::PartKindPair{}; + pathIterable.clear(); + + if (firstPartKind == parser::PathPartKind::PK_RootName) + { + pathIterable.emplace_back(firstPartView, firstPartKind); + } + pathIterable.emplace_back(*pathParser, Kind); + break; + } + case parser::PathPartKind::PK_Filename: + { + // Special Case: The "filename" starts with a root name + // i.e D:/foo/C:/baz + // ^ + // The result should be C:/baz + // In this case restart path parsing at this element into the same PathIterable + // using tail recursion + AZStd::string_view filenameView{ *pathParser }; + if (auto filenameParser = parser::PathParser::CreateBegin(filenameView, pathParser.m_preferred_separator); + filenameParser && parser::ClassifyPathPart(filenameParser) == parser::PathPartKind::PK_RootName) + { + AZ::IO::PathView fileNamePath{ AZStd::string_view{ filenameView.begin(), path.m_path.end() }, + pathParser.m_preferred_separator }; + AppendNormalPathParts(pathIterable, fileNamePath); + return; + } + else + { + // Normal Case: The "filename" does not start with a root name + // Add all non-dot and non-dot-dot elements to the stack of elements. + pathIterable.emplace_back(*pathParser, Kind); + } break; } case parser::PathPartKind::PK_DotDot: { // Only push a ".." element if there are no elements preceding the "..", // or if the preceding element is itself "..". - auto lastPartKind = LastPartKind(); - if (lastPartKind == parser::PathPartKind::PK_Filename) + + if (const auto lastPartKind = !pathIterable.empty() ? pathIterable.back().second : parser::PathPartKind::PK_None; + lastPartKind == parser::PathPartKind::PK_Filename) { - newPathSize -= pathParts[--currentPartSize].first.size(); + // Due to the previous path part being a filename, the and the ".." cancels each other + // So remove the filename from the normalized path + pathIterable.pop_back(); } else if (lastPartKind != parser::PathPartKind::PK_RootSep) { - AddPart(parser::PathPartKind::PK_DotDot, ".."); + pathIterable.emplace_back("..", parser::PathPartKind::PK_DotDot); } break; } @@ -1324,21 +656,13 @@ namespace AZ::IO AZ_Assert(false, "Path Parser is in an invalid state"); } } - //! If the path is empty, add a dot. - if (currentPartSize == 0) - { - pathResult.m_path = AZStd::string_view{ "." }; - return; - } - - pathResult = PathResultType(path.m_preferred_separator); - pathResult.m_path.reserve(currentPartSize + newPathSize); - for (size_t partIndex = 0; partIndex < currentPartSize; ++partIndex) - { - auto& pathPart = pathParts[partIndex]; - pathResult /= pathPart.first; - } + } + constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView& path) noexcept -> PathIterable + { + PathIterable pathIterable; + AppendNormalPathParts(pathIterable, path); + return pathIterable; } } @@ -1573,12 +897,22 @@ namespace AZ::IO // Check if the other path has a root name and // that the root name doesn't match the current path root name // The scenario where this would occur was if the current path object had a path of - // "C:"foo and the other path object had a path "F:bar". + // "C:foo" and the other path object had a path "F:bar". // As the root names are different the other path replaces current path in it's entirety auto postRootNameIter = Internal::ConsumeRootName(m_path.begin(), m_path.end(), m_preferred_separator); auto otherPostRootNameIter = Internal::ConsumeRootName(first, last, m_preferred_separator); AZStd::string_view rootNameView{ m_path.begin(), postRootNameIter }; - if (first != otherPostRootNameIter && !AZStd::equal(rootNameView.begin(), rootNameView.end(), first, otherPostRootNameIter)) + + // The RootName can only ever be two characters long which is ":" + auto ToLower = [](const char element) constexpr -> char + { + return element >= 'A' && element <= 'Z' ? (element - 'A') + 'a' : element; + }; + auto compareRootName = [ToLower = AZStd::move(ToLower), path_separator = m_preferred_separator](const char lhs, const char rhs) constexpr + { + return path_separator == PosixPathSeparator ? lhs == rhs : ToLower(lhs) == ToLower(rhs); + }; + if (first != otherPostRootNameIter && !AZStd::equal(rootNameView.begin(), rootNameView.end(), first, otherPostRootNameIter, compareRootName)) { m_path.assign(first, last); return *this; @@ -1708,31 +1042,36 @@ namespace AZ::IO // native format observers template - constexpr auto BasicPath::Native() const noexcept -> const string_type& + constexpr auto BasicPath::Native() const & noexcept -> const string_type& { return m_path; } + template + constexpr auto BasicPath::Native() const && noexcept -> const string_type&& + { + return AZStd::move(m_path); + } template - constexpr auto BasicPath::Native() noexcept -> string_type& + constexpr auto BasicPath::Native() & noexcept -> string_type& { return m_path; } template - constexpr auto BasicPath::c_str() const noexcept -> const value_type* + constexpr auto BasicPath::Native() && noexcept -> string_type&& { - return m_path.c_str(); + return AZStd::move(m_path); } template - constexpr BasicPath::operator string_type() const + constexpr auto BasicPath::c_str() const noexcept -> const value_type* { - return m_path; + return m_path.c_str(); } template - constexpr BasicPath::operator string_type&() noexcept + constexpr BasicPath::operator string_type() const { return m_path; } @@ -1887,15 +1226,19 @@ namespace AZ::IO template constexpr auto BasicPath::LexicallyNormal() const -> BasicPath { - BasicPath pathResult; - static_cast(*this).LexicallyNormalInplace(pathResult, *this); + BasicPath pathResult(m_preferred_separator); + PathView::PathIterable pathIterable = PathView::GetNormalPathParts(*this); + for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable) + { + pathResult /= pathPartView; + } return pathResult; } template constexpr auto BasicPath::LexicallyRelative(const PathView& base) const -> BasicPath { - BasicPath pathResult; + BasicPath pathResult(m_preferred_separator); static_cast(*this).MakeRelativeTo(pathResult, *this, base); return pathResult; } @@ -1984,20 +1327,45 @@ namespace AZ::IO { [[nodiscard]] constexpr bool PathView::IsRelativeTo(const PathView& base) const { - auto relativePath = LexicallyRelative(base); - return !relativePath.empty() && !relativePath.Native().starts_with(".."); + // PathView::LexicallyRelative is not being used as it returns a FixedMaxPath + // which has a limitation that it requires the relative path to fit within + // an AZ::IO::MaxPathLength buffer + auto ComparePathPart = [pathSeparator = m_preferred_separator]( + const PathIterable::PartKindPair& left, const PathIterable::PartKindPair& right) -> bool + { + return Internal::ComparePathSegment(left.first, right.first, pathSeparator) == 0; + }; + + const PathIterable thisPathParts = GetNormalPathParts(*this); + const PathIterable basePathParts = GetNormalPathParts(base); + [[maybe_unused]] auto [thisPathIter, basePathIter] = AZStd::mismatch(thisPathParts.begin(), thisPathParts.end(), + basePathParts.begin(), basePathParts.end(), ComparePathPart); + // Check if the entire base path has been consumed. If not, *this path cannot be relative to it + if (basePathIter != basePathParts.end()) + { + return false; + } + + // If the base path isn't empty and has been fully consumed, then *this path is relative + // Also if the base path is empty, then any relative path is relative to an empty path('.') + return !basePathParts.empty() || !thisPathParts.IsAbsolute(); } constexpr FixedMaxPath PathView::LexicallyNormal() const { - FixedMaxPath pathResult; - LexicallyNormalInplace(pathResult, *this); + FixedMaxPath pathResult(m_preferred_separator); + PathIterable pathIterable = GetNormalPathParts(*this); + for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable) + { + pathResult /= pathPartView; + } + return pathResult; } constexpr FixedMaxPath PathView::LexicallyRelative(const PathView& base) const { - FixedMaxPath pathResult; + FixedMaxPath pathResult(m_preferred_separator); MakeRelativeTo(pathResult, *this, base); return pathResult; } diff --git a/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl b/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl new file mode 100644 index 0000000000..a1faa29b31 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl @@ -0,0 +1,162 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ::IO +{ + struct PathView::PathIterable + { + inline static constexpr size_t MaxPathParts = 64; + using PartKindPair = AZStd::pair; + using PartKindArray = AZStd::array; + constexpr PathIterable() = default; + + [[nodiscard]] constexpr bool empty() const noexcept; + constexpr auto size() const noexcept-> size_t; + constexpr auto begin() noexcept-> PartKindArray::iterator; + constexpr auto begin() const noexcept -> PartKindArray::const_iterator; + constexpr auto cbegin() const noexcept -> PartKindArray::const_iterator; + constexpr auto end() noexcept -> PartKindArray::iterator; + constexpr auto end() const noexcept -> PartKindArray::const_iterator; + constexpr auto cend() const noexcept -> PartKindArray::const_iterator; + constexpr auto rbegin() noexcept -> PartKindArray::reverse_iterator; + constexpr auto rbegin() const noexcept -> PartKindArray::const_reverse_iterator; + constexpr auto crbegin() const noexcept -> PartKindArray::const_reverse_iterator; + constexpr auto rend() noexcept -> PartKindArray::reverse_iterator; + constexpr auto rend() const noexcept -> PartKindArray::const_reverse_iterator; + constexpr auto crend() const noexcept -> PartKindArray::const_reverse_iterator; + + [[nodiscard]] constexpr bool IsAbsolute() const noexcept; + + private: + template + constexpr PartKindPair& emplace_back(Args&&... args) noexcept; + constexpr void pop_back() noexcept; + constexpr const PartKindPair& back() const noexcept; + constexpr PartKindPair& back() noexcept; + + constexpr const PartKindPair& front() const noexcept; + constexpr PartKindPair& front() noexcept; + + constexpr void clear() noexcept; + + friend constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView&) noexcept -> PathIterable; + friend constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView&) noexcept -> void; + PartKindArray m_parts{}; + size_t m_size{}; + }; + + // public + [[nodiscard]] constexpr auto PathView::PathIterable::empty() const noexcept -> bool + { + return m_size == 0; + } + constexpr auto PathView::PathIterable::size() const noexcept -> size_t + { + return m_size; + } + + constexpr auto PathView::PathIterable::begin() noexcept -> PartKindArray::iterator + { + return m_parts.begin(); + } + constexpr auto PathView::PathIterable::begin() const noexcept -> PartKindArray::const_iterator + { + return m_parts.begin(); + } + constexpr auto PathView::PathIterable::cbegin() const noexcept -> PartKindArray::const_iterator + { + return begin(); + } + constexpr auto PathView::PathIterable::end() noexcept -> PartKindArray::iterator + { + return begin() + size(); + } + constexpr auto PathView::PathIterable::end() const noexcept -> PartKindArray::const_iterator + { + return begin() + size(); + } + constexpr auto PathView::PathIterable::cend() const noexcept -> PartKindArray::const_iterator + { + return end(); + } + constexpr auto PathView::PathIterable::rbegin() noexcept -> PartKindArray::reverse_iterator + { + return PartKindArray::reverse_iterator(begin() + size()); + } + constexpr auto PathView::PathIterable::rbegin() const noexcept -> PartKindArray::const_reverse_iterator + { + return PartKindArray::const_reverse_iterator(begin() + size()); + } + constexpr auto PathView::PathIterable::crbegin() const noexcept -> PartKindArray::const_reverse_iterator + { + return rbegin(); + } + constexpr auto PathView::PathIterable::rend() noexcept -> PartKindArray::reverse_iterator + { + return PartKindArray::reverse_iterator(begin()); + } + constexpr auto PathView::PathIterable::rend() const noexcept -> PartKindArray::const_reverse_iterator + { + return PartKindArray::const_reverse_iterator(begin()); + } + constexpr auto PathView::PathIterable::crend() const noexcept -> PartKindArray::const_reverse_iterator + { + return rend(); + } + + [[nodiscard]] constexpr auto PathView::PathIterable::IsAbsolute() const noexcept -> bool + { + return !empty() && (front().second == parser::PathPartKind::PK_RootSep + || (size() > 1 && front().second == parser::PathPartKind::PK_RootName && m_parts[1].second == parser::PathPartKind::PK_RootSep)); + } + + // private + template + constexpr auto PathView::PathIterable::emplace_back(Args&&... args) noexcept -> PartKindPair& + { + AZ_Assert(m_size < MaxPathParts, "PathIterable cannot be made out of a path with more than %zu parts", MaxPathParts); + m_parts[m_size++] = PartKindPair{ AZStd::forward(args)... }; + return back(); + } + constexpr auto PathView::PathIterable::pop_back() noexcept -> void + { + AZ_Assert(m_size > 0, "Cannot pop_back() from a PathIterable with 0 parts"); + --m_size; + } + constexpr auto PathView::PathIterable::back() const noexcept -> const PartKindPair& + { + AZ_Assert(!empty(), "back() was invoked on PathIterable with 0 parts"); + return m_parts[m_size - 1]; + } + constexpr auto PathView::PathIterable::back() noexcept -> PartKindPair& + { + AZ_Assert(!empty(), "back() was invoked on PathIterable with 0 parts"); + return m_parts[m_size - 1]; + } + + constexpr auto PathView::PathIterable::front() const noexcept -> const PartKindPair& + { + AZ_Assert(!empty(), "front() was invoked on PathIterable with 0 parts"); + return m_parts[0]; + } + constexpr auto PathView::PathIterable::front() noexcept -> PartKindPair& + { + AZ_Assert(!empty(), "front() was invoked on PathIterable with 0 parts"); + return m_parts[0]; + } + + constexpr auto PathView::PathIterable::clear() noexcept -> void + { + m_size = 0; + } +} diff --git a/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl b/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl new file mode 100644 index 0000000000..3ab2c4376c --- /dev/null +++ b/Code/Framework/AzCore/AzCore/IO/Path/PathParser.inl @@ -0,0 +1,706 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ::IO::Internal +{ + constexpr bool IsSeparator(const char elem) + { + return elem == '/' || elem == '\\'; + } + template >> + static constexpr bool HasDrivePrefix(InputIt first, EndIt last) + { + size_t prefixSize = AZStd::distance(first, last); + if (prefixSize < 2 || *AZStd::next(first, 1) != ':') + { + // Drive prefix must be at least two characters and have a colon for the second character + return false; + } + + constexpr size_t ValidDrivePrefixRange = 26; + // Uppercase the drive letter by bitwise and'ing out the the 2^5 bit + unsigned char driveLetter = static_cast(*first); + + driveLetter &= 0b1101'1111; + // normalize the character value in the range of A-Z -> 0-25 + driveLetter -= 'A'; + return driveLetter < ValidDrivePrefixRange; + } + + static constexpr bool HasDrivePrefix(AZStd::string_view prefix) + { + return HasDrivePrefix(prefix.begin(), prefix.end()); + } + + //! Returns an iterator past the end of the consumed root name + //! Windows root names can have include drive letter within them + template + constexpr auto ConsumeRootName(InputIt entryBeginIter, InputIt entryEndIter, const char preferredSeparator) + -> AZStd::enable_if_t, InputIt> + { + if (preferredSeparator == PosixPathSeparator) + { + // If the preferred separator is forward slash the parser is in posix path + // parsing mode, which doesn't have a root name, + // unless we're on a posix platform that uses a custom path root separator +#if defined(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) + const AZStd::string_view path{ entryBeginIter, entryEndIter }; + const auto positionOfPathSeparator = path.find(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR); + if (positionOfPathSeparator != AZStd::string_view::npos) + { + return AZStd::next(entryBeginIter, positionOfPathSeparator + 1); + } +#endif + return entryBeginIter; + } + else + { + // Information for GetRootName has been gathered from Microsoft header + // Below are examples of paths and what there root-name will return + // "/" - returns "" + // "foo/" - returns "" + // "C:DriveRelative" - returns "C:" + // "C:\\DriveAbsolute" - returns "C:" + // "C://DriveAbsolute" - returns "C:" + // "\\server\share" - returns "\\server" + // The following paths are based on the UNC specification to work with paths longer than the 260 character path limit + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#maximum-path-length-limitation + // \\?\device - returns "\\?" + // \??\device - returns "\??" + // \\.\device - returns "\\." + + + AZStd::string_view path{ entryBeginIter, entryEndIter }; + + if (path.size() < 2) + { + // A root name is either or a network path + // therefore it has a least two characters + return entryBeginIter; + } + + if (HasDrivePrefix(path)) + { + // If the path has a drive prefix, then it has a root name of + return AZStd::next(entryBeginIter, 2); + } + + if (!Internal::IsSeparator(path[0])) + { + // At this point all other root names start with a path separator + return entryBeginIter; + } + + // Check if the path has the form of "\\?\, "\??\" or "\\.\" + const bool pathInUncForm = path.size() >= 4 && Internal::IsSeparator(path[3]) + && (path.size() == 4 || !Internal::IsSeparator(path[4])); + if (pathInUncForm) + { + // \\?\<0 or more> or \\.\$ + const bool slashQuestionMark = Internal::IsSeparator(path[1]) && (path[2] == '?' || path[2] == '.'); + // \??\<0 or more> + const bool questionMarkTwice = path[1] == '?' && path[2] == '?'; + if (slashQuestionMark || questionMarkTwice) + { + // Return the root value root slash - i.e "\\?" + return AZStd::next(entryBeginIter, 3); + } + } + + if (path.size() >= 3 && Internal::IsSeparator(path[1]) && !Internal::IsSeparator(path[2])) + { + // Find the next path separator for network paths that have the form of \\server\share + constexpr AZStd::string_view PathSeparators = { "/\\" }; + size_t nextPathSeparatorOffset = path.find_first_of(PathSeparators, 3); + return AZStd::next(entryBeginIter, nextPathSeparatorOffset != AZStd::string_view::npos ? nextPathSeparatorOffset : path.size()); + } + + return entryBeginIter; + } + } + + //! Returns an iterator past the end of the consumed path separator(s) + template + constexpr InputIt ConsumeSeparator(InputIt entryBeginIter, InputIt entryEndIter) noexcept + { + return AZStd::find_if_not(entryBeginIter, entryEndIter, [](const char elem) { return Internal::IsSeparator(elem); }); + } + + //! Returns an iterator past the end of the consumed filename + template + constexpr InputIt ConsumeName(InputIt entryBeginIter, InputIt entryEndIter) noexcept + { + return AZStd::find_if(entryBeginIter, entryEndIter, [](const char elem) { return Internal::IsSeparator(elem); }); + } + + //! Check if a path is absolute on a OS basis + //! If the preferred separator is '/' just checks if the path starts with a '/ + //! Otherwise a check for a Windows absolute path occurs + //! Windows absolute paths can include a RootName + template >> + static constexpr bool IsAbsolute(InputIt first, EndIt last, const char preferredSeparator) + { + size_t pathSize = AZStd::distance(first, last); + + // If the preferred separator is a forward slash + // than an absolute path is simply one that starts with a forward slash, + // unless we're on a posix platform that uses a custom path root separator + if (preferredSeparator == PosixPathSeparator) + { +#if defined(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) + const AZStd::string_view path{ first, last }; + return path.find(AZ_TRAIT_CUSTOM_PATH_ROOT_SEPARATOR) != AZStd::string_view::npos; +#else + return pathSize > 0 && IsSeparator(*first); +#endif + } + else + { + if (Internal::HasDrivePrefix(first, last)) + { + // If a windows path ends starts with C:foo it is a root relative path + // A path is absolute root absolute on windows if it starts with + return pathSize > 2 && Internal::IsSeparator(*AZStd::next(first, 2)); + } + + return first != ConsumeRootName(first, last, preferredSeparator); + } + } + static constexpr bool IsAbsolute(AZStd::string_view pathView, const char preferredSeparator) + { + // Uses the template preferred to branch on the absolute path check + // logic + return IsAbsolute(pathView.begin(), pathView.end(), preferredSeparator); + } + + // Compares path segments using either Posix or Windows path rules based on the path separator in use + // Posix paths perform a case-sensitive comparison, while Windows paths perform a case-insensitive comparison + static int ComparePathSegment(AZStd::string_view left, AZStd::string_view right, char pathSeparator) + { + const size_t maxCharsToCompare = (AZStd::min)(left.size(), right.size()); + + int charCompareResult = pathSeparator == PosixPathSeparator + ? maxCharsToCompare ? strncmp(left.data(), right.data(), maxCharsToCompare) : 0 + : maxCharsToCompare ? azstrnicmp(left.data(), right.data(), maxCharsToCompare) : 0; + return charCompareResult == 0 + ? static_cast(aznumeric_cast(left.size()) - aznumeric_cast(right.size())) + : charCompareResult; + } +} + +//! PathParser implementation +//! For internal use only +namespace AZ::IO::parser +{ + using parser_path_type = PathView; + using string_view_pair = AZStd::pair; + using PosPtr = const typename parser_path_type::value_type*; + + enum ParserState : uint8_t + { + // Zero is a special sentinel value used by default constructed iterators. + PS_BeforeBegin = PathIterator::BeforeBegin, + PS_InRootName = PathIterator::InRootName, + PS_InRootDir = PathIterator::InRootDir, + PS_InFilenames = PathIterator::InFilenames, + PS_AtEnd = PathIterator::AtEnd + }; + + struct PathParser + { + AZStd::string_view m_path_view; + AZStd::string_view m_path_raw_entry; + ParserState m_parser_state{}; + const char m_preferred_separator{ AZ_TRAIT_OS_PATH_SEPARATOR }; + + constexpr PathParser(AZStd::string_view path, ParserState state, const char preferredSeparator) noexcept + : m_path_view(path) + , m_parser_state(state) + , m_preferred_separator(preferredSeparator) + { + } + + constexpr PathParser(AZStd::string_view path, AZStd::string_view entry, ParserState state, const char preferredSeparator) noexcept + : m_path_view(path) + , m_path_raw_entry(entry) + , m_parser_state(static_cast(state)) + , m_preferred_separator(preferredSeparator) + { + } + + constexpr static PathParser CreateBegin(AZStd::string_view path, const char preferredSeparator) noexcept + { + PathParser pathParser(path, PS_BeforeBegin, preferredSeparator); + pathParser.Increment(); + return pathParser; + } + + constexpr static PathParser CreateEnd(AZStd::string_view path, const char preferredSeparator) noexcept + { + PathParser pathParser(path, PS_AtEnd, preferredSeparator); + return pathParser; + } + + constexpr PosPtr Peek() const noexcept + { + auto tokenEnd = getNextTokenStartPos(); + auto End = m_path_view.end(); + return tokenEnd == End ? nullptr : tokenEnd; + } + + constexpr void Increment() noexcept + { + const PosPtr pathEnd = m_path_view.end(); + const PosPtr currentPathEntry = getNextTokenStartPos(); + if (currentPathEntry == pathEnd) + { + return MakeState(PS_AtEnd); + } + + switch (m_parser_state) + { + case PS_BeforeBegin: + { + /* + * First the determine if the path contains only a root-name such as "C:" or is a filename such as "foo" + * root-relative path(Windows only) - C:foo + * root-absolute path - C:\foo + * root-absolute path - /foo + * relative path - foo + * + * Try to consume the root-name then the root directory to determine if path entry + * being parsed is a root-name or filename + * The State transitions from BeforeBegin are + * "C:", "\\server\", "\\?\", "\??\", "\\.\" -> Root Name + * "/", "\" -> Root Directory + * "path/foo", "foo" -> Filename + */ + auto rootNameEnd = Internal::ConsumeRootName(currentPathEntry, pathEnd, m_preferred_separator); + if (currentPathEntry != rootNameEnd) + { + // Transition to the Root Name state + return MakeState(PS_InRootName, currentPathEntry, rootNameEnd); + } + [[fallthrough]]; + } + case PS_InRootName: + { + auto rootDirEnd = Internal::ConsumeSeparator(currentPathEntry, pathEnd); + if (currentPathEntry != rootDirEnd) + { + // Transition to Root Directory state + return MakeState(PS_InRootDir, currentPathEntry, rootDirEnd); + } + [[fallthrough]]; + } + case PS_InRootDir: + { + auto filenameEnd = Internal::ConsumeName(currentPathEntry, pathEnd); + if (currentPathEntry != filenameEnd) + { + return MakeState(PS_InFilenames, currentPathEntry, filenameEnd); + } + [[fallthrough]]; + } + case PS_InFilenames: + { + auto separatorEnd = Internal::ConsumeSeparator(currentPathEntry, pathEnd); + if (separatorEnd != pathEnd) + { + // find the end of the current filename entry + auto filenameEnd = Internal::ConsumeName(separatorEnd, pathEnd); + return MakeState(PS_InFilenames, separatorEnd, filenameEnd); + } + // If after consuming the separator that path entry is at the end iterator + // move the path state to AtEnd + return MakeState(PS_AtEnd); + } + case PS_AtEnd: + AZ_Assert(false, "Path Parser cannot be incremented when it is in the AtEnd state"); + } + } + + constexpr void Decrement() noexcept + { + auto pathStart = m_path_view.begin(); + auto currentPathEntry = getCurrentTokenStartPos(); + + if (currentPathEntry == pathStart) + { + // we're decrementing the begin + return MakeState(PS_BeforeBegin); + } + switch (m_parser_state) + { + case PS_AtEnd: + { + /* + * First the determine if the path contains only a root-name such as "C:" or is a filename such as "foo" + * root-relative path(Windows only) - C:foo + * root-absolute path - C:\foo + * root-absolute path - /foo + * relative path - foo + * Try to consume the root-name then the root directory to determine if path entry + * being parsed is a root-name or filename + * The State transitions from AtEnd are + * "/path/foo/", "foo/", "C:foo\", "C:\foo\" -> Trailing Separator + * "/path/foo", "foo", "C:foo", "C:\foo" -> Filename + * "/", "C:\" or "\\server\" -> Root Directory + * "C:", "\\server", "\\?", "\??", "\\." -> Root Name + */ + auto rootNameEnd = Internal::ConsumeRootName(pathStart, currentPathEntry, m_preferred_separator); + if (pathStart != rootNameEnd && currentPathEntry == rootNameEnd) + { + // Transition to the Root Name state + return MakeState(PS_InRootName, pathStart, currentPathEntry); + } + + auto rootDirEnd = Internal::ConsumeSeparator(rootNameEnd, currentPathEntry); + if (rootNameEnd != rootDirEnd && currentPathEntry == rootDirEnd) + { + // Transition to Root Directory state + return MakeState(PS_InRootDir, rootNameEnd, currentPathEntry); + } + + auto filenameEnd = currentPathEntry; + if (Internal::IsSeparator(*(filenameEnd - 1))) + { + // The last character a path separator that isn't root directory + // consume all the preceding path separators + filenameEnd = Internal::ConsumeSeparator(AZStd::make_reverse_iterator(filenameEnd), + AZStd::make_reverse_iterator(rootDirEnd)).base(); + } + + // The previous state will be Filename, so the beginning of the filename is searched found + auto filenameBegin = Internal::ConsumeName(AZStd::make_reverse_iterator(filenameEnd), + AZStd::make_reverse_iterator(rootDirEnd)).base(); + return MakeState(PS_InFilenames, filenameBegin, filenameEnd); + } + case PS_InFilenames: + { + /* The State transitions from Filename are + * "/path/foo" -> Filename + * ^ + * "C:\foo" -> Root Directory + * ^ + * "C:foo" -> Root Name + * ^ + * "foo" -> This case has been taken care of by the current path entry != path start check + * ^ + */ + auto rootNameEnd = Internal::ConsumeRootName(pathStart, currentPathEntry, m_preferred_separator); + if (pathStart != rootNameEnd && currentPathEntry == rootNameEnd) + { + // Transition to the Root Name state + return MakeState(PS_InRootName, pathStart, rootNameEnd); + } + + auto rootDirEnd = Internal::ConsumeSeparator(rootNameEnd, currentPathEntry); + if (rootNameEnd != rootDirEnd && currentPathEntry == rootDirEnd) + { + // Transition to Root Directory state + return MakeState(PS_InRootDir, rootNameEnd, rootDirEnd); + } + // The previous state will be Filename again, so first the end of that filename is found + // proceeded by finding the beginning of that filename + auto filenameEnd = Internal::ConsumeSeparator(AZStd::make_reverse_iterator(currentPathEntry), + AZStd::make_reverse_iterator(rootDirEnd)).base(); + auto filenameBegin = Internal::ConsumeName(AZStd::make_reverse_iterator(filenameEnd), + AZStd::make_reverse_iterator(rootDirEnd)).base(); + return MakeState(PS_InFilenames, filenameBegin, filenameEnd); + } + case PS_InRootDir: + { + /* The State transitions from Root Directory are + * "C:\" "\\server\", "\\?\", "\??\", "\\.\" -> Root Name + * ^ ^ ^ ^ ^ + * "/" -> This case has been taken care of by the current path entry != path start check + * ^ + */ + return MakeState(PS_InRootName, pathStart, currentPathEntry); + } + case PS_InRootName: + // The only valid state transition from Root Name is BeforeBegin + return MakeState(PS_BeforeBegin); + case PS_BeforeBegin: + AZ_Assert(false, "Path Parser cannot be decremented when it is in the BeforeBegin State"); + } + } + + //! Return a view of the current element in the path processor state + constexpr AZStd::string_view operator*() const noexcept + { + switch (m_parser_state) + { + case PS_BeforeBegin: + [[fallthrough]]; + case PS_AtEnd: + [[fallthrough]]; + case PS_InRootDir: + return m_preferred_separator == '/' ? "/" : "\\"; + case PS_InRootName: + case PS_InFilenames: + return m_path_raw_entry; + default: + AZ_Assert(false, "Path Parser is in an invalid state"); + } + return {}; + } + + constexpr explicit operator bool() const noexcept + { + return m_parser_state != PS_BeforeBegin && m_parser_state != PS_AtEnd; + } + + constexpr PathParser& operator++() noexcept + { + Increment(); + return *this; + } + + constexpr PathParser& operator--() noexcept + { + Decrement(); + return *this; + } + + constexpr bool AtEnd() const noexcept + { + return m_parser_state == PS_AtEnd; + } + + constexpr bool InRootDir() const noexcept + { + return m_parser_state == PS_InRootDir; + } + + constexpr bool InRootName() const noexcept + { + return m_parser_state == PS_InRootName; + } + + constexpr bool InRootPath() const noexcept + { + return InRootName() || InRootDir(); + } + + private: + constexpr void MakeState(ParserState newState, typename AZStd::string_view::iterator start, typename AZStd::string_view::iterator end) noexcept + { + m_parser_state = newState; + m_path_raw_entry = AZStd::string_view(start, end); + } + constexpr void MakeState(ParserState newState) noexcept + { + m_parser_state = newState; + m_path_raw_entry = {}; + } + + //! Return a pointer to the first character after the currently lexed element. + constexpr typename AZStd::string_view::iterator getNextTokenStartPos() const noexcept + { + switch (m_parser_state) + { + case PS_BeforeBegin: + return m_path_view.begin(); + case PS_InRootName: + case PS_InRootDir: + case PS_InFilenames: + return m_path_raw_entry.end(); + case PS_AtEnd: + return m_path_view.end(); + default: + AZ_Assert(false, "Path Parser is in an invalid state"); + } + return m_path_view.end(); + } + + //! Return a pointer to the first character in the currently lexed element. + constexpr typename AZStd::string_view::iterator getCurrentTokenStartPos() const noexcept + { + switch (m_parser_state) + { + case PS_BeforeBegin: + case PS_InRootName: + return m_path_view.begin(); + case PS_InRootDir: + case PS_InFilenames: + return m_path_raw_entry.begin(); + case PS_AtEnd: + return m_path_view.end(); + default: + AZ_Assert(false, "Path Parser is in an invalid state"); + } + return m_path_view.end(); + } + }; + + constexpr string_view_pair SeparateFilename(const AZStd::string_view& srcView) + { + if (srcView == "." || srcView == ".." || srcView.empty()) + { + return string_view_pair{ srcView, "" }; + } + auto pos = srcView.find_last_of('.'); + if (pos == AZStd::string_view::npos || pos == 0) + { + return string_view_pair{ srcView, AZStd::string_view{} }; + } + return string_view_pair{ srcView.substr(0, pos), srcView.substr(pos) }; + } + + + // path part consumption + constexpr bool ConsumeRootName(PathParser* pathParser) + { + static_assert(PS_BeforeBegin == 1 && PS_InRootName == 2, + "PathParser must be in state before begin or in the root name in order to consume the root name"); + while (pathParser->m_parser_state <= PS_InRootName) + { + ++(*pathParser); + } + return pathParser->m_parser_state == PS_AtEnd; + } + constexpr bool ConsumeRootDir(PathParser* pathParser) + { + static_assert(PS_BeforeBegin == 1 && PS_InRootName == 2 && PS_InRootDir == 3, + "PathParser must be in state before begin, in the root name or in the root directory in order to consume the root directory"); + while (pathParser->m_parser_state <= PS_InRootDir) + { + ++(*pathParser); + } + return pathParser->m_parser_state == PS_AtEnd; + } + + // path.comparisons + constexpr int CompareRootName(PathParser* lhsPathParser, PathParser* rhsPathParser) + { + if (!lhsPathParser->InRootName() && !rhsPathParser->InRootName()) + { + return 0; + } + + auto GetRootName = [](PathParser* pathParser) constexpr -> AZStd::string_view + { + return pathParser->InRootName() ? **pathParser : ""; + }; + int res = Internal::ComparePathSegment(GetRootName(lhsPathParser), GetRootName(rhsPathParser), lhsPathParser->m_preferred_separator); + ConsumeRootName(lhsPathParser); + ConsumeRootName(rhsPathParser); + return res; + } + constexpr int CompareRootDir(PathParser* lhsPathParser, PathParser* rhsPathParser) + { + if (!lhsPathParser->InRootDir() && rhsPathParser->InRootDir()) + { + return -1; + } + else if (lhsPathParser->InRootDir() && !rhsPathParser->InRootDir()) + { + return 1; + } + else + { + ConsumeRootDir(lhsPathParser); + ConsumeRootDir(rhsPathParser); + return 0; + } + } + constexpr int CompareRelative(PathParser* lhsPathParserPtr, PathParser* rhsPathParserPtr) + { + auto& lhsPathParser = *lhsPathParserPtr; + auto& rhsPathParser = *rhsPathParserPtr; + + while (lhsPathParser && rhsPathParser) + { + if (int res = Internal::ComparePathSegment(*lhsPathParser, *rhsPathParser, lhsPathParser.m_preferred_separator); + res != 0) + { + return res; + } + ++lhsPathParser; + ++rhsPathParser; + } + return 0; + } + constexpr int CompareEndState(PathParser* lhsPathParser, PathParser* rhsPathParser) + { + if (lhsPathParser->AtEnd() && !rhsPathParser->AtEnd()) + { + return -1; + } + else if (!lhsPathParser->AtEnd() && rhsPathParser->AtEnd()) + { + return 1; + } + return 0; + } + + constexpr int DetermineLexicalElementCount(PathParser pathParser) + { + int count = 0; + for (; pathParser; ++pathParser) + { + auto pathElement = *pathParser; + if (pathElement == "..") + { + --count; + } + else if (pathElement != "." && pathElement != "") + { + ++count; + } + } + return count; + } + + enum class PathPartKind : uint8_t + { + PK_None, + PK_RootName, + PK_RootSep, + PK_Filename, + PK_Dot, + PK_DotDot, + }; + + constexpr PathPartKind ClassifyPathPart(const PathParser& parser) + { + // Check each parser state to determine the PathPartKind + if (parser.m_parser_state == PS_InRootDir) + { + return PathPartKind::PK_RootSep; + } + if (parser.m_parser_state == PS_InRootName) + { + return PathPartKind::PK_RootName; + } + + // Fallback to checking parser pathEntry view value + // to determine if the special "." or ".." values are being used + AZStd::string_view pathPart = *parser; + if (pathPart == ".") + { + return PathPartKind::PK_Dot; + } + if (pathPart == "..") + { + return PathPartKind::PK_DotDot; + } + + // Return PathPartKind of PK_ilename if the parser state doesn't match + // the states of InRootDir or InRootName and the filename + // isn't made up of the special directory values of "." and ".." + return PathPartKind::PK_Filename; + } +} diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index b2ef586053..a41107e539 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -176,6 +176,8 @@ set(FILES IO/Path/Path.cpp IO/Path/Path.h IO/Path/Path.inl + IO/Path/PathIterable.inl + IO/Path/PathParser.inl IO/Path/Path_fwd.h IO/SystemFile.cpp IO/SystemFile.h diff --git a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp index 2fca03bc59..dbdb4fee78 100644 --- a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp +++ b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp @@ -56,7 +56,7 @@ namespace UnitTest } - // filesystem::path::is_absolute test + // PathView::IsAbsolute test TEST_F(PathFixture, IsAbsolute_ReturnsTrue) { using fixed_max_path = AZ::IO::FixedMaxPath; @@ -88,7 +88,7 @@ namespace UnitTest static_assert(IsAbsolute()); } - // filesystem::path::is_relative test + // PathView::isRelative test TEST_F(PathFixture, IsRelative_ReturnsTrue) { using fixed_max_path = AZ::IO::FixedMaxPath; @@ -573,7 +573,16 @@ namespace UnitTest PathLexicallyNormalParams{ '/', "foo/./bar/..", "foo" }, PathLexicallyNormalParams{ '/', "foo/.///bar/../", "foo" }, PathLexicallyNormalParams{ '/', R"(/foo\./bar\..\)", "/foo" }, - PathLexicallyNormalParams{ '\\', R"(C:/O3DE/dev/Cache\game/../pc)", R"(C:\O3DE\dev\Cache\pc)" } + PathLexicallyNormalParams{ '/', R"(/..)", "/" }, + PathLexicallyNormalParams{ '\\', R"(C:/O3DE/dev/Cache\game/../pc)", R"(C:\O3DE\dev\Cache\pc)" }, + PathLexicallyNormalParams{ '\\', R"(C:/foo/C:bar)", R"(C:\foo\bar)" }, + PathLexicallyNormalParams{ '\\', R"(C:foo/C:bar)", R"(C:foo\bar)" }, + PathLexicallyNormalParams{ '\\', R"(C:/foo/C:/bar)", R"(C:\bar)" }, + PathLexicallyNormalParams{ '\\', R"(C:/foo/C:)", R"(C:\foo)" }, + PathLexicallyNormalParams{ '\\', R"(C:/foo/C:/)", R"(C:\)" }, + PathLexicallyNormalParams{ '\\', R"(C:/foo/D:bar)", R"(D:bar)" }, + PathLexicallyNormalParams{ '\\', R"(..)", R"(..)" }, + PathLexicallyNormalParams{ '\\', R"(foo/../../bar)", R"(..\bar)" } ) ); @@ -641,7 +650,12 @@ namespace UnitTest PathViewLexicallyProximateParams{ '\\', "C:\\a\\b", "C:\\a\\d\\c", "..\\..\\b", false }, PathViewLexicallyProximateParams{ '\\', "C:a\\b", "C:\\a\\b", "C:a\\b", false }, PathViewLexicallyProximateParams{ '\\', "C:\\a\\b", "C:a\\b", "C:\\a\\b", false }, - PathViewLexicallyProximateParams{ '\\', "E:\\a\\b", "F:\\a\\b", "E:\\a\\b", false } + PathViewLexicallyProximateParams{ '\\', "E:\\a\\b", "F:\\a\\b", "E:\\a\\b", false }, + PathViewLexicallyProximateParams{ '\\', "D:/o3de/proJECT/cache/asset.txt", "d:\\o3de\\Project\\Cache", "asset.txt", true }, + PathViewLexicallyProximateParams{ '\\', "D:/o3de/proJECT/cache/pc/..", "d:\\o3de\\Project\\Cache", "pc\\..", true }, + PathViewLexicallyProximateParams{ '\\', "D:/o3de/proJECT/cache/pc/asset.txt/..", "d:\\o3de\\Project\\Cache\\", "pc\\asset.txt\\..", true }, + PathViewLexicallyProximateParams{ '\\', "D:/o3de/proJECT/cache\\", "D:\\o3de\\Project\\Cache/", ".", true }, + PathViewLexicallyProximateParams{ '\\', "D:/o3de/proJECT/cache/../foo", "D:\\o3de\\Project\\Cache", "..\\foo", false } ) ); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index e125cc1df6..012d713cf5 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -1912,7 +1912,7 @@ namespace AZ::IO return m_pFileEntry->nFileDataOffset; } - bool Archive::MakeDir(AZStd::string_view szPathIn, [[maybe_unused]] bool bGamePathMapping) + bool Archive::MakeDir(AZStd::string_view szPathIn) { AZ::IO::StackString pathStr{ szPathIn }; // Determine if there is a period ('.') after the last slash to determine if the path contains a file. @@ -2327,7 +2327,9 @@ namespace AZ::IO // we only want to record ASSET access // assets are identified as things which start with no alias, or with the @assets@ alias auto assetPath = AZ::IO::FileIOBase::GetInstance()->ConvertToAlias(szFilename); - if (assetPath && assetPath->Native().starts_with("@assets@")) + if (assetPath && (assetPath->Native().starts_with("@assets@") + || assetPath->Native().starts_with("@root@") + || assetPath->Native().starts_with("@projectplatformcache@"))) { IResourceList* pList = GetResourceList(m_eRecordFileOpenList); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h index bd10387d17..ec964f7fa3 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h @@ -249,7 +249,7 @@ namespace AZ::IO IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) override; // creates a directory - bool MakeDir(AZStd::string_view szPath, bool bGamePathMapping = false) override; + bool MakeDir(AZStd::string_view szPath) override; // compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate) // returns one of the Z_* errors (Z_OK upon success) diff --git a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h index 14a810e2cf..5ac417564a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h @@ -335,7 +335,7 @@ namespace AZ::IO virtual IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) = 0; // creates a directory - virtual bool MakeDir(AZStd::string_view szPath, bool bGamePathMapping = false) = 0; + virtual bool MakeDir(AZStd::string_view szPath) = 0; // open the physical archive file - creates if it doesn't exist // returns NULL if it's invalid or can't open the file diff --git a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp index 55e4f06db0..50190ac7d5 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp @@ -287,11 +287,8 @@ namespace AZ const char* assetAliasPath = GetAlias("@assets@"); if (path && assetAliasPath) { - AZStd::string assetsAlias(assetAliasPath); - AZStd::string pathString = path; - AZStd::to_lower(assetsAlias.begin(), assetsAlias.end()); - AZStd::to_lower(pathString.begin(), pathString.end()); - if (AZ::IO::PathView(pathString.c_str()).IsRelativeTo(assetsAlias.c_str())) + const AZ::IO::PathView pathView(path); + if (pathView.IsRelativeTo(assetAliasPath)) { AZ_Error("FileIO", false, "You may not alter data inside the asset cache. Please check the call stack and consider writing into the source asset folder instead.\n" "Attempted write location: %s", path); @@ -587,26 +584,11 @@ namespace AZ // strings that are shorter than the alias's mapped path without checking. if ((longestMatch == 0) || (resolvedAlias.size() > longestMatch) && (resolvedAlias.size() <= bufStringLength)) { - // custom strcmp that ignores slash directions - constexpr AZStd::string_view pathSeparators{ "/\\" }; - bool allMatch = AZStd::equal(resolvedAlias.begin(), resolvedAlias.end(), inBuffer.begin(), - [&pathSeparators](const char lhs, const char rhs) + // Check if the input path is relative to the alias value + if (AZ::IO::PathView(inBuffer).IsRelativeTo(AZ::IO::PathView(resolvedAlias))) { - const bool lhsIsSeparator = pathSeparators.find_first_of(lhs) != AZStd::string_view::npos; - const bool rhsIsSeparator = pathSeparators.find_first_of(lhs) != AZStd::string_view::npos; - return (lhsIsSeparator && rhsIsSeparator) || tolower(lhs) == tolower(rhs); - }); - - if (allMatch) - { - // Either the resolvedAlias path must match the path exactly or the path must have a path separator character - // right after the resolved alias - if (const size_t matchLen = resolvedAlias.size(); - matchLen == bufStringLength || (pathSeparators.find_first_of(inBuffer[matchLen]) != AZStd::string_view::npos)) - { - longestMatch = matchLen; - longestAlias = alias; - } + longestMatch = resolvedAlias.size(); + longestAlias = alias; } } } @@ -712,6 +694,7 @@ namespace AZ const char* assetAliasPath = GetAlias("@assets@"); const char* rootAliasPath = GetAlias("@root@"); const char* projectPlatformCacheAliasPath = GetAlias("@projectplatformcache@"); + const bool lowercasePath = (assetAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, assetAliasPath)) || (rootAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, rootAliasPath)) || (projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath)); diff --git a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp index 6a359e714c..f3764a5e96 100644 --- a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp +++ b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp @@ -608,6 +608,9 @@ namespace UnitTest archive->ClosePack(genericArchiveFileName); cpfio.Remove(genericArchiveFileName); + // create the asset alias directory + cpfio.CreatePath("@assets@"); + // create generic file HandleType normalFileHandle; @@ -848,13 +851,17 @@ namespace UnitTest const char *assetsPath = ioBase->GetAlias("@assets@"); ASSERT_NE(nullptr, assetsPath); - AZStd::string stringToAdd = AZStd::string::format("%s/textures/test.dds", assetsPath); + auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds"; reslist->Clear(); - reslist->Add(stringToAdd.c_str()); + reslist->Add(stringToAdd.Native()); // it normalizes the string, so the slashes flip and everything is lowercased. - EXPECT_STREQ(reslist->GetFirst(), "@assets@/textures/test.dds"); + AZ::IO::FixedMaxPath resolvedAddedPath; + AZ::IO::FixedMaxPath resolvedResourcePath; + EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@assets@/textures/test.dds")); + EXPECT_TRUE(ioBase->ReplaceAlias(resolvedResourcePath, reslist->GetFirst())); + EXPECT_EQ(resolvedAddedPath, resolvedResourcePath); reslist->Clear(); } diff --git a/Code/Legacy/CryCommon/Mocks/ICryPakMock.h b/Code/Legacy/CryCommon/Mocks/ICryPakMock.h index 94ffbf6f6d..ab3b3875af 100644 --- a/Code/Legacy/CryCommon/Mocks/ICryPakMock.h +++ b/Code/Legacy/CryCommon/Mocks/ICryPakMock.h @@ -70,7 +70,7 @@ struct CryPakMock MOCK_METHOD2(IsFileExist, bool(AZStd::string_view sFilename, EFileSearchLocation)); MOCK_METHOD1(IsFolder, bool(AZStd::string_view sPath)); MOCK_METHOD1(GetFileSizeOnDisk, AZ::IO::IArchive::SignedFileSize(AZStd::string_view filename)); - MOCK_METHOD2(MakeDir, bool(AZStd::string_view szPath, bool bGamePathMapping)); + MOCK_METHOD1(MakeDir, bool(AZStd::string_view szPath)); MOCK_METHOD4(OpenArchive, AZStd::intrusive_ptr (AZStd::string_view szPath, AZStd::string_view bindRoot, uint32_t nFlags, AZStd::intrusive_ptr pData)); MOCK_METHOD1(GetFileArchivePath, const char* (AZ::IO::HandleType f)); MOCK_METHOD5(RawCompress, int(const void* pUncompressed, size_t* pDestSize, void* pCompressed, size_t nSrcSize, int nLevel)); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp index fe1fd99f4e..1a97032864 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -56,7 +56,7 @@ namespace AZ m_predefinedMacros.begin(), m_predefinedMacros.end(), [&](const AZStd::string& predefinedMacro) { // Haystack, needle, bCaseSensitive - if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true)) + if (!AZ::StringFunc::StartsWith(predefinedMacro, macroName, true)) { return false; } @@ -117,11 +117,11 @@ namespace AZ char localBuffer[DefaultFprintfBufferSize]; va_list args; - + va_start(args, format); int count = azvsnprintf(localBuffer, DefaultFprintfBufferSize, format, args); va_end(args); - + char* result = localBuffer; // @result will be bound to @biggerData in case @localBuffer is not big enough. @@ -134,7 +134,7 @@ namespace AZ count++; // vsnprintf returns a size that doesn't include the null character. biggerData.reset(new char[count]); result = &biggerData[0]; - + // Remark: for MacOS & Linux it is important to call va_start again before // each call to azvsnprintf. Not required for Windows. va_start(args, format); @@ -149,7 +149,7 @@ namespace AZ // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/vsnprintf-vsnprintf-vsnprintf-l-vsnwprintf-vsnwprintf-l?view=msvc-160 // In particular: "If the number of characters to write is greater than count, // these functions return -1 indicating that output has been truncated." - + // There wasn't enough space in the local store. // Remark: for MacOS & Linux it is important to call va_start again before // each call to azvsnprintf. Not required for Windows. @@ -157,10 +157,10 @@ namespace AZ count = azvscprintf(format, args); count += 1; // vscprintf returns a size that doesn't include the null character. va_end(args); - + biggerData.reset(new char[count]); result = &biggerData[0]; - + va_start(args, format); count = azvsnprintf(result, count, format, args); va_end(args); @@ -191,7 +191,7 @@ namespace AZ // definitions for the linker AZStd::mutex McppBinder::s_mcppExclusiveProtection; - McppBinder* McppBinder::s_currentInstance = nullptr; + McppBinder* McppBinder::s_currentInstance = nullptr; // McppBinder ends /////////////////////////////////////////////////////////////////////// @@ -204,7 +204,7 @@ namespace AZ // create the argc/argv const char* processName = "builder"; - const char* inputPath = fullPath.c_str(); + const char* inputPath = fullPath.c_str(); // let's create the equivalent of that expression but in dynamic form: //const char* argv[] = { processName, szInPath, "-C", "-+", "-D macro1"..., "-I path"..., NULL }; AZStd::vector< const char* > argv; @@ -230,7 +230,7 @@ namespace AZ argv.push_back(nullptr); // usual argv terminator // output the command line: AZStd::string stringifiedCommandLine; - AzFramework::StringFunc::Join(stringifiedCommandLine, argv.begin(), argv.end() - 1, " "); + AZ::StringFunc::Join(stringifiedCommandLine, argv.begin(), argv.end() - 1, " "); AZ_TracePrintf("Preprocessor", "%s", stringifiedCommandLine.c_str()); // when we don't specify an -o outfile, mcpp uses stdout. // the trick is that since we hijacked putc & puts, stdout will not be written. @@ -238,17 +238,12 @@ namespace AZ return result; } - static void VerifySameFolder(const AZStd::string& path1, const AZStd::string& path2) + static void VerifySameFolder([[maybe_unused]] AZStd::string_view path1, [[maybe_unused]] AZStd::string_view path2) { - AZStd::string folder1, folder2; - AzFramework::StringFunc::Path::GetFolderPath(path1.c_str(), folder1); - AzFramework::StringFunc::Path::GetFolderPath(path2.c_str(), folder2); - AzFramework::StringFunc::Path::Normalize(folder1); - AzFramework::StringFunc::Path::Normalize(folder2); AZ_Warning("Preprocessing", - folder1 == folder2, - "The preprocessed file %s is in a different folder than its origin %s. Watch for #include problems with relative paths.", - path1.c_str(), path2.c_str() + AZ::IO::PathView(path1).ParentPath().LexicallyNormal() == AZ::IO::PathView(path2).ParentPath().LexicallyNormal(), + "The preprocessed file %.*s is in a different folder than its origin %.*s. Watch for #include problems with relative paths.", + AZ_STRING_ARG(path1), AZ_STRING_ARG(path2) ); } @@ -260,7 +255,7 @@ namespace AZ // containing file names, to match the ORIGINAL source, and not the actual source in use by azslc. // That gymnastic is better for error messages anyway, so instead of making the SRG layout builder more intelligent, // we'll fake the origin of the file, by setting the original source as a filename - // note that it is not possible to build a file in a different folder and fake it to a file eslewhere because relative includes will fail. + // note that it is not possible to build a file in a different folder and fake it to a file elsewhere because relative includes will fail. void MutateLineDirectivesFileOrigin( AZStd::string& sourceCode, AZStd::string newFileOrigin) @@ -272,11 +267,11 @@ namespace AZ // we will use that as the information of the source path to mutate. if (sourceCode.starts_with("#line")) { - auto firstQuote = sourceCode.find('"'); - auto secondQuote = sourceCode.find('"', firstQuote + 1); + auto firstQuote = sourceCode.find('"'); + auto secondQuote = firstQuote != AZStd::string::npos ? sourceCode.find('"', firstQuote + 1) : AZStd::string::npos; auto originalFile = sourceCode.substr(firstQuote + 1, secondQuote - firstQuote - 1); // start +1, count -1 because we don't want the quotes included. VerifySameFolder(originalFile, newFileOrigin); - [[maybe_unused]] bool didReplace = AzFramework::StringFunc::Replace(sourceCode, originalFile.c_str(), newFileOrigin.c_str(), true /*case sensitive*/); + [[maybe_unused]] bool didReplace = AZ::StringFunc::Replace(sourceCode, originalFile.c_str(), newFileOrigin.c_str(), true /*case sensitive*/); AZ_Assert(didReplace, "Failed to replace %s for %s in preprocessed source.", originalFile.c_str(), newFileOrigin.c_str()); } else @@ -285,26 +280,6 @@ namespace AZ } } - namespace - { - template< typename Container1, typename Container2 > - void TransferContent(Container1& destination, Container2&& source) - { - destination.insert(AZStd::end(destination), - AZStd::make_move_iterator(AZStd::begin(source)), - AZStd::make_move_iterator(AZStd::end(source))); - } - - void DeleteFromSet(const AZStd::string& string, AZStd::set& set) - { - auto iter = set.find(string); - if (iter != set.end()) - { - set.erase(iter); - } - } - } - // populate options with scan folders and contents of parsing shader_global_build_options.json void InitializePreprocessorOptions( PreprocessorOptions& options, [[maybe_unused]] const char* builderName, const char* optionalIncludeFolder) @@ -315,44 +290,61 @@ namespace AZ bool success = true; AZStd::vector scanFoldersVector; AzToolsFramework::AssetSystemRequestBus::BroadcastResult(success, - &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, - scanFoldersVector); + &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, + scanFoldersVector); AZ_Warning(builderName, success, "Preprocessor option: Could not acquire a list of scan folders from the database."); - // we transfer to a set, to order the folders, uniquify them, and ensure deterministic build behavior - AZStd::set scanFoldersSet; // Add the project path to list of include paths - AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath(); - scanFoldersSet.emplace(projectPath.c_str(), projectPath.size()); + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); + auto FindPath = [](AZ::IO::PathView searchPath) + { + return [searchPath](AZStd::string_view includePathView) + { + return searchPath == AZ::IO::PathView(includePathView); + }; + }; + if (auto it = AZStd::find_if(options.m_projectIncludePaths.begin(), options.m_projectIncludePaths.end(), FindPath(projectPath)); + it == options.m_projectIncludePaths.end()) + { + options.m_projectIncludePaths.emplace_back(projectPath.c_str(), projectPath.Native().size()); + } if (optionalIncludeFolder) { - scanFoldersSet.emplace(optionalIncludeFolder, strnlen(optionalIncludeFolder, AZ::IO::MaxPathLength)); + if (auto it = AZStd::find_if(options.m_projectIncludePaths.begin(), options.m_projectIncludePaths.end(), FindPath(optionalIncludeFolder)); + it == options.m_projectIncludePaths.end()) + { + if (AZ::IO::SystemFile::Exists(optionalIncludeFolder)) + { + options.m_projectIncludePaths.emplace_back(AZStd::move(AZ::IO::Path(optionalIncludeFolder).LexicallyNormal().Native())); + } + } } // but while we transfer to the set, we're going to keep only folders where +/ShaderLib exists - for (AZStd::string folder : scanFoldersVector) + for (AZ::IO::Path shaderScanFolder : scanFoldersVector) { - AzFramework::StringFunc::Path::Join(folder.c_str(), "ShaderLib", folder); - if (AZ::IO::SystemFile::Exists(folder.c_str())) + shaderScanFolder /= "ShaderLib"; + if (auto it = AZStd::find_if(options.m_projectIncludePaths.begin(), options.m_projectIncludePaths.end(), FindPath(shaderScanFolder)); + it == options.m_projectIncludePaths.end()) { - scanFoldersSet.emplace(std::move(folder)); + // the folders constructed this fashion constitute the base of automatic include search paths + if (AZ::IO::SystemFile::Exists(shaderScanFolder.c_str())) + { + options.m_projectIncludePaths.emplace_back(AZStd::move(shaderScanFolder.LexicallyNormal().Native())); + } } - } // the folders constructed this fashion constitute the base of automatic include search paths - - // get the engine root: - AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); + } - // add optional additional options - for (AZStd::string& path : options.m_projectIncludePaths) + // finally the /Gems fallback + AZ::IO::Path engineGemsFolder(AZStd::string_view{ AZ::Utils::GetEnginePath() }); + engineGemsFolder /= "Gems"; + if (auto it = AZStd::find_if(options.m_projectIncludePaths.begin(), options.m_projectIncludePaths.end(), FindPath(engineGemsFolder)); + it == options.m_projectIncludePaths.end()) { - path = (engineRoot / path).String(); - DeleteFromSet(path, scanFoldersSet); // no need to add a path two times. + if (AZ::IO::SystemFile::Exists(engineGemsFolder.c_str())) + { + options.m_projectIncludePaths.emplace_back(AZStd::move(engineGemsFolder.Native())); + } } - // back-insert the default paths (after the config-read paths we just read) - TransferContent(/*to:*/options.m_projectIncludePaths, /*from:*/scanFoldersSet); - // finally the /Gems fallback - AZStd::string gemsFolder; - AzFramework::StringFunc::Path::Join(engineRoot.c_str(), "Gems", gemsFolder); - options.m_projectIncludePaths.push_back(gemsFolder); } } // namespace ShaderBuilder From d366620818760060093e1108904bdcb22a8f5308 Mon Sep 17 00:00:00 2001 From: bosnichd Date: Fri, 10 Sep 2021 09:08:07 -0600 Subject: [PATCH 10/14] More PAL related changes required for restricted platforms. (#4037) Signed-off-by: bosnichd --- .../WinAPI/AzCore/std/parallel/internal/thread_WinAPI.cpp | 6 ++---- .../Windows/AzCore/std/parallel/internal/thread_Windows.cpp | 3 ++- .../Code/Source/Platform/Windows/RHI/Device_Windows.cpp | 5 +++++ Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp | 5 ----- Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp | 4 ++++ 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.cpp index dfe265aaa5..c84b4ecd98 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.cpp +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.cpp @@ -15,7 +15,7 @@ namespace AZStd { namespace Platform { - void PostThreadRun(); + unsigned __stdcall PostThreadRun(); HANDLE CreateThread(unsigned stackSize, unsigned (__stdcall* threadRunFunction)(void*), AZStd::Internal::thread_info* ti, unsigned int* id); unsigned HardwareConcurrency(); void SetThreadName(HANDLE hThread, const char* threadName); @@ -40,9 +40,7 @@ namespace AZStd ThreadEventBus::Broadcast(&ThreadEventBus::Events::OnThreadExit, this_thread::get_id()); // goes to client listeners ThreadDrillerEventBus::Broadcast(&ThreadDrillerEventBus::Events::OnThreadExit, this_thread::get_id()); // goes to the profiler. - Platform::PostThreadRun(); - - return 0; + return Platform::PostThreadRun(); } /** diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/std/parallel/internal/thread_Windows.cpp b/Code/Framework/AzCore/Platform/Windows/AzCore/std/parallel/internal/thread_Windows.cpp index 246181334a..fd6e81e536 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/std/parallel/internal/thread_Windows.cpp +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/std/parallel/internal/thread_Windows.cpp @@ -16,9 +16,10 @@ namespace AZStd { namespace Platform { - void PostThreadRun() + unsigned __stdcall PostThreadRun() { _endthreadex(0); + return 0; } HANDLE CreateThread(unsigned stackSize, unsigned (__stdcall* threadRunFunction)(void*), AZStd::Internal::thread_info* ti, unsigned int* id) diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp index 51272f3488..cb2b8d4ef8 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/Device_Windows.cpp @@ -325,5 +325,10 @@ namespace AZ return formatsList; } + + void Device::BeginFrameInternal() + { + m_commandQueueContext.Begin(); + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp index 371aa20a84..82e4aba57c 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp @@ -189,11 +189,6 @@ namespace AZ m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics); } - void Device::BeginFrameInternal() - { - m_commandQueueContext.Begin(); - } - void Device::EndFrameInternal() { AZ_TRACE_METHOD(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp index 182edb1298..66b3f9623a 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp @@ -262,7 +262,11 @@ namespace AZ bool PipelineLibrary::IsMergeRequired() const { +#if defined (AZ_DX12_USE_PIPELINE_LIBRARY) return !m_pipelineStates.empty(); +#else + return false; +#endif } } } From 81749ac31870eee58ecd860d4d2556b4014a6625 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 10 Sep 2021 08:41:19 -0700 Subject: [PATCH 11/14] Improves numeric_cast compilation (#3995) * Improves numeric_cast compilation: before: 872s after: 824s (5.5% reduction) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../AzCore/AzCore/Casting/numeric_cast.h | 102 ++++-------------- .../AzCore/Casting/numeric_cast_internal.h | 81 ++++++++++++++ .../Serialization/Json/CastingHelpers.h | 8 +- .../AzCore/AzCore/azcore_files.cmake | 1 + .../Components/FancyDocking.cpp | 1 + .../Components/Widgets/GradientSlider.cpp | 2 +- 6 files changed, 106 insertions(+), 89 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Casting/numeric_cast_internal.h diff --git a/Code/Framework/AzCore/AzCore/Casting/numeric_cast.h b/Code/Framework/AzCore/AzCore/Casting/numeric_cast.h index 4cfe3a3d8d..c31a2e2f32 100644 --- a/Code/Framework/AzCore/AzCore/Casting/numeric_cast.h +++ b/Code/Framework/AzCore/AzCore/Casting/numeric_cast.h @@ -8,14 +8,25 @@ #pragma once +// This is disabled by default because it puts in costly runtime checking of casted values. +// You can either change it here to enable it across the engine, or use push/pop_macro to enable per file/feature. +// Note that if using push/pop_macro, you may get some of the functions not inline and the definition coming from +// another compilation unit, in such case, you will have to push/pop_macro on that compilation unit as well. +// #define AZ_NUMERICCAST_ENABLED 1 + +#if !AZ_NUMERICCAST_ENABLED + +#define aznumeric_cast static_cast + +#else + +#include #include #include #include #include #include -#include #include -#include #include #include #include @@ -28,7 +39,7 @@ // enabled. // // Because we can't do partial function specialization, I'm using enable_if to chop up the implementation into one of these -// implementations. If none of these fit, then we will get a compile error because it is an unknown conversionr. +// implementations. If none of these fit, then we will get a compile error because it is an unknown conversion. // //-------------------------------------------- // TYPE <- TYPE DigitLoss @@ -51,85 +62,7 @@ // (K) Floating Floating Y */ -// This is disabled by default because it puts in costly runtime checking of casted values. -// You can either change it here to enable it across the engine, or use push/pop_macro to enable per file/feature. -// Note that if using push/pop_macro, you may get some of the functions not inline and the definition coming from -// another compilation unit, in such case, you will have to push/pop_macro on that compilation unit as well. -// #define AZ_NUMERICCAST_ENABLED 1 - -#if AZ_NUMERICCAST_ENABLED #define AZ_NUMERIC_ASSERT(expr, ...) AZ_Assert(expr, __VA_ARGS__) -#else -#define AZ_NUMERIC_ASSERT(expr, ...) void(0) -#endif - -#pragma push_macro("max") -#undef max - -namespace NumericCastInternal -{ - template - inline constexpr typename AZStd::enable_if< - !AZStd::is_integral::value || !AZStd::is_floating_point::value - , bool> ::type UnderflowsToType(const FromType& value) - { - return (value < static_cast(std::numeric_limits::lowest())); - } - - template - inline constexpr typename AZStd::enable_if< - AZStd::is_integral::value && AZStd::is_floating_point::value - , bool> ::type UnderflowsToType(const FromType& value) - { - return (static_cast(value) < std::numeric_limits::lowest()); - } - - template - inline constexpr typename AZStd::enable_if< - !AZStd::is_integral::value || !AZStd::is_floating_point::value - , bool> ::type OverflowsToType(const FromType& value) - { - return (value > static_cast(std::numeric_limits::max())); - } - - template - inline constexpr typename AZStd::enable_if< - AZStd::is_integral::value && AZStd::is_floating_point::value - , bool> ::type OverflowsToType(const FromType& value) - { - return (static_cast(value) > std::numeric_limits::max()); - } - - template - inline constexpr typename AZStd::enable_if< - AZStd::is_integral::value && AZStd::is_integral::value - && std::numeric_limits::digits <= std::numeric_limits::digits - && AZStd::is_signed::value && AZStd::is_unsigned::value - , bool> ::type FitsInToType(const FromType& value) - { - return !NumericCastInternal::UnderflowsToType(value); - } - - template - inline constexpr typename AZStd::enable_if< - AZStd::is_integral::value && AZStd::is_integral::value - && (std::numeric_limits::digits > std::numeric_limits::digits) - && AZStd::is_unsigned::value - , bool> ::type FitsInToType(const FromType& value) - { - return !NumericCastInternal::OverflowsToType(value); - } - - template - inline constexpr typename AZStd::enable_if< - (!AZStd::is_integral::value || !AZStd::is_integral::value) - || ((std::numeric_limits::digits <= std::numeric_limits::digits) && (AZStd::is_unsigned::value || AZStd::is_signed::value)) - || ((std::numeric_limits::digits > std::numeric_limits::digits) && AZStd::is_signed::value) - , bool> ::type FitsInToType(const FromType& value) - { - return !NumericCastInternal::OverflowsToType(value) && !NumericCastInternal::UnderflowsToType(value); - } -} // namespace AZ // INTEGER -> INTEGER // (A) Not losing digits or risking sign loss @@ -276,8 +209,10 @@ inline constexpr auto aznumeric_cast(FromType&& value) -> return static_cast(value); } +#endif + // This is a helper class that lets us induce the destination type of a numeric cast -// It should never be directly used by anything other than azlossy_caster. +// It should never be directly used by anything other than aznumeric_caster. namespace AZ { template @@ -295,7 +230,7 @@ namespace AZ FromType m_value; }; -} +} // namespace AZ // This is the primary function we should use when doing numeric casting, since it induces the // type we need to cast to from the code rather than requiring an explicit coupling in the source. @@ -305,4 +240,3 @@ inline constexpr AZ::NumericCasted aznumeric_caster(FromType value) return AZ::NumericCasted(value); } -#pragma pop_macro("max") diff --git a/Code/Framework/AzCore/AzCore/Casting/numeric_cast_internal.h b/Code/Framework/AzCore/AzCore/Casting/numeric_cast_internal.h new file mode 100644 index 0000000000..ee2f91e95a --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Casting/numeric_cast_internal.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace NumericCastInternal +{ + template + inline constexpr typename AZStd::enable_if::value || !AZStd::is_floating_point::value, bool>::type + UnderflowsToType(const FromType& value) + { + return (value < static_cast(std::numeric_limits::lowest())); + } + + template + inline constexpr typename AZStd::enable_if::value && AZStd::is_floating_point::value, bool>::type + UnderflowsToType(const FromType& value) + { + return (static_cast(value) < std::numeric_limits::lowest()); + } + + template + inline constexpr typename AZStd::enable_if::value || !AZStd::is_floating_point::value, bool>::type + OverflowsToType(const FromType& value) + { + return (value > static_cast(std::numeric_limits::max())); + } + + template + inline constexpr typename AZStd::enable_if::value && AZStd::is_floating_point::value, bool>::type + OverflowsToType(const FromType& value) + { + return (static_cast(value) > std::numeric_limits::max()); + } + + template + inline constexpr typename AZStd::enable_if< + AZStd::is_integral::value && AZStd::is_integral::value && + std::numeric_limits::digits <= std::numeric_limits::digits && AZStd::is_signed::value && + AZStd::is_unsigned::value, + bool>::type + FitsInToType(const FromType& value) + { + return !NumericCastInternal::UnderflowsToType(value); + } + + template + inline constexpr typename AZStd::enable_if< + AZStd::is_integral::value && AZStd::is_integral::value && + (std::numeric_limits::digits > std::numeric_limits::digits) && AZStd::is_unsigned::value, + bool>::type + FitsInToType(const FromType& value) + { + return !NumericCastInternal::OverflowsToType(value); + } + + template + inline constexpr typename AZStd::enable_if< + (!AZStd::is_integral::value || !AZStd::is_integral::value) || + ((std::numeric_limits::digits <= std::numeric_limits::digits) && + (AZStd::is_unsigned::value || AZStd::is_signed::value)) || + ((std::numeric_limits::digits > std::numeric_limits::digits) && AZStd::is_signed::value), + bool>::type + FitsInToType(const FromType& value) + { + return !NumericCastInternal::OverflowsToType(value) && !NumericCastInternal::UnderflowsToType(value); + } + +} diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/CastingHelpers.h b/Code/Framework/AzCore/AzCore/Serialization/Json/CastingHelpers.h index 36b0426a2d..bcd1a84d03 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/CastingHelpers.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/CastingHelpers.h @@ -8,14 +8,14 @@ #pragma once -#include +#include #include #include #include namespace AZ { - //! A helper function to casts between numeric types, and consider the data which is being converted comse from user data. + //! A helper function to casts between numeric types, and consider the data which is being converted comes from user data. //! If a conversion from FromType to ToType will not cause overflow or underflow, the result is stored in result, and the function returns Success //! Otherwise, the target is left untouched. template @@ -24,9 +24,9 @@ namespace AZ { using namespace JsonSerializationResult; - if (NumericCastInternal::FitsInToType(value)) + if (NumericCastInternal::template FitsInToType(value)) { - result = aznumeric_cast(value); + result = static_cast(value); return reporting("Successfully cast number.", ResultCode(Tasks::Convert, Outcomes::Success), path); } else diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index a41107e539..6c498c3335 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -35,6 +35,7 @@ set(FILES Asset/AssetInternal/WeakAsset.h Casting/lossy_cast.h Casting/numeric_cast.h + Casting/numeric_cast_internal.h Component/Component.cpp Component/Component.h Component/ComponentApplication.cpp diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index 4ced4ea635..d73ebbec28 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/GradientSlider.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/GradientSlider.cpp index 1ffff57ade..920ff17eac 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/GradientSlider.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/GradientSlider.cpp @@ -114,7 +114,7 @@ void GradientSlider::mouseMoveEvent(QMouseEvent* event) { int intValue = Slider::valueFromPosition(this, event->pos(), width(), height(), rect().bottom()); - qreal value = (aznumeric_cast(intValue - minimum()) / aznumeric_cast(maximum() - minimum())); + qreal value = (aznumeric_cast(intValue - minimum()) / aznumeric_cast(maximum() - minimum())); const QString toolTipText = m_toolTipFunction(value); From 280796e1f4c916934c62dbdd0bb65d8717da1e2f Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Fri, 10 Sep 2021 11:59:20 -0500 Subject: [PATCH 12/14] Fix GHI 4041 Terrain Shader Crash (#4050) The terrain feature processor was crashing due to an invalid shader name. The shader name has been fixed, but the feature processor has also been hardened so that it no longer crashes if a shader fails to load. Also, while testing, this inadvertently exposed a second crash in EntitySerializer that occurs when components don't serialize in correctly. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../AzCore/AzCore/Component/EntitySerializer.cpp | 5 +++-- .../TerrainRenderer/TerrainFeatureProcessor.cpp | 14 +++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp b/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp index 803daf1373..1dd685f763 100644 --- a/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp @@ -84,8 +84,9 @@ namespace AZ static TypeId genericComponentWrapperTypeId("{68D358CA-89B9-4730-8BA6-E181DEA28FDE}"); for (auto& [componentKey, component] : componentMap) { - // if underlying type is genericComponentWrapperTypeId, the template is null and the component should not be addded - if (component->GetUnderlyingComponentType() != genericComponentWrapperTypeId) + // if the component didn't serialize (i.e. is null) or the underlying type is genericComponentWrapperTypeId, the + // template is null and the component should not be addded + if (component && (component->GetUnderlyingComponentType() != genericComponentWrapperTypeId)) { entityInstance->m_components.emplace_back(component); } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index f0743cfb52..511b206b84 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -66,7 +66,13 @@ namespace Terrain } void TerrainFeatureProcessor::ConfigurePipelineState(ShaderState& shaderState, bool assertOnFail) - { + { + if (shaderState.m_shader == nullptr) + { + AZ_Assert(shaderState.m_shader || !assertOnFail, "Terrain shader failed to load correctly."); + return; + } + bool success = GetParentScene()->ConfigurePipelineState(shaderState.m_shader->GetDrawListTag(), shaderState.m_pipelineStateDescriptor); AZ_Assert(success || !assertOnFail, "Couldn't configure the pipeline state."); if (success) @@ -110,7 +116,7 @@ namespace Terrain }; LoadShader("Shaders/Terrain/Terrain.azshader", m_shaderStates[ShaderType::Forward]); - LoadShader("Shaders/Terrain/Terrain_DepthPass_WithPS.azshader", m_shaderStates[ShaderType::Depth]); + LoadShader("Shaders/Terrain/Terrain_DepthPass.azshader", m_shaderStates[ShaderType::Depth]); // Forward and depth shader use same srg layout. AZ::RHI::Ptr perObjectSrgLayout = @@ -248,7 +254,9 @@ namespace Terrain { AZ_PROFILE_FUNCTION(AzRender); - if (m_shaderStates[ShaderType::Forward].m_shader->GetDrawListTag().IsNull() || + if ((m_shaderStates[ShaderType::Forward].m_shader == nullptr) || + (m_shaderStates[ShaderType::Depth].m_shader == nullptr) || + m_shaderStates[ShaderType::Forward].m_shader->GetDrawListTag().IsNull() || m_shaderStates[ShaderType::Depth].m_shader->GetDrawListTag().IsNull()) { return; From d9cbc97ec07909e6ddb6687e0c22da07cc6bbdb6 Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Fri, 10 Sep 2021 10:38:16 -0700 Subject: [PATCH 13/14] ATOM-16063 Remove SetShaderResourceGroupCallback in scene and update scene srg handling (#3969) ATOM-16273 Compiling SceneSRG before updating it can cause a gpu crash Changes include: 1. Removed Scene::SetShaderResourceGroupCallback() function and clean up code which use this function. 2. Moved SceneTimeSrg.azsli to RPI's DefaultSceneSrg folder and setup the constants in RPI::Scene 3. Add AZ::Event for Scene's update srg event which features and update scene srg at proper place 4. UpdateTransformServcie FP to use PrepareSceneSrg event handler. 5. Clean up shaders and srgs used in project templates. Signed-off-by: Qing Tao --- AutomatedTesting/Shaders/CommonVS.azsli | 51 ------------------ .../Code/Source/BootstrapSystemComponent.cpp | 33 +----------- .../Code/Source/BootstrapSystemComponent.h | 3 -- .../ShaderResourceGroups/SceneSrgAll.azsli | 2 +- .../Shaders/PostProcessing/EyeAdaptation.azsl | 4 +- .../PostProcessing/EyeAdaptationUtil.azsli | 3 +- .../atom_feature_common_asset_files.cmake | 1 - .../TransformServiceFeatureProcessor.h | 15 +++--- .../ExposureControlRenderProxy.cpp | 12 ++--- .../Source/PostProcessing/EyeAdaptationPass.h | 1 + .../TransformServiceFeatureProcessor.cpp | 25 ++++----- .../DefaultSceneSrg.azsli} | 1 - .../RPI/Code/Include/Atom/RPI.Public/Scene.h | 23 +++++--- .../Code/Include/Atom/RPI.Public/SceneBus.h | 1 - .../RPI.Public/Pass/AttachmentReadback.cpp | 6 +-- .../RPI/Code/Source/RPI.Public/RPISystem.cpp | 2 +- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 47 ++++++++++------- .../Viewport/MaterialViewportRenderer.cpp | 20 +------ .../Viewport/MaterialViewportRenderer.h | 2 - .../Rendering/ThumbnailRendererData.h | 2 - .../ThumbnailRendererSteps/CaptureStep.cpp | 5 +- .../ThumbnailRendererSteps/InitializeStep.cpp | 28 ---------- .../Template/ShaderLib/scenesrg.srgi | 3 +- .../Template/Shaders/CommonVS.azsli | 52 ------------------- .../ShaderResourceGroups/SceneSrg.azsli | 20 ------- Templates/DefaultProject/template.json | 12 ----- .../Template/ShaderLib/scenesrg.srgi | 3 +- .../Template/Shaders/CommonVS.azsli | 52 ------------------- .../ShaderResourceGroups/SceneSrg.azsli | 20 ------- Templates/MinimalProject/template.json | 12 ----- 30 files changed, 80 insertions(+), 381 deletions(-) delete mode 100644 AutomatedTesting/Shaders/CommonVS.azsli rename Gems/Atom/{Feature/Common/Assets/ShaderResourceGroups/SceneTimeSrg.azsli => RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultSceneSrg.azsli} (94%) delete mode 100644 Templates/DefaultProject/Template/Shaders/CommonVS.azsli delete mode 100644 Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli delete mode 100644 Templates/MinimalProject/Template/Shaders/CommonVS.azsli delete mode 100644 Templates/MinimalProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli diff --git a/AutomatedTesting/Shaders/CommonVS.azsli b/AutomatedTesting/Shaders/CommonVS.azsli deleted file mode 100644 index 3d46871b59..0000000000 --- a/AutomatedTesting/Shaders/CommonVS.azsli +++ /dev/null @@ -1,51 +0,0 @@ - -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include - -struct VertexInput -{ - float3 m_position : POSITION; - float3 m_normal : NORMAL; - float4 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float2 m_uv : UV0; -}; - -struct VertexOutput -{ - float4 m_position : SV_Position; - float3 m_normal : NORMAL; - float3 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float2 m_uv : UV0; - float3 m_view : VIEW; -}; - -VertexOutput CommonVS(VertexInput input) -{ - float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); - float3x3 objectToWorldIT = ObjectSrg::GetWorldMatrixInverseTranspose(); - - VertexOutput output; - float3 worldPosition = mul(objectToWorld, float4(input.m_position, 1)).xyz; - output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); - - output.m_uv = input.m_uv; - - output.m_view = worldPosition - ViewSrg::m_worldPosition; - - ConstructTBN(input.m_normal, input.m_tangent, input.m_bitangent, objectToWorld, objectToWorldIT, output.m_normal, output.m_tangent, output.m_bitangent); - - return output; -} diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 11758138e0..d837988cfb 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -253,34 +253,6 @@ namespace AZ RPI::SceneDescriptor sceneDesc; AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc); atomScene->EnableAllFeatureProcessors(); - - // Setup scene srg modification callback. - RPI::ShaderResourceGroupCallback callback = [this](RPI::ShaderResourceGroup* srg) - { - if (srg == nullptr) - { - return; - } - bool needCompile = false; - RHI::ShaderInputConstantIndex timeIndex = srg->FindShaderInputConstantIndex(Name{ "m_time" }); - if (timeIndex.IsValid()) - { - srg->SetConstant(timeIndex, m_simulateTime); - needCompile = true; - } - RHI::ShaderInputConstantIndex deltaTimeIndex = srg->FindShaderInputConstantIndex(Name{ "m_deltaTime" }); - if (deltaTimeIndex.IsValid()) - { - srg->SetConstant(deltaTimeIndex, m_deltaTime); - needCompile = true; - } - - if (needCompile) - { - srg->Compile(); - } - }; - atomScene->SetShaderResourceGroupCallback(callback); atomScene->Activate(); // Register scene to RPI system so it will be processed/rendered per tick @@ -408,11 +380,8 @@ namespace AZ m_renderPipelineId = ""; } - void BootstrapSystemComponent::OnTick(float deltaTime, [[maybe_unused]] ScriptTimePoint time) + void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) { - m_simulateTime += deltaTime; - m_deltaTime = deltaTime; - // Temp: When running in the launcher without the legacy renderer // we need to call RenderTick on the viewport context each frame. if (m_viewportContext) diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h index bd5d417b8f..566d19b1a4 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h @@ -105,9 +105,6 @@ namespace AZ RPI::ScenePtr m_defaultScene = nullptr; AZStd::shared_ptr m_defaultFrameworkScene = nullptr; - float m_simulateTime = 0; - float m_deltaTime = 0.016f; - bool m_isAssetCatalogLoaded = false; // The id of the render pipeline created by this component diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli index 96ed740f98..421178bb01 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli @@ -11,6 +11,6 @@ // Please review README.md to understand how this file is used in SceneSrg.azsrg generation #ifdef AZ_COLLECTING_PARTIAL_SRGS -#include +#include #include #endif diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.azsl index ce10eb9b91..1844144401 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptation.azsl @@ -78,10 +78,12 @@ void MainCS(uint3 dispatch_id : SV_DispatchThreadID) const float speed = exposureDifference > 0.0 ? ViewSrg::m_exposureControl.m_speedUp : ViewSrg::m_exposureControl.m_speedDown; // Update the adjustment for this frame based on the frame deltaTime and speed - float exposureAdjustment = exposureDifference * SceneSrg::m_deltaTime * speed; + float deltaTime = clamp(SceneSrg::m_time - PassSrg::m_eyeAdaptationData[0].m_setValueTime, 0.0f, 1.0f); + float exposureAdjustment = exposureDifference * deltaTime * speed; float newExposureLog2 = previousFrameExposureLog2 + exposureAdjustment; // Store the linear exposure so it can be used by the look modification transform later. // newExposureLog2 is negated because m_exposureValue is used to correct for a given exposure. PassSrg::m_eyeAdaptationData[0].m_exposureValue = pow(2.0f, -newExposureLog2); + PassSrg::m_eyeAdaptationData[0].m_setValueTime = SceneSrg::m_time; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptationUtil.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptationUtil.azsli index 54e96122ac..fb75e1079c 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptationUtil.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/EyeAdaptationUtil.azsli @@ -8,5 +8,6 @@ struct EyeAdaptation { - float m_exposureValue; // current frame's exposure value in stops (logarithmic space) + float m_exposureValue; // current frame's exposure value in stops (logarithmic space) + float m_setValueTime; // the time when the m_exposureValue was set }; diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 75181517a4..e43498c55d 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -289,7 +289,6 @@ set(FILES ShaderLib/Atom/Features/Vertex/VertexHelper.azsli ShaderResourceGroups/SceneSrg.azsli ShaderResourceGroups/SceneSrgAll.azsli - ShaderResourceGroups/SceneTimeSrg.azsli ShaderResourceGroups/ViewSrg.azsli ShaderResourceGroups/ViewSrgAll.azsli ShaderResourceGroups/CoreLights/SceneSrg.azsli diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h index 6482871234..936fc88fa6 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace AZ @@ -36,8 +37,6 @@ namespace AZ void Activate() override; //! Releases GPU resources. void Deactivate() override; - //! Binds buffers - void Render(const FeatureProcessor::RenderPacket& packet) override; // RPI::SceneNotificationBus overrides ... void OnBeginPrepareRender() override; @@ -68,11 +67,13 @@ namespace AZ // Prepare GPU buffers for object transformation matrices // Create the buffers if they don't exist. Otherwise, resize them if they are not large enough for the matrices void PrepareBuffers(); - - Data::Instance m_sceneSrg; - RHI::ShaderInputBufferIndex m_objectToWorldBufferIndex; - RHI::ShaderInputBufferIndex m_objectToWorldInverseTransposeBufferIndex; - RHI::ShaderInputBufferIndex m_objectToWorldHistoryBufferIndex; + + void UpdateSceneSrg(RPI::ShaderResourceGroup *sceneSrg); + + RPI::Scene::PrepareSceneSrgEvent::Handler m_updateSceneSrgHandler; + RHI::ShaderInputNameIndex m_objectToWorldBufferIndex = "m_objectToWorldBuffer"; + RHI::ShaderInputNameIndex m_objectToWorldInverseTransposeBufferIndex = "m_objectToWorldInverseTransposeBuffer"; + RHI::ShaderInputNameIndex m_objectToWorldHistoryBufferIndex = "m_objectToWorldHistoryBuffer"; // Stores transforms that are uploaded to a GPU buffer. Used slots have float12(matrix3x4) values, empty slots // have a uint32_t that points to the next empty slot like a linked list. m_firstAvailableMeshTransformIndex stores the first diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp index 1d345a2432..924af4ab98 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp @@ -36,8 +36,6 @@ namespace AZ m_viewPtr = view; m_viewSrg = view->GetShaderResourceGroup(); - m_exposureControlBufferInputIndex = m_viewSrg->FindShaderInputBufferIndex(Name("m_exposureControl")); - m_eyeAdaptationBuffer.Init(m_viewSrg, idNumber); } @@ -109,14 +107,10 @@ namespace AZ m_eyeAdaptationBuffer.UpdateSrg(); - if (m_exposureControlBufferInputIndex.IsValid()) + m_viewSrg->SetBufferView(m_exposureControlBufferInputIndex, m_buffer->GetBufferView()); + if (m_viewPtr) { - m_viewSrg->SetBufferView(m_exposureControlBufferInputIndex, m_buffer->GetBufferView()); - - if (m_viewPtr) - { - m_viewPtr->InvalidateSrg(); - } + m_viewPtr->InvalidateSrg(); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h index 0d045a0f78..bec3af2934 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h @@ -52,6 +52,7 @@ namespace AZ struct ExposureCalculationData { float m_exposureValue = 1.0f; + float m_setValueTime = 0; }; void BuildInternal() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index 7a76e1b7e0..6f9c0dc73a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -36,11 +36,8 @@ namespace AZ void TransformServiceFeatureProcessor::Activate() { - m_sceneSrg = GetParentScene()->GetShaderResourceGroup(); - - m_objectToWorldBufferIndex = m_sceneSrg->FindShaderInputBufferIndex(Name{"m_objectToWorldBuffer"}); - m_objectToWorldInverseTransposeBufferIndex = m_sceneSrg->FindShaderInputBufferIndex(Name{"m_objectToWorldInverseTransposeBuffer"}); - m_objectToWorldHistoryBufferIndex = m_sceneSrg->FindShaderInputBufferIndex(Name{"m_objectToWorldHistoryBuffer"}); + m_updateSceneSrgHandler = RPI::Scene::PrepareSceneSrgEvent::Handler([this](RPI::ShaderResourceGroup *sceneSrg) { this->UpdateSceneSrg(sceneSrg); }); + GetParentScene()->ConnectEvent(m_updateSceneSrgHandler); m_deviceBufferNeedsUpdate = true; m_objectToWorldTransforms.reserve(BufferReserveCount); @@ -62,9 +59,14 @@ namespace AZ m_firstAvailableTransformIndex = NoAvailableTransformIndices; + m_objectToWorldBufferIndex.Reset(); + m_objectToWorldInverseTransposeBufferIndex.Reset(); + m_objectToWorldHistoryBufferIndex.Reset(); + m_isWriteable = false; RPI::SceneNotificationBus::Handler::BusDisconnect(); + m_updateSceneSrgHandler.Disconnect(); } void TransformServiceFeatureProcessor::PrepareBuffers() @@ -131,16 +133,11 @@ namespace AZ } } - void TransformServiceFeatureProcessor::Render([[maybe_unused]] const FeatureProcessor::RenderPacket& packet) + void TransformServiceFeatureProcessor::UpdateSceneSrg(RPI::ShaderResourceGroup *sceneSrg) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "TransformServiceFeatureProcessor: Render"); - AZ_UNUSED(packet); - - AZ_Assert(!m_isWriteable, "Must be called between OnBeginPrepareRender() and OnEndPrepareRender()"); - - m_sceneSrg->SetBufferView(m_objectToWorldBufferIndex, m_objectToWorldBuffer->GetBufferView()); - m_sceneSrg->SetBufferView(m_objectToWorldInverseTransposeBufferIndex, m_objectToWorldInverseTransposeBuffer->GetBufferView()); - m_sceneSrg->SetBufferView(m_objectToWorldHistoryBufferIndex, m_objectToWorldHistoryBuffer->GetBufferView()); + sceneSrg->SetBufferView(m_objectToWorldBufferIndex, m_objectToWorldBuffer->GetBufferView()); + sceneSrg->SetBufferView(m_objectToWorldInverseTransposeBufferIndex, m_objectToWorldInverseTransposeBuffer->GetBufferView()); + sceneSrg->SetBufferView(m_objectToWorldHistoryBufferIndex, m_objectToWorldHistoryBuffer->GetBufferView()); } void TransformServiceFeatureProcessor::OnBeginPrepareRender() diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneTimeSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultSceneSrg.azsli similarity index 94% rename from Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneTimeSrg.azsli rename to Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultSceneSrg.azsli index c89fc6b4c4..f9428b3125 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneTimeSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultSceneSrg.azsli @@ -13,6 +13,5 @@ partial ShaderResourceGroup SceneSrg { float m_time; - float m_deltaTime; } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h index fa918ae033..b1c6aac92d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -129,10 +129,6 @@ namespace AZ void RemoveRenderPipeline(const RenderPipelineId& pipelineId); - //! Set a callback function to set values for scene's srg. - //! The callback function is usually defined by the one who create the scene since it knows how the layout look like. - void SetShaderResourceGroupCallback(ShaderResourceGroupCallback callback); - const RHI::ShaderResourceGroup* GetRHIShaderResourceGroup() const; Data::Instance GetShaderResourceGroup() const; @@ -166,9 +162,13 @@ namespace AZ RenderPipelinePtr FindRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle); + using PrepareSceneSrgEvent = AZ::Event; + //! Connect a handler to listen to the event that the Scene is ready to update and compile its scene srg + //! User should use this event to update the part scene srg they know of + void ConnectEvent(PrepareSceneSrgEvent::Handler& handler); + protected: // SceneFinder overrides... - Scene* FindSelf(); void OnSceneNotifictaionHandlerConnected(SceneNotification* handler); // Cpu simulation which runs all active FeatureProcessor Simulate() functions. @@ -183,7 +183,7 @@ namespace AZ // Function called when the current frame is finished rendering. void OnFrameEnd(); - // Update and compile view srgs + // Update and compile scene and view srgs // This is called after PassSystem's FramePrepare so passes can still modify view srgs in its FramePrepareIntenal function before they are submitted to command list void UpdateSrgs(); @@ -200,6 +200,10 @@ namespace AZ // Add a created feature processor to this scene void AddFeatureProcessor(FeatureProcessorPtr fp); + // Send out event to PrepareSceneSrgEvent::Handlers so they can update scene srg as needed + // This happens in UpdateSrgs() + void PrepareSceneSrg(); + // List of feature processors that are active for this scene AZStd::vector m_featureProcessors; @@ -215,9 +219,10 @@ namespace AZ AZ::RPI::FeatureProcessor::SimulatePacket m_simulatePacket; AZ::RPI::FeatureProcessor::RenderPacket m_renderPacket; - // Scene's srg and its set function + // Scene's srg Data::Instance m_srg; - ShaderResourceGroupCallback m_srgCallback; + // Event to for prepare scene srg + PrepareSceneSrgEvent m_prepareSrgEvent; // The uuid to identify this scene. SceneId m_id; @@ -234,6 +239,8 @@ namespace AZ // Registry which allocates draw filter tag for RenderPipeline RHI::Ptr m_drawFilterTagRegistry; + + float m_simulationTime; }; // --- Template functions --- diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h index 4222f634cc..ca531d2de6 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/SceneBus.h @@ -80,7 +80,6 @@ namespace AZ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; using BusIdType = SceneId; - virtual Scene* FindSelf() = 0; virtual void OnSceneNotifictaionHandlerConnected(SceneNotification* handler) = 0; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp index 146c37fe1b..c19ae565d0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp @@ -308,10 +308,10 @@ namespace AZ // The fix is to clear the buffer outside of the callback. for (int32_t i = 0; i < RHI::Limits::Device::FrameCountMax; i++) { - if (m_isReadbackComplete[m_readbackBufferCurrentIndex]) + if (m_isReadbackComplete[i]) { - m_isReadbackComplete[m_readbackBufferCurrentIndex] = false; - m_readbackBufferArray[m_readbackBufferCurrentIndex] = nullptr; + m_isReadbackComplete[i] = false; + m_readbackBufferArray[i] = nullptr; } } // Loop the triple buffer index and cache the current index to the callback. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index 66c4f6d20a..500bf21628 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -293,7 +293,7 @@ namespace AZ // scope producers only can be added to the frame when frame started which cleans up previous scope producers. m_passSystem.FrameUpdate(frameGraphBuilder); - // Update View Srgs + // Update Scene and View Srgs for (auto& scenePtr : m_scenes) { scenePtr->UpdateSrgs(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 761ce20ee8..0b0481b960 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -352,6 +352,8 @@ namespace AZ { AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: Simulate"); + m_simulationTime = tickInfo.m_currentGameTime; + // If previous simulation job wasn't done, wait for it to finish. WaitAndCleanCompletionJob(m_simulationCompletion); @@ -395,6 +397,29 @@ namespace AZ } } + void Scene::ConnectEvent(PrepareSceneSrgEvent::Handler& handler) + { + handler.Connect(m_prepareSrgEvent); + } + + void Scene::PrepareSceneSrg() + { + if (m_srg) + { + // Set value for constants defined in SceneTimeSrg.azsli + RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" }); + if (timeIndex.IsValid()) + { + m_srg->SetConstant(timeIndex, m_simulationTime); + } + + // signal any handlers to update values for their partial scene srg + m_prepareSrgEvent.Signal(m_srg.get()); + + m_srg->Compile(); + } + } + void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: PrepareRender"); @@ -407,16 +432,6 @@ namespace AZ SceneNotificationBus::Event(GetId(), &SceneNotification::OnBeginPrepareRender); - { - AZ_PROFILE_SCOPE(RPI, "m_srgCallback"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "ShaderResourceGroupCallback: SrgCallback"); - // Set values for scene srg - if (m_srg && m_srgCallback) - { - m_srgCallback(m_srg.get()); - } - } - // Get active pipelines which need to be rendered and notify them frame started AZStd::vector activePipelines; { @@ -587,17 +602,14 @@ namespace AZ void Scene::UpdateSrgs() { + PrepareSceneSrg(); + for (auto& view : m_renderPacket.m_views) { view->UpdateSrg(); } } - void Scene::SetShaderResourceGroupCallback(ShaderResourceGroupCallback callback) - { - m_srgCallback = callback; - } - const RHI::ShaderResourceGroup* Scene::GetRHIShaderResourceGroup() const { if (m_srg.get()) @@ -641,11 +653,6 @@ namespace AZ return m_pipelines; } - Scene* Scene::FindSelf() - { - return this; - } - void Scene::OnSceneNotifictaionHandlerConnected(SceneNotification* handler) { for (auto renderPipeline : m_pipelines) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index f98cdce91b..36498d6d08 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -70,22 +70,6 @@ namespace MaterialEditor m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); m_scene->EnableAllFeatureProcessors(); - // Setup scene srg modification callback. - AZ::RPI::ShaderResourceGroupCallback callback = [this](AZ::RPI::ShaderResourceGroup* srg) - { - if (srg == nullptr) - { - return; - } - AZ::RHI::ShaderInputConstantIndex timeIndex = srg->FindShaderInputConstantIndex(AZ::Name{ "m_time" }); - if (timeIndex.IsValid()) - { - srg->SetConstant(timeIndex, m_simulateTime); - srg->Compile(); - } - }; - m_scene->SetShaderResourceGroupCallback(callback); - // Bind m_defaultScene to the GameEntityContext's AzFramework::Scene auto sceneSystem = AzFramework::SceneSystemInterface::Get(); AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during construction."); @@ -448,14 +432,12 @@ namespace MaterialEditor } } - void MaterialViewportRenderer::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + void MaterialViewportRenderer::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { m_renderPipeline->AddToRenderTickOnce(); PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::GatherMetrics); - m_simulateTime += deltaTime; - if (m_shadowCatcherMaterial) { // Compile the m_shadowCatcherMaterial in OnTick because changes can only be compiled once per frame. diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h index 240d66fd43..8a3e0ca4ff 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h @@ -114,8 +114,6 @@ namespace MaterialEditor AZ::Entity* m_iblEntity = nullptr; AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr; - float m_simulateTime = 0; - AZStd::shared_ptr m_viewportController; }; } // namespace MaterialEditor diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h index 4ced050fc1..c471cf90d7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h @@ -46,8 +46,6 @@ namespace AZ RPI::ViewPtr m_view = nullptr; Entity* m_modelEntity = nullptr; - double m_simulateTime = 0.0f; - float m_deltaTime = 0.0f; int m_thumbnailSize = 512; //! Incoming thumbnail requests are appended to this queue and processed one at a time in OnTick function. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp index 9a425ddeb4..f728d56bbc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp @@ -81,11 +81,8 @@ namespace AZ m_context->GetData()->m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform)); } - void CaptureStep::OnTick(float deltaTime, ScriptTimePoint time) + void CaptureStep::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) { - m_context->GetData()->m_deltaTime = deltaTime; - m_context->GetData()->m_simulateTime = time.GetSeconds(); - if (m_readyToCapture && m_ticksToCapture-- <= 0) { m_context->GetData()->m_renderPipeline->AddToRenderTickOnce(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp index 4851d8f792..46eb0937bd 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp @@ -72,34 +72,6 @@ namespace AZ data->m_scene = RPI::Scene::CreateScene(sceneDesc); - // Setup scene srg modification callback (to push per-frame values to the shaders) - RPI::ShaderResourceGroupCallback callback = [data](RPI::ShaderResourceGroup* srg) - { - if (srg == nullptr) - { - return; - } - bool needCompile = false; - RHI::ShaderInputConstantIndex timeIndex = srg->FindShaderInputConstantIndex(Name{ "m_time" }); - if (timeIndex.IsValid()) - { - srg->SetConstant(timeIndex, aznumeric_cast(data->m_simulateTime)); - needCompile = true; - } - RHI::ShaderInputConstantIndex deltaTimeIndex = srg->FindShaderInputConstantIndex(Name{ "m_deltaTime" }); - if (deltaTimeIndex.IsValid()) - { - srg->SetConstant(deltaTimeIndex, data->m_deltaTime); - needCompile = true; - } - - if (needCompile) - { - srg->Compile(); - } - }; - data->m_scene->SetShaderResourceGroupCallback(callback); - // Bind m_defaultScene to the GameEntityContext's AzFramework::Scene auto* sceneSystem = AzFramework::SceneSystemInterface::Get(); AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation."); diff --git a/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi b/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi index 38335bfc26..c5024b86ad 100644 --- a/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi +++ b/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi @@ -22,6 +22,5 @@ partial ShaderResourceGroup SceneSrg : SRG_PerScene }; #define AZ_COLLECTING_PARTIAL_SRGS -#include -#include +#include #undef AZ_COLLECTING_PARTIAL_SRGS diff --git a/Templates/DefaultProject/Template/Shaders/CommonVS.azsli b/Templates/DefaultProject/Template/Shaders/CommonVS.azsli deleted file mode 100644 index 4c20d85b88..0000000000 --- a/Templates/DefaultProject/Template/Shaders/CommonVS.azsli +++ /dev/null @@ -1,52 +0,0 @@ -// {BEGIN_LICENSE} -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -// {END_LICENSE} - -#pragma once - -#include -#include -#include - -struct VertexInput -{ - float3 m_position : POSITION; - float3 m_normal : NORMAL; - float4 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float2 m_uv : UV0; -}; - -struct VertexOutput -{ - float4 m_position : SV_Position; - float3 m_normal : NORMAL; - float3 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float2 m_uv : UV0; - float3 m_view : VIEW; -}; - -VertexOutput CommonVS(VertexInput input) -{ - float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); - float3x3 objectToWorldIT = ObjectSrg::GetWorldMatrixInverseTranspose(); - - VertexOutput output; - float3 worldPosition = mul(objectToWorld, float4(input.m_position, 1)).xyz; - output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); - - output.m_uv = input.m_uv; - - output.m_view = worldPosition - ViewSrg::m_worldPosition; - - ConstructTBN(input.m_normal, input.m_tangent, input.m_bitangent, objectToWorld, objectToWorldIT, output.m_normal, output.m_tangent, output.m_bitangent); - - return output; -} diff --git a/Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli b/Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli deleted file mode 100644 index f6422f8b2f..0000000000 --- a/Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli +++ /dev/null @@ -1,20 +0,0 @@ -// {BEGIN_LICENSE} -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -// {END_LICENSE} - -#ifndef AZ_COLLECTING_PARTIAL_SRGS -#error Do not include this file directly. Include the main .srgi file instead. -#endif - -partial ShaderResourceGroup SceneSrg -{ - float m_time; - float m_deltaTime; -} - diff --git a/Templates/DefaultProject/template.json b/Templates/DefaultProject/template.json index 6cf861a2ef..1e84ea8424 100644 --- a/Templates/DefaultProject/template.json +++ b/Templates/DefaultProject/template.json @@ -504,18 +504,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Shaders/CommonVS.azsli", - "origin": "Shaders/CommonVS.azsli", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Shaders/ShaderResourceGroups/SceneSrg.azsli", - "origin": "Shaders/ShaderResourceGroups/SceneSrg.azsli", - "isTemplated": true, - "isOptional": false - }, { "file": "autoexec.cfg", "origin": "autoexec.cfg", diff --git a/Templates/MinimalProject/Template/ShaderLib/scenesrg.srgi b/Templates/MinimalProject/Template/ShaderLib/scenesrg.srgi index 38335bfc26..c5024b86ad 100644 --- a/Templates/MinimalProject/Template/ShaderLib/scenesrg.srgi +++ b/Templates/MinimalProject/Template/ShaderLib/scenesrg.srgi @@ -22,6 +22,5 @@ partial ShaderResourceGroup SceneSrg : SRG_PerScene }; #define AZ_COLLECTING_PARTIAL_SRGS -#include -#include +#include #undef AZ_COLLECTING_PARTIAL_SRGS diff --git a/Templates/MinimalProject/Template/Shaders/CommonVS.azsli b/Templates/MinimalProject/Template/Shaders/CommonVS.azsli deleted file mode 100644 index 4c20d85b88..0000000000 --- a/Templates/MinimalProject/Template/Shaders/CommonVS.azsli +++ /dev/null @@ -1,52 +0,0 @@ -// {BEGIN_LICENSE} -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -// {END_LICENSE} - -#pragma once - -#include -#include -#include - -struct VertexInput -{ - float3 m_position : POSITION; - float3 m_normal : NORMAL; - float4 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float2 m_uv : UV0; -}; - -struct VertexOutput -{ - float4 m_position : SV_Position; - float3 m_normal : NORMAL; - float3 m_tangent : TANGENT; - float3 m_bitangent : BITANGENT; - float2 m_uv : UV0; - float3 m_view : VIEW; -}; - -VertexOutput CommonVS(VertexInput input) -{ - float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); - float3x3 objectToWorldIT = ObjectSrg::GetWorldMatrixInverseTranspose(); - - VertexOutput output; - float3 worldPosition = mul(objectToWorld, float4(input.m_position, 1)).xyz; - output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); - - output.m_uv = input.m_uv; - - output.m_view = worldPosition - ViewSrg::m_worldPosition; - - ConstructTBN(input.m_normal, input.m_tangent, input.m_bitangent, objectToWorld, objectToWorldIT, output.m_normal, output.m_tangent, output.m_bitangent); - - return output; -} diff --git a/Templates/MinimalProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli b/Templates/MinimalProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli deleted file mode 100644 index f6422f8b2f..0000000000 --- a/Templates/MinimalProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli +++ /dev/null @@ -1,20 +0,0 @@ -// {BEGIN_LICENSE} -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -// {END_LICENSE} - -#ifndef AZ_COLLECTING_PARTIAL_SRGS -#error Do not include this file directly. Include the main .srgi file instead. -#endif - -partial ShaderResourceGroup SceneSrg -{ - float m_time; - float m_deltaTime; -} - diff --git a/Templates/MinimalProject/template.json b/Templates/MinimalProject/template.json index 9d9ef730c1..21608e9204 100644 --- a/Templates/MinimalProject/template.json +++ b/Templates/MinimalProject/template.json @@ -490,18 +490,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Shaders/CommonVS.azsli", - "origin": "Shaders/CommonVS.azsli", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Shaders/ShaderResourceGroups/SceneSrg.azsli", - "origin": "Shaders/ShaderResourceGroups/SceneSrg.azsli", - "isTemplated": true, - "isOptional": false - }, { "file": "autoexec.cfg", "origin": "autoexec.cfg", From 5909e471e648b57ed3e111f50b62e3ad2af214b4 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 10 Sep 2021 10:39:42 -0700 Subject: [PATCH 14/14] Limits configuration types a project sees when using an engine SDK (#4033) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/ConfigurationTypes.cmake | 14 ++++++++++++ cmake/Configurations.cmake | 21 +++++++++++++----- cmake/Platform/Common/Install_common.cmake | 22 +++++++++++++++++-- .../install/ConfigurationType_config.cmake.in | 11 ++++++++++ cmake/install/ConfigurationTypes.cmake | 22 +++++++++++++++++++ 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 cmake/ConfigurationTypes.cmake create mode 100644 cmake/install/ConfigurationType_config.cmake.in create mode 100644 cmake/install/ConfigurationTypes.cmake diff --git a/cmake/ConfigurationTypes.cmake b/cmake/ConfigurationTypes.cmake new file mode 100644 index 0000000000..fe9c2daa51 --- /dev/null +++ b/cmake/ConfigurationTypes.cmake @@ -0,0 +1,14 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +include_guard(GLOBAL) + +# By default, CMAKE_CONFIGURATION_TYPES = LY_CONFIGURATION_TYPES, but in installed SDKs, this +# file will be replaced with cmake/install/ConfigurationTypes.cmake and discover configurations +# that are available from the SDK +set(CMAKE_CONFIGURATION_TYPES ${LY_CONFIGURATION_TYPES} CACHE STRING "" FORCE) diff --git a/cmake/Configurations.cmake b/cmake/Configurations.cmake index 693cc23c7d..51dee2f07b 100644 --- a/cmake/Configurations.cmake +++ b/cmake/Configurations.cmake @@ -8,6 +8,17 @@ include_guard(GLOBAL) +# LY_CONFIGURATION_TYPES defines all the configuration types that O3DE supports +# We dont set CMAKE_CONFIGURATION_TYPES directly because we want to be able to configure which +# configuration types are supported in an SDK installation. SDK installations will fill a +# CMAKE_CONFIGURATION_TYPES based on the configurations that were generated during the install process. +# ly_append_configurations_options depends on LY_CONFIGURATION_TYPES being +# set in order to successfully parse the arguments. Even for non-multi-config +# generators, it needs to be set. +set(LY_CONFIGURATION_TYPES "debug;profile;release" CACHE STRING "" FORCE) + +include(cmake/ConfigurationTypes.cmake) + #! ly_append_configurations_options: adds options to the different configurations (debug, profile, release, etc) # # \arg:DEFINES @@ -43,7 +54,9 @@ function(ly_append_configurations_options) ) foreach(arg IN LISTS multiArgs) list(APPEND multiValueArgs ${arg}) - foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + # we parse the parameters based on all configuration types so unknown configurations + # are not passed as values to other parameters + foreach(conf IN LISTS LY_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) list(APPEND multiValueArgs ${arg}_${UCONF}) endforeach() @@ -96,6 +109,7 @@ function(ly_append_configurations_options) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_STR}" PARENT_SCOPE) endif() + # We only iterate for the actual configuration types foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) @@ -143,11 +157,6 @@ endfunction() set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ Standard to target") ly_set(CMAKE_CXX_STANDARD_REQUIRED ON) -# ly_append_configurations_options depends on CMAKE_CONFIGURATION_TYPES being -# set in order to successfully parse the arguments. Even for non-multi-config -# generators, it needs to be set. -set(CMAKE_CONFIGURATION_TYPES "debug;profile;release" CACHE STRING "" FORCE) - get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT _isMultiConfig) # No reason set CMAKE_BUILD_TYPE if it's a multiconfig generator. diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 9a0fbbd872..86f46a6e0b 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -350,8 +350,26 @@ function(ly_setup_cmake_install) DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} PATTERN "__pycache__" EXCLUDE - REGEX "Findo3de.cmake" EXCLUDE - REGEX "Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE + PATTERN "Findo3de.cmake" EXCLUDE + PATTERN "ConfigurationTypes.cmake" EXCLUDE + REGEX "3rdParty/Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE + ) + # Connect configuration types + install(FILES "${LY_ROOT_FOLDER}/cmake/install/ConfigurationTypes.cmake" + DESTINATION cmake + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ) + # Inject code that will generate each ConfigurationType_.cmake file + set(install_configuration_type_template [=[ + configure_file(@LY_ROOT_FOLDER@/cmake/install/ConfigurationType_config.cmake.in + ${CMAKE_INSTALL_PREFIX}/cmake/ConfigurationTypes_${CMAKE_INSTALL_CONFIG_NAME}.cmake + @ONLY + ) + message(STATUS "Generated ${CMAKE_INSTALL_PREFIX}/cmake/ConfigurationTypes_${CMAKE_INSTALL_CONFIG_NAME}.cmake") + ]=]) + string(CONFIGURE "${install_configuration_type_template}" install_configuration_type @ONLY) + install(CODE "${install_configuration_type}" + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) # Transform the LY_EXTERNAL_SUBDIRS list into a json array diff --git a/cmake/install/ConfigurationType_config.cmake.in b/cmake/install/ConfigurationType_config.cmake.in new file mode 100644 index 0000000000..074e034899 --- /dev/null +++ b/cmake/install/ConfigurationType_config.cmake.in @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +include_guard(GLOBAL) + +list(APPEND CMAKE_CONFIGURATION_TYPES @CMAKE_INSTALL_CONFIG_NAME@) diff --git a/cmake/install/ConfigurationTypes.cmake b/cmake/install/ConfigurationTypes.cmake new file mode 100644 index 0000000000..709f3e71ab --- /dev/null +++ b/cmake/install/ConfigurationTypes.cmake @@ -0,0 +1,22 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +include_guard(GLOBAL) + + +# In SDK builds CMAKE_CONFIGURATION_TYPES will be filled by entries generated per configuration build. +# At install time we generate `cmake/ConfigurationTypes_.cmake` files that append the configuration +# to CMAKE_CONFIGURATION_TYPES +set(CMAKE_CONFIGURATION_TYPES "" CACHE STRING "" FORCE) + +# For the SDK case, we want to only define the confiuguration types that have been added to the SDK +file(GLOB configuration_type_files "cmake/ConfigurationTypes_*.cmake") +foreach(configuration_type_file ${configuration_type_files}) + include(${configuration_type_file}) +endforeach() +ly_set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}) # propagate to parent \ No newline at end of file