Commit 6328caf3 by Charlie Lao Committed by Commit Bot

Vulkan: Avoid renderpass break for occlusion query

This ensures beginQuery and endQuery only get inserted into renderpasses and will not close renderpass because of query call Bug: angleproject:4381 Change-Id: I690f096b9e8e4b7ea9a67045d1be0fd7a319c98c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2119246Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Commit-Queue: Charlie Lao <cclao@google.com>
parent 4b94d7bb
......@@ -39,6 +39,15 @@ class QueryImpl : angle::NonCopyable
gl::QueryType getType() const { return mType; }
// Convenient functions
bool isOcclusionQuery() const { return isAnySamplesQuery() || isAnySamplesConservativeQuery(); }
bool isAnySamplesQuery() const { return getType() == gl::QueryType::AnySamples; }
bool isAnySamplesConservativeQuery() const
{
return getType() == gl::QueryType::AnySamplesConservative;
}
private:
gl::QueryType mType;
};
......
......@@ -69,6 +69,7 @@ class Serial final
// Useful for serialization.
constexpr uint64_t getValue() const { return mValue; }
constexpr bool valid() const { return mValue != kInvalid; }
private:
template <typename T>
......
......@@ -599,6 +599,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mDrawFramebuffer(nullptr),
mProgram(nullptr),
mExecutable(nullptr),
mActiveQueryAnySamples(nullptr),
mActiveQueryAnySamplesConservative(nullptr),
mLastIndexBufferOffset(0),
mCurrentDrawElementsType(gl::DrawElementsType::InvalidEnum),
mXfbBaseVertex(0),
......@@ -899,11 +901,8 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
// TODO(jmadill): Use dirty bit. http://anglebug.com/3014
if (!mRenderPassCommandBuffer)
{
mGraphicsDirtyBits |= mNewGraphicsCommandBufferDirtyBits;
gl::Rectangle scissoredRenderArea = mDrawFramebuffer->getScissoredRenderArea(this);
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, scissoredRenderArea,
&mRenderPassCommandBuffer));
ANGLE_TRY(startRenderPass(scissoredRenderArea));
}
// We keep a local copy of the command buffer. It's possible that some state changes could
......@@ -1629,7 +1628,7 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
commandBuffer.waitEvents(1, cpuReady.get().ptr(), VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, nullptr, 0, nullptr, 0,
nullptr);
timestampQuery.writeTimestamp(&commandBuffer);
timestampQuery.writeTimestamp(this, &commandBuffer);
commandBuffer.setEvent(gpuDone.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
ANGLE_VK_TRY(this, commandBuffer.end());
......@@ -1719,7 +1718,7 @@ angle::Result ContextVk::traceGpuEventImpl(vk::PrimaryCommandBuffer *commandBuff
gpuEvent.phase = phase;
ANGLE_TRY(mGpuEventQueryPool.allocateQuery(this, &gpuEvent.queryHelper));
gpuEvent.queryHelper.writeTimestamp(commandBuffer);
gpuEvent.queryHelper.writeTimestamp(this, commandBuffer);
mInFlightGpuEventQueries.push_back(std::move(gpuEvent));
return angle::Result::Continue;
......@@ -2220,8 +2219,7 @@ angle::Result ContextVk::clearWithRenderPassOp(
(mRenderPassCommands.started() && !mRenderPassCommands.empty()) ||
mRenderPassCommands.getRenderArea() != clearArea)
{
mGraphicsDirtyBits |= mNewGraphicsCommandBufferDirtyBits;
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, clearArea, &mRenderPassCommandBuffer));
ANGLE_TRY(startRenderPass(clearArea));
}
else
{
......@@ -3861,7 +3859,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
beginInfo.pInheritanceInfo = nullptr;
ANGLE_VK_TRY(this, commandBuffer.begin(beginInfo));
timestampQuery.writeTimestamp(&commandBuffer);
timestampQuery.writeTimestamp(this, &commandBuffer);
ANGLE_VK_TRY(this, commandBuffer.end());
// Create fence for the submission
......@@ -4072,14 +4070,45 @@ angle::Result ContextVk::flushAndBeginRenderPass(
return angle::Result::Continue;
}
ANGLE_INLINE angle::Result ContextVk::startRenderPass(gl::Rectangle renderArea)
{
mGraphicsDirtyBits |= mNewGraphicsCommandBufferDirtyBits;
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, renderArea, &mRenderPassCommandBuffer));
if (mActiveQueryAnySamples)
{
mActiveQueryAnySamples->getQueryHelper()->beginOcclusionQuery(this, &mPrimaryCommands,
mRenderPassCommandBuffer);
}
if (mActiveQueryAnySamplesConservative)
{
mActiveQueryAnySamplesConservative->getQueryHelper()->beginOcclusionQuery(
this, &mPrimaryCommands, mRenderPassCommandBuffer);
}
return angle::Result::Continue;
}
angle::Result ContextVk::endRenderPass()
{
onRenderPassFinished();
if (mRenderPassCommands.empty())
{
onRenderPassFinished();
return angle::Result::Continue;
}
if (mActiveQueryAnySamples)
{
mActiveQueryAnySamples->getQueryHelper()->endOcclusionQuery(this, mRenderPassCommandBuffer);
ANGLE_TRY(mActiveQueryAnySamples->stashQueryHelper(this));
}
if (mActiveQueryAnySamplesConservative)
{
mActiveQueryAnySamplesConservative->getQueryHelper()->endOcclusionQuery(
this, mRenderPassCommandBuffer);
ANGLE_TRY(mActiveQueryAnySamplesConservative->stashQueryHelper(this));
}
onRenderPassFinished();
if (mGpuEventsEnabled)
{
mRenderPassCounter++;
......@@ -4193,6 +4222,53 @@ void ContextVk::flushOutsideRenderPassCommands()
}
}
void ContextVk::beginOcclusionQuery(QueryVk *queryVk)
{
// To avoid complexity, we always start and end occlusion query inside renderpass. if renderpass
// not yet started, we just remember it and defer the start call.
if (mRenderPassCommands.started())
{
queryVk->getQueryHelper()->beginOcclusionQuery(this, &mPrimaryCommands,
mRenderPassCommandBuffer);
}
if (queryVk->isAnySamplesQuery())
{
ASSERT(mActiveQueryAnySamples == nullptr);
mActiveQueryAnySamples = queryVk;
}
else if (queryVk->isAnySamplesConservativeQuery())
{
ASSERT(mActiveQueryAnySamplesConservative == nullptr);
mActiveQueryAnySamplesConservative = queryVk;
}
else
{
UNREACHABLE();
}
}
void ContextVk::endOcclusionQuery(QueryVk *queryVk)
{
if (mRenderPassCommands.started())
{
queryVk->getQueryHelper()->endOcclusionQuery(this, mRenderPassCommandBuffer);
}
if (queryVk->isAnySamplesQuery())
{
ASSERT(mActiveQueryAnySamples == queryVk);
mActiveQueryAnySamples = nullptr;
}
else if (queryVk->isAnySamplesConservativeQuery())
{
ASSERT(mActiveQueryAnySamplesConservative == queryVk);
mActiveQueryAnySamplesConservative = nullptr;
}
else
{
UNREACHABLE();
}
}
CommandBufferHelper::CommandBufferHelper()
: mImageBarrierSrcStageMask(0),
mImageBarrierDstStageMask(0),
......
......@@ -648,6 +648,7 @@ class ContextVk : public ContextImpl, public vk::Context
}
egl::ContextPriority getContextPriority() const override { return mContextPriority; }
angle::Result startRenderPass(gl::Rectangle renderArea);
angle::Result endRenderPass();
angle::Result syncExternalMemory();
......@@ -663,6 +664,10 @@ class ContextVk : public ContextImpl, public vk::Context
ProgramVk *getShaderProgram(const gl::State &glState, gl::ShaderType shaderType) const;
// occlusion query
void beginOcclusionQuery(QueryVk *queryVk);
void endOcclusionQuery(QueryVk *queryVk);
private:
// Dirty bits.
enum DirtyBitType : size_t
......@@ -958,6 +963,10 @@ class ContextVk : public ContextImpl, public vk::Context
ProgramVk *mProgram;
ProgramExecutableVk *mExecutable;
// occlusion query
QueryVk *mActiveQueryAnySamples;
QueryVk *mActiveQueryAnySamplesConservative;
// Graph resource used to record dispatch commands and hold resource dependencies.
vk::DispatchHelper mDispatcher;
......
......@@ -39,6 +39,29 @@ void QueryVk::onDestroy(const gl::Context *context)
}
}
angle::Result QueryVk::stashQueryHelper(ContextVk *contextVk)
{
ASSERT(isOcclusionQuery());
mStashedQueryHelpers.emplace_back(mQueryHelper);
mQueryHelper.deinit();
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
return angle::Result::Continue;
}
angle::Result QueryVk::retrieveStashedQueryResult(ContextVk *contextVk, uint64_t *result)
{
uint64_t total = 0;
for (vk::QueryHelper &query : mStashedQueryHelpers)
{
uint64_t v;
ANGLE_TRY(query.getUint64Result(contextVk, &v));
total += v;
}
mStashedQueryHelpers.clear();
*result = total;
return angle::Result::Continue;
}
angle::Result QueryVk::begin(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
......@@ -58,9 +81,25 @@ angle::Result QueryVk::begin(const gl::Context *context)
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 (isOcclusionQuery())
{
// 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))
{
ANGLE_TRY(contextVk->flushImpl(nullptr));
// As soon as beginQuery is called, previous query's result will not retrievable by API.
// We must clear it so that it will not count against current beginQuery call.
mStashedQueryHelpers.clear();
mQueryHelper.deinit();
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
contextVk->beginOcclusionQuery(this);
}
else if (getType() == gl::QueryType::TimeElapsed)
{
// Note: TimeElapsed is implemented by using two Timestamp queries and taking the diff.
if (!mQueryHelperTimeElapsedBegin.valid())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(
......@@ -96,6 +135,10 @@ angle::Result QueryVk::end(const gl::Context *context)
mCachedResultValid = true;
// We could consider using VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.
}
else if (isOcclusionQuery())
{
contextVk->endOcclusionQuery(this);
}
else if (getType() == gl::QueryType::TimeElapsed)
{
ANGLE_TRY(mQueryHelper.flushAndWriteTimestamp(contextVk));
......@@ -110,6 +153,7 @@ angle::Result QueryVk::end(const gl::Context *context)
angle::Result QueryVk::queryCounter(const gl::Context *context)
{
ASSERT(getType() == gl::QueryType::Timestamp);
ContextVk *contextVk = vk::GetImpl(context);
mCachedResultValid = false;
......@@ -119,8 +163,6 @@ angle::Result QueryVk::queryCounter(const gl::Context *context)
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
ASSERT(getType() == gl::QueryType::Timestamp);
return mQueryHelper.flushAndWriteTimestamp(contextVk);
}
......@@ -162,6 +204,9 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
if (wait)
{
ANGLE_TRY(mQueryHelper.getUint64Result(contextVk, &mCachedResult));
uint64_t v;
ANGLE_TRY(retrieveStashedQueryResult(contextVk, &v));
mCachedResult += v;
}
else
{
......@@ -172,6 +217,9 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// If the results are not ready, do nothing. mCachedResultValid remains false.
return angle::Result::Continue;
}
uint64_t v;
ANGLE_TRY(retrieveStashedQueryResult(contextVk, &v));
mCachedResult += v;
}
double timestampPeriod = renderer->getPhysicalDeviceProperties().limits.timestampPeriod;
......
......@@ -35,12 +35,17 @@ class QueryVk : public QueryImpl
angle::Result isResultAvailable(const gl::Context *context, bool *available) override;
void onTransformFeedbackEnd(const gl::Context *context);
vk::QueryHelper *getQueryHelper() { return &mQueryHelper; }
angle::Result stashQueryHelper(ContextVk *contextVk);
angle::Result retrieveStashedQueryResult(ContextVk *contextVk, uint64_t *result);
private:
angle::Result getResult(const gl::Context *context, bool wait);
// 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.
std::vector<vk::QueryHelper> mStashedQueryHelpers;
// An additional query used for TimeElapsed (begin), as it is implemented using Timestamp.
vk::QueryHelper mQueryHelperTimeElapsedBegin;
// Used with TransformFeedbackPrimitivesWritten when transform feedback is emulated.
......
......@@ -1006,6 +1006,7 @@ void QueryHelper::deinit()
mDynamicQueryPool = nullptr;
mQueryPoolIndex = 0;
mQuery = 0;
mMostRecentSerial = Serial();
}
angle::Result QueryHelper::beginQuery(ContextVk *contextVk)
......@@ -1028,27 +1029,44 @@ angle::Result QueryHelper::endQuery(ContextVk *contextVk)
return angle::Result::Continue;
}
void QueryHelper::beginOcclusionQuery(ContextVk *contextVk,
PrimaryCommandBuffer *primaryCommands,
CommandBuffer *renderPassCommandBuffer)
{
const QueryPool &queryPool = getQueryPool();
// reset has to be encoded in primary command buffer per spec
primaryCommands->resetQueryPool(queryPool, 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();
}
angle::Result QueryHelper::flushAndWriteTimestamp(ContextVk *contextVk)
{
vk::PrimaryCommandBuffer *primary;
ANGLE_TRY(contextVk->flushAndGetPrimaryCommandBuffer(&primary));
writeTimestamp(primary);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
writeTimestamp(contextVk, primary);
return angle::Result::Continue;
}
void QueryHelper::writeTimestamp(vk::PrimaryCommandBuffer *primary)
void QueryHelper::writeTimestamp(ContextVk *contextVk, PrimaryCommandBuffer *primary)
{
const QueryPool &queryPool = getQueryPool();
primary->resetQueryPool(queryPool, mQuery, 1);
primary->writeTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, queryPool, mQuery);
mMostRecentSerial = contextVk->getCurrentQueueSerial();
}
bool QueryHelper::hasPendingWork(ContextVk *contextVk)
{
// 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 == contextVk->getCurrentQueueSerial();
return mMostRecentSerial.valid() && (mMostRecentSerial == contextVk->getCurrentQueueSerial());
}
angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
......@@ -1056,10 +1074,22 @@ angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
bool *availableOut)
{
ASSERT(valid());
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT;
VkResult result = getQueryPool().getResults(device, mQuery, 1, sizeof(uint64_t), resultOut,
sizeof(uint64_t), kFlags);
VkResult result;
// 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())
{
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT;
result = getQueryPool().getResults(device, mQuery, 1, sizeof(uint64_t), resultOut,
sizeof(uint64_t), kFlags);
}
else
{
result = VK_SUCCESS;
*resultOut = 0;
}
if (result == VK_NOT_READY)
{
......@@ -1077,10 +1107,17 @@ angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
angle::Result QueryHelper::getUint64Result(ContextVk *contextVk, uint64_t *resultOut)
{
ASSERT(valid());
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT;
ANGLE_VK_TRY(contextVk, getQueryPool().getResults(device, mQuery, 1, sizeof(uint64_t),
resultOut, sizeof(uint64_t), kFlags));
if (mMostRecentSerial.valid())
{
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT;
ANGLE_VK_TRY(contextVk, getQueryPool().getResults(device, mQuery, 1, sizeof(uint64_t),
resultOut, sizeof(uint64_t), kFlags));
}
else
{
*resultOut = 0;
}
return angle::Result::Continue;
}
......
......@@ -315,8 +315,14 @@ class QueryHelper final
angle::Result beginQuery(ContextVk *contextVk);
angle::Result endQuery(ContextVk *contextVk);
// for occlusion query
void beginOcclusionQuery(ContextVk *contextVk,
PrimaryCommandBuffer *primaryCommands,
CommandBuffer *renderPassCommandBuffer);
void endOcclusionQuery(ContextVk *contextVk, CommandBuffer *renderPassCommandBuffer);
angle::Result flushAndWriteTimestamp(ContextVk *contextVk);
void writeTimestamp(vk::PrimaryCommandBuffer *primary);
void writeTimestamp(ContextVk *contextVk, PrimaryCommandBuffer *primary);
Serial getStoredQueueSerial() { return mMostRecentSerial; }
bool hasPendingWork(ContextVk *contextVk);
......
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