Commit c2b576d9 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Implement GL_EXT_disjoint_timer_query

- QueryVk::queryCounter() and relevant utils are implemented for the sake of Timestamp queries. - TimeElapsed queries are implemented using two Timestamp queries. Bug: angleproject:2885 Change-Id: Id181bd97f5a24e7e96b3ea1b819483227e64daf0 Reviewed-on: https://chromium-review.googlesource.com/c/1276806 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent c4765aa7
......@@ -65,6 +65,8 @@ const char *GetResourceTypeName(CommandGraphResourceType resourceType,
return "BeginQuery";
case CommandGraphNodeFunction::EndQuery:
return "EndQuery";
case CommandGraphNodeFunction::WriteTimestamp:
return "WriteTimestamp";
default:
UNREACHABLE();
return "Query";
......@@ -196,6 +198,14 @@ void CommandGraphResource::endQuery(Context *context,
mCurrentWritingNode->setQueryPool(queryPool, queryIndex);
}
void CommandGraphResource::writeTimestamp(Context *context,
const QueryPool *queryPool,
uint32_t queryIndex)
{
startNewCommands(context->getRenderer(), CommandGraphNodeFunction::WriteTimestamp);
mCurrentWritingNode->setQueryPool(queryPool, queryIndex);
}
void CommandGraphResource::finishCurrentCommands(RendererVk *renderer)
{
startNewCommands(renderer, CommandGraphNodeFunction::Generic);
......@@ -387,7 +397,8 @@ bool CommandGraphNode::hasParents() const
void CommandGraphNode::setQueryPool(const QueryPool *queryPool, uint32_t queryIndex)
{
ASSERT(mFunction == CommandGraphNodeFunction::BeginQuery ||
mFunction == CommandGraphNodeFunction::EndQuery);
mFunction == CommandGraphNodeFunction::EndQuery ||
mFunction == CommandGraphNodeFunction::WriteTimestamp);
mQueryPool = queryPool->getHandle();
mQueryIndex = queryIndex;
}
......@@ -496,6 +507,16 @@ angle::Result CommandGraphNode::visitAndExecute(vk::Context *context,
break;
case CommandGraphNodeFunction::WriteTimestamp:
ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
ASSERT(mQueryPool != VK_NULL_HANDLE);
primaryCommandBuffer->resetQueryPool(mQueryPool, mQueryIndex, 1);
primaryCommandBuffer->writeTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mQueryPool,
mQueryIndex);
break;
default:
UNREACHABLE();
}
......
......@@ -39,6 +39,7 @@ enum class CommandGraphNodeFunction
Generic,
BeginQuery,
EndQuery,
WriteTimestamp,
};
// Only used internally in the command graph. Kept in the header for better inlining performance.
......@@ -182,6 +183,7 @@ class CommandGraphResource : angle::NonCopyable
void beginQuery(Context *context, const QueryPool *queryPool, uint32_t queryIndex);
void endQuery(Context *context, const QueryPool *queryPool, uint32_t queryIndex);
void writeTimestamp(Context *context, const QueryPool *queryPool, uint32_t queryIndex);
// Checks if we're in a RenderPass, returning true if so. Updates serial internally.
// Returns the started command buffer in commandBufferOut.
......
......@@ -200,6 +200,10 @@ gl::Error ContextVk::initialize()
vk::kDefaultOcclusionQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::AnySamplesConservative].init(
this, VK_QUERY_TYPE_OCCLUSION, vk::kDefaultOcclusionQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::Timestamp].init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::TimeElapsed].init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
// TODO(syoussefi): Initialize other query pools as they get implemented.
size_t minAlignment = static_cast<size_t>(
......@@ -1083,7 +1087,8 @@ vk::DynamicDescriptorPool *ContextVk::getDynamicDescriptorPool(uint32_t descript
vk::DynamicQueryPool *ContextVk::getQueryPool(gl::QueryType queryType)
{
ASSERT(queryType == gl::QueryType::AnySamples ||
queryType == gl::QueryType::AnySamplesConservative);
queryType == gl::QueryType::AnySamplesConservative ||
queryType == gl::QueryType::Timestamp || queryType == gl::QueryType::TimeElapsed);
ASSERT(mQueryPools[queryType].isValid());
return &mQueryPools[queryType];
}
......
......@@ -27,6 +27,7 @@ gl::Error QueryVk::onDestroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
contextVk->getQueryPool(getType())->freeQuery(contextVk, &mQueryHelper);
contextVk->getQueryPool(getType())->freeQuery(contextVk, &mQueryHelperTimeElapsedBegin);
return gl::NoError();
}
......@@ -35,11 +36,30 @@ gl::Error QueryVk::begin(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
mCachedResultValid = false;
mQueryHelper.beginQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
if (!mQueryHelper.getQueryPool())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
// Note: TimeElapsed is implemented by using two Timestamp queries and taking the diff.
if (getType() == gl::QueryType::TimeElapsed)
{
if (!mQueryHelperTimeElapsedBegin.getQueryPool())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(
contextVk, &mQueryHelperTimeElapsedBegin));
}
mQueryHelperTimeElapsedBegin.writeTimestamp(contextVk,
mQueryHelperTimeElapsedBegin.getQueryPool(),
mQueryHelperTimeElapsedBegin.getQuery());
}
else
{
mQueryHelper.beginQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
}
return gl::NoError();
}
......@@ -48,15 +68,35 @@ gl::Error QueryVk::end(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
mQueryHelper.endQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
if (getType() == gl::QueryType::TimeElapsed)
{
mQueryHelper.writeTimestamp(contextVk, mQueryHelper.getQueryPool(),
mQueryHelper.getQuery());
}
else
{
mQueryHelper.endQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
}
return gl::NoError();
}
gl::Error QueryVk::queryCounter(const gl::Context *context)
{
UNIMPLEMENTED();
return gl::InternalError();
ContextVk *contextVk = vk::GetImpl(context);
mCachedResultValid = false;
if (!mQueryHelper.getQueryPool())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
ASSERT(getType() == gl::QueryType::Timestamp);
mQueryHelper.writeTimestamp(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
return gl::NoError();
}
angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
......@@ -71,9 +111,13 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// glGetQueryObject* requires an implicit flush of the command buffers to guarantee execution in
// finite time.
// Note regarding time-elapsed: end should have been called after begin, so flushing when end
// has pending work should flush begin too.
if (mQueryHelper.hasPendingWork(renderer))
{
ANGLE_TRY_HANDLE(context, renderer->flush(contextVk));
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(renderer));
ASSERT(!mQueryHelper.hasPendingWork(renderer));
}
......@@ -113,6 +157,24 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// OpenGL query result in these cases is binary
mCachedResult = !!mCachedResult;
break;
case gl::QueryType::Timestamp:
break;
case gl::QueryType::TimeElapsed:
{
uint64_t timeElapsedEnd = mCachedResult;
result = mQueryHelperTimeElapsedBegin.getQueryPool()->getResults(
contextVk, mQueryHelperTimeElapsedBegin.getQuery(), 1, sizeof(mCachedResult),
&mCachedResult, sizeof(mCachedResult), flags);
ANGLE_TRY(result);
// Since the result of the end query of time-elapsed is already available, the
// result of begin query must be available too.
ASSERT(result != angle::Result::Incomplete());
mCachedResult = timeElapsedEnd - mCachedResult;
break;
}
default:
UNREACHABLE();
break;
......@@ -121,7 +183,6 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
mCachedResultValid = true;
return angle::Result::Continue();
}
gl::Error QueryVk::getResult(const gl::Context *context, GLint *params)
{
ANGLE_TRY(getResult(context, true));
......
......@@ -36,7 +36,10 @@ class QueryVk : public QueryImpl
private:
angle::Result getResult(const gl::Context *context, bool wait);
// Used for AnySamples, AnySamplesConservative, Timestamp and TimeElapsed (end)
vk::QueryHelper mQueryHelper;
// An additional query used for TimeElapsed (begin), as it is implemented using Timestamp
vk::QueryHelper mQueryHelperTimeElapsedBegin;
uint64_t mCachedResult;
bool mCachedResultValid;
};
......
......@@ -769,7 +769,9 @@ void RendererVk::ensureCapsInitialized() const
{
if (!mCapsInitialized)
{
vk::GenerateCaps(mPhysicalDeviceProperties, mPhysicalDeviceFeatures, mNativeTextureCaps,
ASSERT(mCurrentQueueFamilyIndex < mQueueFamilyProperties.size());
vk::GenerateCaps(mPhysicalDeviceProperties, mPhysicalDeviceFeatures,
mQueueFamilyProperties[mCurrentQueueFamilyIndex], mNativeTextureCaps,
&mNativeCaps, &mNativeExtensions, &mNativeLimitations);
mCapsInitialized = true;
}
......
......@@ -31,6 +31,7 @@ namespace vk
void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
const VkPhysicalDeviceFeatures &physicalDeviceFeatures,
const VkQueueFamilyProperties &queueFamilyProperties,
const gl::TextureCapsMap &textureCaps,
gl::Caps *outCaps,
gl::Extensions *outExtensions,
......@@ -53,6 +54,15 @@ void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
// unless that feature is available.
outExtensions->occlusionQueryBoolean = physicalDeviceFeatures.inheritedQueries;
// From the Vulkan specs:
// > The number of valid bits in a timestamp value is determined by the
// > VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is
// > written. Timestamps are supported on any queue which reports a non-zero value for
// > timestampValidBits via vkGetPhysicalDeviceQueueFamilyProperties.
outExtensions->disjointTimerQuery = queueFamilyProperties.timestampValidBits > 0;
outExtensions->queryCounterBitsTimeElapsed = queueFamilyProperties.timestampValidBits;
outExtensions->queryCounterBitsTimestamp = queueFamilyProperties.timestampValidBits;
// TODO(lucferron): Eventually remove everything above this line in this function as the caps
// get implemented.
// https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
......
......@@ -34,6 +34,7 @@ namespace vk
{
void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
const VkPhysicalDeviceFeatures &physicalDeviceFeatures,
const VkQueueFamilyProperties &queueFamilyProperties,
const gl::TextureCapsMap &textureCaps,
gl::Caps *outCaps,
gl::Extensions *outExtensions,
......
......@@ -211,8 +211,9 @@ class DynamicallyGrowingPool : angle::NonCopyable
// DynamicQueryPool allocates indices out of QueryPool as needed. Once a QueryPool is exhausted,
// another is created. The query pools live permanently, but are recycled as indices get freed.
// This is an arbitrary default size for occlusion query pools.
// These are arbitrary default sizes for query pools.
constexpr uint32_t kDefaultOcclusionQueryPoolSize = 64;
constexpr uint32_t kDefaultTimestampQueryPoolSize = 64;
class QueryHelper;
......
......@@ -583,6 +583,14 @@ void CommandBuffer::endQuery(VkQueryPool queryPool, uint32_t query)
vkCmdEndQuery(mHandle, queryPool, query);
}
void CommandBuffer::writeTimestamp(VkPipelineStageFlagBits pipelineStage,
VkQueryPool queryPool,
uint32_t query)
{
ASSERT(valid());
vkCmdWriteTimestamp(mHandle, pipelineStage, queryPool, query);
}
// Image implementation.
Image::Image()
{
......
......@@ -427,6 +427,9 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
void resetQueryPool(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
void beginQuery(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags);
void endQuery(VkQueryPool queryPool, uint32_t query);
void writeTimestamp(VkPipelineStageFlagBits pipelineStage,
VkQueryPool queryPool,
uint32_t query);
};
class Image final : public WrappedObject<Image, VkImage>
......
......@@ -157,6 +157,12 @@ TEST_P(TimerQueriesTest, TimeElapsed)
EXPECT_LT(0ul, result1);
EXPECT_LT(0ul, result2);
// The time elapsed should be less than a second. Not an actual
// requirement, but longer than a second to draw something basic hints at
// an issue with the queries themselves.
EXPECT_LT(result1, 1000000000ul);
EXPECT_LT(result2, 1000000000ul);
// TODO(geofflang): Re-enable this check when it is non-flaky
// The costly quad should take longer than the cheap quad
// EXPECT_LT(result1, result2);
......@@ -216,6 +222,9 @@ TEST_P(TimerQueriesTest, TimeElapsedTextureTest)
std::cout << "Elapsed time: " << result << std::endl;
EXPECT_LT(0ul, result);
// an issue with the queries themselves.
EXPECT_LT(result, 1000000000ul);
}
// Tests validation of query functions with respect to elapsed time query
......@@ -270,6 +279,10 @@ TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
// Test skipped because the Vulkan backend doesn't account for (and remove) time spent in other
// contexts.
ANGLE_SKIP_TEST_IF(IsVulkan());
GLint queryTimeElapsedBits = 0;
glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
ASSERT_GL_NO_ERROR();
......@@ -393,6 +406,8 @@ TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
EXPECT_LT(0ul, result1);
EXPECT_LT(0ul, result2);
EXPECT_LT(result1, 1000000000ul);
EXPECT_LT(result2, 1000000000ul);
EXPECT_LT(result1, result2);
}
......@@ -495,6 +510,7 @@ ANGLE_INSTANTIATE_TEST(TimerQueriesTest,
ES2_D3D11(),
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL());
ES3_OPENGL(),
ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment