0b9497cd45
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
309 lines
13 KiB
C++
309 lines
13 KiB
C++
/*
|
|
* 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 <Atom/RHI/ShaderResourceGroupPool.h>
|
|
#include <Atom/RHI/BufferView.h>
|
|
#include <Atom/RHI/ImageView.h>
|
|
namespace AZ
|
|
{
|
|
namespace RHI
|
|
{
|
|
ShaderResourceGroupPool::ShaderResourceGroupPool() {}
|
|
|
|
ShaderResourceGroupPool::~ShaderResourceGroupPool() {}
|
|
|
|
ResultCode ShaderResourceGroupPool::Init(Device& device, const ShaderResourceGroupPoolDescriptor& descriptor)
|
|
{
|
|
if (Validation::IsEnabled())
|
|
{
|
|
if (descriptor.m_layout == nullptr)
|
|
{
|
|
AZ_Error("ShaderResourceGroupPool", false, "ShaderResourceGroupPoolDescriptor::m_layout must not be null.");
|
|
return ResultCode::InvalidArgument;
|
|
}
|
|
}
|
|
|
|
ResultCode resultCode = ResourcePool::Init(
|
|
device, descriptor,
|
|
[this, &device, &descriptor]()
|
|
{
|
|
return InitInternal(device, descriptor);
|
|
});
|
|
|
|
if (resultCode != ResultCode::Success)
|
|
{
|
|
return resultCode;
|
|
}
|
|
|
|
m_invalidateRegistry.SetCompileGroupFunction(
|
|
[this](ShaderResourceGroup& shaderResourceGroup)
|
|
{
|
|
QueueForCompile(shaderResourceGroup);
|
|
});
|
|
|
|
m_descriptor = descriptor;
|
|
m_hasBufferGroup = m_descriptor.m_layout->GetGroupSizeForBuffers() > 0;
|
|
m_hasImageGroup = m_descriptor.m_layout->GetGroupSizeForImages() > 0;
|
|
m_hasSamplerGroup = m_descriptor.m_layout->GetGroupSizeForSamplers() > 0;
|
|
m_hasConstants = m_descriptor.m_layout->GetConstantDataSize() > 0;
|
|
return ResultCode::Success;
|
|
}
|
|
|
|
void ShaderResourceGroupPool::ShutdownInternal()
|
|
{
|
|
AZ_Error("ShaderResourceGroupPool", m_invalidateRegistry.IsEmpty(), "ShaderResourceGroup Registry is not Empty!");
|
|
}
|
|
|
|
ResultCode ShaderResourceGroupPool::InitGroup(ShaderResourceGroup& group)
|
|
{
|
|
ResultCode resultCode = ResourcePool::InitResource(&group, [this, &group]() { return InitGroupInternal(group); });
|
|
if (resultCode == ResultCode::Success)
|
|
{
|
|
const ShaderResourceGroupLayout* layout = GetLayout();
|
|
|
|
// Pre-initialize the data so that we can build view diffs later.
|
|
group.m_data = ShaderResourceGroupData(layout);
|
|
|
|
// Cache off the binding slot for one less indirection.
|
|
group.m_bindingSlot = layout->GetBindingSlot();
|
|
}
|
|
return resultCode;
|
|
}
|
|
|
|
void ShaderResourceGroupPool::ShutdownResourceInternal(Resource& resourceBase)
|
|
{
|
|
ShaderResourceGroup& shaderResourceGroup = static_cast<ShaderResourceGroup&>(resourceBase);
|
|
|
|
UnqueueForCompile(shaderResourceGroup);
|
|
|
|
// Cease tracking references to buffer / image resources when the SRG shuts down.
|
|
if (HasImageGroup() || HasBufferGroup())
|
|
{
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_invalidateRegistryMutex);
|
|
|
|
for (const ConstPtr<ImageView>& imageView : shaderResourceGroup.GetData().GetImageGroup())
|
|
{
|
|
if (imageView)
|
|
{
|
|
m_invalidateRegistry.OnDetach(&imageView->GetResource(), &shaderResourceGroup);
|
|
}
|
|
}
|
|
|
|
for (const ConstPtr<BufferView>& bufferView : shaderResourceGroup.GetData().GetBufferGroup())
|
|
{
|
|
if (bufferView)
|
|
{
|
|
m_invalidateRegistry.OnDetach(&bufferView->GetResource(), &shaderResourceGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
shaderResourceGroup.SetData(ShaderResourceGroupData());
|
|
}
|
|
|
|
void ShaderResourceGroupPool::QueueForCompile(ShaderResourceGroup& shaderResourceGroup, const ShaderResourceGroupData& groupData)
|
|
{
|
|
AZStd::lock_guard<AZStd::shared_mutex> lock(m_groupsToCompileMutex);
|
|
|
|
bool isQueuedForCompile = shaderResourceGroup.IsQueuedForCompile();
|
|
AZ_Warning(
|
|
"ShaderResourceGroupPool", !isQueuedForCompile,
|
|
"Attempting to compile an SRG that's already been queued for compile. Only compile an SRG once per frame.");
|
|
|
|
if (!isQueuedForCompile)
|
|
{
|
|
CalculateGroupDataDiff(shaderResourceGroup, groupData);
|
|
|
|
shaderResourceGroup.SetData(groupData);
|
|
|
|
QueueForCompileNoLock(shaderResourceGroup);
|
|
}
|
|
}
|
|
|
|
void ShaderResourceGroupPool::QueueForCompile(ShaderResourceGroup& group)
|
|
{
|
|
AZStd::lock_guard<AZStd::shared_mutex> lock(m_groupsToCompileMutex);
|
|
QueueForCompileNoLock(group);
|
|
}
|
|
|
|
void ShaderResourceGroupPool::QueueForCompileNoLock(ShaderResourceGroup& group)
|
|
{
|
|
if (!group.m_isQueuedForCompile)
|
|
{
|
|
group.m_isQueuedForCompile = true;
|
|
m_groupsToCompile.emplace_back(&group);
|
|
}
|
|
}
|
|
|
|
void ShaderResourceGroupPool::UnqueueForCompile(ShaderResourceGroup& shaderResourceGroup)
|
|
{
|
|
AZStd::lock_guard<AZStd::shared_mutex> lock(m_groupsToCompileMutex);
|
|
if (shaderResourceGroup.m_isQueuedForCompile)
|
|
{
|
|
shaderResourceGroup.m_isQueuedForCompile = false;
|
|
m_groupsToCompile.erase(AZStd::find(m_groupsToCompile.begin(), m_groupsToCompile.end(), &shaderResourceGroup));
|
|
}
|
|
}
|
|
|
|
void ShaderResourceGroupPool::Compile(ShaderResourceGroup& group, const ShaderResourceGroupData& groupData)
|
|
{
|
|
CalculateGroupDataDiff(group, groupData);
|
|
group.SetData(groupData);
|
|
CompileGroupInternal(group, group.GetData());
|
|
}
|
|
|
|
void ShaderResourceGroupPool::CalculateGroupDataDiff(ShaderResourceGroup& shaderResourceGroup, const ShaderResourceGroupData& groupData)
|
|
{
|
|
// Calculate diffs for updating the resource registry.
|
|
if (HasImageGroup() || HasBufferGroup())
|
|
{
|
|
/**
|
|
* SRG's hold references to views, and views references to resources. Resources can become invalid, either
|
|
* due to an explicit Shutdown() / Init() event, or an explicit call to RHI::Resource::Invalidate. In either
|
|
* case, the SRG will need to be re-compiled.
|
|
*
|
|
* To facilitate this, we compare the new data with the previous data and compare views. When views are attached
|
|
* and detached from SRG's, we store those associations in an SRG-pool local registry. The system currently takes a
|
|
* lock in order to build the diffs. This means compiling multiple SRG's on the same pool across several jobs is not
|
|
* going to be performant if the SRG's have buffers / images embedded.
|
|
*
|
|
* FUTURE CONSIDERATIONS:
|
|
*
|
|
* - If buffers and images are initialized at allocation time instead of separately, it would only be necessary to track
|
|
* resources which the platform can invalidate. This may result in a smaller set to track. There's insufficient data
|
|
* to determine if this is the case right now.
|
|
*
|
|
* - The locking could be reduced by making the registry lockless (which would be tricky, if not impossible, since it's
|
|
* a map of maps), or by reducing the granularity of locks (perhaps by having multiple registries).
|
|
*/
|
|
|
|
const auto ComputeDiffs = [this, &shaderResourceGroup](const ResourceView* resourceViewOld, const ResourceView* resourceViewNew)
|
|
{
|
|
if (resourceViewOld != resourceViewNew)
|
|
{
|
|
if (resourceViewNew)
|
|
{
|
|
m_invalidateRegistry.OnAttach(&resourceViewNew->GetResource(), &shaderResourceGroup);
|
|
}
|
|
|
|
if (resourceViewOld)
|
|
{
|
|
m_invalidateRegistry.OnDetach(&resourceViewOld->GetResource(), &shaderResourceGroup);
|
|
}
|
|
}
|
|
};
|
|
|
|
AZStd::lock_guard<AZStd::mutex> registryLock(m_invalidateRegistryMutex);
|
|
|
|
// Generate diffs for image views.
|
|
if (HasImageGroup())
|
|
{
|
|
AZStd::array_view<ConstPtr<ImageView>> viewGroupOld = shaderResourceGroup.GetData().GetImageGroup();
|
|
AZStd::array_view<ConstPtr<ImageView>> viewGroupNew = groupData.GetImageGroup();
|
|
AZ_Assert(viewGroupOld.size() == viewGroupNew.size(), "ShaderResourceGroupData layouts do not match.");
|
|
for (size_t i = 0; i < viewGroupOld.size(); ++i)
|
|
{
|
|
ComputeDiffs(viewGroupOld[i].get(), viewGroupNew[i].get());
|
|
}
|
|
}
|
|
|
|
// Generate diffs for buffer views.
|
|
if (HasBufferGroup())
|
|
{
|
|
AZStd::array_view<ConstPtr<BufferView>> viewGroupOld = shaderResourceGroup.GetData().GetBufferGroup();
|
|
AZStd::array_view<ConstPtr<BufferView>> viewGroupNew = groupData.GetBufferGroup();
|
|
AZ_Assert(viewGroupOld.size() == viewGroupNew.size(), "ShaderResourceGroupData layouts do not match.");
|
|
for (size_t i = 0; i < viewGroupOld.size(); ++i)
|
|
{
|
|
ComputeDiffs(viewGroupOld[i].get(), viewGroupNew[i].get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderResourceGroupPool::CompileGroupsBegin()
|
|
{
|
|
AZ_Assert(m_isCompiling == false, "Already compiling! Deadlock imminent.");
|
|
m_groupsToCompileMutex.lock();
|
|
m_isCompiling = true;
|
|
}
|
|
|
|
void ShaderResourceGroupPool::CompileGroupsEnd()
|
|
{
|
|
AZ_Assert(m_isCompiling, "CompileGroupsBegin() was never called.");
|
|
m_isCompiling = false;
|
|
m_groupsToCompile.clear();
|
|
m_groupsToCompileMutex.unlock();
|
|
}
|
|
|
|
uint32_t ShaderResourceGroupPool::GetGroupsToCompileCount() const
|
|
{
|
|
AZ_Assert(m_isCompiling, "You must call this function within a CompileGroups{Begin, End} region!");
|
|
return static_cast<uint32_t>(m_groupsToCompile.size());
|
|
}
|
|
|
|
void ShaderResourceGroupPool::CompileGroupsForInterval(Interval interval)
|
|
{
|
|
AZ_PROFILE_SCOPE(RHI, "CompileGroupsForInterval");
|
|
|
|
AZ_Assert(m_isCompiling, "You must call CompileGroupsBegin() first!");
|
|
AZ_Assert(
|
|
interval.m_max >= interval.m_min &&
|
|
interval.m_max <= static_cast<uint32_t>(m_groupsToCompile.size()),
|
|
"You must specify a valid interval for compilation");
|
|
|
|
for (uint32_t i = interval.m_min; i < interval.m_max; ++i)
|
|
{
|
|
ShaderResourceGroup* group = m_groupsToCompile[i];
|
|
CompileGroupInternal(*group, group->GetData());
|
|
group->m_isQueuedForCompile = false;
|
|
}
|
|
}
|
|
|
|
ResultCode ShaderResourceGroupPool::InitInternal(Device&, const ShaderResourceGroupPoolDescriptor&)
|
|
{
|
|
return ResultCode::Success;
|
|
}
|
|
|
|
ResultCode ShaderResourceGroupPool::InitGroupInternal(ShaderResourceGroup&)
|
|
{
|
|
return ResultCode::Success;
|
|
}
|
|
|
|
const ShaderResourceGroupPoolDescriptor& ShaderResourceGroupPool::GetDescriptor() const
|
|
{
|
|
return m_descriptor;
|
|
}
|
|
|
|
const ShaderResourceGroupLayout* ShaderResourceGroupPool::GetLayout() const
|
|
{
|
|
AZ_Assert(m_descriptor.m_layout, "Shader resource group layout is null");
|
|
return m_descriptor.m_layout.get();
|
|
}
|
|
|
|
bool ShaderResourceGroupPool::HasConstants() const
|
|
{
|
|
return m_hasConstants;
|
|
}
|
|
|
|
bool ShaderResourceGroupPool::HasBufferGroup() const
|
|
{
|
|
return m_hasBufferGroup;
|
|
}
|
|
|
|
bool ShaderResourceGroupPool::HasImageGroup() const
|
|
{
|
|
return m_hasImageGroup;
|
|
}
|
|
|
|
bool ShaderResourceGroupPool::HasSamplerGroup() const
|
|
{
|
|
return m_hasSamplerGroup;
|
|
}
|
|
}
|
|
}
|