Commit cdf280c0 by Jamie Madill Committed by Commit Bot

Vulkan: Fix serial handling for queries

Previous serial mechanism wasn't reliable. QueryHelper is now a vk::Resource and uses the vk::Resource lifecycle. Also added some utility methods to QueryVk to deal with stashed queries. Any question we want to ask about mQueryHelper we want to ask about the stashed queries as well. Bug: b/169055809 Bug: b/169788986 Bug: b/170312581 Change-Id: Ia34a7a433e61a2543cfb09491ebab55b054a26c9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2537718Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Courtney Goeltzenleuchter <courtneygo@google.com>
parent d3a14089
......@@ -1638,6 +1638,8 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
vk::DeviceScoped<vk::PrimaryCommandBuffer> commandBatch(device);
vk::PrimaryCommandBuffer &commandBuffer = commandBatch.get();
vk::ResourceUseList scratchResourceUseList;
ANGLE_TRY(mRenderer->getCommandBufferOneOff(this, &commandBuffer));
commandBuffer.setEvent(gpuReady.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
......@@ -1645,6 +1647,8 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, nullptr, 0, nullptr, 0,
nullptr);
timestampQuery.writeTimestampToPrimary(this, &commandBuffer);
timestampQuery.retain(&scratchResourceUseList);
commandBuffer.setEvent(gpuDone.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
ANGLE_VK_TRY(this, commandBuffer.end());
......@@ -1652,6 +1656,7 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
Serial throwAwaySerial;
ANGLE_TRY(mRenderer->queueSubmitOneOff(this, std::move(commandBuffer), mContextPriority,
nullptr, &throwAwaySerial));
scratchResourceUseList.releaseResourceUsesAndUpdateSerials(throwAwaySerial);
// Wait for GPU to be ready. This is a short busy wait.
VkResult result = VK_EVENT_RESET;
......@@ -1753,7 +1758,7 @@ angle::Result ContextVk::checkCompletedGpuEvents()
for (GpuEventQuery &eventQuery : mInFlightGpuEventQueries)
{
// Only check the timestamp query if the submission has finished.
if (eventQuery.queryHelper.getStoredQueueSerial() > lastCompletedSerial)
if (eventQuery.queryHelper.usedInRunningCommands(lastCompletedSerial))
{
break;
}
......@@ -4183,6 +4188,8 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
ANGLE_TRY(timestampQueryPool.get().init(this, VK_QUERY_TYPE_TIMESTAMP, 1));
ANGLE_TRY(timestampQueryPool.get().allocateQuery(this, &timestampQuery));
vk::ResourceUseList scratchResourceUseList;
// Record the command buffer
vk::DeviceScoped<vk::PrimaryCommandBuffer> commandBatch(device);
vk::PrimaryCommandBuffer &commandBuffer = commandBatch.get();
......@@ -4190,6 +4197,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
ANGLE_TRY(mRenderer->getCommandBufferOneOff(this, &commandBuffer));
timestampQuery.writeTimestampToPrimary(this, &commandBuffer);
timestampQuery.retain(&scratchResourceUseList);
ANGLE_VK_TRY(this, commandBuffer.end());
// Create fence for the submission
......@@ -4218,6 +4226,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
// Wait for the submission to finish. Given no semaphores, there is hope that it would execute
// in parallel with what's already running on the GPU.
ANGLE_VK_TRY(this, fence.get().wait(device, mRenderer->getMaxFenceWaitTimeNs()));
scratchResourceUseList.releaseResourceUsesAndUpdateSerials(throwAwaySerial);
// Get the query results
ANGLE_TRY(timestampQuery.getUint64Result(this, timestampOut));
......
......@@ -42,7 +42,7 @@ void QueryVk::onDestroy(const gl::Context *context)
angle::Result QueryVk::stashQueryHelper(ContextVk *contextVk)
{
ASSERT(isOcclusionQuery());
mStashedQueryHelpers.emplace_back(mQueryHelper);
mStashedQueryHelpers.emplace_back(std::move(mQueryHelper));
mQueryHelper.deinit();
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
return angle::Result::Continue;
......@@ -86,7 +86,7 @@ angle::Result QueryVk::begin(const gl::Context *context)
// For pathological usage case where begin/end is called back to back without flush and get
// result, we have to force flush so that the same mQueryHelper will not encoded in the same
// renderpass twice without resetting it.
if (mQueryHelper.hasPendingWork(contextVk))
if (mQueryHelper.usedInRecordedCommands())
{
ANGLE_TRY(contextVk->flushImpl(nullptr));
// As soon as beginQuery is called, previous query's result will not retrievable by API.
......@@ -166,6 +166,63 @@ angle::Result QueryVk::queryCounter(const gl::Context *context)
return mQueryHelper.flushAndWriteTimestamp(contextVk);
}
bool QueryVk::isUsedInRecordedCommands() const
{
if (mQueryHelper.usedInRecordedCommands())
{
return true;
}
for (const vk::QueryHelper &query : mStashedQueryHelpers)
{
if (query.usedInRecordedCommands())
{
return true;
}
}
return false;
}
bool QueryVk::isCurrentlyInUse(Serial lastCompletedSerial) const
{
if (mQueryHelper.isCurrentlyInUse(lastCompletedSerial))
{
return true;
}
for (const vk::QueryHelper &query : mStashedQueryHelpers)
{
if (query.isCurrentlyInUse(lastCompletedSerial))
{
return true;
}
}
return false;
}
angle::Result QueryVk::finishRunningCommands(ContextVk *contextVk)
{
Serial lastCompletedSerial = contextVk->getLastCompletedQueueSerial();
if (mQueryHelper.usedInRunningCommands(lastCompletedSerial))
{
ANGLE_TRY(mQueryHelper.finishRunningCommands(contextVk));
lastCompletedSerial = contextVk->getLastCompletedQueueSerial();
}
for (vk::QueryHelper &query : mStashedQueryHelpers)
{
if (query.usedInRunningCommands(lastCompletedSerial))
{
ANGLE_TRY(query.finishRunningCommands(contextVk));
lastCompletedSerial = contextVk->getLastCompletedQueueSerial();
}
}
return angle::Result::Continue;
}
angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
{
ANGLE_TRACE_EVENT0("gpu.angle", "QueryVk::getResult");
......@@ -182,19 +239,13 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// finite time.
// Note regarding time-elapsed: end should have been called after begin, so flushing when end
// has pending work should flush begin too.
// TODO: https://issuetracker.google.com/169788986 - can't guarantee hasPendingWork() works when
// using threaded worker
if (mQueryHelper.hasPendingWork(contextVk))
if (isUsedInRecordedCommands())
{
ANGLE_TRY(contextVk->flushImpl(nullptr));
if (renderer->getFeatures().asyncCommandQueue.enabled)
{
// TODO: https://issuetracker.google.com/170312581 - For now just stalling here
ANGLE_TRY(renderer->waitForCommandProcessorIdle(contextVk));
}
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk));
ASSERT(!mQueryHelper.hasPendingWork(contextVk));
ASSERT(!mQueryHelperTimeElapsedBegin.usedInRecordedCommands());
ASSERT(!mQueryHelper.usedInRecordedCommands());
}
ANGLE_TRY(contextVk->checkCompletedCommands());
......@@ -203,7 +254,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// command may not have been performed by the GPU yet. To avoid a race condition in this
// case, wait for the batch to finish first before querying (or return not-ready if not
// waiting).
if (contextVk->isSerialInUse(mQueryHelper.getStoredQueueSerial()))
if (isCurrentlyInUse(contextVk->getLastCompletedQueueSerial()))
{
if (!wait)
{
......@@ -211,7 +262,10 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
}
ANGLE_PERF_WARNING(contextVk->getDebug(), GL_DEBUG_SEVERITY_HIGH,
"GPU stall due to waiting on uncompleted query");
ANGLE_TRY(contextVk->finishToSerial(mQueryHelper.getStoredQueueSerial()));
// Assert that the work has been sent to the GPU
ASSERT(!isUsedInRecordedCommands());
ANGLE_TRY(finishRunningCommands(contextVk));
}
if (wait)
......
......@@ -42,6 +42,10 @@ class QueryVk : public QueryImpl
private:
angle::Result getResult(const gl::Context *context, bool wait);
bool isUsedInRecordedCommands() const;
bool isCurrentlyInUse(Serial lastCompletedSerial) const;
angle::Result finishRunningCommands(ContextVk *contextVk);
// Used for AnySamples, AnySamplesConservative, Timestamp and TimeElapsed (end).
vk::QueryHelper mQueryHelper;
// Used for occlusion query that we may end up with multiple outstanding query helper objects.
......
......@@ -2335,6 +2335,26 @@ QueryHelper::QueryHelper() : mDynamicQueryPool(nullptr), mQueryPoolIndex(0), mQu
QueryHelper::~QueryHelper() {}
// Move constructor
QueryHelper::QueryHelper(QueryHelper &&rhs)
: Resource(std::move(rhs)),
mDynamicQueryPool(rhs.mDynamicQueryPool),
mQueryPoolIndex(rhs.mQueryPoolIndex),
mQuery(rhs.mQuery)
{
rhs.mDynamicQueryPool = nullptr;
rhs.mQueryPoolIndex = 0;
rhs.mQuery = 0;
}
QueryHelper &QueryHelper::operator=(QueryHelper &&rhs)
{
std::swap(mDynamicQueryPool, rhs.mDynamicQueryPool);
std::swap(mQueryPoolIndex, rhs.mQueryPoolIndex);
std::swap(mQuery, rhs.mQuery);
return *this;
}
void QueryHelper::init(const DynamicQueryPool *dynamicQueryPool,
const size_t queryPoolIndex,
uint32_t query)
......@@ -2349,7 +2369,8 @@ void QueryHelper::deinit()
mDynamicQueryPool = nullptr;
mQueryPoolIndex = 0;
mQuery = 0;
mMostRecentSerial = Serial();
mUse.release();
mUse.init();
}
void QueryHelper::resetQueryPool(ContextVk *contextVk,
......@@ -2371,7 +2392,7 @@ angle::Result QueryHelper::beginQuery(ContextVk *contextVk)
const QueryPool &queryPool = getQueryPool();
commandBuffer->resetQueryPool(queryPool.getHandle(), mQuery, 1);
commandBuffer->beginQuery(queryPool.getHandle(), mQuery, 0);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
return angle::Result::Continue;
}
......@@ -2385,7 +2406,11 @@ angle::Result QueryHelper::endQuery(ContextVk *contextVk)
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
commandBuffer->endQuery(getQueryPool().getHandle(), mQuery);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
// Query results are available after endQuery, retain this query so that we get its serial
// updated which is used to indicate that query results are (or will be) available.
retain(&contextVk->getResourceUseList());
return angle::Result::Continue;
}
......@@ -2394,13 +2419,15 @@ void QueryHelper::beginOcclusionQuery(ContextVk *contextVk, CommandBuffer *rende
const QueryPool &queryPool = getQueryPool();
renderPassCommandBuffer->queueResetQueryPool(queryPool.getHandle(), mQuery, 1);
renderPassCommandBuffer->beginQuery(queryPool.getHandle(), mQuery, 0);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
}
void QueryHelper::endOcclusionQuery(ContextVk *contextVk, CommandBuffer *renderPassCommandBuffer)
{
renderPassCommandBuffer->endQuery(getQueryPool().getHandle(), mQuery);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
// Query results are available after endQuery, retain this query so that we get its serial
// updated which is used to indicate that query results are (or will be) available.
retain(&contextVk->getResourceUseList());
}
angle::Result QueryHelper::flushAndWriteTimestamp(ContextVk *contextVk)
......@@ -2423,7 +2450,6 @@ void QueryHelper::writeTimestampToPrimary(ContextVk *contextVk, PrimaryCommandBu
const QueryPool &queryPool = getQueryPool();
primary->resetQueryPool(queryPool, mQuery, 1);
primary->writeTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, queryPool, mQuery);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
}
void QueryHelper::writeTimestamp(ContextVk *contextVk, CommandBuffer *commandBuffer)
......@@ -2432,15 +2458,9 @@ void QueryHelper::writeTimestamp(ContextVk *contextVk, CommandBuffer *commandBuf
commandBuffer->resetQueryPool(queryPool.getHandle(), mQuery, 1);
commandBuffer->writeTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, queryPool.getHandle(),
mQuery);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
}
bool QueryHelper::hasPendingWork(ContextVk *contextVk)
{
// TODO: https://issuetracker.google.com/169788986 - this is not a valid statement with
// CommandProcessor: If the renderer has a queue serial higher than the stored one, the command
// buffers that recorded this query have already been submitted, so there is no pending work.
return mMostRecentSerial.valid() && (mMostRecentSerial == contextVk->getCurrentQueueSerial());
// timestamp results are available immediately, retain this query so that we get its serial
// updated which is used to indicate that query results are (or will be) available.
retain(&contextVk->getResourceUseList());
}
angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
......@@ -2452,7 +2472,7 @@ angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
// Ensure that we only wait if we have inserted a query in command buffer. Otherwise you will
// wait forever and trigger GPU timeout.
if (mMostRecentSerial.valid())
if (mUse.getSerial().valid())
{
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT;
......@@ -2481,7 +2501,7 @@ angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
angle::Result QueryHelper::getUint64Result(ContextVk *contextVk, uint64_t *resultOut)
{
ASSERT(valid());
if (mMostRecentSerial.valid())
if (mUse.getSerial().valid())
{
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT;
......
......@@ -415,12 +415,13 @@ class DynamicQueryPool final : public DynamicallyGrowingPool<QueryPool>
// of a fixed size as needed and allocates indices within those pools.
//
// The QueryHelper class below keeps the pool and index pair together.
class QueryHelper final
class QueryHelper final : public vk::Resource
{
public:
QueryHelper();
~QueryHelper();
~QueryHelper() override;
QueryHelper(QueryHelper &&rhs);
QueryHelper &operator=(QueryHelper &&rhs);
void init(const DynamicQueryPool *dynamicQueryPool,
const size_t queryPoolIndex,
uint32_t query);
......@@ -443,9 +444,6 @@ class QueryHelper final
// All other timestamp accesses should be made on outsideRenderPassCommandBuffer
void writeTimestamp(ContextVk *contextVk, CommandBuffer *outsideRenderPassCommandBuffer);
Serial getStoredQueueSerial() { return mMostRecentSerial; }
bool hasPendingWork(ContextVk *contextVk);
angle::Result getUint64ResultNonBlocking(ContextVk *contextVk,
uint64_t *resultOut,
bool *availableOut);
......@@ -462,7 +460,6 @@ class QueryHelper final
const DynamicQueryPool *mDynamicQueryPool;
size_t mQueryPoolIndex;
uint32_t mQuery;
Serial mMostRecentSerial;
};
// DynamicSemaphorePool allocates semaphores as needed. It uses a std::vector
......
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