ATOM-6088 Unified approach to send draws to a single viewport

- Created a template class TagRegistery which implements the previous DrawListTagRegistry. The template is used for both DrawListTagRegistry and DrawFilterTagRegistry
- Added DrawFilterTag to DrawItemKeyPair
- Added DrawFilterMask support in DrawPacket and DrawPacketBuilder
- Added CreateDynamicContext for render pipeline which allows draw to selected render pipeline.
- Updated RasterPass's BuildCommandListInternal function to filter draw items for its owner RenderPipeline.
- Updated RHI unit tests.
main
qingtao 5 years ago
parent 743d6a511a
commit 12f528d744

@ -34,6 +34,7 @@ namespace AZ
constexpr uint32_t StreamCountMax = 12;
constexpr uint32_t StreamChannelCountMax = 16;
constexpr uint32_t DrawListTagCountMax = 64;
constexpr uint32_t DrawFilterTagCountMax = 32;
constexpr uint32_t MultiSampleCustomLocationsCountMax = 16;
constexpr uint32_t MultiSampleCustomLocationGridSize = 16;
constexpr uint32_t SubpassCountMax = 10;

@ -0,0 +1,23 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/RHI/DrawItem.h>
#include <Atom/RHI/TagRegistry.h>
namespace AZ
{
namespace RHI
{
using DrawFilterTagRegistry = TagRegistry<DrawFilterTag, Limits::Pipeline::DrawFilterTagCountMax>;
}
}

@ -11,6 +11,7 @@
*/
#pragma once
#include <Atom/RHI.Reflect/Handle.h>
#include <Atom/RHI.Reflect/Limits.h>
#include <Atom/RHI/StreamBufferView.h>
#include <Atom/RHI/IndexBufferView.h>
@ -152,21 +153,31 @@ namespace AZ
};
using DrawItemSortKey = int64_t;
// A filter associate to a DrawItem which can be used to filter the DrawItem when submitting to command list
using DrawFilterTag = Handle<uint8_t>;
using DrawFilterMask = uint32_t; // AZStd::bitset's impelmentation is too expensive.
constexpr uint32_t DrawFilterMaskDefaultValue = uint32_t(-1); // Default all bit to 1.
static_assert(sizeof(DrawFilterMask) * 8 >= Limits::Pipeline::DrawFilterTagCountMax, "DrawFilterMask doesn't have enough bits for maximum tag count");
struct DrawItemKeyPair
{
DrawItemKeyPair() = default;
DrawItemKeyPair(const DrawItem* item, DrawItemSortKey sortKey)
DrawItemKeyPair(const DrawItem* item, DrawItemSortKey sortKey = 0, DrawFilterMask filterMask = DrawFilterMaskDefaultValue)
: m_item{item}
, m_sortKey{sortKey}
{}
, m_drawFilterMask{filterMask}
{
}
bool operator == (const DrawItemKeyPair& rhs) const
{
return m_item == rhs.m_item &&
m_sortKey == rhs.m_sortKey &&
m_depth == rhs.m_depth;
m_depth == rhs.m_depth &&
m_drawFilterMask == rhs.m_drawFilterMask
;
}
bool operator != (const DrawItemKeyPair& rhs) const
@ -182,6 +193,7 @@ namespace AZ
const DrawItem* m_item = nullptr;
DrawItemSortKey m_sortKey = 0;
float m_depth = 0.0f;
DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
};
}

@ -12,83 +12,12 @@
#pragma once
#include <Atom/RHI/DrawList.h>
#include <AzCore/Name/Name.h>
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzCore/std/parallel/shared_mutex.h>
#include <Atom/RHI/TagRegistry.h>
namespace AZ
{
namespace RHI
{
/**
* Allocates and registers draw list tags by name, allowing the user to acquire and find tags from names.
* The class is designed to map user-friendly tag names defined through content or higher level code to
* low-level tags, which are simple handles.
*
* Some notes about usage and design:
* - DrawListTag values represent indexes into a bitmask, which allows for fast comparison when filtering
* draw items into draw lists (see View::HasDrawListTag()).
* - Tags are reference counted, which means multiple calls to 'Acquire' with the same name will increment
* the internal reference count on the tag. This allows shared ownership between systems, if necessary.
* - FindTag is provided to search for a tag reference without taking ownership.
* - Names are case sensitive.
*/
class DrawListTagRegistry final
: public AZStd::intrusive_base
{
public:
AZ_CLASS_ALLOCATOR(DrawListTagRegistry, AZ::SystemAllocator, 0);
AZ_DISABLE_COPY_MOVE(DrawListTagRegistry);
static Ptr<DrawListTagRegistry> Create();
/**
* Resets the registry back to an empty state. All references are released.
*/
void Reset();
/**
* Acquires a draw list tag from the provided name (case sensitive). If the tag already existed, it is ref-counted.
* Returns a valid tag on success; returns a null tag if the registry is at full capacity. You must
* call ReleaseTag() if successful.
*/
DrawListTag AcquireTag(const Name& drawListName);
/**
* Releases a reference to a tag. Tags are ref-counted, so it's necessary to maintain ownership of the
* tag and release when its no longer needed.
*/
void ReleaseTag(DrawListTag drawListTag);
/**
* Finds the tag associated with the provided name (case sensitive). If a tag exists with that name, the tag
* is returned. The reference count is NOT incremented on success; ownership is not passed to the user. If
* the tag does not exist, a null tag is returned.
*/
DrawListTag FindTag(const Name& drawListName) const;
/**
* Returns the name of the given DrawListTag, or empty string if the tag is not registered.
*/
Name GetName(DrawListTag tag) const;
/**
* Returns the number of allocated tags in the registry.
*/
size_t GetAllocatedTagCount() const;
private:
DrawListTagRegistry() = default;
struct Entry
{
Name m_name;
size_t m_refCount = 0;
};
mutable AZStd::shared_mutex m_mutex;
AZStd::array<Entry, Limits::Pipeline::DrawListTagCountMax> m_entriesByTag;
size_t m_allocatedTagCount = 0;
};
using DrawListTagRegistry = TagRegistry<DrawListTag, Limits::Pipeline::DrawListTagCountMax>;
}
}

