Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit af3627a

Browse files
author
Jonah Williams
authored
[Impeller] acquire the gpu sync switch when flush stored GPU tasks. (#169596)
Potential fix for flutter/flutter#166668 See: * https://github.com/flutter/flutter/blob/master/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm#L429 * https://github.com/flutter/flutter/blob/5d013c73baa70a8b3e1c541cb63e4c22654aa3cc/engine/src/flutter/fml/synchronization/sync_switch.cc#L41 We don't hold the sync switch lock when we flush tasks. So if we start flushing then immediately go to the background, then we might execute while backgrounded.
1 parent 3e8b531 commit af3627a

10 files changed

Lines changed: 107 additions & 4 deletions

File tree

engine/src/flutter/ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@
191191
../../../flutter/impeller/renderer/backend/gles/test
192192
../../../flutter/impeller/renderer/backend/gles/unique_handle_gles_unittests.cc
193193
../../../flutter/impeller/renderer/backend/metal/allocator_mtl_unittests.mm
194+
../../../flutter/impeller/renderer/backend/metal/context_mtl_unittests.mm
194195
../../../flutter/impeller/renderer/backend/metal/swapchain_transients_mtl_unittests.mm
195196
../../../flutter/impeller/renderer/backend/metal/texture_mtl_unittests.mm
196197
../../../flutter/impeller/renderer/backend/vulkan/allocator_vk_unittests.cc

engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class PlaygroundImplMTL final : public PlaygroundImpl {
3838
std::shared_ptr<ContextMTL> context_;
3939
std::shared_ptr<fml::ConcurrentMessageLoop> concurrent_loop_;
4040
std::shared_ptr<SwapchainTransientsMTL> swapchain_transients_;
41-
std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch_;
41+
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch_;
4242

4343
// |PlaygroundImpl|
4444
std::shared_ptr<Context> GetContext() const override;
@@ -50,6 +50,9 @@ class PlaygroundImplMTL final : public PlaygroundImpl {
5050
std::unique_ptr<Surface> AcquireSurfaceFrame(
5151
std::shared_ptr<Context> context) override;
5252

53+
// |PlaygroundImpl|
54+
void SetGPUDisabled(bool disabled) const override;
55+
5356
PlaygroundImplMTL(const PlaygroundImplMTL&) = delete;
5457

5558
PlaygroundImplMTL& operator=(const PlaygroundImplMTL&) = delete;

engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,8 @@
138138
return fml::Status();
139139
}
140140

141+
void PlaygroundImplMTL::SetGPUDisabled(bool disabled) const {
142+
is_gpu_disabled_sync_switch_->SetSwitch(disabled);
143+
}
144+
141145
} // namespace impeller

engine/src/flutter/impeller/playground/playground.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,4 +527,8 @@ Playground::VKProcAddressResolver Playground::CreateVKProcAddressResolver()
527527
return impl_->CreateVKProcAddressResolver();
528528
}
529529

530+
void Playground::SetGPUDisabled(bool value) const {
531+
impl_->SetGPUDisabled(value);
532+
}
533+
530534
} // namespace impeller

