| // |
| // 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 |