@ -21,45 +21,48 @@ namespace AZ
namespace RHI
{
/**
* DrawPacket is a packed data structure (one contiguous allocation) containing a collection of
* DrawItems and their associated array data. Each draw item in the packet is associated
* with a DrawListTag. All draw items in the packet share the same set of shader resource
* groups, index buffer, and draw arguments.
*
* Some notes about design and usage:
* - Draw packets should be used to 'broadcast' variations of the same 'object' to multiple passes.
* For example: 'Shadow', 'Depth', 'Forward'.
*
* - Draw packets can be re-used between different views, scenes, or passes. The embedded shader resource groups
* should represent only the local data necessary to describe the 'object', not the full context including
* scene / view / pass specific state. They serve as a 'template'.
*
* - The packet is self-contained and does not reference external memory. Use DrawPacketBuilder to construct
* an instance and either store in an RHI::Ptr or call 'delete' to release.
*/
//!
//! DrawPacket is a packed data structure (one contiguous allocation) containing a collection of
//! DrawItems and their associated array data. Each draw item in the packet is associated
//! with a DrawListTag. All draw items in the packet share the same set of shader resource
//! groups, index buffer, and draw arguments.
//!
//! Some notes about design and usage:
//! - Draw packets should be used to 'broadcast' variations of the same 'object' to multiple passes.
//! For example: 'Shadow', 'Depth', 'Forward'.
//!
//! - Draw packets can be re-used between different views, scenes, or passes. The embedded shader resource groups
//! should represent only the local data necessary to describe the 'object', not the full context including
//! scene / view / pass specific state. They serve as a 'template'.
//!
//! - The packet is self-contained and does not reference external memory. Use DrawPacketBuilder to construct
//! an instance and either store in an RHI::Ptr or call 'delete' to release.
//!
class DrawPacket final : public AZStd::intrusive_base
{
friend class DrawPacketBuilder;
public:
using DrawItemVisitor = AZStd::function<void(DrawListTag, DrawItemKeyPair)>;
/// Draw packets cannot be move constructed or copied, as they contain an additional memory payload.
//! Draw packets cannot be move constructed or copied, as they contain an additional memory payload.
AZ_DISABLE_COPY_MOVE(DrawPacket);
/// Returns the mask representing all the draw lists affected by the packet.
//! Returns the mask representing all the draw lists affected by the packet.
DrawListMask GetDrawListMask() const;
/// Returns the number of draw items stored in the packet.
//! Returns the number of draw items stored in the packet.
size_t GetDrawItemCount() const;
/// Returns the draw item / sort key associated with the provided index.
//! Returns the draw item / sort key associated with the provided index.
DrawItemKeyPair GetDrawItem(size_t index) const;
/// Returns the draw list tag associated with the provided index.
//! Returns the draw list tag associated with the provided index.
DrawListTag GetDrawListTag(size_t index) const;
/// Overloaded operator delete for freeing a draw packet.
//! Returns the draw filter mask which applied to all the draw items.
DrawFilterMask GetDrawFilterMask() const;
//! Overloaded operator delete for freeing a draw packet.
void operator delete(void* p, size_t size);
private:
@ -72,6 +75,9 @@ namespace AZ
// The bit-mask of all active filter tags.
DrawListMask m_drawListMask = 0;
// The draw filter applies to each draw item
DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
// The index buffer view used when the draw call is indexed.
IndexBufferView m_indexBufferView;

@ -29,23 +29,26 @@ namespace AZ
{
DrawRequest() = default;
/// The filter tag used to direct the draw item.
//! The filter tag used to direct the draw item.
DrawListTag m_listTag;
/// The stencil ref value used for this draw item.
//! The stencil ref value used for this draw item.
uint8_t m_stencilRef = 0;
/// The array of stream buffers to bind for this draw item.
//! The array of stream buffers to bind for this draw item.
AZStd::array_view<StreamBufferView> m_streamBufferViews;
/// Shader resource group unique for this draw request
//! Shader resource group unique for this draw request
const ShaderResourceGroup* m_uniqueShaderResourceGroup = nullptr;
/// The pipeline state assigned to this draw item.
//! The pipeline state assigned to this draw item.
const PipelineState* m_pipelineState = nullptr;
/// The sort key assigned to this draw item.
//! The sort key assigned to this draw item.
DrawItemSortKey m_sortKey = 0;
//! The filter associates to this draw item.
DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
};
// NOTE: This is configurable; just used to control the amount of memory held by the builder.
@ -69,6 +72,8 @@ namespace AZ
void AddShaderResourceGroup(const ShaderResourceGroup* shaderResourceGroup);
void SetDrawFilterMask(DrawFilterMask filterMask);
void AddDrawItem(const DrawRequest& request);
const DrawPacket* End();
@ -79,6 +84,7 @@ namespace AZ
IAllocatorAllocate* m_allocator = nullptr;
DrawArguments m_drawArguments;
DrawListMask m_drawListMask = 0;
DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
size_t m_streamBufferViewCount = 0;
IndexBufferView m_indexBufferView;
AZStd::fixed_vector<DrawRequest, DrawItemCountMax> m_drawRequests;

@ -65,7 +65,7 @@ namespace AZ
RHI::Ptr<RHI::Device> InitInternalDevice();
RHI::Ptr<RHI::Device> m_device;
RHI::Ptr<RHI::DrawListTagRegistry> m_drawListTagRegistry;
RHI::DrawListTagRegistry m_drawListTagRegistry;
RHI::Ptr<RHI::PipelineStateCache> m_pipelineStateCache;
RHI::FrameScheduler m_frameScheduler;
RHI::FrameSchedulerCompileRequest m_compileRequest;

@ -15,13 +15,13 @@
#include <AzCore/Name/Name.h>
#include <AzCore/EBus/EBus.h>
#include <Atom/RHI.Reflect/FrameSchedulerEnums.h>
#include <Atom/RHI/DrawListTagRegistry.h>
namespace AZ
{
namespace RHI
{
class Device;
class DrawListTagRegistry;
class FrameGraphBuilder;
class PipelineState;
class PipelineStateCache;

@ -0,0 +1,177 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Name/Name.h>
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzCore/std/parallel/shared_mutex.h>
namespace AZ
{
namespace RHI
{
//!
//! Allocates and registers tags by name, allowing the user to acquire and find tags from names.
//! The class is designed to map user-friendly tag names defined through content or higher level code to
//! low-level tags, which are simple handles.
//!
//! Some notes about usage and design:
//! - TagType need to be a Handle<Integer> type.
//! - Tags are reference counted, which means multiple calls to 'Acquire' with the same name will increment
//! the internal reference count on the tag. This allows shared ownership between systems, if necessary.
//! - FindTag is provided to search for a tag reference without taking ownership.
//! - Names are case sensitive.
//!
template<typename TagType, size_t MaxTagCount>
class TagRegistry final
: public AZStd::intrusive_base
{
public:
//! Resets the registry back to an empty state. All references are released.
void Reset();
//! Acquires a tag from the provided name (case sensitive). If the tag already existed, it is ref-counted.
//! Returns a valid tag on success; returns a null tag if the registry is at full capacity. You must
//! call ReleaseTag() if successful.
TagType AcquireTag(const Name& drawListName);
//! Releases a reference to a tag. Tags are ref-counted, so it's necessary to maintain ownership of the
//! tag and release when its no longer needed.
void ReleaseTag(TagType drawListTag);
//! Finds the tag associated with the provided name (case sensitive). If a tag exists with that name, the tag
//! is returned. The reference count is NOT incremented on success; ownership is not passed to the user. If
//! the tag does not exist, a null tag is returned.
TagType FindTag(const Name& drawListName) const;
//! Returns the name of the given DrawListTag, or empty string if the tag is not registered.
Name GetName(TagType tag) const;
//! Returns the number of allocated tags in the registry.
size_t GetAllocatedTagCount() const;
private:
struct Entry
{
Name m_name;
size_t m_refCount = 0;
};
mutable AZStd::shared_mutex m_mutex;
AZStd::array<Entry, MaxTagCount> m_entriesByTag;
size_t m_allocatedTagCount = 0;
};
template<typename TagType, size_t MaxTagCount>
void TagRegistry<TagType, MaxTagCount>::Reset()
{
AZStd::unique_lock<AZStd::shared_mutex> lock(m_mutex);
m_entriesByTag.fill({});
m_allocatedTagCount = 0;
}
template<typename TagType, size_t MaxTagCount>
TagType TagRegistry<TagType, MaxTagCount>::AcquireTag(const Name& tagName)
{
if (tagName.IsEmpty())
{
return {};
}
TagType tag;
Entry* foundEmptyEntry = nullptr;
AZStd::unique_lock<AZStd::shared_mutex> lock(m_mutex);
for (size_t i = 0; i < m_entriesByTag.size(); ++i)
{
Entry& entry = m_entriesByTag[i];
// Found an empty entry. Cache off the tag and pointer, but keep searching to find if
// another entry holds the same name.
if (entry.m_refCount == 0 && !foundEmptyEntry)
{
foundEmptyEntry = &entry;
tag = TagType(i);
}
else if (entry.m_name == tagName)
{
entry.m_refCount++;
return TagType(i);
}
}
// No other entry holds the name, so allocate the empty entry.
if (foundEmptyEntry)
{
foundEmptyEntry->m_refCount = 1;
foundEmptyEntry->m_name = tagName;
++m_allocatedTagCount;
}
return tag;
}
template<typename TagType, size_t MaxTagCount>
void TagRegistry<TagType, MaxTagCount>::ReleaseTag(TagType tag)
{
if (tag.IsValid())
{
AZStd::unique_lock<AZStd::shared_mutex> lock(m_mutex);
Entry& entry = m_entriesByTag[tag.GetIndex()];
const size_t refCount = --entry.m_refCount;
AZ_Assert(
refCount != static_cast<size_t>(-1), "Attempted to forfeit a tag that is not valid. Tag{%d},Name{'%s'}", tag,
entry.m_name.GetCStr());
if (refCount == 0)
{
entry.m_name = Name();
--m_allocatedTagCount;
}
}
}
template<typename TagType, size_t MaxTagCount>
TagType TagRegistry<TagType, MaxTagCount>::FindTag(const Name& tagName) const
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_mutex);
for (size_t i = 0; i < m_entriesByTag.size(); ++i)
{
if (m_entriesByTag[i].m_name == tagName)
{
return TagType(i);
}
}
return {};
}
template<typename TagType, size_t MaxTagCount>
Name TagRegistry<TagType, MaxTagCount>::GetName(TagType tag) const
{
if (tag.GetIndex() < m_entriesByTag.size())
{
return m_entriesByTag[tag.GetIndex()].m_name;
}
else
{
return Name();
}
}
template<typename TagType, size_t MaxTagCount>
size_t TagRegistry<TagType, MaxTagCount>::GetAllocatedTagCount() const
{
return m_allocatedTagCount;
}
}
}

@ -1,117 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/RHI/DrawListTagRegistry.h>
namespace AZ
{
namespace RHI
{
Ptr<DrawListTagRegistry> DrawListTagRegistry::Create()
{
return aznew DrawListTagRegistry;
}
void DrawListTagRegistry::Reset()
{
AZStd::unique_lock<AZStd::shared_mutex> lock(m_mutex);
m_entriesByTag.fill({});
m_allocatedTagCount = 0;
}
DrawListTag DrawListTagRegistry::AcquireTag(const Name& drawListName)
{
if (drawListName.IsEmpty())
{
return {};
}
DrawListTag drawListTag;
Entry* foundEmptyEntry = nullptr;
AZStd::unique_lock<AZStd::shared_mutex> lock(m_mutex);
for (size_t i = 0; i < m_entriesByTag.size(); ++i)
{
Entry& entry = m_entriesByTag[i];
// Found an empty entry. Cache off the tag and pointer, but keep searching to find if
// another entry holds the same name.
if (entry.m_refCount == 0 && !foundEmptyEntry)
{
foundEmptyEntry = &entry;
drawListTag = DrawListTag(i);
}
else if (entry.m_name == drawListName)
{
entry.m_refCount++;
return DrawListTag(i);
}
}
// No other entry holds the name, so allocate the empty entry.
if (foundEmptyEntry)
{
foundEmptyEntry->m_refCount = 1;
foundEmptyEntry->m_name = drawListName;
++m_allocatedTagCount;
}
return drawListTag;
}
void DrawListTagRegistry::ReleaseTag(DrawListTag drawListTag)
{
if (drawListTag.IsValid())
{
AZStd::unique_lock<AZStd::shared_mutex> lock(m_mutex);
Entry& entry = m_entriesByTag[drawListTag.GetIndex()];
const size_t refCount = --entry.m_refCount;
AZ_Assert(refCount != static_cast<size_t>(-1), "Attempted to forfeit a tag that is not valid. Tag{%d},Name{'%s'}", drawListTag, entry.m_name.GetCStr());
if (refCount == 0)
{
entry.m_name = Name();
--m_allocatedTagCount;
}
}
}
DrawListTag DrawListTagRegistry::FindTag(const Name& drawListName) const
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_mutex);
for (size_t i = 0; i < m_entriesByTag.size(); ++i)
{
if (m_entriesByTag[i].m_name == drawListName)
{
return DrawListTag(i);
}
}
return {};
}
Name DrawListTagRegistry::GetName(DrawListTag tag) const
{
if (tag.GetIndex() < m_entriesByTag.size())
{
return m_entriesByTag[tag.GetIndex()].m_name;
}
else
{
return Name();
}
}
size_t DrawListTagRegistry::GetAllocatedTagCount() const
{
return m_allocatedTagCount;
}
}
}

