Commit f691b3b5 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Support PrimitivesGenerated query

This query uses the Vulkan transform feedback extension. In GL, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN and GL_PRIMITIVES_GENERATED queries can be independently begun/ended. However, Vulkan requires that queries from pools of the same type can only be active one at a time. This forbids the two GL queries from being handled by two VK queries when they are simultaneously begun. This change makes these queries share their QueryHelper objects. The Vulkan transform feedback queries unconditionally retrieve both results anyway, so this is just a matter of making sure the two GL queries are merged as one when they are simultaneously used. The change fixes a number of issues as collateral: - TransformFeedbackPrimitivesWritten queries when !emulated were not released - Stashed queries were never released - If no render pass is open when a query ends, then getResult(no_wait) ended up waiting Bug: angleproject:5404 Change-Id: I8ce13ea76ffd31b3152ded7c713c6466d0315504 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2573580 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarCharlie Lao <cclao@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent fa9b803e
......@@ -179,7 +179,7 @@ void ValidateShaderInterface(TDiagnostics *diagnostics,
// A varying is either:
//
// - A vector or matrix, which can take a number of contiguous locations
// - A struct, which also takes a number of contiquous locations
// - A struct, which also takes a number of contiguous locations
// - An interface block.
//
// Interface blocks can assign arbitrary locations to their fields, for example:
......
......@@ -1742,7 +1742,8 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
// Use the first timestamp queried as origin.
if (mGpuEventTimestampOrigin == 0)
{
mGpuEventTimestampOrigin = gpuTimestampCycles.getResult();
mGpuEventTimestampOrigin =
gpuTimestampCycles.getResult(vk::QueryResult::kDefaultResultIndex);
}
// Take these CPU and GPU timestamps if there is better confidence.
......@@ -1751,7 +1752,7 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
{
tightestRangeS = confidenceRangeS;
TcpuS = cpuTimestampS;
TgpuCycles = gpuTimestampCycles.getResult();
TgpuCycles = gpuTimestampCycles.getResult(vk::QueryResult::kDefaultResultIndex);
}
}
......@@ -1820,9 +1821,10 @@ angle::Result ContextVk::checkCompletedGpuEvents()
mGpuEventQueryPool.freeQuery(this, &eventQuery.queryHelper);
GpuEvent gpuEvent;
gpuEvent.gpuTimestampCycles = gpuTimestampCycles.getResult();
gpuEvent.name = eventQuery.name;
gpuEvent.phase = eventQuery.phase;
gpuEvent.gpuTimestampCycles =
gpuTimestampCycles.getResult(vk::QueryResult::kDefaultResultIndex);
gpuEvent.name = eventQuery.name;
gpuEvent.phase = eventQuery.phase;
mGpuEvents.emplace_back(gpuEvent);
......@@ -3600,9 +3602,17 @@ vk::DynamicQueryPool *ContextVk::getQueryPool(gl::QueryType queryType)
{
ASSERT(queryType == gl::QueryType::AnySamples ||
queryType == gl::QueryType::AnySamplesConservative ||
queryType == gl::QueryType::PrimitivesGenerated ||
queryType == gl::QueryType::TransformFeedbackPrimitivesWritten ||
queryType == gl::QueryType::Timestamp || queryType == gl::QueryType::TimeElapsed);
// For PrimitivesGenerated queries, use the same pool as TransformFeedbackPrimitivesWritten.
// They are served with the same Vulkan query.
if (queryType == gl::QueryType::PrimitivesGenerated)
{
queryType = gl::QueryType::TransformFeedbackPrimitivesWritten;
}
// Assert that timestamp extension is available if needed.
ASSERT(queryType != gl::QueryType::Timestamp && queryType != gl::QueryType::TimeElapsed ||
mRenderer->getQueueFamilyProperties().timestampValidBits > 0);
......@@ -4389,7 +4399,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
// Get the query results
vk::QueryResult result(1);
ANGLE_TRY(timestampQuery.getUint64Result(this, &result));
*timestampOut = result.getResult();
*timestampOut = result.getResult(vk::QueryResult::kDefaultResultIndex);
timestampQueryPool.get().freeQuery(this, &timestampQuery);
// Convert results to nanoseconds.
......@@ -4583,7 +4593,7 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass()
return angle::Result::Continue;
}
ANGLE_TRY(pauseRenderPassQueriesIfActive());
pauseRenderPassQueriesIfActive();
mCurrentTransformFeedbackBuffers.clear();
mCurrentIndirectBuffer = nullptr;
......@@ -4770,38 +4780,43 @@ void ContextVk::endRenderPassQuery(QueryVk *queryVk)
mActiveRenderPassQueries[type] = nullptr;
}
angle::Result ContextVk::pauseRenderPassQueriesIfActive()
void ContextVk::pauseRenderPassQueriesIfActive()
{
if (mRenderPassCommandBuffer == nullptr)
{
return angle::Result::Continue;
return;
}
for (QueryVk *activeQuery : mActiveRenderPassQueries)
{
if (activeQuery)
{
activeQuery->getQueryHelper()->endRenderPassQuery(this);
ANGLE_TRY(activeQuery->stashQueryHelper(this));
activeQuery->onRenderPassEnd(this);
}
}
return angle::Result::Continue;
}
angle::Result ContextVk::resumeRenderPassQueriesIfActive()
{
ASSERT(mRenderPassCommandBuffer);
// Note: these queries should be processed in order. See comment in QueryVk::onRenderPassStart.
for (QueryVk *activeQuery : mActiveRenderPassQueries)
{
if (activeQuery)
{
ANGLE_TRY(activeQuery->getQueryHelper()->beginRenderPassQuery(this));
ANGLE_TRY(activeQuery->onRenderPassStart(this));
}
}
return angle::Result::Continue;
}
QueryVk *ContextVk::getActiveRenderPassQuery(gl::QueryType queryType) const
{
return mActiveRenderPassQueries[queryType];
}
bool ContextVk::isRobustResourceInitEnabled() const
{
return mState.isRobustResourceInitEnabled();
......
......@@ -536,13 +536,15 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
bool isRobustResourceInitEnabled() const;
// occlusion query
// Queries that begin and end automatically with render pass start and end
angle::Result beginRenderPassQuery(QueryVk *queryVk);
void endRenderPassQuery(QueryVk *queryVk);
angle::Result pauseRenderPassQueriesIfActive();
void pauseRenderPassQueriesIfActive();
angle::Result resumeRenderPassQueriesIfActive();
// Used by QueryVk to share query helpers between transform feedback queries.
QueryVk *getActiveRenderPassQuery(gl::QueryType queryType) const;
void updateOverlayOnPresent();
void addOverlayUsedBuffersCount(vk::CommandBufferHelper *commandBuffer);
......
......@@ -21,12 +21,21 @@ namespace rx
namespace
{
struct QueryReleaseHelper
{
void operator()(vk::QueryHelper &&query) { queryPool->freeQuery(contextVk, &query); }
ContextVk *contextVk;
vk::DynamicQueryPool *queryPool;
};
bool IsRenderPassQuery(ContextVk *contextVk, gl::QueryType type)
{
switch (type)
{
case gl::QueryType::AnySamples:
case gl::QueryType::AnySamplesConservative:
case gl::QueryType::PrimitivesGenerated:
return true;
case gl::QueryType::TransformFeedbackPrimitivesWritten:
return contextVk->getFeatures().supportsTransformFeedbackExtension.enabled;
......@@ -40,6 +49,46 @@ bool IsEmulatedTransformFeedbackQuery(ContextVk *contextVk, gl::QueryType type)
return type == gl::QueryType::TransformFeedbackPrimitivesWritten &&
contextVk->getFeatures().emulateTransformFeedback.enabled;
}
QueryVk *GetShareQuery(ContextVk *contextVk, gl::QueryType type)
{
QueryVk *shareQuery = nullptr;
switch (type)
{
case gl::QueryType::PrimitivesGenerated:
shareQuery = contextVk->getActiveRenderPassQuery(
gl::QueryType::TransformFeedbackPrimitivesWritten);
break;
case gl::QueryType::TransformFeedbackPrimitivesWritten:
shareQuery = contextVk->getActiveRenderPassQuery(gl::QueryType::PrimitivesGenerated);
break;
default:
break;
}
return shareQuery;
}
// When a render pass starts/ends, onRenderPassStart/End is called for all active queries. For
// shared queries, the one that is called first would actually manage the query helper begin/end and
// allocation, and the one that follows would share it. PrimitivesGenerated and
// TransformFeedbackPrimitivesWritten share queries, and the former is processed first.
QueryVk *GetOnRenderPassStartEndShareQuery(ContextVk *contextVk, gl::QueryType type)
{
static_assert(
gl::QueryType::PrimitivesGenerated < gl::QueryType::TransformFeedbackPrimitivesWritten,
"incorrect assumption about the order in which queries are started in a render pass");
if (type != gl::QueryType::TransformFeedbackPrimitivesWritten)
{
return nullptr;
}
// For TransformFeedbackPrimitivesWritten, return the already-processed PrimitivesGenerated
// share query.
return contextVk->getActiveRenderPassQuery(gl::QueryType::PrimitivesGenerated);
}
} // anonymous namespace
QueryVk::QueryVk(gl::QueryType type)
......@@ -51,35 +100,177 @@ QueryVk::QueryVk(gl::QueryType type)
QueryVk::~QueryVk() = default;
angle::Result QueryVk::allocateQuery(ContextVk *contextVk)
{
ASSERT(!mQueryHelper.isReferenced());
mQueryHelper.setUnreferenced(new vk::RefCounted<vk::QueryHelper>);
return contextVk->getQueryPool(mType)->allocateQuery(contextVk, &mQueryHelper.get());
}
void QueryVk::assignSharedQuery(QueryVk *shareQuery)
{
ASSERT(!mQueryHelper.isReferenced());
ASSERT(shareQuery->mQueryHelper.isReferenced());
mQueryHelper.copyUnreferenced(shareQuery->mQueryHelper);
}
void QueryVk::releaseQueries(ContextVk *contextVk)
{
ASSERT(!IsEmulatedTransformFeedbackQuery(contextVk, mType));
vk::DynamicQueryPool *queryPool = contextVk->getQueryPool(mType);
// Free the main query
if (mQueryHelper.isReferenced())
{
QueryReleaseHelper releaseHelper = {contextVk, queryPool};
mQueryHelper.resetAndRelease(&releaseHelper);
}
// Free the secondary query used to emulate TimeElapsed
queryPool->freeQuery(contextVk, &mQueryHelperTimeElapsedBegin);
// Free any stashed queries used to support queries that start and stop with the render pass.
releaseStashedQueries(contextVk);
}
void QueryVk::releaseStashedQueries(ContextVk *contextVk)
{
vk::DynamicQueryPool *queryPool = contextVk->getQueryPool(mType);
for (vk::Shared<vk::QueryHelper> &query : mStashedQueryHelpers)
{
ASSERT(query.isReferenced());
QueryReleaseHelper releaseHelper = {contextVk, queryPool};
query.resetAndRelease(&releaseHelper);
}
mStashedQueryHelpers.clear();
}
void QueryVk::onDestroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
if (mType != gl::QueryType::TransformFeedbackPrimitivesWritten)
if (!IsEmulatedTransformFeedbackQuery(contextVk, mType))
{
vk::DynamicQueryPool *queryPool = contextVk->getQueryPool(mType);
queryPool->freeQuery(contextVk, &mQueryHelper);
queryPool->freeQuery(contextVk, &mQueryHelperTimeElapsedBegin);
releaseQueries(contextVk);
}
}
angle::Result QueryVk::stashQueryHelper(ContextVk *contextVk)
void QueryVk::stashQueryHelper()
{
ASSERT(mQueryHelper.isReferenced());
mStashedQueryHelpers.push_back(std::move(mQueryHelper));
ASSERT(!mQueryHelper.isReferenced());
}
angle::Result QueryVk::onRenderPassStart(ContextVk *contextVk)
{
ASSERT(IsRenderPassQuery(contextVk, mType));
mStashedQueryHelpers.emplace_back(std::move(mQueryHelper));
mQueryHelper.deinit();
ANGLE_TRY(contextVk->getQueryPool(mType)->allocateQuery(contextVk, &mQueryHelper));
return angle::Result::Continue;
// If there is a query helper already, stash it and allocate a new one for this render pass.
if (mQueryHelper.isReferenced())
{
stashQueryHelper();
}
QueryVk *shareQuery = GetOnRenderPassStartEndShareQuery(contextVk, mType);
if (shareQuery)
{
assignSharedQuery(shareQuery);
// shareQuery has already started the query.
return angle::Result::Continue;
}
ANGLE_TRY(allocateQuery(contextVk));
return mQueryHelper.get().beginRenderPassQuery(contextVk);
}
void QueryVk::onRenderPassEnd(ContextVk *contextVk)
{
ASSERT(IsRenderPassQuery(contextVk, mType));
ASSERT(mQueryHelper.isReferenced());
QueryVk *shareQuery = GetOnRenderPassStartEndShareQuery(contextVk, mType);
// If present, share query has already taken care of ending the query.
if (shareQuery == nullptr)
{
mQueryHelper.get().endRenderPassQuery(contextVk);
}
}
angle::Result QueryVk::accumulateStashedQueryResult(ContextVk *contextVk, vk::QueryResult *result)
{
for (vk::QueryHelper &query : mStashedQueryHelpers)
for (vk::Shared<vk::QueryHelper> &query : mStashedQueryHelpers)
{
vk::QueryResult v(getQueryResultCount());
ANGLE_TRY(query.getUint64Result(contextVk, &v));
ANGLE_TRY(query.get().getUint64Result(contextVk, &v));
*result += v;
}
mStashedQueryHelpers.clear();
releaseStashedQueries(contextVk);
return angle::Result::Continue;
}
angle::Result QueryVk::setupBegin(ContextVk *contextVk)
{
if (IsRenderPassQuery(contextVk, mType))
{
// Clean up query helpers from the previous begin/end call on the same query. Only
// necessary for in-render-pass queries. The other queries can reuse query helpers as they
// are able to reset it ouside the render pass where they are recorded.
if (mQueryHelper.isReferenced())
{
releaseQueries(contextVk);
}
// If either of TransformFeedbackPrimitivesWritten or PrimitivesGenerated queries are
// already active when the other one is begun, we have to switch to a new query helper (if
// in render pass), and have them share the query helper from here on.
// If this is a transform feedback query, see if the other transform feedback query is
// already active.
QueryVk *shareQuery = GetShareQuery(contextVk, mType);
// If so, make the other query stash its results and continue with a new query helper.
if (contextVk->hasStartedRenderPass())
{
if (shareQuery)
{
// This serves the following scenario (TF = TransformFeedbackPrimitivesWritten, PG =
// PrimitivesGenerated):
//
// - TF starts <-- QueryHelper1 starts
// - Draw
// - PG starts <-- QueryHelper1 stashed in TF, TF starts QueryHelper2,
// PG shares QueryHelper2
// - Draw
shareQuery->onRenderPassEnd(contextVk);
shareQuery->stashQueryHelper();
ANGLE_TRY(shareQuery->allocateQuery(contextVk));
// Share the query helper with the other transform feedback query. After
// |setupBegin()| returns, they query helper is started on behalf of the shared
// query.
assignSharedQuery(shareQuery);
}
}
else
{
// Keep the query helper unallocated. When the render pass starts, a new one
// will be allocated / shared.
return angle::Result::Continue;
}
}
// If no query helper, create a new one.
if (!mQueryHelper.isReferenced())
{
ANGLE_TRY(allocateQuery(contextVk));
}
return angle::Result::Continue;
}
......@@ -89,7 +280,7 @@ angle::Result QueryVk::begin(const gl::Context *context)
mCachedResultValid = false;
// Transform feedback query is a handled by a CPU-calculated value when emulated.
// Transform feedback query is handled by a CPU-calculated value when emulated.
if (IsEmulatedTransformFeedbackQuery(contextVk, mType))
{
ASSERT(!contextVk->getFeatures().supportsTransformFeedbackExtension.enabled);
......@@ -98,32 +289,18 @@ angle::Result QueryVk::begin(const gl::Context *context)
return angle::Result::Continue;
}
if (!mQueryHelper.valid())
{
ANGLE_TRY(contextVk->getQueryPool(mType)->allocateQuery(contextVk, &mQueryHelper));
}
ANGLE_TRY(setupBegin(contextVk));
switch (mType)
{
case gl::QueryType::AnySamples:
case gl::QueryType::AnySamplesConservative:
case gl::QueryType::PrimitivesGenerated:
case gl::QueryType::TransformFeedbackPrimitivesWritten:
// 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.usedInRecordedCommands())
{
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(mType)->allocateQuery(contextVk, &mQueryHelper));
}
ANGLE_TRY(contextVk->beginRenderPassQuery(this));
break;
case gl::QueryType::Timestamp:
ANGLE_TRY(mQueryHelper.beginQuery(contextVk));
ANGLE_TRY(mQueryHelper.get().beginQuery(contextVk));
break;
case gl::QueryType::TimeElapsed:
// Note: TimeElapsed is implemented by using two Timestamp queries and taking the diff.
......@@ -147,7 +324,7 @@ angle::Result QueryVk::end(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
// Transform feedback query is a handled by a CPU-calculated value when emulated.
// Transform feedback query is handled by a CPU-calculated value when emulated.
if (IsEmulatedTransformFeedbackQuery(contextVk, mType))
{
ASSERT(contextVk->getFeatures().emulateTransformFeedback.enabled);
......@@ -170,14 +347,40 @@ angle::Result QueryVk::end(const gl::Context *context)
{
case gl::QueryType::AnySamples:
case gl::QueryType::AnySamplesConservative:
case gl::QueryType::PrimitivesGenerated:
case gl::QueryType::TransformFeedbackPrimitivesWritten:
{
QueryVk *shareQuery = GetShareQuery(contextVk, mType);
ASSERT(shareQuery == nullptr || &mQueryHelper.get() == &shareQuery->mQueryHelper.get());
contextVk->endRenderPassQuery(this);
// If another query shares its query helper with this one, its query has just ended!
// Make it stash its query and create a new one so it can continue.
if (shareQuery && shareQuery->mQueryHelper.isReferenced())
{
// This serves the following scenario (TF = TransformFeedbackPrimitivesWritten, PG =
// PrimitivesGenerated):
//
// - TF starts <-- QueryHelper1 starts
// - PG starts <-- PG shares QueryHelper1
// - Draw
// - TF ends <-- Results = QueryHelper1,
// QueryHelper1 stashed in PG, PG starts QueryHelper2
// - Draw
// - PG ends <-- Results = QueryHelper1 + QueryHelper2
if (contextVk->hasStartedRenderPass())
{
ANGLE_TRY(shareQuery->onRenderPassStart(contextVk));
}
}
break;
}
case gl::QueryType::Timestamp:
ANGLE_TRY(mQueryHelper.endQuery(contextVk));
ANGLE_TRY(mQueryHelper.get().endQuery(contextVk));
break;
case gl::QueryType::TimeElapsed:
ANGLE_TRY(mQueryHelper.flushAndWriteTimestamp(contextVk));
ANGLE_TRY(mQueryHelper.get().flushAndWriteTimestamp(contextVk));
break;
default:
UNREACHABLE();
......@@ -194,24 +397,26 @@ angle::Result QueryVk::queryCounter(const gl::Context *context)
mCachedResultValid = false;
if (!mQueryHelper.valid())
if (!mQueryHelper.isReferenced())
{
ANGLE_TRY(contextVk->getQueryPool(mType)->allocateQuery(contextVk, &mQueryHelper));
ANGLE_TRY(allocateQuery(contextVk));
}
return mQueryHelper.flushAndWriteTimestamp(contextVk);
return mQueryHelper.get().flushAndWriteTimestamp(contextVk);
}
bool QueryVk::isUsedInRecordedCommands() const
{
if (mQueryHelper.usedInRecordedCommands())
ASSERT(mQueryHelper.isReferenced());
if (mQueryHelper.get().usedInRecordedCommands())
{
return true;
}
for (const vk::QueryHelper &query : mStashedQueryHelpers)
for (const vk::Shared<vk::QueryHelper> &query : mStashedQueryHelpers)
{
if (query.usedInRecordedCommands())
if (query.get().usedInRecordedCommands())
{
return true;
}
......@@ -222,14 +427,16 @@ bool QueryVk::isUsedInRecordedCommands() const
bool QueryVk::isCurrentlyInUse(Serial lastCompletedSerial) const
{
if (mQueryHelper.isCurrentlyInUse(lastCompletedSerial))
ASSERT(mQueryHelper.isReferenced());
if (mQueryHelper.get().isCurrentlyInUse(lastCompletedSerial))
{
return true;
}
for (const vk::QueryHelper &query : mStashedQueryHelpers)
for (const vk::Shared<vk::QueryHelper> &query : mStashedQueryHelpers)
{
if (query.isCurrentlyInUse(lastCompletedSerial))
if (query.get().isCurrentlyInUse(lastCompletedSerial))
{
return true;
}
......@@ -242,17 +449,17 @@ angle::Result QueryVk::finishRunningCommands(ContextVk *contextVk)
{
Serial lastCompletedSerial = contextVk->getLastCompletedQueueSerial();
if (mQueryHelper.usedInRunningCommands(lastCompletedSerial))
if (mQueryHelper.get().usedInRunningCommands(lastCompletedSerial))
{
ANGLE_TRY(mQueryHelper.finishRunningCommands(contextVk));
ANGLE_TRY(mQueryHelper.get().finishRunningCommands(contextVk));
lastCompletedSerial = contextVk->getLastCompletedQueueSerial();
}
for (vk::QueryHelper &query : mStashedQueryHelpers)
for (vk::Shared<vk::QueryHelper> &query : mStashedQueryHelpers)
{
if (query.usedInRunningCommands(lastCompletedSerial))
if (query.get().usedInRunningCommands(lastCompletedSerial))
{
ANGLE_TRY(query.finishRunningCommands(contextVk));
ANGLE_TRY(query.get().finishRunningCommands(contextVk));
lastCompletedSerial = contextVk->getLastCompletedQueueSerial();
}
}
......@@ -271,6 +478,16 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
// Support the pathological case where begin/end is called on a render pass query but without
// any render passes in between. In this case, the query helper is never allocated.
if (!mQueryHelper.isReferenced())
{
ASSERT(IsRenderPassQuery(contextVk, mType));
mCachedResult = 0;
mCachedResultValid = true;
return angle::Result::Continue;
}
// 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
......@@ -281,7 +498,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
ANGLE_TRY(contextVk->flushImpl(nullptr));
ASSERT(!mQueryHelperTimeElapsedBegin.usedInRecordedCommands());
ASSERT(!mQueryHelper.usedInRecordedCommands());
ASSERT(!mQueryHelper.get().usedInRecordedCommands());
}
ANGLE_TRY(contextVk->checkCompletedCommands());
......@@ -304,17 +521,24 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
ANGLE_TRY(finishRunningCommands(contextVk));
}
// If its a render pass query, the current query helper must have commands recorded (i.e. it's
// not a newly allocated query with the actual queries all stashed). If this is not respected
// and !wait, |mQueryHelper.get().getUint64ResultNonBlocking()| will tell that the result is
// readily available, which may not be true. The subsequent calls to |getUint64Result()| on the
// stashed queries will incur a wait that is not desired by the application.
ASSERT(!IsRenderPassQuery(contextVk, mType) || mQueryHelper.get().hasSubmittedCommands());
vk::QueryResult result(getQueryResultCount());
if (wait)
{
ANGLE_TRY(mQueryHelper.getUint64Result(contextVk, &result));
ANGLE_TRY(mQueryHelper.get().getUint64Result(contextVk, &result));
ANGLE_TRY(accumulateStashedQueryResult(contextVk, &result));
}
else
{
bool available = false;
ANGLE_TRY(mQueryHelper.getUint64ResultNonBlocking(contextVk, &result, &available));
ANGLE_TRY(mQueryHelper.get().getUint64ResultNonBlocking(contextVk, &result, &available));
if (!available)
{
// If the results are not ready, do nothing. mCachedResultValid remains false.
......@@ -331,10 +555,11 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
case gl::QueryType::AnySamples:
case gl::QueryType::AnySamplesConservative:
// OpenGL query result in these cases is binary
mCachedResult = !!result.getResult();
mCachedResult = !!result.getResult(vk::QueryResult::kDefaultResultIndex);
break;
case gl::QueryType::Timestamp:
mCachedResult = static_cast<uint64_t>(result.getResult() * timestampPeriod);
mCachedResult = static_cast<uint64_t>(
result.getResult(vk::QueryResult::kDefaultResultIndex) * timestampPeriod);
break;
case gl::QueryType::TimeElapsed:
{
......@@ -344,12 +569,17 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// result of begin query must be available too.
ANGLE_TRY(mQueryHelperTimeElapsedBegin.getUint64Result(contextVk, &timeElapsedBegin));
uint64_t delta = result.getResult() - timeElapsedBegin.getResult();
mCachedResult = static_cast<uint64_t>(delta * timestampPeriod);
uint64_t delta = result.getResult(vk::QueryResult::kDefaultResultIndex) -
timeElapsedBegin.getResult(vk::QueryResult::kDefaultResultIndex);
mCachedResult = static_cast<uint64_t>(delta * timestampPeriod);
break;
}
case gl::QueryType::TransformFeedbackPrimitivesWritten:
mCachedResult = result.getResult();
mCachedResult =
result.getResult(vk::QueryResult::kTransformFeedbackPrimitivesWrittenIndex);
break;
case gl::QueryType::PrimitivesGenerated:
mCachedResult = result.getResult(vk::QueryResult::kPrimitivesGeneratedIndex);
break;
default:
UNREACHABLE();
......@@ -404,6 +634,7 @@ uint32_t QueryVk::getQueryResultCount() const
{
switch (mType)
{
case gl::QueryType::PrimitivesGenerated:
case gl::QueryType::TransformFeedbackPrimitivesWritten:
return 2;
default:
......
......@@ -35,8 +35,16 @@ class QueryVk : public QueryImpl
angle::Result isResultAvailable(const gl::Context *context, bool *available) override;
void onTransformFeedbackEnd(GLsizeiptr primitivesDrawn);
vk::QueryHelper *getQueryHelper() { return &mQueryHelper; }
angle::Result stashQueryHelper(ContextVk *contextVk);
vk::QueryHelper *getQueryHelper()
{
ASSERT(mQueryHelper.isReferenced());
return &mQueryHelper.get();
}
// Called by ContextVk on render pass start / end for render pass queries. These will
// stash and create new queries as needed.
angle::Result onRenderPassStart(ContextVk *contextVk);
void onRenderPassEnd(ContextVk *contextVk);
private:
angle::Result getResult(const gl::Context *context, bool wait);
......@@ -44,14 +52,26 @@ class QueryVk : public QueryImpl
bool isUsedInRecordedCommands() const;
bool isCurrentlyInUse(Serial lastCompletedSerial) const;
angle::Result finishRunningCommands(ContextVk *contextVk);
void stashQueryHelper();
uint32_t getQueryResultCount() const;
angle::Result accumulateStashedQueryResult(ContextVk *contextVk, vk::QueryResult *result);
// Used for all queries, except TimeElapsed (begin) or those that are emulated.
vk::QueryHelper mQueryHelper;
// Manage query allocations
angle::Result allocateQuery(ContextVk *contextVk);
void assignSharedQuery(QueryVk *shareQuery);
void releaseQueries(ContextVk *contextVk);
void releaseStashedQueries(ContextVk *contextVk);
// Prepare for begin by handling peculiarities such as the two transform feedback queries
// sharing QueryHelpers.
angle::Result setupBegin(ContextVk *contextVk);
// Used for all queries, except TimeElapsed (begin) or those that are emulated. For transform
// feedback queries, these can be shared if the two queries are simultaneously active.
vk::Shared<vk::QueryHelper> mQueryHelper;
// Used for queries that may end up with multiple outstanding query helper objects as they end
// and begin again with render passes.
std::vector<vk::QueryHelper> mStashedQueryHelpers;
std::vector<vk::Shared<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.
......
......@@ -79,6 +79,7 @@ More implementation details can be found in the `doc` directory:
- [OpenGL Line Segment Rasterization](doc/OpenGLLineSegmentRasterization.md)
- [Format Tables and Emulation](doc/FormatTablesAndEmulation.md)
- [Deferred Clears](doc/DeferredClears.md)
- [Queries](doc/Queries.md)
[VkDevice]: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkDevice.html
[VkQueue]: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkQueue.html
......
......@@ -135,17 +135,18 @@ angle::Result TransformFeedbackVk::begin(const gl::Context *context,
angle::Result TransformFeedbackVk::end(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
// If there's an active transform feedback query, accumulate the primitives drawn.
const gl::State &glState = context->getState();
gl::Query *transformFeedbackQuery =
glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
if (transformFeedbackQuery)
if (transformFeedbackQuery && contextVk->getFeatures().emulateTransformFeedback.enabled)
{
vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
}
ContextVk *contextVk = vk::GetImpl(context);
contextVk->onEndTransformFeedback();
return angle::Result::Continue;
}
......
......@@ -1601,7 +1601,7 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
contextVk->pauseTransformFeedbackIfStartedAndRebindBuffersOnResume();
// Make sure this draw call doesn't count towards occlusion query results.
ANGLE_TRY(contextVk->pauseRenderPassQueriesIfActive());
contextVk->pauseRenderPassQueriesIfActive();
commandBuffer->setScissor(0, 1, &scissor);
commandBuffer->draw(3, 0);
ANGLE_TRY(contextVk->resumeRenderPassQueriesIfActive());
......
# Queries
OpenGL queries generally have a straightforward mapping to Vulkan queries, with the exception of
`GL_PRIMITIVES_GENERATED`. Some Vulkan queries are active only inside a render pass, while others
are affected by both inside and outside render pass commands.
## Outside Render Pass Queries
The following queries are recorded outside a render pass. If a render pass is active when
`begin()` or `end()` is called for these queries, it will be broken.
- `GL_TIME_ELAPSED_EXT`
- `GL_TIMESTAMP_EXT`
## Inside Render Pass Queries
The rest of the queries are active only inside render passes. The context (`ContextVk`) keeps track
of currently active "render pass queries" and automatically pauses and resumes them as render passes
are broken and started again.
- On query `begin()`: `ContextVk::beginRenderPassQuery()` is called which:
* If a render pass is open, immediately starts the query and keeps track of it
* Otherwise keeps the query tracked to be started in the next render pass
- On query `end()`: `ContextVk::endRenderPassQuery()` is called which:
* If a render pass is open, stops the query
* Loses track of the query
- On render pass start: `ContextVk::resumeRenderPassQueriesIfActive()` is called which starts all
active queries.
- On render pass end: `ContextVk::pauseRenderPassQueriesIfActive()` is called which stops all
active queries.
In Vulkan, a query cannot be paused or resumed, only begun and ended. This means that GL queries
that span multiple render passes must use multiple Vulkan queries whose results are accumulated.
This is done on render pass start, where `QueryVk::onRenderPassStart()` would stash the previous
Vulkan query (if any) and create a new one before starting it. When a query is begun, the
`QueryVk`'s "current" Vulkan query (`mQueryHelper`) is only allocated if there's a render pass
active.
**Invariant rule**: With the above algorithm, `QueryVk::mQueryHelper` is at all times either
`nullptr` or has commands recorded. This is important when getting query results to be able to
ask `mQueryHelper` for availability of results.
Later on, `QueryVk::getResult()` would take the sum of the current and all stashed Vulkan queries as
the final result.
### Mid-Render-Pass Clears
If a clear is performed while a render pass query is active and if that clear needs to take a
draw-based path, `UtilsVk` ensures that the draw call does not contribute to query results. This is
done by pausing (`ContextVk::pauseRenderPassQueriesIfActive`) the queries before the draw call and
resuming (`ContextVk::resumeRenderPassQueriesIfActive`) afterwards.
The rest of the `UtilsVk` draw-based functions start a render pass out of the knowledge of
`ContextVk`, so queries will not be activated. In the future, if any `UtilsVk` functions use the
current render pass the way `UtilsVk::clearFramebuffer` does, they must also ensure that they pause
and resume queries.
### Transform Feedback Queries
OpenGL has two distinct queries `GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN` and
`GL_PRIMITIVES_GENERATED`. In Vulkan however, these are served by a single query from
`VK_EXT_transform_feedback`. Additionally, Vulkan requires that only a single query of any type can
be active at a time. This forces ANGLE to have the two GL queries share their Vulkan queries when
both transform feedback queries are active.
To support the above use case, `QueryVk`s keep ref-counted (`vk::Shared`) Vulkan query
(`vk::QueryHelper`) objects. When either transform feedback query is begun:
- If the other transform feedback query (`shareQuery`) is active and a render pass is active:
* `shareQuery`'s current Vulkan query is stopped and stashed, and a new one is allocated
* `shareQuery`'s new Vulkan query is taken as this query's current one with the ref-count
incremented
* The Vulkan query is started as usual
- If the other transform feedback query is active and a render pass is not:
* The current Vulkan query is kept `nullptr`. When the next render pass starts, they will share
their Vulkan queries.
- If the other transform feedback query is not active, proceed as usual
Breaking the `shareQuery`'s Vulkan query on begin ensures that whatever results it may have accrued
before do not contribute to this query.
Similarly, when a transform feedback query is ended, the Vulkan query is ended as usual and then:
- If the other transform feedback query is active and a render pass is active:
* `shareQuery`'s Vulkan query (which is the same as this query's, as they share it) is stashed
* `shareQuery` allocates a new Vulkan query and starts it
When a render pass is broken and active queries are paused
(`ContextVk::pauseRenderPassQueriesIfActive`), only one of the queries will close the shared Vulkan
query.
When a render pass is started and active queries are resumed
(`ContextVk::resumeRenderPassQueriesIfActive`), only one of the queries will allocate and start a
new Vulkan query. The other one will just take that and share it (and increment the ref-count).
The above solution supports the following scenarios:
- Simultaneous begin of the two queries:
```
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
glBeginQuery(GL_PRIMITIVES_GENERATED)
glDraw*()
```
- Simultaneous end of the two queries:
```
glDraw*()
glEndQuery(GL_PRIMITIVES_GENERATED)
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
```
- Draw calls between begin calls:
```
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
glDraw*()
glBeginQuery(GL_PRIMITIVES_GENERATED)
glDraw*()
```
- Draw calls between end calls:
```
glDraw*()
glEndQuery(GL_PRIMITIVES_GENERATED)
glDraw*()
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
```
- Queries getting deleted / rebound when the other is active, for example:
```
glDraw*()
glEndQuery(GL_PRIMITIVES_GENERATED)
glDeleteQueries(primitivesGenerated)
glDraw*()
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
```
......@@ -2533,6 +2533,11 @@ void QueryHelper::writeTimestamp(ContextVk *contextVk, CommandBuffer *commandBuf
retain(&contextVk->getResourceUseList());
}
bool QueryHelper::hasSubmittedCommands() const
{
return mUse.getSerial().valid();
}
angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
QueryResult *resultOut,
bool *availableOut)
......@@ -2542,7 +2547,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 (mUse.getSerial().valid())
if (hasSubmittedCommands())
{
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT;
......@@ -2572,7 +2577,7 @@ angle::Result QueryHelper::getUint64ResultNonBlocking(ContextVk *contextVk,
angle::Result QueryHelper::getUint64Result(ContextVk *contextVk, QueryResult *resultOut)
{
ASSERT(valid());
if (mUse.getSerial().valid())
if (hasSubmittedCommands())
{
VkDevice device = contextVk->getDevice();
constexpr VkQueryResultFlags kFlags = VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT;
......
......@@ -420,9 +420,17 @@ class QueryResult final
}
size_t getDataSize() const { return mIntsPerResult * sizeof(uint64_t); }
uint64_t getResult() const { return mResults[0]; }
uint64_t getResult(size_t index) const
{
ASSERT(index < mIntsPerResult);
return mResults[index];
}
uint64_t *getPointerToResults() { return mResults.data(); }
static constexpr size_t kDefaultResultIndex = 0;
static constexpr size_t kTransformFeedbackPrimitivesWrittenIndex = 0;
static constexpr size_t kPrimitivesGeneratedIndex = 1;
private:
uint32_t mIntsPerResult;
std::array<uint64_t, 2> mResults;
......@@ -464,6 +472,9 @@ class QueryHelper final : public Resource
// All other timestamp accesses should be made on outsideRenderPassCommandBuffer
void writeTimestamp(ContextVk *contextVk, CommandBuffer *outsideRenderPassCommandBuffer);
// Whether this query helper has generated and submitted any commands.
bool hasSubmittedCommands() const;
angle::Result getUint64ResultNonBlocking(ContextVk *contextVk,
QueryResult *resultOut,
bool *availableOut);
......
......@@ -607,6 +607,15 @@ class Shared final : angle::NonCopyable
}
}
void setUnreferenced(RefCounted<T> *refCounted)
{
ASSERT(!mRefCounted);
ASSERT(refCounted);
mRefCounted = refCounted;
mRefCounted->addRef();
}
void assign(VkDevice device, T &&newObject)
{
set(device, new RefCounted<T>(std::move(newObject)));
......@@ -614,6 +623,8 @@ class Shared final : angle::NonCopyable
void copy(VkDevice device, const Shared<T> &other) { set(device, other.mRefCounted); }
void copyUnreferenced(const Shared<T> &other) { setUnreferenced(other.mRefCounted); }
void reset(VkDevice device) { set(device, nullptr); }
template <typename RecyclerT>
......@@ -633,6 +644,23 @@ class Shared final : angle::NonCopyable
}
}
template <typename OnRelease>
void resetAndRelease(OnRelease *onRelease)
{
if (mRefCounted)
{
mRefCounted->releaseRef();
if (!mRefCounted->isReferenced())
{
ASSERT(mRefCounted->get().valid());
(*onRelease)(std::move(mRefCounted->get()));
SafeDelete(mRefCounted);
}
mRefCounted = nullptr;
}
}
bool isReferenced() const
{
// If reference is zero, the object should have been deleted. I.e. if the object is not
......
......@@ -185,7 +185,7 @@
// Geometry shader support:
3580 VULKAN : dEQP-GLES31.functional.shaders.linkage.es31.geometry.varying.types.float_struct = SKIP
5404 VULKAN : dEQP-GLES31.functional.geometry_shading.query.primitives_generated* = SKIP
5430 VULKAN : dEQP-GLES31.functional.geometry_shading.query.primitives_generated_* = FAIL
5407 VULKAN : dEQP-GLES31.functional.geometry_shading.layered.* = SKIP
5407 VULKAN : dEQP-GLES31.functional.geometry_shading.instanced.invocation_per_layer* = SKIP
5407 VULKAN : dEQP-GLES31.functional.geometry_shading.instanced.multiple_layers_per_invocation* = SKIP
......
......@@ -583,12 +583,9 @@ TEST_P(OcclusionQueriesTest, MultiContext)
ANGLE_SKIP_TEST_IF(IsWindows() && IsNVIDIA() && IsVulkan());
// Test skipped because the D3D backends cannot support simultaneous queries on multiple
// contexts yet. Same with the Vulkan backend.
// contexts yet.
ANGLE_SKIP_TEST_IF(GetParam() == ES2_D3D9() || GetParam() == ES2_D3D11() ||
GetParam() == ES3_D3D11() || GetParam() == ES2_VULKAN());
// http://anglebug.com/4092
ANGLE_SKIP_TEST_IF(IsVulkan());
GetParam() == ES3_D3D11());
// http://anglebug.com/5400
ANGLE_SKIP_TEST_IF(IsOSX() && IsMetal());
......
......@@ -9,6 +9,7 @@
#include "util/EGLWindow.h"
#include "util/gles_loader_autogen.h"
#include "util/random_utils.h"
#include "util/test_utils.h"
using namespace angle;
......@@ -2547,11 +2548,169 @@ TEST_P(TransformFeedbackWithDepthBufferTest, RecordAndDrawWithDepthWriteEnabled)
EXPECT_GL_NO_ERROR();
}
class TransformFeedbackTestES32 : public TransformFeedbackTest
{};
// Test that simultaneous use of transform feedback primitives written and primitives generated
// queries works.
TEST_P(TransformFeedbackTestES32, PrimitivesWrittenAndGenerated)
{
// TODO(anglebug.com/4533) This fails after the upgrade to the 26.20.100.7870 driver.
ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan());
// No ES3.2 support on out bots. http://anglebug.com/5435
ANGLE_SKIP_TEST_IF(IsPixel2() && IsVulkan());
// No VK_EXT_transform_feedback support on the following configuration.
// http://anglebug.com/5435
ANGLE_SKIP_TEST_IF(IsVulkan() && IsAMD() && IsWindows());
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Set the program's transform feedback varyings (just gl_Position)
std::vector<std::string> tfVaryings;
tfVaryings.push_back("gl_Position");
compileDefaultProgram(tfVaryings, GL_INTERLEAVED_ATTRIBS);
glUseProgram(mProgram);
GLint positionLocation = glGetAttribLocation(mProgram, essl1_shaders::PositionAttrib());
glEnable(GL_RASTERIZER_DISCARD);
const GLfloat vertices[] = {
-1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f, -1.0f, 1.0f, 0.5f,
1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f, -1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f,
1.0f, -1.0f, 0.5f, -1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f,
};
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionLocation);
// Bind the buffer for transform feedback output and start transform feedback
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
glBeginTransformFeedback(GL_POINTS);
EXPECT_GL_NO_ERROR();
// Create a number of queries. The test overview is as follows (PW = PrimitivesWritten, PG =
// Primitives Generated):
//
// PW0 begin
// - Draw 3
// PG0 begin
// - Draw 4
// PW0 end
// - Draw 5
// - Copy
// - Draw 6
// PW1 begin
// - Draw 7
// - Copy
// - Draw 8
// PG0 end
// PG1 begin
// - Draw 9
// - Copy
// PW1 end
// - Draw 10
// - Copy
// PG1 end
// PW2 begin
// PG2 begin
// - Draw 11
// - Copy
// - Draw 12
// PG2 end
// PW2 end
//
// This tests a variety of scenarios where either of PW or PG is active or not when the other
// begins or ends, as well as testing render pass restarts with the queries active and begin and
// end of queries outside or mid render pass.
constexpr size_t kQueryCount = 3;
GLQuery primitivesWrittenQueries[kQueryCount];
GLQuery primitivesGeneratedQueries[kQueryCount];
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
/* PG PW */
/* / */ glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQueries[0]);
/* | */ glDrawArrays(GL_POINTS, 0, 3);
/* / 0 */ glBeginQuery(GL_PRIMITIVES_GENERATED, primitivesGeneratedQueries[0]);
/* | | */ glDrawArrays(GL_POINTS, 0, 4);
/* | \ */ glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
/* | */ glDrawArrays(GL_POINTS, 0, 5);
/* | */ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
/* 0 */ glDrawArrays(GL_POINTS, 0, 6);
/* | / */ glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQueries[1]);
/* | | */ glDrawArrays(GL_POINTS, 0, 7);
/* | | */ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
/* | | */ glDrawArrays(GL_POINTS, 0, 8);
/* \ 1 */ glEndQuery(GL_PRIMITIVES_GENERATED);
/* / | */ glBeginQuery(GL_PRIMITIVES_GENERATED, primitivesGeneratedQueries[1]);
/* | | */ glDrawArrays(GL_POINTS, 0, 9);
/* | | */ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
/* 1 \ */ glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
/* | */ glDrawArrays(GL_POINTS, 0, 10);
/* | */ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
/* \ */ glEndQuery(GL_PRIMITIVES_GENERATED);
/* / */ glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQueries[2]);
/* / | */ glBeginQuery(GL_PRIMITIVES_GENERATED, primitivesGeneratedQueries[2]);
/* | | */ glDrawArrays(GL_POINTS, 0, 11);
/* 2 2 */ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
/* | | */ glDrawArrays(GL_POINTS, 0, 12);
/* \ | */ glEndQuery(GL_PRIMITIVES_GENERATED);
/* \ */ glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
glEndTransformFeedback();
EXPECT_GL_NO_ERROR();
// Check that the queries have correct results. Verify the first of each query with
// GL_QUERY_RESULT_AVAILABLE for no particular reason other than testing different paths.
GLuint readyPW = GL_FALSE;
GLuint readyPG = GL_FALSE;
while (readyPW == GL_FALSE || readyPG == GL_FALSE)
{
angle::Sleep(0);
glGetQueryObjectuiv(primitivesWrittenQueries[0], GL_QUERY_RESULT_AVAILABLE, &readyPW);
glGetQueryObjectuiv(primitivesGeneratedQueries[0], GL_QUERY_RESULT_AVAILABLE, &readyPG);
}
EXPECT_GL_NO_ERROR();
constexpr GLuint kPrimitivesWrittenExpected[kQueryCount] = {
3 + 4,
7 + 8 + 9,
11 + 12,
};
constexpr GLuint kPrimitivesGeneratedExpected[kQueryCount] = {
4 + 5 + 6 + 7 + 8,
9 + 10,
11 + 12,
};
for (size_t queryIndex = 0; queryIndex < kQueryCount; ++queryIndex)
{
GLuint primitivesWritten = 0;
glGetQueryObjectuiv(primitivesWrittenQueries[queryIndex], GL_QUERY_RESULT,
&primitivesWritten);
GLuint primitivesGenerated = 0;
glGetQueryObjectuiv(primitivesGeneratedQueries[queryIndex], GL_QUERY_RESULT,
&primitivesGenerated);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(primitivesWritten, kPrimitivesWrittenExpected[queryIndex]) << queryIndex;
EXPECT_EQ(primitivesGenerated, kPrimitivesGeneratedExpected[queryIndex]) << queryIndex;
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackTest);
ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackLifetimeTest);
ANGLE_INSTANTIATE_TEST_ES31(TransformFeedbackTestES31);
ANGLE_INSTANTIATE_TEST_ES32(TransformFeedbackTestES32);
ANGLE_INSTANTIATE_TEST(TransformFeedbackWithDepthBufferTest, ES3_METAL());
......
......@@ -148,7 +148,8 @@ struct CombinedPrintToStringParamName
ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES(), ES31_VULKAN(), ES31_VULKAN_SWIFTSHADER(), \
WithAsyncCommandQueueFeatureVulkan(ES31_VULKAN())
#define ANGLE_ALL_TEST_PLATFORMS_ES32 ES32_VULKAN(), ES32_VULKAN_SWIFTSHADER()
#define ANGLE_ALL_TEST_PLATFORMS_ES32 \
ES32_VULKAN(), WithAsyncCommandQueueFeatureVulkan(ES32_VULKAN())
#define ANGLE_ALL_TEST_PLATFORMS_NULL ES2_NULL(), ES3_NULL(), ES31_NULL()
......@@ -186,6 +187,17 @@ struct CombinedPrintToStringParamName
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
// Instantiate the test once for each GLES32 platform
#define ANGLE_INSTANTIATE_TEST_ES32(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES32}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
#define ANGLE_INSTANTIATE_TEST_ES32_AND(testName, ...) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES32, __VA_ARGS__}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
// Multiple ES Version macros
#define ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES2, \
......
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