mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] GPU Tracer for GLES. (flutter/engine#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.
This commit is contained in:
parent
3d9bc06e13
commit
8031a09e17
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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",
|
||||
@ -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",
|
||||
|
||||
@ -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.";
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
@ -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;
|
||||
@ -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.
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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,
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -141,7 +143,8 @@ 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()) {
|
||||
@ -149,6 +152,9 @@ struct RenderPassData {
|
||||
}
|
||||
|
||||
const auto& gl = reactor.GetProcTable();
|
||||
#ifdef IMPELLER_DEBUG
|
||||
tracer->MarkFrameStart(gl);
|
||||
#endif // IMPELLER_DEBUG
|
||||
|
||||
fml::ScopedCleanupClosure pop_pass_debug_marker(
|
||||
[&gl]() { gl.PopDebugGroup(); });
|
||||
@ -492,6 +498,11 @@ struct RenderPassData {
|
||||
attachments.data() // size
|
||||
);
|
||||
}
|
||||
#ifdef IMPELLER_DEBUG
|
||||
if (is_default_fbo) {
|
||||
tracer->MarkFrameEnd(gl);
|
||||
}
|
||||
#endif // IMPELLER_DEBUG
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -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.";
|
||||
});
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
// 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(), true);
|
||||
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
|
||||
@ -112,6 +112,53 @@ void mockPushDebugGroupKHR(GLenum source,
|
||||
static_assert(CheckSameSignature<decltype(mockPushDebugGroupKHR), //
|
||||
decltype(glPushDebugGroupKHR)>::value);
|
||||
|
||||
void mockGenQueriesEXT(GLsizei n, GLuint* ids) {
|
||||
RecordGLCall("glGenQueriesEXT");
|
||||
for (auto i = 0; i < n; i++) {
|
||||
ids[i] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(CheckSameSignature<decltype(mockGenQueriesEXT), //
|
||||
decltype(glGenQueriesEXT)>::value);
|
||||
|
||||
void mockBeginQueryEXT(GLenum target, GLuint id) {
|
||||
RecordGLCall("glBeginQueryEXT");
|
||||
}
|
||||
|
||||
static_assert(CheckSameSignature<decltype(mockBeginQueryEXT), //
|
||||
decltype(glBeginQueryEXT)>::value);
|
||||
|
||||
void mockEndQueryEXT(GLuint id) {
|
||||
RecordGLCall("glEndQueryEXT");
|
||||
}
|
||||
|
||||
static_assert(CheckSameSignature<decltype(mockEndQueryEXT), //
|
||||
decltype(glEndQueryEXT)>::value);
|
||||
|
||||
void mockGetQueryObjectuivEXT(GLuint id, GLenum target, GLuint* result) {
|
||||
RecordGLCall("glGetQueryObjectuivEXT");
|
||||
*result = GL_TRUE;
|
||||
}
|
||||
|
||||
static_assert(CheckSameSignature<decltype(mockGetQueryObjectuivEXT), //
|
||||
decltype(glGetQueryObjectuivEXT)>::value);
|
||||
|
||||
void mockGetQueryObjectui64vEXT(GLuint id, GLenum target, GLuint64* result) {
|
||||
RecordGLCall("glGetQueryObjectui64vEXT");
|
||||
*result = 1000u;
|
||||
}
|
||||
|
||||
static_assert(CheckSameSignature<decltype(mockGetQueryObjectui64vEXT), //
|
||||
decltype(glGetQueryObjectui64vEXT)>::value);
|
||||
|
||||
void mockDeleteQueriesEXT(GLsizei size, const GLuint* queries) {
|
||||
RecordGLCall("glDeleteQueriesEXT");
|
||||
}
|
||||
|
||||
static_assert(CheckSameSignature<decltype(mockDeleteQueriesEXT), //
|
||||
decltype(glDeleteQueriesEXT)>::value);
|
||||
|
||||
std::shared_ptr<MockGLES> MockGLES::Init(
|
||||
const std::optional<std::vector<const unsigned char*>>& extensions) {
|
||||
// If we cannot obtain a lock, MockGLES is already being used elsewhere.
|
||||
@ -136,6 +183,18 @@ const ProcTableGLES::Resolver kMockResolver = [](const char* name) {
|
||||
return reinterpret_cast<void*>(&mockGetIntegerv);
|
||||
} else if (strcmp(name, "glGetError") == 0) {
|
||||
return reinterpret_cast<void*>(&mockGetError);
|
||||
} else if (strcmp(name, "glGenQueriesEXT") == 0) {
|
||||
return reinterpret_cast<void*>(&mockGenQueriesEXT);
|
||||
} else if (strcmp(name, "glBeginQueryEXT") == 0) {
|
||||
return reinterpret_cast<void*>(&mockBeginQueryEXT);
|
||||
} else if (strcmp(name, "glEndQueryEXT") == 0) {
|
||||
return reinterpret_cast<void*>(&mockEndQueryEXT);
|
||||
} else if (strcmp(name, "glDeleteQueriesEXT") == 0) {
|
||||
return reinterpret_cast<void*>(&mockDeleteQueriesEXT);
|
||||
} else if (strcmp(name, "glGetQueryObjectui64vEXT") == 0) {
|
||||
return reinterpret_cast<void*>(mockGetQueryObjectui64vEXT);
|
||||
} else if (strcmp(name, "glGetQueryObjectuivEXT") == 0) {
|
||||
return reinterpret_cast<void*>(mockGetQueryObjectuivEXT);
|
||||
} else {
|
||||
return reinterpret_cast<void*>(&doNothing);
|
||||
}
|
||||
|
||||
@ -17,9 +17,8 @@ namespace impeller {
|
||||
class ContextMTL;
|
||||
|
||||
/// @brief Approximate the GPU frame time by computing a difference between the
|
||||
/// smallest
|
||||
/// GPUStartTime and largest GPUEndTime for all cmd buffers submitted in
|
||||
/// a frame workload.
|
||||
/// smallest GPUStartTime and largest GPUEndTime for all command buffers
|
||||
/// submitted in a frame workload.
|
||||
class GPUTracerMTL : public std::enable_shared_from_this<GPUTracerMTL> {
|
||||
public:
|
||||
GPUTracerMTL() = default;
|
||||
@ -27,13 +26,11 @@ class GPUTracerMTL : public std::enable_shared_from_this<GPUTracerMTL> {
|
||||
~GPUTracerMTL() = default;
|
||||
|
||||
/// @brief Record that the current frame has ended. Any additional cmd buffers
|
||||
/// will be
|
||||
/// attributed to the "next" frame.
|
||||
/// will be attributed to the "next" frame.
|
||||
void MarkFrameEnd();
|
||||
|
||||
/// @brief Record the current cmd buffer GPU execution timestamps into an
|
||||
/// aggregate
|
||||
/// frame workload metric.
|
||||
/// aggregate frame workload metric.
|
||||
void RecordCmdBuffer(id<MTLCommandBuffer> buffer);
|
||||
|
||||
private:
|
||||
|
||||
@ -477,6 +477,8 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
|
||||
|
||||
settings.enable_vulkan_validation =
|
||||
command_line.HasOption(FlagForSwitch(Switch::EnableVulkanValidation));
|
||||
settings.enable_opengl_gpu_tracing =
|
||||
command_line.HasOption(FlagForSwitch(Switch::EnableOpenGLGPUTracing));
|
||||
|
||||
settings.enable_embedder_api =
|
||||
command_line.HasOption(FlagForSwitch(Switch::EnableEmbedderAPI));
|
||||
|
||||
@ -275,6 +275,10 @@ DEF_SWITCH(EnableVulkanValidation,
|
||||
"Enable loading Vulkan validation layers. The layers must be "
|
||||
"available to the application and loadable. On non-Vulkan backends, "
|
||||
"this flag does nothing.")
|
||||
DEF_SWITCH(EnableOpenGLGPUTracing,
|
||||
"enable-opengl-gpu-tracing",
|
||||
"Enable tracing of GPU execution time when using the Impeller "
|
||||
"OpenGLES backend.")
|
||||
DEF_SWITCH(LeakVM,
|
||||
"leak-vm",
|
||||
"When the last shell shuts down, the shared VM is leaked by default "
|
||||
|
||||
@ -49,7 +49,8 @@ class AndroidContextGLImpeller::ReactorWorker final
|
||||
};
|
||||
|
||||
static std::shared_ptr<impeller::Context> CreateImpellerContext(
|
||||
const std::shared_ptr<impeller::ReactorGLES::Worker>& worker) {
|
||||
const std::shared_ptr<impeller::ReactorGLES::Worker>& worker,
|
||||
bool enable_gpu_tracing) {
|
||||
auto proc_table = std::make_unique<impeller::ProcTableGLES>(
|
||||
impeller::egl::CreateProcAddressResolver());
|
||||
|
||||
@ -59,19 +60,20 @@ static std::shared_ptr<impeller::Context> CreateImpellerContext(
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
|
||||
std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_gles_data,
|
||||
impeller_entity_shaders_gles_length),
|
||||
std::make_shared<fml::NonOwnedMapping>(
|
||||
impeller_framebuffer_blend_shaders_gles_data,
|
||||
impeller_framebuffer_blend_shaders_gles_length),
|
||||
std::make_shared<fml::NonOwnedMapping>(
|
||||
impeller_entity_shaders_gles_data,
|
||||
impeller_entity_shaders_gles_length),
|
||||
std::make_shared<fml::NonOwnedMapping>(
|
||||
impeller_framebuffer_blend_shaders_gles_data,
|
||||
impeller_framebuffer_blend_shaders_gles_length),
|
||||
#if IMPELLER_ENABLE_3D
|
||||
std::make_shared<fml::NonOwnedMapping>(impeller_scene_shaders_gles_data,
|
||||
impeller_scene_shaders_gles_length),
|
||||
std::make_shared<fml::NonOwnedMapping>(
|
||||
impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length),
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
};
|
||||
|
||||
auto context =
|
||||
impeller::ContextGLES::Create(std::move(proc_table), shader_mappings);
|
||||
auto context = impeller::ContextGLES::Create(
|
||||
std::move(proc_table), shader_mappings, enable_gpu_tracing);
|
||||
if (!context) {
|
||||
FML_LOG(ERROR) << "Could not create OpenGLES Impeller Context.";
|
||||
return nullptr;
|
||||
@ -86,7 +88,8 @@ static std::shared_ptr<impeller::Context> CreateImpellerContext(
|
||||
}
|
||||
|
||||
AndroidContextGLImpeller::AndroidContextGLImpeller(
|
||||
std::unique_ptr<impeller::egl::Display> display)
|
||||
std::unique_ptr<impeller::egl::Display> display,
|
||||
bool enable_gpu_tracing)
|
||||
: AndroidContext(AndroidRenderingAPI::kOpenGLES),
|
||||
reactor_worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())),
|
||||
display_(std::move(display)) {
|
||||
@ -147,7 +150,8 @@ AndroidContextGLImpeller::AndroidContextGLImpeller(
|
||||
return;
|
||||
}
|
||||
|
||||
auto impeller_context = CreateImpellerContext(reactor_worker_);
|
||||
auto impeller_context =
|
||||
CreateImpellerContext(reactor_worker_, enable_gpu_tracing);
|
||||
|
||||
if (!impeller_context) {
|
||||
FML_DLOG(ERROR) << "Could not create Impeller context.";
|
||||
|
||||
@ -13,8 +13,8 @@ namespace flutter {
|
||||
|
||||
class AndroidContextGLImpeller : public AndroidContext {
|
||||
public:
|
||||
explicit AndroidContextGLImpeller(
|
||||
std::unique_ptr<impeller::egl::Display> display);
|
||||
AndroidContextGLImpeller(std::unique_ptr<impeller::egl::Display> display,
|
||||
bool enable_gpu_tracing);
|
||||
|
||||
~AndroidContextGLImpeller();
|
||||
|
||||
|
||||
@ -45,7 +45,8 @@ TEST(AndroidContextGLImpeller, MSAAFirstAttempt) {
|
||||
.WillOnce(Return(ByMove(std::move(second_result))));
|
||||
ON_CALL(*display, ChooseConfig(_))
|
||||
.WillByDefault(Return(ByMove(std::unique_ptr<Config>())));
|
||||
auto context = std::make_unique<AndroidContextGLImpeller>(std::move(display));
|
||||
auto context =
|
||||
std::make_unique<AndroidContextGLImpeller>(std::move(display), true);
|
||||
ASSERT_TRUE(context);
|
||||
}
|
||||
|
||||
@ -76,7 +77,8 @@ TEST(AndroidContextGLImpeller, FallbackForEmulator) {
|
||||
.WillOnce(Return(ByMove(std::move(third_result))));
|
||||
ON_CALL(*display, ChooseConfig(_))
|
||||
.WillByDefault(Return(ByMove(std::unique_ptr<Config>())));
|
||||
auto context = std::make_unique<AndroidContextGLImpeller>(std::move(display));
|
||||
auto context =
|
||||
std::make_unique<AndroidContextGLImpeller>(std::move(display), true);
|
||||
ASSERT_TRUE(context);
|
||||
}
|
||||
} // namespace testing
|
||||
|
||||
@ -14,7 +14,7 @@ namespace flutter {
|
||||
|
||||
class AndroidContextVulkanImpeller : public AndroidContext {
|
||||
public:
|
||||
AndroidContextVulkanImpeller(bool enable_validation);
|
||||
explicit AndroidContextVulkanImpeller(bool enable_validation);
|
||||
|
||||
~AndroidContextVulkanImpeller();
|
||||
|
||||
|
||||
@ -45,6 +45,8 @@ public class FlutterLoader {
|
||||
"io.flutter.embedding.android.EnableVulkanValidation";
|
||||
private static final String IMPELLER_BACKEND_META_DATA_KEY =
|
||||
"io.flutter.embedding.android.ImpellerBackend";
|
||||
private static final String IMPELLER_OPENGL_GPU_TRACING_DATA_KEY =
|
||||
"io.flutter.embedding.android.EnableOpenGLGPUTracing";
|
||||
private static final String DISABLE_IMAGE_READER_PLATFORM_VIEWS_KEY =
|
||||
"io.flutter.embedding.android.DisableImageReaderPlatformViews";
|
||||
|
||||
@ -340,6 +342,9 @@ public class FlutterLoader {
|
||||
ENABLE_VULKAN_VALIDATION_META_DATA_KEY, areValidationLayersOnByDefault())) {
|
||||
shellArgs.add("--enable-vulkan-validation");
|
||||
}
|
||||
if (metaData.getBoolean(IMPELLER_OPENGL_GPU_TRACING_DATA_KEY, false)) {
|
||||
shellArgs.add("--enable-opengl-gpu-tracing");
|
||||
}
|
||||
String backend = metaData.getString(IMPELLER_BACKEND_META_DATA_KEY);
|
||||
if (backend != null) {
|
||||
shellArgs.add("--impeller-backend=" + backend);
|
||||
|
||||
@ -70,7 +70,8 @@ static std::shared_ptr<flutter::AndroidContext> CreateAndroidContext(
|
||||
uint8_t msaa_samples,
|
||||
bool enable_impeller,
|
||||
const std::optional<std::string>& impeller_backend,
|
||||
bool enable_vulkan_validation) {
|
||||
bool enable_vulkan_validation,
|
||||
bool enable_opengl_gpu_tracing) {
|
||||
if (use_software_rendering) {
|
||||
return std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
|
||||
}
|
||||
@ -90,7 +91,8 @@ static std::shared_ptr<flutter::AndroidContext> CreateAndroidContext(
|
||||
switch (backend) {
|
||||
case AndroidRenderingAPI::kOpenGLES:
|
||||
return std::make_unique<AndroidContextGLImpeller>(
|
||||
std::make_unique<impeller::egl::Display>());
|
||||
std::make_unique<impeller::egl::Display>(),
|
||||
enable_opengl_gpu_tracing);
|
||||
case AndroidRenderingAPI::kVulkan:
|
||||
return std::make_unique<AndroidContextVulkanImpeller>(
|
||||
enable_vulkan_validation);
|
||||
@ -99,7 +101,8 @@ static std::shared_ptr<flutter::AndroidContext> CreateAndroidContext(
|
||||
enable_vulkan_validation);
|
||||
if (!vulkan_backend->IsValid()) {
|
||||
return std::make_unique<AndroidContextGLImpeller>(
|
||||
std::make_unique<impeller::egl::Display>());
|
||||
std::make_unique<impeller::egl::Display>(),
|
||||
enable_opengl_gpu_tracing);
|
||||
}
|
||||
return vulkan_backend;
|
||||
}
|
||||
@ -131,7 +134,9 @@ PlatformViewAndroid::PlatformViewAndroid(
|
||||
msaa_samples,
|
||||
delegate.OnPlatformViewGetSettings().enable_impeller,
|
||||
delegate.OnPlatformViewGetSettings().impeller_backend,
|
||||
delegate.OnPlatformViewGetSettings().enable_vulkan_validation)) {}
|
||||
delegate.OnPlatformViewGetSettings().enable_vulkan_validation,
|
||||
delegate.OnPlatformViewGetSettings().enable_opengl_gpu_tracing)) {
|
||||
}
|
||||
|
||||
PlatformViewAndroid::PlatformViewAndroid(
|
||||
PlatformView::Delegate& delegate,
|
||||
|
||||
@ -65,11 +65,12 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller(
|
||||
gl_dispatch_table_.gl_make_current_callback();
|
||||
|
||||
std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
|
||||
std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_gles_data,
|
||||
impeller_entity_shaders_gles_length),
|
||||
std::make_shared<fml::NonOwnedMapping>(
|
||||
impeller_entity_shaders_gles_data,
|
||||
impeller_entity_shaders_gles_length),
|
||||
#if IMPELLER_ENABLE_3D
|
||||
std::make_shared<fml::NonOwnedMapping>(impeller_scene_shaders_gles_data,
|
||||
impeller_scene_shaders_gles_length),
|
||||
std::make_shared<fml::NonOwnedMapping>(
|
||||
impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length),
|
||||
#endif // IMPELLER_ENABLE_3D
|
||||
};
|
||||
auto gl = std::make_unique<impeller::ProcTableGLES>(
|
||||
@ -78,8 +79,8 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller(
|
||||
return;
|
||||
}
|
||||
|
||||
impeller_context_ =
|
||||
impeller::ContextGLES::Create(std::move(gl), shader_mappings);
|
||||
impeller_context_ = impeller::ContextGLES::Create(
|
||||
std::move(gl), shader_mappings, /*enable_gpu_tracing=*/false);
|
||||
|
||||
if (!impeller_context_) {
|
||||
FML_LOG(ERROR) << "Could not create Impeller context.";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user