diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5bcc7614d181b..5499e562b0477 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2128,6 +2128,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.h + . ORIGIN: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/metal/pipeline_mtl.h + ../../../flutter/LICENSE @@ -4893,6 +4895,8 @@ FILE: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/formats_mtl.mm +FILE: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.h +FILE: ../../../flutter/impeller/renderer/backend/metal/gpu_tracer_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/pipeline_library_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/pipeline_mtl.h diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn index 9a0f6a631e3d7..254cae97f063a 100644 --- a/impeller/renderer/backend/metal/BUILD.gn +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -24,6 +24,8 @@ impeller_component("metal") { "device_buffer_mtl.mm", "formats_mtl.h", "formats_mtl.mm", + "gpu_tracer_mtl.h", + "gpu_tracer_mtl.mm", "pipeline_library_mtl.h", "pipeline_library_mtl.mm", "pipeline_mtl.h", diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 49384a9e1cdfd..b3161b74c4e36 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -159,6 +159,13 @@ static bool LogMTLCommandBufferErrorIfPresent(id buffer) { } bool CommandBufferMTL::OnSubmitCommands(CompletionCallback callback) { + auto context = context_.lock(); + if (!context) { + return false; + } +#ifdef IMPELLER_DEBUG + ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer_); +#endif // IMPELLER_DEBUG if (callback) { [buffer_ addCompletedHandler:^(id buffer) { @@ -190,6 +197,10 @@ static bool LogMTLCommandBufferErrorIfPresent(id buffer) { auto buffer = buffer_; buffer_ = nil; +#ifdef IMPELLER_DEBUG + ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer); +#endif // IMPELLER_DEBUG + auto worker_task_runner = ContextMTL::Cast(*context).GetWorkerTaskRunner(); auto mtl_render_pass = static_cast(render_pass.get()); diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index 165ff722e256a..9fbff94d4669b 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -16,6 +16,7 @@ #include "impeller/core/sampler.h" #include "impeller/renderer/backend/metal/allocator_mtl.h" #include "impeller/renderer/backend/metal/command_buffer_mtl.h" +#include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" #include "impeller/renderer/backend/metal/pipeline_library_mtl.h" #include "impeller/renderer/backend/metal/shader_library_mtl.h" #include "impeller/renderer/capabilities.h" @@ -93,6 +94,10 @@ class ContextMTL final : public Context, std::shared_ptr GetIsGpuDisabledSyncSwitch() const; +#ifdef IMPELLER_DEBUG + std::shared_ptr GetGPUTracer() const; +#endif // IMPELLER_DEBUG + // |Context| void StoreTaskForGPU(std::function task) override; @@ -116,6 +121,9 @@ class ContextMTL final : public Context, std::shared_ptr device_capabilities_; std::shared_ptr raster_message_loop_; std::shared_ptr is_gpu_disabled_sync_switch_; +#ifdef IMPELLER_DEBUG + std::shared_ptr gpu_tracer_; +#endif // IMPELLER_DEBUG std::deque> tasks_awaiting_gpu_; std::unique_ptr sync_switch_observer_; bool is_valid_ = false; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 5297ab987145b..06fd34724685a 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/metal/context_mtl.h" #include +#include #include "flutter/fml/concurrent_message_loop.h" #include "flutter/fml/file.h" @@ -12,6 +13,7 @@ #include "flutter/fml/paths.h" #include "flutter/fml/synchronization/sync_switch.h" #include "impeller/core/sampler_descriptor.h" +#include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" #include "impeller/renderer/backend/metal/sampler_library_mtl.h" #include "impeller/renderer/capabilities.h" @@ -145,7 +147,9 @@ static bool DeviceSupportsComputeSubgroups(id device) { device_capabilities_ = InferMetalCapabilities(device_, PixelFormat::kB8G8R8A8UNormInt); - +#ifdef IMPELLER_DEBUG + gpu_tracer_ = std::make_shared(); +#endif // IMPELLER_DEBUG is_valid_ = true; } @@ -330,6 +334,12 @@ new ContextMTL(device, command_queue, raster_message_loop_.reset(); } +#ifdef IMPELLER_DEBUG +std::shared_ptr ContextMTL::GetGPUTracer() const { + return gpu_tracer_; +} +#endif // IMPELLER_DEBUG + const std::shared_ptr ContextMTL::GetWorkerTaskRunner() const { return raster_message_loop_->GetTaskRunner(); diff --git a/impeller/renderer/backend/metal/gpu_tracer_mtl.h b/impeller/renderer/backend/metal/gpu_tracer_mtl.h new file mode 100644 index 0000000000000..bfa227ed10e81 --- /dev/null +++ b/impeller/renderer/backend/metal/gpu_tracer_mtl.h @@ -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 + +#include +#include +#include "impeller/base/thread.h" +#include "impeller/base/thread_safety.h" +#include "impeller/geometry/scalar.h" + +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. +class GPUTracerMTL : public std::enable_shared_from_this { + public: + GPUTracerMTL() = default; + + ~GPUTracerMTL() = default; + + /// @brief Record that the current frame has ended. Any additional cmd buffers + /// will be + /// attributed to the "next" frame. + void MarkFrameEnd(); + + /// @brief Record the current cmd buffer GPU execution timestamps into an + /// aggregate + /// frame workload metric. + void RecordCmdBuffer(id buffer); + + private: + struct GPUTraceState { + Scalar smallest_timestamp = std::numeric_limits::max(); + Scalar largest_timestamp = 0; + size_t pending_buffers = 0; + }; + + mutable Mutex trace_state_mutex_; + GPUTraceState trace_states_[16] IPLR_GUARDED_BY(trace_state_mutex_); + size_t current_state_ IPLR_GUARDED_BY(trace_state_mutex_) = 0u; +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/gpu_tracer_mtl.mm b/impeller/renderer/backend/metal/gpu_tracer_mtl.mm new file mode 100644 index 0000000000000..41f1aa04a3328 --- /dev/null +++ b/impeller/renderer/backend/metal/gpu_tracer_mtl.mm @@ -0,0 +1,56 @@ +// 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 +#include "fml/trace_event.h" +#include "impeller/renderer/backend/metal/context_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" + +#include + +#include "impeller/renderer/backend/metal/gpu_tracer_mtl.h" + +namespace impeller { + +void GPUTracerMTL::MarkFrameEnd() { + if (@available(ios 10.3, tvos 10.2, macos 10.15, macCatalyst 13.0, *)) { + Lock lock(trace_state_mutex_); + current_state_ = (current_state_ + 1) % 16; + } +} + +void GPUTracerMTL::RecordCmdBuffer(id buffer) { + if (@available(ios 10.3, tvos 10.2, macos 10.15, macCatalyst 13.0, *)) { + Lock lock(trace_state_mutex_); + auto current_state = current_state_; + trace_states_[current_state].pending_buffers += 1; + + auto weak_self = weak_from_this(); + [buffer addCompletedHandler:^(id buffer) { + auto self = weak_self.lock(); + if (!self) { + return; + } + Lock lock(self->trace_state_mutex_); + auto& state = self->trace_states_[current_state]; + state.pending_buffers--; + state.smallest_timestamp = std::min( + state.smallest_timestamp, static_cast(buffer.GPUStartTime)); + state.largest_timestamp = std::max( + state.largest_timestamp, static_cast(buffer.GPUEndTime)); + + if (state.pending_buffers == 0) { + auto gpu_ms = + (state.largest_timestamp - state.smallest_timestamp) * 1000; + state.smallest_timestamp = std::numeric_limits::max(); + state.largest_timestamp = 0; + FML_TRACE_COUNTER("flutter", "GPUTracer", + reinterpret_cast(this), // Trace Counter ID + "FrameTimeMS", gpu_ms); + } + }]; + } +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 7244fc3b2a72a..beb900a89be13 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -246,6 +246,9 @@ return false; } } +#ifdef IMPELLER_DEBUG + ContextMTL::Cast(context.get())->GetGPUTracer()->MarkFrameEnd(); +#endif // IMPELLER_DEBUG if (drawable_) { id command_buffer =