b9824ed172
* Updated all array_view uses with the C++20 span. The updates were done in the following order 1. `AZStd::array_view<([^>].+)\* ?>` -> `AZStd::span<\1 const>` 2. `AZStd::array_view<(?:const )(.+)>` -> `AZStd::span<const \1>` 3. `AZStd::array_view` -> `AZStd::span` Removed the implementation of array_view. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added missing whitespace between `const` and the typename for spans. Updated the ShaderTest comparison of the ShaderResourceGroupLayout span to compare the sizes as well Updated comments on some of the methods that stated that they return "an array" to mention they return "a span". Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
257 lines
9.8 KiB
C++
257 lines
9.8 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/Factory.h>
|
|
#include <Atom/RHI/FrameGraphInterface.h>
|
|
#include <Atom/RHI/Query.h>
|
|
#include <Atom/RHI/RHISystemInterface.h>
|
|
#include <Atom/RHI/Scope.h>
|
|
|
|
#include <Atom/RPI.Public/GpuQuery/GpuQuerySystem.h>
|
|
#include <Atom/RPI.Public/GpuQuery/QueryPool.h>
|
|
|
|
namespace AZ
|
|
{
|
|
namespace RPI
|
|
{
|
|
QueryPoolPtr QueryPool::CreateQueryPool(uint32_t queryCount, uint32_t rhiQueriesPerResult, RHI::QueryType queryType, RHI::PipelineStatisticsFlags pipelineStatisticsFlags)
|
|
{
|
|
return AZStd::unique_ptr<QueryPool>(aznew QueryPool(queryCount, rhiQueriesPerResult, queryType, pipelineStatisticsFlags));
|
|
}
|
|
|
|
QueryPool::QueryPool(uint32_t queryCapacity, uint32_t queriesPerResult, RHI::QueryType queryType, RHI::PipelineStatisticsFlags statisticsFlags)
|
|
{
|
|
RHI::Device* device = RHI::RHISystemInterface::Get()->GetDevice();
|
|
|
|
m_queryCapacity = queryCapacity;
|
|
m_queriesPerResult = queriesPerResult;
|
|
m_statisticsFlags = statisticsFlags;
|
|
m_queryType = queryType;
|
|
|
|
// Calculate the total amount of RHI queries the RPI QueryPool needs to initialize.
|
|
m_rhiQueryCapacity = m_queryCapacity * m_queriesPerResult * RPI::Query::BufferedFrames;
|
|
m_rhiQueryArray.resize(m_rhiQueryCapacity);
|
|
|
|
m_availableIntervalArray.reserve(queryCapacity);
|
|
|
|
// Calculate the query result size.
|
|
CalculateResultSize();
|
|
|
|
// Populate the array with available RHI Query intervals.
|
|
CreateRhiQueryIntervals();
|
|
|
|
// Setup the query pool.
|
|
{
|
|
RHI::QueryPoolDescriptor queryPoolDesc;
|
|
queryPoolDesc.m_queriesCount = m_rhiQueryCapacity;
|
|
queryPoolDesc.m_type = m_queryType;
|
|
queryPoolDesc.m_pipelineStatisticsMask = m_statisticsFlags;
|
|
|
|
m_rhiQueryPool = RHI::Factory::Get().CreateQueryPool();
|
|
[[maybe_unused]] auto result = m_rhiQueryPool->Init(*device, queryPoolDesc);
|
|
AZ_Assert(result == RHI::ResultCode::Success, "Failed to create the query pool");
|
|
}
|
|
|
|
// Create the RHI queries.
|
|
{
|
|
AZStd::vector<RHI::Query*> rawQueryArray;
|
|
rawQueryArray.reserve(m_rhiQueryArray.size());
|
|
|
|
for (RHI::Ptr<RHI::Query>& query : m_rhiQueryArray)
|
|
{
|
|
query = RHI::Factory::Get().CreateQuery();
|
|
rawQueryArray.emplace_back(query.get());
|
|
}
|
|
|
|
m_rhiQueryPool->InitQuery(rawQueryArray.data(), static_cast<uint32_t>(rawQueryArray.size()));
|
|
}
|
|
}
|
|
|
|
QueryPool::~QueryPool()
|
|
{
|
|
// Unregister the queries first
|
|
for (auto& query : m_queryRegistry)
|
|
{
|
|
query->UnregisterFromPool();
|
|
}
|
|
AZ_Assert(m_queryRegistry.empty(), "The QueryRegistry should be empty.");
|
|
|
|
m_availableIntervalArray.clear();
|
|
m_rhiQueryArray.clear();
|
|
m_rhiQueryPool = nullptr;
|
|
}
|
|
|
|
void QueryPool::Update()
|
|
{
|
|
// Increment the QueryPool's FrameIndex.
|
|
m_poolFrameIndex++;
|
|
}
|
|
|
|
RHI::Ptr<RPI::Query> QueryPool::CreateQuery(RHI::QueryPoolScopeAttachmentType attachmentType, RHI::ScopeAttachmentAccess attachmentAccess)
|
|
{
|
|
AZStd::unique_lock<AZStd::mutex> lock(m_queryRegistryMutex);
|
|
|
|
// Get an available RHI Query interval.
|
|
if (m_availableIntervalArray.empty())
|
|
{
|
|
AZ_WarningOnce("Gpu QueryPool", false,
|
|
"There are no more available query indices left. This will result in Query data not being available for certain passes. \
|
|
Initialize the RPI::QueryPool with a bigger capacity.");
|
|
return nullptr;
|
|
}
|
|
|
|
RHI::Interval rhiQueryIndices = m_availableIntervalArray.back();
|
|
m_availableIntervalArray.pop_back();
|
|
|
|
// Create the RPI Query, and add it to the registry.
|
|
auto* query = aznew RPI::Query(this, rhiQueryIndices, m_queryType, attachmentType, attachmentAccess);
|
|
m_queryRegistry.emplace(query);
|
|
|
|
return query;
|
|
}
|
|
|
|
void QueryPool::UnregisterQuery(RPI::Query* query)
|
|
{
|
|
AZ_Assert(query, "The RPI::Query has to be valid");
|
|
|
|
AZStd::unique_lock<AZStd::mutex> lock(m_queryRegistryMutex);
|
|
|
|
// Push the RHI Query indices back into the array of available indices for reuse.
|
|
m_availableIntervalArray.emplace_back(query->m_rhiQueryIndices);
|
|
|
|
// Invalidate the RPI Query's QueryPool.
|
|
query->m_queryPool = nullptr;
|
|
|
|
// Remove the RPI Query from the registry.
|
|
m_queryRegistry.erase(query);
|
|
}
|
|
|
|
RHI::ResultCode QueryPool::BeginQueryInternal(RHI::Interval rhiQueryIndices, RHI::CommandList& commandList)
|
|
{
|
|
auto rhiQueryArray = GetRhiQueryArray();
|
|
RHI::Ptr<RHI::Query> beginQuery = rhiQueryArray[rhiQueryIndices.m_min];
|
|
|
|
return beginQuery->Begin(commandList);
|
|
}
|
|
|
|
RHI::ResultCode QueryPool::EndQueryInternal(RHI::Interval rhiQueryIndices, RHI::CommandList& commandList)
|
|
{
|
|
auto rhiQueryArray = GetRhiQueryArray();
|
|
RHI::Ptr<RHI::Query> endQuery = rhiQueryArray[rhiQueryIndices.m_max];
|
|
|
|
return endQuery->End(commandList);
|
|
}
|
|
|
|
AZStd::span<const RHI::Ptr<RHI::Query>> RPI::QueryPool::GetRhiQueryArray() const
|
|
{
|
|
return m_rhiQueryArray;
|
|
}
|
|
|
|
QueryResultCode QueryPool::GetQueryResultFromIndices(uint64_t* result, RHI::Interval rhiQueryIndices, RHI::QueryResultFlagBits queryResultFlag)
|
|
{
|
|
// Get the raw RHI Query pointers.
|
|
AZStd::vector<RHI::Query*> queryArray = GetRawRhiQueriesFromInterval(rhiQueryIndices);
|
|
|
|
// RHI Query results are readback with values that are a multiple of uint64_t.
|
|
const uint32_t resultCount = m_queryResultSize / sizeof(uint64_t);
|
|
const RHI::ResultCode resultCode = m_rhiQueryPool->GetResults(queryArray.data(), m_queriesPerResult, result, resultCount, queryResultFlag);
|
|
|
|
return resultCode == RHI::ResultCode::Success ? QueryResultCode::Success : QueryResultCode::Fail;
|
|
}
|
|
|
|
uint32_t QueryPool::GetQueryResultSize() const
|
|
{
|
|
return m_queryResultSize;
|
|
}
|
|
|
|
void QueryPool::CalculateResultSize()
|
|
{
|
|
using namespace RHI;
|
|
|
|
// Query result element count per QueryType.
|
|
const uint32_t TimestampResultCount = 2u;
|
|
const uint32_t OcclusionResultCount = 1u;
|
|
|
|
uint32_t resultCount = 0u;
|
|
|
|
// Determine the result size in uint64 by the QueryType.
|
|
switch (m_queryType)
|
|
{
|
|
case QueryType::PipelineStatistics:
|
|
// Each bit set, is translated to an additional result.
|
|
resultCount = CountBitsSet(static_cast<uint64_t>(m_statisticsFlags));
|
|
break;
|
|
case QueryType::Timestamp:
|
|
// A single timestamp result consists of two values.
|
|
resultCount = TimestampResultCount;
|
|
break;
|
|
case QueryType::Occlusion:
|
|
// A single occlusion result consists of one value.
|
|
resultCount = OcclusionResultCount;
|
|
break;
|
|
default:
|
|
AZ_Assert(false, "Unsupported QueryType");
|
|
break;
|
|
}
|
|
|
|
m_queryResultSize = resultCount * sizeof(uint64_t);
|
|
}
|
|
|
|
void QueryPool::CreateRhiQueryIntervals()
|
|
{
|
|
// Calculates the RHI Query indices that are associated with the RPI Query.
|
|
const auto getRhiQuriesFromRpiQueryIndex = [this](uint32_t rpiQueryIndex)
|
|
{
|
|
// The amount of RHI Queries that are required for a single RPI Query.
|
|
const uint32_t queryIntervalSize = m_queriesPerResult * RPI::Query::BufferedFrames;
|
|
const uint32_t queryIntervalOffset = rpiQueryIndex * queryIntervalSize;
|
|
|
|
return RHI::Interval(queryIntervalOffset, queryIntervalOffset + queryIntervalSize - 1u);
|
|
};
|
|
|
|
for (uint32_t i = 0u; i < m_queryCapacity; i++)
|
|
{
|
|
m_availableIntervalArray.emplace_back(getRhiQuriesFromRpiQueryIndex(i));
|
|
}
|
|
}
|
|
|
|
uint64_t QueryPool::GetPoolFrameIndex() const
|
|
{
|
|
return m_poolFrameIndex;
|
|
}
|
|
|
|
uint32_t QueryPool::GetQueriesPerResult() const
|
|
{
|
|
return m_queriesPerResult;
|
|
}
|
|
|
|
AZStd::span<const RHI::Ptr<RHI::Query>> QueryPool::GetRhiQueriesFromInterval(const RHI::Interval& rhiQueryIndices) const
|
|
{
|
|
const uint32_t queryCount = rhiQueryIndices.m_max - rhiQueryIndices.m_min + 1u;
|
|
AZ_Assert(rhiQueryIndices.m_max < m_rhiQueryCapacity, "Query array index is going over the limit");
|
|
|
|
return AZStd::span<const RHI::Ptr<RHI::Query>>(m_rhiQueryArray.begin() + rhiQueryIndices.m_min, queryCount);
|
|
}
|
|
|
|
AZStd::vector<RHI::Query*> QueryPool::GetRawRhiQueriesFromInterval(const RHI::Interval& rhiQueryIndices) const
|
|
{
|
|
auto rhiQueries = GetRhiQueriesFromInterval(rhiQueryIndices);
|
|
|
|
AZStd::vector<RHI::Query*> queryArray;
|
|
queryArray.reserve(rhiQueries.size());
|
|
for (RHI::Ptr<RHI::Query> rhiQuery : rhiQueries)
|
|
{
|
|
queryArray.emplace_back(rhiQuery.get());
|
|
}
|
|
|
|
return queryArray;
|
|
}
|
|
|
|
}; // Namespace RPI
|
|
}; // Namespace AZ
|