Skip to content

Commit f91ccb4

Browse files
authored
[impeller] OpenGL: Add support for threads and contexts. (flutter#33575)
1 parent 26816a7 commit f91ccb4

18 files changed

+357
-56
lines changed

impeller/base/comparable.h

+8
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,12 @@ struct hash<impeller::UniqueID> {
103103
}
104104
};
105105

106+
template <>
107+
struct less<impeller::UniqueID> {
108+
constexpr bool operator()(const impeller::UniqueID& lhs,
109+
const impeller::UniqueID& rhs) const {
110+
return lhs.id < rhs.id;
111+
}
112+
};
113+
106114
} // namespace std

impeller/playground/backend/gles/playground_impl_gles.cc

+43-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@
1616

1717
namespace impeller {
1818

19+
class PlaygroundImplGLES::ReactorWorker final : public ReactorGLES::Worker {
20+
public:
21+
ReactorWorker() = default;
22+
23+
// |ReactorGLES::Worker|
24+
bool CanReactorReactOnCurrentThreadNow(
25+
const ReactorGLES& reactor) const override {
26+
ReaderLock lock(mutex_);
27+
auto found = reactions_allowed_.find(std::this_thread::get_id());
28+
if (found == reactions_allowed_.end()) {
29+
return false;
30+
}
31+
return found->second;
32+
}
33+
34+
void SetReactionsAllowedOnCurrentThread(bool allowed) {
35+
WriterLock lock(mutex_);
36+
reactions_allowed_[std::this_thread::get_id()] = allowed;
37+
}
38+
39+
private:
40+
mutable RWMutex mutex_;
41+
std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_);
42+
43+
FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker);
44+
};
45+
1946
void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
2047
if (!handle) {
2148
return;
@@ -24,7 +51,8 @@ void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
2451
}
2552

2653
PlaygroundImplGLES::PlaygroundImplGLES()
27-
: handle_(nullptr, &DestroyWindowHandle) {
54+
: handle_(nullptr, &DestroyWindowHandle),
55+
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())) {
2856
::glfwDefaultWindowHints();
2957

3058
#if FML_OS_MACOSX
@@ -48,6 +76,7 @@ PlaygroundImplGLES::PlaygroundImplGLES()
4876
auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, nullptr);
4977

5078
::glfwMakeContextCurrent(window);
79+
worker_->SetReactionsAllowedOnCurrentThread(true);
5180

5281
handle_.reset(window);
5382
}
@@ -79,8 +108,19 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
79108
return nullptr;
80109
}
81110

82-
return ContextGLES::Create(std::move(gl),
83-
ShaderLibraryMappingsForPlayground());
111+
auto context =
112+
ContextGLES::Create(std::move(gl), ShaderLibraryMappingsForPlayground());
113+
if (!context) {
114+
FML_LOG(ERROR) << "Could not create context.";
115+
return nullptr;
116+
}
117+
118+
auto worker_id = context->AddReactorWorker(worker_);
119+
if (!worker_id.has_value()) {
120+
FML_LOG(ERROR) << "Could not add reactor worker.";
121+
return nullptr;
122+
}
123+
return context;
84124
}
85125

86126
// |PlaygroundImpl|

impeller/playground/backend/gles/playground_impl_gles.h

