Wire up Metal shader precompilation from offline training runs. (flutter/engine#25644)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<SkData> ParseBase64(const std::string& input) {
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<PersistentCache::SkSLCache> 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<int64_t>(this), // Trace Counter ID
|
||||
"Successful", precompiled_count);
|
||||
return precompiled_count;
|
||||
}
|
||||
|
||||
std::vector<PersistentCache::SkSLCache> PersistentCache::LoadSkSLs() const {
|
||||
TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs");
|
||||
std::vector<PersistentCache::SkSLCache> result;
|
||||
fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory,
|
||||
|
||||
@@ -72,7 +72,22 @@ class PersistentCache : public GrContextOptions::PersistentCache {
|
||||
using SkSLCache = std::pair<sk_sp<SkData>, sk_sp<SkData>>;
|
||||
|
||||
/// Load all the SkSL shader caches in the right directory.
|
||||
std::vector<SkSLCache> LoadSkSLs();
|
||||
std::vector<SkSLCache> 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<std::unique_ptr<fml::Mapping>> GetSkpsFromAssetManager() const;
|
||||
@@ -82,7 +97,9 @@ class PersistentCache : public GrContextOptions::PersistentCache {
|
||||
static void SetAssetManager(std::shared_ptr<AssetManager> value);
|
||||
|
||||
static bool cache_sksl() { return cache_sksl_; }
|
||||
|
||||
static void SetCacheSkSL(bool value);
|
||||
|
||||
static void MarkStrategySet() { strategy_set_ = true; }
|
||||
|
||||
static constexpr char kSkSLSubdirName[] = "sksl";
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
using PersistentCacheTest = ShellTest;
|
||||
|
||||
static void WaitForIO(Shell* shell) {
|
||||
std::promise<bool> 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<bool> 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> 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<ContainerLayer> root) {
|
||||
SkPath path;
|
||||
path.addCircle(50, 50, 20);
|
||||
auto physical_shape_layer = std::make_shared<PhysicalShapeLayer>(
|
||||
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<SkData> shader_key = SkData::MakeWithCString("key");
|
||||
sk_sp<SkData> shader_value = SkData::MakeWithCString("value");
|
||||
std::string shader_filename = PersistentCache::SkKeyToFilePath(*shader_key);
|
||||
|
||||
@@ -315,8 +315,10 @@ std::unique_ptr<Shell> ShellTest::CreateShell(
|
||||
bool simulate_vsync,
|
||||
std::shared_ptr<ShellTestExternalViewEmbedder>
|
||||
shell_test_external_view_embedder,
|
||||
bool is_gpu_disabled) {
|
||||
bool is_gpu_disabled,
|
||||
ShellTestPlatformView::BackendType rendering_backend) {
|
||||
const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
|
||||
|
||||
CreateVsyncWaiter create_vsync_waiter = [&]() {
|
||||
if (simulate_vsync) {
|
||||
return static_cast<std::unique_ptr<VsyncWaiter>>(
|
||||
@@ -326,19 +328,35 @@ std::unique_ptr<Shell> ShellTest::CreateShell(
|
||||
std::make_unique<VsyncWaiterFallback>(task_runners));
|
||||
}
|
||||
};
|
||||
return Shell::Create(
|
||||
flutter::PlatformData(), task_runners, settings,
|
||||
[vsync_clock, &create_vsync_waiter,
|
||||
shell_test_external_view_embedder](Shell& shell) {
|
||||
|
||||
Shell::CreateCallback<PlatformView> 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<Rasterizer>(shell); },
|
||||
is_gpu_disabled);
|
||||
shell, //
|
||||
shell.GetTaskRunners(), //
|
||||
vsync_clock, //
|
||||
std::move(create_vsync_waiter), //
|
||||
rendering_backend, //
|
||||
shell_test_external_view_embedder //
|
||||
);
|
||||
};
|
||||
|
||||
Shell::CreateCallback<Rasterizer> rasterizer_create_callback =
|
||||
[](Shell& shell) { return std::make_unique<Rasterizer>(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> shell) {
|
||||
DestroyShell(std::move(shell), GetTaskRunnersForFixture());
|
||||
}
|
||||
|
||||
@@ -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<ShellTestExternalViewEmbedder>
|
||||
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> shell);
|
||||
void DestroyShell(std::unique_ptr<Shell> shell, TaskRunners task_runners);
|
||||
TaskRunners GetTaskRunnersForFixture();
|
||||
|
||||
@@ -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> 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<ShellTestPlatformViewMetal>(
|
||||
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;
|
||||
|
||||
@@ -17,6 +17,7 @@ class ShellTestPlatformView : public PlatformView {
|
||||
enum class BackendType {
|
||||
kGLBackend,
|
||||
kVulkanBackend,
|
||||
kMetalBackend,
|
||||
kDefaultBackend,
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ std::unique_ptr<VsyncWaiter> ShellTestPlatformViewGL::CreateVSyncWaiter() {
|
||||
return create_vsync_waiter_();
|
||||
}
|
||||
|
||||
// |ShellTestPlatformView|
|
||||
void ShellTestPlatformViewGL::SimulateVSync() {
|
||||
vsync_clock_->SimulateVSync();
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView,
|
||||
std::shared_ptr<ShellTestExternalViewEmbedder>
|
||||
shell_test_external_view_embedder);
|
||||
|
||||
// |ShellTestPlatformView|
|
||||
virtual ~ShellTestPlatformViewGL() override;
|
||||
|
||||
// |ShellTestPlatformView|
|
||||
virtual void SimulateVSync() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -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<ShellTestVsyncClock> vsync_clock,
|
||||
CreateVsyncWaiter create_vsync_waiter,
|
||||
std::shared_ptr<ShellTestExternalViewEmbedder>
|
||||
shell_test_external_view_embedder);
|
||||
|
||||
// |ShellTestPlatformView|
|
||||
virtual ~ShellTestPlatformViewMetal() override;
|
||||
|
||||
private:
|
||||
const std::unique_ptr<DarwinContextMetal> metal_context_;
|
||||
const CreateVsyncWaiter create_vsync_waiter_;
|
||||
const std::shared_ptr<ShellTestVsyncClock> vsync_clock_;
|
||||
const std::shared_ptr<ShellTestExternalViewEmbedder>
|
||||
shell_test_external_view_embedder_;
|
||||
|
||||
// |ShellTestPlatformView|
|
||||
virtual void SimulateVSync() override;
|
||||
|
||||
// |PlatformView|
|
||||
std::unique_ptr<VsyncWaiter> CreateVSyncWaiter() override;
|
||||
|
||||
// |PlatformView|
|
||||
std::shared_ptr<ExternalViewEmbedder> CreateExternalViewEmbedder() override;
|
||||
|
||||
// |PlatformView|
|
||||
PointerDataDispatcherMaker GetDispatcherMaker() override;
|
||||
|
||||
// |PlatformView|
|
||||
std::unique_ptr<Surface> 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_
|
||||
@@ -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 <Metal/Metal.h>
|
||||
|
||||
#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<id<MTLTexture>> CreateOffscreenTexture(id<MTLDevice> device) {
|
||||
auto descriptor =
|
||||
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:800
|
||||
height:600
|
||||
mipmapped:NO];
|
||||
descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
|
||||
return fml::scoped_nsprotocol<id<MTLTexture>>{[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<FlutterDarwinContextMetal> context() const { return context_; }
|
||||
|
||||
fml::scoped_nsprotocol<id<MTLTexture>> offscreen_texture() const { return offscreen_texture_; }
|
||||
|
||||
GPUMTLTextureInfo offscreen_texture_info() const {
|
||||
GPUMTLTextureInfo info = {};
|
||||
info.texture_id = 0;
|
||||
info.texture = reinterpret_cast<GPUMTLTextureHandle>(offscreen_texture_.get());
|
||||
return info;
|
||||
}
|
||||
|
||||
private:
|
||||
const fml::scoped_nsobject<FlutterDarwinContextMetal> context_;
|
||||
const fml::scoped_nsprotocol<id<MTLTexture>> offscreen_texture_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(DarwinContextMetal);
|
||||
};
|
||||
|
||||
ShellTestPlatformViewMetal::ShellTestPlatformViewMetal(
|
||||
PlatformView::Delegate& delegate,
|
||||
TaskRunners task_runners,
|
||||
std::shared_ptr<ShellTestVsyncClock> vsync_clock,
|
||||
CreateVsyncWaiter create_vsync_waiter,
|
||||
std::shared_ptr<ShellTestExternalViewEmbedder> shell_test_external_view_embedder)
|
||||
: ShellTestPlatformView(delegate, std::move(task_runners)),
|
||||
GPUSurfaceMetalDelegate(MTLRenderTargetType::kMTLTexture),
|
||||
metal_context_(std::make_unique<DarwinContextMetal>()),
|
||||
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<VsyncWaiter> ShellTestPlatformViewMetal::CreateVSyncWaiter() {
|
||||
return create_vsync_waiter_();
|
||||
}
|
||||
|
||||
// |ShellTestPlatformView|
|
||||
void ShellTestPlatformViewMetal::SimulateVSync() {
|
||||
vsync_clock_->SimulateVSync();
|
||||
}
|
||||
|
||||
// |PlatformView|
|
||||
std::shared_ptr<ExternalViewEmbedder> ShellTestPlatformViewMetal::CreateExternalViewEmbedder() {
|
||||
return shell_test_external_view_embedder_;
|
||||
}
|
||||
|
||||
// |PlatformView|
|
||||
PointerDataDispatcherMaker ShellTestPlatformViewMetal::GetDispatcherMaker() {
|
||||
return [](DefaultPointerDataDispatcher::Delegate& delegate) {
|
||||
return std::make_unique<SmoothPointerDataDispatcher>(delegate);
|
||||
};
|
||||
}
|
||||
|
||||
// |PlatformView|
|
||||
std::unique_ptr<Surface> ShellTestPlatformViewMetal::CreateRenderingSurface() {
|
||||
return std::make_unique<GPUSurfaceMetal>(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
|
||||
@@ -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> 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> 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> 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
|
||||
|
||||
@@ -70,14 +70,7 @@ sk_sp<GrDirectContext> GPUSurfaceGL::MakeGLContext(
|
||||
|
||||
context->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
|
||||
|
||||
std::vector<PersistentCache::SkSLCache> 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;
|
||||
}
|
||||
|
||||
@@ -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<GrDirectContext> context_;
|
||||
GrDirectContext* precompiled_sksl_context_ = nullptr;
|
||||
|
||||
// |Surface|
|
||||
std::unique_ptr<SurfaceFrame> 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);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
#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<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& frame_size) {
|
||||
if (!IsValid()) {
|
||||
@@ -44,6 +55,8 @@ std::unique_ptr<SurfaceFrame> 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<GLContextResult> 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<GLContextDefaultResult>(true);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user