Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -4854,6 +4856,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
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
4 changes: 3 additions & 1 deletion impeller/renderer/backend/gles/context_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
// 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 {

Expand Down Expand Up @@ -61,7 +63,7 @@ ContextGLES::ContextGLES(std::unique_ptr<ProcTableGLES> gl,
std::shared_ptr<SamplerLibraryGLES>(new SamplerLibraryGLES(
device_capabilities_->SupportsDecalSamplerAddressMode()));
}

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

Expand Down
7 changes: 7 additions & 0 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 Down Expand Up @@ -38,12 +41,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 Down
94 changes: 94 additions & 0 deletions impeller/renderer/backend/gles/gpu_tracer_gles.cc
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());
Copy link
Member

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 above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

FML_DCHECK(query != 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is redundant with the query == 0 check above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "on" -> "one"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// It does not crash on an S10.
Copy link
Member

Choose a reason for hiding this comment

The 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 MarkFrameStart call, then that means the pending traces queue will grow unbounded each time a query result is unavailable at the next MarkFrameStart.

auto latest_query = pending_traces_.front();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think latest_query implies that this is the most recent query, when it's actually the least recent.

I'd rename this to query

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// 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
41 changes: 41 additions & 0 deletions impeller/renderer/backend/gles/gpu_tracer_gles.h
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_started_frame_ can be removed. The code can instead check whether active_frame_ contains a value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


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
49 changes: 49 additions & 0 deletions impeller/renderer/backend/gles/test/gpu_tracer_gles_unittests.cc
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
Loading