Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,7 @@ FILE: ../../../flutter/shell/common/display_manager.h
FILE: ../../../flutter/shell/common/engine.cc
FILE: ../../../flutter/shell/common/engine.h
FILE: ../../../flutter/shell/common/engine_unittests.cc
FILE: ../../../flutter/shell/common/fixtures/hello_loop_2.gif
FILE: ../../../flutter/shell/common/fixtures/shell_test.dart
FILE: ../../../flutter/shell/common/fixtures/shelltest_screenshot.png
FILE: ../../../flutter/shell/common/input_events_unittests.cc
Expand All @@ -1075,6 +1076,7 @@ FILE: ../../../flutter/shell/common/shell_benchmarks.cc
FILE: ../../../flutter/shell/common/shell_fuchsia_unittests.cc
FILE: ../../../flutter/shell/common/shell_io_manager.cc
FILE: ../../../flutter/shell/common/shell_io_manager.h
FILE: ../../../flutter/shell/common/shell_io_manager_unittests.cc
FILE: ../../../flutter/shell/common/shell_test.cc
FILE: ../../../flutter/shell/common/shell_test.h
FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.cc
Expand Down
2 changes: 1 addition & 1 deletion flow/skia_gpu_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace flutter {

SkiaUnrefQueue::SkiaUnrefQueue(fml::RefPtr<fml::TaskRunner> task_runner,
fml::TimeDelta delay,
fml::WeakPtr<GrDirectContext> context)
sk_sp<GrDirectContext> context)
: task_runner_(std::move(task_runner)),
drain_delay_(delay),
drain_pending_(false),
Expand Down
8 changes: 6 additions & 2 deletions flow/skia_gpu_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,24 @@ class SkiaUnrefQueue : public fml::RefCountedThreadSafe<SkiaUnrefQueue> {
// after this call.
void Drain();

void UpdateResourceContext(sk_sp<GrDirectContext> context) {
context_ = context;
}

private:
const fml::RefPtr<fml::TaskRunner> task_runner_;
const fml::TimeDelta drain_delay_;
std::mutex mutex_;
std::deque<SkRefCnt*> objects_;
bool drain_pending_;
fml::WeakPtr<GrDirectContext> context_;
sk_sp<GrDirectContext> context_;

// The `GrDirectContext* context` is only used for signaling Skia to
// performDeferredCleanup. It can be nullptr when such signaling is not needed
// (e.g., in unit tests).
SkiaUnrefQueue(fml::RefPtr<fml::TaskRunner> task_runner,
fml::TimeDelta delay,
fml::WeakPtr<GrDirectContext> context = {});
sk_sp<GrDirectContext> context = nullptr);

~SkiaUnrefQueue();

Expand Down
8 changes: 7 additions & 1 deletion shell/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ if (enable_unittests) {
test_fixtures("shell_unittests_fixtures") {
dart_main = "fixtures/shell_test.dart"

fixtures = [ "fixtures/shelltest_screenshot.png" ]
fixtures = [
"fixtures/shelltest_screenshot.png",
"fixtures/hello_loop_2.gif",
]
}

shell_host_executable("shell_benchmarks") {
Expand Down Expand Up @@ -293,6 +296,9 @@ if (enable_unittests) {
"$fuchsia_sdk_root/pkg:fidl_cpp",
"$fuchsia_sdk_root/pkg:sys_cpp",
]
} else {
# TODO(63837): This test is hard-coded to use a TestGLSurface so it cannot run on fuchsia.
sources += [ "shell_io_manager_unittests.cc" ]
}
}
}
Binary file added shell/common/fixtures/hello_loop_2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions shell/common/fixtures/shell_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,8 @@ void onBeginFrameWithNotifyNativeMain() {
};
notifyNative();
}

