Tuning resource cache max bytes in lightweight engine scenarios (flutter/engine#32156)

This commit is contained in:
ColdPaleLight 2022-04-01 12:11:05 +08:00 committed by GitHub
parent ebbfcee25d
commit 31e332ee32
15 changed files with 381 additions and 9 deletions

View File

@ -1108,6 +1108,9 @@ FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h
FILE: ../../../flutter/shell/common/rasterizer.cc
FILE: ../../../flutter/shell/common/rasterizer.h
FILE: ../../../flutter/shell/common/rasterizer_unittests.cc
FILE: ../../../flutter/shell/common/resource_cache_limit_calculator.cc
FILE: ../../../flutter/shell/common/resource_cache_limit_calculator.h
FILE: ../../../flutter/shell/common/resource_cache_limit_calculator_unittests.cc
FILE: ../../../flutter/shell/common/run_configuration.cc
FILE: ../../../flutter/shell/common/run_configuration.h
FILE: ../../../flutter/shell/common/serialization_callbacks.cc

View File

@ -294,6 +294,9 @@ struct Settings {
/// https://github.com/dart-lang/sdk/blob/ca64509108b3e7219c50d6c52877c85ab6a35ff2/runtime/vm/flag_list.h#L150
int64_t old_gen_heap_size = -1;
// Max bytes threshold of resource cache, or 0 for unlimited.
size_t resource_cache_max_bytes_threshold = 0;
/// A timestamp representing when the engine started. The value is based
/// on the clock used by the Dart timeline APIs. This timestamp is used
/// to log a timeline event that tracks the latency of engine startup.

View File

@ -81,6 +81,8 @@ source_set("common") {
"pointer_data_dispatcher.h",
"rasterizer.cc",
"rasterizer.h",
"resource_cache_limit_calculator.cc",
"resource_cache_limit_calculator.h",
"run_configuration.cc",
"run_configuration.h",
"serialization_callbacks.cc",
@ -270,6 +272,7 @@ if (enable_unittests) {
"persistent_cache_unittests.cc",
"pipeline_unittests.cc",
"rasterizer_unittests.cc",
"resource_cache_limit_calculator_unittests.cc",
"shell_unittests.cc",
"skp_shader_warmup_unittests.cc",
"switches_unittests.cc",

View File

@ -0,0 +1,25 @@
// 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/resource_cache_limit_calculator.h"
namespace flutter {
size_t ResourceCacheLimitCalculator::GetResourceCacheMaxBytes() {
size_t max_bytes = 0;
size_t max_bytes_threshold = max_bytes_threshold_ > 0
? max_bytes_threshold_
: std::numeric_limits<size_t>::max();
std::vector<fml::WeakPtr<ResourceCacheLimitItem>> live_items;
for (auto item : items_) {
if (item) {
live_items.push_back(item);
max_bytes += item->GetResourceCacheLimit();
}
}
items_ = std::move(live_items);
return std::min(max_bytes, max_bytes_threshold);
}
} // namespace flutter

View File

@ -0,0 +1,48 @@
// 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_RESOURCE_CACHE_LIMIT_CALCULATOR_
#define FLUTTER_SHELL_COMMON_RESOURCE_CACHE_LIMIT_CALCULATOR_
#include <cstdint>
#include <unordered_map>
#include "flutter/fml/macros.h"
#include "flutter/fml/memory/weak_ptr.h"
namespace flutter {
class ResourceCacheLimitItem {
public:
// The expected GPU resource cache limit in bytes. This will be called on the
// platform thread.
virtual size_t GetResourceCacheLimit() = 0;
protected:
virtual ~ResourceCacheLimitItem() = default;
};
class ResourceCacheLimitCalculator {
public:
ResourceCacheLimitCalculator(size_t max_bytes_threshold)
: max_bytes_threshold_(max_bytes_threshold) {}
~ResourceCacheLimitCalculator() = default;
// This will be called on the platform thread.
void AddResourceCacheLimitItem(fml::WeakPtr<ResourceCacheLimitItem> item) {
items_.push_back(item);
}
// The maximum GPU resource cache limit in bytes calculated by
// 'ResourceCacheLimitItem's. This will be called on the platform thread.
size_t GetResourceCacheMaxBytes();
private:
std::vector<fml::WeakPtr<ResourceCacheLimitItem>> items_;
size_t max_bytes_threshold_;
FML_DISALLOW_COPY_AND_ASSIGN(ResourceCacheLimitCalculator);
};
} // namespace flutter
#endif // FLUTTER_SHELL_COMMON_RESOURCE_CACHE_LIMIT_CALCULATOR_

View File

@ -0,0 +1,54 @@
// 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/resource_cache_limit_calculator.h"
#include "gtest/gtest.h"
namespace flutter {
namespace testing {
class TestResourceCacheLimitItem : public ResourceCacheLimitItem {
public:
explicit TestResourceCacheLimitItem(size_t resource_cache_limit)
: resource_cache_limit_(resource_cache_limit), weak_factory_(this) {}
size_t GetResourceCacheLimit() override { return resource_cache_limit_; }
fml::WeakPtr<TestResourceCacheLimitItem> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
size_t resource_cache_limit_;
fml::WeakPtrFactory<TestResourceCacheLimitItem> weak_factory_;
};
TEST(ResourceCacheLimitCalculatorTest, GetResourceCacheMaxBytes) {
ResourceCacheLimitCalculator calculator(800U);
auto item1 = std::make_unique<TestResourceCacheLimitItem>(100.0);
calculator.AddResourceCacheLimitItem(item1->GetWeakPtr());
EXPECT_EQ(calculator.GetResourceCacheMaxBytes(), static_cast<size_t>(100U));
auto item2 = std::make_unique<TestResourceCacheLimitItem>(200.0);
calculator.AddResourceCacheLimitItem(item2->GetWeakPtr());
EXPECT_EQ(calculator.GetResourceCacheMaxBytes(), static_cast<size_t>(300U));
auto item3 = std::make_unique<TestResourceCacheLimitItem>(300.0);
calculator.AddResourceCacheLimitItem(item3->GetWeakPtr());
EXPECT_EQ(calculator.GetResourceCacheMaxBytes(), static_cast<size_t>(600U));
auto item4 = std::make_unique<TestResourceCacheLimitItem>(400.0);
calculator.AddResourceCacheLimitItem(item4->GetWeakPtr());
EXPECT_EQ(calculator.GetResourceCacheMaxBytes(), static_cast<size_t>(800U));
item3.reset();
EXPECT_EQ(calculator.GetResourceCacheMaxBytes(), static_cast<size_t>(700U));
item2.reset();
EXPECT_EQ(calculator.GetResourceCacheMaxBytes(), static_cast<size_t>(500U));
}
} // namespace testing
} // namespace flutter

View File

@ -147,10 +147,14 @@ std::unique_ptr<Shell> Shell::Create(
if (!isolate_snapshot) {
isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
}
auto resource_cache_limit_calculator =
std::make_shared<ResourceCacheLimitCalculator>(
settings.resource_cache_max_bytes_threshold);
return CreateWithSnapshot(std::move(platform_data), //
std::move(task_runners), //
/*parent_merger=*/nullptr, //
/*parent_io_manager=*/nullptr, //
resource_cache_limit_calculator, //
std::move(settings), //
std::move(vm), //
std::move(isolate_snapshot), //
@ -163,6 +167,8 @@ std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
DartVMRef vm,
fml::RefPtr<fml::RasterThreadMerger> parent_merger,
std::shared_ptr<ShellIOManager> parent_io_manager,
const std::shared_ptr<ResourceCacheLimitCalculator>&
resource_cache_limit_calculator,
TaskRunners task_runners,
const PlatformData& platform_data,
Settings settings,
@ -177,7 +183,8 @@ std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
}
auto shell = std::unique_ptr<Shell>(
new Shell(std::move(vm), task_runners, parent_merger, settings,
new Shell(std::move(vm), task_runners, parent_merger,
resource_cache_limit_calculator, settings,
std::make_shared<VolatilePathTracker>(
task_runners.GetUITaskRunner(),
!settings.skia_deterministic_rendering_on_cpu),
@ -313,6 +320,8 @@ std::unique_ptr<Shell> Shell::CreateWithSnapshot(
TaskRunners task_runners,
fml::RefPtr<fml::RasterThreadMerger> parent_thread_merger,
std::shared_ptr<ShellIOManager> parent_io_manager,
const std::shared_ptr<ResourceCacheLimitCalculator>&
resource_cache_limit_calculator,
Settings settings,
DartVMRef vm,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
@ -340,6 +349,7 @@ std::unique_ptr<Shell> Shell::CreateWithSnapshot(
&shell, //
parent_thread_merger, //
parent_io_manager, //
resource_cache_limit_calculator, //
task_runners = std::move(task_runners), //
platform_data = std::move(platform_data), //
settings = std::move(settings), //
@ -353,6 +363,7 @@ std::unique_ptr<Shell> Shell::CreateWithSnapshot(
std::move(vm), //
parent_thread_merger, //
parent_io_manager, //
resource_cache_limit_calculator, //
std::move(task_runners), //
std::move(platform_data), //
std::move(settings), //
@ -369,11 +380,14 @@ std::unique_ptr<Shell> Shell::CreateWithSnapshot(
Shell::Shell(DartVMRef vm,
TaskRunners task_runners,
fml::RefPtr<fml::RasterThreadMerger> parent_merger,
const std::shared_ptr<ResourceCacheLimitCalculator>&
resource_cache_limit_calculator,
Settings settings,
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
bool is_gpu_disabled)
: task_runners_(std::move(task_runners)),
parent_raster_thread_merger_(parent_merger),
resource_cache_limit_calculator_(resource_cache_limit_calculator),
settings_(std::move(settings)),
vm_(std::move(vm)),
is_gpu_disabled_sync_switch_(new fml::SyncSwitch(is_gpu_disabled)),
@ -385,6 +399,8 @@ Shell::Shell(DartVMRef vm,
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
display_manager_ = std::make_unique<DisplayManager>();
resource_cache_limit_calculator->AddResourceCacheLimitItem(
weak_factory_.GetWeakPtr());
// Generate a WeakPtrFactory for use with the raster thread. This does not
// need to wait on a latch because it can only ever be used from the raster
@ -502,8 +518,9 @@ std::unique_ptr<Shell> Shell::Spawn(
.SetIfTrue([&is_gpu_disabled] { is_gpu_disabled = true; }));
std::unique_ptr<Shell> result = CreateWithSnapshot(
PlatformData{}, task_runners_, rasterizer_->GetRasterThreadMerger(),
io_manager_, GetSettings(), vm_, vm_->GetVMData()->GetIsolateSnapshot(),
on_create_platform_view, on_create_rasterizer,
io_manager_, resource_cache_limit_calculator_, GetSettings(), vm_,
vm_->GetVMData()->GetIsolateSnapshot(), on_create_platform_view,
on_create_rasterizer,
[engine = this->engine_.get(), initial_route](
Engine::Delegate& delegate,
const PointerDataDispatcherMaker& dispatcher_maker, DartVM& vm,
@ -911,12 +928,15 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) {
}
// This is the formula Android uses.
// https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41
size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4;
// https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45
resource_cache_limit_ =
metrics.physical_width * metrics.physical_height * 12 * 4;
size_t resource_cache_max_bytes =
resource_cache_limit_calculator_->GetResourceCacheMaxBytes();
task_runners_.GetRasterTaskRunner()->PostTask(
[rasterizer = rasterizer_->GetWeakPtr(), max_bytes] {
[rasterizer = rasterizer_->GetWeakPtr(), resource_cache_max_bytes] {
if (rasterizer) {
rasterizer->SetResourceCacheMaxBytes(max_bytes, false);
rasterizer->SetResourceCacheMaxBytes(resource_cache_max_bytes, false);
}
});

View File

@ -38,6 +38,7 @@
#include "flutter/shell/common/engine.h"
#include "flutter/shell/common/platform_view.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/resource_cache_limit_calculator.h"
#include "flutter/shell/common/shell_io_manager.h"
namespace flutter {
@ -108,7 +109,8 @@ class Shell final : public PlatformView::Delegate,
public Animator::Delegate,
public Engine::Delegate,
public Rasterizer::Delegate,
public ServiceProtocol::Handler {
public ServiceProtocol::Handler,
public ResourceCacheLimitItem {
public:
template <class T>
using CreateCallback = std::function<std::unique_ptr<T>(Shell&)>;
@ -410,6 +412,9 @@ class Shell final : public PlatformView::Delegate,
const TaskRunners task_runners_;
const fml::RefPtr<fml::RasterThreadMerger> parent_raster_thread_merger_;
std::shared_ptr<ResourceCacheLimitCalculator>
resource_cache_limit_calculator_;
size_t resource_cache_limit_;
const Settings settings_;
DartVMRef vm_;
mutable std::mutex time_recorder_mutex_;
@ -476,6 +481,8 @@ class Shell final : public PlatformView::Delegate,
Shell(DartVMRef vm,
TaskRunners task_runners,
fml::RefPtr<fml::RasterThreadMerger> parent_merger,
const std::shared_ptr<ResourceCacheLimitCalculator>&
resource_cache_limit_calculator,
Settings settings,
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
bool is_gpu_disabled);
@ -484,6 +491,8 @@ class Shell final : public PlatformView::Delegate,
DartVMRef vm,
fml::RefPtr<fml::RasterThreadMerger> parent_merger,
std::shared_ptr<ShellIOManager> parent_io_manager,
const std::shared_ptr<ResourceCacheLimitCalculator>&
resource_cache_limit_calculator,
TaskRunners task_runners,
const PlatformData& platform_data,
Settings settings,
@ -492,11 +501,14 @@ class Shell final : public PlatformView::Delegate,
const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
const EngineCreateCallback& on_create_engine,
bool is_gpu_disabled);
static std::unique_ptr<Shell> CreateWithSnapshot(
const PlatformData& platform_data,
TaskRunners task_runners,
fml::RefPtr<fml::RasterThreadMerger> parent_thread_merger,
std::shared_ptr<ShellIOManager> parent_io_manager,
const std::shared_ptr<ResourceCacheLimitCalculator>&
resource_cache_limit_calculator,
Settings settings,
DartVMRef vm,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
@ -694,6 +706,9 @@ class Shell final : public PlatformView::Delegate,
const ServiceProtocol::Handler::ServiceProtocolMap& params,
rapidjson::Document* response);
// |ResourceCacheLimitItem|
size_t GetResourceCacheLimit() override { return resource_cache_limit_; };
// Creates an asset bundle from the original settings asset path or
// directory.
std::unique_ptr<DirectoryAssetBundle> RestoreOriginalAssetResolver();

View File

@ -107,6 +107,7 @@ class MockPlatformViewDelegate : public PlatformView::Delegate {
};
class MockSurface : public Surface {
public:
MOCK_METHOD0(IsValid, bool());
MOCK_METHOD1(AcquireFrame,
@ -130,6 +131,13 @@ class MockPlatformView : public PlatformView {
std::shared_ptr<PlatformMessageHandler>());
};
class TestPlatformView : public PlatformView {
public:
TestPlatformView(Shell& shell, TaskRunners task_runners)
: PlatformView(shell, task_runners) {}
MOCK_METHOD0(CreateRenderingSurface, std::unique_ptr<Surface>());
};
class MockPlatformMessageHandler : public PlatformMessageHandler {
public:
MOCK_METHOD1(HandlePlatformMessage,
@ -1562,6 +1570,128 @@ static size_t GetRasterizerResourceCacheBytesSync(const Shell& shell) {
return bytes;
}
TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) {
TaskRunners task_runners = GetTaskRunnersForFixture();
auto settings = CreateSettingsForFixture();
settings.resource_cache_max_bytes_threshold = 4000000U;
GrMockOptions main_context_options;
sk_sp<GrDirectContext> main_context =
GrDirectContext::MakeMock(&main_context_options);
Shell::CreateCallback<PlatformView> platform_view_create_callback =
[task_runners, main_context](flutter::Shell& shell) {
auto result = std::make_unique<TestPlatformView>(shell, task_runners);
ON_CALL(*result, CreateRenderingSurface())
.WillByDefault(::testing::Invoke([main_context] {
auto surface = std::make_unique<MockSurface>();
ON_CALL(*surface, GetContext())
.WillByDefault(Return(main_context.get()));
ON_CALL(*surface, IsValid()).WillByDefault(Return(true));
ON_CALL(*surface, MakeRenderContextCurrent())
.WillByDefault(::testing::Invoke([] {
return std::make_unique<GLContextDefaultResult>(true);
}));
return surface;
}));
return result;
};
auto shell = CreateShell(
/*settings=*/std::move(settings),
/*task_runners=*/task_runners,
/*simulate_vsync=*/false,
/*shell_test_external_view_embedder=*/nullptr,
/*is_gpu_disabled=*/false,
/*rendering_backend=*/
ShellTestPlatformView::BackendType::kDefaultBackend,
/*platform_view_create_callback=*/platform_view_create_callback);
// Create the surface needed by rasterizer
PlatformViewNotifyCreated(shell.get());
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("emptyMain");
RunEngine(shell.get(), std::move(configuration));
PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() {
shell->GetPlatformView()->SetViewportMetrics({1.0, 100, 100, 22});
});
// first cache bytes
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(480000U));
auto shell_spawn_callback = [&]() {
std::unique_ptr<Shell> spawn;
PostSync(
shell->GetTaskRunners().GetPlatformTaskRunner(),
[this, &spawn, &spawner = shell, platform_view_create_callback]() {
auto configuration =
RunConfiguration::InferFromSettings(CreateSettingsForFixture());
configuration.SetEntrypoint("emptyMain");
spawn = spawner->Spawn(
std::move(configuration), "", platform_view_create_callback,
[](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
ASSERT_NE(nullptr, spawn.get());
ASSERT_TRUE(ValidateShell(spawn.get()));
});
return spawn;
};
std::unique_ptr<Shell> second_shell = shell_spawn_callback();
PlatformViewNotifyCreated(second_shell.get());
PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(),
[&second_shell]() {
second_shell->GetPlatformView()->SetViewportMetrics(
{1.0, 100, 100, 22});
});
// first cache bytes + second cache bytes
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(960000U));
PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(),
[&second_shell]() {
second_shell->GetPlatformView()->SetViewportMetrics(
{1.0, 100, 300, 22});
});
// first cache bytes + second cache bytes
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(1920000U));
std::unique_ptr<Shell> third_shell = shell_spawn_callback();
PlatformViewNotifyCreated(third_shell.get());
PostSync(
third_shell->GetTaskRunners().GetPlatformTaskRunner(), [&third_shell]() {
third_shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 100, 22});
});
// first cache bytes + second cache bytes + third cache bytes
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(3840000U));
PostSync(
third_shell->GetTaskRunners().GetPlatformTaskRunner(), [&third_shell]() {
third_shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 100, 22});
});
// max bytes threshold
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(4000000U));
DestroyShell(std::move(third_shell), std::move(task_runners));
// max bytes threshold
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(4000000U));
PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(),
[&second_shell]() {
second_shell->GetPlatformView()->SetViewportMetrics(
{1.0, 100, 100, 22});
});
// first cache bytes + second cache bytes
EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell),
static_cast<size_t>(960000U));
DestroyShell(std::move(second_shell), std::move(task_runners));
DestroyShell(std::move(shell), std::move(task_runners));
}
TEST_F(ShellTest, SetResourceCacheSize) {
Settings settings = CreateSettingsForFixture();
auto task_runner = CreateNewThread();

View File

@ -470,6 +470,16 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.old_gen_heap_size = std::stoi(old_gen_heap_size);
}
if (command_line.HasOption(
FlagForSwitch(Switch::ResourceCacheMaxBytesThreshold))) {
std::string resource_cache_max_bytes_threshold;
command_line.GetOptionValue(
FlagForSwitch(Switch::ResourceCacheMaxBytesThreshold),
&resource_cache_max_bytes_threshold);
settings.resource_cache_max_bytes_threshold =
std::stoi(resource_cache_max_bytes_threshold);
}
if (command_line.HasOption(FlagForSwitch(Switch::MsaaSamples))) {
std::string msaa_samples;
command_line.GetOptionValue(FlagForSwitch(Switch::MsaaSamples),

View File

@ -220,6 +220,10 @@ DEF_SWITCH(
DEF_SWITCH(OldGenHeapSize,
"old-gen-heap-size",
"The size limit in megabytes for the Dart VM old gen heap space.")
DEF_SWITCH(ResourceCacheMaxBytesThreshold,
"resource-cache-max-bytes-threshold",
"The max bytes threshold of resource cache, or 0 for unlimited.")
DEF_SWITCH(EnableSkParagraph,
"enable-skparagraph",
"Selects the SkParagraph implementation of the text layout engine.")

View File

@ -15,6 +15,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -302,9 +303,16 @@ public class FlutterLoader {
activityManager.getMemoryInfo(memInfo);
oldGenHeapSizeMegaBytes = (int) (memInfo.totalMem / 1e6 / 2);
}
shellArgs.add("--old-gen-heap-size=" + oldGenHeapSizeMegaBytes);
DisplayMetrics displayMetrics = applicationContext.getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
// This is the formula Android uses.
// https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45
int resourceCacheMaxBytesThreshold = screenWidth * screenHeight * 12 * 4;
shellArgs.add("--resource-cache-max-bytes-threshold=" + resourceCacheMaxBytesThreshold);
shellArgs.add("--prefetched-default-font-manager");
if (metaData == null || metaData.getBoolean(ENABLE_SKPARAGRAPH_META_DATA_KEY, true)) {

View File

@ -22,6 +22,7 @@ import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Bundle;
import android.util.DisplayMetrics;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.FlutterJNI;
import java.util.Arrays;
@ -87,6 +88,36 @@ public class FlutterLoaderTest {
assertTrue(arguments.contains(oldGenHeapArg));
}
@Test
public void itDefaultsTheResourceCacheMaxBytesThresholdAppropriately() {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
assertFalse(flutterLoader.initialized());
flutterLoader.startInitialization(RuntimeEnvironment.application);
flutterLoader.ensureInitializationComplete(RuntimeEnvironment.application, null);
shadowOf(getMainLooper()).idle();
DisplayMetrics displayMetrics =
RuntimeEnvironment.application.getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
int resourceCacheMaxBytesThreshold = screenWidth * screenHeight * 12 * 4;
final String resourceCacheMaxBytesThresholdArg =
"--resource-cache-max-bytes-threshold=" + resourceCacheMaxBytesThreshold;
ArgumentCaptor<String[]> shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI, times(1))
.init(
eq(RuntimeEnvironment.application),
shellArgsCaptor.capture(),
anyString(),
anyString(),
anyString(),
anyLong());
List<String> arguments = Arrays.asList(shellArgsCaptor.getValue());
assertTrue(arguments.contains(resourceCacheMaxBytesThresholdArg));
}
@Test
public void itSetsLeakVMToTrueByDefault() {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);

View File

@ -195,6 +195,14 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle) {
settings.old_gen_heap_size = std::round([NSProcessInfo processInfo].physicalMemory * .48 /
flutter::kMegaByteSizeInBytes);
}
// This is the formula Android uses.
// https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45
CGFloat scale = [UIScreen mainScreen].scale;
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width * scale;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height * scale;
settings.resource_cache_max_bytes_threshold = screenWidth * screenHeight * 12 * 4;
return settings;
}

View File

@ -29,6 +29,16 @@ FLUTTER_ASSERT_ARC
XCTAssertEqual(project.settings.old_gen_heap_size, old_gen_heap_size);
}
- (void)testResourceCacheMaxBytesThresholdSetting {
FlutterDartProject* project = [[FlutterDartProject alloc] init];
CGFloat scale = [UIScreen mainScreen].scale;
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width * scale;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height * scale;
size_t resource_cache_max_bytes_threshold = screenWidth * screenHeight * 12 * 4;
XCTAssertEqual(project.settings.resource_cache_max_bytes_threshold,
resource_cache_max_bytes_threshold);
}
- (void)testMainBundleSettingsAreCorrectlyParsed {
NSBundle* mainBundle = [NSBundle mainBundle];
NSDictionary* appTransportSecurity =