blob: 376fb4af512babf38ebc508ff015977bb60fe8a4 [file] [log] [blame]
//
// Copyright 2015 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.
//
// QueryGL.cpp: Implements the class methods for QueryGL.
#include "libANGLE/renderer/gl/QueryGL.h"
#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/gl/ContextGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/StateManagerGL.h"
#include "libANGLE/renderer/gl/renderergl_utils.h"
namespace
{
GLuint64 MergeQueryResults(gl::QueryType type, GLuint64 currentResult, GLuint64 newResult)
{
switch (type)
{
case gl::QueryType::AnySamples:
case gl::QueryType::AnySamplesConservative:
return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
case gl::QueryType::TransformFeedbackPrimitivesWritten:
return currentResult + newResult;
case gl::QueryType::TimeElapsed:
return currentResult + newResult;
case gl::QueryType::Timestamp:
return newResult;
case gl::QueryType::PrimitivesGenerated:
return currentResult + newResult;
default:
UNREACHABLE();
return 0;
}
}
// Some drivers tend to hang when flushing pending queries. Wait until this number of queries have
// added up before checking if results are ready.
constexpr uint32_t kPauseResumeFlushThreshold = 5;
} // anonymous namespace
namespace rx
{
QueryGL::QueryGL(gl::QueryType type) : QueryImpl(type) {}
QueryGL::~QueryGL() {}
StandardQueryGL::StandardQueryGL(gl::QueryType type,
const FunctionsGL *functions,
StateManagerGL *stateManager)
: QueryGL(type),
mFunctions(functions),
mStateManager(stateManager),
mActiveQuery(0),
mPendingQueries(),
mResultSum(0)
{}
StandardQueryGL::~StandardQueryGL()
{
if (mActiveQuery != 0)
{
mStateManager->endQuery(mType, this, mActiveQuery);
mFunctions->deleteQueries(1, &mActiveQuery);
mActiveQuery = 0;
}
while (!mPendingQueries.empty())
{
GLuint id = mPendingQueries.front();
mFunctions->deleteQueries(1, &id);
mPendingQueries.pop_front();
}
}
angle::Result StandardQueryGL::begin(const gl::Context *context)
{
mResultSum = 0;
return resume(context);
}
angle::Result StandardQueryGL::end(const gl::Context *context)
{
return pause(context);
}
angle::Result StandardQueryGL::queryCounter(const gl::Context *context)
{
ASSERT(mType == gl::QueryType::Timestamp);
// Directly create a query for the timestamp and add it to the pending query queue, as timestamp
// queries do not have the traditional begin/end block and never need to be paused/resumed
GLuint query;
mFunctions->genQueries(1, &query);
mFunctions->queryCounter(query, GL_TIMESTAMP);
mPendingQueries.push_back(query);
return angle::Result::Continue;
}
template <typename T>
angle::Result StandardQueryGL::getResultBase(const gl::Context *context, T *params)
{
ASSERT(mActiveQuery == 0);
ANGLE_TRY(flush(context, true));
ASSERT(mPendingQueries.empty());
*params = static_cast<T>(mResultSum);
return angle::Result::Continue;
}
angle::Result StandardQueryGL::getResult(const gl::Context *context, GLint *params)
{
return getResultBase(context, params);
}
angle::Result StandardQueryGL::getResult(const gl::Context *context, GLuint *params)
{
return getResultBase(context, params);
}
angle::Result StandardQueryGL::getResult(const gl::Context *context, GLint64 *params)
{
return getResultBase(context, params);
}
angle::Result StandardQueryGL::getResult(const gl::Context *context, GLuint64 *params)
{
return getResultBase(context, params);
}
angle::Result StandardQueryGL::isResultAvailable(const gl::Context *context, bool *available)
{
ASSERT(mActiveQuery == 0);
ANGLE_TRY(flush(context, false));
*available = mPendingQueries.empty();
return angle::Result::Continue;
}
angle::Result StandardQueryGL::pause(const gl::Context *context)
{
if (mActiveQuery != 0)
{
mStateManager->endQuery(mType, this, mActiveQuery);
mPendingQueries.push_back(mActiveQuery);
mActiveQuery = 0;
}
// Flush to make sure the pending queries don't add up too much.
if (mPendingQueries.size() >= kPauseResumeFlushThreshold)
{
ANGLE_TRY(flush(context, false));
}
return angle::Result::Continue;
}
angle::Result StandardQueryGL::resume(const gl::Context *context)
{
if (mActiveQuery == 0)
{
// Flush to make sure the pending queries don't add up too much.
if (mPendingQueries.size() >= kPauseResumeFlushThreshold)
{
ANGLE_TRY(flush(context, false));
}
mFunctions->genQueries(1, &mActiveQuery);
mStateManager->beginQuery(mType, this, mActiveQuery);
ContextGL *contextGL = GetImplAs<ContextGL>(context);
contextGL->markWorkSubmitted();
}
return angle::Result::Continue;
}
angle::Result StandardQueryGL::flush(const gl::Context *context, bool force)
{
while (!mPendingQueries.empty())
{
GLuint id = mPendingQueries.front();
if (!force)
{
GLuint resultAvailable = 0;
mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT_AVAILABLE, &resultAvailable);
if (resultAvailable == GL_FALSE)
{
return angle::Result::Continue;
}
}
// Even though getQueryObjectui64v was introduced for timer queries, there is nothing in the
// standard that says that it doesn't work for any other queries. It also passes on all the
// trybots, so we use it if it is available
if (mFunctions->getQueryObjectui64v != nullptr)
{
GLuint64 result = 0;
mFunctions->getQueryObjectui64v(id, GL_QUERY_RESULT, &result);
mResultSum = MergeQueryResults(mType, mResultSum, result);
}
else
{
GLuint result = 0;
mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT, &result);
mResultSum = MergeQueryResults(mType, mResultSum, static_cast<GLuint64>(result));
}
mFunctions->deleteQueries(1, &id);
mPendingQueries.pop_front();
}
return angle::Result::Continue;
}
class SyncProviderGL
{
public:
virtual ~SyncProviderGL() {}
virtual angle::Result init(const gl::Context *context, gl::QueryType queryType)
{
return angle::Result::Continue;
}
virtual angle::Result flush(const gl::Context *context, bool force, bool *finished) = 0;
};
class SyncProviderGLSync : public SyncProviderGL
{
public:
SyncProviderGLSync(const FunctionsGL *functions) : mFunctions(functions), mSync(nullptr) {}
~SyncProviderGLSync() override { mFunctions->deleteSync(mSync); }
angle::Result init(const gl::Context *context, gl::QueryType type) override
{
ContextGL *contextGL = GetImplAs<ContextGL>(context);
mSync = mFunctions->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
ANGLE_CHECK(contextGL, mSync != 0, "glFenceSync failed to create a GLsync object.",
GL_OUT_OF_MEMORY);
contextGL->markWorkSubmitted();
return angle::Result::Continue;
}
angle::Result flush(const gl::Context *context, bool force, bool *finished) override
{
if (force)
{
mFunctions->clientWaitSync(mSync, 0, 0);
*finished = true;
}
else
{
GLint value = 0;
mFunctions->getSynciv(mSync, GL_SYNC_STATUS, 1, nullptr, &value);
*finished = (value == GL_SIGNALED);
}
return angle::Result::Continue;
}
private:
const FunctionsGL *mFunctions;
GLsync mSync;
};
class SyncProviderGLQuery : public SyncProviderGL
{
public:
SyncProviderGLQuery(const FunctionsGL *functions) : mFunctions(functions), mQuery(0) {}
angle::Result init(const gl::Context *context, gl::QueryType type) override
{
StateManagerGL *stateManager = GetStateManagerGL(context);
mFunctions->genQueries(1, &mQuery);
ANGLE_TRY(stateManager->pauseQuery(context, type));
mFunctions->beginQuery(ToGLenum(type), mQuery);
mFunctions->endQuery(ToGLenum(type));
return stateManager->resumeQuery(context, type);
}
~SyncProviderGLQuery() override { mFunctions->deleteQueries(1, &mQuery); }
angle::Result flush(const gl::Context *context, bool force, bool *finished) override
{
if (force)
{
GLint result = 0;
mFunctions->getQueryObjectiv(mQuery, GL_QUERY_RESULT, &result);
*finished = true;
}
else
{
GLint available = 0;
mFunctions->getQueryObjectiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &available);
*finished = (available == GL_TRUE);
}
return angle::Result::Continue;
}
private:
const FunctionsGL *mFunctions;
GLuint mQuery;
};
SyncQueryGL::SyncQueryGL(gl::QueryType type, const FunctionsGL *functions)
: QueryGL(type), mFunctions(functions), mSyncProvider(nullptr), mFinished(false)
{
ASSERT(IsSupported(mFunctions));
ASSERT(type == gl::QueryType::CommandsCompleted);
}
SyncQueryGL::~SyncQueryGL() {}
bool SyncQueryGL::IsSupported(const FunctionsGL *functions)
{
return nativegl::SupportsFenceSync(functions) || nativegl::SupportsOcclusionQueries(functions);
}
angle::Result SyncQueryGL::begin(const gl::Context *context)
{
return angle::Result::Continue;
}
angle::Result SyncQueryGL::end(const gl::Context *context)
{
if (nativegl::SupportsFenceSync(mFunctions))
{
mSyncProvider.reset(new SyncProviderGLSync(mFunctions));
}
else if (nativegl::SupportsOcclusionQueries(mFunctions))
{
mSyncProvider.reset(new SyncProviderGLQuery(mFunctions));
}
else
{
ANGLE_GL_UNREACHABLE(GetImplAs<ContextGL>(context));
}
ANGLE_TRY(mSyncProvider->init(context, gl::QueryType::AnySamples));
return angle::Result::Continue;
}
angle::Result SyncQueryGL::queryCounter(const gl::Context *context)
{
UNREACHABLE();
return angle::Result::Continue;
}
angle::Result SyncQueryGL::getResult(const gl::Context *context, GLint *params)
{
return getResultBase(context, params);
}
angle::Result SyncQueryGL::getResult(const gl::Context *context, GLuint *params)
{
return getResultBase(context, params);
}
angle::Result SyncQueryGL::getResult(const gl::Context *context, GLint64 *params)
{
return getResultBase(context, params);
}
angle::Result SyncQueryGL::getResult(const gl::Context *context, GLuint64 *params)
{
return getResultBase(context, params);
}
angle::Result SyncQueryGL::isResultAvailable(const gl::Context *context, bool *available)
{
ANGLE_TRY(flush(context, false));
*available = mFinished;
return angle::Result::Continue;
}
angle::Result SyncQueryGL::pause(const gl::Context *context)
{
return angle::Result::Continue;
}
angle::Result SyncQueryGL::resume(const gl::Context *context)
{
return angle::Result::Continue;
}
angle::Result SyncQueryGL::flush(const gl::Context *context, bool force)
{
if (mSyncProvider == nullptr)
{
ASSERT(mFinished);
return angle::Result::Continue;
}
ANGLE_TRY(mSyncProvider->flush(context, force, &mFinished));
if (mFinished)
{
mSyncProvider.reset();
}
return angle::Result::Continue;
}
template <typename T>
angle::Result SyncQueryGL::getResultBase(const gl::Context *context, T *params)
{
ANGLE_TRY(flush(context, true));
*params = static_cast<T>(mFinished ? GL_TRUE : GL_FALSE);
return angle::Result::Continue;
}
} // namespace rx