Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
surface.cc
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
6
7#include <cstdint>
8
11#include "fml/logging.h"
14
15#if IMPELLER_SUPPORTS_RENDERING
17#endif
18
19namespace flutter {
20namespace gpu {
21
23
24namespace {
25
26constexpr int64_t kMaxInternalTextureReferences = 3;
27
28} // namespace
29
30Surface::TextureRecord::TextureRecord(
31 std::shared_ptr<impeller::Texture> texture,
32 sk_sp<DlImage> image,
35 : texture(std::move(texture)),
36 image(std::move(image)),
37 size(size),
38 format(format) {}
39
40Surface::Surface(std::shared_ptr<impeller::Context> context,
43 : context_(std::move(context)), size_(size), format_(format) {}
44
45Surface::~Surface() = default;
46
47std::shared_ptr<Surface::TextureRecord> Surface::CreateTextureRecord() const {
48#if !IMPELLER_SUPPORTS_RENDERING
49 FML_LOG(ERROR) << "Flutter GPU surfaces require Impeller rendering support.";
50 return nullptr;
51#else
54 desc.size = size_;
55 desc.format = format_;
58 desc.mip_count = 1;
59 desc.usage = {};
62
63 auto texture = context_->GetResourceAllocator()->CreateTexture(desc, true);
64 if (!texture) {
65 FML_LOG(ERROR) << "Failed to create Flutter GPU surface texture.";
66 return nullptr;
67 }
68
70 if (!image) {
71 FML_LOG(ERROR) << "Failed to create Flutter GPU surface image.";
72 return nullptr;
73 }
74
75 return std::make_shared<TextureRecord>(std::move(texture), std::move(image),
76 size_, format_);
77#endif
78}
79
80bool Surface::IsReusable(const std::shared_ptr<TextureRecord>& record,
81 size_t index) const {
82 if (current_index_.has_value() && current_index_.value() == index) {
83 return false;
84 }
85 return record && record->size.width == size_.width &&
86 record->size.height == size_.height && record->format == format_ &&
87 !record->acquired && !record->producer_pending.load() &&
88 record->image && record->image->unique() &&
89 static_cast<int64_t>(record->texture.use_count()) <=
90 kMaxInternalTextureReferences;
91}
92
93void Surface::PruneTextureRecords() {
94 for (size_t i = 0; i < records_.size(); i++) {
95 auto& record = records_[i];
96 if (!record) {
97 continue;
98 }
99
100 const bool is_current =
101 current_index_.has_value() && current_index_.value() == i;
102 const bool matches_surface = record->size.width == size_.width &&
103 record->size.height == size_.height &&
104 record->format == format_;
105 // The surface record, its image, and the Dart texture wrapper handed out
106 // for the completed frame may all still reference an otherwise
107 // unreferenced texture.
108 const bool has_external_references =
109 !record->image || !record->image->unique() ||
110 static_cast<int64_t>(record->texture.use_count()) >
111 kMaxInternalTextureReferences;
112
113 if (!is_current && !matches_surface && !record->acquired &&
114 !record->producer_pending.load() && !has_external_references) {
115 record.reset();
116 }
117 }
118
119 while (!records_.empty() && !records_.back()) {
120 records_.pop_back();
121 }
122}
123
124int Surface::AcquireNextFrame(Dart_Handle texture_wrapper) {
125 std::optional<size_t> available_index;
126 size_t index = 0;
127 for (const auto& record : records_) {
128 if (!record && !available_index.has_value()) {
129 available_index = index;
130 }
131 if (IsReusable(record, index)) {
132 record->acquired = true;
133 auto texture = fml::MakeRefCounted<Texture>(record->texture);
134 texture->AssociateWithDartWrapper(texture_wrapper);
135 return static_cast<int>(index);
136 }
137 index++;
138 }
139
140 auto record = CreateTextureRecord();
141 if (!record) {
142 return -1;
143 }
144 record->acquired = true;
145
146 if (available_index.has_value()) {
147 records_[available_index.value()] = record;
148 index = available_index.value();
149 } else {
150 records_.push_back(record);
151 index = records_.size() - 1u;
152 }
153
154 auto texture = fml::MakeRefCounted<Texture>(record->texture);
155 texture->AssociateWithDartWrapper(texture_wrapper);
156 return static_cast<int>(index);
157}
158
159Dart_Handle Surface::CreateImage(const sk_sp<DlImage>& image) const {
160 if (!image) {
161 return Dart_Null();
162 }
163 auto canvas_image = CanvasImage::Create();
164 canvas_image->set_image(image);
165 return canvas_image->CreateOuterWrapping();
166}
167
168Dart_Handle Surface::PresentFrame(size_t texture_index,
170 if (texture_index >= records_.size()) {
171 return tonic::ToDart("SurfaceFrame does not belong to this GpuSurface.");
172 }
173
174 auto record = records_[texture_index];
175 if (!record || !record->acquired) {
176 return tonic::ToDart(
177 "SurfaceFrame has already been presented or discarded.");
178 }
179
180 record->producer_pending.store(true);
181 if (!command_buffer.AddCompletionCallback(
182 [record]([[maybe_unused]] impeller::CommandBuffer::Status status) {
183 record->producer_pending.store(false);
184 })) {
185 record->producer_pending.store(false);
186 return tonic::ToDart(
187 "SurfaceFrame.present must be called before submitting the command "
188 "buffer.");
189 }
190
191 record->acquired = false;
192 current_index_ = texture_index;
193 PruneTextureRecords();
194 // The presented image is read separately via GetCurrentImage, so avoid
195 // allocating an image wrapper here on every presented frame.
196 return Dart_Null();
197}
198
199void Surface::DiscardFrame(size_t texture_index) {
200 if (texture_index >= records_.size()) {
201 return;
202 }
203 auto record = records_[texture_index];
204 if (record) {
205 record->acquired = false;
206 }
207}
208
209Dart_Handle Surface::GetCurrentImage() const {
210 if (!current_index_.has_value() ||
211 current_index_.value() >= records_.size()) {
212 return Dart_Null();
213 }
214 auto record = records_[current_index_.value()];
215 if (!record) {
216 return Dart_Null();
217 }
218 return CreateImage(record->image);
219}
220
221std::optional<std::string> Surface::Resize(impeller::ISize size) {
222 for (const auto& record : records_) {
223 if (record && record->acquired) {
224 return "GpuSurface.resize cannot be called while a SurfaceFrame is "
225 "acquired.";
226 }
227 }
228 size_ = size;
229 PruneTextureRecords();
230 return std::nullopt;
231}
232
234 size_t count = 0;
235 for (const auto& record : records_) {
236 if (record) {
237 count++;
238 }
239 }
240 return count;
241}
242
243} // namespace gpu
244} // namespace flutter
245
246//----------------------------------------------------------------------------
247/// Exports
248///
249
251 [[maybe_unused]] Dart_Handle wrapper,
252 [[maybe_unused]] flutter::gpu::Context* gpu_context,
253 int width,
254 int height,
255 [[maybe_unused]] int format) {
256 if (width <= 0 || height <= 0) {
257 return tonic::ToDart("GpuSurface dimensions must be greater than zero.");
258 }
259
260#if !IMPELLER_SUPPORTS_RENDERING
261 return tonic::ToDart("GpuSurface requires Impeller rendering support.");
262#else
263 auto pixel_format = flutter::gpu::ToImpellerPixelFormat(format);
264 if (pixel_format == impeller::PixelFormat::kUnknown) {
265 return tonic::ToDart("Unsupported GpuSurface pixel format.");
266 }
267
268 auto res = fml::MakeRefCounted<flutter::gpu::Surface>(
269 gpu_context->GetContextShared(), impeller::ISize{width, height},
270 pixel_format);
271 res->AssociateWithDartWrapper(wrapper);
272 return Dart_Null();
273#endif
274}
275
277 Dart_Handle texture_wrapper) {
278 return wrapper->AcquireNextFrame(texture_wrapper);
279}
280
282 flutter::gpu::Surface* wrapper,
283 int texture_index,
285 if (texture_index < 0) {
286 return tonic::ToDart("Invalid SurfaceFrame texture index.");
287 }
288 return wrapper->PresentFrame(static_cast<size_t>(texture_index),
290}
291
293 int texture_index) {
294 if (texture_index < 0) {
295 return;
296 }
297 wrapper->DiscardFrame(static_cast<size_t>(texture_index));
298}
299
301 flutter::gpu::Surface* wrapper) {
302 return wrapper->GetCurrentImage();
303}
304
306 int width,
307 int height) {
308 if (width <= 0 || height <= 0) {
309 return tonic::ToDart("GpuSurface dimensions must be greater than zero.");
310 }
311 auto error = wrapper->Resize(impeller::ISize{width, height});
312 if (error.has_value()) {
313 return tonic::ToDart(error.value());
314 }
315 return Dart_Null();
316}
317
319 flutter::gpu::Surface* wrapper) {
320 return static_cast<int>(wrapper->GetBackingTextureCount());
321}
static fml::RefPtr< CanvasImage > Create()
Definition image.h:36
int AcquireNextFrame(Dart_Handle texture_wrapper)
Definition surface.cc:124
Dart_Handle GetCurrentImage() const
Definition surface.cc:209
size_t GetBackingTextureCount() const
Definition surface.cc:233
Dart_Handle PresentFrame(size_t texture_index, CommandBuffer &command_buffer)
Definition surface.cc:168
std::optional< std::string > Resize(impeller::ISize size)
Definition surface.cc:221
void DiscardFrame(size_t texture_index)
Definition surface.cc:199
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
#define IMPLEMENT_WRAPPERTYPEINFO(LibraryName, ClassName)
FlutterVulkanImage * image
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
#define FML_LOG(severity)
Definition logging.h:101
int InternalFlutterGpu_Surface_AcquireNextFrame(flutter::gpu::Surface *wrapper, Dart_Handle texture_wrapper)
Definition surface.cc:276
Dart_Handle InternalFlutterGpu_Surface_Initialize(Dart_Handle wrapper, flutter::gpu::Context *gpu_context, int width, int height, int format)
Definition surface.cc:250
Dart_Handle InternalFlutterGpu_Surface_PresentFrame(flutter::gpu::Surface *wrapper, int texture_index, flutter::gpu::CommandBuffer *command_buffer)
Definition surface.cc:281
Dart_Handle InternalFlutterGpu_Surface_Resize(flutter::gpu::Surface *wrapper, int width, int height)
Definition surface.cc:305
int InternalFlutterGpu_Surface_GetBackingTextureCount(flutter::gpu::Surface *wrapper)
Definition surface.cc:318
void InternalFlutterGpu_Surface_DiscardFrame(flutter::gpu::Surface *wrapper, int texture_index)
Definition surface.cc:292
Dart_Handle InternalFlutterGpu_Surface_GetCurrentImage(flutter::gpu::Surface *wrapper)
Definition surface.cc:300
FlTexture * texture
constexpr impeller::PixelFormat ToImpellerPixelFormat(FlutterGPUPixelFormat value)
Definition formats.h:104
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
Definition ref_ptr.h:261
Dart_Handle ToDart(const T &object)
std::shared_ptr< ContextGLES > context
std::shared_ptr< CommandBuffer > command_buffer
int32_t height
int32_t width
Type height
Definition size.h:29
Type width
Definition size.h:28
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
VkFormat format_