You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
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::array_view<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::array_view<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::array_view<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
|