diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg
new file mode 100644
index 0000000000..f1d36d3e41
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg
@@ -0,0 +1,7 @@
+
diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg
new file mode 100644
index 0000000000..9fc9fe52c2
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg
@@ -0,0 +1,5 @@
+
diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg
new file mode 100644
index 0000000000..7a61db38e0
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg
@@ -0,0 +1,6 @@
+
diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg
new file mode 100644
index 0000000000..c6fb977c52
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg
@@ -0,0 +1,6 @@
+
diff --git a/Assets/Editor/Translation/scriptcanvas_en_us.ts b/Assets/Editor/Translation/scriptcanvas_en_us.ts
index 937f6a4d96..7b8361c879 100644
--- a/Assets/Editor/Translation/scriptcanvas_en_us.ts
+++ b/Assets/Editor/Translation/scriptcanvas_en_us.ts
@@ -8098,7 +8098,7 @@
COLOR_FROMVALUES_PARAM0_TOOLTIP
- The Red value of hte Color [0, 255]
+ The Red value of the Color [0.0-1.0]
COLOR_FROMVALUES_PARAM1_NAME
@@ -8107,7 +8107,7 @@
COLOR_FROMVALUES_PARAM1_TOOLTIP
- The Green value of the Color [0, 255]
+ The Green value of the Color [0.0-1.0]
COLOR_FROMVALUES_PARAM2_NAME
@@ -8116,7 +8116,7 @@
COLOR_FROMVALUES_PARAM2_TOOLTIP
- The Blue value of the Color [0, 255]
+ The Blue value of the Color [0.0-1.0]
COLOR_FROMVALUES_PARAM3_NAME
@@ -8125,7 +8125,7 @@
COLOR_FROMVALUES_PARAM3_TOOLTIP
- The Alpha value of the Color [0, 255]
+ The Alpha value of the Color [0.0-1.0]
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt
index 4a26500ee2..52682e70bb 100644
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt
@@ -17,5 +17,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AssetProcessorBatch
AZ::AssetProcessor
)
+
+ ly_add_pytest(
+ NAME AssetPipelineTests.Fbx_Tests
+ PATH ${CMAKE_CURRENT_LIST_DIR}/fbx_test/fbx_test.py
+ TEST_SUITE sandbox
+ RUNTIME_DEPENDENCIES
+ AZ::AssetProcessorBatch
+ AZ::AssetProcessor
+ )
endif()
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
index 2b90f53fc3..5cb61da68e 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
@@ -34,11 +34,13 @@ logger = logging.getLogger(__name__)
targetProjects = ["AutomatedTesting"]
@pytest.fixture
+@pytest.mark.SUITE_sandbox
def local_resources(request, workspace, ap_setup_fixture):
ap_setup_fixture["tests_dir"] = os.path.dirname(os.path.realpath(__file__))
@dataclass
+@pytest.mark.SUITE_sandbox
class BlackboxAssetTest:
test_name: str
asset_folder: str
@@ -338,9 +340,11 @@ blackbox_fbx_special_tests = [
@pytest.mark.usefixtures("local_resources")
@pytest.mark.parametrize("project", targetProjects)
@pytest.mark.assetpipeline
+@pytest.mark.SUITE_sandbox
class TestsFBX_AllPlatforms(object):
@pytest.mark.BAT
+ @pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests)
def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace,
ap_setup_fixture, asset_processor, project,
@@ -359,6 +363,7 @@ class TestsFBX_AllPlatforms(object):
asset_processor, project, blackbox_param)
@pytest.mark.BAT
+ @pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests)
def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self,
workspace, ap_setup_fixture,
diff --git a/Code/Framework/AzCore/AzCore/Component/Entity.cpp b/Code/Framework/AzCore/AzCore/Component/Entity.cpp
index 00c1895261..2e92873238 100644
--- a/Code/Framework/AzCore/AzCore/Component/Entity.cpp
+++ b/Code/Framework/AzCore/AzCore/Component/Entity.cpp
@@ -207,12 +207,6 @@ namespace AZ
ActivateComponent(**it);
}
- // Cache the transform interface to the transform interface
- // Generally this pattern is not recommended unless for component event buses
- // As we have a guarantee (by design) that components can't change during active state)
- // Even though technically they can connect disconnect from the bus.
- m_transform = TransformBus::FindFirstHandler(m_id);
-
SetState(State::Active);
EBUS_EVENT_ID(m_id, EntityBus, OnEntityActivated, m_id);
@@ -1320,6 +1314,19 @@ namespace AZ
return *processSignature;
}
+ AZ::TransformInterface* Entity::GetTransform() const
+ {
+ // Lazy evaluation of the cached entity transform.
+ if(!m_transform)
+ {
+ // Generally this pattern is not recommended unless for component event buses
+ // As we have a guarantee (by design) that components can't change during active state)
+ // Even though technically they can connect disconnect from the bus.
+ m_transform = TransformBus::FindFirstHandler(m_id);
+ }
+ return m_transform;
+ }
+
//=========================================================================
// MakeId
// Ids must be unique across a project at authoring time. Runtime doesn't matter
diff --git a/Code/Framework/AzCore/AzCore/Component/Entity.h b/Code/Framework/AzCore/AzCore/Component/Entity.h
index 7ec63a56ac..356533f268 100644
--- a/Code/Framework/AzCore/AzCore/Component/Entity.h
+++ b/Code/Framework/AzCore/AzCore/Component/Entity.h
@@ -354,10 +354,9 @@ namespace AZ
//! @return The Process Signature of the local machine.
static AZ::u32 GetProcessSignature();
- /// @cond EXCLUDE_DOCS
- //! @deprecated Use the TransformBus to communicate with the TransformInterface.
- inline TransformInterface* GetTransform() const { return m_transform; }
- /// @endcond
+ //! Gets the TransformInterface for the entity.
+ //! @return The TransformInterface for the entity.
+ TransformInterface* GetTransform() const;
//! Sorts an entity's components based on the dependencies between components.
//! If all dependencies are met, the required services can be activated
@@ -406,7 +405,7 @@ namespace AZ
//! A cached pointer to the transform interface.
//! We recommend using AZ::TransformBus and caching locally instead of accessing
//! the transform interface directly through this pointer.
- TransformInterface* m_transform;
+ mutable TransformInterface* m_transform;
//! A user-friendly name for the entity. This makes error messages easier to read.
AZStd::string m_name;
diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.cpp b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp
new file mode 100644
index 0000000000..94118cdfe4
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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
+
+namespace AZ::IO
+{
+ FileReader::FileReader() = default;
+
+ FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
+ {
+ Open(fileIoBase, filePath);
+ }
+
+ FileReader::~FileReader()
+ {
+ Close();
+ }
+
+ FileReader::FileReader(FileReader&& other)
+ {
+ AZStd::swap(m_file, other.m_file);
+ AZStd::swap(m_fileIoBase, other.m_fileIoBase);
+ }
+
+ FileReader& FileReader::operator=(FileReader&& other)
+ {
+ // Close the current file and take over other file
+ Close();
+ m_file = AZStd::move(other.m_file);
+ m_fileIoBase = AZStd::move(other.m_fileIoBase);
+ other.m_file = AZStd::monostate{};
+ other.m_fileIoBase = {};
+
+ return *this;
+ }
+
+ bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
+ {
+ // Close file if the FileReader has an instance open
+ Close();
+
+ if (fileIoBase != nullptr)
+ {
+ AZ::IO::HandleType fileHandle;
+ if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
+ {
+ m_file = fileHandle;
+ m_fileIoBase = fileIoBase;
+ return true;
+ }
+ }
+ else
+ {
+ AZ::IO::SystemFile file;
+ if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
+ {
+ m_file = AZStd::move(file);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool FileReader::IsOpen() const
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ return *fileHandle != AZ::IO::InvalidHandle;
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->IsOpen();
+ }
+
+ return false;
+ }
+
+ void FileReader::Close()
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr)
+ {
+ fileIo->Close(*fileHandle);
+ }
+ }
+
+ m_file = AZStd::monostate{};
+ m_fileIoBase = {};
+ }
+
+ auto FileReader::Length() const -> SizeType
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize))
+ {
+ return fileSize;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Length();
+ }
+
+ return 0;
+ }
+
+ auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
+ {
+ return bytesRead;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Read(byteSize, buffer);
+ }
+
+ return 0;
+ }
+
+ auto FileReader::Tell() const -> SizeType
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset))
+ {
+ return fileOffset;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Tell();
+ }
+
+ return 0;
+ }
+
+ bool FileReader::Seek(AZ::s64 offset, SeekType type)
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ return m_fileIoBase->Seek(*fileHandle, offset, type);
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ systemFile->Seek(offset, static_cast(type));
+ return true;
+ }
+
+ return false;
+ }
+
+ bool FileReader::Eof() const
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ return m_fileIoBase->Eof(*fileHandle);
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Eof();
+ }
+
+ return false;
+ }
+
+ bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native();
+ if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity()))
+ {
+ pathStringRef.resize_no_construct(AZStd::char_traits::length(pathStringRef.data()));
+ return true;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ filePath = systemFile->Name();
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.h b/Code/Framework/AzCore/AzCore/IO/FileReader.h
new file mode 100644
index 0000000000..4fdb18b2b2
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/IO/FileReader.h
@@ -0,0 +1,92 @@
+/*
+ * 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
+
+namespace AZ::IO
+{
+ class FileIOBase;
+ enum class SeekType : AZ::u32;
+
+ //! Structure which encapsulates delegates File Read operations
+ //! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied
+ //! to the FileSystemReader class
+ //! the SettingsRegistry option to use FileIO
+ class FileReader
+ {
+ using HandleType = AZ::u32;
+ using FileHandleType = AZStd::variant;
+ public:
+ using SizeType = AZ::u64;
+
+ //! Creates FileReader instance in the default state with no file opend
+ FileReader();
+ ~FileReader();
+
+ //! Creates a new FileReader instance and attempts to open the file at the supplied path
+ //! Uses the FileIOBase instance if supplied
+ //! @param fileIOBase pointer to fileIOBase instance
+ //! @param null-terminated filePath to open
+ FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
+
+ //! Takes ownership of the supplied FileReader handle
+ FileReader(FileReader&& other);
+
+ //! Moves ownership of FileReader handle to this instance
+ FileReader& operator=(FileReader&& other);
+
+ //! Opens a File using the FileIOBase instance if non-nullptr
+ //! Otherwise fall back to use SystemFile
+ //! @param fileIOBase pointer to fileIOBase instance
+ //! @param null-terminated filePath to open
+ //! @return true if the File is opened successfully
+ bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
+
+ //! Returns true if a file is currently open
+ //! @return true if the file is open
+ bool IsOpen() const;
+
+ //! Closes the File
+ void Close();
+
+ //! Retrieve the length of the OpenFile
+ SizeType Length() const;
+
+ //! Attempts to read up to byte size bytes into the supplied buffer
+ //! @param byteSize - Maximum number of bytes to read
+ //! @param buffer - Buffer to read bytes into
+ //! @returns the number of bytes read if the file is open, otherwise 0
+ SizeType Read(SizeType byteSize, void* buffer);
+
+ //! Returns the current file offset
+ //! @returns file offset if the file is open, otherwise 0
+ SizeType Tell() const;
+
+ //! Seeks within the open file to the offset supplied
+ //! @param offset File offset to seek to
+ //! @param type parameter to indicate the reference point to start the seek from
+ //! @returns true if the file is open and the seek succeeded
+ bool Seek(AZ::s64 offset, SeekType type);
+
+ //! Returns true if the file is open and in the EOF state
+ bool Eof() const;
+
+ //! Store the file path of the open file into the output file path parameter
+ //! The filePath reference is left unmodified, if the path was not stored
+ //! @return true if the filePath was stored
+ bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const;
+
+ private:
+
+ FileHandleType m_file;
+ AZ::IO::FileIOBase* m_fileIoBase{};
+ };
+}
diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp
index 5bff79b422..651abb89fe 100644
--- a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp
+++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp
@@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
Platform::Seek(m_handle, this, offset, mode);
}
-SystemFile::SizeType SystemFile::Tell()
+SystemFile::SizeType SystemFile::Tell() const
{
return Platform::Tell(m_handle, this);
}
-bool SystemFile::Eof()
+bool SystemFile::Eof() const
{
return Platform::Eof(m_handle, this);
}
diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.h b/Code/Framework/AzCore/AzCore/IO/SystemFile.h
index 8a5b2b2521..551ce89ce7 100644
--- a/Code/Framework/AzCore/AzCore/IO/SystemFile.h
+++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.h
@@ -72,9 +72,9 @@ namespace AZ
/// Seek in current file.
void Seek(SeekSizeType offset, SeekMode mode);
/// Get the cursor position in the current file.
- SizeType Tell();
+ SizeType Tell() const;
/// Is the cursor at the end of the file?
- bool Eof();
+ bool Eof() const;
/// Get the time the file was last modified.
AZ::u64 ModificationTime();
/// Read data from a file synchronous. Return number of bytes actually read in the buffer.
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
index 7ef1fa661d..92f546815e 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1116,118 +1117,6 @@ namespace AZ
}
}
- //! Structure which encapsulates Commands to either the FileIOBase or SystemFile classes based on
- //! the SettingsRegistry option to use FileIO
- struct SettingsRegistryFileReader
- {
- using FileHandleType = AZStd::variant;
-
- SettingsRegistryFileReader() = default;
- SettingsRegistryFileReader(bool useFileIo, const char* filePath)
- {
- Open(useFileIo, filePath);
- }
-
- ~SettingsRegistryFileReader()
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
- {
- fileIo->Close(*fileHandle);
- }
- }
- }
-
- bool Open(bool useFileIo, const char* filePath)
- {
- Close();
- if (AZ::IO::FileIOBase* fileIo = useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr)
- {
- AZ::IO::HandleType fileHandle;
- if (fileIo->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
- {
- m_file = fileHandle;
- return true;
- }
- }
- else
- {
- AZ::IO::SystemFile file;
- if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
- {
- m_file = AZStd::move(file);
- return true;
- }
- }
-
- return false;
- }
-
- bool IsOpen() const
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- return *fileHandle != AZ::IO::InvalidHandle;
- }
- else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
- {
- return systemFile->IsOpen();
- }
-
- return false;
- }
-
- void Close()
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
- {
- fileIo->Close(*fileHandle);
- }
- }
-
- m_file = AZStd::monostate{};
- }
-
- u64 Length() const
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize))
- {
- return fileSize;
- }
- }
- else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
- {
- return systemFile->Length();
- }
-
- return 0;
- }
-
- AZ::IO::SizeType Read(AZ::IO::SizeType byteSize, void* buffer)
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (AZ::u64 bytesRead{}; AZ::IO::FileIOBase::GetInstance()->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
- {
- return bytesRead;
- }
- }
- else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
- {
- return systemFile->Read(byteSize, buffer);
- }
-
- return 0;
- }
-
- FileHandleType m_file;
- };
-
bool SettingsRegistryImpl::MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey,
AZStd::vector& scratchBuffer)
{
@@ -1236,7 +1125,7 @@ namespace AZ
Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-");
- SettingsRegistryFileReader fileReader(m_useFileIo, path);
+ FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path);
if (!fileReader.IsOpen())
{
AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path);
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 5290c9b02a..da7110e36e 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -6,6 +6,8 @@
*
*/
+#include
+#include
#include
#include
#include
@@ -388,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils
const ConfigParserSettings& configParserSettings)
{
auto configPath = FindEngineRoot(registry) / filePath;
- IO::SystemFile configFile;
- if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
+ IO::FileReader configFile;
+ bool configFileOpened{};
+ switch (configParserSettings.m_fileReaderClass)
+ {
+ case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile:
+ {
+ auto fileIo = AZ::IO::FileIOBase::GetInstance();
+ configFileOpened = configFile.Open(fileIo, configPath.c_str());
+ break;
+ }
+ case ConfigParserSettings::FileReaderClass::UseSystemFileOnly:
+ {
+ configFileOpened = configFile.Open(nullptr, configPath.c_str());
+ break;
+ }
+ case ConfigParserSettings::FileReaderClass::UseFileIOOnly:
+ {
+ auto fileIo = AZ::IO::FileIOBase::GetInstance();
+ if (fileIo == nullptr)
+ {
+ return false;
+ }
+ configFileOpened = configFile.Open(fileIo, configPath.c_str());
+ break;
+ }
+ default:
+ AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied");
+ return false;
+ }
+ if (!configFileOpened)
{
AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str());
return false;
@@ -480,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ_Error("SettingsRegistryMergeUtils", false,
R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n"
R"(Parsing will halt. The line content so far is:)" "\n"
- R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(),
+ R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(),
aznumeric_cast(configBuffer.size()), configBuffer.data());
configFileParsed = false;
break;
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
index 02346c2ba1..daa64c0343 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
@@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils
//! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function
//! The structure contains a functor which returns true if a character is a valid delimiter
SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings;
+
+ //! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile
+ enum class FileReaderClass
+ {
+ UseFileIOIfAvailableFallbackToSystemFile,
+ UseSystemFileOnly,
+ UseFileIOOnly
+ };
+ FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile;
};
//! Loads basic configuration files which have structures similar to Windows INI files
//! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 6c498c3335..aa07959997 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -166,6 +166,8 @@ set(FILES
IO/FileIO.cpp
IO/FileIO.h
IO/FileIOEventBus.h
+ IO/FileReader.cpp
+ IO/FileReader.h
IO/IOUtils.h
IO/IOUtils.cpp
IO/IStreamer.h
diff --git a/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp
new file mode 100644
index 0000000000..691b3f2821
--- /dev/null
+++ b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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
+
+namespace UnitTest
+{
+ template
+ class FileReaderTestFixture
+ : public ScopedAllocatorSetupFixture
+ {
+ public:
+ void SetUp() override
+ {
+ if constexpr (AZStd::is_same_v)
+ {
+ m_fileIo = AZStd::make_unique();
+ }
+ }
+
+ void TearDown() override
+ {
+ m_fileIo.reset();
+ }
+
+ protected:
+ AZStd::unique_ptr m_fileIo{};
+ };
+
+ using FileIOTypes = ::testing::Types;
+
+ TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes);
+
+ TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully)
+ {
+ AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ EXPECT_TRUE(fileReader.IsOpen());
+ }
+
+ TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully)
+ {
+ AZ::IO::FileReader fileReader;
+ fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ EXPECT_TRUE(fileReader.IsOpen());
+ }
+
+ TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds)
+ {
+ AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ EXPECT_TRUE(fileReader.Eof());
+ }
+
+ TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds)
+ {
+ AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ AZ::IO::FixedMaxPath filePath;
+ EXPECT_TRUE(fileReader.GetFilePath(filePath));
+ AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() };
+ if (this->m_fileIo)
+ {
+ EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename));
+ }
+ EXPECT_EQ(nulFilename, filePath);
+ }
+
+} // namespace UnitTest
diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake
index d4d107f094..c36d37d874 100644
--- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake
+++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake
@@ -37,6 +37,7 @@ set(FILES
FileIOBaseTestTypes.h
Geometry2DUtils.cpp
Interface.cpp
+ IO/FileReaderTests.cpp
IO/Path/PathTests.cpp
IPC.cpp
Jobs.cpp
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp
index 469a235b9f..ec709aef8a 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp
@@ -138,7 +138,13 @@ namespace AzToolsFramework
m_indexMap[row] = index;
m_rowMap[index] = row;
++row;
- ++m_displayedItemsCounter;
+
+ // We only want to increase the displayed counter if it is a parent (Source)
+ // so we don't cut children entries.
+ if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
+ {
+ ++m_displayedItemsCounter;
+ }
}
if (model->hasChildren(index))
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp
index 37091c11e2..217e282a37 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp
@@ -29,20 +29,20 @@ namespace AzToolsFramework
{
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
: AzQtComponents::TableView(parent)
- , m_delegate(new EntryDelegate(this))
+ , m_delegate(new SearchEntryDelegate(this))
{
- setSortingEnabled(true);
+ setSortingEnabled(false);
setItemDelegate(m_delegate);
setRootIsDecorated(false);
//Styling the header aligning text to the left and using a bold font.
header()->setDefaultAlignment(Qt::AlignLeft);
- header()->setStyleSheet("QHeaderView { font-weight: bold; }");
+ header()->setStyleSheet("QHeaderView { font-weight: bold; };");
+
setContextMenuPolicy(Qt::CustomContextMenu);
setMouseTracking(true);
- setSortingEnabled(false);
setSelectionMode(QAbstractItemView::SingleSelection);
connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
@@ -67,6 +67,8 @@ namespace AzToolsFramework
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
+ header()->setSortIndicatorShown(false);
+ header()->setSectionsClickable(false);
}
void AssetBrowserTableView::SetName(const QString& name)
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
index 94e9bdc4e4..b4eca59cef 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
@@ -26,7 +26,7 @@ namespace AzToolsFramework
class AssetBrowserEntry;
class AssetBrowserTableModel;
class AssetBrowserFilterModel;
- class EntryDelegate;
+ class SearchEntryDelegate;
class AssetBrowserTableView //! Table view that displays the asset browser entries in a list.
: public AzQtComponents::TableView
@@ -67,9 +67,9 @@ namespace AzToolsFramework
private:
QString m_name;
- QPointer m_tableModel = nullptr;
- QPointer m_sourceFilterModel = nullptr;
- EntryDelegate* m_delegate = nullptr;
+ QPointer m_tableModel;
+ QPointer m_sourceFilterModel;
+ SearchEntryDelegate* m_delegate = nullptr;
private Q_SLOTS:
void OnContextMenu(const QPoint& point);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp
index 29324c612c..755c59b55b 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp
@@ -11,12 +11,13 @@
#include
#include
#include
-
+#include
#include
#include
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
+#include
#include
AZ_POP_DISABLE_WARNING
@@ -24,8 +25,13 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
- const int ENTRY_SPACING_LEFT_PIXELS = 8;
- const int ENTRY_ICON_MARGIN_LEFT_PIXELS = 2;
+ static constexpr const char* TreeIconPathFirst = "Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg";
+ static constexpr const char* TreeIconPathMiddle = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg";
+ static constexpr const char* TreeIconPathLast = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg";
+ static constexpr const char* TreeIconPathOneChild = "Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg";
+
+ const int EntrySpacingLeftPixels = 8;
+ const int EntryIconMarginLeftPixels = 2;
EntryDelegate::EntryDelegate(QWidget* parent)
: QStyledItemDelegate(parent)
@@ -62,7 +68,7 @@ namespace AzToolsFramework
// Draw main entry thumbnail.
QRect remainingRect(option.rect);
- remainingRect.adjust(ENTRY_ICON_MARGIN_LEFT_PIXELS, 0, 0, 0); // bump it rightwards to give some margin to the icon.
+ remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon.
QSize iconSize(m_iconSize, m_iconSize);
// Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size
@@ -89,7 +95,7 @@ namespace AzToolsFramework
}
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
- remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
+ remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing.
}
QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)
? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name)))
@@ -148,7 +154,162 @@ namespace AzToolsFramework
return m_iconSize;
}
- } // namespace Thumbnailer
+ SearchEntryDelegate::SearchEntryDelegate(QWidget* parent)
+ : EntryDelegate(parent)
+ {
+ LoadBranchPixMaps();
+ }
+
+ void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+ {
+ auto data = index.data(AssetBrowserModel::Roles::EntryRole);
+ if (data.canConvert())
+ {
+ bool isEnabled = (option.state & QStyle::State_Enabled) != 0;
+ bool isSelected = (option.state & QStyle::State_Selected) != 0;
+
+ QStyle* style = option.widget ? option.widget->style() : QApplication::style();
+
+ // draw the background
+ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget);
+
+ // Draw main entry thumbnail.
+ QRect remainingRect(option.rect);
+
+ QSize iconSize(m_iconSize, m_iconSize);
+ // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size
+ // so it needs to center vertically with padding in that case:
+ QPoint iconTopLeft;
+ QPoint branchIconTopLeft = QPoint();
+
+ auto entry = qvariant_cast(data);
+ auto sourceEntry = azrtti_cast(entry);
+
+ //If it is a SourceEntry or it is not the column name we don't want to add space for the branch Icon
+ if (sourceEntry || index.column() != aznumeric_cast(AssetBrowserEntry::Column::Name))
+ {
+ remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon.
+ iconTopLeft = QPoint(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
+ }
+ else
+ {
+ remainingRect.adjust(EntryIconMarginLeftPixels + m_iconSize, 0, 0, 0); // bump it rightwards to give some margin to the icon.
+ iconTopLeft = QPoint(remainingRect.x() / 2 + m_iconSize, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
+ branchIconTopLeft = QPoint((remainingRect.x() / 2) - 2, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
+ }
+
+ QPalette actualPalette(option.palette);
+
+ if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name))
+ {
+ int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
+ if (sourceEntry)
+ {
+ if (m_showSourceControl)
+ {
+ DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
+ }
+ // sources with no children should be greyed out.
+ if (sourceEntry->GetChildCount() == 0)
+ {
+ isEnabled = false; // draw in disabled style.
+ actualPalette.setCurrentColorGroup(QPalette::Disabled);
+ }
+ }
+ else
+ {
+ //Get the indexes above and below our entry to see what type are they.
+ QAbstractItemView* view = qobject_cast(option.styleObject);
+ const QAbstractItemModel* viewModel = view->model();
+
+ const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column());
+ const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column());
+
+ auto aboveEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
+ auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
+
+ auto aboveSourceEntry = azrtti_cast(aboveEntry);
+ auto belowSourceEntry = azrtti_cast(belowEntry);
+
+ // if current index is the last entry in the view
+ // or the index above it is a Source Entry and
+ // the index below is invalid or is valid but it is also a source entry
+ // then the current index is the only child.
+ if (index.row() == viewModel->rowCount() - 1 ||
+ (indexBelow.isValid() && aboveSourceEntry &&
+ (!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry))))
+ {
+ DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon
+ }
+ else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry
+ {
+ DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon
+ }
+ else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry
+ {
+ DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon
+ }
+ else //the index above and below are also child entries
+ {
+ DrawBranchPixMap(EntryBranchType::Middle, painter, branchIconTopLeft, iconSize); // Draw Default child Icon.
+ }
+ }
+
+ remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
+ remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing.
+ }
+ QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)
+ ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name)))
+ : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path)));
+
+ style->drawItemText(
+ painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString,
+ isSelected ? QPalette::HighlightedText : QPalette::Text);
+ }
+ }
+
+ void SearchEntryDelegate::LoadBranchPixMaps()
+ {
+ AZ::IO::BasicPath absoluteIconPath;
+ for (int branchType = EntryBranchType::First; branchType != EntryBranchType::Count; ++branchType)
+ {
+ QPixmap pixmap;
+ switch (branchType)
+ {
+ case AzToolsFramework::AssetBrowser::EntryBranchType::First:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathFirst;
+ break;
+ case AzToolsFramework::AssetBrowser::EntryBranchType::Middle:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathMiddle;
+ break;
+ case AzToolsFramework::AssetBrowser::EntryBranchType::Last:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast;
+ break;
+ case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild:
+ default:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild;
+ break;
+ }
+ bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str());
+ AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate");
+
+ m_branchIcons[static_cast(branchType)] = pixmap;
+
+ }
+ }
+
+ void SearchEntryDelegate::DrawBranchPixMap(
+ EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const
+ {
+ const QPixmap& pixmap = m_branchIcons[branchType];
+
+ pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ const QSize sizeDelta = size - pixmap.size();
+ const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2);
+ painter->drawPixmap(point + pointDelta, pixmap);
+ }
+
+ } // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Views/moc_EntryDelegate.cpp"
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h
index 643bedec7c..ac68c19248 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h
@@ -27,9 +27,19 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
+ //! Type of branch icon the delegate should paint.
+ enum EntryBranchType
+ {
+ First,
+ Middle,
+ Last,
+ OneChild,
+ Count
+ };
+
class AssetBrowserFilterModel;
- //! EntryDelegate draws a single item in AssetBrowser
+ //! EntryDelegate draws a single item in AssetBrowser.
class EntryDelegate
: public QStyledItemDelegate
{
@@ -52,5 +62,23 @@ namespace AzToolsFramework
//! Draw a thumbnail and return its width
int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const;
};
+
+ //! SearchEntryDelegate draws a single item in AssetBrowserTableView.
+ class SearchEntryDelegate
+ : public EntryDelegate
+ {
+ Q_OBJECT
+ public:
+ explicit SearchEntryDelegate(QWidget* parent = nullptr);
+
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+
+ private:
+ void LoadBranchPixMaps();
+ void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const;
+
+ private:
+ QMap m_branchIcons;
+ };
} // namespace AssetBrowser
} // namespace AzToolsFramework
diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss
index 30117d6636..957b2b4fa6 100644
--- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss
+++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss
@@ -498,6 +498,18 @@ QProgressBar::chunk {
font-size: 10px;
}
+/************** Gems SubWidget **************/
+
+#gemSubWidgetTitleLabel {
+ color: #FFFFFF;
+ font-size: 16px;
+}
+
+#gemSubWidgetTextLabel {
+ color: #DDDDDD;
+ font-size: 10px;
+}
+
/************** Gem Catalog (Inspector) **************/
#GemCatalogInspector {
@@ -597,3 +609,20 @@ QProgressBar::chunk {
#gemRepoInspector {
background: #444444;
}
+
+/************** Gem Repo Inspector **************/
+
+#gemRepoInspectorNameLabel {
+ font-size: 18px;
+ color: #FFFFFF;
+}
+
+#gemRepoInspectorBodyLabel {
+ font-size: 12px;
+ color: #DDDDDD;
+}
+
+#gemRepoInspectorAddInfoTitleLabel {
+ font-size: 16px;
+ color: #FFFFFF;
+}
\ No newline at end of file
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp
index 282aa2193e..7630e92e88 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp
@@ -8,6 +8,7 @@
#include
#include
+
#include
#include
#include
@@ -89,7 +90,7 @@ namespace O3DE::ProjectManager
// Additional information
m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
- m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(QString::number(m_model->GetBinarySizeInKB(modelIndex))));
+ m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex)));
m_mainWidget->adjustSize();
m_mainWidget->show();
@@ -185,27 +186,4 @@ namespace O3DE::ProjectManager
m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
}
-
- GemInspector::GemsSubWidget::GemsSubWidget(QWidget* parent)
- : QWidget(parent)
- {
- m_layout = new QVBoxLayout();
- m_layout->setAlignment(Qt::AlignTop);
- m_layout->setMargin(0);
- setLayout(m_layout);
-
- m_titleLabel = GemInspector::CreateStyledLabel(m_layout, 16, s_headerColor);
- m_textLabel = GemInspector::CreateStyledLabel(m_layout, 10, s_textColor);
- m_textLabel->setWordWrap(true);
-
- m_tagWidget = new TagContainerWidget();
- m_layout->addWidget(m_tagWidget);
- }
-
- void GemInspector::GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames)
- {
- m_titleLabel->setText(title);
- m_textLabel->setText(text);
- m_tagWidget->Update(gemNames);
- }
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h
index 0c41d1033c..ca36cef240 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h
@@ -9,10 +9,11 @@
#pragma once
#if !defined(Q_MOC_RUN)
-#include
-#include
#include
#include
+#include
+#include
+
#include
#include
#include
@@ -43,21 +44,6 @@ namespace O3DE::ProjectManager
void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
private:
- // Title, description and tag widget container used for the depending and conflicting gems
- class GemsSubWidget
- : public QWidget
- {
- public:
- GemsSubWidget(QWidget* parent = nullptr);
- void Update(const QString& title, const QString& text, const QStringList& gemNames);
-
- private:
- QLabel* m_titleLabel = nullptr;
- QLabel* m_textLabel = nullptr;
- QVBoxLayout* m_layout = nullptr;
- TagContainerWidget* m_tagWidget = nullptr;
- };
-
void InitMainWidget();
GemModel* m_model = nullptr;
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp
index 3e524d8ec8..88216398db 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp
@@ -11,10 +11,12 @@
namespace O3DE::ProjectManager
{
GemRepoInfo::GemRepoInfo(
- const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled = true)
+ const QString& name,
+ const QString& creator,
+ const QDateTime& lastUpdated,
+ bool isEnabled = true)
: m_name(name)
, m_creator(creator)
- , m_summary(summary)
, m_lastUpdated(lastUpdated)
, m_isEnabled(isEnabled)
{
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h
index 6f4f828951..14c76bd0c2 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h
@@ -19,19 +19,25 @@ namespace O3DE::ProjectManager
{
public:
GemRepoInfo() = default;
- GemRepoInfo(const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled);
+ GemRepoInfo(
+ const QString& name,
+ const QString& creator,
+ const QDateTime& lastUpdated,
+ bool isEnabled);
bool IsValid() const;
bool operator<(const GemRepoInfo& gemRepoInfo) const;
- QString m_path;
+ QString m_path = "";
QString m_name = "Unknown Gem Repo Name";
QString m_creator = "Unknown Creator";
bool m_isEnabled = false; //! Is the repo currently enabled for this engine?
QString m_summary = "No summary provided.";
- QString m_directoryLink;
- QString m_repoLink;
+ QString m_additionalInfo = "";
+ QString m_directoryLink = "";
+ QString m_repoLink = "";
+ QStringList m_includedGemPaths = {};
QDateTime m_lastUpdated;
};
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp
new file mode 100644
index 0000000000..93f5890b94
--- /dev/null
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 O3DE::ProjectManager
+{
+ GemRepoInspector::GemRepoInspector(GemRepoModel* model, QWidget* parent)
+ : QScrollArea(parent)
+ , m_model(model)
+ {
+ setObjectName("gemRepoInspector");
+ setWidgetResizable(true);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+
+ m_mainWidget = new QWidget();
+ setWidget(m_mainWidget);
+
+ m_mainLayout = new QVBoxLayout();
+ m_mainLayout->setMargin(15);
+ m_mainLayout->setAlignment(Qt::AlignTop);
+ m_mainWidget->setLayout(m_mainLayout);
+
+ InitMainWidget();
+
+ connect(m_model->GetSelectionModel(), &QItemSelectionModel::selectionChanged, this, &GemRepoInspector::OnSelectionChanged);
+ Update({});
+ }
+
+ void GemRepoInspector::OnSelectionChanged(const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected)
+ {
+ const QModelIndexList selectedIndices = selected.indexes();
+ if (selectedIndices.empty())
+ {
+ Update({});
+ return;
+ }
+
+ Update(selectedIndices[0]);
+ }
+
+ void GemRepoInspector::Update(const QModelIndex& modelIndex)
+ {
+ if (!modelIndex.isValid())
+ {
+ m_mainWidget->hide();
+ }
+
+ // Repo name and url link
+ m_nameLabel->setText(m_model->GetName(modelIndex));
+ m_repoLinkLabel->setText(m_model->GetRepoLink(modelIndex));
+ m_repoLinkLabel->SetUrl(m_model->GetRepoLink(modelIndex));
+
+ // Repo summary
+ m_summaryLabel->setText(m_model->GetSummary(modelIndex));
+ m_summaryLabel->adjustSize();
+
+ // Additional information
+ if (m_model->HasAdditionalInfo(modelIndex))
+ {
+ m_addInfoTitleLabel->show();
+ m_addInfoTextLabel->show();
+
+ m_addInfoSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ m_addInfoTextLabel->setText(m_model->GetAdditionalInfo(modelIndex));
+ }
+ else
+ {
+ m_addInfoTitleLabel->hide();
+ m_addInfoTextLabel->hide();
+
+ m_addInfoSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+ }
+
+ // Included Gems
+ m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex));
+
+ m_mainWidget->adjustSize();
+ m_mainWidget->show();
+ }
+
+ void GemRepoInspector::InitMainWidget()
+ {
+ // Repo name and url link
+ m_nameLabel = new QLabel();
+ m_nameLabel->setObjectName("gemRepoInspectorNameLabel");
+ m_mainLayout->addWidget(m_nameLabel);
+
+ m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this);
+ m_mainLayout->addWidget(m_repoLinkLabel);
+ m_mainLayout->addSpacing(5);
+
+ // Repo summary
+ m_summaryLabel = new QLabel();
+ m_summaryLabel->setObjectName("gemRepoInspectorBodyLabel");
+ m_summaryLabel->setWordWrap(true);
+ m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ m_summaryLabel->setOpenExternalLinks(true);
+ m_mainLayout->addWidget(m_summaryLabel);
+ m_mainLayout->addSpacing(20);
+
+ // Separating line
+ QFrame* hLine = new QFrame();
+ hLine->setFrameShape(QFrame::HLine);
+ hLine->setObjectName("horizontalSeparatingLine");
+ m_mainLayout->addWidget(hLine);
+ m_mainLayout->addSpacing(10);
+
+ // Additional information
+ m_addInfoTitleLabel = new QLabel();
+ m_addInfoTitleLabel->setObjectName("gemRepoInspectorAddInfoTitleLabel");
+ m_addInfoTitleLabel->setText(tr("Additional Information"));
+ m_mainLayout->addWidget(m_addInfoTitleLabel);
+
+ m_addInfoTextLabel = new QLabel();
+ m_addInfoTextLabel->setObjectName("gemRepoInspectorBodyLabel");
+ m_addInfoTextLabel->setWordWrap(true);
+ m_addInfoTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ m_addInfoTextLabel->setOpenExternalLinks(true);
+ m_mainLayout->addWidget(m_addInfoTextLabel);
+
+ // Conditional spacing for additional info section
+ m_addInfoSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
+ m_mainLayout->addSpacerItem(m_addInfoSpacer);
+
+ // Included Gems
+ m_includedGems = new GemsSubWidget();
+ m_mainLayout->addWidget(m_includedGems);
+ m_mainLayout->addSpacing(20);
+ }
+} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h
new file mode 100644
index 0000000000..a14472e6a6
--- /dev/null
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h
@@ -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
+ *
+ */
+
+#pragma once
+
+#if !defined(Q_MOC_RUN)
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#endif
+
+QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
+QT_FORWARD_DECLARE_CLASS(QLabel)
+
+namespace O3DE::ProjectManager
+{
+ class GemRepoInspector : public QScrollArea
+ {
+ Q_OBJECT // AUTOMOC
+
+ public : explicit GemRepoInspector(GemRepoModel* model, QWidget* parent = nullptr);
+ ~GemRepoInspector() = default;
+
+ void Update(const QModelIndex& modelIndex);
+
+ private slots:
+ void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
+
+ private:
+ void InitMainWidget();
+
+ GemRepoModel* m_model = nullptr;
+ QWidget* m_mainWidget = nullptr;
+ QVBoxLayout* m_mainLayout = nullptr;
+
+ // General info section
+ QLabel* m_nameLabel = nullptr;
+ LinkLabel* m_repoLinkLabel = nullptr;
+ QLabel* m_summaryLabel = nullptr;
+
+ // Additional information
+ QLabel* m_addInfoTitleLabel = nullptr;
+ QLabel* m_addInfoTextLabel = nullptr;
+ QSpacerItem* m_addInfoSpacer = nullptr;
+
+ // Included Gems
+ GemsSubWidget* m_includedGems = nullptr;
+ };
+} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp
index 519d52cb35..54d5b337e5 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp
@@ -11,13 +11,14 @@
namespace O3DE::ProjectManager
{
- GemRepoListView::GemRepoListView(QAbstractItemModel* model, QWidget* parent)
+ GemRepoListView::GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent)
: QListView(parent)
{
setObjectName("gemRepoListView");
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
setModel(model);
+ setSelectionModel(selectionModel);
setItemDelegate(new GemRepoItemDelegate(model, this));
}
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h
index 0fd5d5c180..b71b49f390 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h
@@ -10,6 +10,7 @@
#if !defined(Q_MOC_RUN)
#include
+#include
#endif
QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
@@ -22,7 +23,7 @@ namespace O3DE::ProjectManager
Q_OBJECT // AUTOMOC
public:
- explicit GemRepoListView(QAbstractItemModel* model, QWidget* parent = nullptr);
+ explicit GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr);
~GemRepoListView() = default;
};
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp
index 7a42c135e9..61ac6dc8a3 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp
@@ -7,8 +7,10 @@
*/
#include
+#include
#include
+#include
namespace O3DE::ProjectManager
{
@@ -16,6 +18,7 @@ namespace O3DE::ProjectManager
: QStandardItemModel(parent)
{
m_selectionModel = new QItemSelectionModel(this, parent);
+ m_gemModel = new GemModel(this);
}
QItemSelectionModel* GemRepoModel::GetSelectionModel() const
@@ -37,8 +40,17 @@ namespace O3DE::ProjectManager
item->setData(gemRepoInfo.m_repoLink, RoleRepoLink);
item->setData(gemRepoInfo.m_lastUpdated, RoleLastUpdated);
item->setData(gemRepoInfo.m_path, RolePath);
+ item->setData(gemRepoInfo.m_additionalInfo, RoleAdditionalInfo);
+ item->setData(gemRepoInfo.m_includedGemPaths, RoleIncludedGems);
appendRow(item);
+
+ QVector includedGemInfos = GetIncludedGemInfos(item->index());
+
+ for (const GemInfo& gemInfo : includedGemInfos)
+ {
+ m_gemModel->AddGem(gemInfo);
+ }
}
void GemRepoModel::Clear()
@@ -61,6 +73,11 @@ namespace O3DE::ProjectManager
return modelIndex.data(RoleSummary).toString();
}
+ QString GemRepoModel::GetAdditionalInfo(const QModelIndex& modelIndex)
+ {
+ return modelIndex.data(RoleAdditionalInfo).toString();
+ }
+
QString GemRepoModel::GetDirectoryLink(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleDirectoryLink).toString();
@@ -81,6 +98,45 @@ namespace O3DE::ProjectManager
return modelIndex.data(RolePath).toString();
}
+ QStringList GemRepoModel::GetIncludedGemPaths(const QModelIndex& modelIndex)
+ {
+ return modelIndex.data(RoleIncludedGems).toStringList();
+ }
+
+ QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex)
+ {
+ QStringList gemNames;
+ QVector gemInfos = GetIncludedGemInfos(modelIndex);
+
+ for (const GemInfo& gemInfo : gemInfos)
+ {
+ gemNames.append(gemInfo.m_displayName);
+ }
+
+ return gemNames;
+ }
+
+ QVector GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)
+ {
+ QVector allGemInfos;
+ QStringList repoGemPaths = GetIncludedGemPaths(modelIndex);
+
+ for (const QString& gemPath : repoGemPaths)
+ {
+ AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(gemPath);
+ if (gemInfoResult.IsSuccess())
+ {
+ allGemInfos.append(gemInfoResult.GetValue());
+ }
+ else
+ {
+ QMessageBox::critical(nullptr, tr("Gem Not Found"), tr("Cannot find info for gem %1.").arg(gemPath));
+ }
+ }
+
+ return allGemInfos;
+ }
+
bool GemRepoModel::IsEnabled(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleIsEnabled).toBool();
@@ -91,4 +147,9 @@ namespace O3DE::ProjectManager
model.setData(modelIndex, isEnabled, RoleIsEnabled);
}
+ bool GemRepoModel::HasAdditionalInfo(const QModelIndex& modelIndex)
+ {
+ return !modelIndex.data(RoleAdditionalInfo).toString().isEmpty();
+ }
+
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h
index 2f1537d339..ad139bc12b 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h
@@ -11,6 +11,7 @@
#if !defined(Q_MOC_RUN)
#include
#include
+#include
#endif
QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
@@ -32,13 +33,19 @@ namespace O3DE::ProjectManager
static QString GetName(const QModelIndex& modelIndex);
static QString GetCreator(const QModelIndex& modelIndex);
static QString GetSummary(const QModelIndex& modelIndex);
+ static QString GetAdditionalInfo(const QModelIndex& modelIndex);
static QString GetDirectoryLink(const QModelIndex& modelIndex);
static QString GetRepoLink(const QModelIndex& modelIndex);
static QDateTime GetLastUpdated(const QModelIndex& modelIndex);
static QString GetPath(const QModelIndex& modelIndex);
+ static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex);
+ static QStringList GetIncludedGemNames(const QModelIndex& modelIndex);
+ static QVector GetIncludedGemInfos(const QModelIndex& modelIndex);
+
static bool IsEnabled(const QModelIndex& modelIndex);
static void SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled);
+ static bool HasAdditionalInfo(const QModelIndex& modelIndex);
private:
enum UserRole
@@ -50,9 +57,13 @@ namespace O3DE::ProjectManager
RoleDirectoryLink,
RoleRepoLink,
RoleLastUpdated,
- RolePath
+ RolePath,
+ RoleAdditionalInfo,
+ RoleIncludedGems,
};
QItemSelectionModel* m_selectionModel = nullptr;
+
+ GemModel* m_gemModel = nullptr;
};
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp
index 82de53a0d0..c0b17904f8 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
@@ -40,10 +41,6 @@ namespace O3DE::ProjectManager
hLayout->addSpacing(60);
- m_gemRepoInspector = new QFrame(this);
- m_gemRepoInspector->setObjectName(tr("gemRepoInspector"));
- m_gemRepoInspector->setFixedWidth(240);
-
QVBoxLayout* middleVLayout = new QVBoxLayout();
middleVLayout->setMargin(0);
middleVLayout->setSpacing(0);
@@ -99,10 +96,13 @@ namespace O3DE::ProjectManager
m_gemRepoHeaderTable->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color:transparent; color:white; font-size:12px; text-align:left; border-style:none; }");
middleVLayout->addWidget(m_gemRepoHeaderTable);
- m_gemRepoListView = new GemRepoListView(m_gemRepoModel, this);
+ m_gemRepoListView = new GemRepoListView(m_gemRepoModel, m_gemRepoModel->GetSelectionModel(), this);
middleVLayout->addWidget(m_gemRepoListView);
hLayout->addLayout(middleVLayout);
+
+ m_gemRepoInspector = new GemRepoInspector(m_gemRepoModel, this);
+ m_gemRepoInspector->setFixedWidth(240);
hLayout->addWidget(m_gemRepoInspector);
Reinit();
@@ -134,7 +134,7 @@ namespace O3DE::ProjectManager
}
else
{
- QMessageBox::critical(this, tr("Operation failed"), QString("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str()));
+ QMessageBox::critical(this, tr("Operation failed"), tr("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str()));
}
}
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h
index b5316db84f..f7d943fc2a 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h
@@ -19,6 +19,7 @@ QT_FORWARD_DECLARE_CLASS(QTableWidget)
namespace O3DE::ProjectManager
{
+ QT_FORWARD_DECLARE_CLASS(GemRepoInspector)
QT_FORWARD_DECLARE_CLASS(GemRepoListView)
QT_FORWARD_DECLARE_CLASS(GemRepoModel)
@@ -40,7 +41,7 @@ namespace O3DE::ProjectManager
QTableWidget* m_gemRepoHeaderTable = nullptr;
QHeaderView* m_gemRepoListHeader = nullptr;
GemRepoListView* m_gemRepoListView = nullptr;
- QFrame* m_gemRepoInspector = nullptr;
+ GemRepoInspector* m_gemRepoInspector = nullptr;
GemRepoModel* m_gemRepoModel = nullptr;
QLabel* m_lastAllUpdateLabel;
diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp
new file mode 100644
index 0000000000..eb24008eb1
--- /dev/null
+++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+namespace O3DE::ProjectManager
+{
+ GemsSubWidget::GemsSubWidget(QWidget* parent)
+ : QWidget(parent)
+ {
+ m_layout = new QVBoxLayout();
+ m_layout->setAlignment(Qt::AlignTop);
+ m_layout->setMargin(0);
+ setLayout(m_layout);
+
+ m_titleLabel = new QLabel();
+ m_titleLabel->setObjectName("gemSubWidgetTitleLabel");
+ m_layout->addWidget(m_titleLabel);
+
+ m_textLabel = new QLabel();
+ m_textLabel->setObjectName("gemSubWidgetTextLabel");
+ m_textLabel->setWordWrap(true);
+ m_layout->addWidget(m_textLabel);
+
+ m_tagWidget = new TagContainerWidget();
+ m_layout->addWidget(m_tagWidget);
+ }
+
+ void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames)
+ {
+ m_titleLabel->setText(title);
+ m_textLabel->setText(text);
+ m_tagWidget->Update(gemNames);
+ }
+} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.h b/Code/Tools/ProjectManager/Source/GemsSubWidget.h
new file mode 100644
index 0000000000..1b10ec8861
--- /dev/null
+++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include
+#include
+#endif
+
+QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
+QT_FORWARD_DECLARE_CLASS(QLabel)
+
+namespace O3DE::ProjectManager
+{
+ // Title, description and tag widget container used for the depending and conflicting gems
+ class GemsSubWidget
+ : public QWidget
+ {
+ public:
+ GemsSubWidget(QWidget* parent = nullptr);
+ void Update(const QString& title, const QString& text, const QStringList& gemNames);
+
+ private:
+ QLabel* m_titleLabel = nullptr;
+ QLabel* m_textLabel = nullptr;
+ QVBoxLayout* m_layout = nullptr;
+ TagContainerWidget* m_tagWidget = nullptr;
+ };
+} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.cpp b/Code/Tools/ProjectManager/Source/LinkWidget.cpp
index ccee7e8ec6..9c8c78ed37 100644
--- a/Code/Tools/ProjectManager/Source/LinkWidget.cpp
+++ b/Code/Tools/ProjectManager/Source/LinkWidget.cpp
@@ -14,9 +14,10 @@
namespace O3DE::ProjectManager
{
- LinkLabel::LinkLabel(const QString& text, const QUrl& url, QWidget* parent)
+ LinkLabel::LinkLabel(const QString& text, const QUrl& url, int fontSize, QWidget* parent)
: QLabel(text, parent)
, m_url(url)
+ , m_fontSize(fontSize)
{
SetDefaultStyle();
}
@@ -33,7 +34,7 @@ namespace O3DE::ProjectManager
void LinkLabel::enterEvent([[maybe_unused]] QEvent* event)
{
- setStyleSheet("font-size: 10px; color: #94D2FF; text-decoration: underline;");
+ setStyleSheet(QString("font-size: %1px; color: #94D2FF; text-decoration: underline;").arg(m_fontSize));
}
void LinkLabel::leaveEvent([[maybe_unused]] QEvent* event)
@@ -48,6 +49,6 @@ namespace O3DE::ProjectManager
void LinkLabel::SetDefaultStyle()
{
- setStyleSheet("font-size: 10px; color: #94D2FF;");
+ setStyleSheet(QString("font-size: %1px; color: #94D2FF;").arg(m_fontSize));
}
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.h b/Code/Tools/ProjectManager/Source/LinkWidget.h
index a50007cf1f..eb0b9bb528 100644
--- a/Code/Tools/ProjectManager/Source/LinkWidget.h
+++ b/Code/Tools/ProjectManager/Source/LinkWidget.h
@@ -25,7 +25,7 @@ namespace O3DE::ProjectManager
Q_OBJECT // AUTOMOC
public:
- LinkLabel(const QString& text = {}, const QUrl& url = {}, QWidget* parent = nullptr);
+ LinkLabel(const QString& text = {}, const QUrl& url = {}, int fontSize = 10, QWidget* parent = nullptr);
void SetUrl(const QUrl& url);
@@ -40,5 +40,6 @@ namespace O3DE::ProjectManager
private:
QUrl m_url;
+ int m_fontSize;
};
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp
index 9f0c6a51ba..284ed9dcec 100644
--- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp
+++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp
@@ -954,8 +954,16 @@ namespace O3DE::ProjectManager
return AZ::Failure(result.GetError().c_str());
}
#else
- gemRepos.push_back(GemRepoInfo("JohnCreates", "John Smith", "", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true));
- gemRepos.push_back(GemRepoInfo("JanesGems", "Jane Doe", "", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false));
+ GemRepoInfo mockJohnRepo("JohnCreates", "John Smith", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true);
+ mockJohnRepo.m_summary = "John's Summary. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitudin dapibus urna";
+ mockJohnRepo.m_repoLink = "https://github.com/o3de/o3de";
+ mockJohnRepo.m_additionalInfo = "John's additional info. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitu.";
+ gemRepos.push_back(mockJohnRepo);
+
+ GemRepoInfo mockJaneRepo("JanesGems", "Jane Doe", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false);
+ mockJaneRepo.m_summary = "Jane's Summary.";
+ mockJaneRepo.m_repoLink = "https://github.com/o3de/o3de.org";
+ gemRepos.push_back(mockJaneRepo);
#endif // MOCK_GEM_REPO_INFO
std::sort(gemRepos.begin(), gemRepos.end());
diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h
index 9fd3002f93..ccf217d25b 100644
--- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h
+++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h
@@ -56,8 +56,9 @@ namespace O3DE::ProjectManager
// Gems
/**
- * Get info about a Gem
- * @param projectPath the absolute path to the Gem
+ * Get info about a Gem.
+ * @param path The absolute path to the Gem
+ * @param projectPath (Optional) The absolute path to the Gem project
* @return an outcome with GemInfo on success
*/
virtual AZ::Outcome GetGemInfo(const QString& path, const QString& projectPath = {}) = 0;
diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake
index 7a336972e0..f71ae290e7 100644
--- a/Code/Tools/ProjectManager/project_manager_files.cmake
+++ b/Code/Tools/ProjectManager/project_manager_files.cmake
@@ -27,6 +27,8 @@ set(FILES
Source/FormFolderBrowseEditWidget.cpp
Source/FormImageBrowseEditWidget.h
Source/FormImageBrowseEditWidget.cpp
+ Source/GemsSubWidget.h
+ Source/GemsSubWidget.cpp
Source/PathValidator.h
Source/PathValidator.cpp
Source/ProjectManagerWindow.h
@@ -104,6 +106,8 @@ set(FILES
Source/GemRepo/GemRepoScreen.cpp
Source/GemRepo/GemRepoInfo.h
Source/GemRepo/GemRepoInfo.cpp
+ Source/GemRepo/GemRepoInspector.h
+ Source/GemRepo/GemRepoInspector.cpp
Source/GemRepo/GemRepoItemDelegate.h
Source/GemRepo/GemRepoItemDelegate.cpp
Source/GemRepo/GemRepoListView.h
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h
index 36511ebccf..b6f6e0df11 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h
@@ -28,25 +28,19 @@ namespace AZ
size_t m_accumulatedInBytes = 0;
};
- /**
- * Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints:
- *
- * 1) Reserved <= Budget (unless the budget is 0).
- * 2) Resident <= Reserved.
- */
+ //! Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints:
+ //! 1) Reserved <= Budget (unless the budget is 0).
+ //! 2) Resident <= Reserved.
struct HeapMemoryUsage
{
HeapMemoryUsage() = default;
HeapMemoryUsage(const HeapMemoryUsage&);
HeapMemoryUsage& operator=(const HeapMemoryUsage&);
- /**
- * This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely
- * reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected.
- *
- * @param sizeInBytes The amount of bytes to reserve.
- * @return Whether the reservation was successful.
- */
+ //! This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely
+ //! reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected.
+ //! @param sizeInBytes The amount of bytes to reserve.
+ //! @return Whether the reservation was successful.
bool TryReserveMemory(size_t sizeInBytes)
{
const size_t reservationInBytes = (m_reservedInBytes += sizeInBytes);
@@ -60,45 +54,41 @@ namespace AZ
return true;
}
- /**
- * Helper function to validate sizes
- */
+ //! Helper function to validate sizes
void Validate()
{
if (Validation::IsEnabled())
{
- AZ_Assert(m_budgetInBytes >= m_reservedInBytes, "Reserved memory is larger than memory budget");
- AZ_Assert(m_reservedInBytes >= m_residentInBytes, "Resident memory is larger than reserved memory");
+ AZ_Assert(
+ m_budgetInBytes >= m_reservedInBytes,
+ "Reserved memory is larger than memory budget. Memory budget %zu Reserved %zu", m_budgetInBytes, m_reservedInBytes.load());
+ AZ_Assert(
+ m_reservedInBytes >= m_residentInBytes,
+ "Resident memory is larger than reserved memory. Reserved Memory %zu Resident memory %zu", m_reservedInBytes.load(),
+ m_residentInBytes.load());
}
}
- /**
- * The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests
- * once the budget is exceeded. A zero budget effectively disables this check. On certain platforms,
- * it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain
- * heaps.
- */
+ // The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests
+ // once the budget is exceeded. A zero budget effectively disables this check. On certain platforms,
+ // it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain
+ // heaps.
size_t m_budgetInBytes = 0;
- /**
- * Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for
- * the platform. It is validated against the budget and may not exceed it.
- */
+ // Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for
+ // the platform. It is validated against the budget and may not exceed it.
AZStd::atomic_size_t m_reservedInBytes{ 0 };
- /**
- * Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms
- * may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver.
- */
+ // Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms
+ // may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver.
AZStd::atomic_size_t m_residentInBytes{ 0 };
};
- /**
- * Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single
- * device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms
- * may not require one or the other. In this case, the memory usage / budget will report empty values for
- * that heap type.
- */
+ //!
+ //! Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single
+ //! device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms
+ //! may not require one or the other. In this case, the memory usage / budget will report empty values for
+ //! that heap type.
struct PoolMemoryUsage
{
PoolMemoryUsage() = default;
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h
index 0bd0056a0b..dc95078f80 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h
@@ -13,13 +13,12 @@ namespace AZ
{
namespace RHI
{
- /**
- * A virtual address which may be relative to a base resource. This means
- * 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value).
- * To account for this, VirtualAddress::Null is used instead. Check validity of the address
- * using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized
- * to Null, so returning the default constructor is sufficient to represent an invalid address.
- */
+
+ //! A virtual address which may be relative to a base resource. This means
+ //! 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value).
+ //! To account for this, VirtualAddress::Null is used instead. Check validity of the address
+ //! using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized
+ //! to Null, so returning the default constructor is sufficient to represent an invalid address.
class VirtualAddress
{
static const VirtualAddress Null;
@@ -29,13 +28,13 @@ namespace AZ
static VirtualAddress CreateNull();
- /// Creates a valid address with a zero offset.
+ //! Creates a valid address with a zero offset.
static VirtualAddress CreateZero();
- /// Creates an address from a pointer.
+ //! Creates an address from a pointer.
static VirtualAddress CreateFromPointer(void* ptr);
- /// Creates an address from an offset from a base pointer.
+ //! Creates an address from an offset from a base pointer.
static VirtualAddress CreateFromOffset(uint64_t offset);
inline bool IsValid() const
@@ -51,15 +50,13 @@ namespace AZ
uintptr_t m_ptr;
};
- /**
- * An allocator interface used for external GPU allocations. The allocator
- * does not manage the host memory. Instead, the user specifies a base address
- * (which may be 0, in order to allocate offsets from a base resource). The allocator
- * interface also provides an API for garbage collection. If used to manage GPU resources,
- * these are often deferred-released after N frames. The user may provide a garbage collection
- * latency, which controls the number of GarbageCollect calls that must occur before an allocation
- * is actually reclaimed. The intended use case is to garbage collect at the end of each frame.
- */
+ //! An allocator interface used for external GPU allocations. The allocator
+ //! does not manage the host memory. Instead, the user specifies a base address
+ //! (which may be 0, in order to allocate offsets from a base resource). The allocator
+ //! interface also provides an API for garbage collection. If used to manage GPU resources,
+ //! these are often deferred-released after N frames. The user may provide a garbage collection
+ //! latency, which controls the number of GarbageCollect calls that must occur before an allocation
+ //! is actually reclaimed. The intended use case is to garbage collect at the end of each frame.
class Allocator
{
public:
@@ -86,44 +83,42 @@ namespace AZ
virtual void Shutdown() = 0;
- /**
- * Allocates a virtual address relative to the base address provided at initialization time.
- * @param byteCount The number of bytes to allocate.
- * @param byteAlignement The alignment used to align the allocation.
- */
+ //! Allocates a virtual address relative to the base address provided at initialization time.
+ //! @param byteCount The number of bytes to allocate.
+ //! @param byteAlignement The alignment used to align the allocation.
virtual VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) = 0;
- /**
- * Deallocates an allocation. The memory is not reclaimed until garbage collect is called.
- * Depending on the garbage collection latency, it may take several garbage collection cycles
- * before the memory is reclaimed.
- */
+ //! Deallocates an allocation. The memory is not reclaimed until garbage collect is called.
+ //! Depending on the garbage collection latency, it may take several garbage collection cycles
+ //! before the memory is reclaimed.
virtual void DeAllocate(VirtualAddress offset) = 0;
- /// Allocations are deferred-released until a specific number of GC cycles have occurred. This
- /// is useful for allocations actively being consumed by the GPU.
+ //! Allocations are deferred-released until a specific number of GC cycles have occurred. This
+ //! is useful for allocations actively being consumed by the GPU.
virtual void GarbageCollect() = 0;
- /// Forces garbage collection of all allocations, regardless of the GC latency.
+ //! Forces garbage collection of all allocations, regardless of the GC latency.
virtual void GarbageCollectForce() = 0;
- /**
- * Returns the number of allocations active for this allocator. This includes
- * allocations that are pending garbage collection.
- */
+ //! Returns the number of allocations active for this allocator. This includes
+ //! allocations that are pending garbage collection.
virtual size_t GetAllocationCount() const { return 0; }
- /**
- * Returns the number of bytes used by the allocator. This includes
- * allocations that are pending garbage collection.
- */
+ //! Returns the number of bytes used by the allocator. This includes
+ //! allocations that are pending garbage collection.
virtual size_t GetAllocatedByteCount() const { return 0; }
- /// Returns the descriptor used to initialize the allocator.
+ //! Returns the descriptor used to initialize the allocator.
virtual const Descriptor& GetDescriptor() const = 0;
- /// Helper for converting agnostic VirtualAddress type to pointer type. Will convert
- /// VirtualAddress::Null to nullptr.
+ //! Clone the current allocator to the new allocator passed in
+ virtual void Clone([[maybe_unused]] RHI::Allocator* newAllocator)
+ {
+ AZ_Assert(false, "Not Implemented");
+ };
+
+ //! Helper for converting agnostic VirtualAddress type to pointer type. Will convert
+ //! VirtualAddress::Null to nullptr.
template
T* AllocateAs(size_t byteCount, size_t byteAlignment)
{
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h
index f9df29cf74..fb4082bb25 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h
@@ -139,6 +139,12 @@ namespace AZ
//! Notifies after all objects currently in the platform release queue are released
virtual void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) = 0;
+ //! Allows the back-ends to compact SRG related memory if applicable
+ virtual RHI::ResultCode CompactSRGMemory()
+ {
+ return RHI::ResultCode::Success;
+ };
+
protected:
DeviceFeatures m_features;
DeviceLimits m_limits;
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h
index 992cc0e79a..71f9f1605b 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h
@@ -112,6 +112,9 @@ namespace AZ
//! Returns true if Pix dll is loaded
static bool IsPixModuleLoaded();
+ //! Returns true if Warp is enabled
+ static bool UsingWarpDevice();
+
//! Returns the name of the Factory.
virtual Name GetName() = 0;
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h
index ee1cb7c7f4..ce20f9cbe5 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h
@@ -55,6 +55,7 @@ namespace AZ
size_t GetAllocationCount() const override;
size_t GetAllocatedByteCount() const override;
const Descriptor& GetDescriptor() const override;
+ void Clone(RHI::Allocator* newAllocator) override;
//////////////////////////////////////////////////////////////////////////
private:
diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
index ab45d2239b..ef330e7902 100644
--- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
@@ -155,11 +155,6 @@ namespace AZ
{
arguments += " -Zpr";
}
- if (m_dxcGenerateDebugInfo)
- {
- arguments += " -Zi"; // Generate debug information
- arguments += " -Zss"; // Compute Shader Hash considering source information
- }
// strip spaces at both sides
AZStd::string dxcAdditionalFreeArguments = m_dxcAdditionalFreeArguments;
AzFramework::StringFunc::TrimWhiteSpace(dxcAdditionalFreeArguments, true, true);
diff --git a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp
index 48b64c17c0..82b0a13c86 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp
@@ -8,12 +8,12 @@
#include
#include
+#include
#include
#include
#if defined(USE_RENDERDOC) || defined(USE_PIX)
#include
-#include
#include
#endif
@@ -28,6 +28,8 @@ static AZStd::unique_ptr s_pixModule;
static bool s_isPixGpuCaptureDllLoaded = false;
#endif
+static bool s_usingWarpDevice = false;
+
namespace AZ
{
namespace RHI
@@ -55,6 +57,8 @@ namespace AZ
Factory::Factory()
{
+ AZStd::string preferredUserAdapterName = RHI::GetCommandLineValue("forceAdapter");
+ s_usingWarpDevice = preferredUserAdapterName == "Microsoft Basic Render Driver";
#if defined(USE_RENDERDOC)
// If RenderDoc is requested, we need to load the library as early as possible (before device queries/factories are made)
bool enableRenderDoc = RHI::QueryCommandLineOption("enableRenderDoc");
@@ -197,5 +201,10 @@ namespace AZ
return false;
#endif
}
+
+ bool Factory::UsingWarpDevice()
+ {
+ return s_usingWarpDevice;
+ }
}
}
diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp
index df887379fe..5d2feb1e34 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp
@@ -314,6 +314,11 @@ namespace AZ
resourcePoolDatabase.ForEachShaderResourceGroupPool(compileAllLambda);
}
+
+ //It is possible for certain back ends to run out of SRG memory (due to fragmentation) in which case
+ //we try to compact and re-compile SRGs.
+ RHI::ResultCode resultCode = m_device->CompactSRGMemory();
+ AZ_Assert(resultCode == RHI::ResultCode::Success, "SRG compaction failed and this can lead to a gpu crash.");
}
void FrameScheduler::BuildRayTracingShaderTables()
diff --git a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp
index c646700495..77877b0020 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp
@@ -344,5 +344,17 @@ namespace AZ
handle = node.m_nextFree;
}
}
+
+ void FreeListAllocator::Clone(RHI::Allocator* newAllocator)
+ {
+ FreeListAllocator* newFreeListAllocator = static_cast(newAllocator);
+ newFreeListAllocator->m_headHandle = m_headHandle;
+ newFreeListAllocator->m_nodeFreeList = m_nodeFreeList;
+ newFreeListAllocator->m_nodes = m_nodes;
+ newFreeListAllocator->m_allocations = m_allocations;
+ newFreeListAllocator->m_garbage = m_garbage;
+ newFreeListAllocator->m_garbageCollectCycle = m_garbageCollectCycle;
+ newFreeListAllocator->m_byteCountTotal = m_byteCountTotal;
+ }
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h
index 1f22599b38..63c2d69ea2 100644
--- a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h
+++ b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h
@@ -64,6 +64,12 @@ namespace AZ
//! int array: Max count for descriptors
AZStd::unordered_map> m_descriptorHeapLimits;
+ // Number of max static handles for shader visible srv/uav/cbv views
+ uint32_t m_numShaderVisibleCbvSrvUavStaticHandles = 2000;
+
+ //Bool to indicate allowing compaction of shader visible srv/uav/cbv heap in case of fragmentation
+ bool m_allowDescriptorHeapCompaction = false;
+
FrameGraphExecuterData m_frameGraphExecuterData;
void LoadPlatformLimitsDescriptor(const char* rhiName) override;
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
index 89ae3ede46..f30fc72ace 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
@@ -255,6 +255,11 @@ namespace AZ
// Compilation parameters
AZStd::string params = shaderCompilerArguments.MakeAdditionalDxcCommandLineString();
+ if (BuildHasDebugInfo(shaderCompilerArguments))
+ {
+ params += " -Zi"; // Generate debug information
+ params += " -Zss"; // Compute Shader Hash considering source information
+ }
// Enable half precision types when shader model >= 6.2
int shaderModelMajor = 0;
@@ -281,12 +286,11 @@ namespace AZ
AZStd::string symbolDatabaseFileCliArgument{" "}; // when not debug: still insert a space between 5.dxil and 7.hlsl-in
if (BuildHasDebugInfo(shaderCompilerArguments))
{
- // prepare .ldd filename:
+ // prepare .pdb filename:
AZStd::string md5hex = RHI::ByteToHexString(md5);
AZStd::string symbolDatabaseFilePath = dxcInputFile.c_str(); // mutate from source
- AZStd::string lldFileName = md5hex // lld is like pdb but it's the default symbol database extension in dxc
- + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps...
- AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, lldFileName.c_str(), "lld");
+ AZStd::string pdbFileName = md5hex + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps...
+ AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, pdbFileName.c_str(), "pdb");
// it is possible that another activated platform/profile, already exported that file. (since it's hashed on the source file)
// dxc returns an error in such case. we get less surprising effets by just not mentionning an -Fd argument
if (AZ::IO::SystemFile::Exists(symbolDatabaseFilePath.c_str()))
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp
index 77b41d6ffc..fd9ad84511 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp
@@ -19,8 +19,10 @@ namespace AZ
if (SerializeContext* serializeContext = azrtti_cast(context))
{
serializeContext->Class()
- ->Version(0)
+ ->Version(1)
->Field("DescriptorHeapLimits", &PlatformLimitsDescriptor::m_descriptorHeapLimits)
+ ->Field("NumShaderVisibleCbvSrvUavStaticHandles", &PlatformLimitsDescriptor::m_numShaderVisibleCbvSrvUavStaticHandles)
+ ->Field("AllowDescriptorHeapCompaction", &PlatformLimitsDescriptor::m_allowDescriptorHeapCompaction)
->Field("FrameGraphExecuterData", &PlatformLimitsDescriptor::m_frameGraphExecuterData)
;
}
@@ -54,7 +56,7 @@ namespace AZ
// Map default value must be initialized after attempting to serialize (and result in failure).
// Otherwise, serialization won't overwrite the default values.
m_descriptorHeapLimits = AZStd::unordered_map>({
- { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 1000000, 1000000 } },
+ { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 100000, 1000000 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_SAMPLER"), { 2048, 2048 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_RTV"), { 2048, 0 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_DSV"), { 2048, 0 } }
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h
index 443ab3e73a..1472cdc80e 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h
@@ -438,7 +438,7 @@ namespace AZ
switch (pipelineType)
{
case RHI::PipelineStateType::Draw:
- if (binding.m_resourceTable.IsValid())
+ if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr)
{
GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle);
}
@@ -448,14 +448,15 @@ namespace AZ
GetCommandList()->SetGraphicsRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress);
}
- if (binding.m_samplerTable.IsValid())
+ if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr)
{
GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle);
}
for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex)
{
- if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid())
+ if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() &&
+ compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr)
{
GetCommandList()->SetGraphicsRootDescriptorTable(
binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(),
@@ -465,7 +466,7 @@ namespace AZ
break;
case RHI::PipelineStateType::Dispatch:
- if (binding.m_resourceTable.IsValid())
+ if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr)
{
GetCommandList()->SetComputeRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle);
}
@@ -475,14 +476,15 @@ namespace AZ
GetCommandList()->SetComputeRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress);
}
- if (binding.m_samplerTable.IsValid())
+ if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr)
{
GetCommandList()->SetComputeRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle);
}
for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex)
{
- if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid())
+ if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() &&
+ compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr)
{
GetCommandList()->SetComputeRootDescriptorTable(
binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(),
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp
index efa9518ee1..9733045179 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp
@@ -50,7 +50,7 @@ namespace AZ
void CommandListBase::SetNameInternal(const AZStd::string_view& name)
{
- AZStd::wstring wname;
+ AZStd::fixed_wstring<256> wname;
AZStd::to_wstring(wname, name.data());
GetCommandList()->SetName(wname.data());
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp
index 9bf25d8bc6..012784d3fc 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp
@@ -39,7 +39,7 @@ namespace AZ
{
Device& device = static_cast(deviceBase);
m_currentFrameIndex = 0;
- m_frameFences.resize(RHI::Limits::Device::FrameCountMax - 1);
+ m_frameFences.resize(RHI::Limits::Device::FrameCountMax);
for (FenceSet& fences : m_frameFences)
{
fences.Init(device.GetDevice(), RHI::FenceState::Signaled);
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp
index 635c66f1cd..1b0df5846c 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp
@@ -10,7 +10,9 @@
#include
#include
#include
+#include
#include
+#include
namespace AZ
{
@@ -40,7 +42,7 @@ namespace AZ
for (D3D12_SRV_DIMENSION dimension : validSRVDimensions)
{
- DescriptorHandle srvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ DescriptorHandle srvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {};
desc.Format = DXGI_FORMAT_R32_UINT;
@@ -62,7 +64,7 @@ namespace AZ
for (D3D12_UAV_DIMENSION dimension : UAVDimensions)
{
- DescriptorHandle uavDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ DescriptorHandle uavDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {};
desc.Format = DXGI_FORMAT_R32_UINT;
@@ -75,14 +77,14 @@ namespace AZ
void DescriptorContext::CreateNullDescriptorsCBV()
{
D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferDesc = {};
- DescriptorHandle cbvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ DescriptorHandle cbvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
m_device->CreateConstantBufferView(&constantBufferDesc, GetCpuPlatformHandle(cbvDescriptorHandle));
m_nullDescriptorCBV = cbvDescriptorHandle;
}
void DescriptorContext::CreateNullDescriptorsSampler()
{
- m_nullSamplerDescriptor = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ m_nullSamplerDescriptor = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
D3D12_SAMPLER_DESC samplerDesc = {};
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
@@ -102,7 +104,7 @@ namespace AZ
AZ_Assert(platformLimitsDescriptor.get(), "Platform limits information is missing");
m_platformLimitsDescriptor = platformLimitsDescriptor;
-
+ m_allowDescriptorHeapCompaction = m_platformLimitsDescriptor->m_allowDescriptorHeapCompaction;
for (const auto& itr : platformLimitsDescriptor->m_descriptorHeapLimits)
{
for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx)
@@ -114,11 +116,33 @@ namespace AZ
if (descriptorCountMax)
{
- GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax);
+ if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flags))
+ {
+ //Init the two heaps to help support compaction after fragmentation
+ m_shaderVisibleCbvSrvUavPools[0].Init(
+ m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
+ descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles);
+
+ m_shaderVisibleCbvSrvUavPools[1].Init(
+ m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
+ descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles);
+ }
+ else
+ {
+ GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax, descriptorCountMax);
+ }
}
}
}
-
+
+ if (m_allowDescriptorHeapCompaction)
+ {
+ m_backupStaticHandles.Init(
+ m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
+ platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles,
+ platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles);
+ }
+
CreateNullDescriptors();
}
@@ -129,7 +153,7 @@ namespace AZ
{
if (constantBufferView.IsNull())
{
- constantBufferView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ constantBufferView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(constantBufferView);
@@ -145,7 +169,7 @@ namespace AZ
{
if (shaderResourceView.IsNull())
{
- shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView);
@@ -165,7 +189,7 @@ namespace AZ
{
if (unorderedAccessView.IsNull())
{
- unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView);
@@ -176,7 +200,24 @@ namespace AZ
// Copy the UAV descriptor into the GPU-visible version for clearing.
if (unorderedAccessViewClear.IsNull())
{
- unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset();
+ unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1);
+
+ if (unorderedAccessViewClear.IsNull())
+ {
+ AZ_Assert(
+ false,
+ "Descriptor heap ran out of memory for static handles. Please consider increasing the value of NumShaderVisibleCbvSrvUavStaticHandles"
+ "within platformlimits.azasset file for dx12.");
+ return;
+ }
+
+ if (m_allowDescriptorHeapCompaction)
+ {
+ //We make a copy of static handles in case we need to compact and recreate the shader visible heap
+ m_device->CopyDescriptorsSimple(
+ 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor,
+ unorderedAccessViewClear.m_type);
+ }
}
CopyDescriptor(unorderedAccessViewClear, unorderedAccessView);
}
@@ -188,7 +229,7 @@ namespace AZ
{
if (shaderResourceView.IsNull())
{
- shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView);
@@ -205,7 +246,7 @@ namespace AZ
{
if (unorderedAccessView.IsNull())
{
- unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView);
@@ -216,7 +257,24 @@ namespace AZ
// Copy the UAV descriptor into the GPU-visible version for clearing.
if (unorderedAccessViewClear.IsNull())
{
- unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset();
+ unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1);
+
+ if (unorderedAccessViewClear.IsNull())
+ {
+ AZ_Assert(
+ false,
+ "Descriptor heap ran out of memory for static handles. Please consider increasing the value of "
+ "NumShaderVisibleCbvSrvUavStaticHandles within platformlimits.azasset file for dx12.");
+ return;
+ }
+
+ if (m_allowDescriptorHeapCompaction)
+ {
+ // We make a copy of static handles in case we need to compact and recreate the shader visible heap
+ m_device->CopyDescriptorsSimple(
+ 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor,
+ unorderedAccessViewClear.m_type);
+ }
}
CopyDescriptor(unorderedAccessViewClear, unorderedAccessView);
}
@@ -228,7 +286,7 @@ namespace AZ
{
if (renderTargetView.IsNull())
{
- renderTargetView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ renderTargetView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor = GetCpuPlatformHandle(renderTargetView);
@@ -245,13 +303,13 @@ namespace AZ
{
if (depthStencilView.IsNull())
{
- depthStencilView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ depthStencilView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor = GetCpuPlatformHandle(depthStencilView);
if (depthStencilReadView.IsNull())
{
- depthStencilReadView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ depthStencilReadView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilReadDescriptor = GetCpuPlatformHandle(depthStencilReadView);
@@ -274,7 +332,7 @@ namespace AZ
{
if (samplerHandle.IsNull())
{
- samplerHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
+ samplerHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_SAMPLER_DESC samplerDesc;
@@ -286,17 +344,49 @@ namespace AZ
{
if (!descriptorHandle.IsNull())
{
- ReleaseDescriptorTable(DescriptorTable(descriptorHandle, 1));
+ GetPool(descriptorHandle.m_type, descriptorHandle.m_flags).ReleaseHandle(descriptorHandle);
}
}
DescriptorTable DescriptorContext::CreateDescriptorTable(
- D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType,
- uint32_t descriptorCount)
+ D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg)
{
- return Allocate(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, descriptorCount);
+ if (m_allowDescriptorHeapCompaction && !m_compactionInProgress)
+ {
+ // Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap
+ AZStd::scoped_lock lock{ m_srgMapMutex };
+ auto iter = m_srgAllocations.find(srg);
+ if (iter == m_srgAllocations.end())
+ {
+ m_srgAllocations.emplace(srg, 1);
+ }
+ else
+ {
+ m_srgAllocations[srg]++;
+ }
+ }
+
+ return GetPool(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE).AllocateTable(descriptorCount);
}
+ void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table, ShaderResourceGroup* srg)
+ {
+ if (m_allowDescriptorHeapCompaction && !m_compactionInProgress)
+ {
+ //Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap
+ AZStd::scoped_lock lock{ m_srgMapMutex };
+ auto iter = m_srgAllocations.find(srg);
+ AZ_Assert(iter != m_srgAllocations.end(), "Srg entry not found");
+ m_srgAllocations[srg]--;
+ if (m_srgAllocations[srg] == 0)
+ {
+ m_srgAllocations.erase(srg);
+ }
+ }
+
+ GetPool(table.GetType(), table.GetFlags()).ReleaseTable(table);
+ }
+
void DescriptorContext::UpdateDescriptorTableRange(
DescriptorTable gpuDestinationTable,
const DescriptorHandle* cpuSourceDescriptors,
@@ -313,14 +403,12 @@ namespace AZ
}
// Resolve destination descriptor to platform handle.
- D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandle(gpuDestinationTable.GetOffset());
+ D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandleForTable(gpuDestinationTable);
// An array of descriptor sizes for each range. We just want N ranges with 1 descriptor each.
AZStd::vector rangeCounts(DescriptorCount, 1);
- /**
- * We are gathering N source descriptors into a contiguous destination table.
- */
+ //We are gathering N source descriptors into a contiguous destination table.
m_device->CopyDescriptors(
1, // Number of destination ranges.
&gpuDestinationHandle, // Destination range array.
@@ -353,19 +441,24 @@ namespace AZ
}
}
}
+
+ if (m_allowDescriptorHeapCompaction)
+ {
+ m_backupStaticHandles.GarbageCollect();
+ }
}
- DescriptorTable DescriptorContext::Allocate(
+ DescriptorTable DescriptorContext::AllocateTable(
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t count)
{
- return GetPool(type, flags).Allocate(count);
+ return GetPool(type, flags).AllocateTable(count);
}
- void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table)
+ DescriptorHandle DescriptorContext::AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count)
{
- GetPool(table.GetType(), table.GetFlags()).Release(table);
+ return GetPool(type, flags).AllocateHandle(count);
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandle(DescriptorHandle handle) const
@@ -378,6 +471,16 @@ namespace AZ
return GetPool(handle.m_type, handle.m_flags).GetGpuPlatformHandle(handle);
}
+ D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandleForTable(DescriptorTable descTable) const
+ {
+ return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetCpuPlatformHandleForTable(descTable);
+ }
+
+ D3D12_GPU_DESCRIPTOR_HANDLE DescriptorContext::GetGpuPlatformHandleForTable(DescriptorTable descTable) const
+ {
+ return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetGpuPlatformHandleForTable(descTable);
+ }
+
DescriptorHandle DescriptorContext::GetNullHandleSRV(D3D12_SRV_DIMENSION dimension) const
{
auto iter = m_nullDescriptorsSRV.find(dimension);
@@ -431,14 +534,88 @@ namespace AZ
{
AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type);
AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag);
- return m_pools[type][flag];
+
+ if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag))
+ {
+ return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex];
+ }
+ else
+ {
+ return m_pools[type][flag];
+ }
}
const DescriptorPool& DescriptorContext::GetPool(uint32_t type, uint32_t flag) const
{
AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type);
AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag);
- return m_pools[type][flag];
+ if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag))
+ {
+ return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex];
+ }
+ else
+ {
+ return m_pools[type][flag];
+ }
+ }
+
+ RHI::ResultCode DescriptorContext::CompactDescriptorHeap()
+ {
+ //Check if heap compaction is enabled by the user. Since there is an overhead associated with heap compaction it is not enabled by default
+ if(!m_allowDescriptorHeapCompaction)
+ {
+ AZ_Assert(
+ false,
+ "Descriptor heap Compaction not allowed. Please consider increasing number of handles allowed for the second value"
+ "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV or enabling AllowDescriptorHeapCompaction within platformlimits.azasset file for dx12.");
+ return RHI::ResultCode::OutOfMemory;
+ }
+
+ //We need to ping-pong between two heaps as we cannot compact the active heap without updating it and that is not allowed as
+ //we need to keep that gpu memory untouched until GPU is finished consuming which can take up to 3 frames.
+ m_compactionInProgress = true;
+ DescriptorPool& srcPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
+
+ //Update the currently active heap index
+ m_currentHeapIndex = !m_currentHeapIndex;
+ DescriptorPool& destPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
+
+ //Copy over all the static handles first
+ for (size_t i = 0; i < m_platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles; i++)
+ {
+ DescriptorHandle srcHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, static_cast(i));
+ DescriptorHandle destHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast(i));
+ m_device->CopyDescriptorsSimple(1, destPool.GetCpuPlatformHandle(destHandle), m_backupStaticHandles.GetCpuPlatformHandle(srcHandle), destHandle.m_type);
+ }
+
+ //Clone the allocator of the source pool into the destination pool
+ srcPool.CloneAllocator(destPool.GetAllocator());
+
+ {
+ //The mutex is here 'just in case' Compaction is called from more than one thread.
+ AZStd::scoped_lock lock{ m_srgMapMutex };
+ //Re-update all the descriptor tables associated with active SRGs
+ for (const auto& [srg, numAllocations] : m_srgAllocations)
+ {
+ RHI::ResultCode resultCode = static_cast(srg->GetPool())->UpdateDescriptorTableAfterCompaction(*srg, srg->GetData());
+ if (resultCode != RHI::ResultCode::Success)
+ {
+ return resultCode;
+ }
+ }
+ }
+
+ //Clear the allocator of the source pool
+ srcPool.ClearAllocator();
+
+ m_compactionInProgress = false;
+
+ return RHI::ResultCode::Success;
+ }
+
+ bool DescriptorContext::IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const
+ {
+ return type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV && flag == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
}
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h
index d4b6479fb2..88d2781329 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h
@@ -15,6 +15,8 @@
#include
#include
#include
+#include
+#include
namespace AZ
{
@@ -82,12 +84,14 @@ namespace AZ
//! Creates a GPU-visible descriptor table.
//! @param descriptorHeapType The descriptor heap to allocate from.
//! @param descriptorCount The number of descriptors to allocate.
+ //! @param srg Shader resource group with which the descriptor table is associated with
DescriptorTable CreateDescriptorTable(
- D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType,
- uint32_t descriptorCount);
-
- void ReleaseDescriptorTable(DescriptorTable descriptorTable);
+ D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg);
+ //! Releases a GPU-visible descriptor table.
+ //! @param descriptorHeapType The descriptor heap to allocate from.
+ //! @param srg Shader resource group with which the descriptor table is associated with
+ void ReleaseDescriptorTable(DescriptorTable descriptorTable, ShaderResourceGroup* srg);
//! Performs a gather of disjoint CPU-side descriptors and copies to a contiguous GPU-side descriptor table.
//! @param gpuDestinationTable The destination descriptor table that the descriptors will be uploaded to.
@@ -110,6 +114,8 @@ namespace AZ
D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const;
D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const;
+ D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable descTable) const;
+ D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable descTable) const;
void SetDescriptorHeaps(ID3D12GraphicsCommandList* commandList) const;
@@ -117,6 +123,12 @@ namespace AZ
ID3D12DeviceX* GetDevice();
+ //! Since we are only allowed one shader visible CbvSrvUav heap of a limited size in certain hardware, it is possible that
+ //! it can get fragmented by constant alloc/de-alloc of descriptor tables related to direct views or unbounded resource views within a SRG. We use two
+ //! heaps to ping pong during compaction as fragmentation can occur many times. It copies static handles directly and for all the
+ //! dynamic handles we re-update the new heap by copying over the handles from the 'non-shader visible' heap.
+ RHI::ResultCode CompactDescriptorHeap();
+
private:
void CopyDescriptor(DescriptorHandle dst, DescriptorHandle src);
@@ -129,10 +141,13 @@ namespace AZ
DescriptorPool& GetPool(uint32_t type, uint32_t flag);
const DescriptorPool& GetPool(uint32_t type, uint32_t flag) const;
- DescriptorTable Allocate(
- D3D12_DESCRIPTOR_HEAP_TYPE type,
- D3D12_DESCRIPTOR_HEAP_FLAGS flags,
- uint32_t count);
+ //! Allocates a Descriptor table which describes a contiguous range of descriptor handles
+ DescriptorTable AllocateTable(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count);
+
+ //! Allocates a single descriptor handle
+ DescriptorHandle AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count);
+
+ bool IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const;
static const uint32_t NumHeapFlags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE + 1;
static const uint32_t s_descriptorCountMax[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES][NumHeapFlags];
@@ -147,6 +162,25 @@ namespace AZ
DescriptorHandle m_nullSamplerDescriptor;
RHI::ConstPtr m_platformLimitsDescriptor;
+
+ // Use 2 heaps below in order to ping-pong between shader visible CbvSrvUav heap when one of them fragments and run out of memory.
+ static const uint32_t MaxShaderVisibleCbvSrvUavHeaps = 2;
+ DescriptorPoolShaderVisibleCbvSrvUav m_shaderVisibleCbvSrvUavPools[MaxShaderVisibleCbvSrvUavHeaps];
+ //This pool stores a copy of static handles that can later be used to recreate the compacted shader visible CbvSrvUav heap.
+ DescriptorPool m_backupStaticHandles;
+
+ //Boolean to dictate when compaction was in progress
+ bool m_compactionInProgress = false;
+
+ //Boolean to dictate if we should support compaction for shader visible CbvSrvUav heap
+ bool m_allowDescriptorHeapCompaction = false;
+
+ //Map to store active SRGs and the number of associated descriptor tables. This is used to recreate the new compacted heap when we switch heaps
+ AZStd::unordered_map m_srgAllocations;
+ AZStd::mutex m_srgMapMutex;
+
+ //Index that holds the currently active shader visible CbvSrvUav heap
+ uint32_t m_currentHeapIndex = 0;
};
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp
index 7a6d96a519..96a3979f41 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp
@@ -18,34 +18,38 @@ namespace AZ
ID3D12DeviceX* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
- uint32_t descriptorCount)
+ uint32_t descriptorCountForHeap,
+ uint32_t descriptorCountForAllocator)
{
- m_Desc.Type = type;
- m_Desc.Flags = flags;
- m_Desc.NumDescriptors = descriptorCount;
- m_Desc.NodeMask = 1;
+ m_desc.Type = type;
+ m_desc.Flags = flags;
+ m_desc.NumDescriptors = descriptorCountForHeap;
+ m_desc.NodeMask = 1;
ID3D12DescriptorHeap* heap;
- DX12::AssertSuccess(device->CreateDescriptorHeap(&m_Desc, IID_GRAPHICS_PPV_ARGS(&heap)));
+ DX12::AssertSuccess(device->CreateDescriptorHeap(&m_desc, IID_GRAPHICS_PPV_ARGS(&heap)));
heap->SetName(L"DescriptorHeap");
- m_DescriptorHeap.Attach(heap);
- m_Stride = device->GetDescriptorHandleIncrementSize(m_Desc.Type);
+ m_descriptorHeap.Attach(heap);
+ m_stride = device->GetDescriptorHandleIncrementSize(m_desc.Type);
- m_CpuStart = heap->GetCPUDescriptorHandleForHeapStart();
- m_GpuStart = {};
-
- if (RHI::CheckBitsAny(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE))
- {
- m_GpuStart = heap->GetGPUDescriptorHandleForHeapStart();
- }
+ m_cpuStart = heap->GetCPUDescriptorHandleForHeapStart();
+ m_gpuStart = {};
const bool isGpuVisible = RHI::CheckBitsAll(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
+ if (isGpuVisible)
+ {
+ m_gpuStart = heap->GetGPUDescriptorHandleForHeapStart();
+ }
+
if (isGpuVisible)
{
RHI::FreeListAllocator::Descriptor descriptor;
descriptor.m_alignmentInBytes = 1;
- descriptor.m_capacityInBytes = descriptorCount;
+
+ //It is possible for descriptorCountForAllocator to not match descriptorCountForHeap for DescriptorPoolShaderVisibleCbvSrvUav
+ //heaps in which case descriptorCountForAllocator defines the number of static handles
+ descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator);
descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax;
RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator();
@@ -56,10 +60,11 @@ namespace AZ
{
// Non-shader-visible heaps don't require contiguous descriptors. Therefore, we can allocate
// them using a block allocator.
+
RHI::PoolAllocator::Descriptor descriptor;
descriptor.m_alignmentInBytes = 1;
descriptor.m_elementSize = 1;
- descriptor.m_capacityInBytes = descriptorCount;
+ descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator);
descriptor.m_garbageCollectLatency = 0;
RHI::PoolAllocator* allocator = aznew RHI::PoolAllocator();
@@ -68,7 +73,7 @@ namespace AZ
}
}
- DescriptorTable DescriptorPool::Allocate(uint32_t count)
+ DescriptorHandle DescriptorPool::AllocateHandle(uint32_t count)
{
RHI::VirtualAddress address;
{
@@ -78,24 +83,34 @@ namespace AZ
if (address.IsValid())
{
- DescriptorHandle handle(m_Desc.Type, m_Desc.Flags, static_cast(address.m_ptr));
- return DescriptorTable(handle, static_cast(count));
+ DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr));
+ return handle;
}
else
{
- return DescriptorTable{};
+ return DescriptorHandle{};
}
}
- void DescriptorPool::Release(DescriptorTable table)
+ void DescriptorPool::ReleaseHandle(DescriptorHandle handle)
{
- if (table.IsNull())
+ if (handle.IsNull())
{
return;
}
AZStd::lock_guard lock(m_mutex);
- m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index));
+ m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(handle.m_index));
+ }
+
+ DescriptorTable DescriptorPool::AllocateTable(uint32_t count)
+ {
+ return DescriptorTable(AllocateHandle(count), static_cast(count));
+ }
+
+ void DescriptorPool::ReleaseTable(DescriptorTable table)
+ {
+ ReleaseHandle(table.GetOffset());
}
void DescriptorPool::GarbageCollect()
@@ -106,20 +121,134 @@ namespace AZ
ID3D12DescriptorHeap* DescriptorPool::GetPlatformHeap() const
{
- return m_DescriptorHeap.Get();
+ return m_descriptorHeap.Get();
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandle(DescriptorHandle handle) const
{
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
- return D3D12_CPU_DESCRIPTOR_HANDLE{ m_CpuStart.ptr + handle.m_index * m_Stride };
+ return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride };
}
D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandle(DescriptorHandle handle) const
{
AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible");
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
- return D3D12_GPU_DESCRIPTOR_HANDLE{ m_GpuStart.ptr + handle.m_index * m_Stride };
+ return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) };
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandleForTable(DescriptorTable descTable) const
+ {
+ DescriptorHandle handle = descTable.GetOffset();
+ AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
+ return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride };
+ }
+
+ D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandleForTable(DescriptorTable descTable) const
+ {
+ DescriptorHandle handle = descTable.GetOffset();
+ AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible");
+ AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
+ return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) };
+ }
+
+ void DescriptorPool::CloneAllocator(RHI::Allocator* newAllocator)
+ {
+ m_allocator->Clone(newAllocator);
+ }
+
+ void DescriptorPool::ClearAllocator()
+ {
+ AZ_Assert(m_gpuStart.ptr, "Clearing the allocator is only supported for the gpu visible heap as only this heap can be compacted");
+ static_cast(m_allocator.get())
+ ->Init(static_cast(m_allocator.get())->GetDescriptor());
+ }
+
+ RHI::Allocator* DescriptorPool::GetAllocator() const
+ {
+ return m_allocator.get();
+ }
+
+ void DescriptorPoolShaderVisibleCbvSrvUav::Init(
+ ID3D12DeviceX* device,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ D3D12_DESCRIPTOR_HEAP_FLAGS flags,
+ uint32_t descriptorCount,
+ uint32_t staticHandlesCount)
+ {
+ //This pool manages two allocators. The allocator in the base class manages static handles
+ Base::Init(device, type, flags, descriptorCount, staticHandlesCount);
+
+ //This allocator manages dynamic handles associated with descriptor tables. This allows us to
+ //reconstruct the full heap in a compact manner if it ever fragments.
+ RHI::FreeListAllocator::Descriptor descriptor;
+ descriptor.m_alignmentInBytes = 1;
+ descriptor.m_capacityInBytes = aznumeric_cast(descriptorCount - staticHandlesCount);
+ descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax;
+
+ RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator();
+ allocator->Init(descriptor);
+ m_unboundedArrayAllocator.reset(allocator);
+
+ //Cache the starting point of the dynamic section of the heap
+ m_startingHandleIndex = staticHandlesCount;
+ }
+
+ DescriptorTable DescriptorPoolShaderVisibleCbvSrvUav::AllocateTable(uint32_t count)
+ {
+ RHI::VirtualAddress address;
+ {
+ AZStd::lock_guard lock(m_mutex);
+ address = m_unboundedArrayAllocator->Allocate(count, 1);
+ }
+
+ if (address.IsValid())
+ {
+ DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr));
+ return DescriptorTable(handle, static_cast(count));
+ }
+ else
+ {
+ return DescriptorTable{};
+ }
+ }
+
+ void DescriptorPoolShaderVisibleCbvSrvUav::ReleaseTable(DescriptorTable table)
+ {
+ if (table.IsNull())
+ {
+ return;
+ }
+
+ AZStd::lock_guard lock(m_mutex);
+ m_unboundedArrayAllocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index));
+ }
+
+ void DescriptorPoolShaderVisibleCbvSrvUav::GarbageCollect()
+ {
+ Base::GarbageCollect();
+ m_unboundedArrayAllocator->GarbageCollect();
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetCpuPlatformHandleForTable(DescriptorTable descTable) const
+ {
+ DescriptorHandle handle = descTable.GetOffset();
+ AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
+ return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) };
+ }
+
+ D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetGpuPlatformHandleForTable(DescriptorTable descTable) const
+ {
+ DescriptorHandle handle = descTable.GetOffset();
+ AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible");
+ AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
+ return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) };
+ }
+
+ void DescriptorPoolShaderVisibleCbvSrvUav::ClearAllocator()
+ {
+ Base::ClearAllocator();
+ static_cast(m_unboundedArrayAllocator.get())->Init(static_cast(m_unboundedArrayAllocator.get())->GetDescriptor());
}
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h
index cd90d1aba3..2237841ea7 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h
@@ -18,37 +18,91 @@ namespace AZ
{
namespace DX12
{
+ //! This class defines a Descriptor pool which manages all the descriptors used for binding resources
class DescriptorPool
{
public:
DescriptorPool() = default;
+ virtual ~DescriptorPool() = default;
- void Init(
+ //! Initialize the native heap as well as init the allocators tracking the memory for descriptor handles
+ virtual void Init(
ID3D12DeviceX* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
- uint32_t descriptorCount);
+ uint32_t descriptorCountForHeap,
+ uint32_t descriptorCountForAllocator);
ID3D12DescriptorHeap* GetPlatformHeap() const;
- DescriptorTable Allocate(uint32_t count = 1);
+ //! Allocate a Descriptor handles
+ DescriptorHandle AllocateHandle(uint32_t count = 1);
+ //! Release a descriptor handle
+ void ReleaseHandle(DescriptorHandle table);
+ //! Allocate a range contiguous handles (i.e Descriptor table)
+ virtual DescriptorTable AllocateTable(uint32_t count = 1);
+ //! Release a range contiguous handles (i.e Descriptor table)
+ virtual void ReleaseTable(DescriptorTable table);
+ //! Garbage collection for freed handles or tables
+ virtual void GarbageCollect();
+ //Get native pointers from the heap
+ virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const;
+ virtual D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const;
+ //Clear the tracking allocator
+ virtual void ClearAllocator();
- void Release(DescriptorTable table);
-
- void GarbageCollect();
D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const;
D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const;
- private:
- D3D12_CPU_DESCRIPTOR_HANDLE m_CpuStart = {};
- D3D12_GPU_DESCRIPTOR_HANDLE m_GpuStart = {};
- D3D12_CPU_DESCRIPTOR_HANDLE m_NullDescriptor = {};
- uint32_t m_Stride = 0;
- D3D12_DESCRIPTOR_HEAP_DESC m_Desc;
- Microsoft::WRL::ComPtr m_DescriptorHeap;
+ //Clone the tracking allocator
+ void CloneAllocator(RHI::Allocator* newAllocator);
+ RHI::Allocator* GetAllocator() const;
+
+ protected:
+ D3D12_DESCRIPTOR_HEAP_DESC m_desc;
AZStd::mutex m_mutex;
+ D3D12_CPU_DESCRIPTOR_HANDLE m_cpuStart = {};
+ D3D12_GPU_DESCRIPTOR_HANDLE m_gpuStart = {};
+ uint32_t m_stride = 0;
+ private:
+
+ // Native heap
+ Microsoft::WRL::ComPtr m_descriptorHeap;
+
+ // Allocator used to manage the whole native heap. In the case of DescriptorPoolShaderVisibleCbvSrvUav this allocator
+ // is used to manage the part of the heap that only manages static handles.
AZStd::unique_ptr m_allocator;
};
+
+ //! A specialized pool that specifically handles Descriptor tables for Cbv/Srv/Uav views and allows for Compaction
+ //! Specifically this pool handles the dynamic part of the heap
+ class DescriptorPoolShaderVisibleCbvSrvUav : public DescriptorPool
+ {
+ using Base = DescriptorPool;
+
+ public:
+ void Init(
+ ID3D12DeviceX* device,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ D3D12_DESCRIPTOR_HEAP_FLAGS flags,
+ uint32_t descriptorCount,
+ uint32_t staticHandlesCount);
+
+ DescriptorTable AllocateTable(uint32_t count = 1) override;
+ void ReleaseTable(DescriptorTable table) override;
+ void GarbageCollect() override;
+
+ D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const override;
+ D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const override;
+ void ClearAllocator() override;
+
+ private:
+
+ // A separate allocator that handles descriptor tables which are dynamic in nature and may fragment and require compaction
+ AZStd::unique_ptr m_unboundedArrayAllocator;
+ //Starting index of the dynamic part of the heap
+ uint32_t m_startingHandleIndex = 0;
+ };
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp
index 82e4aba57c..6f614499d2 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp
@@ -625,5 +625,20 @@ namespace AZ
{
return m_isAftermathInitialized;
}
+
+ RHI::ResultCode Device::CompactSRGMemory()
+ {
+ if (m_isDescriptorHeapCompactionNeeded)
+ {
+ m_isDescriptorHeapCompactionNeeded = false;
+ return m_descriptorContext->CompactDescriptorHeap();
+ }
+ return RHI::ResultCode::Success;
+ }
+
+ void Device::DescriptorHeapCompactionNeeded()
+ {
+ m_isDescriptorHeapCompactionNeeded = true;
+ }
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h
index 7004900ec5..40e26db891 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h
@@ -98,39 +98,27 @@ namespace AZ
D3D12_RESOURCE_STATES initialState,
ImageTileLayout& imageTilingInfo);
- /**
- * Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed
- * through the GPU.
- */
+ //! Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed
+ //! through the GPU.
void QueueForRelease(RHI::Ptr dx12Object);
- /**
- * Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the
- * current frame has flushed through the GPU. The reference on the MemoryView itself is not released.
- */
+ //! Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the
+ //! current frame has flushed through the GPU. The reference on the MemoryView itself is not released.
void QueueForRelease(const MemoryView& memoryView);
- /**
- * Allocates host memory from the internal frame allocator that is suitable for staging
- * uploads to the GPU for the current frame. The memory is valid for the lifetime of
- * the frame and is automatically reclaimed after the frame has completed on the GPU.
- */
+ //! Allocates host memory from the internal frame allocator that is suitable for staging
+ //! uploads to the GPU for the current frame. The memory is valid for the lifetime of
+ //! the frame and is automatically reclaimed after the frame has completed on the GPU.
MemoryView AcquireStagingMemory(size_t size, size_t alignment);
- /**
- * Acquires a pipeline layout from the internal cache.
- */
+ //! Acquires a pipeline layout from the internal cache.
RHI::ConstPtr AcquirePipelineLayout(const RHI::PipelineLayoutDescriptor& descriptor);
- /**
- * Acquires a new command list for the frame given the hardware queue class. The command list is
- * automatically reclaimed after the current frame has flushed through the GPU.
- */
+ //! Acquires a new command list for the frame given the hardware queue class. The command list is
+ //! automatically reclaimed after the current frame has flushed through the GPU.
CommandList* AcquireCommandList(RHI::HardwareQueueClass hardwareQueueClass);
- /**
- * Acquires a sampler from the internal cache.
- */
+ //! Acquires a sampler from the internal cache.
RHI::ConstPtr AcquireSampler(const RHI::SamplerState& state);
const PhysicalDevice& GetPhysicalDevice() const;
@@ -146,6 +134,10 @@ namespace AZ
AsyncUploadQueue& GetAsyncUploadQueue();
bool IsAftermathInitialized() const;
+
+ //! Indicate that we need to compact the shader visible srv/uav/cbv shader visible heap.
+ void DescriptorHeapCompactionNeeded();
+
private:
Device();
@@ -167,6 +159,7 @@ namespace AZ
RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override;
RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override;
void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override;
+ RHI::ResultCode CompactSRGMemory() override;
//////////////////////////////////////////////////////////////////////////
RHI::ResultCode InitSubPlatform(RHI::PhysicalDevice& physicalDevice);
@@ -198,6 +191,9 @@ namespace AZ
AZStd::mutex m_samplerCacheMutex;
bool m_isAftermathInitialized = false;
+
+ // Boolean used to compact the view specific shader visible heap
+ bool m_isDescriptorHeapCompactionNeeded = false;
};
}
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp
index 66b3f9623a..63625fde72 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp
@@ -49,9 +49,11 @@ namespace AZ
AZStd::array_view bytes;
bool shouldCreateLibFromSerializedData = true;
- if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded())
+ if (RHI::Factory::Get().IsRenderDocModuleLoaded() ||
+ RHI::Factory::Get().IsPixModuleLoaded() ||
+ RHI::Factory::Get().UsingWarpDevice())
{
- // CreatePipelineLibrary api does not function properly if Renderdoc or Pix is enabled
+ // CreatePipelineLibrary api does not function properly if Renderdoc, Pix or Warp is enabled
shouldCreateLibFromSerializedData = false;
}
@@ -215,9 +217,11 @@ namespace AZ
RHI::ResultCode PipelineLibrary::MergeIntoInternal([[maybe_unused]] AZStd::array_view pipelineLibraries)
{
- if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded())
+ if (RHI::Factory::Get().IsRenderDocModuleLoaded() ||
+ RHI::Factory::Get().IsPixModuleLoaded() ||
+ RHI::Factory::Get().UsingWarpDevice())
{
- // StorePipeline api does not function properly if RenderDoc or Pix is enabled
+ // StorePipeline api does not function properly if RenderDoc, Pix or Warp is enabled
return RHI::ResultCode::Fail;
}
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h
index 4c686fe525..0a7185ccd5 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h
@@ -51,6 +51,7 @@ namespace AZ
ShaderResourceGroup() = default;
friend class ShaderResourceGroupPool;
+ friend class DescriptorContext;
/// The current index into the compiled data array.
uint32_t m_compiledDataIndex = 0;
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp
index d51fdd3495..bd648cf535 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp
@@ -132,33 +132,17 @@ namespace AZ
compiledData.m_cpuConstantAddress = cpuAddress + m_constantBufferSize * i;
}
}
-
- if (m_viewsDescriptorTableSize)
- {
- group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize);
-
- if (!group.m_viewsDescriptorTable.IsValid())
- {
- AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate view descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12");
- return RHI::ResultCode::OutOfMemory;
- }
-
- for (uint32_t i = 0; i < copyCount; ++i)
- {
- const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i;
-
- ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i];
- compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle);
- }
- }
-
+
if (m_samplersDescriptorTableSize)
{
- group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize);
+ group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize, &group);
if (!group.m_samplersDescriptorTable.IsValid())
{
- AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate sampler descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12.");
+ AZ_Error(
+ "ShaderResourceGroupPool", false,
+ "Descriptor context failed to allocate sampler descriptor table. Please consider increasing number of handles "
+ "allowed for the second value of DESCRIPTOR_HEAP_TYPE_SAMPLER within platformlimits.azasset file for dx12.");
return RHI::ResultCode::OutOfMemory;
}
@@ -167,7 +151,7 @@ namespace AZ
const DescriptorHandle descriptorHandle = group.m_samplersDescriptorTable.GetOffset() + m_samplersDescriptorTableSize * i;
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i];
- compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle);
+ compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(DescriptorTable(descriptorHandle, static_cast(m_samplersDescriptorTableSize)));
}
}
@@ -186,19 +170,25 @@ namespace AZ
if (m_viewsDescriptorTableSize)
{
- m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable);
+ if (group.m_viewsDescriptorTable.IsValid())
+ {
+ m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable, &group);
+ }
}
if (m_samplersDescriptorTableSize)
{
- m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable);
+ if (group.m_viewsDescriptorTable.IsValid())
+ {
+ m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable, &group);
+ }
}
for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex)
{
if (group.m_unboundedDescriptorTables[unboundedArrayindex].IsValid())
{
- m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex]);
+ m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex], &group);
}
}
@@ -213,6 +203,7 @@ namespace AZ
const RHI::ShaderResourceGroupData& groupData)
{
ShaderResourceGroup& group = static_cast(groupBase);
+ auto& device = static_cast(GetDevice());
group.m_compiledDataIndex = (group.m_compiledDataIndex + 1) % RHI::Limits::Device::FrameCountMax;
if (m_constantBufferSize)
@@ -222,6 +213,22 @@ namespace AZ
if (m_viewsDescriptorTableSize)
{
+ //Lazy initialization for cbv/srv/uav Descriptor Tables
+ if (!group.m_viewsDescriptorTable.IsValid())
+ {
+ group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group);
+
+ if (!group.m_viewsDescriptorTable.IsValid())
+ {
+ //We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that.
+ device.DescriptorHeapCompactionNeeded();
+ return RHI::ResultCode::Success;
+ }
+
+ CacheGpuHandlesForViews(group);
+ }
+
const DescriptorTable descriptorTable(
group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize,
static_cast(m_viewsDescriptorTableSize));
@@ -246,6 +253,18 @@ namespace AZ
return RHI::ResultCode::Success;
}
+ void ShaderResourceGroupPool::CacheGpuHandlesForViews(ShaderResourceGroup& group)
+ {
+ for (uint32_t i = 0; i < RHI::Limits::Device::FrameCountMax; ++i)
+ {
+ const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i;
+
+ ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i];
+ compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(
+ DescriptorTable(descriptorHandle, static_cast(m_viewsDescriptorTableSize)));
+ }
+ }
+
void ShaderResourceGroupPool::UpdateViewsDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData)
{
const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
@@ -261,27 +280,27 @@ namespace AZ
AZStd::vector descriptorHandles;
switch (descriptorRangeType)
{
- case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
- {
- descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER);
- break;
+ case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
+ {
+ descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER);
+ break;
+ }
+ case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
+ {
+ descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
+ break;
+ }
+ case D3D12_DESCRIPTOR_RANGE_TYPE_CBV:
+ {
+ descriptorHandles = GetCBVsFromBufferViews(bufferViews);
+ break;
+ }
+ default:
+ AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
+ break;
}
- case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
- {
- descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
- break;
- }
- case D3D12_DESCRIPTOR_RANGE_TYPE_CBV:
- {
- descriptorHandles = GetCBVsFromBufferViews(bufferViews);
- break;
- }
- default:
- AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
- break;
- }
- UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex);
+ UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex);
++shaderInputIndex;
}
@@ -297,23 +316,24 @@ namespace AZ
AZStd::vector descriptorHandles;
switch (descriptorRangeType)
{
- case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
- {
- descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type));
- break;
- }
- case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
- {
- descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type));
- break;
- }
- default:
+ case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
+ {
+ descriptorHandles =
+ GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type));
+ break;
+ }
+ case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
+ {
+ descriptorHandles =
+ GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type));
+ break;
+ }
+ default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
UpdateDescriptorTableRange(descriptorTable, descriptorHandles, imageInputIndex);
-
++shaderInputIndex;
}
}
@@ -334,7 +354,7 @@ namespace AZ
void ShaderResourceGroupPool::UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData)
{
const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
-
+ auto& device = static_cast(GetDevice());
uint32_t shaderInputIndex = 0;
// process buffer unbounded arrays
@@ -350,50 +370,30 @@ namespace AZ
{
if (group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
- m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]);
+ m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group);
group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{};
}
if (!bufferViews.empty())
{
- group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()));
- AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory.");
+ group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group);
+
+ if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
+ {
+ // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that.
+ device.DescriptorHeapCompactionNeeded();
+ return;
+ }
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
- compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset());
+ compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
}
}
-
- ++shaderInputIndex;
-
- if (bufferViews.empty())
- {
- // we don't need to update descriptors since the buffer list is empty
- continue;
- }
-
- D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(shaderInputBufferUnboundedArray.m_access);
-
- AZStd::vector descriptorHandles;
- switch (descriptorRangeType)
- {
- case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
- {
- descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER);
- break;
- }
- case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
- {
- descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
- break;
- }
- default:
- AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
- break;
- }
-
+
const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size()));
- m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access);
+ ++shaderInputIndex;
}
// process image unbounded arrays
@@ -407,55 +407,223 @@ namespace AZ
// resize the descriptor table allocation if necessary
if (group.m_unboundedDescriptorTables[tableIndex].GetSize() != imageViews.size())
{
+
if (group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
- m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]);
+ m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group);
group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{};
}
if (!imageViews.empty())
{
- group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()));
- AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory.");
+ group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group);
+
+ if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
+ {
+ // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that
+ device.DescriptorHeapCompactionNeeded();
+ return;
+ }
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
- compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset());
+ compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
}
}
- ++shaderInputIndex;
-
- if (imageViews.empty())
- {
- // we don't need to update descriptors since the image list is empty
- continue;
- }
-
- D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(shaderInputImageUnboundedArray.m_access);
-
- AZStd::vector descriptorHandles;
- switch (descriptorRangeType)
- {
- case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
- {
- descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImageUnboundedArray.m_type));
- break;
- }
- case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
- {
- descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImageUnboundedArray.m_type));
- break;
- }
- default:
- AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
- break;
- }
-
const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size()));
- m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type);
+ ++shaderInputIndex;
}
}
+ void ShaderResourceGroupPool::UpdateUnboundedBuffersDescTable(
+ DescriptorTable descriptorTable,
+ const RHI::ShaderResourceGroupData& groupData,
+ uint32_t shaderInputIndex,
+ RHI::ShaderInputBufferAccess bufferAccess)
+ {
+ const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex);
+ AZStd::array_view> bufferViews =
+ groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex);
+
+ if (bufferViews.empty())
+ {
+ // we don't need to update descriptors since the buffer list is empty
+ return;
+ }
+
+ D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(bufferAccess);
+
+ AZStd::vector descriptorHandles;
+ switch (descriptorRangeType)
+ {
+ case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
+ {
+ descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER);
+ break;
+ }
+ case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
+ {
+ descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
+ break;
+ }
+ default:
+ AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
+ break;
+ }
+
+ m_descriptorContext->UpdateDescriptorTableRange(
+ descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+
+ void ShaderResourceGroupPool::UpdateUnboundedImagesDescTable(
+ DescriptorTable descriptorTable,
+ const RHI::ShaderResourceGroupData& groupData,
+ uint32_t shaderInputIndex,
+ RHI::ShaderInputImageAccess imageAccess,
+ RHI::ShaderInputImageType imageType)
+ {
+ const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex);
+ AZStd::array_view> imageViews =
+ groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex);
+
+ if (imageViews.empty())
+ {
+ // we don't need to update descriptors since the image list is empty
+ return;
+ }
+
+ D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(imageAccess);
+
+ AZStd::vector descriptorHandles;
+ switch (descriptorRangeType)
+ {
+ case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
+ {
+ descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(imageType));
+ break;
+ }
+ case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
+ {
+ descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(imageType));
+ break;
+ }
+ default:
+ AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
+ break;
+ }
+
+ m_descriptorContext->UpdateDescriptorTableRange(
+ descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+
+ RHI::ResultCode ShaderResourceGroupPool::UpdateDescriptorTableAfterCompaction(
+ RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData)
+ {
+ // Since we are trying to compact we will re-create all the descriptor tables and re-update them all
+ ShaderResourceGroup& group = static_cast(groupBase);
+
+ if (m_viewsDescriptorTableSize)
+ {
+ group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group);
+
+ if (!group.m_viewsDescriptorTable.IsValid())
+ {
+ AZ_Assert(
+ false,
+ "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value"
+ "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12.");
+ return RHI::ResultCode::OutOfMemory;
+ }
+
+ CacheGpuHandlesForViews(group);
+
+ const DescriptorTable descriptorTable(
+ group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize,
+ static_cast(m_viewsDescriptorTableSize));
+
+ UpdateViewsDescriptorTable(descriptorTable, groupData);
+ }
+
+ if (m_unboundedArrayCount)
+ {
+ //Reset all the old descriptor tables as the previous heap is gone.
+ for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex)
+ {
+ group.m_unboundedDescriptorTables[unboundedArrayindex] = DescriptorTable{};
+ }
+
+ const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
+ uint32_t shaderInputIndex = 0;
+
+ // process buffer unbounded arrays
+ for (const RHI::ShaderInputBufferUnboundedArrayDescriptor& shaderInputBufferUnboundedArray : groupLayout.GetShaderInputListForBufferUnboundedArrays())
+ {
+ const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex);
+ AZStd::array_view> bufferViews = groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex);
+
+ uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex;
+ if (!bufferViews.empty())
+ {
+ group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group);
+
+ if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
+ {
+ AZ_Assert(
+ false,
+ "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value"
+ "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12.");
+ return RHI::ResultCode::OutOfMemory;
+ }
+
+ ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
+ compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
+
+ const DescriptorTable descriptorTable(
+ group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size()));
+ UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access);
+ }
+ shaderInputIndex++;
+ }
+
+ // process image unbounded arrays
+ for (const RHI::ShaderInputImageUnboundedArrayDescriptor& shaderInputImageUnboundedArray :
+ groupLayout.GetShaderInputListForImageUnboundedArrays())
+ {
+ const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex);
+ AZStd::array_view> imageViews =
+ groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex);
+
+ uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex;
+ if (!imageViews.empty())
+ {
+ group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group);
+
+ if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
+ {
+ AZ_Assert(
+ false,
+ "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value"
+ "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12.");
+ return RHI::ResultCode::OutOfMemory;
+ }
+
+ ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
+ compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
+
+ const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size()));
+ UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type);
+ }
+ shaderInputIndex++;
+ }
+ }
+ return RHI::ResultCode::Success;
+ }
+
void ShaderResourceGroupPool::OnFrameEnd()
{
m_constantAllocator.GarbageCollect();
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h
index bbe7e6596d..e1b2145097 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h
@@ -30,6 +30,9 @@ namespace AZ
static RHI::Ptr Create();
+ //! Re-Update the descriptor tables for all the cbv/srv/uav views (direct and via unbounded array)
+ RHI::ResultCode UpdateDescriptorTableAfterCompaction(RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData);
+
private:
ShaderResourceGroupPool() = default;
@@ -51,6 +54,21 @@ namespace AZ
void UpdateSamplersDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData);
void UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData);
+ //! Update all the buffer views for the unbounded array
+ void UpdateUnboundedBuffersDescTable(
+ DescriptorTable descriptorTable,
+ const RHI::ShaderResourceGroupData& groupData,
+ uint32_t shaderInputIndex,
+ RHI::ShaderInputBufferAccess bufferAccess);
+
+ //! Update all the image views for the unbounded array
+ void UpdateUnboundedImagesDescTable(
+ DescriptorTable descriptorTable,
+ const RHI::ShaderResourceGroupData& groupData,
+ uint32_t shaderInputIndex,
+ RHI::ShaderInputImageAccess imageAccess,
+ RHI::ShaderInputImageType imageType);
+
void UpdateDescriptorTableRange(
DescriptorTable descriptorTable,
const AZStd::vector& descriptors,
@@ -66,6 +84,9 @@ namespace AZ
RHI::ShaderInputSamplerIndex samplerIndex,
AZStd::array_view samplerStates);
+ //Cache all the gpu handles for the Descriptor tables related to all the views
+ void CacheGpuHandlesForViews(ShaderResourceGroup& group);
+
DescriptorTable GetBufferTable(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const;
DescriptorTable GetBufferTableUnbounded(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const;
DescriptorTable GetImageTable(DescriptorTable descriptorTable, RHI::ShaderInputImageIndex imageIndex) const;
@@ -79,7 +100,6 @@ namespace AZ
AZStd::vector GetCBVsFromBufferViews(const AZStd::array_view