Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ed4662f

Browse files
Jonah Williamsharryterkelsen
authored andcommitted
[Impeller] GPU Tracer for GLES. (#47080)
Trace GPU execution time on GLES using GL_EXT_disjoint_timer_query. This requires a per-app opt in from the Android Manifest with the key `"io.flutter.embedding.android.EnableOpenGLGPUTracing` set to true.
1 parent 15a05a3 commit ed4662f

23 files changed

+364
-49
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,6 +2086,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h + .
20862086
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc + ../../../flutter/LICENSE
20872087
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h + ../../../flutter/LICENSE
20882088
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gles.h + ../../../flutter/LICENSE
2089+
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.cc + ../../../flutter/LICENSE
2090+
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.h + ../../../flutter/LICENSE
20892091
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/handle_gles.cc + ../../../flutter/LICENSE
20902092
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/handle_gles.h + ../../../flutter/LICENSE
20912093
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc + ../../../flutter/LICENSE
@@ -4856,6 +4858,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h
48564858
FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc
48574859
FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h
48584860
FILE: ../../../flutter/impeller/renderer/backend/gles/gles.h
4861+
FILE: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.cc
4862+
FILE: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.h
48594863
FILE: ../../../flutter/impeller/renderer/backend/gles/handle_gles.cc
48604864
FILE: ../../../flutter/impeller/renderer/backend/gles/handle_gles.h
48614865
FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc

common/settings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ struct Settings {
228228
// must be available to the application.
229229
bool enable_vulkan_validation = false;
230230

231+
// Enable GPU tracing in GLES backends.
232+
// Some devices claim to support the required APIs but crash on their usage.
233+
bool enable_opengl_gpu_tracing = false;
234+
231235
// Data set by platform-specific embedders for use in font initialization.
232236
uint32_t font_initialization_data = 0;
233237

impeller/playground/backend/gles/playground_impl_gles.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
112112
return nullptr;
113113
}
114114

115-
auto context =
116-
ContextGLES::Create(std::move(gl), ShaderLibraryMappingsForPlayground());
115+
auto context = ContextGLES::Create(
116+
std::move(gl), ShaderLibraryMappingsForPlayground(), true);
117117
if (!context) {
118118
FML_LOG(ERROR) << "Could not create context.";
119119
return nullptr;

impeller/renderer/backend/gles/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ impeller_component("gles_unittests") {
1616
sources = [
1717
"test/capabilities_unittests.cc",
1818
"test/formats_gles_unittests.cc",
19+
"test/gpu_tracer_gles_unittests.cc",
1920
"test/mock_gles.cc",
2021
"test/mock_gles.h",
2122
"test/mock_gles_unittests.cc",
@@ -51,6 +52,8 @@ impeller_component("gles") {
5152
"formats_gles.cc",
5253
"formats_gles.h",
5354
"gles.h",
55+
"gpu_tracer_gles.cc",
56+
"gpu_tracer_gles.h",
5457
"handle_gles.cc",
5558
"handle_gles.h",
5659
"pipeline_gles.cc",

impeller/renderer/backend/gles/context_gles.cc

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,27 @@
33
// found in the LICENSE file.
44

55
#include "impeller/renderer/backend/gles/context_gles.h"
6+
#include <memory>
67

78
#include "impeller/base/config.h"
89
#include "impeller/base/validation.h"
910
#include "impeller/renderer/backend/gles/command_buffer_gles.h"
11+
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
1012

1113
namespace impeller {
1214

1315
std::shared_ptr<ContextGLES> ContextGLES::Create(
1416
std::unique_ptr<ProcTableGLES> gl,
15-
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries) {
17+
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
18+
bool enable_gpu_tracing) {
1619
return std::shared_ptr<ContextGLES>(
17-
new ContextGLES(std::move(gl), shader_libraries));
20+
new ContextGLES(std::move(gl), shader_libraries, enable_gpu_tracing));
1821
}
1922

20-
ContextGLES::ContextGLES(std::unique_ptr<ProcTableGLES> gl,
21-
const std::vector<std::shared_ptr<fml::Mapping>>&
22-
shader_libraries_mappings) {
23+
ContextGLES::ContextGLES(
24+
std::unique_ptr<ProcTableGLES> gl,
25+
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_mappings,
26+
bool enable_gpu_tracing) {
2327
reactor_ = std::make_shared<ReactorGLES>(std::move(gl));
2428
if (!reactor_->IsValid()) {
2529
VALIDATION_LOG << "Could not create valid reactor.";
@@ -61,7 +65,8 @@ ContextGLES::ContextGLES(std::unique_ptr<ProcTableGLES> gl,
6165
std::shared_ptr<SamplerLibraryGLES>(new SamplerLibraryGLES(
6266
device_capabilities_->SupportsDecalSamplerAddressMode()));
6367
}
64-
68+
gpu_tracer_ = std::make_shared<GPUTracerGLES>(GetReactor()->GetProcTable(),
69+
enable_gpu_tracing);
6570
is_valid_ = true;
6671
}
6772

impeller/renderer/backend/gles/context_gles.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
#pragma once
66

7+
#include <thread>
8+
#include <unordered_map>
79
#include "flutter/fml/macros.h"
810
#include "impeller/base/backend_cast.h"
911
#include "impeller/renderer/backend/gles/allocator_gles.h"
1012
#include "impeller/renderer/backend/gles/capabilities_gles.h"
13+
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
1114
#include "impeller/renderer/backend/gles/pipeline_library_gles.h"
1215
#include "impeller/renderer/backend/gles/reactor_gles.h"
1316
#include "impeller/renderer/backend/gles/sampler_library_gles.h"
@@ -23,7 +26,8 @@ class ContextGLES final : public Context,
2326
public:
2427
static std::shared_ptr<ContextGLES> Create(
2528
std::unique_ptr<ProcTableGLES> gl,
26-
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries);
29+
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
30+
bool enable_gpu_tracing);
2731

2832
// |Context|
2933
~ContextGLES() override;
@@ -38,12 +42,16 @@ class ContextGLES final : public Context,
3842

3943
bool RemoveReactorWorker(ReactorGLES::WorkerID id);
4044

45+
std::shared_ptr<GPUTracerGLES> GetGPUTracer() const { return gpu_tracer_; }
46+
4147
private:
4248
ReactorGLES::Ref reactor_;
4349
std::shared_ptr<ShaderLibraryGLES> shader_library_;
4450
std::shared_ptr<PipelineLibraryGLES> pipeline_library_;
4551
std::shared_ptr<SamplerLibraryGLES> sampler_library_;
4652
std::shared_ptr<AllocatorGLES> resource_allocator_;
53+
std::shared_ptr<GPUTracerGLES> gpu_tracer_;
54+
4755
// Note: This is stored separately from the ProcTableGLES CapabilitiesGLES
4856
// in order to satisfy the Context::GetCapabilities signature which returns
4957
// a reference.
@@ -52,7 +60,8 @@ class ContextGLES final : public Context,
5260

5361
ContextGLES(
5462
std::unique_ptr<ProcTableGLES> gl,
55-
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries);
63+
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
64+
bool enable_gpu_tracing);
5665

5766
// |Context|
5867
std::string DescribeGpuModel() const override;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
6+
#include <thread>
7+
#include "fml/trace_event.h"
8+
9+
namespace impeller {
10+
11+
GPUTracerGLES::GPUTracerGLES(const ProcTableGLES& gl, bool enable_tracing) {
12+
#ifdef IMPELLER_DEBUG
13+
auto desc = gl.GetDescription();
14+
enabled_ =
15+
enable_tracing && desc->HasExtension("GL_EXT_disjoint_timer_query");
16+
#endif // IMPELLER_DEBUG
17+
}
18+
19+
void GPUTracerGLES::MarkFrameStart(const ProcTableGLES& gl) {
20+
if (!enabled_ || active_frame_.has_value() ||
21+
std::this_thread::get_id() != raster_thread_) {
22+
return;
23+
}
24+
25+
// At the beginning of a frame, check the status of all pending
26+
// previous queries.
27+
ProcessQueries(gl);
28+
29+
uint32_t query = 0;
30+
gl.GenQueriesEXT(1, &query);
31+
if (query == 0) {
32+
return;
33+
}
34+
35+
active_frame_ = query;
36+
gl.BeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
37+
}
38+
39+
void GPUTracerGLES::RecordRasterThread() {
40+
raster_thread_ = std::this_thread::get_id();
41+
}
42+
43+
void GPUTracerGLES::ProcessQueries(const ProcTableGLES& gl) {
44+
// For reasons unknown to me, querying the state of more than
45+
// one query object per frame causes crashes on a Pixel 6 pro.
46+
// It does not crash on an S10.
47+
while (!pending_traces_.empty()) {
48+
auto query = pending_traces_.front();
49+
50+
// First check if the query is complete without blocking
51+
// on the result. Incomplete results are left in the pending
52+
// trace vector and will not be checked again for another
53+
// frame.
54+
GLuint available = GL_FALSE;
55+
gl.GetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
56+
57+
if (available != GL_TRUE) {
58+
// If a query is not available, then all subsequent queries will be
59+
// unavailable.
60+
return;
61+
}
62+
// Return the timer resolution in nanoseconds.
63+
uint64_t duration = 0;
64+
gl.GetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &duration);
65+
auto gpu_ms = duration / 1000000.0;
66+
67+
FML_TRACE_COUNTER("flutter", "GPUTracer",
68+
reinterpret_cast<int64_t>(this), // Trace Counter ID
69+
"FrameTimeMS", gpu_ms);
70+
gl.DeleteQueriesEXT(1, &query);
71+
pending_traces_.pop_front();
72+
}
73+
}
74+
75+
void GPUTracerGLES::MarkFrameEnd(const ProcTableGLES& gl) {
76+
if (!enabled_ || std::this_thread::get_id() != raster_thread_ ||
77+
!active_frame_.has_value()) {
78+
return;
79+
}
80+
81+
auto query = active_frame_.value();
82+
gl.EndQueryEXT(GL_TIME_ELAPSED_EXT);
83+
84+
pending_traces_.push_back(query);
85+
active_frame_ = std::nullopt;
86+
}
87+
88+
} // namespace impeller
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <deque>
8+
#include <thread>
9+
10+
#include "impeller/renderer/backend/gles/proc_table_gles.h"
11+
12+
namespace impeller {
13+
14+
/// @brief Trace GPU execution times using GL_EXT_disjoint_timer_query on GLES.
15+
///
16+
/// Note: there are a substantial number of GPUs where usage of the this API is
17+
/// known to cause crashes. As a result, this functionality is disabled by
18+
/// default and can only be enabled in debug/profile mode via a specific opt-in
19+
/// flag that is exposed in the Android manifest.
20+
///
21+
/// To enable, add the following metadata to the application's Android manifest:
22+
/// <meta-data
23+
/// android:name="io.flutter.embedding.android.EnableOpenGLGPUTracing"
24+
/// android:value="false" />
25+
class GPUTracerGLES {
26+
public:
27+
GPUTracerGLES(const ProcTableGLES& gl, bool enable_tracing);
28+
29+
~GPUTracerGLES() = default;
30+
31+
/// @brief Record the thread id of the raster thread.
32+
void RecordRasterThread();
33+
34+
/// @brief Record the start of a frame workload, if one hasn't already been
35+
/// started.
36+
void MarkFrameStart(const ProcTableGLES& gl);
37+
38+
/// @brief Record the end of a frame workload.
39+
void MarkFrameEnd(const ProcTableGLES& gl);
40+
41+
private:
42+
void ProcessQueries(const ProcTableGLES& gl);
43+
44+
std::deque<uint32_t> pending_traces_;
45+
std::optional<uint32_t> active_frame_ = std::nullopt;
46+
std::thread::id raster_thread_;
47+
48+
bool enabled_ = false;
49+
};
50+
51+
} // namespace impeller

impeller/renderer/backend/gles/proc_table_gles.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,13 @@ struct GLProc {
196196
PROC(PushDebugGroupKHR); \
197197
PROC(PopDebugGroupKHR); \
198198
PROC(ObjectLabelKHR); \
199-
PROC(RenderbufferStorageMultisampleEXT);
199+
PROC(RenderbufferStorageMultisampleEXT); \
200+
PROC(GenQueriesEXT); \
201+
PROC(DeleteQueriesEXT); \
202+
PROC(GetQueryObjectui64vEXT); \
203+
PROC(BeginQueryEXT); \
204+
PROC(EndQueryEXT); \
205+
PROC(GetQueryObjectuivEXT);
200206

201207
enum class DebugResourceType {
202208
kTexture,

impeller/renderer/backend/gles/render_pass_gles.cc

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
#include "flutter/fml/trace_event.h"
88
#include "fml/closure.h"
99
#include "impeller/base/validation.h"
10+
#include "impeller/renderer/backend/gles/context_gles.h"
1011
#include "impeller/renderer/backend/gles/device_buffer_gles.h"
1112
#include "impeller/renderer/backend/gles/formats_gles.h"
13+
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
1214
#include "impeller/renderer/backend/gles/pipeline_gles.h"
1315
#include "impeller/renderer/backend/gles/texture_gles.h"
1416

@@ -141,14 +143,18 @@ struct RenderPassData {
141143
const RenderPassData& pass_data,
142144
const std::shared_ptr<Allocator>& transients_allocator,
143145
const ReactorGLES& reactor,
144-
const std::vector<Command>& commands) {
146+
const std::vector<Command>& commands,
147+
const std::shared_ptr<GPUTracerGLES>& tracer) {
145148
TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor");
146149

147150
if (commands.empty()) {
148151
return true;
149152
}
150153

151154
const auto& gl = reactor.GetProcTable();
155+
#ifdef IMPELLER_DEBUG
156+
tracer->MarkFrameStart(gl);
157+
#endif // IMPELLER_DEBUG
152158

153159
fml::ScopedCleanupClosure pop_pass_debug_marker(
154160
[&gl]() { gl.PopDebugGroup(); });
@@ -492,6 +498,11 @@ struct RenderPassData {
492498
attachments.data() // size
493499
);
494500
}
501+
#ifdef IMPELLER_DEBUG
502+
if (is_default_fbo) {
503+
tracer->MarkFrameEnd(gl);
504+
}
505+
#endif // IMPELLER_DEBUG
495506

496507
return true;
497508
}
@@ -549,12 +560,13 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
549560
}
550561

551562
std::shared_ptr<const RenderPassGLES> shared_this = shared_from_this();
563+
auto tracer = ContextGLES::Cast(context).GetGPUTracer();
552564
return reactor_->AddOperation([pass_data,
553565
allocator = context.GetResourceAllocator(),
554-
render_pass = std::move(shared_this)](
555-
const auto& reactor) {
566+
render_pass = std::move(shared_this),
567+
tracer](const auto& reactor) {
556568
auto result = EncodeCommandsInReactor(*pass_data, allocator, reactor,
557-
render_pass->commands_);
569+
render_pass->commands_, tracer);
558570
FML_CHECK(result) << "Must be able to encode GL commands without error.";
559571
});
560572
}

0 commit comments

Comments
 (0)