未验证 提交 97a23a80 编写于 作者: G gaaclarke 提交者: GitHub

Made a way to turn off the OpenGL operations on the IO thread for backgrounded apps (#13908)

上级 0d60e1a3
......@@ -203,6 +203,9 @@ FILE: ../../../flutter/fml/synchronization/semaphore_unittest.cc
FILE: ../../../flutter/fml/synchronization/shared_mutex.h
FILE: ../../../flutter/fml/synchronization/shared_mutex_std.cc
FILE: ../../../flutter/fml/synchronization/shared_mutex_std.h
FILE: ../../../flutter/fml/synchronization/sync_switch.cc
FILE: ../../../flutter/fml/synchronization/sync_switch.h
FILE: ../../../flutter/fml/synchronization/sync_switch_unittest.cc
FILE: ../../../flutter/fml/synchronization/waitable_event.cc
FILE: ../../../flutter/fml/synchronization/waitable_event.h
FILE: ../../../flutter/fml/synchronization/waitable_event_unittest.cc
......
......@@ -57,7 +57,7 @@ class SkiaGPUObject {
SkiaGPUObject(sk_sp<SkiaObjectType> object, fml::RefPtr<SkiaUnrefQueue> queue)
: object_(std::move(object)), queue_(std::move(queue)) {
FML_DCHECK(queue_ && object_);
FML_DCHECK(object_);
}
SkiaGPUObject(SkiaGPUObject&&) = default;
......@@ -69,7 +69,7 @@ class SkiaGPUObject {
sk_sp<SkiaObjectType> get() const { return object_; }
void reset() {
if (object_) {
if (object_ && queue_) {
queue_->Unref(object_.release());
}
queue_ = nullptr;
......
......@@ -64,6 +64,8 @@ source_set("fml") {
"synchronization/semaphore.cc",
"synchronization/semaphore.h",
"synchronization/shared_mutex.h",
"synchronization/sync_switch.cc",
"synchronization/sync_switch.h",
"synchronization/waitable_event.cc",
"synchronization/waitable_event.h",
"task_runner.cc",
......@@ -210,6 +212,7 @@ executable("fml_unittests") {
"paths_unittests.cc",
"platform/darwin/string_range_sanitization_unittests.mm",
"synchronization/semaphore_unittest.cc",
"synchronization/sync_switch_unittest.cc",
"synchronization/waitable_event_unittest.cc",
"thread_local_unittests.cc",
"time/time_delta_unittest.cc",
......
// 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/fml/synchronization/sync_switch.h"
namespace fml {
SyncSwitch::Handlers& SyncSwitch::Handlers::SetIfTrue(
const std::function<void()>& handler) {
true_handler = std::move(handler);
return *this;
}
SyncSwitch::Handlers& SyncSwitch::Handlers::SetIfFalse(
const std::function<void()>& handler) {
false_handler = std::move(handler);
return *this;
}
SyncSwitch::SyncSwitch() : SyncSwitch(false) {}
SyncSwitch::SyncSwitch(bool value) : value_(value) {}
void SyncSwitch::Execute(const SyncSwitch::Handlers& handlers) {
std::scoped_lock guard(mutex_);
if (value_) {
handlers.true_handler();
} else {
handlers.false_handler();
}
}
void SyncSwitch::SetSwitch(bool value) {
std::scoped_lock guard(mutex_);
value_ = value;
}
} // namespace fml
// 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_FML_SYNCHRONIZATION_SYNC_SWITCH_H_
#define FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_
#include <forward_list>
#include <functional>
#include <mutex>
#include "flutter/fml/macros.h"
namespace fml {
/// A threadsafe structure that allows you to switch between 2 different
/// execution paths.
///
/// Execution and setting the switch is exclusive, i.e. only one will happen
/// at a time.
class SyncSwitch {
public:
/// Represents the 2 code paths available when calling |SyncSwitch::Execute|.
struct Handlers {
/// Sets the handler that will be executed if the |SyncSwitch| is true.
Handlers& SetIfTrue(const std::function<void()>& handler);
/// Sets the handler that will be executed if the |SyncSwitch| is false.
Handlers& SetIfFalse(const std::function<void()>& handler);
std::function<void()> true_handler = [] {};
std::function<void()> false_handler = [] {};
};
/// Create a |SyncSwitch| with the false value.
SyncSwitch();
/// Create a |SyncSwitch| with the specified value.
///
/// @param[in] value Default value for the |SyncSwitch|.
SyncSwitch(bool value);
/// Diverge execution between true and false values of the SyncSwitch.
///
/// This can be called on any thread. Note that attempting to call
/// |SetSwitch| inside of the handlers will result in a self deadlock.
///
/// @param[in] handlers Called for the correct value of the |SyncSwitch|.
void Execute(const Handlers& handlers);
/// Set the value of the SyncSwitch.
///
/// This can be called on any thread.
///
/// @param[in] value New value for the |SyncSwitch|.
void SetSwitch(bool value);
private:
std::mutex mutex_;
bool value_;
FML_DISALLOW_COPY_AND_ASSIGN(SyncSwitch);
};
} // namespace fml
#endif // FLUTTER_FML_SYNCHRONIZATION_SYNC_SWITCH_H_
// 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/fml/synchronization/sync_switch.h"
#include "gtest/gtest.h"
using fml::SyncSwitch;
TEST(SyncSwitchTest, Basic) {
SyncSwitch syncSwitch;
bool switchValue = false;
syncSwitch.Execute(SyncSwitch::Handlers()
.SetIfTrue([&] { switchValue = true; })
.SetIfFalse([&] { switchValue = false; }));
EXPECT_FALSE(switchValue);
syncSwitch.SetSwitch(true);
syncSwitch.Execute(SyncSwitch::Handlers()
.SetIfTrue([&] { switchValue = true; })
.SetIfFalse([&] { switchValue = false; }));
EXPECT_TRUE(switchValue);
}
TEST(SyncSwitchTest, NoopIfUndefined) {
SyncSwitch syncSwitch;
bool switchValue = false;
syncSwitch.Execute(SyncSwitch::Handlers());
EXPECT_FALSE(switchValue);
}
......@@ -7,6 +7,7 @@
#include "flutter/flow/skia_gpu_object.h"
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/fml/synchronization/sync_switch.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace flutter {
......@@ -22,6 +23,8 @@ class IOManager {
virtual fml::WeakPtr<GrContext> GetResourceContext() const = 0;
virtual fml::RefPtr<flutter::SkiaUnrefQueue> GetSkiaUnrefQueue() const = 0;
virtual std::shared_ptr<fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() = 0;
};
} // namespace flutter
......
......@@ -151,8 +151,7 @@ static sk_sp<SkImage> ImageFromCompressedData(
static SkiaGPUObject<SkImage> UploadRasterImage(
sk_sp<SkImage> image,
fml::WeakPtr<GrContext> context,
fml::RefPtr<flutter::SkiaUnrefQueue> queue,
fml::WeakPtr<IOManager> io_manager,
const fml::tracing::TraceFlow& flow) {
TRACE_EVENT0("flutter", __FUNCTION__);
flow.Step(__FUNCTION__);
......@@ -161,7 +160,7 @@ static SkiaGPUObject<SkImage> UploadRasterImage(
// the this method.
FML_DCHECK(!image->isTextureBacked());
if (!context || !queue) {
if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) {
FML_LOG(ERROR)
<< "Could not acquire context of release queue for texture upload.";
return {};
......@@ -173,19 +172,36 @@ static SkiaGPUObject<SkImage> UploadRasterImage(
return {};
}
auto texture_image =
SkImage::MakeCrossContextFromPixmap(context.get(), // context
pixmap, // pixmap
true, // buildMips,
true // limitToMaxTextureSize
);
if (!texture_image) {
FML_LOG(ERROR) << "Could not make x-context image.";
return {};
}
return {texture_image, queue};
SkiaGPUObject<SkImage> result;
io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
fml::SyncSwitch::Handlers()
.SetIfTrue([&result, &pixmap, &image] {
SkSafeRef(image.get());
sk_sp<SkImage> texture_image = SkImage::MakeFromRaster(
pixmap,
[](const void* pixels, SkImage::ReleaseContext context) {
SkSafeUnref(static_cast<SkImage*>(context));
},
image.get());
result = {texture_image, nullptr};
})
.SetIfFalse([&result, context = io_manager->GetResourceContext(),
&pixmap, queue = io_manager->GetSkiaUnrefQueue()] {
sk_sp<SkImage> texture_image = SkImage::MakeCrossContextFromPixmap(
context.get(), // context
pixmap, // pixmap
true, // buildMips,
true // limitToMaxTextureSize
);
if (!texture_image) {
FML_LOG(ERROR) << "Could not make x-context image.";
result = {};
} else {
result = {texture_image, queue};
}
}));
return result;
}
void ImageDecoder::Decode(ImageDescriptor descriptor,
......@@ -265,9 +281,8 @@ void ImageDecoder::Decode(ImageDescriptor descriptor,
return;
}
auto uploaded = UploadRasterImage(
std::move(decompressed), io_manager->GetResourceContext(),
io_manager->GetSkiaUnrefQueue(), flow);
auto uploaded =
UploadRasterImage(std::move(decompressed), io_manager, flow);
if (!uploaded.get()) {
FML_LOG(ERROR) << "Could not upload image to the GPU.";
......
......@@ -29,7 +29,8 @@ class TestIOManager final : public IOManager {
task_runner,
fml::TimeDelta::FromNanoseconds(0))),
runner_(task_runner),
weak_factory_(this) {
weak_factory_(this),
is_gpu_disabled_sync_switch_(std::make_shared<fml::SyncSwitch>()) {
FML_CHECK(task_runner->RunsTasksOnCurrentThread())
<< "The IO manager must be initialized its primary task runner. The "
"test harness may not be setup correctly/safely.";
......@@ -62,6 +63,14 @@ class TestIOManager final : public IOManager {
return unref_queue_;
}
// |IOManager|
std::shared_ptr<fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() override {
did_access_is_gpu_disabled_sync_switch_ = true;
return is_gpu_disabled_sync_switch_;
}
bool did_access_is_gpu_disabled_sync_switch_ = false;
private:
TestGLSurface gl_surface_;
sk_sp<GrContext> gl_context_;
......@@ -70,6 +79,7 @@ class TestIOManager final : public IOManager {
fml::WeakPtr<TestIOManager> weak_prototype_;
fml::RefPtr<fml::TaskRunner> runner_;
fml::WeakPtrFactory<TestIOManager> weak_factory_;
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch_;
FML_DISALLOW_COPY_AND_ASSIGN(TestIOManager);
};
......@@ -167,7 +177,7 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) {
fml::AutoResetWaitableEvent latch;
std::unique_ptr<IOManager> io_manager;
std::unique_ptr<TestIOManager> io_manager;
auto release_io_manager = [&]() {
io_manager.reset();
......@@ -187,8 +197,10 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) {
ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());
ASSERT_TRUE(image.get());
EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_);
runners.GetIOTaskRunner()->PostTask(release_io_manager);
};
EXPECT_FALSE(io_manager->did_access_is_gpu_disabled_sync_switch_);
image_decoder->Decode(std::move(image_descriptor), callback);
};
......@@ -198,7 +210,6 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) {
};
runners.GetIOTaskRunner()->PostTask(setup_io_manager_and_decode);
latch.Wait();
}
......
......@@ -104,15 +104,17 @@ std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
// https://github.com/flutter/flutter/issues/42948
fml::TaskRunner::RunNowOrPostTask(
io_task_runner,
[&io_manager_promise, //
&weak_io_manager_promise, //
&unref_queue_promise, //
platform_view = platform_view->GetWeakPtr(), //
io_task_runner //
[&io_manager_promise, //
&weak_io_manager_promise, //
&unref_queue_promise, //
platform_view = platform_view->GetWeakPtr(), //
io_task_runner, //
is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() //
]() {
TRACE_EVENT0("flutter", "ShellSetupIOSubsystem");
auto io_manager = std::make_unique<ShellIOManager>(
platform_view.getUnsafe()->CreateResourceContext(), io_task_runner);
platform_view.getUnsafe()->CreateResourceContext(),
is_backgrounded_sync_switch, io_task_runner);
weak_io_manager_promise.set_value(io_manager->GetWeakPtr());
unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue());
io_manager_promise.set_value(std::move(io_manager));
......@@ -289,6 +291,7 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings)
: task_runners_(std::move(task_runners)),
settings_(std::move(settings)),
vm_(std::move(vm)),
is_gpu_disabled_sync_switch_(new fml::SyncSwitch()),
weak_factory_(this),
weak_factory_gpu_(nullptr) {
FML_CHECK(vm_) << "Must have access to VM to create a shell.";
......@@ -647,7 +650,9 @@ void Shell::OnPlatformViewDestroyed() {
auto io_task = [io_manager = io_manager_.get(), &latch]() {
// Execute any pending Skia object deletions while GPU access is still
// allowed.
io_manager->GetSkiaUnrefQueue()->Drain();
io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
fml::SyncSwitch::Handlers().SetIfFalse(
[&] { io_manager->GetSkiaUnrefQueue()->Drain(); }));
// Step 3: All done. Signal the latch that the platform thread is waiting
// on.
latch.Signal();
......@@ -1419,4 +1424,8 @@ bool Shell::ReloadSystemFonts() {
return true;
}
std::shared_ptr<fml::SyncSwitch> Shell::GetIsGpuDisabledSyncSwitch() const {
return is_gpu_disabled_sync_switch_;
}
} // namespace flutter
......@@ -18,6 +18,7 @@
#include "flutter/fml/memory/thread_checker.h"
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/fml/status.h"
#include "flutter/fml/synchronization/sync_switch.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/thread.h"
#include "flutter/lib/ui/semantics/custom_accessibility_action.h"
......@@ -305,6 +306,10 @@ class Shell final : public PlatformView::Delegate,
///
bool EngineHasLivePorts() const;
//----------------------------------------------------------------------------
/// @brief Accessor for the disable GPU SyncSwitch
std::shared_ptr<fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() const;
private:
using ServiceProtocolHandler =
std::function<bool(const ServiceProtocol::Handler::ServiceProtocolMap&,
......@@ -317,6 +322,7 @@ class Shell final : public PlatformView::Delegate,
std::unique_ptr<Engine> engine_; // on UI task runner
std::unique_ptr<Rasterizer> rasterizer_; // on GPU task runner
std::unique_ptr<ShellIOManager> io_manager_; // on IO task runner
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch_;
fml::WeakPtr<Engine> weak_engine_; // to be shared across threads
fml::WeakPtr<Rasterizer> weak_rasterizer_; // to be shared across threads
......
......@@ -53,6 +53,7 @@ sk_sp<GrContext> ShellIOManager::CreateCompatibleResourceLoadingContext(
ShellIOManager::ShellIOManager(
sk_sp<GrContext> resource_context,
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch,
fml::RefPtr<fml::TaskRunner> unref_queue_task_runner)
: resource_context_(std::move(resource_context)),
resource_context_weak_factory_(
......@@ -62,7 +63,8 @@ ShellIOManager::ShellIOManager(
unref_queue_(fml::MakeRefCounted<flutter::SkiaUnrefQueue>(
std::move(unref_queue_task_runner),
fml::TimeDelta::FromMilliseconds(8))),
weak_factory_(this) {
weak_factory_(this),
is_gpu_disabled_sync_switch_(is_gpu_disabled_sync_switch) {
if (!resource_context_) {
#ifndef OS_FUCHSIA
FML_DLOG(WARNING) << "The IO manager was initialized without a resource "
......@@ -75,7 +77,8 @@ ShellIOManager::ShellIOManager(
ShellIOManager::~ShellIOManager() {
// Last chance to drain the IO queue as the platform side reference to the
// underlying OpenGL context may be going away.
unref_queue_->Drain();
is_gpu_disabled_sync_switch_->Execute(
fml::SyncSwitch::Handlers().SetIfFalse([&] { unref_queue_->Drain(); }));
}
void ShellIOManager::NotifyResourceContextAvailable(
......@@ -117,4 +120,9 @@ fml::WeakPtr<IOManager> ShellIOManager::GetWeakIOManager() const {
return weak_factory_.GetWeakPtr();
}
// |IOManager|
std::shared_ptr<fml::SyncSwitch> ShellIOManager::GetIsGpuDisabledSyncSwitch() {
return is_gpu_disabled_sync_switch_;
}
} // namespace flutter
......@@ -25,6 +25,7 @@ class ShellIOManager final : public IOManager {
sk_sp<const GrGLInterface> gl_interface);
ShellIOManager(sk_sp<GrContext> resource_context,
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch,
fml::RefPtr<fml::TaskRunner> unref_queue_task_runner);
~ShellIOManager() override;
......@@ -51,6 +52,9 @@ class ShellIOManager final : public IOManager {
// |IOManager|
fml::RefPtr<flutter::SkiaUnrefQueue> GetSkiaUnrefQueue() const override;
// |IOManager|
std::shared_ptr<fml::SyncSwitch> GetIsGpuDisabledSyncSwitch() override;
private:
// Resource context management.
sk_sp<GrContext> resource_context_;
......@@ -62,6 +66,8 @@ class ShellIOManager final : public IOManager {
fml::WeakPtrFactory<ShellIOManager> weak_factory_;
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch_;
FML_DISALLOW_COPY_AND_ASSIGN(ShellIOManager);
};
......
......@@ -302,6 +302,13 @@ FLUTTER_EXPORT
*/
@property(nonatomic, readonly, copy, nullable) NSString* isolateId;
/**
* Whether or not GPU calls are allowed.
*
* Typically this is set when the app is backgrounded and foregrounded.
*/
@property(nonatomic, assign) BOOL isGpuDisabled;
@end
NS_ASSUME_NONNULL_END
......
......@@ -108,6 +108,16 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
[center addObserver:self
selector:@selector(applicationBecameActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[center addObserver:self
selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
return self;
}
......@@ -117,11 +127,11 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
[_binaryMessenger release];
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
if (_flutterViewControllerWillDeallocObserver) {
[center removeObserver:_flutterViewControllerWillDeallocObserver];
[_flutterViewControllerWillDeallocObserver release];
}
[center removeObserver:self];
[super dealloc];
}
......@@ -456,6 +466,7 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
}
_publisher.reset([[FlutterObservatoryPublisher alloc] init]);
[self maybeSetupPlatformViewChannels];
_shell->GetIsGpuDisabledSyncSwitch()->SetSwitch(_isGpuDisabled ? true : false);
}
return _shell != nullptr;
......@@ -639,7 +650,15 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
return _pluginPublications[pluginKey];
}
#pragma mark - Memory Notifications
#pragma mark - Notifications
- (void)applicationBecameActive:(NSNotification*)notification {
[self setIsGpuDisabled:NO];
}
- (void)applicationWillResignActive:(NSNotification*)notification {
[self setIsGpuDisabled:YES];
}
- (void)onMemoryWarning:(NSNotification*)notification {
if (_shell) {
......@@ -648,6 +667,13 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
[_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
}
- (void)setIsGpuDisabled:(BOOL)value {
if (_shell) {
_shell->GetIsGpuDisabledSyncSwitch()->SetSwitch(value ? true : false);
}
_isGpuDisabled = value;
}
@end
@implementation FlutterEngineRegistrar {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册