| // |
| // 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/renderer/gl/FunctionsGL.h" |
| #include "libANGLE/renderer/gl/StateManagerGL.h" |
| #include "libANGLE/renderer/gl/renderergl_utils.h" |
| |
| namespace |
| { |
| |
| GLuint64 MergeQueryResults(GLenum type, GLuint64 currentResult, GLuint64 newResult) |
| { |
| switch (type) |
| { |
| case GL_ANY_SAMPLES_PASSED: |
| case GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
| return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE; |
| |
| case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
| return currentResult + newResult; |
| |
| case GL_TIME_ELAPSED: |
| return currentResult + newResult; |
| |
| case GL_TIMESTAMP: |
| return newResult; |
| |
| default: |
| UNREACHABLE(); |
| return 0; |
| } |
| } |
| |
| } // anonymous namespace |
| |
| namespace rx |
| { |
| |
| QueryGL::QueryGL(GLenum type) : QueryImpl(type) |
| { |
| } |
| |
| QueryGL::~QueryGL() |
| { |
| } |
| |
| StandardQueryGL::StandardQueryGL(GLenum type, |
| const FunctionsGL *functions, |
| StateManagerGL *stateManager) |
| : QueryGL(type), |
| mType(type), |
| mFunctions(functions), |
| mStateManager(stateManager), |
| mActiveQuery(0), |
| mPendingQueries(), |
| mResultSum(0) |
| { |
| } |
| |
| StandardQueryGL::~StandardQueryGL() |
| { |
| mStateManager->deleteQuery(mActiveQuery); |
| mStateManager->onDeleteQueryObject(this); |
| while (!mPendingQueries.empty()) |
| { |
| mStateManager->deleteQuery(mPendingQueries.front()); |
| mPendingQueries.pop_front(); |
| } |
| } |
| |
| gl::Error StandardQueryGL::begin() |
| { |
| mResultSum = 0; |
| mStateManager->onBeginQuery(this); |
| return resume(); |
| } |
| |
| gl::Error StandardQueryGL::end() |
| { |
| return pause(); |
| } |
| |
| gl::Error StandardQueryGL::queryCounter() |
| { |
| ASSERT(mType == GL_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 gl::NoError(); |
| } |
| |
| template <typename T> |
| gl::Error StandardQueryGL::getResultBase(T *params) |
| { |
| ASSERT(mActiveQuery == 0); |
| |
| gl::Error error = flush(true); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ASSERT(mPendingQueries.empty()); |
| *params = static_cast<T>(mResultSum); |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error StandardQueryGL::getResult(GLint *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error StandardQueryGL::getResult(GLuint *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error StandardQueryGL::getResult(GLint64 *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error StandardQueryGL::getResult(GLuint64 *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error StandardQueryGL::isResultAvailable(bool *available) |
| { |
| ASSERT(mActiveQuery == 0); |
| |
| gl::Error error = flush(false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| *available = mPendingQueries.empty(); |
| return gl::NoError(); |
| } |
| |
| gl::Error StandardQueryGL::pause() |
| { |
| if (mActiveQuery != 0) |
| { |
| mStateManager->endQuery(mType, mActiveQuery); |
| |
| mPendingQueries.push_back(mActiveQuery); |
| mActiveQuery = 0; |
| } |
| |
| // Flush to make sure the pending queries don't add up too much. |
| gl::Error error = flush(false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error StandardQueryGL::resume() |
| { |
| if (mActiveQuery == 0) |
| { |
| // Flush to make sure the pending queries don't add up too much. |
| gl::Error error = flush(false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| mFunctions->genQueries(1, &mActiveQuery); |
| mStateManager->beginQuery(mType, mActiveQuery); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error StandardQueryGL::flush(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 gl::NoError(); |
| } |
| } |
| |
| // 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)); |
| } |
| |
| mStateManager->deleteQuery(id); |
| |
| mPendingQueries.pop_front(); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| class SyncProviderGL |
| { |
| public: |
| virtual ~SyncProviderGL() {} |
| virtual gl::Error flush(bool force, bool *finished) = 0; |
| }; |
| |
| class SyncProviderGLSync : public SyncProviderGL |
| { |
| public: |
| SyncProviderGLSync(const FunctionsGL *functions) : mFunctions(functions), mSync(nullptr) |
| { |
| mSync = mFunctions->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
| } |
| |
| virtual ~SyncProviderGLSync() { mFunctions->deleteSync(mSync); } |
| |
| gl::Error flush(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 gl::NoError(); |
| } |
| |
| private: |
| const FunctionsGL *mFunctions; |
| GLsync mSync; |
| }; |
| |
| class SyncProviderGLQuery : public SyncProviderGL |
| { |
| public: |
| SyncProviderGLQuery(const FunctionsGL *functions, |
| StateManagerGL *stateManager, |
| GLenum queryType) |
| : mFunctions(functions), mQuery(0) |
| { |
| mFunctions->genQueries(1, &mQuery); |
| stateManager->pauseQuery(queryType); |
| mFunctions->beginQuery(queryType, mQuery); |
| mFunctions->endQuery(queryType); |
| stateManager->resumeQuery(queryType); |
| } |
| |
| virtual ~SyncProviderGLQuery() { mFunctions->deleteQueries(1, &mQuery); } |
| |
| gl::Error flush(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 gl::NoError(); |
| } |
| |
| private: |
| const FunctionsGL *mFunctions; |
| GLuint mQuery; |
| }; |
| |
| SyncQueryGL::SyncQueryGL(GLenum type, const FunctionsGL *functions, StateManagerGL *stateManager) |
| : QueryGL(type), |
| mFunctions(functions), |
| mStateManager(stateManager), |
| mSyncProvider(nullptr), |
| mFinished(false) |
| { |
| ASSERT(IsSupported(mFunctions)); |
| ASSERT(type == GL_COMMANDS_COMPLETED_CHROMIUM); |
| } |
| |
| SyncQueryGL::~SyncQueryGL() |
| { |
| } |
| |
| bool SyncQueryGL::IsSupported(const FunctionsGL *functions) |
| { |
| return nativegl::SupportsFenceSync(functions) || nativegl::SupportsOcclusionQueries(functions); |
| } |
| |
| gl::Error SyncQueryGL::begin() |
| { |
| return gl::NoError(); |
| } |
| |
| gl::Error SyncQueryGL::end() |
| { |
| if (nativegl::SupportsFenceSync(mFunctions)) |
| { |
| mSyncProvider.reset(new SyncProviderGLSync(mFunctions)); |
| } |
| else if (nativegl::SupportsOcclusionQueries(mFunctions)) |
| { |
| mSyncProvider.reset( |
| new SyncProviderGLQuery(mFunctions, mStateManager, GL_ANY_SAMPLES_PASSED)); |
| } |
| else |
| { |
| ASSERT(false); |
| return gl::Error(GL_INVALID_OPERATION, "No native support for sync queries."); |
| } |
| return gl::NoError(); |
| } |
| |
| gl::Error SyncQueryGL::queryCounter() |
| { |
| UNREACHABLE(); |
| return gl::NoError(); |
| } |
| |
| gl::Error SyncQueryGL::getResult(GLint *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error SyncQueryGL::getResult(GLuint *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error SyncQueryGL::getResult(GLint64 *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error SyncQueryGL::getResult(GLuint64 *params) |
| { |
| return getResultBase(params); |
| } |
| |
| gl::Error SyncQueryGL::isResultAvailable(bool *available) |
| { |
| ANGLE_TRY(flush(false)); |
| *available = mFinished; |
| return gl::NoError(); |
| } |
| |
| gl::Error SyncQueryGL::pause() |
| { |
| return gl::NoError(); |
| } |
| |
| gl::Error SyncQueryGL::resume() |
| { |
| return gl::NoError(); |
| } |
| |
| gl::Error SyncQueryGL::flush(bool force) |
| { |
| if (mSyncProvider == nullptr) |
| { |
| ASSERT(mFinished); |
| return gl::NoError(); |
| } |
| |
| ANGLE_TRY(mSyncProvider->flush(force, &mFinished)); |
| if (mFinished) |
| { |
| mSyncProvider.reset(); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| template <typename T> |
| gl::Error SyncQueryGL::getResultBase(T *params) |
| { |
| ANGLE_TRY(flush(true)); |
| *params = static_cast<T>(mFinished ? GL_TRUE : GL_FALSE); |
| return gl::NoError(); |
| } |
| } |