Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
software_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 <fuchsia/sysmem/cpp/fidl.h>
8#include <lib/async/default.h>
9#include <zircon/rights.h>
10#include <zircon/status.h>
11#include <zircon/types.h>
12
13#include <cmath>
14
15#include "flutter/fml/logging.h"
17
18#include "third_party/skia/include/core/SkColorSpace.h"
19#include "third_party/skia/include/core/SkImageInfo.h"
20#include "third_party/skia/include/core/SkSurface.h"
21
23
24namespace flutter_runner {
25
26namespace {
27
28constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
29
30uint32_t BytesPerRow(const fuchsia::sysmem2::SingleBufferSettings& settings,
31 uint32_t bytes_per_pixel,
32 uint32_t image_width) {
33 const uint32_t bytes_per_row_divisor =
34 settings.image_format_constraints().bytes_per_row_divisor();
35 const uint32_t min_bytes_per_row =
36 settings.image_format_constraints().min_bytes_per_row();
37 const uint32_t unrounded_bytes_per_row =
38 std::max(image_width * bytes_per_pixel, min_bytes_per_row);
39 const uint32_t roundup_bytes =
40 unrounded_bytes_per_row % bytes_per_row_divisor;
41
42 return unrounded_bytes_per_row + roundup_bytes;
43}
44
45} // namespace
46
48 fuchsia::sysmem2::AllocatorSyncPtr& sysmem_allocator,
49 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
50 const SkISize& size)
51 : wait_for_surface_read_finished_(this) {
52 FML_CHECK(flatland_allocator.is_bound());
53
54 if (!SetupSkiaSurface(sysmem_allocator, flatland_allocator, size)) {
55 FML_LOG(ERROR) << "Could not create render surface.";
56 return;
57 }
58
59 if (!CreateFences()) {
60 FML_LOG(ERROR) << "Could not create signal fences.";
61 return;
62 }
63
64 wait_for_surface_read_finished_.set_object(release_event_.get());
65 wait_for_surface_read_finished_.set_trigger(ZX_EVENT_SIGNALED);
66 Reset();
67
68 valid_ = true;
69}
70
72 release_image_callback_();
73 wait_for_surface_read_finished_.Cancel();
74 wait_for_surface_read_finished_.set_object(ZX_HANDLE_INVALID);
75}
76
78 return valid_;
79}
80
81SkISize SoftwareSurface::GetSize() const {
82 if (!valid_) {
83 return SkISize::Make(0, 0);
84 }
85
86 return SkISize::Make(sk_surface_->width(), sk_surface_->height());
87}
88
89bool SoftwareSurface::CreateFences() {
90 if (zx::event::create(0, &acquire_event_) != ZX_OK) {
91 FML_LOG(ERROR) << "Failed to create acquire event.";
92 return false;
93 }
94
95 if (zx::event::create(0, &release_event_) != ZX_OK) {
96 FML_LOG(ERROR) << "Failed to create release event.";
97 return false;
98 }
99
100 return true;
101}
102
103bool SoftwareSurface::SetupSkiaSurface(
104 fuchsia::sysmem2::AllocatorSyncPtr& sysmem_allocator,
105 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
106 const SkISize& size) {
107 if (size.isEmpty()) {
108 FML_LOG(ERROR) << "Failed to allocate surface, size is empty.";
109 return false;
110 }
111
112 // Allocate a "local" sysmem token to represent flutter's handle to the
113 // sysmem buffer.
114 fuchsia::sysmem2::BufferCollectionTokenSyncPtr local_token;
115 zx_status_t allocate_status = sysmem_allocator->AllocateSharedCollection(
116 std::move(fuchsia::sysmem2::AllocatorAllocateSharedCollectionRequest{}
117 .set_token_request(local_token.NewRequest())));
118 if (allocate_status != ZX_OK) {
119 FML_LOG(ERROR) << "Failed to allocate collection: "
120 << zx_status_get_string(allocate_status);
121 return false;
122 }
123
124 // Create a single Duplicate of the token and Sync it; the single duplicate
125 // token represents scenic's handle to the sysmem buffer.
126 fuchsia::sysmem2::BufferCollectionToken_DuplicateSync_Result duplicate_result;
127 zx_status_t duplicate_status = local_token->DuplicateSync(
128 std::move(fuchsia::sysmem2::BufferCollectionTokenDuplicateSyncRequest{}
129 .set_rights_attenuation_masks(
130 std::vector<zx_rights_t>{ZX_RIGHT_SAME_RIGHTS})),
131 &duplicate_result);
132 if (duplicate_status != ZX_OK) {
133 FML_LOG(ERROR) << "Failed to duplicate collection token: "
134 << zx_status_get_string(duplicate_status);
135 return false;
136 }
137 auto duplicate_tokens =
138 std::move(*duplicate_result.response().mutable_tokens());
139 if (duplicate_tokens.size() != 1u) {
140 FML_LOG(ERROR) << "Failed to duplicate collection token: Incorrect number "
141 "of tokens returned.";
142 return false;
143 }
144 auto scenic_token = std::move(duplicate_tokens[0]);
145
146 // Register the sysmem token with flatland.
147 //
148 // This binds the sysmem token to a composition token, which is used later
149 // to associate the rendering surface with a specific flatland Image.
150 fuchsia::ui::composition::BufferCollectionExportToken export_token;
151 zx_status_t token_create_status =
152 zx::eventpair::create(0, &export_token.value, &import_token_.value);
153 if (token_create_status != ZX_OK) {
154 FML_LOG(ERROR) << "Failed to create flatland export token: "
155 << zx_status_get_string(token_create_status);
156 return false;
157 }
158
159 fuchsia::ui::composition::RegisterBufferCollectionArgs args;
160 args.set_export_token(std::move(export_token));
161 args.set_buffer_collection_token2(std::move(scenic_token));
162 args.set_usage(
163 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
164 flatland_allocator->RegisterBufferCollection(
165 std::move(args),
166 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
167 result) {
168 if (result.is_err()) {
169 FML_LOG(ERROR)
170 << "RegisterBufferCollection call to Scenic Allocator failed.";
171 }
172 });
173
174 // Acquire flutter's local handle to the sysmem buffer.
175 fuchsia::sysmem2::BufferCollectionSyncPtr buffer_collection;
176 zx_status_t bind_status = sysmem_allocator->BindSharedCollection(std::move(
177 fuchsia::sysmem2::AllocatorBindSharedCollectionRequest{}
178 .set_token(std::move(local_token))
179 .set_buffer_collection_request(buffer_collection.NewRequest())));
180 if (bind_status != ZX_OK) {
181 FML_LOG(ERROR) << "Failed to bind collection token: "
182 << zx_status_get_string(bind_status);
183 return false;
184 }
185
186 // Set flutter's constraints on the sysmem buffer. Software rendering only
187 // requires CPU access to the surface and a basic R8G8B8A8 pixel format.
188 fuchsia::sysmem2::BufferCollectionConstraints constraints;
189 constraints.set_min_buffer_count(1);
190 constraints.mutable_usage()->set_cpu(fuchsia::sysmem2::CPU_USAGE_WRITE |
191 fuchsia::sysmem2::CPU_USAGE_WRITE_OFTEN);
192 auto& bmc = *constraints.mutable_buffer_memory_constraints();
193 bmc.set_physically_contiguous_required(false);
194 bmc.set_secure_required(false);
195 bmc.set_ram_domain_supported(true);
196 bmc.set_cpu_domain_supported(true);
197 bmc.set_inaccessible_domain_supported(false);
198 auto& ifc = constraints.mutable_image_format_constraints()->emplace_back();
199 ifc.set_min_size(fuchsia::math::SizeU{static_cast<uint32_t>(size.fWidth),
200 static_cast<uint32_t>(size.fHeight)});
201 ifc.set_min_bytes_per_row(static_cast<uint32_t>(size.fWidth) * 4);
202 ifc.set_pixel_format(fuchsia::images2::PixelFormat::R8G8B8A8);
203 ifc.mutable_color_spaces()->emplace_back(fuchsia::images2::ColorSpace::SRGB);
204 ifc.set_pixel_format_modifier(fuchsia::images2::PixelFormatModifier::LINEAR);
205 zx_status_t set_constraints_status = buffer_collection->SetConstraints(
206 std::move(fuchsia::sysmem2::BufferCollectionSetConstraintsRequest{}
207 .set_constraints(std::move(constraints))));
208 if (set_constraints_status != ZX_OK) {
209 FML_LOG(ERROR) << "Failed to set constraints: "
210 << zx_status_get_string(set_constraints_status);
211 return false;
212 }
213
214 // Wait for sysmem to allocate, now that constraints are set.
215 fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result
216 wait_result;
217 zx_status_t wait_for_allocated_status =
218 buffer_collection->WaitForAllBuffersAllocated(&wait_result);
219 if (wait_for_allocated_status != ZX_OK) {
220 FML_LOG(ERROR) << "Failed to wait for allocate: "
221 << zx_status_get_string(wait_for_allocated_status);
222 return false;
223 }
224 if (!wait_result.is_response()) {
225 if (wait_result.is_framework_err()) {
226 FML_LOG(ERROR) << "Failed to allocate (framework_err): "
227 << fidl::ToUnderlying(wait_result.framework_err());
228 } else {
229 FML_DCHECK(wait_result.is_err());
230 FML_LOG(ERROR) << "Failed to allocate (err): "
231 << static_cast<uint32_t>(wait_result.err());
232 }
233 return false;
234 }
235 auto buffer_collection_info =
236 std::move(*wait_result.response().mutable_buffer_collection_info());
237
238 // Cache the allocated surface VMO and metadata.
239 FML_CHECK(buffer_collection_info.settings().buffer_settings().size_bytes() !=
240 0);
241 FML_CHECK(buffer_collection_info.buffers()[0].vmo().is_valid());
242 surface_vmo_ =
243 std::move(*buffer_collection_info.mutable_buffers()->at(0).mutable_vmo());
244 surface_size_bytes_ =
245 buffer_collection_info.settings().buffer_settings().size_bytes();
246 if (buffer_collection_info.settings().buffer_settings().coherency_domain() ==
247 fuchsia::sysmem2::CoherencyDomain::RAM) {
248 // RAM coherency domain requires a cache clean when writes are finished.
249 needs_cache_clean_ = true;
250 }
251
252 // Map the allocated buffer to the CPU.
253 uint8_t* vmo_base = nullptr;
254 zx_status_t buffer_map_status = zx::vmar::root_self()->map(
255 ZX_VM_PERM_WRITE | ZX_VM_PERM_READ, 0, surface_vmo_, 0,
256 surface_size_bytes_, reinterpret_cast<uintptr_t*>(&vmo_base));
257 if (buffer_map_status != ZX_OK) {
258 FML_LOG(ERROR) << "Failed to map buffer memory: "
259 << zx_status_get_string(buffer_map_status);
260 return false;
261 }
262
263 // Now that the buffer is CPU-readable, it's safe to discard flutter's
264 // connection to sysmem.
265 zx_status_t close_status = buffer_collection->Release();
266 if (close_status != ZX_OK) {
267 FML_LOG(ERROR) << "Failed to close buffer: "
268 << zx_status_get_string(close_status);
269 return false;
270 }
271
272 // Wrap the buffer in a software-rendered Skia surface.
273 const uint64_t vmo_offset =
274 buffer_collection_info.buffers()[0].vmo_usable_start();
275 const size_t vmo_stride =
276 BytesPerRow(buffer_collection_info.settings(), 4u, size.width());
277 SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry);
278 sk_surface_ = SkSurfaces::WrapPixels(
279 SkImageInfo::Make(size, kSkiaColorType, kPremul_SkAlphaType,
280 SkColorSpace::MakeSRGB()),
281 vmo_base + vmo_offset, vmo_stride, &sk_surface_props);
282 if (!sk_surface_ || sk_surface_->getCanvas() == nullptr) {
283 FML_LOG(ERROR) << "SkSurfaces::WrapPixels failed.";
284 return false;
285 }
286
287 return true;
288}
289
290void SoftwareSurface::SetImageId(uint32_t image_id) {
291 FML_CHECK(image_id_ == 0);
292 image_id_ = image_id;
293}
294
296 return image_id_;
297}
298
299sk_sp<SkSurface> SoftwareSurface::GetSkiaSurface() const {
300 return valid_ ? sk_surface_ : nullptr;
301}
302
303fuchsia::ui::composition::BufferCollectionImportToken
305 fuchsia::ui::composition::BufferCollectionImportToken import_dup;
306 import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
307 return import_dup;
308}
309
311 zx::event fence;
312 acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
313 return fence;
314}
315
317 zx::event fence;
318 release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
319 return fence;
320}
322 ReleaseImageCallback release_image_callback) {
323 release_image_callback_ = release_image_callback;
324}
325
327 return ++age_;
328}
329
331 age_ = 0;
332 return true;
333}
334
336 const std::function<void(void)>& on_surface_read_finished) {
337 FML_CHECK(on_surface_read_finished);
338
339 if (!valid_) {
340 on_surface_read_finished();
341 return;
342 }
343
344 FML_CHECK(surface_read_finished_callback_ == nullptr)
345 << "Attempted to signal a write on the surface when the "
346 "previous write has not yet been acknowledged by the "
347 "compositor.";
348 surface_read_finished_callback_ = on_surface_read_finished;
349
350 // Sysmem *may* require the cache to be cleared after writes to the surface
351 // are complete.
352 if (needs_cache_clean_) {
353 surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_,
354 /*buffer*/ nullptr,
355 /*buffer_size*/ 0);
356 }
357
358 // Inform scenic that flutter is finished writing to the surface.
359 zx_status_t signal_status = acquire_event_.signal(0u, ZX_EVENT_SIGNALED);
360 if (signal_status != ZX_OK) {
361 FML_LOG(ERROR) << "Failed to signal acquire event; "
362 << zx_status_get_string(signal_status);
363 }
364}
365
366void SoftwareSurface::Reset() {
367 if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
368 release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
369 valid_ = false;
370 FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid.";
371 }
372
373 wait_for_surface_read_finished_.Begin(async_get_default_dispatcher());
374
375 // It is safe for the caller to collect the surface in the callback.
376 auto callback = surface_read_finished_callback_;
377 surface_read_finished_callback_ = nullptr;
378 if (callback) {
379 callback();
380 }
381}
382
383void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
384 async::WaitBase* wait,
385 zx_status_t status,
386 const zx_packet_signal_t* signal) {
387 if (status != ZX_OK) {
388 return;
389 }
390 FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
391
392 Reset();
393}
394
395} // namespace flutter_runner
bool FlushSessionAcquireAndReleaseEvents() override
fuchsia::ui::composition::BufferCollectionImportToken GetBufferCollectionImportToken() override
void SetReleaseImageCallback(ReleaseImageCallback release_image_callback) override
sk_sp< SkSurface > GetSkiaSurface() const override
void SetImageId(uint32_t image_id) override
SkISize GetSize() const override
SoftwareSurface(fuchsia::sysmem2::AllocatorSyncPtr &sysmem_allocator, fuchsia::ui::composition::AllocatorPtr &flatland_allocator, const SkISize &size)
void SignalWritesFinished(const std::function< void(void)> &on_surface_read_finished) override
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
std::function< void()> ReleaseImageCallback
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