@ -27,7 +27,7 @@ namespace AZ
DrawItemKeyPair DrawPacket::GetDrawItem(size_t index) const
{
AZ_Assert(index < GetDrawItemCount(), "Out of bounds array access!");
return DrawItemKeyPair(&m_drawItems[index], m_drawItemSortKeys[index]);
return DrawItemKeyPair(&m_drawItems[index], m_drawItemSortKeys[index], m_drawFilterMask);
}
DrawListTag DrawPacket::GetDrawListTag(size_t index) const
@ -36,6 +36,11 @@ namespace AZ
return m_drawListTags[index];
}
DrawFilterMask DrawPacket::GetDrawFilterMask() const
{
return m_drawFilterMask;
}
DrawListMask DrawPacket::GetDrawListMask() const
{
return m_drawListMask;
@ -46,4 +51,4 @@ namespace AZ
reinterpret_cast<const DrawPacket*>(p)->m_allocator->DeAllocate(p);
}
}
}
}

@ -82,6 +82,11 @@ namespace AZ
}
}
void DrawPacketBuilder::SetDrawFilterMask(DrawFilterMask filterMask)
{
m_drawFilterMask = filterMask;
}
void DrawPacketBuilder::AddDrawItem(const DrawRequest& request)
{
if (request.m_listTag.IsValid())
@ -165,6 +170,7 @@ namespace AZ
drawPacket->m_allocator = m_allocator;
drawPacket->m_indexBufferView = m_indexBufferView;
drawPacket->m_drawListMask = m_drawListMask;
drawPacket->m_drawFilterMask = m_drawFilterMask;
if (shaderResourceGroupsOffset.IsValid())
{
@ -288,6 +294,7 @@ namespace AZ
m_rootConstants = {};
m_scissors.clear();
m_viewports.clear();
m_drawFilterMask = DrawFilterMaskDefaultValue;
}
}
}