+3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ class PlaygroundImplGLES final : public PlaygroundImpl {
1616
~PlaygroundImplGLES();
1717

1818
private:
19+
class ReactorWorker;
20+
1921
static void DestroyWindowHandle(WindowHandle handle);
2022
using UniqueHandle = std::unique_ptr<void, decltype(&DestroyWindowHandle)>;
2123
UniqueHandle handle_;
24+
std::shared_ptr<ReactorWorker> worker_;
2225

2326
// |PlaygroundImpl|
2427
std::shared_ptr<Context> GetContext() const override;

impeller/renderer/backend/gles/context_gles.cc

+18-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
namespace impeller {
1111

12-
std::shared_ptr<Context> ContextGLES::Create(
12+
std::shared_ptr<ContextGLES> ContextGLES::Create(
1313
std::unique_ptr<ProcTableGLES> gl,
1414
std::vector<std::shared_ptr<fml::Mapping>> shader_libraries) {
15-
return std::shared_ptr<Context>(
15+
return std::shared_ptr<ContextGLES>(
1616
new ContextGLES(std::move(gl), std::move(shader_libraries)));
1717
}
1818

@@ -70,10 +70,25 @@ ContextGLES::ContextGLES(
7070

7171
ContextGLES::~ContextGLES() = default;
7272

73-
const ReactorGLES::Ref ContextGLES::GetReactor() const {
73+
const ReactorGLES::Ref& ContextGLES::GetReactor() const {
7474
return reactor_;
7575
}
7676

77+
std::optional<ReactorGLES::WorkerID> ContextGLES::AddReactorWorker(
78+
std::shared_ptr<ReactorGLES::Worker> worker) {
79+
if (!IsValid()) {
80+
return std::nullopt;
81+
}
82+
return reactor_->AddWorker(std::move(worker));
83+
}
84+
85+
bool ContextGLES::RemoveReactorWorker(ReactorGLES::WorkerID id) {
86+
if (!IsValid()) {
87+
return false;
88+
}
89+
return reactor_->RemoveWorker(id);
90+
}
91+
7792
bool ContextGLES::IsValid() const {
7893
return is_valid_;
7994
}

impeller/renderer/backend/gles/context_gles.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@ namespace impeller {
1919
class ContextGLES final : public Context,
2020
public BackendCast<ContextGLES, Context> {
2121
public:
22-
static std::shared_ptr<Context> Create(
22+
static std::shared_ptr<ContextGLES> Create(
2323
std::unique_ptr<ProcTableGLES> gl,
2424
std::vector<std::shared_ptr<fml::Mapping>> shader_libraries);
2525

2626
// |Context|
2727
~ContextGLES() override;
2828

29-
const ReactorGLES::Ref GetReactor() const;
29+
const ReactorGLES::Ref& GetReactor() const;
30+
31+
std::optional<ReactorGLES::WorkerID> AddReactorWorker(
32+
std::shared_ptr<ReactorGLES::Worker> worker);
33+
34+
bool RemoveReactorWorker(ReactorGLES::WorkerID id);
3035

3136
private:
3237
ReactorGLES::Ref reactor_;

impeller/renderer/backend/gles/proc_table_gles.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ struct AutoErrorCheck {
2727
~AutoErrorCheck() {
2828
if (error_fn) {
2929
auto error = error_fn();
30-
FML_CHECK(error == GL_NO_ERROR)
31-
<< "GL Error " << GLErrorToString(error) << "(" << error << ")"
32-
<< " encountered on call to " << name;
30+
if (error != GL_NO_ERROR) {
31+
FML_LOG(ERROR) << "GL Error " << GLErrorToString(error) << "(" << error
32+
<< ")"
33+
<< " encountered on call to " << name;
34+
}
3335
}
3436
}
3537
};

impeller/renderer/backend/gles/reactor_gles.cc

+92-36
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,20 @@ bool ReactorGLES::IsValid() const {
2727
return is_valid_;
2828
}
2929

30+
ReactorGLES::WorkerID ReactorGLES::AddWorker(std::weak_ptr<Worker> worker) {
31+
Lock lock(workers_mutex_);
32+
auto id = WorkerID{};
33+
workers_[id] = std::move(worker);
34+
return id;
35+
}
36+
37+
bool ReactorGLES::RemoveWorker(WorkerID worker) {
38+
Lock lock(workers_mutex_);
39+
return workers_.erase(worker) == 1;
40+
}
41+
3042
bool ReactorGLES::HasPendingOperations() const {
43+
Lock ops_lock(ops_mutex_);
3144
return !pending_operations_.empty() || !gl_handles_to_collect_.empty();
3245
}
3346

@@ -37,6 +50,7 @@ const ProcTableGLES& ReactorGLES::GetProcTable() const {
3750
}
3851

3952
std::optional<GLuint> ReactorGLES::GetGLHandle(const HandleGLES& handle) const {
53+
ReaderLock handles_lock(handles_mutex_);
4054
auto found = live_gl_handles_.find(handle);
4155
if (found != live_gl_handles_.end()) {
4256
return found->second;
@@ -48,8 +62,13 @@ bool ReactorGLES::AddOperation(Operation operation) {
4862
if (!operation) {
4963
return false;
5064
}
51-
pending_operations_.emplace_back(std::move(operation));
52-
return React();
65+
{
66+
Lock ops_lock(ops_mutex_);
67+
pending_operations_.emplace_back(std::move(operation));
68+
}
69+
// Attempt a reaction if able but it is not an error if this isn't possible.
70+
[[maybe_unused]] auto result = React();
71+
return true;
5372
}
5473

5574
static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
@@ -109,24 +128,30 @@ HandleGLES ReactorGLES::CreateHandle(HandleType type) {
109128
if (new_handle.IsDead()) {
110129
return HandleGLES::DeadHandle();
111130
}
131+
WriterLock handles_lock(handles_mutex_);
112132
live_gl_handles_[new_handle] =
113133
in_reaction_ ? CreateGLHandle(GetProcTable(), type) : std::nullopt;
114134
return new_handle;
115135
}
116136

117137
void ReactorGLES::CollectHandle(HandleGLES handle) {
138+
WriterLock handles_lock(handles_mutex_);
118139
auto live_handle = live_gl_handles_.find(handle);
119140
if (live_handle == live_gl_handles_.end()) {
120141
return;
121142
}
122143
if (live_handle->second.has_value()) {
144+
Lock ops_lock(ops_mutex_);
123145
gl_handles_to_collect_[live_handle->first] = live_handle->second.value();
124146
}
125147
live_gl_handles_.erase(live_handle);
126148
}
127149

128150
bool ReactorGLES::React() {
129151
TRACE_EVENT0("impeller", "ReactorGLES::React");
152+
if (!CanReactOnCurrentThread()) {
153+
return false;
154+
}
130155
in_reaction_ = true;
131156
fml::ScopedCleanupClosure reset_in_reaction([&]() { in_reaction_ = false; });
132157
while (HasPendingOperations()) {
@@ -165,7 +190,13 @@ bool ReactorGLES::ReactOnce() {
165190
//----------------------------------------------------------------------------
166191
/// Collect all the handles for whom there is a GL handle sibling.
167192
///
168-
for (const auto& handle_to_collect : gl_handles_to_collect_) {
193+
decltype(gl_handles_to_collect_) gl_handles_to_collect;
194+
{
195+
Lock ops_lock(ops_mutex_);
196+
std::swap(gl_handles_to_collect_, gl_handles_to_collect);
197+
FML_DCHECK(gl_handles_to_collect_.empty());
198+
}
199+
for (const auto& handle_to_collect : gl_handles_to_collect) {
169200
if (!CollectGLHandle(gl, // proc table
170201
handle_to_collect.first.type, // handle type
171202
handle_to_collect.second // GL handle name
@@ -174,26 +205,52 @@ bool ReactorGLES::ReactOnce() {
174205
return false;
175206
}
176207
}
177-
gl_handles_to_collect_.clear();
178208

179209
//----------------------------------------------------------------------------
180210
/// Make sure all pending handles have a GL handle sibling.
181211
///
182-
for (auto& live_handle : live_gl_handles_) {
183-
if (live_handle.second.has_value()) {
184-
// Already a realized GL handle.
185-
continue;
186-
}
187-
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
188-
if (!gl_handle.has_value()) {
189-
VALIDATION_LOG << "Could not create GL handle.";
190-
return false;
212+
{
213+
WriterLock handles_lock(handles_mutex_);
214+
for (auto& live_handle : live_gl_handles_) {
215+
if (live_handle.second.has_value()) {
216+
// Already a realized GL handle.
217+
continue;
218+
}
219+
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
220+
if (!gl_handle.has_value()) {
221+
VALIDATION_LOG << "Could not create GL handle.";
222+
return false;
223+
}
224+
live_handle.second = gl_handle;
191225
}
192-
live_handle.second = gl_handle;
193226
}
194227

195-
if (can_set_debug_labels_) {
196-
for (const auto& label : pending_debug_labels_) {
228+
//----------------------------------------------------------------------------
229+
/// Flush all pending operations in order.
230+
///
231+
decltype(pending_operations_) pending_operations;
232+
{
233+
Lock ops_lock(ops_mutex_);
234+
std::swap(pending_operations_, pending_operations);
235+
FML_DCHECK(pending_operations_.empty());
236+
}
237+
for (const auto& operation : pending_operations) {
238+
TRACE_EVENT0("impeller", "ReactorGLES::Operation");
239+
operation(*this);
240+
}
241+
242+
//----------------------------------------------------------------------------
243+
/// Make sure all pending debug labels have been flushed.
244+
///
245+
decltype(pending_debug_labels_) pending_debug_labels;
246+
{
247+
WriterLock handles_lock(handles_mutex_);
248+
std::swap(pending_debug_labels_, pending_debug_labels);
249+
FML_DCHECK(pending_debug_labels_.empty());
250+
}
251+
if (!pending_debug_labels.empty()) {
252+
ReaderLock handles_lock(handles_mutex_);
253+
for (const auto& label : pending_debug_labels) {
197254
auto live_handle = live_gl_handles_.find(label.first);
198255
if (live_handle == live_gl_handles_.end() ||
199256
!live_handle->second.has_value()) {
@@ -205,16 +262,6 @@ bool ReactorGLES::ReactOnce() {
205262
);
206263
}
207264
}
208-
pending_debug_labels_.clear();
209-
210-
//----------------------------------------------------------------------------
211-
/// Flush all pending operations in order.
212-
///
213-
auto operations = std::move(pending_operations_);
214-
for (const auto& operation : operations) {
215-
operation(*this);
216-
}
217-
pending_operations_.clear();
218265

219266
return true;
220267
}
@@ -229,18 +276,27 @@ void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
229276
if (handle.IsDead()) {
230277
return;
231278
}
232-
if (in_reaction_) {
233-
if (auto found = live_gl_handles_.find(handle);
234-
found != live_gl_handles_.end() && found->second.has_value()) {
235-
GetProcTable().SetDebugLabel(
236-
ToDebugResourceType(found->first.type), // type
237-
found->second.value(), // name
238-
label // label
239-
);
240-
return;
279+
WriterLock handles_lock(handles_mutex_);
280+
pending_debug_labels_[handle] = std::move(label);
281+
}
282+
283+
bool ReactorGLES::CanReactOnCurrentThread() const {
284+
std::vector<WorkerID> dead_workers;
285+
Lock lock(workers_mutex_);
286+
for (const auto& worker : workers_) {
287+
auto worker_ptr = worker.second.lock();
288+
if (!worker_ptr) {
289+
dead_workers.push_back(worker.first);
290+
continue;
291+
}
292+
if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
293+
return true;
241294
}
242295
}
243-
pending_debug_labels_[handle] = std::move(label);
296+
for (const auto& worker_id : dead_workers) {
297+
workers_.erase(worker_id);
298+
}
299+
return false;
244300
}
245301

246302
} // namespace impeller

0 commit comments

Comments
 (0)