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

Commit

Permalink
[Impeller] GPU Tracer for GLES. (#47080)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jonahwilliams authored Oct 20, 2023
1 parent 8aee4dd commit 29168dd
Show file tree
Hide file tree
Showing 23 changed files with 364 additions and 49 deletions.
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2086,6 +2086,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h + .
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/handle_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/handle_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -4855,6 +4857,8 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/gpu_tracer_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/handle_gles.cc
FILE: ../../../flutter/impeller/renderer/backend/gles/handle_gles.h
FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc
Expand Down
4 changes: 4 additions & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ struct Settings {
// must be available to the application.
bool enable_vulkan_validation = false;

// Enable GPU tracing in GLES backends.
// Some devices claim to support the required APIs but crash on their usage.
bool enable_opengl_gpu_tracing = false;

// Data set by platform-specific embedders for use in font initialization.
uint32_t font_initialization_data = 0;

Expand Down
4 changes: 2 additions & 2 deletions impeller/playground/backend/gles/playground_impl_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
return nullptr;
}

auto context =
ContextGLES::Create(std::move(gl), ShaderLibraryMappingsForPlayground());
auto context = ContextGLES::Create(
std::move(gl), ShaderLibraryMappingsForPlayground(), true);
if (!context) {
FML_LOG(ERROR) << "Could not create context.";
return nullptr;
Expand Down
3 changes: 3 additions & 0 deletions impeller/renderer/backend/gles/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ impeller_component("gles_unittests") {
sources = [
"test/capabilities_unittests.cc",
"test/formats_gles_unittests.cc",
"test/gpu_tracer_gles_unittests.cc",
"test/mock_gles.cc",
"test/mock_gles.h",
"test/mock_gles_unittests.cc",
Expand Down Expand Up @@ -51,6 +52,8 @@ impeller_component("gles") {
"formats_gles.cc",
"formats_gles.h",
"gles.h",
"gpu_tracer_gles.cc",
"gpu_tracer_gles.h",
"handle_gles.cc",
"handle_gles.h",
"pipeline_gles.cc",
Expand Down
17 changes: 11 additions & 6 deletions impeller/renderer/backend/gles/context_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@
// found in the LICENSE file.

#include "impeller/renderer/backend/gles/context_gles.h"
#include <memory>

#include "impeller/base/config.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/gles/command_buffer_gles.h"
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"

namespace impeller {

std::shared_ptr<ContextGLES> ContextGLES::Create(
std::unique_ptr<ProcTableGLES> gl,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries) {
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
bool enable_gpu_tracing) {
return std::shared_ptr<ContextGLES>(
new ContextGLES(std::move(gl), shader_libraries));
new ContextGLES(std::move(gl), shader_libraries, enable_gpu_tracing));
}

ContextGLES::ContextGLES(std::unique_ptr<ProcTableGLES> gl,
const std::vector<std::shared_ptr<fml::Mapping>>&
shader_libraries_mappings) {
ContextGLES::ContextGLES(
std::unique_ptr<ProcTableGLES> gl,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_mappings,
bool enable_gpu_tracing) {
reactor_ = std::make_shared<ReactorGLES>(std::move(gl));
if (!reactor_->IsValid()) {
VALIDATION_LOG << "Could not create valid reactor.";
Expand Down Expand Up @@ -61,7 +65,8 @@ ContextGLES::ContextGLES(std::unique_ptr<ProcTableGLES> gl,
std::shared_ptr<SamplerLibraryGLES>(new SamplerLibraryGLES(
device_capabilities_->SupportsDecalSamplerAddressMode()));
}

gpu_tracer_ = std::make_shared<GPUTracerGLES>(GetReactor()->GetProcTable(),
enable_gpu_tracing);
is_valid_ = true;
}

Expand Down
13 changes: 11 additions & 2 deletions impeller/renderer/backend/gles/context_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

#pragma once

#include <thread>
#include <unordered_map>
#include "flutter/fml/macros.h"
#include "impeller/base/backend_cast.h"
#include "impeller/renderer/backend/gles/allocator_gles.h"
#include "impeller/renderer/backend/gles/capabilities_gles.h"
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
#include "impeller/renderer/backend/gles/pipeline_library_gles.h"
#include "impeller/renderer/backend/gles/reactor_gles.h"
#include "impeller/renderer/backend/gles/sampler_library_gles.h"
Expand All @@ -23,7 +26,8 @@ class ContextGLES final : public Context,
public:
static std::shared_ptr<ContextGLES> Create(
std::unique_ptr<ProcTableGLES> gl,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries);
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
bool enable_gpu_tracing);

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

bool RemoveReactorWorker(ReactorGLES::WorkerID id);

std::shared_ptr<GPUTracerGLES> GetGPUTracer() const { return gpu_tracer_; }

private:
ReactorGLES::Ref reactor_;
std::shared_ptr<ShaderLibraryGLES> shader_library_;
std::shared_ptr<PipelineLibraryGLES> pipeline_library_;
std::shared_ptr<SamplerLibraryGLES> sampler_library_;
std::shared_ptr<AllocatorGLES> resource_allocator_;
std::shared_ptr<GPUTracerGLES> gpu_tracer_;

// Note: This is stored separately from the ProcTableGLES CapabilitiesGLES
// in order to satisfy the Context::GetCapabilities signature which returns
// a reference.
Expand All @@ -52,7 +60,8 @@ class ContextGLES final : public Context,

ContextGLES(
std::unique_ptr<ProcTableGLES> gl,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries);
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
bool enable_gpu_tracing);

// |Context|
std::string DescribeGpuModel() const override;
Expand Down
88 changes: 88 additions & 0 deletions impeller/renderer/backend/gles/gpu_tracer_gles.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// 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, bool enable_tracing) {
#ifdef IMPELLER_DEBUG
auto desc = gl.GetDescription();
enabled_ =
enable_tracing && desc->HasExtension("GL_EXT_disjoint_timer_query");
#endif // IMPELLER_DEBUG
}

void GPUTracerGLES::MarkFrameStart(const ProcTableGLES& gl) {
if (!enabled_ || active_frame_.has_value() ||
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;
}

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) {
// For reasons unknown to me, querying the state of more than
// one query object per frame causes crashes on a Pixel 6 pro.
// It does not crash on an S10.
while (!pending_traces_.empty()) {
auto 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(query, GL_QUERY_RESULT_AVAILABLE_EXT, &available);

if (available != GL_TRUE) {
// If a query is not available, then all subsequent queries will be
// unavailable.
return;
}
// Return the timer resolution in nanoseconds.
uint64_t duration = 0;
gl.GetQueryObjectui64vEXT(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, &query);
pending_traces_.pop_front();
}
}

void GPUTracerGLES::MarkFrameEnd(const ProcTableGLES& gl) {
if (!enabled_ || std::this_thread::get_id() != raster_thread_ ||
!active_frame_.has_value()) {
return;
}

auto query = active_frame_.value();
gl.EndQueryEXT(GL_TIME_ELAPSED_EXT);

pending_traces_.push_back(query);
active_frame_ = std::nullopt;
}

} // namespace impeller
51 changes: 51 additions & 0 deletions impeller/renderer/backend/gles/gpu_tracer_gles.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 {

/// @brief Trace GPU execution times using GL_EXT_disjoint_timer_query on GLES.
///
/// Note: there are a substantial number of GPUs where usage of the this API is
/// known to cause crashes. As a result, this functionality is disabled by
/// default and can only be enabled in debug/profile mode via a specific opt-in
/// flag that is exposed in the Android manifest.
///
/// To enable, add the following metadata to the application's Android manifest:
/// <meta-data
/// android:name="io.flutter.embedding.android.EnableOpenGLGPUTracing"
/// android:value="false" />
class GPUTracerGLES {
public:
GPUTracerGLES(const ProcTableGLES& gl, bool enable_tracing);

~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 enabled_ = false;
};

} // namespace impeller
8 changes: 7 additions & 1 deletion impeller/renderer/backend/gles/proc_table_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,13 @@ struct GLProc {
PROC(PushDebugGroupKHR); \
PROC(PopDebugGroupKHR); \
PROC(ObjectLabelKHR); \
PROC(RenderbufferStorageMultisampleEXT);
PROC(RenderbufferStorageMultisampleEXT); \
PROC(GenQueriesEXT); \
PROC(DeleteQueriesEXT); \
PROC(GetQueryObjectui64vEXT); \
PROC(BeginQueryEXT); \
PROC(EndQueryEXT); \
PROC(GetQueryObjectuivEXT);

enum class DebugResourceType {
kTexture,
Expand Down
20 changes: 16 additions & 4 deletions impeller/renderer/backend/gles/render_pass_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include "flutter/fml/trace_event.h"
#include "fml/closure.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/device_buffer_gles.h"
#include "impeller/renderer/backend/gles/formats_gles.h"
#include "impeller/renderer/backend/gles/gpu_tracer_gles.h"
#include "impeller/renderer/backend/gles/pipeline_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h"

Expand Down Expand Up @@ -141,14 +143,18 @@ struct RenderPassData {
const RenderPassData& pass_data,
const std::shared_ptr<Allocator>& transients_allocator,
const ReactorGLES& reactor,
const std::vector<Command>& commands) {
const std::vector<Command>& commands,
const std::shared_ptr<GPUTracerGLES>& tracer) {
TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor");

if (commands.empty()) {
return true;
}

const auto& gl = reactor.GetProcTable();
#ifdef IMPELLER_DEBUG
tracer->MarkFrameStart(gl);
#endif // IMPELLER_DEBUG

fml::ScopedCleanupClosure pop_pass_debug_marker(
[&gl]() { gl.PopDebugGroup(); });
Expand Down Expand Up @@ -492,6 +498,11 @@ struct RenderPassData {
attachments.data() // size
);
}
#ifdef IMPELLER_DEBUG
if (is_default_fbo) {
tracer->MarkFrameEnd(gl);
}
#endif // IMPELLER_DEBUG

return true;
}
Expand Down Expand Up @@ -549,12 +560,13 @@ bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
}

std::shared_ptr<const RenderPassGLES> shared_this = shared_from_this();
auto tracer = ContextGLES::Cast(context).GetGPUTracer();
return reactor_->AddOperation([pass_data,
allocator = context.GetResourceAllocator(),
render_pass = std::move(shared_this)](
const auto& reactor) {
render_pass = std::move(shared_this),
tracer](const auto& reactor) {
auto result = EncodeCommandsInReactor(*pass_data, allocator, reactor,
render_pass->commands_);
render_pass->commands_, tracer);
FML_CHECK(result) << "Must be able to encode GL commands without error.";
});
}
Expand Down
4 changes: 4 additions & 0 deletions impeller/renderer/backend/gles/surface_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ std::unique_ptr<Surface> SurfaceGLES::WrapFBO(
render_target_desc.SetColorAttachment(color0, 0u);
render_target_desc.SetStencilAttachment(stencil0);

#ifdef IMPELLER_DEBUG
gl_context.GetGPUTracer()->RecordRasterThread();
#endif // IMPELLER_DEBUG

return std::unique_ptr<SurfaceGLES>(
new SurfaceGLES(std::move(swap_callback), render_target_desc));
}
Expand Down
Loading

0 comments on commit 29168dd

Please sign in to comment.