@ -61,7 +61,6 @@ namespace AZ
return;
}
m_drawListTagRegistry = RHI::DrawListTagRegistry::Create();
m_pipelineStateCache = RHI::PipelineStateCache::Create(*m_device);
frameSchedulerDescriptor.m_transientAttachmentPoolDescriptor.m_renderTargetBudgetInBytes = m_platformLimitsDescriptor->m_transientAttachmentPoolBudgets.m_renderTargetBudgetInBytes;
@ -107,7 +106,7 @@ namespace AZ
// Register draw list tags declared from content.
for (const Name& drawListName : descriptor.m_drawListTags)
{
RHI::DrawListTag drawListTag = m_drawListTagRegistry->AcquireTag(drawListName);
RHI::DrawListTag drawListTag = m_drawListTagRegistry.AcquireTag(drawListName);
AZ_Warning("RHISystem", drawListTag.IsValid(), "Failed to register draw list tag '%s'. Registry at capacity.", drawListName.GetCStr());
}
@ -199,7 +198,6 @@ namespace AZ
m_frameScheduler.Shutdown();
m_platformLimitsDescriptor = nullptr;
m_drawListTagRegistry = nullptr;
m_pipelineStateCache = nullptr;
m_device->PreShutdown();
AZ_Assert(m_device->use_count()==1, "The ref count for Device is %i but it should be 1 here to ensure all the resources are released", m_device->use_count());
@ -252,7 +250,7 @@ namespace AZ
RHI::DrawListTagRegistry* RHISystem::GetDrawListTagRegistry()
{
return m_drawListTagRegistry.get();
return &m_drawListTagRegistry;
}
const RHI::FrameSchedulerCompileRequest& RHISystem::GetFrameSchedulerCompileRequest() const

