blob: c2e47a85270e24a5fa545a8d0b505e698f316515 [file] [log] [blame]
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// QueryVk.cpp:
// Implements the class methods for QueryVk.
//
#include "libANGLE/renderer/vulkan/QueryVk.h"
#include "libANGLE/Context.h"
#include "libANGLE/TransformFeedback.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
#include "common/debug.h"
namespace rx
{
QueryVk::QueryVk(gl::QueryType type)
: QueryImpl(type),
mTransformFeedbackPrimitivesDrawn(0),
mCachedResult(0),
mCachedResultValid(false)
{}
QueryVk::~QueryVk() = default;
void QueryVk::onDestroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
if (getType() != gl::QueryType::TransformFeedbackPrimitivesWritten)
{
vk::DynamicQueryPool *queryPool = contextVk->getQueryPool(getType());
queryPool->freeQuery(contextVk, &mQueryHelper);
queryPool->freeQuery(contextVk, &mQueryHelperTimeElapsedBegin);
}
}
angle::Result QueryVk::begin(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
mCachedResultValid = false;
// Transform feedback query is a handled by a CPU-calculated value when emulated.
if (getType() == gl::QueryType::TransformFeedbackPrimitivesWritten)
{
mTransformFeedbackPrimitivesDrawn = 0;
// We could consider using VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.
return angle::Result::Continue;
}
if (!mQueryHelper.valid())
{
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.valid())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(
contextVk, &mQueryHelperTimeElapsedBegin));
}
ANGLE_TRY(mQueryHelperTimeElapsedBegin.flushAndWriteTimestamp(contextVk));
}
else
{
ANGLE_TRY(mQueryHelper.beginQuery(contextVk));
}
return angle::Result::Continue;
}
angle::Result QueryVk::end(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
if (getType() == gl::QueryType::TransformFeedbackPrimitivesWritten)
{
mCachedResult = mTransformFeedbackPrimitivesDrawn;
// There could be transform feedback in progress, so add the primitives drawn so far from
// the current transform feedback object.
gl::TransformFeedback *transformFeedback =
context->getState().getCurrentTransformFeedback();
if (transformFeedback)
{
mCachedResult += transformFeedback->getPrimitivesDrawn();
}
mCachedResultValid = true;
// We could consider using VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.
}
else if (getType() == gl::QueryType::TimeElapsed)
{
ANGLE_TRY(mQueryHelper.flushAndWriteTimestamp(contextVk));
}
else
{
ANGLE_TRY(mQueryHelper.endQuery(contextVk));
}
return angle::Result::Continue;
}
angle::Result QueryVk::queryCounter(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
mCachedResultValid = false;
if (!mQueryHelper.valid())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
ASSERT(getType() == gl::QueryType::Timestamp);
return mQueryHelper.flushAndWriteTimestamp(contextVk);
}
angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
{
if (mCachedResultValid)
{
return angle::Result::Continue;
}
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
// 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(contextVk))
{
ANGLE_TRY(contextVk->flushImpl(nullptr));
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(contextVk));
ASSERT(!mQueryHelper.hasPendingWork(contextVk));
}
// 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(contextVk->checkCompletedCommands());
if (contextVk->isSerialInUse(mQueryHelper.getStoredQueueSerial()))
{
if (!wait)
{
return angle::Result::Continue;
}
ANGLE_TRY(contextVk->finishToSerial(mQueryHelper.getStoredQueueSerial()));
}
if (wait)
{
ANGLE_TRY(mQueryHelper.getUint64Result(contextVk, &mCachedResult));
}
else
{
bool available = false;
ANGLE_TRY(mQueryHelper.getUint64ResultNonBlocking(contextVk, &mCachedResult, &available));
if (!available)
{
// If the results are not ready, do nothing. mCachedResultValid remains false.
return angle::Result::Continue;
}
}
double timestampPeriod = renderer->getPhysicalDeviceProperties().limits.timestampPeriod;
// 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;
case gl::QueryType::Timestamp:
mCachedResult = static_cast<uint64_t>(mCachedResult * timestampPeriod);
break;
case gl::QueryType::TimeElapsed:
{
uint64_t timeElapsedEnd = mCachedResult;
// Since the result of the end query of time-elapsed is already available, the
// result of begin query must be available too.
ANGLE_TRY(mQueryHelperTimeElapsedBegin.getUint64Result(contextVk, &mCachedResult));
mCachedResult = timeElapsedEnd - mCachedResult;
mCachedResult = static_cast<uint64_t>(mCachedResult * timestampPeriod);
break;
}
default:
UNREACHABLE();
break;
}
mCachedResultValid = true;
return angle::Result::Continue;
}
angle::Result QueryVk::getResult(const gl::Context *context, GLint *params)
{
ANGLE_TRY(getResult(context, true));
*params = static_cast<GLint>(mCachedResult);
return angle::Result::Continue;
}
angle::Result QueryVk::getResult(const gl::Context *context, GLuint *params)
{
ANGLE_TRY(getResult(context, true));
*params = static_cast<GLuint>(mCachedResult);
return angle::Result::Continue;
}
angle::Result QueryVk::getResult(const gl::Context *context, GLint64 *params)
{
ANGLE_TRY(getResult(context, true));
*params = static_cast<GLint64>(mCachedResult);
return angle::Result::Continue;
}
angle::Result QueryVk::getResult(const gl::Context *context, GLuint64 *params)
{
ANGLE_TRY(getResult(context, true));
*params = mCachedResult;
return angle::Result::Continue;
}
angle::Result QueryVk::isResultAvailable(const gl::Context *context, bool *available)
{
ANGLE_TRY(getResult(context, false));
*available = mCachedResultValid;
return angle::Result::Continue;
}
void QueryVk::onTransformFeedbackEnd(const gl::Context *context)
{
gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
ASSERT(transformFeedback);
mTransformFeedbackPrimitivesDrawn += transformFeedback->getPrimitivesDrawn();
}
} // namespace rx