Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
pipeline.h
Go to the documentation of this file.
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_SHELL_COMMON_PIPELINE_H_
6#define FLUTTER_SHELL_COMMON_PIPELINE_H_
7
8#include <deque>
9#include <memory>
10#include <mutex>
11
12#include "flutter/flow/frame_timings.h"
13#include "flutter/flow/layers/layer_tree.h"
14#include "flutter/fml/macros.h"
15#include "flutter/fml/memory/ref_counted.h"
16#include "flutter/fml/synchronization/semaphore.h"
17#include "flutter/fml/trace_event.h"
18
19namespace flutter {
20
22 // Whether the item was successfully pushed into the pipeline.
23 bool success = false;
24 // Whether it is the first item of the pipeline. Only valid when 'success' is
25 // 'true'.
26 bool is_first_item = false;
27};
28
30 // NOLINTBEGIN(readability-identifier-naming)
32 Done,
34 // NOLINTEND(readability-identifier-naming)
35};
36
38
39/// A thread-safe queue of resources for a single consumer and a single
40/// producer, with a maximum queue depth.
41///
42/// Pipelines support two key operations: produce and consume.
43///
44/// The consumer calls |Consume| to wait for a resource to be produced and
45/// consume it when ready.
46///
47/// The producer calls |Produce| to generate a `ProducerContinuation` which
48/// provides a means to enqueue a resource in the pipeline, if the pipeline is
49/// below its maximum depth. When the resource has been prepared, the producer
50/// calls `Complete` on the continuation, which enqueues the resource and
51/// signals the waiting consumer.
52///
53/// Pipelines generate the following tracing information:
54/// * PipelineItem: async flow tracking time taken from the time a producer
55/// calls |Produce| to the time a consumer consumes calls |Consume|.
56/// * PipelineProduce: async flow tracking time taken from the time a producer
57/// calls |Produce| to the time they complete the `ProducerContinuation` with
58/// a resource.
59/// * Pipeline Depth: counter of inflight resource producers.
60///
61/// The primary use of this class is as the frame pipeline used in Flutter's
62/// animator/rasterizer.
63template <class R>
64class Pipeline {
65 public:
66 using Resource = R;
67 using ResourcePtr = std::unique_ptr<Resource>;
68
69 /// Denotes a spot in the pipeline reserved for the producer to finish
70 /// preparing a completed pipeline resource.
72 public:
73 ProducerContinuation() : trace_id_(0) {}
74
76 : continuation_(other.continuation_), trace_id_(other.trace_id_) {
77 other.continuation_ = nullptr;
78 other.trace_id_ = 0;
79 }
80
82 std::swap(continuation_, other.continuation_);
83 std::swap(trace_id_, other.trace_id_);
84 return *this;
85 }
86
88 if (continuation_) {
89 continuation_(nullptr, trace_id_);
90 TRACE_EVENT_ASYNC_END0("flutter", "PipelineProduce", trace_id_);
91 // The continuation is being dropped on the floor. End the flow.
92 TRACE_FLOW_END("flutter", "PipelineItem", trace_id_);
93 TRACE_EVENT_ASYNC_END0("flutter", "PipelineItem", trace_id_);
94 }
95 }
96
97 /// Completes the continuation with the specified resource.
98 [[nodiscard]] PipelineProduceResult Complete(ResourcePtr resource) {
100 if (continuation_) {
101 result = continuation_(std::move(resource), trace_id_);
102 continuation_ = nullptr;
103 TRACE_EVENT_ASYNC_END0("flutter", "PipelineProduce", trace_id_);
104 TRACE_FLOW_STEP("flutter", "PipelineItem", trace_id_);
105 }
106 return result;
107 }
108
109 explicit operator bool() const { return continuation_ != nullptr; }
110
111 private:
112 friend class Pipeline;
113 using Continuation =
114 std::function<PipelineProduceResult(ResourcePtr, size_t)>;
115
116 Continuation continuation_;
117 uint64_t trace_id_;
118
119 ProducerContinuation(const Continuation& continuation, uint64_t trace_id)
120 : continuation_(continuation), trace_id_(trace_id) {
121 TRACE_EVENT_ASYNC_BEGIN0_WITH_FLOW_IDS("flutter", "PipelineItem",
122 trace_id_, /*flow_id_count=*/1,
123 /*flow_ids=*/&trace_id);
124 TRACE_FLOW_BEGIN("flutter", "PipelineItem", trace_id_);
125 TRACE_EVENT_ASYNC_BEGIN0("flutter", "PipelineProduce", trace_id_);
126 }
127
129 };
130
131 explicit Pipeline(uint32_t depth)
132 : empty_(depth), available_(0), inflight_(0) {}
133
134 ~Pipeline() = default;
135
136 bool IsValid() const { return empty_.IsValid() && available_.IsValid(); }
137
138 /// Creates a `ProducerContinuation` that a producer can use to add a
139 /// resource to the queue.
140 ///
141 /// If the queue is already at its maximum depth, the `ProducerContinuation`
142 /// is returned with success = false.
144 if (!empty_.TryWait()) {
145 return {};
146 }
147 ++inflight_;
148 FML_TRACE_COUNTER("flutter", "Pipeline Depth",
149 reinterpret_cast<int64_t>(this), //
150 "frames in flight", inflight_.load() //
151 );
152
154 std::bind(&Pipeline::ProducerCommit, this, std::placeholders::_1,
155 std::placeholders::_2), // continuation
156 GetNextPipelineTraceID()}; // trace id
157 }
158
159 /// Creates a `ProducerContinuation` that will only push the task if the
160 /// queue is empty.
161 ///
162 /// Prefer using |Produce|. ProducerContinuation returned by this method
163 /// doesn't guarantee that the frame will be rendered.
165 if (!empty_.TryWait()) {
166 return {};
167 }
168 ++inflight_;
169 FML_TRACE_COUNTER("flutter", "Pipeline Depth",
170 reinterpret_cast<int64_t>(this), //
171 "frames in flight", inflight_.load() //
172 );
173
175 std::bind(&Pipeline::ProducerCommitIfEmpty, this, std::placeholders::_1,
176 std::placeholders::_2), // continuation
177 GetNextPipelineTraceID()}; // trace id
178 }
179
180 using Consumer = std::function<void(ResourcePtr)>;
181
182 /// @note Procedure doesn't copy all closures.
183 [[nodiscard]] PipelineConsumeResult Consume(const Consumer& consumer) {
184 if (consumer == nullptr) {
186 }
187
188 if (!available_.TryWait()) {
190 }
191
192 ResourcePtr resource;
193 size_t trace_id = 0;
194 size_t items_count = 0;
195
196 {
197 std::scoped_lock lock(queue_mutex_);
198 std::tie(resource, trace_id) = std::move(queue_.front());
199 queue_.pop_front();
200 items_count = queue_.size();
201 }
202
203 consumer(std::move(resource));
204
205 empty_.Signal();
206 --inflight_;
207
208 TRACE_FLOW_END("flutter", "PipelineItem", trace_id);
209 TRACE_EVENT_ASYNC_END0("flutter", "PipelineItem", trace_id);
210
211 return items_count > 0 ? PipelineConsumeResult::MoreAvailable
213 }
214
215 private:
216 fml::Semaphore empty_;
217 fml::Semaphore available_;
218 std::atomic<int> inflight_;
219 std::mutex queue_mutex_;
220 std::deque<std::pair<ResourcePtr, size_t>> queue_;
221
222 /// Commits a produced resource to the queue and signals the consumer that a
223 /// resource is available.
224 PipelineProduceResult ProducerCommit(ResourcePtr resource, size_t trace_id) {
225 bool is_first_item = false;
226 {
227 std::scoped_lock lock(queue_mutex_);
228 is_first_item = queue_.empty();
229 queue_.emplace_back(std::move(resource), trace_id);
230 }
231
232 // Ensure the queue mutex is not held as that would be a pessimization.
233 available_.Signal();
234 return {.success = true, .is_first_item = is_first_item};
235 }
236
237 PipelineProduceResult ProducerCommitIfEmpty(ResourcePtr resource,
238 size_t trace_id) {
239 {
240 std::scoped_lock lock(queue_mutex_);
241 if (!queue_.empty()) {
242 // Bail if the queue is not empty, opens up spaces to produce other
243 // frames.
244 empty_.Signal();
245 return {.success = false, .is_first_item = false};
246 }
247 queue_.emplace_back(std::move(resource), trace_id);
248 }
249
250 // Ensure the queue mutex is not held as that would be a pessimization.
251 available_.Signal();
252 return {.success = true, .is_first_item = true};
253 }
254
256};
257
258} // namespace flutter
259
260#endif // FLUTTER_SHELL_COMMON_PIPELINE_H_
ProducerContinuation(ProducerContinuation &&other)
Definition pipeline.h:75
PipelineProduceResult Complete(ResourcePtr resource)
Completes the continuation with the specified resource.
Definition pipeline.h:98
ProducerContinuation & operator=(ProducerContinuation &&other)
Definition pipeline.h:81
std::function< void(ResourcePtr)> Consumer
Definition pipeline.h:180
~Pipeline()=default
ProducerContinuation ProduceIfEmpty()
Definition pipeline.h:164
ProducerContinuation Produce()
Definition pipeline.h:143
PipelineConsumeResult Consume(const Consumer &consumer)
Definition pipeline.h:183
bool IsValid() const
Definition pipeline.h:136
std::unique_ptr< Resource > ResourcePtr
Definition pipeline.h:67
Pipeline(uint32_t depth)
Definition pipeline.h:131
A traditional counting semaphore. Waits decrement the counter and Signal increments it.
Definition semaphore.h:26
bool IsValid() const
Check if the underlying semaphore handle could be created. Failure modes are platform specific and ma...
Definition semaphore.cc:177
void Signal()
Increment the count by one. Any pending Waits will be resolved at this point.
Definition semaphore.cc:189
bool TryWait()
Decrement the counts if it is greater than zero. Returns false if the counter is already at zero.
Definition semaphore.cc:185
GAsyncResult * result
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
#define R(r)
size_t GetNextPipelineTraceID()
Definition pipeline.cc:9
PipelineConsumeResult
Definition pipeline.h:29
#define TRACE_FLOW_BEGIN(category, name, id)
#define TRACE_FLOW_STEP(category, name, id)
#define FML_TRACE_COUNTER(category_group, name, counter_id, arg1,...)
Definition trace_event.h:85
#define TRACE_EVENT_ASYNC_BEGIN0_WITH_FLOW_IDS(category_group, name, id, flow_id_count, flow_ids)
#define TRACE_EVENT_ASYNC_END0(category_group, name, id)
#define TRACE_FLOW_END(category, name, id)
#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id)