@ -170,12 +170,10 @@ namespace UnitTest
RHITestFixture::SetUp();
m_factory.reset(aznew Factory());
m_drawListTagRegistry = RHI::DrawListTagRegistry::Create();
}
void TearDown() override
{
m_drawListTagRegistry = nullptr;
m_factory.reset();
RHITestFixture::TearDown();
@ -184,7 +182,7 @@ namespace UnitTest
protected:
static const uint32_t s_randomSeed = 1234;
RHI::Ptr<RHI::DrawListTagRegistry> m_drawListTagRegistry;
RHI::DrawListTagRegistry m_drawListTagRegistry;
RHI::DrawListContext m_drawListContext;
AZStd::unique_ptr<Factory> m_factory;
@ -192,12 +190,12 @@ namespace UnitTest
TEST_F(DrawPacketTest, TestDrawListTagRegistryNullCase)
{
RHI::DrawListTag nullTag = m_drawListTagRegistry->AcquireTag(Name());
RHI::DrawListTag nullTag = m_drawListTagRegistry.AcquireTag(Name());
EXPECT_TRUE(nullTag.IsNull());
EXPECT_EQ(m_drawListTagRegistry->GetAllocatedTagCount(), 0);
EXPECT_EQ(m_drawListTagRegistry.GetAllocatedTagCount(), 0);
m_drawListTagRegistry->ReleaseTag(nullTag);
EXPECT_EQ(m_drawListTagRegistry->GetAllocatedTagCount(), 0);
m_drawListTagRegistry.ReleaseTag(nullTag);
EXPECT_EQ(m_drawListTagRegistry.GetAllocatedTagCount(), 0);
}
TEST_F(DrawPacketTest, TestDrawListTagRegistrySimple)
@ -205,39 +203,39 @@ namespace UnitTest
const Name forwardName1("Forward");
const Name forwardName2("forward");
RHI::DrawListTag forwardTag1 = m_drawListTagRegistry->AcquireTag(forwardName1);
RHI::DrawListTag forwardTag2 = m_drawListTagRegistry->AcquireTag(forwardName2);
RHI::DrawListTag forwardTag1 = m_drawListTagRegistry.AcquireTag(forwardName1);
RHI::DrawListTag forwardTag2 = m_drawListTagRegistry.AcquireTag(forwardName2);
EXPECT_FALSE(forwardTag1.IsNull());
EXPECT_FALSE(forwardTag2.IsNull());
EXPECT_NE(forwardTag1, forwardTag2);
RHI::DrawListTag forwardTag3 = m_drawListTagRegistry->AcquireTag(forwardName1);
RHI::DrawListTag forwardTag3 = m_drawListTagRegistry.AcquireTag(forwardName1);
EXPECT_EQ(forwardTag1, forwardTag3);
m_drawListTagRegistry->ReleaseTag(forwardTag1);
m_drawListTagRegistry->ReleaseTag(forwardTag2);
m_drawListTagRegistry->ReleaseTag(forwardTag3);
m_drawListTagRegistry.ReleaseTag(forwardTag1);
m_drawListTagRegistry.ReleaseTag(forwardTag2);
m_drawListTagRegistry.ReleaseTag(forwardTag3);
EXPECT_EQ(m_drawListTagRegistry->GetAllocatedTagCount(), 0);
EXPECT_EQ(m_drawListTagRegistry.GetAllocatedTagCount(), 0);
}
TEST_F(DrawPacketTest, TestDrawListTagRegistryDeAllocateAssert)
{
AZ_TEST_START_ASSERTTEST;
EXPECT_EQ(m_drawListTagRegistry->GetAllocatedTagCount(), 0);
EXPECT_EQ(m_drawListTagRegistry.GetAllocatedTagCount(), 0);
const Name tagName{"Test"};
RHI::DrawListTag tag = m_drawListTagRegistry->AcquireTag(tagName);
m_drawListTagRegistry->AcquireTag(tagName);
m_drawListTagRegistry->AcquireTag(tagName);
m_drawListTagRegistry->ReleaseTag(tag);
m_drawListTagRegistry->ReleaseTag(tag);
m_drawListTagRegistry->ReleaseTag(tag);
RHI::DrawListTag tag = m_drawListTagRegistry.AcquireTag(tagName);
m_drawListTagRegistry.AcquireTag(tagName);
m_drawListTagRegistry.AcquireTag(tagName);
m_drawListTagRegistry.ReleaseTag(tag);
m_drawListTagRegistry.ReleaseTag(tag);
m_drawListTagRegistry.ReleaseTag(tag);
// One additional forfeit should assert.
m_drawListTagRegistry->ReleaseTag(tag);
m_drawListTagRegistry.ReleaseTag(tag);
AZ_TEST_STOP_ASSERTTEST(1);
}
@ -256,15 +254,15 @@ namespace UnitTest
// Acquire
if (random.GetRandom() % 2)
{
RHI::DrawListTag tag = m_drawListTagRegistry->AcquireTag(tagNameUnique);
RHI::DrawListTag tag = m_drawListTagRegistry.AcquireTag(tagNameUnique);
if (tag.IsNull())
{
EXPECT_EQ(m_drawListTagRegistry->GetAllocatedTagCount(), RHI::Limits::Pipeline::DrawListTagCountMax);
EXPECT_EQ(m_drawListTagRegistry.GetAllocatedTagCount(), RHI::Limits::Pipeline::DrawListTagCountMax);
}
else
{
EXPECT_LT(m_drawListTagRegistry->GetAllocatedTagCount(), RHI::Limits::Pipeline::DrawListTagCountMax);
EXPECT_LT(m_drawListTagRegistry.GetAllocatedTagCount(), RHI::Limits::Pipeline::DrawListTagCountMax);
acquiredTags.emplace_back(tag);
}
}
@ -276,26 +274,26 @@ namespace UnitTest
RHI::DrawListTag tag = acquiredTags[tagIndex];
size_t allocationCountBefore = m_drawListTagRegistry->GetAllocatedTagCount();
m_drawListTagRegistry->ReleaseTag(tag);
size_t allocationCountAfter = m_drawListTagRegistry->GetAllocatedTagCount();
size_t allocationCountBefore = m_drawListTagRegistry.GetAllocatedTagCount();
m_drawListTagRegistry.ReleaseTag(tag);
size_t allocationCountAfter = m_drawListTagRegistry.GetAllocatedTagCount();
EXPECT_EQ(allocationCountBefore - allocationCountAfter, 1);
acquiredTags.erase(acquiredTags.begin() + tagIndex);
}
EXPECT_EQ(acquiredTags.size(), m_drawListTagRegistry->GetAllocatedTagCount());
EXPECT_EQ(acquiredTags.size(), m_drawListTagRegistry.GetAllocatedTagCount());
}
// Erase all references, make sure the registry is empty again.
for (RHI::DrawListTag tag : acquiredTags)
{
m_drawListTagRegistry->ReleaseTag(tag);
m_drawListTagRegistry.ReleaseTag(tag);
}
acquiredTags.clear();
EXPECT_EQ(m_drawListTagRegistry->GetAllocatedTagCount(), 0);
EXPECT_EQ(m_drawListTagRegistry.GetAllocatedTagCount(), 0);
}
TEST_F(DrawPacketTest, DrawPacketEmpty)

