-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] GPU Tracer for GLES. #47080
Changes from 7 commits
eb8b915
c46d3eb
6034da3
c014eba
bf6f77b
3eb2edd
12f86c3
51ad782
cd0c46d
6a93525
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #include "impeller/renderer/backend/gles/gpu_tracer_gles.h" | ||
| #include <thread> | ||
| #include "fml/trace_event.h" | ||
|
|
||
| namespace impeller { | ||
|
|
||
| GPUTracerGLES::GPUTracerGLES(const ProcTableGLES& gl) { | ||
| #ifdef IMPELLER_DEBUG | ||
| auto desc = gl.GetDescription(); | ||
| enabled_ = desc->HasExtension("GL_EXT_disjoint_timer_query"); | ||
| #endif // IMPELLER_DEBUG | ||
| } | ||
|
|
||
| void GPUTracerGLES::MarkFrameStart(const ProcTableGLES& gl) { | ||
| if (!enabled_ || has_started_frame_ || | ||
| std::this_thread::get_id() != raster_thread_) { | ||
| return; | ||
| } | ||
|
|
||
| // At the beginning of a frame, check the status of all pending | ||
| // previous queries. | ||
| ProcessQueries(gl); | ||
|
|
||
| uint32_t query = 0; | ||
| gl.GenQueriesEXT(1, &query); | ||
| if (query == 0) { | ||
| return; | ||
| } | ||
|
|
||
| has_started_frame_ = true; | ||
|
|
||
| FML_DCHECK(!active_frame_.has_value()); | ||
| FML_DCHECK(query != 0); | ||
|
||
| active_frame_ = query; | ||
| gl.BeginQueryEXT(GL_TIME_ELAPSED_EXT, query); | ||
| } | ||
|
|
||
| void GPUTracerGLES::RecordRasterThread() { | ||
| raster_thread_ = std::this_thread::get_id(); | ||
| } | ||
|
|
||
| void GPUTracerGLES::ProcessQueries(const ProcTableGLES& gl) { | ||
| if (pending_traces_.empty()) { | ||
| return; | ||
| } | ||
|
|
||
| // For reasons unknown to me, querying the state of more than | ||
| // on query object per frame causes crashes on a Pixel 6 pro. | ||
|
||
| // It does not crash on an S10. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where exactly is the crash happening? If the tracer can only consume one pending query per |
||
| auto latest_query = pending_traces_.front(); | ||
|
||
|
|
||
| // First check if the query is complete without blocking | ||
| // on the result. Incomplete results are left in the pending | ||
| // trace vector and will not be checked again for another | ||
| // frame. | ||
| GLuint available = GL_FALSE; | ||
| gl.GetQueryObjectuivEXT(latest_query, GL_QUERY_RESULT_AVAILABLE_EXT, | ||
| &available); | ||
|
|
||
| if (available != GL_TRUE) { | ||
| return; | ||
| } | ||
| // Return the timer resolution in nanoseconds. | ||
| uint64_t duration = 0; | ||
| gl.GetQueryObjectui64vEXT(latest_query, GL_QUERY_RESULT_EXT, &duration); | ||
| auto gpu_ms = duration / 1000000.0; | ||
|
|
||
| FML_TRACE_COUNTER("flutter", "GPUTracer", | ||
| reinterpret_cast<int64_t>(this), // Trace Counter ID | ||
| "FrameTimeMS", gpu_ms); | ||
|
|
||
| gl.DeleteQueriesEXT(1, &latest_query); | ||
| pending_traces_.pop_front(); | ||
| } | ||
|
|
||
| void GPUTracerGLES::MarkFrameEnd(const ProcTableGLES& gl) { | ||
| if (!enabled_ || std::this_thread::get_id() != raster_thread_ || | ||
| !active_frame_.has_value() || !has_started_frame_) { | ||
| return; | ||
| } | ||
|
|
||
| auto query = active_frame_.value(); | ||
| gl.EndQueryEXT(GL_TIME_ELAPSED_EXT); | ||
|
|
||
| pending_traces_.push_back(query); | ||
| active_frame_ = std::nullopt; | ||
| has_started_frame_ = false; | ||
| } | ||
|
|
||
| } // namespace impeller | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <deque> | ||
| #include <thread> | ||
|
|
||
| #include "impeller/renderer/backend/gles/proc_table_gles.h" | ||
|
|
||
| namespace impeller { | ||
|
|
||
| class GPUTracerGLES { | ||
| public: | ||
| explicit GPUTracerGLES(const ProcTableGLES& gl); | ||
|
|
||
| ~GPUTracerGLES() = default; | ||
|
|
||
| /// @brief Record the thread id of the raster thread. | ||
| void RecordRasterThread(); | ||
|
|
||
| /// @brief Record the start of a frame workload, if one hasn't already been | ||
| /// started. | ||
| void MarkFrameStart(const ProcTableGLES& gl); | ||
|
|
||
| /// @brief Record the end of a frame workload. | ||
| void MarkFrameEnd(const ProcTableGLES& gl); | ||
|
|
||
| private: | ||
| void ProcessQueries(const ProcTableGLES& gl); | ||
|
|
||
| std::deque<uint32_t> pending_traces_; | ||
| std::optional<uint32_t> active_frame_ = std::nullopt; | ||
| std::thread::id raster_thread_; | ||
| bool has_started_frame_ = false; | ||
|
||
|
|
||
| bool enabled_ = false; | ||
| }; | ||
|
|
||
| } // namespace impeller | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #include "flutter/testing/testing.h" // IWYU pragma: keep | ||
| #include "gtest/gtest.h" | ||
| #include "impeller/renderer/backend/gles/gpu_tracer_gles.h" | ||
| #include "impeller/renderer/backend/gles/test/mock_gles.h" | ||
|
|
||
| namespace impeller { | ||
| namespace testing { | ||
|
|
||
| #ifdef IMPELLER_DEBUG | ||
| TEST(GPUTracerGLES, CanFormatFramebufferErrorMessage) { | ||
| auto const extensions = std::vector<const unsigned char*>{ | ||
| reinterpret_cast<const unsigned char*>("GL_KHR_debug"), // | ||
| reinterpret_cast<const unsigned char*>("GL_EXT_disjoint_timer_query"), // | ||
| }; | ||
| auto mock_gles = MockGLES::Init(extensions); | ||
| auto tracer = std::make_shared<GPUTracerGLES>(mock_gles->GetProcTable()); | ||
| tracer->RecordRasterThread(); | ||
| tracer->MarkFrameStart(mock_gles->GetProcTable()); | ||
| tracer->MarkFrameEnd(mock_gles->GetProcTable()); | ||
|
|
||
| auto calls = mock_gles->GetCapturedCalls(); | ||
|
|
||
| std::vector<std::string> expected = {"glGenQueriesEXT", "glBeginQueryEXT", | ||
| "glEndQueryEXT"}; | ||
| for (auto i = 0; i < 3; i++) { | ||
| EXPECT_EQ(calls[i], expected[i]); | ||
| } | ||
|
|
||
| // Begin second frame, which prompts the tracer to query the result | ||
| // from the previous frame. | ||
| tracer->MarkFrameStart(mock_gles->GetProcTable()); | ||
|
|
||
| calls = mock_gles->GetCapturedCalls(); | ||
| std::vector<std::string> expected_b = {"glGetQueryObjectuivEXT", | ||
| "glGetQueryObjectui64vEXT", | ||
| "glDeleteQueriesEXT"}; | ||
| for (auto i = 0; i < 3; i++) { | ||
| EXPECT_EQ(calls[i], expected_b[i]); | ||
| } | ||
| } | ||
|
|
||
| #endif // IMPELLER_DEBUG | ||
|
|
||
| } // namespace testing | ||
| } // namespace impeller |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This duplicates the
active_frame_.has_value()check aboveThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done