Skip to content

Commit 91f921a

Browse files
xiaowei-guanJSUYA
authored andcommitted
[Impeller] Use the IO context for OpenGL program setup (#37)
Fix flutter-tizen/embedder#157 Impeller eagerly sets up the entire set of pipeline state objects (PSOs) it may need during renderer setup. This works fine on mid to high-end devices as the setup completes well before the first frame is rendered. However on low-end devices, not all PSOs may be ready before the first frame is rendered. Low-end device usually don't have as much available concurrency and are slower to boot. On these devices, the rendering thread had to wait for the background compile job to be completed. It was also observed that the relatively higher priority render thread was waiting on a background thread to finish a PSO compile job. The PSO compile job could also be stuck behind another job that was not immediately needed. This made the situation on low end devices less than ideal. flutter#180022, this PR fix PSO stuck issue for Vulkan on low end devices. Current PR can fix the PSO stuck issue for GLES on low end devices. Related issue : flutter#176657 This PR can improve app startup time for impeller+GLES on low-end devices. flutter#180022 has provided solution for impeller + vulkan. Impeller+GLES has a special case: resource loading cannot be arbitrarily placed on other threads; it needs to be done on the I/O thread because this thread shares the context with the GPU thread. Therefore, this PR introduces the I/O task runner into PipelineCompileQueueGLES. However, since the I/O task runner may handle other tasks, we cannot put all jobs into the I/O thread at once. My current solution is to wait for the previous job to finish before putting the next one into the I/O thread. Main changes: Introduce fml::RefPtrfml::TaskRunner into PipelineCompileQueueGLES. Extract new class PipelineCompileQueueVulkan for vulkan, because the runners are on different threads. (cherry picked from commit 92a8430)
1 parent d8e3bb9 commit 91f921a

27 files changed

Lines changed: 885 additions & 93 deletions

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ template("renderer_unittests_component") {
104104
"blit_pass_unittests.cc",
105105
"capabilities_unittests.cc",
106106
"device_buffer_unittests.cc",
107+
"pipeline_compile_queue_unittests.cc",
107108
"pipeline_descriptor_unittests.cc",
108109
"pipeline_library_unittests.cc",
109110
"pool_unittests.cc",

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impeller_component("gles_unittests") {
2424
"test/mock_gles.cc",
2525
"test/mock_gles.h",
2626
"test/mock_gles_unittests.cc",
27+
"test/pipeline_compile_queue_gles_unittests.cc",
2728
"test/pipeline_library_gles_unittests.cc",
2829
"test/proc_table_gles_unittests.cc",
2930
"test/reactor_unittests.cc",
@@ -34,6 +35,7 @@ impeller_component("gles_unittests") {
3435
]
3536
deps = [
3637
":gles",
38+
"//flutter/fml",
3739
"//flutter/impeller/playground:playground_test",
3840
"//flutter/testing:testing_lib",
3941
]
@@ -68,6 +70,8 @@ impeller_component("gles") {
6870
"gpu_tracer_gles.h",
6971
"handle_gles.cc",
7072
"handle_gles.h",
73+
"pipeline_compile_queue_gles.cc",
74+
"pipeline_compile_queue_gles.h",
7175
"pipeline_gles.cc",
7276
"pipeline_gles.h",
7377
"pipeline_library_gles.cc",

engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,19 @@ std::shared_ptr<ContextGLES> ContextGLES::Create(
2222
const Flags& flags,
2323
std::unique_ptr<ProcTableGLES> gl,
2424
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
25-
bool enable_gpu_tracing) {
26-
return std::shared_ptr<ContextGLES>(new ContextGLES(
27-
flags, std::move(gl), shader_libraries, enable_gpu_tracing));
25+
bool enable_gpu_tracing,
26+
fml::RefPtr<fml::TaskRunner> io_task_runner) {
27+
return std::shared_ptr<ContextGLES>(
28+
new ContextGLES(flags, std::move(gl), shader_libraries,
29+
enable_gpu_tracing, std::move(io_task_runner)));
2830
}
2931

3032
ContextGLES::ContextGLES(
3133
const Flags& flags,
3234
std::unique_ptr<ProcTableGLES> gl,
3335
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_mappings,
34-
bool enable_gpu_tracing)
36+
bool enable_gpu_tracing,
37+
fml::RefPtr<fml::TaskRunner> io_task_runner)
3538
: Context(flags) {
3639
reactor_ = std::make_shared<ReactorGLES>(std::move(gl));
3740
if (!reactor_->IsValid()) {
@@ -52,8 +55,8 @@ ContextGLES::ContextGLES(
5255

5356
// Create the pipeline library.
5457
{
55-
pipeline_library_ =
56-
std::shared_ptr<PipelineLibraryGLES>(new PipelineLibraryGLES(reactor_));
58+
pipeline_library_ = std::shared_ptr<PipelineLibraryGLES>(
59+
new PipelineLibraryGLES(reactor_, std::move(io_task_runner)));
5760
}
5861

5962
// Create allocators.

engine/src/flutter/impeller/renderer/backend/gles/context_gles.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class ContextGLES final : public Context,
2828
const Flags& flags,
2929
std::unique_ptr<ProcTableGLES> gl,
3030
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
31-
bool enable_gpu_tracing);
31+
bool enable_gpu_tracing,
32+
fml::RefPtr<fml::TaskRunner> io_task_runner = nullptr);
3233

3334
// |Context|
3435
~ContextGLES() override;
@@ -64,7 +65,8 @@ class ContextGLES final : public Context,
6465
const Flags& flags,
6566
std::unique_ptr<ProcTableGLES> gl,
6667
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries,
67-
bool enable_gpu_tracing);
68+
bool enable_gpu_tracing,
69+
fml::RefPtr<fml::TaskRunner> io_task_runner = nullptr);
6870

6971
// |Context|
7072
std::string DescribeGpuModel() const override;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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 "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h"
6+
7+
#include "flutter/fml/logging.h"
8+
#include "flutter/fml/trace_event.h"
9+
#include "impeller/base/validation.h"
10+
11+
namespace impeller {
12+
13+
std::shared_ptr<PipelineCompileQueueGLES> PipelineCompileQueueGLES::Create(
14+
fml::RefPtr<fml::TaskRunner> worker_task_runner) {
15+
if (!worker_task_runner) {
16+
return nullptr;
17+
}
18+
return std::shared_ptr<PipelineCompileQueueGLES>(
19+
new PipelineCompileQueueGLES(std::move(worker_task_runner)));
20+
}
21+
22+
PipelineCompileQueueGLES::PipelineCompileQueueGLES(
23+
fml::RefPtr<fml::TaskRunner> worker_task_runner)
24+
: worker_task_runner_(std::move(worker_task_runner)) {}
25+
26+
PipelineCompileQueueGLES::~PipelineCompileQueueGLES() = default;
27+
28+
void PipelineCompileQueueGLES::OnJobAdded() {
29+
// To prevent potential deadlocks and reduce lock contention, avoid calling
30+
// external or virtual methods (such as DrainPendingJobs, which posts tasks
31+
// to the task runner) while holding a mutex. Instead, minimize the scope of
32+
// the lock by using a local boolean flag to trigger the draining process
33+
// outside the lock block.
34+
bool should_drain = false;
35+
{
36+
Lock lock(processing_mutex_);
37+
if (!is_processing_) {
38+
is_processing_ = true;
39+
should_drain = true;
40+
}
41+
}
42+
if (should_drain) {
43+
DrainPendingJobs();
44+
}
45+
}
46+
47+
void PipelineCompileQueueGLES::PostJob(const fml::closure& job) {
48+
if (!job) {
49+
return;
50+
}
51+
52+
worker_task_runner_->PostTask(job);
53+
}
54+
55+
void PipelineCompileQueueGLES::DrainPendingJobs() {
56+
PostJob([weak_queue = weak_from_this()]() {
57+
if (auto queue = std::static_pointer_cast<PipelineCompileQueueGLES>(
58+
weak_queue.lock())) {
59+
queue->DoOneJob();
60+
{
61+
Lock lock(queue->processing_mutex_);
62+
if (!queue->HasPendingJobs()) {
63+
queue->is_processing_ = false;
64+
return;
65+
}
66+
}
67+
queue->DrainPendingJobs();
68+
}
69+
});
70+
}
71+
72+
} // namespace impeller
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_
6+
#define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_
7+
8+
#include "flutter/fml/closure.h"
9+
#include "flutter/fml/task_runner.h"
10+
#include "impeller/base/thread.h"
11+
#include "impeller/renderer/pipeline_compile_queue.h"
12+
13+
namespace impeller {
14+
15+
//------------------------------------------------------------------------------
16+
/// @brief A task queue designed for managing compilation of pipeline state
17+
/// objects for OpenGL ES backend.
18+
///
19+
/// This subclass uses a fml::TaskRunner as the worker task runner
20+
/// and implements a sequential job processing mechanism to prevent
21+
/// blocking the IO task runner.
22+
///
23+
/// Key characteristics:
24+
/// - Uses fml::RefPtr<fml::TaskRunner> for worker_task_runner_
25+
/// - Processes jobs sequentially: loads one job at a time before
26+
/// proceeding to the next, preventing IO task runner blocking
27+
/// - Uses DrainPendingJobs() to recursively process jobs one by one
28+
/// - Employs is_processing_ flag and processing_mutex_ to control
29+
/// sequential processing
30+
///
31+
/// The sequential processing ensures that pipeline compilation jobs
32+
/// do not overwhelm the task runner, which is particularly
33+
/// important for GLES backend where resource loading patterns
34+
/// differ from Vulkan.
35+
///
36+
class PipelineCompileQueueGLES : public PipelineCompileQueue {
37+
public:
38+
static std::shared_ptr<PipelineCompileQueueGLES> Create(
39+
fml::RefPtr<fml::TaskRunner> worker_task_runner);
40+
41+
~PipelineCompileQueueGLES() override;
42+
43+
PipelineCompileQueueGLES(const PipelineCompileQueueGLES&) = delete;
44+
45+
PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete;
46+
47+
void PostJob(const fml::closure& job) override;
48+
49+
void OnJobAdded() override;
50+
51+
private:
52+
explicit PipelineCompileQueueGLES(
53+
fml::RefPtr<fml::TaskRunner> worker_task_runner);
54+
void DrainPendingJobs();
55+
56+
fml::RefPtr<fml::TaskRunner> worker_task_runner_;
57+
Mutex processing_mutex_;
58+
bool is_processing_ IPLR_GUARDED_BY(processing_mutex_) = false;
59+
};
60+
61+
} // namespace impeller
62+
63+
#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_

engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616

1717
namespace impeller {
1818

19-
PipelineLibraryGLES::PipelineLibraryGLES(std::shared_ptr<ReactorGLES> reactor)
20-
: reactor_(std::move(reactor)) {}
19+
PipelineLibraryGLES::PipelineLibraryGLES(
20+
std::shared_ptr<ReactorGLES> reactor,
21+
fml::RefPtr<fml::TaskRunner> io_task_runner)
22+
: reactor_(std::move(reactor)),
23+
compile_queue_(
24+
PipelineCompileQueueGLES::Create(std::move(io_task_runner))) {}
2125

2226
static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) {
2327
GLint log_length = 0;
@@ -296,17 +300,34 @@ PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
296300
PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
297301
pipelines_[descriptor] = pipeline_future;
298302

299-
const auto result = reactor_->AddOperation([promise, //
300-
weak_this = weak_from_this(), //
301-
descriptor, //
302-
vert_function, //
303-
frag_function, //
304-
threadsafe //
305-
](const ReactorGLES& reactor) {
306-
promise->set_value(CreatePipeline(weak_this, descriptor, vert_function,
307-
frag_function, threadsafe));
308-
});
309-
FML_CHECK(result);
303+
auto weak_this = weak_from_this();
304+
auto reactor = reactor_;
305+
auto generation_task = [promise, weak_this, descriptor, vert_function,
306+
frag_function, threadsafe, reactor]() {
307+
auto thiz = weak_this.lock();
308+
if (!thiz) {
309+
promise->set_value(nullptr);
310+
return;
311+
}
312+
const auto result = reactor->AddOperation([promise, //
313+
weak_this, //
314+
descriptor, //
315+
vert_function, //
316+
frag_function, //
317+
threadsafe //
318+
](const ReactorGLES& reactor) {
319+
promise->set_value(CreatePipeline(weak_this, descriptor, vert_function,
320+
frag_function, threadsafe));
321+
});
322+
FML_CHECK(result);
323+
};
324+
325+
if (async && compile_queue_) {
326+
compile_queue_->PostJobForDescriptor(descriptor,
327+
std::move(generation_task));
328+
} else {
329+
generation_task();
330+
}
310331

311332
return pipeline_future;
312333
}
@@ -373,4 +394,8 @@ void PipelineLibraryGLES::SetProgramForKey(
373394
programs_[key] = std::move(program);
374395
}
375396

397+
PipelineCompileQueue* PipelineLibraryGLES::GetPipelineCompileQueue() const {
398+
return compile_queue_.get();
399+
}
400+
376401
} // namespace impeller

engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include <vector>
1010

1111
#include "flutter/fml/hash_combine.h"
12+
#include "flutter/fml/task_runner.h"
1213
#include "impeller/base/thread.h"
14+
#include "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h"
1315
#include "impeller/renderer/backend/gles/reactor_gles.h"
1416
#include "impeller/renderer/backend/gles/unique_handle_gles.h"
1517
#include "impeller/renderer/pipeline_library.h"
@@ -91,8 +93,10 @@ class PipelineLibraryGLES final
9193
PipelineMap pipelines_;
9294
Mutex programs_mutex_;
9395
ProgramMap programs_ IPLR_GUARDED_BY(programs_mutex_);
96+
std::shared_ptr<PipelineCompileQueueGLES> compile_queue_;
9497

95-
explicit PipelineLibraryGLES(std::shared_ptr<ReactorGLES> reactor);
98+
explicit PipelineLibraryGLES(std::shared_ptr<ReactorGLES> reactor,
99+
fml::RefPtr<fml::TaskRunner> io_task_runner);
96100

97101
// |PipelineLibrary|
98102
bool IsValid() const override;
@@ -127,6 +131,8 @@ class PipelineLibraryGLES final
127131

128132
void SetProgramForKey(const ProgramKey& key,
129133
std::shared_ptr<UniqueHandleGLES> program);
134+
// |PipelineLibrary|
135+
PipelineCompileQueue* GetPipelineCompileQueue() const override;
130136
};
131137

132138
} // namespace impeller

0 commit comments

Comments
 (0)