@ -36,6 +36,7 @@ set(FILES
Include/Atom/RHI/CopyItem.h
Include/Atom/RHI/ConstantsData.h
Include/Atom/RHI/DispatchItem.h
Include/Atom/RHI/DrawFilterTagRegistry.h
Include/Atom/RHI/DrawItem.h
Include/Atom/RHI/DrawList.h
Include/Atom/RHI/DrawListTagRegistry.h
@ -48,7 +49,6 @@ set(FILES
Source/RHI/ConstantsData.cpp
Source/RHI/DrawList.cpp
Source/RHI/DrawListContext.cpp
Source/RHI/DrawListTagRegistry.cpp
Source/RHI/DrawPacket.cpp
Source/RHI/DrawPacketBuilder.cpp
Include/Atom/RHI/Device.h
@ -201,4 +201,5 @@ set(FILES
Include/Atom/RHI/CpuProfiler.h
Include/Atom/RHI/CpuProfilerImpl.h
Source/RHI/CpuProfilerImpl.cpp
Include/Atom/RHI/TagRegistry.h
)

@ -214,6 +214,10 @@ namespace AZ
Scene* m_scene = nullptr;
RHI::DrawListTag m_drawListTag;
// All draw items use this filter when submit them to views
// It's set to RenderPipeline's draw filter mask if the DynamicDrawContext was created for a render pipeline.
RHI::DrawFilterMask m_drawFilter = RHI::DrawFilterMaskDefaultValue;
// Cached draw data
AZStd::vector<RHI::StreamBufferView> m_cachedStreamBufferViews;
AZStd::vector<RHI::IndexBufferView> m_cachedIndexBufferViews;

@ -56,9 +56,10 @@ namespace AZ
//! Draw calls which are made to this DynamicDrawContext will only be submitted for this scene.
//! The created DynamicDrawContext is managed by dynamic draw system.
virtual RHI::Ptr<DynamicDrawContext> CreateDynamicDrawContext(Scene* scene) = 0;
//! Create a DynamicDrawContext for specified pass
virtual RHI::Ptr<DynamicDrawContext> CreateDynamicDrawContext(Pass* pass = nullptr) = 0;
//! Create a DynamicDrawContext for specified render pipeline
//! This allows draw calls are only submitted to selected render pipeline (viewport)
virtual RHI::Ptr<DynamicDrawContext> CreateDynamicDrawContext(RenderPipeline* pipeline) = 0;
//! Get a DynamicBuffer from DynamicDrawSystem.
//! The returned buffer will be invalidated every time the RPISystem's RenderTick is called

@ -36,7 +36,7 @@ namespace AZ
// DynamicDrawInterface overrides...
RHI::Ptr<DynamicDrawContext> CreateDynamicDrawContext(Scene* scene) override;
RHI::Ptr<DynamicDrawContext> CreateDynamicDrawContext(Pass* pass) override;
RHI::Ptr<DynamicDrawContext> CreateDynamicDrawContext(RenderPipeline* pipeline) override;
RHI::Ptr<DynamicBuffer> GetDynamicBuffer(uint32_t size, uint32_t alignment = 1) override;
void DrawGeometry(Data::Instance<Material> material, const GeometryData& geometry, ScenePtr scene) override;
void AddDrawPacket(Scene* scene, AZStd::unique_ptr<const RHI::DrawPacket> drawPacket) override;

@ -184,6 +184,11 @@ namespace AZ
//! Get current render mode
RenderMode GetRenderMode() const;
//! Get draw filter tag
RHI::DrawFilterTag GetDrawFilterTag() const;
RHI::DrawFilterMask GetDrawFilterMask() const;
private:
RenderPipeline() = default;
@ -211,6 +216,8 @@ namespace AZ
// if the view already exists in map, its DrawListMask will be combined to the existing one's
void CollectPersistentViews(AZStd::map<ViewPtr, RHI::DrawListMask>& outViewMasks) const;
void SetDrawFilterTag(RHI::DrawFilterTag);
// End of functions accessed by Scene class
//////////////////////////////////////////////////
@ -250,6 +257,11 @@ namespace AZ
// Original settings from RenderPipelineDescriptor, used to revert active render settings to original settings from RenderPipelineDescriptor
PipelineRenderSettings m_originalRenderSettings;
// A tag to filter draw items submitted by passes of this render pipeline.
// This tag is allocated when it's added to a scene. It's set to invalid when it's removed to the scene.
RHI::DrawFilterTag m_drawFilterTag;
RHI::DrawFilterMask m_drawFilterMask = 0; // The corresponding mask of the m_drawFilterTag
};
} // namespace RPI

