Merge branch 'development' of https://github.com/o3de/o3de into mp_autogen_move

This commit is contained in:
puvvadar
2021-09-27 10:41:53 -07:00
81 changed files with 2168 additions and 573 deletions
@@ -0,0 +1,7 @@
<svg width="24" height="33" viewBox="0 0 24 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" y="7" width="2" height="12" fill="#808080"/>
<rect x="5" y="19" width="2" height="14" fill="#808080"/>
<rect x="17" y="16" width="2" height="12" transform="rotate(90 17 16)" fill="#808080"/>
<circle cx="6" cy="6" r="2" fill="#808080"/>
<circle cx="18" cy="17" r="2" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 398 B

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" width="2" height="12" fill="grey"/>
<rect x="17" y="12" width="2" height="12" transform="rotate(90 17 12)" fill="grey"/>
<circle cx="18" cy="13" r="2" fill="grey"/>
</svg>

After

Width:  |  Height:  |  Size: 280 B

@@ -0,0 +1,6 @@
<svg width="24" height="26" viewBox="0 0 24 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" width="2" height="12" fill="grey"/>
<rect x="5" y="14" width="2" height="12" fill="grey"/>
<rect x="17" y="12" width="2" height="12" transform="rotate(90 17 12)" fill="grey"/>
<circle cx="18" cy="13" r="2" fill="grey"/>
</svg>

After

Width:  |  Height:  |  Size: 335 B

@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" y="7" width="2" height="7" fill="#808080"/>
<rect x="17" y="12" width="2" height="12" transform="rotate(90 17 12)" fill="#808080"/>
<circle cx="6" cy="6" r="2" fill="#808080"/>
<circle cx="18" cy="13" r="2" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 339 B