engine/src/flutter/impeller/playground/playground.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ class Playground {
122122
std::function<void*(void* instance, const char* proc_name)>;
123123
VKProcAddressResolver CreateVKProcAddressResolver() const;
124124

125+
/// @brief Mark the GPU as unavilable.
126+
///
127+
/// Only supported on the Metal backend.
128+
void SetGPUDisabled(bool disabled) const;
129+
125130
protected:
126131
const PlaygroundSwitches switches_;
127132

engine/src/flutter/impeller/playground/playground_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class PlaygroundImpl {
4040

4141
virtual Playground::VKProcAddressResolver CreateVKProcAddressResolver() const;
4242

43+
virtual void SetGPUDisabled(bool disabled) const {}
44+
4345
protected:
4446
const PlaygroundSwitches switches_;
4547

engine/src/flutter/impeller/renderer/backend/metal/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impeller_component("metal_unittests") {
7070

7171
sources = [
7272
"allocator_mtl_unittests.mm",
73+
"context_mtl_unittests.mm",
7374
"swapchain_transients_mtl_unittests.mm",
7475
"texture_mtl_unittests.mm",
7576
]

engine/src/flutter/impeller/renderer/backend/metal/context_mtl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ class ContextMTL final : public Context,
146146
void StoreTaskForGPU(const fml::closure& task,
147147
const fml::closure& failure) override;
148148

149+
// visible for testing.
150+
void FlushTasksAwaitingGPU();
151+
149152
private:
150153
class SyncSwitchObserver : public fml::SyncSwitch::Observer {
151154
public:
@@ -191,8 +194,6 @@ class ContextMTL final : public Context,
191194
std::shared_ptr<CommandBuffer> CreateCommandBufferInQueue(
192195
id<MTLCommandQueue> queue) const;
193196

194-
void FlushTasksAwaitingGPU();
195-
196197
ContextMTL(const ContextMTL&) = delete;
197198

198199
ContextMTL& operator=(const ContextMTL&) = delete;

engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,26 @@ new ContextMTL(flags, device, command_queue,
418418
Lock lock(tasks_awaiting_gpu_mutex_);
419419
std::swap(tasks_awaiting_gpu, tasks_awaiting_gpu_);
420420
}
421+
std::vector<PendingTasks> tasks_to_queue;
421422
for (const auto& task : tasks_awaiting_gpu) {
422-
task.task();
423+
is_gpu_disabled_sync_switch_->Execute(fml::SyncSwitch::Handlers()
424+
.SetIfFalse([&] { task.task(); })
425+
.SetIfTrue([&] {
426+
// Lost access to the GPU
427+
// immediately after it was
428+
// activated. This may happen if
429+
// the app was quickly
430+
// foregrounded/backgrounded
431+
// from a push notification.
432+
// Store the tasks on the
433+
// context again.
434+
tasks_to_queue.push_back(task);
435+
}));
436+
}
437+
if (!tasks_to_queue.empty()) {
438+
Lock lock(tasks_awaiting_gpu_mutex_);
439+
tasks_awaiting_gpu_.insert(tasks_awaiting_gpu_.end(),
440+
tasks_to_queue.begin(), tasks_to_queue.end());
423441
}
424442
}
425443

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/testing/testing.h"
6+
#include "impeller/core/device_buffer_descriptor.h"
7+
#include "impeller/core/formats.h"
8+
#include "impeller/core/texture_descriptor.h"
9+
#include "impeller/playground/playground_test.h"
10+
#include "impeller/renderer/backend/metal/allocator_mtl.h"
11+
#include "impeller/renderer/backend/metal/context_mtl.h"
12+
#include "impeller/renderer/backend/metal/formats_mtl.h"
13+
#include "impeller/renderer/backend/metal/texture_mtl.h"
14+
#include "impeller/renderer/capabilities.h"
15+
16+
#include <QuartzCore/CAMetalLayer.h>
17+
#include <memory>
18+
#include <thread>
19+
20+
#include "gtest/gtest.h"
21+
22+
namespace impeller {
23+
namespace testing {
24+
25+
using ContextMTLTest = PlaygroundTest;
26+
INSTANTIATE_METAL_PLAYGROUND_SUITE(ContextMTLTest);
27+
28+
TEST_P(ContextMTLTest, FlushTask) {
29+
auto& context_mtl = ContextMTL::Cast(*GetContext());
30+
31+
int executed = 0;
32+
int failed = 0;
33+
context_mtl.StoreTaskForGPU([&]() { executed++; }, [&]() { failed++; });
34+
35+
context_mtl.FlushTasksAwaitingGPU();
36+
37+
EXPECT_EQ(executed, 1);
38+
EXPECT_EQ(failed, 0);
39+
}
40+
41+
TEST_P(ContextMTLTest, FlushTaskWithGPULoss) {
42+
auto& context_mtl = ContextMTL::Cast(*GetContext());
43+
44+
int executed = 0;
45+
int failed = 0;
46+
context_mtl.StoreTaskForGPU([&]() { executed++; }, [&]() { failed++; });
47+
48+
// If tasks are flushed while the GPU is disabled, then
49+
// they should not be executed.
50+
SetGPUDisabled(/*disabled=*/true);
51+
context_mtl.FlushTasksAwaitingGPU();
52+
53+
EXPECT_EQ(executed, 0);
54+
EXPECT_EQ(failed, 0);
55+
56+
// Toggling availibility should flush tasks.
57+
SetGPUDisabled(/*disabled=*/false);
58+
59+
EXPECT_EQ(executed, 1);
60+
EXPECT_EQ(failed, 0);
61+
}
62+
63+
} // namespace testing
64+
} // namespace impeller

0 commit comments

Comments
 (0)