diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 655f487bcd..7dd4e2f1c0 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -2741,11 +2741,13 @@ void OpenGLDriver::flush(int) { if (!gl.bugs.disable_glFlush) { glFlush(); } + mTimerQueryImpl->flush(); } void OpenGLDriver::finish(int) { DEBUG_MARKER() glFinish(); + mTimerQueryImpl->flush(); executeGpuCommandsCompleteOps(); executeFrameBeginsOps(); // since we executed a glFinish(), all pending tasks should be done diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index c5e8df65f9..242c6633a2 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -125,7 +125,7 @@ public: struct GLTimerQuery : public backend::HwTimerQuery { struct State { - uint64_t elapsed = 0; + std::atomic elapsed{}; std::atomic_bool available{}; }; struct { diff --git a/filament/backend/src/opengl/TimerQuery.cpp b/filament/backend/src/opengl/TimerQuery.cpp index 59d447b12e..4e42b04494 100644 --- a/filament/backend/src/opengl/TimerQuery.cpp +++ b/filament/backend/src/opengl/TimerQuery.cpp @@ -39,6 +39,9 @@ TimerQueryNative::TimerQueryNative(OpenGLContext& context) TimerQueryNative::~TimerQueryNative() = default; +void TimerQueryNative::flush() { +} + void TimerQueryNative::beginTimeElapsedQuery(GLTimerQuery* query) { gl.beginQuery(GL_TIME_ELAPSED, query->gl.query); CHECK_GL_ERROR(utils::slog.e) @@ -107,32 +110,44 @@ void TimerQueryFence::enqueue(TimerQueryFence::Job&& job) { mCondition.notify_one(); } +void TimerQueryFence::flush() { + // Use calls to flush() as a proxy for when the GPU work started. + GLTimerQuery* query = mActiveQuery; + if (query) { + auto 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 TimerQueryFence::beginTimeElapsedQuery(GLTimerQuery* query) { - Platform::Fence* fence = mPlatform.createFence(); - if (!query->gl.emulation) { + assert(!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(); } query->gl.emulation->available.store(false); - std::weak_ptr weak = query->gl.emulation; - push([&platform = mPlatform, fence, weak]() { - auto emulation = weak.lock(); - if (emulation) { - platform.waitFence(fence, FENCE_WAIT_FOR_EVER); - emulation->elapsed = clock::now().time_since_epoch().count(); - } - platform.destroyFence(fence); - }); + mActiveQuery = query; } void TimerQueryFence::endTimeElapsedQuery(GLTimerQuery* query) { + assert(mActiveQuery); Platform::Fence* fence = mPlatform.createFence(); std::weak_ptr 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); - emulation->elapsed = clock::now().time_since_epoch().count() - emulation->elapsed; + 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); }); @@ -152,6 +167,9 @@ TimerQueryFallback::TimerQueryFallback() = default; TimerQueryFallback::~TimerQueryFallback() = default; +void TimerQueryFallback::flush() { +} + void TimerQueryFallback::beginTimeElapsedQuery(TimerQueryInterface::GLTimerQuery* query) { if (!query->gl.emulation) { query->gl.emulation = std::make_shared(); diff --git a/filament/backend/src/opengl/TimerQuery.h b/filament/backend/src/opengl/TimerQuery.h index 45a9a87d43..512eee2c83 100644 --- a/filament/backend/src/opengl/TimerQuery.h +++ b/filament/backend/src/opengl/TimerQuery.h @@ -41,6 +41,7 @@ protected: public: virtual ~TimerQueryInterface(); + virtual void flush() = 0; virtual void beginTimeElapsedQuery(GLTimerQuery* query) = 0; virtual void endTimeElapsedQuery(GLTimerQuery* query) = 0; virtual bool queryResultAvailable(GLTimerQuery* query) = 0; @@ -52,6 +53,7 @@ public: explicit TimerQueryNative(OpenGLContext& context); ~TimerQueryNative() override; private: + void flush() override; void beginTimeElapsedQuery(GLTimerQuery* query) override; void endTimeElapsedQuery(GLTimerQuery* query) override; bool queryResultAvailable(GLTimerQuery* query) override; @@ -65,6 +67,7 @@ public: ~TimerQueryFence() override; private: using Job = std::function; + void flush() override; void beginTimeElapsedQuery(GLTimerQuery* query) override; void endTimeElapsedQuery(GLTimerQuery* query) override; bool queryResultAvailable(GLTimerQuery* query) override; @@ -82,6 +85,7 @@ private: mutable utils::Condition mCondition; std::vector mQueue; bool mExitRequested = false; + GLTimerQuery* mActiveQuery = nullptr; }; class TimerQueryFallback : public TimerQueryInterface { @@ -89,6 +93,7 @@ public: explicit TimerQueryFallback(); ~TimerQueryFallback() override; private: + void flush() override; void beginTimeElapsedQuery(GLTimerQuery* query) override; void endTimeElapsedQuery(GLTimerQuery* query) override; bool queryResultAvailable(GLTimerQuery* query) override; diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index d75964d7c8..60d8e58379 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -247,7 +247,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // TODO: use the framegraph for the shadow passes RenderPass shadowMapPass = pass; view.renderShadowMaps(engine, driver, shadowMapPass); - driver.flush(); // Kick the GPU since we're done with this render target engine.flush(); // Wake-up the driver thread }