Reland: [Impeller] add a configuration option that allows defering all PSO construction until needed. (#165622)

The cost of bootstapping the initial PSOs can regress cold startup time
for customer money. As an experiment, attempt to defer PSO construction
to skia like.

---------

Co-authored-by: Aaron Clarke <aaclarke@google.com>
Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
This commit is contained in:
Jonah Williams 2025-03-21 18:34:05 -07:00 committed by GitHub
parent a4b982738e
commit 31ff6497f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 424 additions and 211 deletions

View File

@ -2730,6 +2730,17 @@ targets:
["devicelab", "android", "linux"] ["devicelab", "android", "linux"]
task_name: flutter_gallery__start_up 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 # linux mokey benchmark
- name: Linux_mokey flutter_gallery__start_up_delayed - name: Linux_mokey flutter_gallery__start_up_delayed
recipe: devicelab/devicelab_drone recipe: devicelab/devicelab_drone

View File

@ -45,6 +45,7 @@
/dev/devicelab/bin/tasks/flutter_gallery__image_cache_memory.dart @jtmcdole @flutter/engine /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__memory_nav.dart @jtmcdole @flutter/engine
/dev/devicelab/bin/tasks/flutter_gallery__start_up.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__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.dart @jtmcdole @flutter/engine
/dev/devicelab/bin/tasks/flutter_gallery__transition_perf_e2e.dart @jtmcdole @flutter/engine /dev/devicelab/bin/tasks/flutter_gallery__transition_perf_e2e.dart @jtmcdole @flutter/engine

View File

@ -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<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.android;
await task(createFlutterGalleryStartupTest(enableLazyShaderMode: true));
}

View File

@ -270,11 +270,13 @@ TaskFunction createOpenPayScrollPerfTest({bool measureCpuGpu = true}) {
TaskFunction createFlutterGalleryStartupTest({ TaskFunction createFlutterGalleryStartupTest({
String target = 'lib/main.dart', String target = 'lib/main.dart',
Map<String, String>? runEnvironment, Map<String, String>? runEnvironment,
bool enableLazyShaderMode = false,
}) { }) {
return StartupTest( return StartupTest(
'${flutterDirectory.path}/dev/integration_tests/flutter_gallery', '${flutterDirectory.path}/dev/integration_tests/flutter_gallery',
target: target, target: target,
runEnvironment: runEnvironment, runEnvironment: runEnvironment,
enableLazyShaderMode: enableLazyShaderMode,
).run; ).run;
} }
@ -840,6 +842,17 @@ void _addVulkanGPUTracingToManifest(String testDirectory) {
_addMetadataToManifest(testDirectory, keyPairs); _addMetadataToManifest(testDirectory, keyPairs);
} }
/// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml'
/// <meta-data
/// android:name="io.flutter.embedding.android.ImpellerShaderMode"
/// android:value="lazy" />
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' /// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml'
/// and adds the following entry to the application. /// and adds the following entry to the application.
/// <meta-data /// <meta-data
@ -881,10 +894,12 @@ class StartupTest {
this.reportMetrics = true, this.reportMetrics = true,
this.target = 'lib/main.dart', this.target = 'lib/main.dart',
this.runEnvironment, this.runEnvironment,
this.enableLazyShaderMode = false,
}); });
final String testDirectory; final String testDirectory;
final bool reportMetrics; final bool reportMetrics;
final bool enableLazyShaderMode;
final String target; final String target;
final Map<String, String>? runEnvironment; final Map<String, String>? runEnvironment;
@ -895,6 +910,11 @@ class StartupTest {
const int iterations = 5; const int iterations = 5;
final List<Map<String, dynamic>> results = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> results = <Map<String, dynamic>>[];
if (enableLazyShaderMode) {
_addLazyShaderMode(testDirectory);
}
try {
section('Building application'); section('Building application');
String? applicationBinaryPath; String? applicationBinaryPath;
switch (deviceOperatingSystem) { switch (deviceOperatingSystem) {
@ -1031,8 +1051,14 @@ class StartupTest {
return TaskResult.success( return TaskResult.success(
averageResults, averageResults,
benchmarkScoreKeys: <String>['timeToFirstFrameMicros', 'timeToFirstFrameRasterizedMicros'], benchmarkScoreKeys: <String>[
'timeToFirstFrameMicros',
'timeToFirstFrameRasterizedMicros',
],
); );
} finally {
await _resetManifest(testDirectory);
}
}); });
} }
@ -1180,6 +1206,7 @@ class PerfTest {
this.disablePartialRepaint = false, this.disablePartialRepaint = false,
this.enableMergedPlatformThread = false, this.enableMergedPlatformThread = false,
this.enableSurfaceControl = false, this.enableSurfaceControl = false,
this.enableLazyShaderMode = false,
this.createPlatforms = const <String>[], this.createPlatforms = const <String>[],
}) : _resultFilename = resultFilename; }) : _resultFilename = resultFilename;
@ -1202,6 +1229,7 @@ class PerfTest {
this.disablePartialRepaint = false, this.disablePartialRepaint = false,
this.enableMergedPlatformThread = false, this.enableMergedPlatformThread = false,
this.enableSurfaceControl = false, this.enableSurfaceControl = false,
this.enableLazyShaderMode = false,
this.createPlatforms = const <String>[], this.createPlatforms = const <String>[],
}) : saveTraceFile = false, }) : saveTraceFile = false,
timelineFileName = null, timelineFileName = null,
@ -1261,6 +1289,9 @@ class PerfTest {
/// Whether to enable SurfaceControl swapchain. /// Whether to enable SurfaceControl swapchain.
final bool enableSurfaceControl; 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. /// Number of seconds to time out the test after, allowing debug callbacks to run.
final int? timeoutSeconds; final int? timeoutSeconds;
@ -1359,6 +1390,9 @@ class PerfTest {
if (enableSurfaceControl) { if (enableSurfaceControl) {
_addSurfaceControlSupportToManifest(testDirectory); _addSurfaceControlSupportToManifest(testDirectory);
} }
if (enableLazyShaderMode) {
_addLazyShaderMode(testDirectory);
}
} }
if (disablePartialRepaint || enableMergedPlatformThread) { if (disablePartialRepaint || enableMergedPlatformThread) {
changedPlist = true; changedPlist = true;

View File

@ -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.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/comparable.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/comparable.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/config.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/mask.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/promise.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/base/promise.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/base/promise.h + ../../../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.cc
FILE: ../../../flutter/impeller/base/comparable.h FILE: ../../../flutter/impeller/base/comparable.h
FILE: ../../../flutter/impeller/base/config.h FILE: ../../../flutter/impeller/base/config.h
FILE: ../../../flutter/impeller/base/flags.h
FILE: ../../../flutter/impeller/base/mask.h FILE: ../../../flutter/impeller/base/mask.h
FILE: ../../../flutter/impeller/base/promise.cc FILE: ../../../flutter/impeller/base/promise.cc
FILE: ../../../flutter/impeller/base/promise.h FILE: ../../../flutter/impeller/base/promise.h

View File

@ -228,6 +228,9 @@ struct Settings {
// Enable android surface control swapchains where supported. // Enable android surface control swapchains where supported.
bool enable_surface_control = false; 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. // Log a warning during shell initialization if Impeller is not enabled.
bool warn_on_impeller_opt_out = false; bool warn_on_impeller_opt_out = false;

View File

@ -14,6 +14,7 @@ impeller_component("base") {
"comparable.cc", "comparable.cc",
"comparable.h", "comparable.h",
"config.h", "config.h",
"flags.h",
"mask.h", "mask.h",
"promise.cc", "promise.cc",
"promise.h", "promise.h",

View File

@ -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_

View File

@ -216,7 +216,6 @@ void ContentContextOptions::ApplyToPipelineDescriptor(
} }
desc.SetPrimitiveType(primitive_type); desc.SetPrimitiveType(primitive_type);
desc.SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill); desc.SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill);
} }
@ -283,11 +282,13 @@ ContentContext::ContentContext(
desc.format = PixelFormat::kR8G8B8A8UNormInt; desc.format = PixelFormat::kR8G8B8A8UNormInt;
desc.size = ISize{1, 1}; desc.size = ISize{1, 1};
empty_texture_ = GetContext()->GetResourceAllocator()->CreateTexture(desc); empty_texture_ = GetContext()->GetResourceAllocator()->CreateTexture(desc);
auto data = Color::BlackTransparent().ToR8G8B8A8();
auto cmd_buffer = GetContext()->CreateCommandBuffer(); std::array<uint8_t, 4> data = Color::BlackTransparent().ToR8G8B8A8();
auto blit_pass = cmd_buffer->CreateBlitPass(); std::shared_ptr<CommandBuffer> cmd_buffer =
auto& host_buffer = GetTransientsBuffer(); GetContext()->CreateCommandBuffer();
auto buffer_view = host_buffer.Emplace(data); std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
HostBuffer& host_buffer = GetTransientsBuffer();
BufferView buffer_view = host_buffer.Emplace(data);
blit_pass->AddCopy(buffer_view, empty_texture_); blit_pass->AddCopy(buffer_view, empty_texture_);
if (!blit_pass->EncodeCommands() || !GetContext() if (!blit_pass->EncodeCommands() || !GetContext()
@ -383,9 +384,14 @@ ContentContext::ContentContext(
} }
clip_pipeline_descriptor->SetColorAttachmentDescriptors( clip_pipeline_descriptor->SetColorAttachmentDescriptors(
std::move(clip_color_attachments)); std::move(clip_color_attachments));
if (GetContext()->GetFlags().lazy_shader_mode) {
clip_pipelines_.SetDefaultDescriptor(clip_pipeline_descriptor);
clip_pipelines_.SetDefault(options, nullptr);
} else {
clip_pipelines_.SetDefault( clip_pipelines_.SetDefault(
options, options,
std::make_unique<ClipPipeline>(*context_, clip_pipeline_descriptor)); std::make_unique<ClipPipeline>(*context_, clip_pipeline_descriptor));
}
texture_downsample_pipelines_.CreateDefault( texture_downsample_pipelines_.CreateDefault(
*context_, options_no_msaa_no_depth_stencil); *context_, options_no_msaa_no_depth_stencil);
rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
@ -671,7 +677,9 @@ void ContentContext::ClearCachedRuntimeEffectPipeline(
} }
void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const { void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const {
TRACE_EVENT0("flutter", "InitializeCommonlyUsedShadersIfNeeded"); if (GetContext()->GetFlags().lazy_shader_mode) {
return;
}
GetContext()->InitializeCommonlyUsedShadersIfNeeded(); GetContext()->InitializeCommonlyUsedShadersIfNeeded();
} }

View File

@ -9,6 +9,7 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <utility>
#include "flutter/fml/logging.h" #include "flutter/fml/logging.h"
#include "flutter/fml/status_or.h" #include "flutter/fml/status_or.h"
@ -521,8 +522,14 @@ class ContentContext {
void SetDefault(const ContentContextOptions& options, void SetDefault(const ContentContextOptions& options,
std::unique_ptr<PipelineHandleT> pipeline) { std::unique_ptr<PipelineHandleT> pipeline) {
default_options_ = options; default_options_ = options;
if (pipeline) {
Set(options, std::move(pipeline)); Set(options, std::move(pipeline));
} }
}
void SetDefaultDescriptor(std::optional<PipelineDescriptor> desc) {
desc_ = std::move(desc);
}
void CreateDefault(const Context& context, void CreateDefault(const Context& context,
const ContentContextOptions& options, const ContentContextOptions& options,
@ -534,7 +541,13 @@ class ContentContext {
return; return;
} }
options.ApplyToPipelineDescriptor(*desc); options.ApplyToPipelineDescriptor(*desc);
SetDefault(options, std::make_unique<PipelineHandleT>(context, desc)); desc_ = desc;
if (context.GetFlags().lazy_shader_mode) {
SetDefault(options, nullptr);
} else {
SetDefault(options, std::make_unique<PipelineHandleT>(context, desc_,
/*async=*/true));
}
} }
PipelineHandleT* Get(const ContentContextOptions& options) const { PipelineHandleT* Get(const ContentContextOptions& options) const {
@ -547,16 +560,29 @@ class ContentContext {
return nullptr; 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()) { if (!default_options_.has_value()) {
return nullptr; return nullptr;
} }
PipelineHandleT* result = Get(default_options_.value());
if (result != nullptr) {
return result;
}
SetDefault(
default_options_.value(),
std::make_unique<PipelineHandleT>(context, desc_, /*async=*/false));
return Get(default_options_.value()); return Get(default_options_.value());
} }
size_t GetPipelineCount() const { return pipelines_.size(); } size_t GetPipelineCount() const { return pipelines_.size(); }
private: private:
std::optional<PipelineDescriptor> desc_;
std::optional<ContentContextOptions> default_options_; std::optional<ContentContextOptions> default_options_;
std::vector<std::pair<uint64_t, std::unique_ptr<PipelineHandleT>>> std::vector<std::pair<uint64_t, std::unique_ptr<PipelineHandleT>>>
pipelines_; pipelines_;
@ -688,7 +714,10 @@ class ContentContext {
return found; 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. // The default must always be initialized in the constructor.
FML_CHECK(default_handle != nullptr); FML_CHECK(default_handle != nullptr);

View File

@ -134,7 +134,7 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
} }
auto context = ContextGLES::Create( auto context = ContextGLES::Create(
std::move(gl), ShaderLibraryMappingsForPlayground(), true); Flags{}, std::move(gl), ShaderLibraryMappingsForPlayground(), true);
if (!context) { if (!context) {
FML_LOG(ERROR) << "Could not create context."; FML_LOG(ERROR) << "Could not create context.";
return nullptr; return nullptr;

View File

@ -77,8 +77,8 @@ PlaygroundImplMTL::PlaygroundImplMTL(PlaygroundSwitches switches)
} }
auto context = ContextMTL::Create( auto context = ContextMTL::Create(
ShaderLibraryMappingsForPlayground(), is_gpu_disabled_sync_switch_, impeller::Flags{}, ShaderLibraryMappingsForPlayground(),
"Playground Library", is_gpu_disabled_sync_switch_, "Playground Library",
switches.enable_wide_gamut switches.enable_wide_gamut
? std::optional<PixelFormat>(PixelFormat::kB10G10R10A10XR) ? std::optional<PixelFormat>(PixelFormat::kB10G10R10A10XR)
: std::nullopt); : std::nullopt);

View File

@ -96,7 +96,7 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches)
context_settings.fatal_missing_validations = context_settings.fatal_missing_validations =
switches_.enable_vulkan_validation; 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()) { if (!context_vk || !context_vk->IsValid()) {
VALIDATION_LOG << "Could not create Vulkan context in the playground."; VALIDATION_LOG << "Could not create Vulkan context in the playground.";
return; return;

View File

@ -19,17 +19,20 @@
namespace impeller { namespace impeller {
std::shared_ptr<ContextGLES> ContextGLES::Create( std::shared_ptr<ContextGLES> ContextGLES::Create(
const Flags& flags,
std::unique_ptr<ProcTableGLES> gl, 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) { bool enable_gpu_tracing) {
return std::shared_ptr<ContextGLES>( return std::shared_ptr<ContextGLES>(new ContextGLES(
new ContextGLES(std::move(gl), shader_libraries, enable_gpu_tracing)); flags, std::move(gl), shader_libraries, enable_gpu_tracing));
} }
ContextGLES::ContextGLES( ContextGLES::ContextGLES(
const Flags& flags,
std::unique_ptr<ProcTableGLES> gl, std::unique_ptr<ProcTableGLES> gl,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_mappings, const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_mappings,
bool enable_gpu_tracing) { bool enable_gpu_tracing)
: Context(flags) {
reactor_ = std::make_shared<ReactorGLES>(std::move(gl)); reactor_ = std::make_shared<ReactorGLES>(std::move(gl));
if (!reactor_->IsValid()) { if (!reactor_->IsValid()) {
VALIDATION_LOG << "Could not create valid reactor."; VALIDATION_LOG << "Could not create valid reactor.";

View File

@ -25,6 +25,7 @@ class ContextGLES final : public Context,
public std::enable_shared_from_this<ContextGLES> { public std::enable_shared_from_this<ContextGLES> {
public: public:
static std::shared_ptr<ContextGLES> Create( static std::shared_ptr<ContextGLES> Create(
const Flags& flags,
std::unique_ptr<ProcTableGLES> gl, 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); bool enable_gpu_tracing);
@ -60,6 +61,7 @@ class ContextGLES final : public Context,
bool is_valid_ = false; bool is_valid_ = false;
ContextGLES( ContextGLES(
const Flags& flags,
std::unique_ptr<ProcTableGLES> gl, 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); bool enable_gpu_tracing);

View File

@ -67,16 +67,19 @@ class ContextMTL final : public Context,
public std::enable_shared_from_this<ContextMTL> { public std::enable_shared_from_this<ContextMTL> {
public: public:
static std::shared_ptr<ContextMTL> Create( static std::shared_ptr<ContextMTL> Create(
const Flags& flags,
const std::vector<std::string>& shader_library_paths, const std::vector<std::string>& shader_library_paths,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch); std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch);
static std::shared_ptr<ContextMTL> Create( static std::shared_ptr<ContextMTL> Create(
const Flags& flags,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data, const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch, std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
const std::string& label, const std::string& label,
std::optional<PixelFormat> pixel_format_override = std::nullopt); std::optional<PixelFormat> pixel_format_override = std::nullopt);
static std::shared_ptr<ContextMTL> Create( static std::shared_ptr<ContextMTL> Create(
const Flags& flags,
id<MTLDevice> device, id<MTLDevice> device,
id<MTLCommandQueue> command_queue, id<MTLCommandQueue> command_queue,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data, const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
@ -178,7 +181,8 @@ class ContextMTL final : public Context,
#endif // IMPELLER_DEBUG #endif // IMPELLER_DEBUG
bool is_valid_ = false; bool is_valid_ = false;
ContextMTL(id<MTLDevice> device, ContextMTL(const Flags& flags,
id<MTLDevice> device,
id<MTLCommandQueue> command_queue, id<MTLCommandQueue> command_queue,
NSArray<id<MTLLibrary>>* shader_libraries, NSArray<id<MTLLibrary>>* shader_libraries,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch, std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,

View File

@ -86,12 +86,14 @@ static std::unique_ptr<Capabilities> InferMetalCapabilities(
} }
ContextMTL::ContextMTL( ContextMTL::ContextMTL(
const Flags& flags,
id<MTLDevice> device, id<MTLDevice> device,
id<MTLCommandQueue> command_queue, id<MTLCommandQueue> command_queue,
NSArray<id<MTLLibrary>>* shader_libraries, NSArray<id<MTLLibrary>>* shader_libraries,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch, std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
std::optional<PixelFormat> pixel_format_override) std::optional<PixelFormat> pixel_format_override)
: device_(device), : Context(flags),
device_(device),
command_queue_(command_queue), command_queue_(command_queue),
is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) { is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) {
// Validate device. // Validate device.
@ -235,6 +237,7 @@ static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
} }
std::shared_ptr<ContextMTL> ContextMTL::Create( std::shared_ptr<ContextMTL> ContextMTL::Create(
const Flags& flags,
const std::vector<std::string>& shader_library_paths, const std::vector<std::string>& shader_library_paths,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) { std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
auto device = CreateMetalDevice(); auto device = CreateMetalDevice();
@ -243,7 +246,7 @@ std::shared_ptr<ContextMTL> ContextMTL::Create(
return nullptr; return nullptr;
} }
auto context = std::shared_ptr<ContextMTL>(new ContextMTL( auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
device, command_queue, flags, device, command_queue,
MTLShaderLibraryFromFilePaths(device, shader_library_paths), MTLShaderLibraryFromFilePaths(device, shader_library_paths),
std::move(is_gpu_disabled_sync_switch))); std::move(is_gpu_disabled_sync_switch)));
if (!context->IsValid()) { if (!context->IsValid()) {
@ -254,6 +257,7 @@ std::shared_ptr<ContextMTL> ContextMTL::Create(
} }
std::shared_ptr<ContextMTL> ContextMTL::Create( std::shared_ptr<ContextMTL> ContextMTL::Create(
const Flags& flags,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data, const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch, std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
const std::string& library_label, const std::string& library_label,
@ -264,7 +268,7 @@ std::shared_ptr<ContextMTL> ContextMTL::Create(
return nullptr; return nullptr;
} }
auto context = std::shared_ptr<ContextMTL>(new ContextMTL( auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
device, command_queue, flags, device, command_queue,
MTLShaderLibraryFromFileData(device, shader_libraries_data, MTLShaderLibraryFromFileData(device, shader_libraries_data,
library_label), library_label),
std::move(is_gpu_disabled_sync_switch), pixel_format_override)); std::move(is_gpu_disabled_sync_switch), pixel_format_override));
@ -276,13 +280,14 @@ std::shared_ptr<ContextMTL> ContextMTL::Create(
} }
std::shared_ptr<ContextMTL> ContextMTL::Create( std::shared_ptr<ContextMTL> ContextMTL::Create(
const Flags& flags,
id<MTLDevice> device, id<MTLDevice> device,
id<MTLCommandQueue> command_queue, id<MTLCommandQueue> command_queue,
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data, const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch, std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
const std::string& library_label) { const std::string& library_label) {
auto context = std::shared_ptr<ContextMTL>( auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(device, command_queue, new ContextMTL(flags, device, command_queue,
MTLShaderLibraryFromFileData(device, shader_libraries_data, MTLShaderLibraryFromFileData(device, shader_libraries_data,
library_label), library_label),
std::move(is_gpu_disabled_sync_switch))); std::move(is_gpu_disabled_sync_switch)));

View File

@ -34,7 +34,7 @@ std::shared_ptr<Context> CreateContext() {
settings.enable_gpu_tracing = false; settings.enable_gpu_tracing = false;
settings.enable_surface_control = false; settings.enable_surface_control = false;
return ContextVK::Create(std::move(settings)); return ContextVK::Create(impeller::Flags{}, std::move(settings));
} }
TEST(AndroidVulkanTest, CanImportRGBA) { TEST(AndroidVulkanTest, CanImportRGBA) {

View File

@ -102,8 +102,9 @@ static std::optional<QueueIndexVK> PickQueue(const vk::PhysicalDevice& device,
return std::nullopt; return std::nullopt;
} }
std::shared_ptr<ContextVK> ContextVK::Create(Settings settings) { std::shared_ptr<ContextVK> ContextVK::Create(const Flags& flags,
auto context = std::shared_ptr<ContextVK>(new ContextVK()); Settings settings) {
auto context = std::shared_ptr<ContextVK>(new ContextVK(flags));
context->Setup(std::move(settings)); context->Setup(std::move(settings));
if (!context->IsValid()) { if (!context->IsValid()) {
return nullptr; return nullptr;
@ -127,7 +128,8 @@ uint64_t CalculateHash(void* ptr) {
} }
} // namespace } // namespace
ContextVK::ContextVK() : hash_(CalculateHash(this)) {} ContextVK::ContextVK(const Flags& flags)
: Context(flags), hash_(CalculateHash(this)) {}
ContextVK::~ContextVK() { ContextVK::~ContextVK() {
if (device_holder_ && device_holder_->device) { if (device_holder_ && device_holder_->device) {
@ -150,16 +152,6 @@ void ContextVK::Setup(Settings settings) {
raster_message_loop_ = fml::ConcurrentMessageLoop::Create( raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
ChooseThreadCountForWorkers(std::thread::hardware_concurrency())); 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; auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER;
dispatcher.init(settings.proc_address_callback); dispatcher.init(settings.proc_address_callback);

View File

@ -98,7 +98,8 @@ class ContextVK final : public Context,
/// Visible for testing. /// Visible for testing.
static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency); static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency);
static std::shared_ptr<ContextVK> Create(Settings settings); static std::shared_ptr<ContextVK> Create(const Flags& flags,
Settings settings);
uint64_t GetHash() const { return hash_; } uint64_t GetHash() const { return hash_; }
@ -300,7 +301,7 @@ class ContextVK final : public Context,
bool is_valid_ = false; bool is_valid_ = false;
ContextVK(); explicit ContextVK(const Flags& flags);
void Setup(Settings settings); void Setup(Settings settings);

View File

@ -14,7 +14,7 @@
namespace impeller { namespace impeller {
SurfaceContextVK::SurfaceContextVK(const std::shared_ptr<ContextVK>& parent) SurfaceContextVK::SurfaceContextVK(const std::shared_ptr<ContextVK>& parent)
: parent_(parent) {} : Context(parent->GetFlags()), parent_(parent) {}
SurfaceContextVK::~SurfaceContextVK() = default; SurfaceContextVK::~SurfaceContextVK() = default;

View File

@ -955,7 +955,8 @@ std::shared_ptr<ContextVK> MockVulkanContextBuilder::Build() {
g_format_properties_callback = format_properties_callback_; g_format_properties_callback = format_properties_callback_;
g_physical_device_properties_callback = physical_properties_callback_; g_physical_device_properties_callback = physical_properties_callback_;
settings.embedder_data = embedder_data_; settings.embedder_data = embedder_data_;
std::shared_ptr<ContextVK> result = ContextVK::Create(std::move(settings)); std::shared_ptr<ContextVK> result =
ContextVK::Create(Flags{}, std::move(settings));
return result; return result;
} }

View File

@ -10,7 +10,7 @@ namespace impeller {
Context::~Context() = default; Context::~Context() = default;
Context::Context() = default; Context::Context(const Flags& flags) : flags_(flags) {}
bool Context::UpdateOffscreenLayerPixelFormat(PixelFormat format) { bool Context::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
return false; return false;

View File

@ -9,6 +9,7 @@
#include <string> #include <string>
#include "fml/closure.h" #include "fml/closure.h"
#include "impeller/base/flags.h"
#include "impeller/core/allocator.h" #include "impeller/core/allocator.h"
#include "impeller/core/formats.h" #include "impeller/core/formats.h"
#include "impeller/renderer/capabilities.h" #include "impeller/renderer/capabilities.h"
@ -245,9 +246,12 @@ class Context {
/// @brief Submit the command buffer that renders to the onscreen surface. /// @brief Submit the command buffer that renders to the onscreen surface.
virtual bool SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer); virtual bool SubmitOnscreen(std::shared_ptr<CommandBuffer> cmd_buffer);
protected: const Flags& GetFlags() const { return flags_; }
Context();
protected:
explicit Context(const Flags& flags);
Flags flags_;
std::vector<std::function<void()>> per_frame_task_; std::vector<std::function<void()>> per_frame_task_;
private: private:

View File

@ -23,13 +23,15 @@ Pipeline<T>::~Pipeline() = default;
PipelineFuture<PipelineDescriptor> CreatePipelineFuture( PipelineFuture<PipelineDescriptor> CreatePipelineFuture(
const Context& context, const Context& context,
std::optional<PipelineDescriptor> desc) { std::optional<PipelineDescriptor> desc,
bool async) {
if (!context.IsValid()) { if (!context.IsValid()) {
return {desc, RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>( return {desc, RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(
nullptr)}; nullptr)};
} }
return context.GetPipelineLibrary()->GetPipeline(std::move(desc)); return context.GetPipelineLibrary()->GetPipeline(std::move(desc),
/*async=*/async);
} }
PipelineFuture<ComputePipelineDescriptor> CreatePipelineFuture( PipelineFuture<ComputePipelineDescriptor> CreatePipelineFuture(

View File

@ -90,9 +90,18 @@ using PipelineRef = raw_ptr<Pipeline<PipelineDescriptor>>;
extern template class Pipeline<PipelineDescriptor>; extern template class Pipeline<PipelineDescriptor>;
extern template class Pipeline<ComputePipelineDescriptor>; extern template class Pipeline<ComputePipelineDescriptor>;
/// @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<PipelineDescriptor> CreatePipelineFuture( PipelineFuture<PipelineDescriptor> CreatePipelineFuture(
const Context& context, const Context& context,
std::optional<PipelineDescriptor> desc); std::optional<PipelineDescriptor> desc,
bool async = true);
PipelineFuture<ComputePipelineDescriptor> CreatePipelineFuture( PipelineFuture<ComputePipelineDescriptor> CreatePipelineFuture(
const Context& context, const Context& context,
@ -116,14 +125,17 @@ class RenderPipelineHandle {
using FragmentShader = FragmentShader_; using FragmentShader = FragmentShader_;
using Builder = PipelineBuilder<VertexShader, FragmentShader>; using Builder = PipelineBuilder<VertexShader, FragmentShader>;
explicit RenderPipelineHandle(const Context& context) explicit RenderPipelineHandle(const Context& context, bool async = true)
: RenderPipelineHandle(CreatePipelineFuture( : RenderPipelineHandle(CreatePipelineFuture(
context, context,
Builder::MakeDefaultPipelineDescriptor(context))) {} Builder::MakeDefaultPipelineDescriptor(context),
async)) {}
explicit RenderPipelineHandle(const Context& context, explicit RenderPipelineHandle(const Context& context,
std::optional<PipelineDescriptor> desc) std::optional<PipelineDescriptor> desc,
: RenderPipelineHandle(CreatePipelineFuture(context, desc)) {} bool async = true)
: RenderPipelineHandle(
CreatePipelineFuture(context, desc, /*async=*/async)) {}
explicit RenderPipelineHandle(PipelineFuture<PipelineDescriptor> future) explicit RenderPipelineHandle(PipelineFuture<PipelineDescriptor> future)
: pipeline_future_(std::move(future)) {} : pipeline_future_(std::move(future)) {}

View File

@ -140,6 +140,8 @@ class MockCommandBuffer : public CommandBuffer {
class MockImpellerContext : public Context { class MockImpellerContext : public Context {
public: public:
MockImpellerContext() : Context(Flags{}) {}
MOCK_METHOD(Context::BackendType, GetBackendType, (), (const, override)); MOCK_METHOD(Context::BackendType, GetBackendType, (), (const, override));
MOCK_METHOD(std::string, DescribeGpuModel, (), (const, override)); MOCK_METHOD(std::string, DescribeGpuModel, (), (const, override));

View File

@ -27,8 +27,8 @@ ScopedObject<Context> ContextGLES::Create(
impeller_framebuffer_blend_shaders_gles_data, impeller_framebuffer_blend_shaders_gles_data,
impeller_framebuffer_blend_shaders_gles_length), impeller_framebuffer_blend_shaders_gles_length),
}; };
auto impeller_context = impeller::ContextGLES::Create(std::move(proc_table), auto impeller_context = impeller::ContextGLES::Create(
shader_mappings, false); Flags{}, std::move(proc_table), shader_mappings, false);
if (!impeller_context) { if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context."; VALIDATION_LOG << "Could not create Impeller context.";
return {}; return {};

View File

@ -28,7 +28,7 @@ CreateShaderLibraryMappings() {
ScopedObject<Context> ContextMTL::Create() { ScopedObject<Context> ContextMTL::Create() {
auto impeller_context = auto impeller_context =
impeller::ContextMTL::Create(CreateShaderLibraryMappings(), // impeller::ContextMTL::Create(Flags{}, CreateShaderLibraryMappings(), //
std::make_shared<fml::SyncSwitch>(), // std::make_shared<fml::SyncSwitch>(), //
"Impeller" // "Impeller" //
); );

View File

@ -53,8 +53,8 @@ ScopedObject<Context> ContextVK::Create(const Settings& settings) {
impeller_settings.enable_validation = true; impeller_settings.enable_validation = true;
sContextVKProcAddressCallback = settings.instance_proc_address_callback; sContextVKProcAddressCallback = settings.instance_proc_address_callback;
impeller_settings.proc_address_callback = ContextVKGetInstanceProcAddress; impeller_settings.proc_address_callback = ContextVKGetInstanceProcAddress;
auto impeller_context = auto impeller_context = impeller::ContextVK::Create(
impeller::ContextVK::Create(std::move(impeller_settings)); impeller::Flags{}, std::move(impeller_settings));
sContextVKProcAddressCallback = nullptr; sContextVKProcAddressCallback = nullptr;
if (!impeller_context) { if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context."; VALIDATION_LOG << "Could not create Impeller context.";

View File

@ -40,7 +40,7 @@ namespace impeller {
class TestImpellerContext : public impeller::Context { class TestImpellerContext : public impeller::Context {
public: public:
TestImpellerContext() = default; TestImpellerContext() : impeller::Context(impeller::Flags{}) {}
BackendType GetBackendType() const override { return BackendType::kMetal; } BackendType GetBackendType() const override { return BackendType::kMetal; }

View File

@ -61,7 +61,7 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL(
return; return;
} }
impeller_context_ = impeller::ContextGLES::Create( impeller_context_ = impeller::ContextGLES::Create(
std::move(gl), ShaderLibraryMappings(), true); impeller::Flags{}, std::move(gl), ShaderLibraryMappings(), true);
} }
} }

View File

@ -532,6 +532,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.merged_platform_ui_thread = !command_line.HasOption( settings.merged_platform_ui_thread = !command_line.HasOption(
FlagForSwitch(Switch::DisableMergedPlatformUIThread)); FlagForSwitch(Switch::DisableMergedPlatformUIThread));
settings.impeller_enable_lazy_shader_mode =
command_line.HasOption(FlagForSwitch(Switch::ImpellerLazyShaderMode));
return settings; return settings;
} }

View File

@ -300,6 +300,10 @@ DEF_SWITCH(DisableMergedPlatformUIThread,
DEF_SWITCH(EnableAndroidSurfaceControl, DEF_SWITCH(EnableAndroidSurfaceControl,
"enable-surface-control", "enable-surface-control",
"Enable the SurfaceControl backed swapchain when supported.") "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 DEF_SWITCHES_END
void PrintUsage(const std::string& executable_name); void PrintUsage(const std::string& executable_name);

View File

@ -54,7 +54,8 @@ static std::shared_ptr<impeller::ContextMTL> CreateImpellerContext() {
impeller_framebuffer_blend_shaders_length), impeller_framebuffer_blend_shaders_length),
}; };
auto sync_switch = std::make_shared<fml::SyncSwitch>(false); auto sync_switch = std::make_shared<fml::SyncSwitch>(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) { TEST(GPUSurfaceMetalImpeller, InvalidImpellerContextCreatesCausesSurfaceToBeInvalid) {

View File

@ -79,7 +79,7 @@ static std::shared_ptr<impeller::Context> CreateImpellerContext(
}; };
auto context = impeller::ContextGLES::Create( auto context = impeller::ContextGLES::Create(
std::move(proc_table), impeller::Flags{}, std::move(proc_table),
is_gles3 ? gles3_shader_mappings : gles2_shader_mappings, is_gles3 ? gles3_shader_mappings : gles2_shader_mappings,
enable_gpu_tracing); enable_gpu_tracing);
if (!context) { if (!context) {

View File

@ -36,7 +36,7 @@ TaskRunners MakeTaskRunners(const std::string& thread_label,
class TestImpellerContext : public impeller::Context { class TestImpellerContext : public impeller::Context {
public: public:
TestImpellerContext() {} TestImpellerContext() : Context(impeller::Flags{}) {}
~TestImpellerContext() {} ~TestImpellerContext() {}

View File

@ -49,7 +49,11 @@ static std::shared_ptr<impeller::Context> CreateImpellerContext(
settings.enable_gpu_tracing = p_settings.enable_gpu_tracing; settings.enable_gpu_tracing = p_settings.enable_gpu_tracing;
settings.enable_surface_control = p_settings.enable_surface_control; 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 (!p_settings.quiet) {
if (context && impeller::CapabilitiesVK::Cast(*context->GetCapabilities()) if (context && impeller::CapabilitiesVK::Cast(*context->GetCapabilities())

View File

@ -7,6 +7,7 @@
#include "flutter/fml/concurrent_message_loop.h" #include "flutter/fml/concurrent_message_loop.h"
#include "flutter/fml/macros.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/impeller/renderer/backend/vulkan/surface_context_vk.h"
#include "flutter/shell/platform/android/android_context_vk_impeller.h" #include "flutter/shell/platform/android/android_context_vk_impeller.h"
#include "flutter/shell/platform/android/surface/android_native_window.h" #include "flutter/shell/platform/android/surface/android_native_window.h"

View File

@ -25,6 +25,7 @@ class AndroidContext {
bool enable_validation = false; bool enable_validation = false;
bool enable_gpu_tracing = false; bool enable_gpu_tracing = false;
bool enable_surface_control = false; bool enable_surface_control = false;
bool enable_lazy_shader_mode = false;
bool quiet = false; bool quiet = false;
}; };

View File

@ -51,6 +51,8 @@ public class FlutterLoader {
"io.flutter.embedding.android.DisableMergedPlatformUIThread"; "io.flutter.embedding.android.DisableMergedPlatformUIThread";
private static final String ENABLE_SURFACE_CONTROL = private static final String ENABLE_SURFACE_CONTROL =
"io.flutter.embedding.android.EnableSurfaceControl"; "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 * 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) { if (backend != null) {
shellArgs.add("--impeller-backend=" + backend); 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"; final String leakVM = isLeakVM(metaData) ? "true" : "false";

View File

@ -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_gl_skia.h"
#include "flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.h" #include "flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.h"
#include "fml/logging.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 #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/android_surface_vk_impeller.h"
#include "flutter/shell/platform/android/image_external_texture_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_gpu_tracing = p_settings.enable_vulkan_gpu_tracing;
settings.enable_validation = p_settings.enable_vulkan_validation; settings.enable_validation = p_settings.enable_vulkan_validation;
settings.enable_surface_control = p_settings.enable_surface_control; settings.enable_surface_control = p_settings.enable_surface_control;
settings.enable_lazy_shader_mode =
p_settings.impeller_enable_lazy_shader_mode;
return settings; return settings;
} }
} // namespace } // namespace
AndroidSurfaceFactoryImpl::AndroidSurfaceFactoryImpl( AndroidSurfaceFactoryImpl::AndroidSurfaceFactoryImpl(
const std::shared_ptr<AndroidContext>& context, const std::shared_ptr<AndroidContext>& context,
bool enable_impeller) bool enable_impeller,
: android_context_(context), enable_impeller_(enable_impeller) {} bool lazy_shader_mode)
: android_context_(context),
enable_impeller_(enable_impeller),
lazy_shader_mode_(lazy_shader_mode) {}
AndroidSurfaceFactoryImpl::~AndroidSurfaceFactoryImpl() = default; AndroidSurfaceFactoryImpl::~AndroidSurfaceFactoryImpl() = default;
@ -133,7 +139,9 @@ PlatformViewAndroid::PlatformViewAndroid(
<< "Could not create surface from invalid Android context."; << "Could not create surface from invalid Android context.";
surface_factory_ = std::make_shared<AndroidSurfaceFactoryImpl>( surface_factory_ = std::make_shared<AndroidSurfaceFactoryImpl>(
android_context_, // android_context_, //
delegate.OnPlatformViewGetSettings().enable_impeller // delegate.OnPlatformViewGetSettings().enable_impeller, //
delegate.OnPlatformViewGetSettings()
.impeller_enable_lazy_shader_mode //
); );
android_surface_ = surface_factory_->CreateSurface(); android_surface_ = surface_factory_->CreateSurface();
android_use_new_platform_view_ = android_use_new_platform_view_ =

View File

@ -26,7 +26,8 @@ namespace flutter {
class AndroidSurfaceFactoryImpl : public AndroidSurfaceFactory { class AndroidSurfaceFactoryImpl : public AndroidSurfaceFactory {
public: public:
AndroidSurfaceFactoryImpl(const std::shared_ptr<AndroidContext>& context, AndroidSurfaceFactoryImpl(const std::shared_ptr<AndroidContext>& context,
bool enable_impeller); bool enable_impeller,
bool lazy_shader_mode);
~AndroidSurfaceFactoryImpl() override; ~AndroidSurfaceFactoryImpl() override;
@ -35,6 +36,7 @@ class AndroidSurfaceFactoryImpl : public AndroidSurfaceFactory {
private: private:
const std::shared_ptr<AndroidContext>& android_context_; const std::shared_ptr<AndroidContext>& android_context_;
const bool enable_impeller_; const bool enable_impeller_;
const bool lazy_shader_mode_;
}; };
class PlatformViewAndroid final : public PlatformView { class PlatformViewAndroid final : public PlatformView {

View File

@ -315,6 +315,35 @@ public class FlutterLoaderTest {
assertTrue(arguments.contains(disabledControlArg)); 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<String[]> shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI, times(1))
.init(
eq(ctx),
shellArgsCaptor.capture(),
anyString(),
anyString(),
anyString(),
anyLong(),
anyInt());
List<String> arguments = Arrays.asList(shellArgsCaptor.getValue());
assertTrue(arguments.contains(shaderModeArg));
}
@Test @Test
@TargetApi(API_LEVELS.API_23) @TargetApi(API_LEVELS.API_23)
@Config(sdk = API_LEVELS.API_23) @Config(sdk = API_LEVELS.API_23)

View File

@ -25,8 +25,8 @@ static std::shared_ptr<impeller::ContextMTL> CreateImpellerContext(
std::make_shared<fml::NonOwnedMapping>(impeller_framebuffer_blend_shaders_data, std::make_shared<fml::NonOwnedMapping>(impeller_framebuffer_blend_shaders_data,
impeller_framebuffer_blend_shaders_length), impeller_framebuffer_blend_shaders_length),
}; };
return impeller::ContextMTL::Create(shader_mappings, is_gpu_disabled_sync_switch, return impeller::ContextMTL::Create(impeller::Flags{}, shader_mappings,
"Impeller Library"); is_gpu_disabled_sync_switch, "Impeller Library");
} }
@implementation FlutterDarwinContextMetalImpeller @implementation FlutterDarwinContextMetalImpeller

View File

@ -41,7 +41,8 @@ static std::shared_ptr<impeller::ContextMTL> CreateImpellerContext() {
impeller_framebuffer_blend_shaders_length), impeller_framebuffer_blend_shaders_length),
}; };
auto sync_switch = std::make_shared<fml::SyncSwitch>(false); auto sync_switch = std::make_shared<fml::SyncSwitch>(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 <FlutterTexture> @interface TestExternalTexture : NSObject <FlutterTexture>

View File

@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include "impeller/display_list/aiks_context.h"
#include "impeller/entity/gles/entity_shaders_gles.h" #include "impeller/entity/gles/entity_shaders_gles.h"
#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" #include "impeller/entity/gles/framebuffer_blend_shaders_gles.h"
#include "impeller/entity/gles/modern_shaders_gles.h" #include "impeller/entity/gles/modern_shaders_gles.h"
@ -80,7 +81,8 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller(
} }
impeller_context_ = impeller::ContextGLES::Create( 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_) { if (!impeller_context_) {
FML_LOG(ERROR) << "Could not create Impeller context."; FML_LOG(ERROR) << "Could not create Impeller context.";

View File

@ -41,6 +41,7 @@ EmbedderSurfaceMetalImpeller::EmbedderSurfaceMetalImpeller(
impeller_framebuffer_blend_shaders_length), impeller_framebuffer_blend_shaders_length),
}; };
context_ = impeller::ContextMTL::Create( context_ = impeller::ContextMTL::Create(
impeller::Flags{},
(__bridge id<MTLDevice>)device, // device (__bridge id<MTLDevice>)device, // device
(__bridge id<MTLCommandQueue>)command_queue, // command_queue (__bridge id<MTLCommandQueue>)command_queue, // command_queue
shader_mappings, // shader_libraries_data shader_mappings, // shader_libraries_data

View File

@ -10,6 +10,7 @@
#include "flutter/impeller/entity/vk/framebuffer_blend_shaders_vk.h" #include "flutter/impeller/entity/vk/framebuffer_blend_shaders_vk.h"
#include "flutter/impeller/entity/vk/modern_shaders_vk.h" #include "flutter/impeller/entity/vk/modern_shaders_vk.h"
#include "flutter/shell/gpu/gpu_surface_vulkan.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 "impeller/renderer/backend/vulkan/context_vk.h"
#include "include/gpu/ganesh/GrDirectContext.h" #include "include/gpu/ganesh/GrDirectContext.h"
#include "shell/gpu/gpu_surface_vulkan_impeller.h" #include "shell/gpu/gpu_surface_vulkan_impeller.h"
@ -70,7 +71,8 @@ EmbedderSurfaceVulkanImpeller::EmbedderSurfaceVulkanImpeller(
} }
settings.embedder_data = data; settings.embedder_data = data;
context_ = impeller::ContextVK::Create(std::move(settings)); context_ =
impeller::ContextVK::Create(impeller::Flags{}, std::move(settings));
if (!context_) { if (!context_) {
FML_LOG(ERROR) << "Failed to initialize Vulkan Context."; FML_LOG(ERROR) << "Failed to initialize Vulkan Context.";
return; return;

View File

@ -35,6 +35,7 @@
#if ALLOW_IMPELLER #if ALLOW_IMPELLER
#include <vulkan/vulkan.h> // nogncheck #include <vulkan/vulkan.h> // nogncheck
#include "impeller/display_list/aiks_context.h" // nogncheck
#include "impeller/entity/vk/entity_shaders_vk.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/framebuffer_blend_shaders_vk.h" // nogncheck
#include "impeller/entity/vk/modern_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.cache_directory = fml::paths::GetCachesDirectory();
context_settings.enable_validation = enable_validation; 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()) { if (!context || !context->IsValid()) {
VALIDATION_LOG << "Could not create Vulkan context."; VALIDATION_LOG << "Could not create Vulkan context.";
return false; return false;