Flutter Engine
The Flutter Engine
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
5#include "software_surface.h"
6
7#include <lib/async/default.h>
8#include <zircon/rights.h>
9#include <zircon/status.h>
10#include <zircon/types.h>
11
12#include <cmath>
13
14#include "flutter/fml/logging.h"
15#include "flutter/fml/trace_event.h"
16#include "fuchsia/sysmem/cpp/fidl.h"
18
22
23#include "../runtime/dart/utils/inlines.h"
24
25namespace flutter_runner {
26
27namespace {
28
29constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
30
31uint32_t BytesPerRow(const fuchsia::sysmem::SingleBufferSettings& settings,
32 uint32_t bytes_per_pixel,
33 uint32_t image_width) {
34 const uint32_t bytes_per_row_divisor =
35 settings.image_format_constraints.bytes_per_row_divisor;
36 const uint32_t min_bytes_per_row =
37 settings.image_format_constraints.min_bytes_per_row;
38 const uint32_t unrounded_bytes_per_row =
39 std::max(image_width * bytes_per_pixel, min_bytes_per_row);
40 const uint32_t roundup_bytes =
41 unrounded_bytes_per_row % bytes_per_row_divisor;
42
43 return unrounded_bytes_per_row + roundup_bytes;
44}
45
46} // namespace
47
49 fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
50 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
51 const SkISize& size)
52 : wait_for_surface_read_finished_(this) {
53 FML_CHECK(flatland_allocator.is_bound());
54
55 if (!SetupSkiaSurface(sysmem_allocator, flatland_allocator, size)) {
56 FML_LOG(ERROR) << "Could not create render surface.";
57 return;
58 }
59
60 if (!CreateFences()) {
61 FML_LOG(ERROR) << "Could not create signal fences.";
62 return;
63 }
64
65 wait_for_surface_read_finished_.set_object(release_event_.get());
66 wait_for_surface_read_finished_.set_trigger(ZX_EVENT_SIGNALED);
67 Reset();
68
69 valid_ = true;
70}
71
73 release_image_callback_();
74 wait_for_surface_read_finished_.Cancel();
75 wait_for_surface_read_finished_.set_object(ZX_HANDLE_INVALID);
76}
77
79 return valid_;
80}
81
83 if (!valid_) {
84 return SkISize::Make(0, 0);
85 }
86
87 return SkISize::Make(sk_surface_->width(), sk_surface_->height());
88}
89
90bool SoftwareSurface::CreateFences() {
91 if (zx::event::create(0, &acquire_event_) != ZX_OK) {
92 FML_LOG(ERROR) << "Failed to create acquire event.";
93 return false;
94 }
95
96 if (zx::event::create(0, &release_event_) != ZX_OK) {
97 FML_LOG(ERROR) << "Failed to create release event.";
98 return false;
99 }
100
101 return true;
102}
103
104bool SoftwareSurface::SetupSkiaSurface(
105 fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
106 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
107 const SkISize& size) {
108 if (size.isEmpty()) {
109 FML_LOG(ERROR) << "Failed to allocate surface, size is empty.";
110 return false;
111 }
112
113 // Allocate a "local" sysmem token to represent flutter's handle to the
114 // sysmem buffer.
115 fuchsia::sysmem::BufferCollectionTokenSyncPtr local_token;
116 zx_status_t allocate_status =
117 sysmem_allocator->AllocateSharedCollection(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 std::vector<fuchsia::sysmem::BufferCollectionTokenHandle> duplicate_tokens;
127 zx_status_t duplicate_status = local_token->DuplicateSync(
128 std::vector<zx_rights_t>{ZX_RIGHT_SAME_RIGHTS}, &duplicate_tokens);
129 if (duplicate_status != ZX_OK) {
130 FML_LOG(ERROR) << "Failed to duplicate collection token: "
131 << zx_status_get_string(duplicate_status);
132 return false;
133 }
134 if (duplicate_tokens.size() != 1u) {
135 FML_LOG(ERROR) << "Failed to duplicate collection token: Incorrect number "
136 "of tokens returned.";
137 return false;
138 }
139 auto scenic_token = std::move(duplicate_tokens[0]);
140
141 // Register the sysmem token with flatland.
142 //
143 // This binds the sysmem token to a composition token, which is used later
144 // to associate the rendering surface with a specific flatland Image.
145 fuchsia::ui::composition::BufferCollectionExportToken export_token;
146 zx_status_t token_create_status =
147 zx::eventpair::create(0, &export_token.value, &import_token_.value);
148 if (token_create_status != ZX_OK) {
149 FML_LOG(ERROR) << "Failed to create flatland export token: "
150 << zx_status_get_string(token_create_status);
151 return false;
152 }
153
154 fuchsia::ui::composition::RegisterBufferCollectionArgs args;
155 args.set_export_token(std::move(export_token));
156 args.set_buffer_collection_token(std::move(scenic_token));
157 args.set_usage(
158 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
159 flatland_allocator->RegisterBufferCollection(
160 std::move(args),
161 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
162 result) {
163 if (result.is_err()) {
164 FML_LOG(ERROR)
165 << "RegisterBufferCollection call to Scenic Allocator failed.";
166 }
167 });
168
169 // Acquire flutter's local handle to the sysmem buffer.
170 fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
171 zx_status_t bind_status = sysmem_allocator->BindSharedCollection(
172 std::move(local_token), buffer_collection.NewRequest());
173 if (bind_status != ZX_OK) {
174 FML_LOG(ERROR) << "Failed to bind collection token: "
175 << zx_status_get_string(bind_status);
176 return false;
177 }
178
179 // Set flutter's constraints on the sysmem buffer. Software rendering only
180 // requires CPU access to the surface and a basic R8G8B8A8 pixel format.
181 fuchsia::sysmem::BufferCollectionConstraints constraints;
182 constraints.min_buffer_count = 1;
183 constraints.usage.cpu =
184 fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften;
185 constraints.has_buffer_memory_constraints = true;
186 constraints.buffer_memory_constraints.physically_contiguous_required = false;
187 constraints.buffer_memory_constraints.secure_required = false;
188 constraints.buffer_memory_constraints.ram_domain_supported = true;
189 constraints.buffer_memory_constraints.cpu_domain_supported = true;
190 constraints.buffer_memory_constraints.inaccessible_domain_supported = false;
191 constraints.image_format_constraints_count = 1;
192 fuchsia::sysmem::ImageFormatConstraints& image_constraints =
193 constraints.image_format_constraints[0];
194 image_constraints = fuchsia::sysmem::ImageFormatConstraints();
195 image_constraints.min_coded_width = static_cast<uint32_t>(size.fWidth);
196 image_constraints.min_coded_height = static_cast<uint32_t>(size.fHeight);
197 image_constraints.min_bytes_per_row = static_cast<uint32_t>(size.fWidth) * 4;
198 image_constraints.pixel_format.type =
199 fuchsia::sysmem::PixelFormatType::R8G8B8A8;
200 image_constraints.color_spaces_count = 1;
201 image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB;
202 image_constraints.pixel_format.has_format_modifier = true;
203 image_constraints.pixel_format.format_modifier.value =
204 fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
205 zx_status_t set_constraints_status =
206 buffer_collection->SetConstraints(true, constraints);
207 if (set_constraints_status != ZX_OK) {
208 FML_LOG(ERROR) << "Failed to set constraints: "
209 << zx_status_get_string(set_constraints_status);
210 return false;
211 }
212
213 // Wait for sysmem to allocate, now that constraints are set.
214 fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info;
215 zx_status_t allocation_status = ZX_OK;
216 zx_status_t wait_for_allocated_status =
217 buffer_collection->WaitForBuffersAllocated(&allocation_status,
218 &buffer_collection_info);
219 if (allocation_status != ZX_OK) {
220 FML_LOG(ERROR) << "Failed to allocate: "
221 << zx_status_get_string(allocation_status);
222 return false;
223 }
224 if (wait_for_allocated_status != ZX_OK) {
225 FML_LOG(ERROR) << "Failed to wait for allocate: "
226 << zx_status_get_string(wait_for_allocated_status);
227 return false;
228 }
229
230 // Cache the allocated surface VMO and metadata.
231 FML_CHECK(buffer_collection_info.settings.buffer_settings.size_bytes != 0);
232 FML_CHECK(buffer_collection_info.buffers[0].vmo != ZX_HANDLE_INVALID);
233 surface_vmo_ = std::move(buffer_collection_info.buffers[0].vmo);
234 surface_size_bytes_ =
235 buffer_collection_info.settings.buffer_settings.size_bytes;
236 if (buffer_collection_info.settings.buffer_settings.coherency_domain ==
237 fuchsia::sysmem::CoherencyDomain::RAM) {
238 // RAM coherency domain requires a cache clean when writes are finished.
239 needs_cache_clean_ = true;
240 }
241
242 // Map the allocated buffer to the CPU.
243 uint8_t* vmo_base = nullptr;
244 zx_status_t buffer_map_status = zx::vmar::root_self()->map(
245 ZX_VM_PERM_WRITE | ZX_VM_PERM_READ, 0, surface_vmo_, 0,
246 surface_size_bytes_, reinterpret_cast<uintptr_t*>(&vmo_base));
247 if (buffer_map_status != ZX_OK) {
248 FML_LOG(ERROR) << "Failed to map buffer memory: "
249 << zx_status_get_string(buffer_map_status);
250 return false;
251 }
252
253 // Now that the buffer is CPU-readable, it's safe to discard flutter's
254 // connection to sysmem.
255 zx_status_t close_status = buffer_collection->Close();
256 if (close_status != ZX_OK) {
257 FML_LOG(ERROR) << "Failed to close buffer: "
258 << zx_status_get_string(close_status);
259 return false;
260 }
261
262 // Wrap the buffer in a software-rendered Skia surface.
263 const uint64_t vmo_offset =
264 buffer_collection_info.buffers[0].vmo_usable_start;
265 const size_t vmo_stride =
266 BytesPerRow(buffer_collection_info.settings, 4u, size.width());
267 SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry);
268 sk_surface_ = SkSurfaces::WrapPixels(
269 SkImageInfo::Make(size, kSkiaColorType, kPremul_SkAlphaType,
271 vmo_base + vmo_offset, vmo_stride, &sk_surface_props);
272 if (!sk_surface_ || sk_surface_->getCanvas() == nullptr) {
273 FML_LOG(ERROR) << "SkSurfaces::WrapPixels failed.";
274 return false;
275 }
276
277 return true;
278}
279
280void SoftwareSurface::SetImageId(uint32_t image_id) {
281 FML_CHECK(image_id_ == 0);
282 image_id_ = image_id;
283}
284
286 return image_id_;
287}
288
290 return valid_ ? sk_surface_ : nullptr;
291}
292
293fuchsia::ui::composition::BufferCollectionImportToken
295 fuchsia::ui::composition::BufferCollectionImportToken import_dup;
296 import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
297 return import_dup;
298}
299
301 zx::event fence;
302 acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
303 return fence;
304}
305
307 zx::event fence;
308 release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
309 return fence;
310}
312 ReleaseImageCallback release_image_callback) {
313 release_image_callback_ = release_image_callback;
314}
315
317 return ++age_;
318}
319
321 age_ = 0;
322 return true;
323}
324
326 const std::function<void(void)>& on_surface_read_finished) {
327 FML_CHECK(on_surface_read_finished);
328
329 if (!valid_) {
330 on_surface_read_finished();
331 return;
332 }
333
334 FML_CHECK(surface_read_finished_callback_ == nullptr)
335 << "Attempted to signal a write on the surface when the "
336 "previous write has not yet been acknowledged by the "
337 "compositor.";
338 surface_read_finished_callback_ = on_surface_read_finished;
339
340 // Sysmem *may* require the cache to be cleared after writes to the surface
341 // are complete.
342 if (needs_cache_clean_) {
343 surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_,
344 /*buffer*/ nullptr,
345 /*buffer_size*/ 0);
346 }
347
348 // Inform scenic that flutter is finished writing to the surface.
349 zx_status_t signal_status = acquire_event_.signal(0u, ZX_EVENT_SIGNALED);
350 if (signal_status != ZX_OK) {
351 FML_LOG(ERROR) << "Failed to signal acquire event; "
352 << zx_status_get_string(signal_status);
353 }
354}
355
356void SoftwareSurface::Reset() {
357 if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
358 release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
359 valid_ = false;
360 FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid.";
361 }
362
363 wait_for_surface_read_finished_.Begin(async_get_default_dispatcher());
364
365 // It is safe for the caller to collect the surface in the callback.
366 auto callback = surface_read_finished_callback_;
367 surface_read_finished_callback_ = nullptr;
368 if (callback) {
369 callback();
370 }
371}
372
373void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
374 async::WaitBase* wait,
375 zx_status_t status,
376 const zx_packet_signal_t* signal) {
377 if (status != ZX_OK) {
378 return;
379 }
380 FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
381
382 Reset();
383}
384
385} // namespace flutter_runner
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
SkColorType
Definition SkColorType.h:19
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kUnknown_SkPixelGeometry
static sk_sp< SkColorSpace > MakeSRGB()
SkCanvas * getCanvas()
Definition SkSurface.cpp:82
int width() const
Definition SkSurface.h:178
int height() const
Definition SkSurface.h:184
bool FlushSessionAcquireAndReleaseEvents() override
fuchsia::ui::composition::BufferCollectionImportToken GetBufferCollectionImportToken() override
void SetReleaseImageCallback(ReleaseImageCallback release_image_callback) override
SoftwareSurface(fuchsia::sysmem::AllocatorSyncPtr &sysmem_allocator, fuchsia::ui::composition::AllocatorPtr &flatland_allocator, const SkISize &size)
sk_sp< SkSurface > GetSkiaSurface() const override
void SetImageId(uint32_t image_id) override
SkISize GetSize() const override
void SignalWritesFinished(const std::function< void(void)> &on_surface_read_finished) override
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
#define FML_LOG(severity)
Definition logging.h:82
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
SK_API sk_sp< SkSurface > WrapPixels(const SkImageInfo &imageInfo, void *pixels, size_t rowBytes, const SkSurfaceProps *surfaceProps=nullptr)
std::function< void()> ReleaseImageCallback
it will be possible to load the file into Perfetto s trace viewer 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
Definition switches.h:259
static size_t bytes_per_pixel(skcms_PixelFormat fmt)
Definition skcms.cc:2449
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
#define ERROR(message)