diff --git a/.ci.yaml b/.ci.yaml index acf9f02beb1..3e1de1be447 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -2730,6 +2730,17 @@ targets: ["devicelab", "android", "linux"] task_name: flutter_gallery__start_up + # linux mokey benchmark + - name: Linux_mokey flutter_gallery_lazy__start_up + recipe: devicelab/devicelab_drone + presubmit: false + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "android", "linux"] + task_name: flutter_gallery_lazy__start_up + # linux mokey benchmark - name: Linux_mokey flutter_gallery__start_up_delayed recipe: devicelab/devicelab_drone diff --git a/TESTOWNERS b/TESTOWNERS index 70e20fa5df5..ddf30ed2f11 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -45,6 +45,7 @@ /dev/devicelab/bin/tasks/flutter_gallery__image_cache_memory.dart @jtmcdole @flutter/engine /dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart @jtmcdole @flutter/engine /dev/devicelab/bin/tasks/flutter_gallery__start_up.dart @jtmcdole @flutter/engine +/dev/devicelab/bin/tasks/flutter_gallery_lazy__start_up.dart @jtmcdole @flutter/engine /dev/devicelab/bin/tasks/flutter_gallery__start_up_delayed.dart @jtmcdole @flutter/engine /dev/devicelab/bin/tasks/flutter_gallery__transition_perf.dart @jtmcdole @flutter/engine /dev/devicelab/bin/tasks/flutter_gallery__transition_perf_e2e.dart @jtmcdole @flutter/engine diff --git a/dev/devicelab/bin/tasks/flutter_gallery_lazy__start_up.dart b/dev/devicelab/bin/tasks/flutter_gallery_lazy__start_up.dart new file mode 100644 index 00000000000..039347c9e2f --- /dev/null +++ b/dev/devicelab/bin/tasks/flutter_gallery_lazy__start_up.dart @@ -0,0 +1,12 @@ +// Copyright 2014 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. + +import 'package:flutter_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/perf_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.android; + await task(createFlutterGalleryStartupTest(enableLazyShaderMode: true)); +} diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index 267eb3c36e2..609298bf0a4 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -270,11 +270,13 @@ TaskFunction createOpenPayScrollPerfTest({bool measureCpuGpu = true}) { TaskFunction createFlutterGalleryStartupTest({ String target = 'lib/main.dart', Map? runEnvironment, + bool enableLazyShaderMode = false, }) { return StartupTest( '${flutterDirectory.path}/dev/integration_tests/flutter_gallery', target: target, runEnvironment: runEnvironment, + enableLazyShaderMode: enableLazyShaderMode, ).run; } @@ -840,6 +842,17 @@ void _addVulkanGPUTracingToManifest(String testDirectory) { _addMetadataToManifest(testDirectory, keyPairs); } +/// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml' +/// +void _addLazyShaderMode(String testDirectory) { + final List<(String, String)> keyPairs = <(String, String)>[ + ('io.flutter.embedding.android.ImpellerLazyShaderInitialization', 'true'), + ]; + _addMetadataToManifest(testDirectory, keyPairs); +} + /// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml' /// and adds the following entry to the application. /// ? runEnvironment; @@ -895,144 +910,155 @@ class StartupTest { const int iterations = 5; final List> results = >[]; - section('Building application'); - String? applicationBinaryPath; - switch (deviceOperatingSystem) { - case DeviceOperatingSystem.android: - await flutter( - 'build', - options: [ - 'apk', - '-v', - '--profile', - '--target-platform=android-arm,android-arm64', - '--target=$target', - ], - ); - applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk'; - case DeviceOperatingSystem.androidArm: - await flutter( - 'build', - options: [ - 'apk', - '-v', - '--profile', - '--target-platform=android-arm', - '--target=$target', - ], - ); - applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk'; - case DeviceOperatingSystem.androidArm64: - await flutter( - 'build', - options: [ - 'apk', - '-v', - '--profile', - '--target-platform=android-arm64', - '--target=$target', - ], - ); - applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk'; - case DeviceOperatingSystem.fake: - case DeviceOperatingSystem.fuchsia: - case DeviceOperatingSystem.linux: - break; - case DeviceOperatingSystem.ios: - case DeviceOperatingSystem.macos: - await flutter( - 'build', - options: [ - if (deviceOperatingSystem == DeviceOperatingSystem.ios) 'ios' else 'macos', - '-v', - '--profile', - '--target=$target', - if (deviceOperatingSystem == DeviceOperatingSystem.ios) '--no-publish-port', - ], - ); - final String buildRoot = path.join(testDirectory, 'build'); - applicationBinaryPath = _findDarwinAppInBuildDirectory(buildRoot); - case DeviceOperatingSystem.windows: - await flutter( - 'build', - options: ['windows', '-v', '--profile', '--target=$target'], - ); - final String basename = path.basename(testDirectory); - final String arch = Abi.current() == Abi.windowsX64 ? 'x64' : 'arm64'; - applicationBinaryPath = path.join( - testDirectory, - 'build', - 'windows', - arch, - 'runner', - 'Profile', - '$basename.exe', - ); + if (enableLazyShaderMode) { + _addLazyShaderMode(testDirectory); } - const int maxFailures = 3; - int currentFailures = 0; - for (int i = 0; i < iterations; i += 1) { - // Startup should not take more than a few minutes. After 10 minutes, - // take a screenshot to help debug. - final Timer timer = Timer(const Duration(minutes: 10), () async { - print('Startup not completed within 10 minutes. Taking a screenshot...'); - await _flutterScreenshot( - device.deviceId, - 'screenshot_startup_${DateTime.now().toLocal().toIso8601String()}.png', - ); - }); - final int result = await flutter( - 'run', - options: [ - '--no-android-gradle-daemon', - '--no-publish-port', - '--verbose', - '--profile', - '--trace-startup', - '--target=$target', - '-d', - device.deviceId, - if (applicationBinaryPath != null) '--use-application-binary=$applicationBinaryPath', - ], - environment: runEnvironment, - canFail: true, - ); - timer.cancel(); - if (result == 0) { - final Map data = - json.decode( - file( - '${testOutputDirectory(testDirectory)}/start_up_info.json', - ).readAsStringSync(), - ) - as Map; - results.add(data); - } else { - currentFailures += 1; - await _flutterScreenshot( - device.deviceId, - 'screenshot_startup_failure_$currentFailures.png', - ); - i -= 1; - if (currentFailures == maxFailures) { - return TaskResult.failure('Application failed to start $maxFailures times'); - } + try { + section('Building application'); + String? applicationBinaryPath; + switch (deviceOperatingSystem) { + case DeviceOperatingSystem.android: + await flutter( + 'build', + options: [ + 'apk', + '-v', + '--profile', + '--target-platform=android-arm,android-arm64', + '--target=$target', + ], + ); + applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk'; + case DeviceOperatingSystem.androidArm: + await flutter( + 'build', + options: [ + 'apk', + '-v', + '--profile', + '--target-platform=android-arm', + '--target=$target', + ], + ); + applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk'; + case DeviceOperatingSystem.androidArm64: + await flutter( + 'build', + options: [ + 'apk', + '-v', + '--profile', + '--target-platform=android-arm64', + '--target=$target', + ], + ); + applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk'; + case DeviceOperatingSystem.fake: + case DeviceOperatingSystem.fuchsia: + case DeviceOperatingSystem.linux: + break; + case DeviceOperatingSystem.ios: + case DeviceOperatingSystem.macos: + await flutter( + 'build', + options: [ + if (deviceOperatingSystem == DeviceOperatingSystem.ios) 'ios' else 'macos', + '-v', + '--profile', + '--target=$target', + if (deviceOperatingSystem == DeviceOperatingSystem.ios) '--no-publish-port', + ], + ); + final String buildRoot = path.join(testDirectory, 'build'); + applicationBinaryPath = _findDarwinAppInBuildDirectory(buildRoot); + case DeviceOperatingSystem.windows: + await flutter( + 'build', + options: ['windows', '-v', '--profile', '--target=$target'], + ); + final String basename = path.basename(testDirectory); + final String arch = Abi.current() == Abi.windowsX64 ? 'x64' : 'arm64'; + applicationBinaryPath = path.join( + testDirectory, + 'build', + 'windows', + arch, + 'runner', + 'Profile', + '$basename.exe', + ); } - await device.uninstallApp(); + const int maxFailures = 3; + int currentFailures = 0; + for (int i = 0; i < iterations; i += 1) { + // Startup should not take more than a few minutes. After 10 minutes, + // take a screenshot to help debug. + final Timer timer = Timer(const Duration(minutes: 10), () async { + print('Startup not completed within 10 minutes. Taking a screenshot...'); + await _flutterScreenshot( + device.deviceId, + 'screenshot_startup_${DateTime.now().toLocal().toIso8601String()}.png', + ); + }); + final int result = await flutter( + 'run', + options: [ + '--no-android-gradle-daemon', + '--no-publish-port', + '--verbose', + '--profile', + '--trace-startup', + '--target=$target', + '-d', + device.deviceId, + if (applicationBinaryPath != null) '--use-application-binary=$applicationBinaryPath', + ], + environment: runEnvironment, + canFail: true, + ); + timer.cancel(); + if (result == 0) { + final Map data = + json.decode( + file( + '${testOutputDirectory(testDirectory)}/start_up_info.json', + ).readAsStringSync(), + ) + as Map; + results.add(data); + } else { + currentFailures += 1; + await _flutterScreenshot( + device.deviceId, + 'screenshot_startup_failure_$currentFailures.png', + ); + i -= 1; + if (currentFailures == maxFailures) { + return TaskResult.failure('Application failed to start $maxFailures times'); + } + } + + await device.uninstallApp(); + } + + final Map averageResults = _average(results, iterations); + + if (!reportMetrics) { + return TaskResult.success(averageResults); + } + + return TaskResult.success( + averageResults, + benchmarkScoreKeys: [ + 'timeToFirstFrameMicros', + 'timeToFirstFrameRasterizedMicros', + ], + ); + } finally { + await _resetManifest(testDirectory); } - - final Map averageResults = _average(results, iterations); - - if (!reportMetrics) { - return TaskResult.success(averageResults); - } - - return TaskResult.success( - averageResults, - benchmarkScoreKeys: ['timeToFirstFrameMicros', 'timeToFirstFrameRasterizedMicros'], - ); }); } @@ -1180,6 +1206,7 @@ class PerfTest { this.disablePartialRepaint = false, this.enableMergedPlatformThread = false, this.enableSurfaceControl = false, + this.enableLazyShaderMode = false, this.createPlatforms = const [], }) : _resultFilename = resultFilename; @@ -1202,6 +1229,7 @@ class PerfTest { this.disablePartialRepaint = false, this.enableMergedPlatformThread = false, this.enableSurfaceControl = false, + this.enableLazyShaderMode = false, this.createPlatforms = const [], }) : saveTraceFile = false, timelineFileName = null, @@ -1261,6 +1289,9 @@ class PerfTest { /// Whether to enable SurfaceControl swapchain. final bool enableSurfaceControl; + /// Whether to defer construction of all PSO objects in the Impeller backend. + final bool enableLazyShaderMode; + /// Number of seconds to time out the test after, allowing debug callbacks to run. final int? timeoutSeconds; @@ -1359,6 +1390,9 @@ class PerfTest { if (enableSurfaceControl) { _addSurfaceControlSupportToManifest(testDirectory); } + if (enableLazyShaderMode) { + _addLazyShaderMode(testDirectory); + } } if (disablePartialRepaint || enableMergedPlatformThread) { changedPlist = true; diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 7b39bb743c6..b2e13bd5240 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -41560,6 +41560,7 @@ ORIGIN: ../../../flutter/impeller/base/backend_cast.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/comparable.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/comparable.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/config.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/base/flags.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/mask.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/promise.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/promise.h + ../../../flutter/LICENSE @@ -44532,6 +44533,7 @@ FILE: ../../../flutter/impeller/base/backend_cast.h FILE: ../../../flutter/impeller/base/comparable.cc FILE: ../../../flutter/impeller/base/comparable.h FILE: ../../../flutter/impeller/base/config.h +FILE: ../../../flutter/impeller/base/flags.h FILE: ../../../flutter/impeller/base/mask.h FILE: ../../../flutter/impeller/base/promise.cc FILE: ../../../flutter/impeller/base/promise.h diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index b80fb59f22c..f25d302dcef 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -228,6 +228,9 @@ struct Settings { // Enable android surface control swapchains where supported. bool enable_surface_control = false; + // Whether to lazily initialize impeller PSO state. + bool impeller_enable_lazy_shader_mode = false; + // Log a warning during shell initialization if Impeller is not enabled. bool warn_on_impeller_opt_out = false; diff --git a/engine/src/flutter/impeller/base/BUILD.gn b/engine/src/flutter/impeller/base/BUILD.gn index aa99334f465..1e33823d78f 100644 --- a/engine/src/flutter/impeller/base/BUILD.gn +++ b/engine/src/flutter/impeller/base/BUILD.gn @@ -14,6 +14,7 @@ impeller_component("base") { "comparable.cc", "comparable.h", "config.h", + "flags.h", "mask.h", "promise.cc", "promise.h", diff --git a/engine/src/flutter/impeller/base/flags.h b/engine/src/flutter/impeller/base/flags.h new file mode 100644 index 00000000000..258a8331e2f --- /dev/null +++ b/engine/src/flutter/impeller/base/flags.h @@ -0,0 +1,16 @@ +// 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. + +#ifndef FLUTTER_IMPELLER_BASE_FLAGS_H_ +#define FLUTTER_IMPELLER_BASE_FLAGS_H_ + +namespace impeller { +struct Flags { + /// Whether to defer PSO construction until first use. Usage Will introduce + /// raster jank. + bool lazy_shader_mode = false; +}; +} // namespace impeller + +#endif // FLUTTER_IMPELLER_BASE_FLAGS_H_ diff --git a/engine/src/flutter/impeller/entity/contents/content_context.cc b/engine/src/flutter/impeller/entity/contents/content_context.cc index 980656d1120..376836fe8f7 100644 --- a/engine/src/flutter/impeller/entity/contents/content_context.cc +++ b/engine/src/flutter/impeller/entity/contents/content_context.cc @@ -216,7 +216,6 @@ void ContentContextOptions::ApplyToPipelineDescriptor( } desc.SetPrimitiveType(primitive_type); - desc.SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill); } @@ -283,11 +282,13 @@ ContentContext::ContentContext( desc.format = PixelFormat::kR8G8B8A8UNormInt; desc.size = ISize{1, 1}; empty_texture_ = GetContext()->GetResourceAllocator()->CreateTexture(desc); - auto data = Color::BlackTransparent().ToR8G8B8A8(); - auto cmd_buffer = GetContext()->CreateCommandBuffer(); - auto blit_pass = cmd_buffer->CreateBlitPass(); - auto& host_buffer = GetTransientsBuffer(); - auto buffer_view = host_buffer.Emplace(data); + + std::array data = Color::BlackTransparent().ToR8G8B8A8(); + std::shared_ptr cmd_buffer = + GetContext()->CreateCommandBuffer(); + std::shared_ptr blit_pass = cmd_buffer->CreateBlitPass(); + HostBuffer& host_buffer = GetTransientsBuffer(); + BufferView buffer_view = host_buffer.Emplace(data); blit_pass->AddCopy(buffer_view, empty_texture_); if (!blit_pass->EncodeCommands() || !GetContext() @@ -383,9 +384,14 @@ ContentContext::ContentContext( } clip_pipeline_descriptor->SetColorAttachmentDescriptors( std::move(clip_color_attachments)); - clip_pipelines_.SetDefault( - options, - std::make_unique(*context_, clip_pipeline_descriptor)); + if (GetContext()->GetFlags().lazy_shader_mode) { + clip_pipelines_.SetDefaultDescriptor(clip_pipeline_descriptor); + clip_pipelines_.SetDefault(options, nullptr); + } else { + clip_pipelines_.SetDefault( + options, + std::make_unique(*context_, clip_pipeline_descriptor)); + } texture_downsample_pipelines_.CreateDefault( *context_, options_no_msaa_no_depth_stencil); rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); @@ -671,7 +677,9 @@ void ContentContext::ClearCachedRuntimeEffectPipeline( } void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const { - TRACE_EVENT0("flutter", "InitializeCommonlyUsedShadersIfNeeded"); + if (GetContext()->GetFlags().lazy_shader_mode) { + return; + } GetContext()->InitializeCommonlyUsedShadersIfNeeded(); } diff --git a/engine/src/flutter/impeller/entity/contents/content_context.h b/engine/src/flutter/impeller/entity/contents/content_context.h index 03487affeb7..5daf9c19304 100644 --- a/engine/src/flutter/impeller/entity/contents/content_context.h +++ b/engine/src/flutter/impeller/entity/contents/content_context.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "flutter/fml/logging.h" #include "flutter/fml/status_or.h" @@ -521,7 +522,13 @@ class ContentContext { void SetDefault(const ContentContextOptions& options, std::unique_ptr pipeline) { default_options_ = options; - Set(options, std::move(pipeline)); + if (pipeline) { + Set(options, std::move(pipeline)); + } + } + + void SetDefaultDescriptor(std::optional desc) { + desc_ = std::move(desc); } void CreateDefault(const Context& context, @@ -534,7 +541,13 @@ class ContentContext { return; } options.ApplyToPipelineDescriptor(*desc); - SetDefault(options, std::make_unique(context, desc)); + desc_ = desc; + if (context.GetFlags().lazy_shader_mode) { + SetDefault(options, nullptr); + } else { + SetDefault(options, std::make_unique(context, desc_, + /*async=*/true)); + } } PipelineHandleT* Get(const ContentContextOptions& options) const { @@ -547,16 +560,29 @@ class ContentContext { return nullptr; } - PipelineHandleT* GetDefault() const { + bool IsDefault(const ContentContextOptions& opts) { + return default_options_.has_value() && + opts.ToKey() == default_options_.value().ToKey(); + } + + PipelineHandleT* GetDefault(const Context& context) { if (!default_options_.has_value()) { return nullptr; } + PipelineHandleT* result = Get(default_options_.value()); + if (result != nullptr) { + return result; + } + SetDefault( + default_options_.value(), + std::make_unique(context, desc_, /*async=*/false)); return Get(default_options_.value()); } size_t GetPipelineCount() const { return pipelines_.size(); } private: + std::optional desc_; std::optional default_options_; std::vector>> pipelines_; @@ -688,7 +714,10 @@ class ContentContext { return found; } - RenderPipelineHandleT* default_handle = container.GetDefault(); + RenderPipelineHandleT* default_handle = container.GetDefault(*GetContext()); + if (container.IsDefault(opts)) { + return default_handle; + } // The default must always be initialized in the constructor. FML_CHECK(default_handle != nullptr); diff --git a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc index d4138d111e9..e5d446b729e 100644 --- a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc +++ b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc @@ -134,7 +134,7 @@ std::shared_ptr PlaygroundImplGLES::GetContext() const { } auto context = ContextGLES::Create( - std::move(gl), ShaderLibraryMappingsForPlayground(), true); + Flags{}, std::move(gl), ShaderLibraryMappingsForPlayground(), true); if (!context) { FML_LOG(ERROR) << "Could not create context."; return nullptr; diff --git a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm index 4948eaee810..7d00c4aeb38 100644 --- a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm +++ b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -77,8 +77,8 @@ PlaygroundImplMTL::PlaygroundImplMTL(PlaygroundSwitches switches) } auto context = ContextMTL::Create( - ShaderLibraryMappingsForPlayground(), is_gpu_disabled_sync_switch_, - "Playground Library", + impeller::Flags{}, ShaderLibraryMappingsForPlayground(), + is_gpu_disabled_sync_switch_, "Playground Library", switches.enable_wide_gamut ? std::optional(PixelFormat::kB10G10R10A10XR) : std::nullopt); diff --git a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc index 95e695bf302..57ae5be45a3 100644 --- a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -96,7 +96,7 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) context_settings.fatal_missing_validations = switches_.enable_vulkan_validation; - auto context_vk = ContextVK::Create(std::move(context_settings)); + auto context_vk = ContextVK::Create(Flags{}, std::move(context_settings)); if (!context_vk || !context_vk->IsValid()) { VALIDATION_LOG << "Could not create Vulkan context in the playground."; return; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc index f8cf2841cd0..084b74d4e0e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc @@ -19,17 +19,20 @@ namespace impeller { std::shared_ptr ContextGLES::Create( + const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, bool enable_gpu_tracing) { - return std::shared_ptr( - new ContextGLES(std::move(gl), shader_libraries, enable_gpu_tracing)); + return std::shared_ptr(new ContextGLES( + flags, std::move(gl), shader_libraries, enable_gpu_tracing)); } ContextGLES::ContextGLES( + const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries_mappings, - bool enable_gpu_tracing) { + bool enable_gpu_tracing) + : Context(flags) { reactor_ = std::make_shared(std::move(gl)); if (!reactor_->IsValid()) { VALIDATION_LOG << "Could not create valid reactor."; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h index 2e7e87a01c9..44f2f873bb2 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h @@ -25,6 +25,7 @@ class ContextGLES final : public Context, public std::enable_shared_from_this { public: static std::shared_ptr Create( + const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, bool enable_gpu_tracing); @@ -60,6 +61,7 @@ class ContextGLES final : public Context, bool is_valid_ = false; ContextGLES( + const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, bool enable_gpu_tracing); diff --git a/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.h b/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.h index a0af10d442a..712cf7c1f69 100644 --- a/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.h +++ b/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.h @@ -67,16 +67,19 @@ class ContextMTL final : public Context, public std::enable_shared_from_this { public: static std::shared_ptr Create( + const Flags& flags, const std::vector& shader_library_paths, std::shared_ptr is_gpu_disabled_sync_switch); static std::shared_ptr Create( + const Flags& flags, const std::vector>& shader_libraries_data, std::shared_ptr is_gpu_disabled_sync_switch, const std::string& label, std::optional pixel_format_override = std::nullopt); static std::shared_ptr Create( + const Flags& flags, id device, id command_queue, const std::vector>& shader_libraries_data, @@ -178,7 +181,8 @@ class ContextMTL final : public Context, #endif // IMPELLER_DEBUG bool is_valid_ = false; - ContextMTL(id device, + ContextMTL(const Flags& flags, + id device, id command_queue, NSArray>* shader_libraries, std::shared_ptr is_gpu_disabled_sync_switch, diff --git a/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm b/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm index 7421a330bb9..30e509bdd87 100644 --- a/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm +++ b/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm @@ -86,12 +86,14 @@ static std::unique_ptr InferMetalCapabilities( } ContextMTL::ContextMTL( + const Flags& flags, id device, id command_queue, NSArray>* shader_libraries, std::shared_ptr is_gpu_disabled_sync_switch, std::optional pixel_format_override) - : device_(device), + : Context(flags), + device_(device), command_queue_(command_queue), is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) { // Validate device. @@ -235,6 +237,7 @@ static id CreateMetalCommandQueue(id device) { } std::shared_ptr ContextMTL::Create( + const Flags& flags, const std::vector& shader_library_paths, std::shared_ptr is_gpu_disabled_sync_switch) { auto device = CreateMetalDevice(); @@ -243,7 +246,7 @@ std::shared_ptr ContextMTL::Create( return nullptr; } auto context = std::shared_ptr(new ContextMTL( - device, command_queue, + flags, device, command_queue, MTLShaderLibraryFromFilePaths(device, shader_library_paths), std::move(is_gpu_disabled_sync_switch))); if (!context->IsValid()) { @@ -254,6 +257,7 @@ std::shared_ptr ContextMTL::Create( } std::shared_ptr ContextMTL::Create( + const Flags& flags, const std::vector>& shader_libraries_data, std::shared_ptr is_gpu_disabled_sync_switch, const std::string& library_label, @@ -264,7 +268,7 @@ std::shared_ptr ContextMTL::Create( return nullptr; } auto context = std::shared_ptr(new ContextMTL( - device, command_queue, + flags, device, command_queue, MTLShaderLibraryFromFileData(device, shader_libraries_data, library_label), std::move(is_gpu_disabled_sync_switch), pixel_format_override)); @@ -276,13 +280,14 @@ std::shared_ptr ContextMTL::Create( } std::shared_ptr ContextMTL::Create( + const Flags& flags, id device, id command_queue, const std::vector>& shader_libraries_data, std::shared_ptr is_gpu_disabled_sync_switch, const std::string& library_label) { auto context = std::shared_ptr( - new ContextMTL(device, command_queue, + new ContextMTL(flags, device, command_queue, MTLShaderLibraryFromFileData(device, shader_libraries_data, library_label), std::move(is_gpu_disabled_sync_switch))); diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc index 4e40da07f59..fe1ff030cba 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc @@ -34,7 +34,7 @@ std::shared_ptr CreateContext() { settings.enable_gpu_tracing = false; settings.enable_surface_control = false; - return ContextVK::Create(std::move(settings)); + return ContextVK::Create(impeller::Flags{}, std::move(settings)); } TEST(AndroidVulkanTest, CanImportRGBA) { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc index fd3955bcce9..9956c84745a 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc @@ -102,8 +102,9 @@ static std::optional PickQueue(const vk::PhysicalDevice& device, return std::nullopt; } -std::shared_ptr ContextVK::Create(Settings settings) { - auto context = std::shared_ptr(new ContextVK()); +std::shared_ptr ContextVK::Create(const Flags& flags, + Settings settings) { + auto context = std::shared_ptr(new ContextVK(flags)); context->Setup(std::move(settings)); if (!context->IsValid()) { return nullptr; @@ -127,7 +128,8 @@ uint64_t CalculateHash(void* ptr) { } } // namespace -ContextVK::ContextVK() : hash_(CalculateHash(this)) {} +ContextVK::ContextVK(const Flags& flags) + : Context(flags), hash_(CalculateHash(this)) {} ContextVK::~ContextVK() { if (device_holder_ && device_holder_->device) { @@ -150,16 +152,6 @@ void ContextVK::Setup(Settings settings) { raster_message_loop_ = fml::ConcurrentMessageLoop::Create( ChooseThreadCountForWorkers(std::thread::hardware_concurrency())); - raster_message_loop_->PostTaskToAllWorkers([]() { - // Currently we only use the worker task pool for small parts of a frame - // workload, if this changes this setting may need to be adjusted. - fml::RequestAffinity(fml::CpuAffinity::kNotPerformance); -#ifdef FML_OS_ANDROID - if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) { - VALIDATION_LOG << "Failed to set Workers task runner priority"; - } -#endif // FML_OS_ANDROID - }); auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER; dispatcher.init(settings.proc_address_callback); diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h index 6fb3779957d..86607633118 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h @@ -98,7 +98,8 @@ class ContextVK final : public Context, /// Visible for testing. static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency); - static std::shared_ptr Create(Settings settings); + static std::shared_ptr Create(const Flags& flags, + Settings settings); uint64_t GetHash() const { return hash_; } @@ -300,7 +301,7 @@ class ContextVK final : public Context, bool is_valid_ = false; - ContextVK(); + explicit ContextVK(const Flags& flags); void Setup(Settings settings); diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc index 33cbc58e856..e14320f134d 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -14,7 +14,7 @@ namespace impeller { SurfaceContextVK::SurfaceContextVK(const std::shared_ptr& parent) - : parent_(parent) {} + : Context(parent->GetFlags()), parent_(parent) {} SurfaceContextVK::~SurfaceContextVK() = default; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc index 0ea8beaec33..c3a3540f008 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -955,7 +955,8 @@ std::shared_ptr MockVulkanContextBuilder::Build() { g_format_properties_callback = format_properties_callback_; g_physical_device_properties_callback = physical_properties_callback_; settings.embedder_data = embedder_data_; - std::shared_ptr result = ContextVK::Create(std::move(settings)); + std::shared_ptr result = + ContextVK::Create(Flags{}, std::move(settings)); return result; } diff --git a/engine/src/flutter/impeller/renderer/context.cc b/engine/src/flutter/impeller/renderer/context.cc index a741ce1bb9a..cab4104b3b8 100644 --- a/engine/src/flutter/impeller/renderer/context.cc +++ b/engine/src/flutter/impeller/renderer/context.cc @@ -10,7 +10,7 @@ namespace impeller { Context::~Context() = default; -Context::Context() = default; +Context::Context(const Flags& flags) : flags_(flags) {} bool Context::UpdateOffscreenLayerPixelFormat(PixelFormat format) { return false; diff --git a/engine/src/flutter/impeller/renderer/context.h b/engine/src/flutter/impeller/renderer/context.h index b7e8dee5742..36e88e9bf58 100644 --- a/engine/src/flutter/impeller/renderer/context.h +++ b/engine/src/flutter/impeller/renderer/context.h @@ -9,6 +9,7 @@ #include #include "fml/closure.h" +#include "impeller/base/flags.h" #include "impeller/core/allocator.h" #include "impeller/core/formats.h" #include "impeller/renderer/capabilities.h" @@ -245,9 +246,12 @@ class Context { /// @brief Submit the command buffer that renders to the onscreen surface. virtual bool SubmitOnscreen(std::shared_ptr cmd_buffer); - protected: - Context(); + const Flags& GetFlags() const { return flags_; } + protected: + explicit Context(const Flags& flags); + + Flags flags_; std::vector> per_frame_task_; private: diff --git a/engine/src/flutter/impeller/renderer/pipeline.cc b/engine/src/flutter/impeller/renderer/pipeline.cc index 302b62d7360..79adb18078f 100644 --- a/engine/src/flutter/impeller/renderer/pipeline.cc +++ b/engine/src/flutter/impeller/renderer/pipeline.cc @@ -23,13 +23,15 @@ Pipeline::~Pipeline() = default; PipelineFuture CreatePipelineFuture( const Context& context, - std::optional desc) { + std::optional desc, + bool async) { if (!context.IsValid()) { return {desc, RealizedFuture>>( nullptr)}; } - return context.GetPipelineLibrary()->GetPipeline(std::move(desc)); + return context.GetPipelineLibrary()->GetPipeline(std::move(desc), + /*async=*/async); } PipelineFuture CreatePipelineFuture( diff --git a/engine/src/flutter/impeller/renderer/pipeline.h b/engine/src/flutter/impeller/renderer/pipeline.h index a6bbd982aea..edaf7821d86 100644 --- a/engine/src/flutter/impeller/renderer/pipeline.h +++ b/engine/src/flutter/impeller/renderer/pipeline.h @@ -90,9 +90,18 @@ using PipelineRef = raw_ptr>; extern template class Pipeline; extern template class Pipeline; +/// @brief Create a pipeline for the given descriptor. +/// +/// If `async` is true, the compilation is performed on a worker thread. The +/// returned future will complete once that work is done. If `async` is false, +/// the work is done on the current thread. +/// +/// It is more performant to set async to false than to spawn a +/// worker and immediately block on the future completion. PipelineFuture CreatePipelineFuture( const Context& context, - std::optional desc); + std::optional desc, + bool async = true); PipelineFuture CreatePipelineFuture( const Context& context, @@ -116,14 +125,17 @@ class RenderPipelineHandle { using FragmentShader = FragmentShader_; using Builder = PipelineBuilder; - explicit RenderPipelineHandle(const Context& context) + explicit RenderPipelineHandle(const Context& context, bool async = true) : RenderPipelineHandle(CreatePipelineFuture( context, - Builder::MakeDefaultPipelineDescriptor(context))) {} + Builder::MakeDefaultPipelineDescriptor(context), + async)) {} explicit RenderPipelineHandle(const Context& context, - std::optional desc) - : RenderPipelineHandle(CreatePipelineFuture(context, desc)) {} + std::optional desc, + bool async = true) + : RenderPipelineHandle( + CreatePipelineFuture(context, desc, /*async=*/async)) {} explicit RenderPipelineHandle(PipelineFuture future) : pipeline_future_(std::move(future)) {} diff --git a/engine/src/flutter/impeller/renderer/testing/mocks.h b/engine/src/flutter/impeller/renderer/testing/mocks.h index 1b1171a7318..2cac383196f 100644 --- a/engine/src/flutter/impeller/renderer/testing/mocks.h +++ b/engine/src/flutter/impeller/renderer/testing/mocks.h @@ -140,6 +140,8 @@ class MockCommandBuffer : public CommandBuffer { class MockImpellerContext : public Context { public: + MockImpellerContext() : Context(Flags{}) {} + MOCK_METHOD(Context::BackendType, GetBackendType, (), (const, override)); MOCK_METHOD(std::string, DescribeGpuModel, (), (const, override)); diff --git a/engine/src/flutter/impeller/toolkit/interop/backend/gles/context_gles.cc b/engine/src/flutter/impeller/toolkit/interop/backend/gles/context_gles.cc index 1199410a0b3..cbdfc78cdc6 100644 --- a/engine/src/flutter/impeller/toolkit/interop/backend/gles/context_gles.cc +++ b/engine/src/flutter/impeller/toolkit/interop/backend/gles/context_gles.cc @@ -27,8 +27,8 @@ ScopedObject ContextGLES::Create( impeller_framebuffer_blend_shaders_gles_data, impeller_framebuffer_blend_shaders_gles_length), }; - auto impeller_context = impeller::ContextGLES::Create(std::move(proc_table), - shader_mappings, false); + auto impeller_context = impeller::ContextGLES::Create( + Flags{}, std::move(proc_table), shader_mappings, false); if (!impeller_context) { VALIDATION_LOG << "Could not create Impeller context."; return {}; diff --git a/engine/src/flutter/impeller/toolkit/interop/backend/metal/context_mtl.mm b/engine/src/flutter/impeller/toolkit/interop/backend/metal/context_mtl.mm index 21b7ddc52f1..755beca4829 100644 --- a/engine/src/flutter/impeller/toolkit/interop/backend/metal/context_mtl.mm +++ b/engine/src/flutter/impeller/toolkit/interop/backend/metal/context_mtl.mm @@ -28,9 +28,9 @@ CreateShaderLibraryMappings() { ScopedObject ContextMTL::Create() { auto impeller_context = - impeller::ContextMTL::Create(CreateShaderLibraryMappings(), // - std::make_shared(), // - "Impeller" // + impeller::ContextMTL::Create(Flags{}, CreateShaderLibraryMappings(), // + std::make_shared(), // + "Impeller" // ); if (!impeller_context) { VALIDATION_LOG << "Could not create Impeller context."; diff --git a/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc index 52ad4f5d99a..92e192010ce 100644 --- a/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc @@ -53,8 +53,8 @@ ScopedObject ContextVK::Create(const Settings& settings) { impeller_settings.enable_validation = true; sContextVKProcAddressCallback = settings.instance_proc_address_callback; impeller_settings.proc_address_callback = ContextVKGetInstanceProcAddress; - auto impeller_context = - impeller::ContextVK::Create(std::move(impeller_settings)); + auto impeller_context = impeller::ContextVK::Create( + impeller::Flags{}, std::move(impeller_settings)); sContextVKProcAddressCallback = nullptr; if (!impeller_context) { VALIDATION_LOG << "Could not create Impeller context."; diff --git a/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc b/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc index c89386a4874..5e021c33908 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc +++ b/engine/src/flutter/lib/ui/painting/image_decoder_unittests.cc @@ -40,7 +40,7 @@ namespace impeller { class TestImpellerContext : public impeller::Context { public: - TestImpellerContext() = default; + TestImpellerContext() : impeller::Context(impeller::Flags{}) {} BackendType GetBackendType() const override { return BackendType::kMetal; } diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc b/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc index 6719673f665..fbb629e464d 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc +++ b/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc @@ -61,7 +61,7 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( return; } impeller_context_ = impeller::ContextGLES::Create( - std::move(gl), ShaderLibraryMappings(), true); + impeller::Flags{}, std::move(gl), ShaderLibraryMappings(), true); } } diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index 5083992be65..ff45ade1e43 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -532,6 +532,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.merged_platform_ui_thread = !command_line.HasOption( FlagForSwitch(Switch::DisableMergedPlatformUIThread)); + settings.impeller_enable_lazy_shader_mode = + command_line.HasOption(FlagForSwitch(Switch::ImpellerLazyShaderMode)); + return settings; } diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index b0878ab349d..02c7d1ec759 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -300,6 +300,10 @@ DEF_SWITCH(DisableMergedPlatformUIThread, DEF_SWITCH(EnableAndroidSurfaceControl, "enable-surface-control", "Enable the SurfaceControl backed swapchain when supported.") +DEF_SWITCH(ImpellerLazyShaderMode, + "impeller-lazy-shader-mode", + "Whether to defer initialization of all required PSOs for the " + "Impeller backend. Defaults to false.") DEF_SWITCHES_END void PrintUsage(const std::string& executable_name); diff --git a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm index dff019c873e..6ea12464434 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm @@ -54,7 +54,8 @@ static std::shared_ptr CreateImpellerContext() { impeller_framebuffer_blend_shaders_length), }; auto sync_switch = std::make_shared(false); - return impeller::ContextMTL::Create(shader_mappings, sync_switch, "Impeller Library"); + return impeller::ContextMTL::Create(impeller::Flags{}, shader_mappings, sync_switch, + "Impeller Library"); } TEST(GPUSurfaceMetalImpeller, InvalidImpellerContextCreatesCausesSurfaceToBeInvalid) { diff --git a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc index 1c670561324..f67f5899bc1 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc @@ -79,7 +79,7 @@ static std::shared_ptr CreateImpellerContext( }; auto context = impeller::ContextGLES::Create( - std::move(proc_table), + impeller::Flags{}, std::move(proc_table), is_gles3 ? gles3_shader_mappings : gles2_shader_mappings, enable_gpu_tracing); if (!context) { diff --git a/engine/src/flutter/shell/platform/android/android_context_gl_unittests.cc b/engine/src/flutter/shell/platform/android/android_context_gl_unittests.cc index 049f3f0745f..d19d2d2cb9f 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl_unittests.cc +++ b/engine/src/flutter/shell/platform/android/android_context_gl_unittests.cc @@ -36,7 +36,7 @@ TaskRunners MakeTaskRunners(const std::string& thread_label, class TestImpellerContext : public impeller::Context { public: - TestImpellerContext() {} + TestImpellerContext() : Context(impeller::Flags{}) {} ~TestImpellerContext() {} diff --git a/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc index 0bf90af09b9..e2b3edefcab 100644 --- a/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc @@ -49,7 +49,11 @@ static std::shared_ptr CreateImpellerContext( settings.enable_gpu_tracing = p_settings.enable_gpu_tracing; settings.enable_surface_control = p_settings.enable_surface_control; - auto context = impeller::ContextVK::Create(std::move(settings)); + auto context = impeller::ContextVK::Create( + impeller::Flags{ + .lazy_shader_mode = p_settings.enable_lazy_shader_mode, + }, + std::move(settings)); if (!p_settings.quiet) { if (context && impeller::CapabilitiesVK::Cast(*context->GetCapabilities()) diff --git a/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.h b/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.h index b962270321f..8921ee0a8e2 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.h +++ b/engine/src/flutter/shell/platform/android/android_surface_vk_impeller.h @@ -7,6 +7,7 @@ #include "flutter/fml/concurrent_message_loop.h" #include "flutter/fml/macros.h" +#include "flutter/impeller/display_list/aiks_context.h" #include "flutter/impeller/renderer/backend/vulkan/surface_context_vk.h" #include "flutter/shell/platform/android/android_context_vk_impeller.h" #include "flutter/shell/platform/android/surface/android_native_window.h" diff --git a/engine/src/flutter/shell/platform/android/context/android_context.h b/engine/src/flutter/shell/platform/android/context/android_context.h index 7eec67b4210..0e4ea0cbdca 100644 --- a/engine/src/flutter/shell/platform/android/context/android_context.h +++ b/engine/src/flutter/shell/platform/android/context/android_context.h @@ -25,6 +25,7 @@ class AndroidContext { bool enable_validation = false; bool enable_gpu_tracing = false; bool enable_surface_control = false; + bool enable_lazy_shader_mode = false; bool quiet = false; }; diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index b269697e4a4..dedd4de0c0d 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -51,6 +51,8 @@ public class FlutterLoader { "io.flutter.embedding.android.DisableMergedPlatformUIThread"; private static final String ENABLE_SURFACE_CONTROL = "io.flutter.embedding.android.EnableSurfaceControl"; + private static final String IMPELLER_LAZY_SHADER_MODE = + "io.flutter.embedding.android.ImpellerLazyShaderInitialization"; /** * Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's @@ -377,6 +379,9 @@ public class FlutterLoader { if (backend != null) { shellArgs.add("--impeller-backend=" + backend); } + if (metaData.getBoolean(IMPELLER_LAZY_SHADER_MODE)) { + shellArgs.add("--impeller-lazy-shader-mode"); + } } final String leakVM = isLeakVM(metaData) ? "true" : "false"; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.cc b/engine/src/flutter/shell/platform/android/platform_view_android.cc index fbcfdcfb700..eb51dbca1da 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -26,6 +26,7 @@ #include "flutter/shell/platform/android/surface_texture_external_texture_gl_skia.h" #include "flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.h" #include "fml/logging.h" +#include "impeller/display_list/aiks_context.h" #if IMPELLER_ENABLE_VULKAN // b/258506856 for why this is behind an if #include "flutter/shell/platform/android/android_surface_vk_impeller.h" #include "flutter/shell/platform/android/image_external_texture_vk_impeller.h" @@ -52,14 +53,19 @@ AndroidContext::ContextSettings CreateContextSettings( settings.enable_gpu_tracing = p_settings.enable_vulkan_gpu_tracing; settings.enable_validation = p_settings.enable_vulkan_validation; settings.enable_surface_control = p_settings.enable_surface_control; + settings.enable_lazy_shader_mode = + p_settings.impeller_enable_lazy_shader_mode; return settings; } } // namespace AndroidSurfaceFactoryImpl::AndroidSurfaceFactoryImpl( const std::shared_ptr& context, - bool enable_impeller) - : android_context_(context), enable_impeller_(enable_impeller) {} + bool enable_impeller, + bool lazy_shader_mode) + : android_context_(context), + enable_impeller_(enable_impeller), + lazy_shader_mode_(lazy_shader_mode) {} AndroidSurfaceFactoryImpl::~AndroidSurfaceFactoryImpl() = default; @@ -132,8 +138,10 @@ PlatformViewAndroid::PlatformViewAndroid( FML_CHECK(android_context_->IsValid()) << "Could not create surface from invalid Android context."; surface_factory_ = std::make_shared( - android_context_, // - delegate.OnPlatformViewGetSettings().enable_impeller // + android_context_, // + delegate.OnPlatformViewGetSettings().enable_impeller, // + delegate.OnPlatformViewGetSettings() + .impeller_enable_lazy_shader_mode // ); android_surface_ = surface_factory_->CreateSurface(); android_use_new_platform_view_ = diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.h b/engine/src/flutter/shell/platform/android/platform_view_android.h index 726159d4924..c1e40a31303 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.h +++ b/engine/src/flutter/shell/platform/android/platform_view_android.h @@ -26,7 +26,8 @@ namespace flutter { class AndroidSurfaceFactoryImpl : public AndroidSurfaceFactory { public: AndroidSurfaceFactoryImpl(const std::shared_ptr& context, - bool enable_impeller); + bool enable_impeller, + bool lazy_shader_mode); ~AndroidSurfaceFactoryImpl() override; @@ -35,6 +36,7 @@ class AndroidSurfaceFactoryImpl : public AndroidSurfaceFactory { private: const std::shared_ptr& android_context_; const bool enable_impeller_; + const bool lazy_shader_mode_; }; class PlatformViewAndroid final : public PlatformView { diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java index 373fca59924..c7bfc8406bc 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java @@ -315,6 +315,35 @@ public class FlutterLoaderTest { assertTrue(arguments.contains(disabledControlArg)); } + @Test + public void itSetsShaderInitModeFromMetaData() { + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI); + Bundle metaData = new Bundle(); + metaData.putBoolean("io.flutter.embedding.android.ImpellerLazyShaderInitialization", true); + ctx.getApplicationInfo().metaData = metaData; + + FlutterLoader.Settings settings = new FlutterLoader.Settings(); + assertFalse(flutterLoader.initialized()); + flutterLoader.startInitialization(ctx, settings); + flutterLoader.ensureInitializationComplete(ctx, null); + shadowOf(getMainLooper()).idle(); + + final String shaderModeArg = "--impeller-lazy-shader-mode"; + ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class); + verify(mockFlutterJNI, times(1)) + .init( + eq(ctx), + shellArgsCaptor.capture(), + anyString(), + anyString(), + anyString(), + anyLong(), + anyInt()); + List arguments = Arrays.asList(shellArgsCaptor.getValue()); + assertTrue(arguments.contains(shaderModeArg)); + } + @Test @TargetApi(API_LEVELS.API_23) @Config(sdk = API_LEVELS.API_23) diff --git a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm index 3e41eedf91e..36d95acb092 100644 --- a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm +++ b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm @@ -25,8 +25,8 @@ static std::shared_ptr CreateImpellerContext( std::make_shared(impeller_framebuffer_blend_shaders_data, impeller_framebuffer_blend_shaders_length), }; - return impeller::ContextMTL::Create(shader_mappings, is_gpu_disabled_sync_switch, - "Impeller Library"); + return impeller::ContextMTL::Create(impeller::Flags{}, shader_mappings, + is_gpu_disabled_sync_switch, "Impeller Library"); } @implementation FlutterDarwinContextMetalImpeller diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureTest.mm index 09fcb20a40c..4159e966278 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureTest.mm @@ -41,7 +41,8 @@ static std::shared_ptr CreateImpellerContext() { impeller_framebuffer_blend_shaders_length), }; auto sync_switch = std::make_shared(false); - return impeller::ContextMTL::Create(shader_mappings, sync_switch, "Impeller Library"); + return impeller::ContextMTL::Create(impeller::Flags{}, shader_mappings, sync_switch, + "Impeller Library"); } @interface TestExternalTexture : NSObject diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc index 19713b02613..452ac9fe4ac 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc @@ -6,6 +6,7 @@ #include +#include "impeller/display_list/aiks_context.h" #include "impeller/entity/gles/entity_shaders_gles.h" #include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" #include "impeller/entity/gles/modern_shaders_gles.h" @@ -80,7 +81,8 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( } impeller_context_ = impeller::ContextGLES::Create( - std::move(gl), shader_mappings, /*enable_gpu_tracing=*/false); + impeller::Flags{}, std::move(gl), shader_mappings, + /*enable_gpu_tracing=*/false); if (!impeller_context_) { FML_LOG(ERROR) << "Could not create Impeller context."; diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_metal_impeller.mm b/engine/src/flutter/shell/platform/embedder/embedder_surface_metal_impeller.mm index 65f239211c2..8c2ffbd8b54 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_metal_impeller.mm +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_metal_impeller.mm @@ -41,6 +41,7 @@ EmbedderSurfaceMetalImpeller::EmbedderSurfaceMetalImpeller( impeller_framebuffer_blend_shaders_length), }; context_ = impeller::ContextMTL::Create( + impeller::Flags{}, (__bridge id)device, // device (__bridge id)command_queue, // command_queue shader_mappings, // shader_libraries_data diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc index a590898843e..6a55973a80d 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc @@ -10,6 +10,7 @@ #include "flutter/impeller/entity/vk/framebuffer_blend_shaders_vk.h" #include "flutter/impeller/entity/vk/modern_shaders_vk.h" #include "flutter/shell/gpu/gpu_surface_vulkan.h" +#include "impeller/display_list/aiks_context.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "include/gpu/ganesh/GrDirectContext.h" #include "shell/gpu/gpu_surface_vulkan_impeller.h" @@ -70,7 +71,8 @@ EmbedderSurfaceVulkanImpeller::EmbedderSurfaceVulkanImpeller( } settings.embedder_data = data; - context_ = impeller::ContextVK::Create(std::move(settings)); + context_ = + impeller::ContextVK::Create(impeller::Flags{}, std::move(settings)); if (!context_) { FML_LOG(ERROR) << "Failed to initialize Vulkan Context."; return; diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc index c29fe2e6097..9a740e342e7 100644 --- a/engine/src/flutter/shell/testing/tester_main.cc +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -35,6 +35,7 @@ #if ALLOW_IMPELLER #include // nogncheck +#include "impeller/display_list/aiks_context.h" // nogncheck #include "impeller/entity/vk/entity_shaders_vk.h" // nogncheck #include "impeller/entity/vk/framebuffer_blend_shaders_vk.h" // nogncheck #include "impeller/entity/vk/modern_shaders_vk.h" // nogncheck @@ -74,7 +75,10 @@ bool ImpellerVulkanContextHolder::Initialize(bool enable_validation) { context_settings.cache_directory = fml::paths::GetCachesDirectory(); context_settings.enable_validation = enable_validation; - context = impeller::ContextVK::Create(std::move(context_settings)); + context = impeller::ContextVK::Create( + // Enable lazy shader mode for faster test execution as most tests + // will never render anything at all. + impeller::Flags{.lazy_shader_mode = true}, std::move(context_settings)); if (!context || !context->IsValid()) { VALIDATION_LOG << "Could not create Vulkan context."; return false;