@pragma('vm:entry-point')
void frameCallback(_Image, int) {
print('called back');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the print

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and consider adding a comment about why this method is empty and what test is using it)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
8 changes: 5 additions & 3 deletions shell/common/shell_io_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ sk_sp<GrDirectContext> ShellIOManager::CreateCompatibleResourceLoadingContext(
ShellIOManager::ShellIOManager(
sk_sp<GrDirectContext> resource_context,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
fml::RefPtr<fml::TaskRunner> unref_queue_task_runner)
fml::RefPtr<fml::TaskRunner> unref_queue_task_runner,
fml::TimeDelta unref_queue_drain_delay)
: resource_context_(std::move(resource_context)),
resource_context_weak_factory_(
resource_context_
Expand All @@ -43,8 +44,8 @@ ShellIOManager::ShellIOManager(
: nullptr),
unref_queue_(fml::MakeRefCounted<flutter::SkiaUnrefQueue>(
std::move(unref_queue_task_runner),
fml::TimeDelta::FromMilliseconds(8),
GetResourceContext())),
unref_queue_drain_delay,
resource_context_)),
is_gpu_disabled_sync_switch_(is_gpu_disabled_sync_switch),
weak_factory_(this) {
if (!resource_context_) {
Expand Down Expand Up @@ -81,6 +82,7 @@ void ShellIOManager::UpdateResourceContext(
? std::make_unique<fml::WeakPtrFactory<GrDirectContext>>(
resource_context_.get())
: nullptr;
unref_queue_->UpdateResourceContext(resource_context_);
}

fml::WeakPtr<ShellIOManager> ShellIOManager::GetWeakPtr() {
Expand Down
4 changes: 3 additions & 1 deletion shell/common/shell_io_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ class ShellIOManager final : public IOManager {
ShellIOManager(
sk_sp<GrDirectContext> resource_context,
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
fml::RefPtr<fml::TaskRunner> unref_queue_task_runner);
fml::RefPtr<fml::TaskRunner> unref_queue_task_runner,
fml::TimeDelta unref_queue_drain_delay =
fml::TimeDelta::FromMilliseconds(8));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ColdPaleLight I'm wondering whether number 8 here has any potential meaning?
I'm facing a problem when Supporting another platform because the delayed task posted in UnrefQueue#Unref in release mode (In debug mode, everything goes as expected)
When I change 8 to 0, it goes as expected in release mode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ColdPaleLight I'm wondering whether number 8 here has any potential meaning? I'm facing a problem when Supporting another platform because the delayed task posted in UnrefQueue#Unref in release mode (In debug mode, everything goes as expected) When I change 8 to 0, it goes as expected in release mode.

I guess you can find the answer in the PR #9486. My commit did not change the value of delay, it just moved it to another place.


~ShellIOManager() override;

Expand Down
124 changes: 124 additions & 0 deletions shell/common/shell_io_manager_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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_io_manager.h"

#include "flutter/common/task_runners.h"
#include "flutter/fml/mapping.h"
#include "flutter/lib/ui/painting/multi_frame_codec.h"
#include "flutter/testing/dart_isolate_runner.h"
#include "flutter/testing/fixture_test.h"
#include "flutter/testing/post_task_sync.h"
#include "flutter/testing/test_gl_surface.h"
#include "flutter/testing/testing.h"

namespace flutter {
namespace testing {

static sk_sp<SkData> OpenFixtureAsSkData(const char* name) {
auto fixtures_directory =
fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead);
if (!fixtures_directory.is_valid()) {
return nullptr;
}

auto fixture_mapping =
fml::FileMapping::CreateReadOnly(fixtures_directory, name);

if (!fixture_mapping) {
return nullptr;
}

SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void {
delete reinterpret_cast<fml::FileMapping*>(context);
};

auto data = SkData::MakeWithProc(fixture_mapping->GetMapping(),
fixture_mapping->GetSize(), on_release,
fixture_mapping.get());

if (!data) {
return nullptr;
}
// The data is now owned by Skia.
fixture_mapping.release();
return data;
}

class ShellIOManagerTest : public FixtureTest {};

// Regression test for https://github.com/flutter/engine/pull/32106.
TEST_F(ShellIOManagerTest,
ItDoesNotCrashThatSkiaUnrefQueueDrainAfterIOManagerReset) {
auto settings = CreateSettingsForFixture();
auto vm_ref = DartVMRef::Create(settings);
auto vm_data = vm_ref.GetVMData();
auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif");
ASSERT_TRUE(gif_mapping);

ImageGeneratorRegistry registry;
std::shared_ptr<ImageGenerator> gif_generator =
registry.CreateCompatibleGenerator(gif_mapping);
ASSERT_TRUE(gif_generator);

TaskRunners runners(GetCurrentTestName(), // label
CreateNewThread("platform"), // platform
CreateNewThread("raster"), // raster
CreateNewThread("ui"), // ui
CreateNewThread("io") // io
);

std::unique_ptr<TestGLSurface> gl_surface;
std::unique_ptr<ShellIOManager> io_manager;
fml::RefPtr<MultiFrameCodec> codec;

// Setup the IO manager.
PostTaskSync(runners.GetIOTaskRunner(), [&]() {
gl_surface = std::make_unique<TestGLSurface>(SkISize::Make(1, 1));
io_manager = std::make_unique<ShellIOManager>(
gl_surface->CreateGrContext(), std::make_shared<fml::SyncSwitch>(),
runners.GetIOTaskRunner(), fml::TimeDelta::FromMilliseconds(0));
});

auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "emptyMain",
{}, GetDefaultKernelFilePath(),
io_manager->GetWeakIOManager());

PostTaskSync(runners.GetUITaskRunner(), [&]() {
fml::AutoResetWaitableEvent isolate_latch;

EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool {
Dart_Handle library = Dart_RootLibrary();
if (Dart_IsError(library)) {
isolate_latch.Signal();
return false;
}
Dart_Handle closure =
Dart_GetField(library, Dart_NewStringFromCString("frameCallback"));
if (Dart_IsError(closure) || !Dart_IsClosure(closure)) {
isolate_latch.Signal();
return false;
}

codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(gif_generator));
codec->getNextFrame(closure);
isolate_latch.Signal();
return true;
}));
isolate_latch.Wait();
});

// Destroy the IO manager
PostTaskSync(runners.GetIOTaskRunner(), [&]() {
// 'SkiaUnrefQueue.Drain' will be called after 'io_manager.reset()' in this
// test, If the resource context has been destroyed at that time, it will
// crash.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is surprising to me. But tracing through it seems to make sense. Adding some notes here so I dont' forget this later:

  • Drain() currently checks whether the weak pointer is still valid or not before trying to call anything on it
  • However, calling unref on the SkImage_Lazy ends up freeing a GrBackendTexture. That object seems to assume that something else is keeping the context alive. This seems like it might be a bad assumption on Skia's part, but in Skia's defense we're doing something pretty weird here by keeping GPU resident objects alive without keeping the GrDirectContext alive ourselves.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// See https://github.com/flutter/flutter/issues/87895
io_manager.reset();
gl_surface.reset();
});
}

} // namespace testing
} // namespace flutter