Vulkan: fix glGetQueryObject not flushing
glGetQueryObject* requires forward progress in the queue regardless of
whether we are waiting on the result or busy-looping over whether the
results are available. This commit calls flush() if the query has
pending work.
Additionally, this fixes a race condition where glGetQueryObject* may be
accessing a query whose corresponding batch has been submitted but not
yet executed. In such a case, the GPU may not have already reset the
query, so we have to wait on the fence of that batch to make sure the
query results are reliably available.
Bug: angleproject:2855
Change-Id: I977909c6526c0778a13722a8b8b73e54ad0202f6
Reviewed-on: https://chromium-review.googlesource.com/c/1279125
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/QueryVk.cpp b/src/libANGLE/renderer/vulkan/QueryVk.cpp
index a8b3e9a..d6e11df 100644
--- a/src/libANGLE/renderer/vulkan/QueryVk.cpp
+++ b/src/libANGLE/renderer/vulkan/QueryVk.cpp
@@ -10,6 +10,7 @@
#include "libANGLE/renderer/vulkan/QueryVk.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "common/debug.h"
@@ -66,6 +67,28 @@
}
ContextVk *contextVk = vk::GetImpl(context);
+ RendererVk *renderer = contextVk->getRenderer();
+
+ // glGetQueryObject* requires an implicit flush of the command buffers to guarantee execution in
+ // finite time.
+ if (mQueryHelper.hasPendingWork(renderer))
+ {
+ ANGLE_TRY_HANDLE(context, renderer->flush(contextVk));
+ ASSERT(!mQueryHelper.hasPendingWork(renderer));
+ }
+
+ // If the command buffer this query is being written to is still in flight, its reset 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).
+ ANGLE_TRY(renderer->checkCompletedCommands(contextVk));
+ if (mQueryHelper.isResourceInUse(renderer))
+ {
+ if (!wait)
+ {
+ return angle::Result::Continue();
+ }
+ ANGLE_TRY(renderer->finishToSerial(contextVk, mQueryHelper.getStoredQueueSerial()));
+ }
VkQueryResultFlags flags = (wait ? VK_QUERY_RESULT_WAIT_BIT : 0) | VK_QUERY_RESULT_64_BIT;
@@ -74,23 +97,28 @@
sizeof(mCachedResult), flags);
ANGLE_TRY(result);
- if (result == angle::Result::Continue())
+ // If the results are not ready, do nothing. mCachedResultValid remains false.
+ if (result == angle::Result::Incomplete())
{
- mCachedResultValid = true;
-
- switch (getType())
- {
- case gl::QueryType::AnySamples:
- case gl::QueryType::AnySamplesConservative:
- // OpenGL query result in these cases is binary
- mCachedResult = !!mCachedResult;
- break;
- default:
- UNREACHABLE();
- break;
- }
+ // If VK_QUERY_RESULT_WAIT_BIT was given, Incomplete() cannot have been returned.
+ ASSERT(!wait);
+ return angle::Result::Continue();
}
+ // Fix up the results to what OpenGL expects.
+ switch (getType())
+ {
+ case gl::QueryType::AnySamples:
+ case gl::QueryType::AnySamplesConservative:
+ // OpenGL query result in these cases is binary
+ mCachedResult = !!mCachedResult;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ mCachedResultValid = true;
return angle::Result::Continue();
}
@@ -124,18 +152,6 @@
gl::Error QueryVk::isResultAvailable(const gl::Context *context, bool *available)
{
- ContextVk *contextVk = vk::GetImpl(context);
-
- // Make sure the command buffer for this query is submitted. If not, *available should always
- // be false. This is because the reset command is not yet executed (it's only put in the command
- // graph), so actually checking the results may return "true" because of a previous submission.
-
- if (mQueryHelper.hasPendingWork(contextVk->getRenderer()))
- {
- *available = false;
- return gl::NoError();
- }
-
ANGLE_TRY(getResult(context, false));
*available = mCachedResultValid;