@@ -8098,7 +8098,7 @@
</message>
<message id="COLOR_FROMVALUES_PARAM0_TOOLTIP">
<source>COLOR_FROMVALUES_PARAM0_TOOLTIP</source>
<translation type="unfinished">The Red value of hte Color [0, 255]</translation>
<translation type="unfinished">The Red value of the Color [0.0-1.0]</translation>
</message>
<message id="COLOR_FROMVALUES_PARAM1_NAME">
<source>COLOR_FROMVALUES_PARAM1_NAME</source>
@@ -8107,7 +8107,7 @@
</message>
<message id="COLOR_FROMVALUES_PARAM1_TOOLTIP">
<source>COLOR_FROMVALUES_PARAM1_TOOLTIP</source>
<translation type="unfinished">The Green value of the Color [0, 255]</translation>
<translation type="unfinished">The Green value of the Color [0.0-1.0]</translation>
</message>
<message id="COLOR_FROMVALUES_PARAM2_NAME">
<source>COLOR_FROMVALUES_PARAM2_NAME</source>
@@ -8116,7 +8116,7 @@
</message>
<message id="COLOR_FROMVALUES_PARAM2_TOOLTIP">
<source>COLOR_FROMVALUES_PARAM2_TOOLTIP</source>
<translation type="unfinished">The Blue value of the Color [0, 255]</translation>
<translation type="unfinished">The Blue value of the Color [0.0-1.0]</translation>
</message>
<message id="COLOR_FROMVALUES_PARAM3_NAME">
<source>COLOR_FROMVALUES_PARAM3_NAME</source>
@@ -8125,7 +8125,7 @@
</message>
<message id="COLOR_FROMVALUES_PARAM3_TOOLTIP">
<source>COLOR_FROMVALUES_PARAM3_TOOLTIP</source>
<translation type="unfinished">The Alpha value of the Color [0, 255]</translation>
<translation type="unfinished">The Alpha value of the Color [0.0-1.0]</translation>
</message>
</context>
<context>
@@ -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()
@@ -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,
@@ -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
@@ -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;
@@ -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 <AzCore/IO/FileReader.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
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<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return *fileHandle != AZ::IO::InvalidHandle;
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->IsOpen();
}
return false;
}
void FileReader::Close()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&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<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize))
{
return fileSize;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Length();
}
return 0;
}
auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
{
return bytesRead;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Read(byteSize, buffer);
}
return 0;
}
auto FileReader::Tell() const -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset))
{
return fileOffset;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Tell();
}
return 0;
}
bool FileReader::Seek(AZ::s64 offset, SeekType type)
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return m_fileIoBase->Seek(*fileHandle, offset, type);
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
systemFile->Seek(offset, static_cast<AZ::IO::SystemFile::SeekMode>(type));
return true;
}
return false;
}
bool FileReader::Eof() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return m_fileIoBase->Eof(*fileHandle);
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Eof();
}
return false;
}
bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&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<char>::length(pathStringRef.data()));
return true;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
filePath = systemFile->Name();
return true;
}
return false;
}
}
@@ -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 <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/variant.h>
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<AZStd::monostate, AZ::IO::SystemFile, HandleType>;
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{};
};
}
@@ -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);
}
+2 -2
View File
@@ -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.
@@ -10,6 +10,7 @@
#include <cerrno>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileReader.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/NativeUI//NativeUIRequests.h>
@@ -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<AZStd::monostate, AZ::IO::SystemFile, AZ::IO::HandleType>;
SettingsRegistryFileReader() = default;
SettingsRegistryFileReader(bool useFileIo, const char* filePath)
{
Open(useFileIo, filePath);
}
~SettingsRegistryFileReader()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&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<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return *fileHandle != AZ::IO::InvalidHandle;
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->IsOpen();
}
return false;
}
void Close()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&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<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize))
{
return fileSize;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&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<AZ::IO::HandleType>(&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<AZ::IO::SystemFile>(&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<char>& 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);
@@ -6,6 +6,8 @@
*
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileReader.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/TextStreamWriters.h>
@@ -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<int>(configBuffer.size()), configBuffer.data());
configFileParsed = false;
break;
@@ -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
@@ -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
@@ -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 <AzCore/IO/FileReader.h>
#include <FileIOBaseTestTypes.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
template <typename FileIOType>
class FileReaderTestFixture
: public ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
if constexpr (AZStd::is_same_v<FileIOType, TestFileIOBase>)
{
m_fileIo = AZStd::make_unique<TestFileIOBase>();
}
}
void TearDown() override
{
m_fileIo.reset();
}
protected:
AZStd::unique_ptr<AZ::IO::FileIOBase> m_fileIo{};
};
using FileIOTypes = ::testing::Types<void, TestFileIOBase>;
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
@@ -37,6 +37,7 @@ set(FILES
FileIOBaseTestTypes.h
Geometry2DUtils.cpp
Interface.cpp
IO/FileReaderTests.cpp
IO/Path/PathTests.cpp
IPC.cpp
Jobs.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))
@@ -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)
@@ -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<AssetBrowserTableModel> m_tableModel = nullptr;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel = nullptr;
EntryDelegate* m_delegate = nullptr;
QPointer<AssetBrowserTableModel> m_tableModel;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel;
SearchEntryDelegate* m_delegate = nullptr;
private Q_SLOTS:
void OnContextMenu(const QPoint& point);
@@ -11,12 +11,13 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/AssetBrowser/Views/EntryDelegate.h>
#include <AzCore/Utils/Utils.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
#include <QApplication>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' 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 <QAbstractItemView>
#include <QPainter>
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<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(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<const AssetBrowserEntry*>())
{
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<const AssetBrowserEntry*>(data);
auto sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(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<int>(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<int>(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<QAbstractItemView*>(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<const AssetBrowserEntry*>(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
auto belowEntry = qvariant_cast<const AssetBrowserEntry*>(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
auto aboveSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(aboveEntry);
auto belowSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(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<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Name)))
: qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Path)));
style->drawItemText(
painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString,
isSelected ? QPalette::HighlightedText : QPalette::Text);
}
}
void SearchEntryDelegate::LoadBranchPixMaps()
{
AZ::IO::BasicPath<AZ::IO::FixedMaxPathString> 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<EntryBranchType>(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"
@@ -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<EntryBranchType, QPixmap> m_branchIcons;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -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;
}
@@ -8,6 +8,7 @@
#include <GemCatalog/GemInspector.h>
#include <GemCatalog/GemItemDelegate.h>
#include <QFrame>
#include <QLabel>
#include <QSpacerItem>
@@ -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
@@ -9,10 +9,11 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <LinkWidget.h>
#include <TagWidget.h>
#include <GemCatalog/GemInfo.h>
#include <GemCatalog/GemModel.h>
#include <GemsSubWidget.h>
#include <LinkWidget.h>
#include <QItemSelection>
#include <QScrollArea>
#include <QWidget>
@@ -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;
@@ -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)
{
@@ -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
@@ -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 <GemRepo/GemRepoInspector.h>
#include <GemRepo/GemRepoItemDelegate.h>
#include <QFrame>
#include <QLabel>
#include <QVBoxLayout>
#include <QIcon>
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
@@ -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 <GemRepo/GemRepoModel.h>
#include <LinkWidget.h>
#include <GemsSubWidget.h>
#include <QItemSelection>
#include <QScrollArea>
#include <QSpacerItem>
#include <QWidget>
#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
@@ -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
@@ -10,6 +10,7 @@
#if !defined(Q_MOC_RUN)
#include <QListView>
#include <QItemSelectionModel>
#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
@@ -7,8 +7,10 @@
*/
#include <GemRepo/GemRepoModel.h>
#include <PythonBindings.h>
#include <QItemSelectionModel>
#include <QMessageBox>
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<GemInfo> 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<GemInfo> gemInfos = GetIncludedGemInfos(modelIndex);
for (const GemInfo& gemInfo : gemInfos)
{
gemNames.append(gemInfo.m_displayName);
}
return gemNames;
}
QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)
{
QVector<GemInfo> allGemInfos;
QStringList repoGemPaths = GetIncludedGemPaths(modelIndex);
for (const QString& gemPath : repoGemPaths)
{
AZ::Outcome<GemInfo> 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
@@ -11,6 +11,7 @@
#if !defined(Q_MOC_RUN)
#include <QStandardItemModel>
#include <GemRepo/GemRepoInfo.h>
#include <GemCatalog/GemModel.h>
#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<GemInfo> 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
@@ -10,6 +10,7 @@
#include <GemRepo/GemRepoItemDelegate.h>
#include <GemRepo/GemRepoListView.h>
#include <GemRepo/GemRepoModel.h>
#include <GemRepo/GemRepoInspector.h>
#include <PythonBindingsInterface.h>
#include <QVBoxLayout>
@@ -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()));
}
}
@@ -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;
@@ -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 <GemsSubWidget.h>
#include <TagWidget.h>
#include <QFrame>
#include <QLabel>
#include <QVBoxLayout>
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
@@ -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 <QWidget>
#include <TagWidget.h>
#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
@@ -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
@@ -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
@@ -954,8 +954,16 @@ namespace O3DE::ProjectManager
return AZ::Failure<AZStd::string>(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());
@@ -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<GemInfo> GetGemInfo(const QString& path, const QString& projectPath = {}) = 0;
@@ -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
@@ -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;
+38 -43
View File
@@ -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 <typename T>
T* AllocateAs(size_t byteCount, size_t byteAlignment)
{
@@ -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;
@@ -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;
@@ -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:
@@ -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);
+10 -1
View File
@@ -8,12 +8,12 @@
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/ResourceInvalidateBus.h>
#include <Atom/RHI/RHIUtils.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Component/TickBus.h>
#if defined(USE_RENDERDOC) || defined(USE_PIX)
#include <AzCore/Module/DynamicModuleHandle.h>
#include <Atom/RHI/RHIUtils.h>
#include <Atom_RHI_Traits_Platform.h>
#endif
@@ -28,6 +28,8 @@ static AZStd::unique_ptr<AZ::DynamicModuleHandle> 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;
}
}
}
@@ -314,6 +314,11 @@ namespace AZ
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileAllLambda)>(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()
@@ -344,5 +344,17 @@ namespace AZ
handle = node.m_nextFree;
}
}
void FreeListAllocator::Clone(RHI::Allocator* newAllocator)
{
FreeListAllocator* newFreeListAllocator = static_cast<FreeListAllocator*>(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;
}
}
}
@@ -64,6 +64,12 @@ namespace AZ
//! int array: Max count for descriptors
AZStd::unordered_map<AZStd::string, AZStd::array<uint32_t, NumHeapFlags>> 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;
@@ -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()))
@@ -19,8 +19,10 @@ namespace AZ
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<PlatformLimitsDescriptor, Base>()
->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, AZStd::array<uint32_t, NumHeapFlags>>({
{ 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 } }
@@ -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(),
@@ -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());
}
@@ -39,7 +39,7 @@ namespace AZ
{
Device& device = static_cast<Device&>(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);
@@ -10,7 +10,9 @@
#include <RHI/Conversions.h>
#include <RHI/Device.h>
#include <RHI/Image.h>
#include <RHI/ShaderResourceGroupPool.h>
#include <Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h>
#include <Atom/RHI/ShaderResourceGroupPool.h>
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<uint32_t>(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<uint32_t>(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<uint32_t> 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<uint32_t>(i));
DescriptorHandle destHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast<uint32_t>(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<ShaderResourceGroupPool*>(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;
}
}
}
@@ -15,6 +15,8 @@
#include <Atom/RHI.Reflect/SamplerState.h>
#include <Atom/RHI/Buffer.h>
#include <Atom/RHI/Image.h>
#include <AzCore/std/containers/unordered_map.h>
#include <RHI/ShaderResourceGroup.h>
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<PlatformLimitsDescriptor> 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<ShaderResourceGroup*, uint32_t> m_srgAllocations;
AZStd::mutex m_srgMapMutex;
//Index that holds the currently active shader visible CbvSrvUav heap
uint32_t m_currentHeapIndex = 0;
};
}
}
@@ -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<uint32_t>(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<uint32_t>(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<uint32_t>(address.m_ptr));
return DescriptorTable(handle, static_cast<uint16_t>(count));
DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast<uint32_t>(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<AZStd::mutex> 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<uint16_t>(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<RHI::FreeListAllocator*>(m_allocator.get())
->Init(static_cast<RHI::FreeListAllocator*>(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<uint32_t>(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<AZStd::mutex> lock(m_mutex);
address = m_unboundedArrayAllocator->Allocate(count, 1);
}
if (address.IsValid())
{
DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast<uint32_t>(address.m_ptr));
return DescriptorTable(handle, static_cast<uint16_t>(count));
}
else
{
return DescriptorTable{};
}
}
void DescriptorPoolShaderVisibleCbvSrvUav::ReleaseTable(DescriptorTable table)
{
if (table.IsNull())
{
return;
}
AZStd::lock_guard<AZStd::mutex> 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<RHI::FreeListAllocator*>(m_unboundedArrayAllocator.get())->Init(static_cast<RHI::FreeListAllocator*>(m_unboundedArrayAllocator.get())->GetDescriptor());
}
}
}
@@ -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<ID3D12DescriptorHeap> 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<ID3D12DescriptorHeap> 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<RHI::Allocator> 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<RHI::Allocator> m_unboundedArrayAllocator;
//Starting index of the dynamic part of the heap
uint32_t m_startingHandleIndex = 0;
};
}
}
@@ -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;
}
}
}
+19 -23
View File
@@ -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<ID3D12Object> 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<PipelineLayout> 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<Sampler> 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;
};
}
}
@@ -49,9 +49,11 @@ namespace AZ
AZStd::array_view<uint8_t> 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<const RHI::PipelineLibrary*> 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;
}
@@ -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;
@@ -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<uint16_t>(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<ShaderResourceGroup&>(groupBase);
auto& device = static_cast<Device&>(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<uint16_t>(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<uint16_t>(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<DescriptorHandle> 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<RHI::BufferView, BufferView>(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<RHI::BufferView, BufferView>(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<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(shaderInputImage.m_type));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertUAVDimension(shaderInputImage.m_type));
break;
}
default:
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles =
GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(shaderInputImage.m_type));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles =
GetUAVsFromImageViews<RHI::ImageView, ImageView>(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<Device&>(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<uint32_t>(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<uint32_t>(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<DescriptorHandle> 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_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::BufferView, BufferView>(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<uint16_t>(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<uint32_t>(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<uint32_t>(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<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(shaderInputImageUnboundedArray.m_type));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::ImageView, ImageView>(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<uint16_t>(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<RHI::ConstPtr<RHI::BufferView>> 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<DescriptorHandle> 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_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::BufferView, BufferView>(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<RHI::ConstPtr<RHI::ImageView>> 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<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(imageType));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::ImageView, ImageView>(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<ShaderResourceGroup&>(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<uint16_t>(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<RHI::ConstPtr<RHI::BufferView>> 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<uint32_t>(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<uint16_t>(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<RHI::ConstPtr<RHI::ImageView>> 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<uint32_t>(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<uint16_t>(imageViews.size()));
UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type);
}
shaderInputIndex++;
}
}
return RHI::ResultCode::Success;
}
void ShaderResourceGroupPool::OnFrameEnd()
{
m_constantAllocator.GarbageCollect();
@@ -30,6 +30,9 @@ namespace AZ
static RHI::Ptr<ShaderResourceGroupPool> 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<DescriptorHandle>& descriptors,
@@ -66,6 +84,9 @@ namespace AZ
RHI::ShaderInputSamplerIndex samplerIndex,
AZStd::array_view<RHI::SamplerState> 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<DescriptorHandle> GetCBVsFromBufferViews(const AZStd::array_view<RHI::ConstPtr<RHI::BufferView>>& bufferViews);
AZStd::mutex m_constantAllocatorMutex;
MemoryPoolSubAllocator m_constantAllocator;
DescriptorContext* m_descriptorContext = nullptr;
uint32_t m_constantBufferSize = 0;
@@ -21,11 +21,13 @@
"$type": "AZ::DX12::PlatformLimitsDescriptor",
"DescriptorHeapLimits":
{
"DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000],
"DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [100000, 1000000],
"DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048],
"DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0],
"DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0]
}
},
"NumShaderVisibleCbvSrvUavStaticHandles": 2000,
"AllowDescriptorHeapCompaction": false
},
"vulkan":
{
@@ -40,7 +40,7 @@ namespace AZ
//! by ShaderVariantAssetBuilder this is 1+.
static uint32_t MakeAssetProductSubId(
uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, ShaderVariantStableId variantStableId,
uint32_t subProductType = ShaderVariantAssetSubProductType);
uint32_t subProductType = 0);
ShaderVariantAsset() = default;
~ShaderVariantAsset() = default;
@@ -909,6 +909,7 @@ enum class NetworkProperties
controller->Set{{ UpperFirst(Property.attrib['Name']) }}({{ LowerFirst(Property.attrib['Name']) }});
{% endif %}
})
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' -%}
->Method("GetOn{{ UpperFirst(Property.attrib['Name']) }}ChangedEvent", [](AZ::EntityId id) -> AZ::Event<int32_t, {{ Property.attrib['Type'] }}>*
{% else %}
@@ -936,6 +937,7 @@ enum class NetworkProperties
{% else %}
->Attribute(AZ::Script::Attributes::AzEventDescription, AZ::BehaviorAzEventDescription{ "On {{ UpperFirst(Property.attrib['Name']) }} Changed Event", {"New {{ Property.attrib['Type'] }}"} })
{% endif %}
{% endif %}
{% endif %}
{% endcall %}
@@ -1518,7 +1520,7 @@ namespace {{ Component.attrib['Namespace'] }}
{{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}}
{{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) }}
behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}")
behaviorContext->Class<{{ ComponentBaseName }}>("{{ ComponentBaseName }}")
->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}")
->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}")
@@ -19,6 +19,22 @@ namespace Physics
namespace Multiplayer
{
//! NetworkCharacterRequests
//! ComponentBus handled by NetworkCharacterComponentController.
//! Bus was created for exposing controller methods to script; C++ users should access the controller directly.
class NetworkCharacterRequests : public AZ::ComponentBus
{
public:
//! TryMoveWithVelocity
//! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync.
//! Velocity will be applied over delta-time to determine the movement amount.
//! Returns this entity's world-space position after the move.
virtual AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) = 0;
};
typedef AZ::EBus<NetworkCharacterRequests> NetworkCharacterRequestBus;
//! NetworkCharacterComponent
//! Provides multiplayer support for game-play player characters.
class NetworkCharacterComponent
@@ -39,6 +55,12 @@ namespace Multiplayer
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
NetworkCharacterComponentBase::GetRequiredServices(required);
required.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
}
// AZ::Component
void OnInit() override {}
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
@@ -65,18 +87,22 @@ namespace Multiplayer
//! Class provides the ability to move characters in physical space while keeping the network in-sync.
class NetworkCharacterComponentController
: public NetworkCharacterComponentControllerBase
, private NetworkCharacterRequestBus::Handler
{
public:
AZ_RTTI(NetworkCharacterComponentController, "{C91851A2-8B95-4484-9F97-BFF9D1F528A0}")
static void Reflect(AZ::ReflectContext* context);
NetworkCharacterComponentController(NetworkCharacterComponent& parent);
// NetworkCharacterComponentControllerBase
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
// NetworkCharacterRequestBus::Handler
//! TryMoveWithVelocity
//! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync.
//! Velocity will be applied over delta-time to determine the movement amount.
//! Returns this entity's world-space position after the move.
AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime);
AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) override;
};
}
@@ -1,5 +1,6 @@
/*
* 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.
* 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
*
@@ -83,7 +83,7 @@ namespace Multiplayer
return physx::PxQueryHitType::eNONE;
}
void NetworkCharacterComponent::NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
void NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
@@ -92,6 +92,7 @@ namespace Multiplayer
->Version(1);
}
NetworkCharacterComponentBase::Reflect(context);
NetworkCharacterComponentController::Reflect(context);
}
NetworkCharacterComponent::NetworkCharacterComponent()
@@ -161,6 +162,18 @@ namespace Multiplayer
return state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0;
}
void NetworkCharacterComponentController::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<NetworkCharacterRequestBus>("NetworkCharacterRequestBus")
->Event("TryMoveWithVelocity", &NetworkCharacterRequestBus::Events::TryMoveWithVelocity, {{ { "Velocity" }, { "DeltaTime" } }});
behaviorContext->Class<NetworkCharacterComponentController>("NetworkCharacterComponentController")
->RequestBus("NetworkCharacterRequestBus");
}
}
NetworkCharacterComponentController::NetworkCharacterComponentController(NetworkCharacterComponent& parent)
: NetworkCharacterComponentControllerBase(parent)
{
@@ -169,12 +182,12 @@ namespace Multiplayer
void NetworkCharacterComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
NetworkCharacterRequestBus::Handler::BusConnect(GetEntity()->GetId());
}
void NetworkCharacterComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
NetworkCharacterRequestBus::Handler::BusDisconnect(GetEntity()->GetId());
}
AZ::Vector3 NetworkCharacterComponentController::TryMoveWithVelocity(const AZ::Vector3& velocity, [[maybe_unused]] float deltaTime)
@@ -1,5 +1,6 @@
/*
* 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.
* 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
*
@@ -24,6 +24,13 @@ namespace Multiplayer
}
}
void NetworkSpawnableHolderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
// TransformService isn't strictly required in this component (Identity transform will be used by default)
// However we need to make sure if there's a component providing TransformService it is activated first.
dependent.push_back(AZ_CRC_CE("TransformService"));
}
NetworkSpawnableHolderComponent::NetworkSpawnableHolderComponent()
{
}
@@ -38,11 +45,9 @@ namespace Multiplayer
{
AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity();
AzFramework::TransformComponent* rootEntityTransformComponent =
GetEntity()->FindComponent<AzFramework::TransformComponent>();
if (rootEntityTransformComponent)
if(auto* transformInterface = GetEntity()->GetTransform())
{
rootEntityTransform = rootEntityTransformComponent->GetWorldTM();
rootEntityTransform = transformInterface->GetWorldTM();
}
INetworkEntityManager* networkEntityManager = GetNetworkEntityManager();
@@ -22,11 +22,12 @@ namespace Multiplayer
public:
AZ_COMPONENT(NetworkSpawnableHolderComponent, "{B0E3ADEE-FCB4-4A32-8D4F-6920F1CB08E4}");
static void Reflect(AZ::ReflectContext* context);
NetworkSpawnableHolderComponent();;
~NetworkSpawnableHolderComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
//! AZ::Component overrides.
//! @{
void Activate() override;
@@ -652,8 +652,13 @@ namespace ScriptCanvas
return {};
}
bool Method::GetBehaviorContextClassMethod(const AZStd::string&, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const
bool Method::GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const
{
if (m_lookupName.empty() && m_className.empty())
{
return false;
}
AZStd::string prettyClassName;
AZStd::string methodName = m_lookupName;
@@ -749,15 +754,14 @@ namespace ScriptCanvas
AZStd::tuple<const AZ::BehaviorMethod*, MethodType, EventType, const AZ::BehaviorClass*> Method::LookupMethod() const
{
using TupleType = AZStd::tuple<const AZ::BehaviorMethod*, MethodType, EventType, const AZ::BehaviorClass*>;
AZStd::string methodName = m_lookupName;
AZStd::string prettyClassName;
const AZ::BehaviorClass* bcClass{};
const AZ::BehaviorMethod* method{};
EventType eventType;
if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType))
if (GetBehaviorContextClassMethod(bcClass, method, eventType))
{
return TupleType{ method, m_methodType, eventType, bcClass };
}
@@ -776,7 +780,7 @@ namespace ScriptCanvas
const AZ::BehaviorMethod* method{};
EventType eventType;
if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType))
if (GetBehaviorContextClassMethod(bcClass, method, eventType))
{
m_eventType = eventType;
ConfigureMethod(*method, bcClass);
@@ -168,7 +168,7 @@ namespace ScriptCanvas
AZ_INLINE void SetWarnOnMissingFunction(bool enabled) { m_warnOnMissingFunction = enabled; }
bool GetBehaviorContextClassMethod(const AZStd::string& name, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const;
bool GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const;
private:
friend struct ScriptCanvas::BehaviorContextMethodHelper;
+3 -1
View File
@@ -155,7 +155,9 @@ function(ly_copy source_file target_directory)
endfunction()
function(ly_download_and_codesign_sdk_python)
execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake)
execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}
)
fixup_python_framework(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework)
codesign_python_framework_binaries(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework)
codesign_file(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework @LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist)