diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index a1f8472a87..d6d82f0520 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -681,6 +681,8 @@ FILE: ../../../flutter/shell/common/shell_test_platform_view.cc FILE: ../../../flutter/shell/common/shell_test_platform_view.h FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.cc FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.h +FILE: ../../../flutter/shell/common/shell_test_platform_view_metal.h +FILE: ../../../flutter/shell/common/shell_test_platform_view_metal.mm FILE: ../../../flutter/shell/common/shell_test_platform_view_vulkan.cc FILE: ../../../flutter/shell/common/shell_test_platform_view_vulkan.h FILE: ../../../flutter/shell/common/shell_unittests.cc diff --git a/engine/src/flutter/common/graphics/persistent_cache.cc b/engine/src/flutter/common/graphics/persistent_cache.cc index 360b32e07d..69c906fdd3 100644 --- a/engine/src/flutter/common/graphics/persistent_cache.cc +++ b/engine/src/flutter/common/graphics/persistent_cache.cc @@ -18,6 +18,7 @@ #include "flutter/fml/trace_event.h" #include "flutter/shell/version/version.h" #include "rapidjson/document.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/utils/SkBase64.h" namespace flutter { @@ -191,7 +192,31 @@ sk_sp ParseBase64(const std::string& input) { return data; } -std::vector PersistentCache::LoadSkSLs() { +size_t PersistentCache::PrecompileKnownSkSLs(GrDirectContext* context) const { + auto known_sksls = LoadSkSLs(); + // A trace must be present even if no precompilations have been completed. + FML_TRACE_EVENT("flutter", "PersistentCache::PrecompileKnownSkSLs", "count", + known_sksls.size()); + + if (context == nullptr) { + return 0; + } + + size_t precompiled_count = 0; + for (const auto& sksl : known_sksls) { + TRACE_EVENT0("flutter", "PrecompilingSkSL"); + if (context->precompileShader(*sksl.first, *sksl.second)) { + precompiled_count++; + } + } + + FML_TRACE_COUNTER("flutter", "PersistentCache::PrecompiledSkSLs", + reinterpret_cast(this), // Trace Counter ID + "Successful", precompiled_count); + return precompiled_count; +} + +std::vector PersistentCache::LoadSkSLs() const { TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs"); std::vector result; fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory, diff --git a/engine/src/flutter/common/graphics/persistent_cache.h b/engine/src/flutter/common/graphics/persistent_cache.h index efb3935c58..03dc449742 100644 --- a/engine/src/flutter/common/graphics/persistent_cache.h +++ b/engine/src/flutter/common/graphics/persistent_cache.h @@ -72,7 +72,22 @@ class PersistentCache : public GrContextOptions::PersistentCache { using SkSLCache = std::pair, sk_sp>; /// Load all the SkSL shader caches in the right directory. - std::vector LoadSkSLs(); + std::vector LoadSkSLs() const; + + //---------------------------------------------------------------------------- + /// @brief Precompile SkSLs packaged with the application and gathered + /// during previous runs in the given context. + /// + /// @warning The context must be the rendering context. This context may be + /// destroyed during application suspension and subsequently + /// recreated. The SkSLs must be precompiled again in the new + /// context. + /// + /// @param context The rendering context to precompile shaders in. + /// + /// @return The number of SkSLs precompiled. + /// + size_t PrecompileKnownSkSLs(GrDirectContext* context) const; // Return mappings for all skp's accessible through the AssetManager std::vector> GetSkpsFromAssetManager() const; @@ -82,7 +97,9 @@ class PersistentCache : public GrContextOptions::PersistentCache { static void SetAssetManager(std::shared_ptr value); static bool cache_sksl() { return cache_sksl_; } + static void SetCacheSkSL(bool value); + static void MarkStrategySet() { strategy_set_ = true; } static constexpr char kSkSLSubdirName[] = "sksl"; diff --git a/engine/src/flutter/shell/common/BUILD.gn b/engine/src/flutter/shell/common/BUILD.gn index ef390e5d06..0e24c721d7 100644 --- a/engine/src/flutter/shell/common/BUILD.gn +++ b/engine/src/flutter/shell/common/BUILD.gn @@ -232,6 +232,15 @@ if (enable_unittests) { public_deps += [ "//flutter/vulkan" ] } + + if (test_enable_metal) { + sources += [ + "shell_test_platform_view_metal.h", + "shell_test_platform_view_metal.mm", + ] + + public_deps += [ "//flutter/shell/platform/darwin/graphics" ] + } } shell_host_executable("shell_unittests") { diff --git a/engine/src/flutter/shell/common/persistent_cache_unittests.cc b/engine/src/flutter/shell/common/persistent_cache_unittests.cc index 3d45c09302..a83f42649f 100644 --- a/engine/src/flutter/shell/common/persistent_cache_unittests.cc +++ b/engine/src/flutter/shell/common/persistent_cache_unittests.cc @@ -24,6 +24,8 @@ namespace flutter { namespace testing { +using PersistentCacheTest = ShellTest; + static void WaitForIO(Shell* shell) { std::promise io_task_finished; shell->GetTaskRunners().GetIOTaskRunner()->PostTask( @@ -31,7 +33,14 @@ static void WaitForIO(Shell* shell) { io_task_finished.get_future().wait(); } -TEST_F(ShellTest, CacheSkSLWorks) { +static void WaitForRaster(Shell* shell) { + std::promise raster_task_finished; + shell->GetTaskRunners().GetRasterTaskRunner()->PostTask( + [&raster_task_finished]() { raster_task_finished.set_value(true); }); + raster_task_finished.get_future().wait(); +} + +TEST_F(PersistentCacheTest, CacheSkSLWorks) { // Create a temp dir to store the persistent cache fml::ScopedTemporaryDirectory dir; PersistentCache::SetCacheDirectoryPath(dir.path()); @@ -41,9 +50,11 @@ TEST_F(ShellTest, CacheSkSLWorks) { settings.cache_sksl = true; settings.dump_skp_on_shader_compilation = true; - fml::AutoResetWaitableEvent firstFrameLatch; + fml::AutoResetWaitableEvent first_frame_latch; settings.frame_rasterized_callback = - [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); }; + [&first_frame_latch](const FrameTiming& t) { + first_frame_latch.Signal(); + }; auto sksl_config = RunConfiguration::InferFromSettings(settings); sksl_config.SetEntrypoint("emptyMain"); @@ -64,7 +75,7 @@ TEST_F(ShellTest, CacheSkSLWorks) { root->Add(physical_shape_layer); }; PumpOneFrame(shell.get(), 100, 100, builder); - firstFrameLatch.Wait(); + first_frame_latch.Wait(); WaitForIO(shell.get()); // Some skp should be dumped due to shader compilations. @@ -95,14 +106,14 @@ TEST_F(ShellTest, CacheSkSLWorks) { shell = CreateShell(settings); PlatformViewNotifyCreated(shell.get()); RunEngine(shell.get(), std::move(normal_config)); - firstFrameLatch.Reset(); + first_frame_latch.Reset(); PumpOneFrame(shell.get(), 100, 100, builder); - firstFrameLatch.Wait(); + first_frame_latch.Wait(); WaitForIO(shell.get()); -// Shader precompilation from SKSL is not implemented on the Skia Vulkan +// Shader precompilation from SkSL is not implemented on the Skia Vulkan // backend so don't run the second half of this test on Vulkan. This can get -// removed if SKSL precompilation is implemented in the Skia Vulkan backend. +// removed if SkSL precompilation is implemented in the Skia Vulkan backend. #if !defined(SHELL_ENABLE_VULKAN) // To check that all shaders are precompiled, verify that no new skp is dumped // due to shader compilations. @@ -113,22 +124,66 @@ TEST_F(ShellTest, CacheSkSLWorks) { #endif // !defined(SHELL_ENABLE_VULKAN) // Remove all files generated - fml::FileVisitor remove_visitor = [&remove_visitor]( - const fml::UniqueFD& directory, - const std::string& filename) { - if (fml::IsDirectory(directory, filename.c_str())) { - { // To trigger fml::~UniqueFD before fml::UnlinkDirectory - fml::UniqueFD sub_dir = - fml::OpenDirectoryReadOnly(directory, filename.c_str()); - fml::VisitFiles(sub_dir, remove_visitor); - } - fml::UnlinkDirectory(directory, filename.c_str()); - } else { - fml::UnlinkFile(directory, filename.c_str()); - } - return true; + fml::RemoveFilesInDirectory(dir.fd()); + DestroyShell(std::move(shell)); +} + +TEST_F(PersistentCacheTest, CanPrecompileMetalShaders) { +#if !SHELL_ENABLE_METAL + GTEST_SKIP(); +#endif // !SHELL_ENABLE_METAL + fml::ScopedTemporaryDirectory dir; + PersistentCache::SetCacheDirectoryPath(dir.path()); + PersistentCache::ResetCacheForProcess(); + + auto settings = CreateSettingsForFixture(); + settings.cache_sksl = true; + settings.dump_skp_on_shader_compilation = true; + + fml::AutoResetWaitableEvent first_frame_latch; + settings.frame_rasterized_callback = + [&first_frame_latch](const FrameTiming& t) { + first_frame_latch.Signal(); + }; + + auto sksl_config = RunConfiguration::InferFromSettings(settings); + sksl_config.SetEntrypoint("emptyMain"); + std::unique_ptr shell = + CreateShell(settings, // + GetTaskRunnersForFixture(), // + false, // + nullptr, // + false, // + ShellTestPlatformView::BackendType::kMetalBackend // + ); + PlatformViewNotifyCreated(shell.get()); + RunEngine(shell.get(), std::move(sksl_config)); + + // Initially, we should have no SkSL cache + { + auto empty_cache = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + ASSERT_EQ(empty_cache.size(), 0u); + } + + // Draw something to trigger shader compilations. + LayerTreeBuilder builder = [](std::shared_ptr root) { + SkPath path; + path.addCircle(50, 50, 20); + auto physical_shape_layer = std::make_shared( + SK_ColorRED, SK_ColorBLUE, 1.0f, path, Clip::antiAlias); + root->Add(physical_shape_layer); }; - fml::VisitFiles(dir.fd(), remove_visitor); + PumpOneFrame(shell.get(), 100, 100, builder); + first_frame_latch.Wait(); + WaitForRaster(shell.get()); + WaitForIO(shell.get()); + + // Assert that SkSLs have been generated. + auto filled_cache = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + ASSERT_GT(filled_cache.size(), 0u); + + // Remove all files generated. + fml::RemoveFilesInDirectory(dir.fd()); DestroyShell(std::move(shell)); } @@ -148,7 +203,7 @@ static void CheckTwoSkSLsAreLoaded() { ASSERT_EQ(shaders.size(), 2u); } -TEST_F(ShellTest, CanLoadSkSLsFromAsset) { +TEST_F(PersistentCacheTest, CanLoadSkSLsFromAsset) { // Avoid polluting unit tests output by hiding INFO level logging. fml::LogSettings warning_only = {fml::LOG_WARNING}; fml::ScopedSetLogSettings scoped_set_log_settings(warning_only); @@ -212,7 +267,7 @@ TEST_F(ShellTest, CanLoadSkSLsFromAsset) { fml::UnlinkFile(asset_dir.fd(), PersistentCache::kAssetFileName); } -TEST_F(ShellTest, CanRemoveOldPersistentCache) { +TEST_F(PersistentCacheTest, CanRemoveOldPersistentCache) { fml::ScopedTemporaryDirectory base_dir; ASSERT_TRUE(base_dir.fd().is_valid()); @@ -242,7 +297,7 @@ TEST_F(ShellTest, CanRemoveOldPersistentCache) { fml::RemoveFilesInDirectory(base_dir.fd()); } -TEST_F(ShellTest, CanPurgePersistentCache) { +TEST_F(PersistentCacheTest, CanPurgePersistentCache) { fml::ScopedTemporaryDirectory base_dir; ASSERT_TRUE(base_dir.fd().is_valid()); auto cache_dir = fml::CreateDirectory( @@ -274,7 +329,7 @@ TEST_F(ShellTest, CanPurgePersistentCache) { DestroyShell(std::move(shell)); } -TEST_F(ShellTest, PurgeAllowsFutureSkSLCache) { +TEST_F(PersistentCacheTest, PurgeAllowsFutureSkSLCache) { sk_sp shader_key = SkData::MakeWithCString("key"); sk_sp shader_value = SkData::MakeWithCString("value"); std::string shader_filename = PersistentCache::SkKeyToFilePath(*shader_key); diff --git a/engine/src/flutter/shell/common/shell_test.cc b/engine/src/flutter/shell/common/shell_test.cc index 380da75ec7..e763696618 100644 --- a/engine/src/flutter/shell/common/shell_test.cc +++ b/engine/src/flutter/shell/common/shell_test.cc @@ -315,8 +315,10 @@ std::unique_ptr ShellTest::CreateShell( bool simulate_vsync, std::shared_ptr shell_test_external_view_embedder, - bool is_gpu_disabled) { + bool is_gpu_disabled, + ShellTestPlatformView::BackendType rendering_backend) { const auto vsync_clock = std::make_shared(); + CreateVsyncWaiter create_vsync_waiter = [&]() { if (simulate_vsync) { return static_cast>( @@ -326,19 +328,35 @@ std::unique_ptr ShellTest::CreateShell( std::make_unique(task_runners)); } }; - return Shell::Create( - flutter::PlatformData(), task_runners, settings, - [vsync_clock, &create_vsync_waiter, - shell_test_external_view_embedder](Shell& shell) { + + Shell::CreateCallback platfrom_view_create_callback = + [vsync_clock, // + &create_vsync_waiter, // + shell_test_external_view_embedder, // + rendering_backend // + ](Shell& shell) { return ShellTestPlatformView::Create( - shell, shell.GetTaskRunners(), vsync_clock, - std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend, - shell_test_external_view_embedder); - }, - [](Shell& shell) { return std::make_unique(shell); }, - is_gpu_disabled); + shell, // + shell.GetTaskRunners(), // + vsync_clock, // + std::move(create_vsync_waiter), // + rendering_backend, // + shell_test_external_view_embedder // + ); + }; + + Shell::CreateCallback rasterizer_create_callback = + [](Shell& shell) { return std::make_unique(shell); }; + + return Shell::Create(flutter::PlatformData(), // + task_runners, // + settings, // + platfrom_view_create_callback, // + rasterizer_create_callback, // + is_gpu_disabled // + ); } + void ShellTest::DestroyShell(std::unique_ptr shell) { DestroyShell(std::move(shell), GetTaskRunnersForFixture()); } diff --git a/engine/src/flutter/shell/common/shell_test.h b/engine/src/flutter/shell/common/shell_test.h index 71ce1037f3..dbbbe8c2b8 100644 --- a/engine/src/flutter/shell/common/shell_test.h +++ b/engine/src/flutter/shell/common/shell_test.h @@ -18,6 +18,7 @@ #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell_test_external_view_embedder.h" +#include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiters_test.h" #include "flutter/testing/elf_loader.h" @@ -40,7 +41,9 @@ class ShellTest : public FixtureTest { bool simulate_vsync = false, std::shared_ptr shell_test_external_view_embedder = nullptr, - bool is_gpu_disabled = false); + bool is_gpu_disabled = false, + ShellTestPlatformView::BackendType rendering_backend = + ShellTestPlatformView::BackendType::kDefaultBackend); void DestroyShell(std::unique_ptr shell); void DestroyShell(std::unique_ptr shell, TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); diff --git a/engine/src/flutter/shell/common/shell_test_platform_view.cc b/engine/src/flutter/shell/common/shell_test_platform_view.cc index 29bd24d313..a0a75c4647 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view.cc +++ b/engine/src/flutter/shell/common/shell_test_platform_view.cc @@ -10,6 +10,9 @@ #ifdef SHELL_ENABLE_VULKAN #include "flutter/shell/common/shell_test_platform_view_vulkan.h" #endif // SHELL_ENABLE_VULKAN +#ifdef SHELL_ENABLE_METAL +#include "flutter/shell/common/shell_test_platform_view_metal.h" +#endif // SHELL_ENABLE_METAL namespace flutter { namespace testing { @@ -38,6 +41,13 @@ std::unique_ptr ShellTestPlatformView::Create( delegate, task_runners, vsync_clock, create_vsync_waiter, shell_test_external_view_embedder); #endif // SHELL_ENABLE_VULKAN +#ifdef SHELL_ENABLE_METAL + case BackendType::kMetalBackend: + return std::make_unique( + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); +#endif // SHELL_ENABLE_METAL + default: FML_LOG(FATAL) << "No backends supported for ShellTestPlatformView"; return nullptr; diff --git a/engine/src/flutter/shell/common/shell_test_platform_view.h b/engine/src/flutter/shell/common/shell_test_platform_view.h index 9a850b1d48..9ba69ec5bb 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view.h +++ b/engine/src/flutter/shell/common/shell_test_platform_view.h @@ -17,6 +17,7 @@ class ShellTestPlatformView : public PlatformView { enum class BackendType { kGLBackend, kVulkanBackend, + kMetalBackend, kDefaultBackend, }; 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 6c1214abb9..8717991c10 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 @@ -28,6 +28,7 @@ std::unique_ptr ShellTestPlatformViewGL::CreateVSyncWaiter() { return create_vsync_waiter_(); } +// |ShellTestPlatformView| void ShellTestPlatformViewGL::SimulateVSync() { vsync_clock_->SimulateVSync(); } diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_gl.h b/engine/src/flutter/shell/common/shell_test_platform_view_gl.h index bb6d744865..d072dd9731 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view_gl.h +++ b/engine/src/flutter/shell/common/shell_test_platform_view_gl.h @@ -23,8 +23,10 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr shell_test_external_view_embedder); + // |ShellTestPlatformView| virtual ~ShellTestPlatformViewGL() override; + // |ShellTestPlatformView| virtual void SimulateVSync() override; private: diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_metal.h b/engine/src/flutter/shell/common/shell_test_platform_view_metal.h new file mode 100644 index 0000000000..319fa04f7a --- /dev/null +++ b/engine/src/flutter/shell/common/shell_test_platform_view_metal.h @@ -0,0 +1,71 @@ +// 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_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_METAL_H_ +#define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_METAL_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/common/shell_test_platform_view.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" + +namespace flutter { +namespace testing { + +class DarwinContextMetal; + +class ShellTestPlatformViewMetal final : public ShellTestPlatformView, + public GPUSurfaceMetalDelegate { + public: + ShellTestPlatformViewMetal(PlatformView::Delegate& delegate, + TaskRunners task_runners, + std::shared_ptr vsync_clock, + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); + + // |ShellTestPlatformView| + virtual ~ShellTestPlatformViewMetal() override; + + private: + const std::unique_ptr metal_context_; + const CreateVsyncWaiter create_vsync_waiter_; + const std::shared_ptr vsync_clock_; + const std::shared_ptr + shell_test_external_view_embedder_; + + // |ShellTestPlatformView| + virtual void SimulateVSync() override; + + // |PlatformView| + std::unique_ptr CreateVSyncWaiter() override; + + // |PlatformView| + std::shared_ptr CreateExternalViewEmbedder() override; + + // |PlatformView| + PointerDataDispatcherMaker GetDispatcherMaker() override; + + // |PlatformView| + std::unique_ptr CreateRenderingSurface() override; + + // |GPUSurfaceMetalDelegate| + GPUCAMetalLayerHandle GetCAMetalLayer( + const SkISize& frame_info) const override; + + // |GPUSurfaceMetalDelegate| + bool PresentDrawable(GrMTLHandle drawable) const override; + + // |GPUSurfaceMetalDelegate| + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; + + // |GPUSurfaceMetalDelegate| + bool PresentTexture(GPUMTLTextureInfo texture) const override; + + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformViewMetal); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_METAL_H_ diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm b/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm new file mode 100644 index 0000000000..585d1e0e04 --- /dev/null +++ b/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/common/shell_test_platform_view_metal.h" + +#import + +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/gpu/gpu_surface_metal.h" +#include "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h" + +namespace flutter { +namespace testing { + +static fml::scoped_nsprotocol> CreateOffscreenTexture(id device) { + auto descriptor = + [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:800 + height:600 + mipmapped:NO]; + descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; + return fml::scoped_nsprotocol>{[device newTextureWithDescriptor:descriptor]}; +} + +// This is out of the header so that shell_test_platform_view_metal.h can be included in +// non-Objective-C TUs. +class DarwinContextMetal { + public: + DarwinContextMetal() + : context_([[FlutterDarwinContextMetal alloc] initWithDefaultMTLDevice]), + offscreen_texture_(CreateOffscreenTexture([context_.get() device])) {} + + ~DarwinContextMetal() = default; + + fml::scoped_nsobject context() const { return context_; } + + fml::scoped_nsprotocol> offscreen_texture() const { return offscreen_texture_; } + + GPUMTLTextureInfo offscreen_texture_info() const { + GPUMTLTextureInfo info = {}; + info.texture_id = 0; + info.texture = reinterpret_cast(offscreen_texture_.get()); + return info; + } + + private: + const fml::scoped_nsobject context_; + const fml::scoped_nsprotocol> offscreen_texture_; + + FML_DISALLOW_COPY_AND_ASSIGN(DarwinContextMetal); +}; + +ShellTestPlatformViewMetal::ShellTestPlatformViewMetal( + PlatformView::Delegate& delegate, + TaskRunners task_runners, + std::shared_ptr vsync_clock, + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr shell_test_external_view_embedder) + : ShellTestPlatformView(delegate, std::move(task_runners)), + GPUSurfaceMetalDelegate(MTLRenderTargetType::kMTLTexture), + metal_context_(std::make_unique()), + create_vsync_waiter_(std::move(create_vsync_waiter)), + vsync_clock_(vsync_clock), + shell_test_external_view_embedder_(shell_test_external_view_embedder) { + FML_CHECK([metal_context_->context() mainContext] != nil); +} + +ShellTestPlatformViewMetal::~ShellTestPlatformViewMetal() = default; + +std::unique_ptr ShellTestPlatformViewMetal::CreateVSyncWaiter() { + return create_vsync_waiter_(); +} + +// |ShellTestPlatformView| +void ShellTestPlatformViewMetal::SimulateVSync() { + vsync_clock_->SimulateVSync(); +} + +// |PlatformView| +std::shared_ptr ShellTestPlatformViewMetal::CreateExternalViewEmbedder() { + return shell_test_external_view_embedder_; +} + +// |PlatformView| +PointerDataDispatcherMaker ShellTestPlatformViewMetal::GetDispatcherMaker() { + return [](DefaultPointerDataDispatcher::Delegate& delegate) { + return std::make_unique(delegate); + }; +} + +// |PlatformView| +std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingSurface() { + return std::make_unique(this, [metal_context_->context() mainContext]); +} + +// |GPUSurfaceMetalDelegate| +GPUCAMetalLayerHandle ShellTestPlatformViewMetal::GetCAMetalLayer(const SkISize& frame_info) const { + FML_CHECK(false) << "A Metal Delegate configured with MTLRenderTargetType::kMTLTexture was asked " + "to acquire a layer."; + return nullptr; +} + +// |GPUSurfaceMetalDelegate| +bool ShellTestPlatformViewMetal::PresentDrawable(GrMTLHandle drawable) const { + FML_CHECK(false) << "A Metal Delegate configured with MTLRenderTargetType::kMTLTexture was asked " + "to present a layer drawable."; + return true; +} + +// |GPUSurfaceMetalDelegate| +GPUMTLTextureInfo ShellTestPlatformViewMetal::GetMTLTexture(const SkISize& frame_info) const { + return metal_context_->offscreen_texture_info(); +} + +// |GPUSurfaceMetalDelegate| +bool ShellTestPlatformViewMetal::PresentTexture(GPUMTLTextureInfo texture) const { + // The texture resides offscreen. There is nothing to render to. + return true; +} + +} // namespace testing +} // namespace flutter diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index 0daddce767..5a21085446 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -2765,5 +2765,78 @@ TEST_F(ShellTest, UpdateAssetResolverByTypeDoesNotReplaceMismatchType) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); } +TEST_F(ShellTest, CanCreateShellsWithGLBackend) { +#if !SHELL_ENABLE_GL + // GL emulation does not exist on Fuchsia. + GTEST_SKIP(); +#endif // !SHELL_ENABLE_GL + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = + CreateShell(settings, // + GetTaskRunnersForFixture(), // + false, // + nullptr, // + false, // + ShellTestPlatformView::BackendType::kGLBackend // + ); + ASSERT_NE(shell, nullptr); + ASSERT_TRUE(shell->IsSetup()); + PlatformViewNotifyCreated(shell.get()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + PlatformViewNotifyDestroyed(shell.get()); + DestroyShell(std::move(shell)); +} + +TEST_F(ShellTest, CanCreateShellsWithVulkanBackend) { +#if !SHELL_ENABLE_VULKAN + GTEST_SKIP(); +#endif // !SHELL_ENABLE_VULKAN + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = + CreateShell(settings, // + GetTaskRunnersForFixture(), // + false, // + nullptr, // + false, // + ShellTestPlatformView::BackendType::kVulkanBackend // + ); + ASSERT_NE(shell, nullptr); + ASSERT_TRUE(shell->IsSetup()); + PlatformViewNotifyCreated(shell.get()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + PlatformViewNotifyDestroyed(shell.get()); + DestroyShell(std::move(shell)); +} + +TEST_F(ShellTest, CanCreateShellsWithMetalBackend) { +#if !SHELL_ENABLE_METAL + GTEST_SKIP(); +#endif // !SHELL_ENABLE_METAL + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = + CreateShell(settings, // + GetTaskRunnersForFixture(), // + false, // + nullptr, // + false, // + ShellTestPlatformView::BackendType::kMetalBackend // + ); + ASSERT_NE(shell, nullptr); + ASSERT_TRUE(shell->IsSetup()); + PlatformViewNotifyCreated(shell.get()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + PlatformViewNotifyDestroyed(shell.get()); + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/gpu/gpu_surface_gl.cc b/engine/src/flutter/shell/gpu/gpu_surface_gl.cc index a7d1e4d444..b78c0a5bf1 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl.cc @@ -70,14 +70,7 @@ sk_sp GPUSurfaceGL::MakeGLContext( context->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize); - std::vector caches = - PersistentCache::GetCacheForProcess()->LoadSkSLs(); - int compiled_count = 0; - for (const auto& cache : caches) { - compiled_count += context->precompileShader(*cache.first, *cache.second); - } - FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " - << compiled_count; + PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(context.get()); return context; } diff --git a/engine/src/flutter/shell/gpu/gpu_surface_metal.h b/engine/src/flutter/shell/gpu/gpu_surface_metal.h index 0ef1b8c4a1..c99073f2a1 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal.h +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal.h @@ -29,6 +29,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface { const MTLRenderTargetType render_target_type_; GrMTLHandle next_drawable_ = nullptr; sk_sp context_; + GrDirectContext* precompiled_sksl_context_ = nullptr; // |Surface| std::unique_ptr AcquireFrame(const SkISize& size) override; @@ -50,6 +51,8 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetal : public Surface { void ReleaseUnusedDrawableIfNecessary(); + void PrecompileKnownSkSLsIfNecessary(); + FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetal); }; diff --git a/engine/src/flutter/shell/gpu/gpu_surface_metal.mm b/engine/src/flutter/shell/gpu/gpu_surface_metal.mm index 96ec00a325..0267a5ac86 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal.mm +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal.mm @@ -6,6 +6,7 @@ #import +#include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/trace_event.h" @@ -32,6 +33,16 @@ bool GPUSurfaceMetal::IsValid() { return context_ != nullptr; } +void GPUSurfaceMetal::PrecompileKnownSkSLsIfNecessary() { + auto* current_context = GetContext(); + if (current_context == precompiled_sksl_context_) { + // Known SkSLs have already been prepared in this context. + return; + } + precompiled_sksl_context_ = current_context; + flutter::PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(precompiled_sksl_context_); +} + // |Surface| std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& frame_size) { if (!IsValid()) { @@ -44,6 +55,8 @@ std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& frame return nullptr; } + PrecompileKnownSkSLsIfNecessary(); + switch (render_target_type_) { case MTLRenderTargetType::kCAMetalLayer: return AcquireFrameFromCAMetalLayer(frame_size); @@ -155,6 +168,10 @@ GrDirectContext* GPUSurfaceMetal::GetContext() { // |Surface| std::unique_ptr GPUSurfaceMetal::MakeRenderContextCurrent() { + // A context may either be necessary to render to the surface or to snapshot an offscreen + // surface. Either way, SkSL precompilation must be attempted. + PrecompileKnownSkSLsIfNecessary(); + // This backend has no such concept. return std::make_unique(true); } diff --git a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm index 7955b72aa7..6e88205db3 100644 --- a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm +++ b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm @@ -59,6 +59,9 @@ static GrContextOptions CreateMetalGrContextOptions() { return nil; } + // The devices are in the same "sharegroup" because they share the same device and command + // queues for now. When the resource context gets its own transfer queue, this will have to be + // refactored. _mainContext = [self createGrContext]; _resourceContext = [self createGrContext];