Files
filament/filament/backend/src/opengl/OpenGLTimerQuery.cpp
2023-03-08 12:33:11 -08:00

202 lines
6.9 KiB
C++

/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OpenGLTimerQuery.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <utils/debug.h>
namespace filament::backend {
using namespace backend;
using namespace GLUtils;
// ------------------------------------------------------------------------------------------------
OpenGLTimerQueryInterface::~OpenGLTimerQueryInterface() = default;
// ------------------------------------------------------------------------------------------------
#if defined(GL_VERSION_3_3) || defined(GL_EXT_disjoint_timer_query)
TimerQueryNative::TimerQueryNative(OpenGLContext&) {
}
TimerQueryNative::~TimerQueryNative() = default;
void TimerQueryNative::flush() {
}
void TimerQueryNative::beginTimeElapsedQuery(GLTimerQuery* query) {
glBeginQuery(GL_TIME_ELAPSED, query->gl.query);
CHECK_GL_ERROR(utils::slog.e)
}
void TimerQueryNative::endTimeElapsedQuery(GLTimerQuery*) {
glEndQuery(GL_TIME_ELAPSED);
CHECK_GL_ERROR(utils::slog.e)
}
bool TimerQueryNative::queryResultAvailable(GLTimerQuery* query) {
GLuint available = 0;
glGetQueryObjectuiv(query->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
CHECK_GL_ERROR(utils::slog.e)
return available != 0;
}
uint64_t TimerQueryNative::queryResult(GLTimerQuery* query) {
GLuint64 elapsedTime = 0;
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
glGetQueryObjectui64v(query->gl.query, GL_QUERY_RESULT, &elapsedTime);
CHECK_GL_ERROR(utils::slog.e)
return elapsedTime;
}
#endif
// ------------------------------------------------------------------------------------------------
OpenGLTimerQueryFence::OpenGLTimerQueryFence(OpenGLPlatform& platform)
: mPlatform(platform) {
mQueue.reserve(2);
mThread = std::thread([this]() {
auto& queue = mQueue;
bool exitRequested;
do {
std::unique_lock<utils::Mutex> lock(mLock);
mCondition.wait(lock, [this, &queue]() -> bool {
return mExitRequested || !queue.empty();
});
exitRequested = mExitRequested;
if (!queue.empty()) {
Job const job(queue.front());
queue.erase(queue.begin());
lock.unlock();
job();
}
} while (!exitRequested);
});
}
OpenGLTimerQueryFence::~OpenGLTimerQueryFence() {
if (mThread.joinable()) {
std::unique_lock<utils::Mutex> lock(mLock);
mExitRequested = true;
mCondition.notify_one();
lock.unlock();
mThread.join();
}
}
void OpenGLTimerQueryFence::enqueue(OpenGLTimerQueryFence::Job&& job) {
std::unique_lock<utils::Mutex> const lock(mLock);
mQueue.push_back(std::forward<Job>(job));
mCondition.notify_one();
}
void OpenGLTimerQueryFence::flush() {
// Use calls to flush() as a proxy for when the GPU work started.
GLTimerQuery* query = mActiveQuery;
if (query) {
uint64_t const elapsed = query->gl.emulation->elapsed.load(std::memory_order_relaxed);
if (!elapsed) {
uint64_t const now = clock::now().time_since_epoch().count();
query->gl.emulation->elapsed.store(now, std::memory_order_relaxed);
//SYSTRACE_CONTEXT();
//SYSTRACE_ASYNC_BEGIN("gpu", query->gl.query);
}
}
}
void OpenGLTimerQueryFence::beginTimeElapsedQuery(GLTimerQuery* query) {
assert_invariant(!mActiveQuery);
// We can't use a fence to figure out when a GPU operation starts (only when it finishes)
// so instead, we use when glFlush() was issued as a proxy.
if (UTILS_UNLIKELY(!query->gl.emulation)) {
query->gl.emulation = std::make_shared<GLTimerQuery::State>();
}
query->gl.emulation->elapsed.store(0, std::memory_order_relaxed);
query->gl.emulation->available.store(false);
mActiveQuery = query;
}
void OpenGLTimerQueryFence::endTimeElapsedQuery(GLTimerQuery* query) {
assert_invariant(mActiveQuery);
Platform::Fence* fence = mPlatform.createFence();
std::weak_ptr<GLTimerQuery::State> const weak = query->gl.emulation;
mActiveQuery = nullptr;
//uint32_t cookie = cookie = query->gl.query;
push([&platform = mPlatform, fence, weak]() {
auto emulation = weak.lock();
if (emulation) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
auto now = clock::now().time_since_epoch().count();
auto then = emulation->elapsed.load(std::memory_order_relaxed);
emulation->elapsed.store(now - then, std::memory_order_relaxed);
emulation->available.store(true);
//SYSTRACE_CONTEXT();
//SYSTRACE_ASYNC_END("gpu", cookie);
}
platform.destroyFence(fence);
});
}
bool OpenGLTimerQueryFence::queryResultAvailable(GLTimerQuery* query) {
return query->gl.emulation->available.load();
}
uint64_t OpenGLTimerQueryFence::queryResult(GLTimerQuery* query) {
return query->gl.emulation->elapsed;
}
// ------------------------------------------------------------------------------------------------
TimerQueryFallback::TimerQueryFallback() = default;
TimerQueryFallback::~TimerQueryFallback() = default;
void TimerQueryFallback::flush() {
}
void TimerQueryFallback::beginTimeElapsedQuery(OpenGLTimerQueryInterface::GLTimerQuery* query) {
if (!query->gl.emulation) {
query->gl.emulation = std::make_shared<GLTimerQuery::State>();
}
// this implementation clearly doesn't work at all, but we have no h/w support
query->gl.emulation->available.store(false, std::memory_order_relaxed);
query->gl.emulation->elapsed = clock::now().time_since_epoch().count();
}
void TimerQueryFallback::endTimeElapsedQuery(OpenGLTimerQueryInterface::GLTimerQuery* query) {
// this implementation clearly doesn't work at all, but we have no h/w support
query->gl.emulation->elapsed = clock::now().time_since_epoch().count() - query->gl.emulation->elapsed;
query->gl.emulation->available.store(true, std::memory_order_relaxed);
}
bool TimerQueryFallback::queryResultAvailable(OpenGLTimerQueryInterface::GLTimerQuery* query) {
return query->gl.emulation->available.load(std::memory_order_relaxed);
}
uint64_t TimerQueryFallback::queryResult(OpenGLTimerQueryInterface::GLTimerQuery* query) {
return query->gl.emulation->elapsed;
}
} // namespace filament::backend