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.
o3de/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp

316 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 <AzCore/UnitTest/TestTypes.h>
#include <Atom/RPI.Public/GpuQuery/GpuQueryTypes.h>
#include <Atom/RPI.Public/GpuQuery/GpuQuerySystem.h>
#include <Atom/RPI.Public/GpuQuery/TimestampQueryPool.h>
#include <Atom/RPI.Public/GpuQuery/GpuQuerySystemInterface.h>
#include <Atom/RHI/BufferScopeAttachment.h>
#include <Atom/RHI/FrameGraph.h>
#include <Atom/RHI/QueryPool.h>
#include <AzTest/AzTest.h>
#include <Common/RPITestFixture.h>
namespace UnitTest
{
using namespace AZ;
using namespace AZ::RPI;
class GpuQueryTests
: public RPITestFixture
{
void SetUp() final
{
RPITestFixture::SetUp();
}
void TearDown() final
{
RPITestFixture::TearDown();
}
};
// Unit test the RPI::QueryPool
TEST_F(GpuQueryTests, TestQueryPools)
{
const uint32_t QueryCount = 1024u;
const uint32_t QueriesPerInstance = 1u;
const RHI::QueryType Type = RHI::QueryType::Occlusion;
const RHI::PipelineStatisticsFlags StatisticsFlags = RHI::PipelineStatisticsFlags::None;
const uint32_t OcclusionQueryResultSize = sizeof(uint64_t);
const RHI::FrameGraphExecuteContext::Descriptor desc = {};
RHI::FrameGraphExecuteContext context(desc);
// Setup the FrameGraph
RHI::Scope scope;
scope.Init(RHI::ScopeId("StubScope"));
RHI::FrameGraph frameGraph;
frameGraph.BeginScope(scope);
AZStd::unique_ptr<RPI::QueryPool> queryPool = RPI::QueryPool::CreateQueryPool(QueryCount, QueriesPerInstance, Type, StatisticsFlags);
const uint32_t resultSize = queryPool->GetQueryResultSize();
EXPECT_EQ(resultSize, OcclusionQueryResultSize);
// Test valid Queries
{
RHI::Ptr<RPI::Query> query = queryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
}
// Test adding to the FrameGraph multiple times with one Query within a single frame
{
RHI::Ptr<RPI::Query> query = queryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
QueryResultCode resultCode = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode, QueryResultCode::Success);
resultCode = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode, QueryResultCode::Fail);
// Next frame
queryPool->Update();
}
// Test recording the same Query in different frames
{
RHI::Ptr<RPI::Query> query = queryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
QueryResultCode resultCode = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode, QueryResultCode::Success);
// Next frame
queryPool->Update();
resultCode = query->BeginQuery(context);
EXPECT_EQ(resultCode, QueryResultCode::Fail);
}
// Test Query result
{
// Occlusion QueryPool result size is sizeof(uint64_t)
EXPECT_EQ(queryPool->GetQueryResultSize(), sizeof(uint64_t));
}
// Test get result with invalid size
{
RHI::Ptr<RPI::Query> query = queryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
QueryResultCode resultCode = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode, QueryResultCode::Success);
void* data = nullptr;
// Query type of the pool is occlusion, which expects a result size of sizeof(uint64_t).
resultCode = query->GetLatestResult(data, sizeof(uint32_t));
EXPECT_EQ(resultCode, QueryResultCode::Fail);
}
// Test going over QueryPool limit
{
// Temporary QueryPool with a limit of 1 Query
AZStd::unique_ptr<RPI::QueryPool> tempRpiQueryPool = RPI::QueryPool::CreateQueryPool(1u, QueriesPerInstance, Type, StatisticsFlags);
RHI::Ptr<RPI::Query> query = tempRpiQueryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
RHI::Ptr<RPI::Query> query1 = tempRpiQueryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(!query1.get());
}
// Test different Scopes for Begin() and End()
{
RHI::FrameGraphExecuteContext::Descriptor desc2;
desc2.m_scopeId = RHI::ScopeId("Test");
desc2.m_commandListIndex = 0;
desc2.m_commandListCount = 1;
desc2.m_commandList = nullptr;
RHI::FrameGraphExecuteContext context2(desc2);
RHI::Ptr<RPI::Query> query = queryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
QueryResultCode resultCode = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode, QueryResultCode::Success);
resultCode = query->BeginQuery(context);
EXPECT_EQ(resultCode, QueryResultCode::Success);
resultCode = query->EndQuery(context2);
EXPECT_EQ(resultCode, QueryResultCode::Fail);
}
}
// Test occlusion QueryPool
TEST_F(GpuQueryTests, TestOcclusionQueryPool)
{
const uint32_t QueryCount = 1024u;
const uint32_t QueriesPerInstance = 1u;
const RHI::QueryType Type = RHI::QueryType::Occlusion;
const RHI::PipelineStatisticsFlags StatisticsFlags = RHI::PipelineStatisticsFlags::None;
const uint32_t ResultSize = sizeof(uint64_t);
uint64_t mockData;
RHI::FrameGraphExecuteContext::Descriptor desc = {};
uint64_t dummyCommandList;
desc.m_commandList = reinterpret_cast<RHI::CommandList*>(&dummyCommandList);
RHI::FrameGraphExecuteContext context(desc);
RHI::Scope scope;
scope.Init(RHI::ScopeId("StubScope"));
RHI::FrameGraph frameGraph;
frameGraph.BeginScope(scope);
// Test get result of a Query successfully
{
const uint32_t LoopCount = 4u;
QueryResultCode resultCode;
AZStd::unique_ptr<RPI::QueryPool> tempRpiQueryPool = RPI::QueryPool::CreateQueryPool(QueryCount, QueriesPerInstance, Type, StatisticsFlags);
RHI::Ptr<RPI::Query> query = tempRpiQueryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
for (uint32_t i = 0u; i < LoopCount; i++)
{
QueryResultCode resultCode2;
resultCode2 = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
resultCode2 = query->BeginQuery(context);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
resultCode2 = query->EndQuery(context);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
tempRpiQueryPool->Update();
}
void* data = reinterpret_cast<void*>(&mockData);
resultCode = query->GetLatestResult(data, ResultSize);
EXPECT_EQ(resultCode, QueryResultCode::Success);
resultCode = query->GetLatestResultAndWait(data, ResultSize);
EXPECT_EQ(resultCode, QueryResultCode::Success);
}
}
// Test statistics QueryPool
TEST_F(GpuQueryTests, TestStatisticsQueryPool)
{
const uint32_t QueryCount = 1024u;
const uint32_t QueriesPerInstance = 1u;
const RHI::QueryType Type = RHI::QueryType::PipelineStatistics;
const RHI::PipelineStatisticsFlags StatisticsFlags =
RHI::PipelineStatisticsFlags::CInvocations |
RHI::PipelineStatisticsFlags::CPrimitives |
RHI::PipelineStatisticsFlags::CSInvocations |
RHI::PipelineStatisticsFlags::DSInvocations;
const uint32_t ResultSize = sizeof(uint64_t) * 4u;
uint64_t mockData;
RHI::FrameGraphExecuteContext::Descriptor desc = {};
uint64_t dummyCommandList;
desc.m_commandList = reinterpret_cast<RHI::CommandList*>(&dummyCommandList);
RHI::FrameGraphExecuteContext context(desc);
RHI::Scope scope;
scope.Init(RHI::ScopeId("StubScope"));
RHI::FrameGraph frameGraph;
frameGraph.BeginScope(scope);
AZStd::unique_ptr<RPI::QueryPool> queryPool = RPI::QueryPool::CreateQueryPool(QueryCount, QueriesPerInstance, Type, StatisticsFlags);
// Test expected result size
{
const uint32_t resultSize = queryPool->GetQueryResultSize();
EXPECT_EQ(resultSize, ResultSize);
}
// Test get result with invalid size
{
RHI::Ptr<RPI::Query> query = queryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
QueryResultCode resultCode = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode, QueryResultCode::Success);
void* data = nullptr;
// Query type of the pool is statistics, which expects a result size of sizeof(uint64_t) * number of active flags.
resultCode = query->GetLatestResult(data, sizeof(uint64_t) * 3u);
EXPECT_EQ(resultCode, QueryResultCode::Fail);
}
// Test get result of a Query successfully
{
const uint32_t LoopCount = 4u;
QueryResultCode resultCode;
AZStd::unique_ptr<RPI::QueryPool> tempRpiQueryPool = RPI::QueryPool::CreateQueryPool(QueryCount, QueriesPerInstance, Type, StatisticsFlags);
RHI::Ptr<RPI::Query> query = tempRpiQueryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
for (uint32_t i = 0u; i < LoopCount; i++)
{
QueryResultCode resultCode2;
resultCode2 = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
resultCode2 = query->BeginQuery(context);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
resultCode2 = query->EndQuery(context);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
tempRpiQueryPool->Update();
}
void* data = reinterpret_cast<void*>(&mockData);
resultCode = query->GetLatestResult(data, ResultSize);
EXPECT_EQ(resultCode, QueryResultCode::Success);
resultCode = query->GetLatestResultAndWait(data, ResultSize);
EXPECT_EQ(resultCode, QueryResultCode::Success);
}
}
// Test timestamp QueryPool
TEST_F(GpuQueryTests, TestTimestampQueryPool)
{
const uint32_t QueryCount = 1024u;
const uint32_t ResultSize = sizeof(uint64_t) * 2u;
uint64_t mockData;
RHI::FrameGraphExecuteContext::Descriptor desc = {};
uint64_t dummyCommandList;
desc.m_commandList = reinterpret_cast<RHI::CommandList*>(&dummyCommandList);
RHI::FrameGraphExecuteContext context(desc);
RHI::Scope scope;
scope.Init(RHI::ScopeId("StubScope"));
RHI::FrameGraph frameGraph;
frameGraph.BeginScope(scope);
// Test get result of a Query successfully
{
const uint32_t LoopCount = 4u;
QueryResultCode resultCode;
AZStd::unique_ptr<RPI::QueryPool> tempRpiQueryPool = RPI::TimestampQueryPool::CreateTimestampQueryPool(QueryCount);
RHI::Ptr<RPI::Query> query = tempRpiQueryPool->CreateQuery(RHI::QueryPoolScopeAttachmentType::Global, RHI::ScopeAttachmentAccess::Write);
EXPECT_TRUE(query.get());
for (uint32_t i = 0u; i < LoopCount; i++)
{
QueryResultCode resultCode2;
resultCode2 = query->AddToFrameGraph(frameGraph);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
resultCode2 = query->BeginQuery(context);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
resultCode2 = query->EndQuery(context);
EXPECT_EQ(resultCode2, QueryResultCode::Success);
tempRpiQueryPool->Update();
}
void* data = reinterpret_cast<void*>(&mockData);
resultCode = query->GetLatestResult(data, ResultSize);
EXPECT_EQ(resultCode, QueryResultCode::Success);
resultCode = query->GetLatestResultAndWait(data, ResultSize);
EXPECT_EQ(resultCode, QueryResultCode::Success);
}
}
}; // namespace UnitTest