@ -14,6 +14,7 @@
#include <Atom/RHI/DrawList.h>
#include <Atom/RHI/PipelineStateDescriptor.h>
#include <Atom/RHI/DrawFilterTagRegistry.h>
#include <Atom/RHI.Reflect/FrameSchedulerEnums.h>
#include <Atom/RHI.Reflect/ShaderResourceGroupLayoutDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
@ -234,6 +235,9 @@ namespace AZ
// reference of dynamic draw system (from RPISystem)
DynamicDrawSystem* m_dynamicDrawSystem = nullptr;
// Registry which allocates draw filter tag for RenderPipeline
RHI::DrawFilterTagRegistry m_drawFilterTagRegistry;
};
// --- Template functions ---

@ -17,6 +17,7 @@
#include <Atom/RPI.Public/DynamicDraw/DynamicBuffer.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/View.h>
@ -604,6 +605,7 @@ namespace AZ
RHI::DrawItemKeyPair drawItemKeyPair;
drawItemKeyPair.m_sortKey = sortKey;
drawItemKeyPair.m_item = &drawItemInfo.m_drawItem;
drawItemKeyPair.m_drawFilterMask = m_drawFilter;
view->AddDrawItem(m_drawListTag, drawItemKeyPair);
sortKey++;
}

@ -59,6 +59,11 @@ namespace AZ
RHI::Ptr<DynamicDrawContext> DynamicDrawSystem::CreateDynamicDrawContext(Scene* scene)
{
if (!scene)
{
AZ_Error("RPI", false, "Failed to create a DynamicDrawContext: the input scene is invalid");
return nullptr;
}
RHI::Ptr<DynamicDrawContext> drawContext = aznew DynamicDrawContext();
drawContext->m_scene = scene;
@ -67,11 +72,17 @@ namespace AZ
return drawContext;
}
// [GFX TODO][ATOM-13185] Add support for creating DynamicDrawContext for Pass
RHI::Ptr<DynamicDrawContext> DynamicDrawSystem::CreateDynamicDrawContext([[maybe_unused]] Pass* pass)
RHI::Ptr<DynamicDrawContext> DynamicDrawSystem::CreateDynamicDrawContext(RenderPipeline* pipeline)
{
AZ_Error("RPI", false, "Unimplemented function");
return nullptr;
if (!pipeline || !pipeline->GetScene())
{
AZ_Error("RPI", false, "Failed to create a DynamicDrawContext: the input RenderPipeline is invalid or wasn't added to a Scene");
return nullptr;
}
auto context = CreateDynamicDrawContext(pipeline->GetScene());
context->m_drawFilter = pipeline->GetDrawFilterMask();
return context;
}
// [GFX TODO][ATOM-13184] Add support of draw geometry with material for DynamicDrawSystemInterface

