blob: 534aa6a7c714e702c2b3526f4c80acadc535eacf [file] [log] [blame]
// Copyright 2020 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 <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/component_context.h>
#include "assets/directory_asset_bundle.h"
#include "flutter/common/graphics/persistent_cache.h"
#include "flutter/fml/memory/ref_ptr.h"
#include "flutter/fml/message_loop_impl.h"
#include "flutter/fml/task_runner.h"
#include "flutter/shell/common/serialization_callbacks.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/shell/platform/fuchsia/flutter/gfx_session_connection.h"
#include "flutter/shell/platform/fuchsia/flutter/logging.h"
#include "flutter/shell/platform/fuchsia/flutter/runner.h"
#include "gtest/gtest.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkSerialProcs.h"
using namespace flutter_runner;
using namespace flutter;
namespace flutter_runner {
namespace testing {
namespace {
std::string GetCurrentTestName() {
return ::testing::UnitTest::GetInstance()->current_test_info()->name();
}
} // namespace
class MockTaskRunner : public fml::BasicTaskRunner {
public:
MockTaskRunner() {}
virtual ~MockTaskRunner() {}
void PostTask(const fml::closure& task) override {
outstanding_tasks_.push(task);
}
int GetTaskCount() { return task_count_; }
void Run() {
while (!outstanding_tasks_.empty()) {
outstanding_tasks_.front()();
outstanding_tasks_.pop();
task_count_++;
}
}
private:
int task_count_ = 0;
std::queue<const fml::closure> outstanding_tasks_;
};
class EngineTest : public ::testing::Test {
public:
void WarmupSkps(
uint64_t width,
uint64_t height,
std::shared_ptr<flutter::AssetManager> asset_manager,
std::optional<const std::vector<std::string>> skp_names,
std::optional<std::function<void(uint32_t)>> completion_callback) {
// Have to create a message loop so default async dispatcher gets set,
// otherwise we segfault creating the VulkanSurfaceProducer
auto loop = fml::MessageLoopImpl::Create();
context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
scenic_ = context_->svc()->Connect<fuchsia::ui::scenic::Scenic>();
session_.emplace(scenic_.get());
surface_producer_ =
std::make_unique<VulkanSurfaceProducer>(&session_.value());
Engine::WarmupSkps(&concurrent_task_runner_, &raster_task_runner_,
*surface_producer_, width, height, asset_manager,
std::nullopt, std::nullopt);
}
protected:
MockTaskRunner concurrent_task_runner_;
MockTaskRunner raster_task_runner_;
std::unique_ptr<VulkanSurfaceProducer> surface_producer_;
std::unique_ptr<sys::ComponentContext> context_;
fuchsia::ui::scenic::ScenicPtr scenic_;
std::optional<scenic::Session> session_;
};
TEST_F(EngineTest, ThreadNames) {
std::string prefix = GetCurrentTestName();
flutter::ThreadHost engine_thread_host = Engine::CreateThreadHost(prefix);
char thread_name[ZX_MAX_NAME_LEN];
zx::thread::self()->get_property(ZX_PROP_NAME, thread_name,
sizeof(thread_name));
EXPECT_EQ(std::string(thread_name), prefix + std::string(".platform"));
EXPECT_EQ(engine_thread_host.platform_thread, nullptr);
engine_thread_host.raster_thread->GetTaskRunner()->PostTask([&prefix]() {
char thread_name[ZX_MAX_NAME_LEN];
zx::thread::self()->get_property(ZX_PROP_NAME, thread_name,
sizeof(thread_name));
EXPECT_EQ(std::string(thread_name), prefix + std::string(".raster"));
});
engine_thread_host.raster_thread->Join();
engine_thread_host.ui_thread->GetTaskRunner()->PostTask([&prefix]() {
char thread_name[ZX_MAX_NAME_LEN];
zx::thread::self()->get_property(ZX_PROP_NAME, thread_name,
sizeof(thread_name));
EXPECT_EQ(std::string(thread_name), prefix + std::string(".ui"));
});
engine_thread_host.ui_thread->Join();
engine_thread_host.io_thread->GetTaskRunner()->PostTask([&prefix]() {
char thread_name[ZX_MAX_NAME_LEN];
zx::thread::self()->get_property(ZX_PROP_NAME, thread_name,
sizeof(thread_name));
EXPECT_EQ(std::string(thread_name), prefix + std::string(".io"));
});
engine_thread_host.io_thread->Join();
}
TEST_F(EngineTest, SkpWarmup) {
SkISize draw_size = SkISize::Make(100, 100);
SkPictureRecorder recorder;
auto canvas = recorder.beginRecording(draw_size.width(), draw_size.height());
// adapted from https://fiddle.skia.org/c/@Canvas_drawLine
SkPaint paint;
paint.setColor(0xFF9a67be);
paint.setStrokeWidth(20);
canvas->drawLine(0, 0, draw_size.width(), draw_size.height(), paint);
canvas->drawLine(0, draw_size.height(), draw_size.width(), 0, paint);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
SkSerialProcs procs = {0};
procs.fImageProc = SerializeImageWithoutData;
procs.fTypefaceProc = SerializeTypefaceWithoutData;
sk_sp<SkData> data = picture->serialize(&procs);
ASSERT_TRUE(data);
ASSERT_GT(data->size(), 0u);
fml::NonOwnedMapping mapping(data->bytes(), data->size());
fml::ScopedTemporaryDirectory asset_dir;
fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
asset_dir.path().c_str(), false, fml::FilePermission::kRead);
fml::UniqueFD subdir_fd = fml::OpenDirectory(asset_dir_fd, "shaders", true,
fml::FilePermission::kReadWrite);
bool success = fml::WriteAtomically(subdir_fd, "test.skp", mapping);
ASSERT_TRUE(success);
auto asset_manager = std::make_shared<AssetManager>();
asset_manager->PushBack(
std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
WarmupSkps(draw_size.width(), draw_size.height(), asset_manager, std::nullopt,
std::nullopt);
concurrent_task_runner_.Run();
raster_task_runner_.Run();
EXPECT_EQ(concurrent_task_runner_.GetTaskCount(), 1);
EXPECT_EQ(raster_task_runner_.GetTaskCount(), 1);
}
TEST_F(EngineTest, SkpWarmupAsync) {
SkISize draw_size = SkISize::Make(100, 100);
SkPictureRecorder recorder;
auto canvas = recorder.beginRecording(draw_size.width(), draw_size.height());
// adapted from https://fiddle.skia.org/c/@Canvas_drawLine
SkPaint paint;
paint.setColor(0xFF9a67be);
paint.setStrokeWidth(20);
canvas->drawLine(0, 0, draw_size.width(), draw_size.height(), paint);
canvas->drawLine(0, draw_size.height(), draw_size.width(), 0, paint);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
SkSerialProcs procs = {0};
procs.fImageProc = SerializeImageWithoutData;
procs.fTypefaceProc = SerializeTypefaceWithoutData;
sk_sp<SkData> data = picture->serialize(&procs);
ASSERT_TRUE(data);
ASSERT_GT(data->size(), 0u);
fml::NonOwnedMapping mapping(data->bytes(), data->size());
fml::ScopedTemporaryDirectory asset_dir;
fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
asset_dir.path().c_str(), false, fml::FilePermission::kRead);
fml::UniqueFD subdir_fd = fml::OpenDirectory(asset_dir_fd, "shaders", true,
fml::FilePermission::kReadWrite);
std::string skp_name = "test.skp";
bool success = fml::WriteAtomically(subdir_fd, skp_name.c_str(), mapping);
ASSERT_TRUE(success);
auto asset_manager = std::make_shared<AssetManager>();
asset_manager->PushBack(
std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
std::vector<std::string> skp_names = {skp_name};
WarmupSkps(draw_size.width(), draw_size.height(), asset_manager, skp_names,
[](uint32_t count) { EXPECT_EQ(1u, count); });
concurrent_task_runner_.Run();
raster_task_runner_.Run();
EXPECT_EQ(concurrent_task_runner_.GetTaskCount(), 1);
EXPECT_EQ(raster_task_runner_.GetTaskCount(), 1);
}
} // namespace testing
} // namespace flutter_runner