Merge branch 'development' of https://github.com/o3de/o3de into mp_autogen_move
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+7
-1
@@ -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))
|
||||
|
||||
+6
-4
@@ -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)
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
+167
-6
@@ -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"
|
||||
|
||||
+29
-1
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user