@ -197,7 +197,10 @@ namespace AZ
for (const RHI::DrawItemKeyPair& drawItemKeyPair : drawListViewPartition)
{
commandList->Submit(*drawItemKeyPair.m_item);
if (drawItemKeyPair.m_drawFilterMask & m_pipeline->GetDrawFilterMask())
{
commandList->Submit(*drawItemKeyPair.m_item);
}
}
}

@ -300,6 +300,9 @@ namespace AZ
m_scene = nullptr;
m_rootPass->SetEnabled(false);
m_rootPass->QueueForRemoval();
m_drawFilterTag.Reset();
m_drawFilterMask = 0;
}
void RenderPipeline::OnPassModified()
@ -506,5 +509,28 @@ namespace AZ
{
return m_renderMode != RenderMode::NoRender;
}
RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const
{
return m_drawFilterTag;
}
RHI::DrawFilterMask RenderPipeline::GetDrawFilterMask() const
{
return m_drawFilterMask;
}
void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag)
{
m_drawFilterTag = tag;
if (m_drawFilterTag.IsValid())
{
m_drawFilterMask = 1 << tag.GetIndex();
}
else
{
m_drawFilterMask = 0;
}
}
}
}

@ -269,6 +269,8 @@ namespace AZ
return;
}
pipeline->SetDrawFilterTag(m_drawFilterTagRegistry.AcquireTag(pipelineId));
m_pipelines.push_back(pipeline);
// Set this pipeline as default if the default pipeline was empty. This pipeline should be the first pipeline be added to the scene
@ -303,6 +305,8 @@ namespace AZ
m_defaultPipeline = nullptr;
}
m_drawFilterTagRegistry.ReleaseTag(pipelineToRemove->GetDrawFilterTag());
pipelineToRemove->OnRemovedFromScene(this);
m_pipelines.erase(it);

Loading…
Cancel
Save