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"
16#include "fuchsia/sysmem/cpp/fidl.h"
17#include "include/core/SkImageInfo.h"
18
19#include "third_party/skia/include/core/SkColorSpace.h"
20#include "third_party/skia/include/core/SkImageInfo.h"
21#include "third_party/skia/include/core/SkSurface.h"
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::sysmem2::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::sysmem2::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
82SkISize SoftwareSurface::GetSize() const {
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::sysmem2::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::sysmem2::BufferCollectionTokenSyncPtr local_token;
116 zx_status_t allocate_status = sysmem_allocator->AllocateSharedCollection(
117 std::move(fuchsia::sysmem2::AllocatorAllocateSharedCollectionRequest{}
118 .set_token_request(local_token.NewRequest())));
119 if (allocate_status != ZX_OK) {
120 FML_LOG(ERROR) << "Failed to allocate collection: "
121 << zx_status_get_string(allocate_status);
122 return false;
123 }
124
125 // Create a single Duplicate of the token and Sync it; the single duplicate
126 // token represents scenic's handle to the sysmem buffer.
127 fuchsia::sysmem2::BufferCollectionToken_DuplicateSync_Result duplicate_result;
128 zx_status_t duplicate_status = local_token->DuplicateSync(
129 std::move(fuchsia::sysmem2::BufferCollectionTokenDuplicateSyncRequest{}
130 .set_rights_attenuation_masks(
131 std::vector<zx_rights_t>{ZX_RIGHT_SAME_RIGHTS})),
132 &duplicate_result);
133 if (duplicate_status != ZX_OK) {
134 FML_LOG(ERROR) << "Failed to duplicate collection token: "
135 << zx_status_get_string(duplicate_status);
136 return false;
137 }
138 auto duplicate_tokens =
139 std::move(*duplicate_result.response().mutable_tokens());
140 if (duplicate_tokens.size() != 1u) {
141 FML_LOG(ERROR) << "Failed to duplicate collection token: Incorrect number "
142 "of tokens returned.";
143 return false;
144 }
145 auto scenic_token = std::move(duplicate_tokens[0]);
146
147 // Register the sysmem token with flatland.
148 //
149 // This binds the sysmem token to a composition token, which is used later
150 // to associate the rendering surface with a specific flatland Image.
151 fuchsia::ui::composition::BufferCollectionExportToken export_token;
152 zx_status_t token_create_status =
153 zx::eventpair::create(0, &export_token.value, &import_token_.value);
154 if (token_create_status != ZX_OK) {
155 FML_LOG(ERROR) << "Failed to create flatland export token: "
156 << zx_status_get_string(token_create_status);
157 return false;
158 }
159
160 fuchsia::ui::composition::RegisterBufferCollectionArgs args;
161 args.set_export_token(std::move(export_token));
162 args.set_buffer_collection_token2(std::move(scenic_token));
163 args.set_usage(
164 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
165 flatland_allocator->RegisterBufferCollection(
166 std::move(args),
167 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
168 result) {
169 if (result.is_err()) {
170 FML_LOG(ERROR)
171 << "RegisterBufferCollection call to Scenic Allocator failed.";
172 }
173 });
174
175 // Acquire flutter's local handle to the sysmem buffer.
176 fuchsia::sysmem2::BufferCollectionSyncPtr buffer_collection;
177 zx_status_t bind_status = sysmem_allocator->BindSharedCollection(std::move(
178 fuchsia::sysmem2::AllocatorBindSharedCollectionRequest{}
179 .set_token(std::move(local_token))
180 .set_buffer_collection_request(buffer_collection.NewRequest())));
181 if (bind_status != ZX_OK) {
182 FML_LOG(ERROR) << "Failed to bind collection token: "
183 << zx_status_get_string(bind_status);
184 return false;
185 }
186
187 // Set flutter's constraints on the sysmem buffer. Software rendering only
188 // requires CPU access to the surface and a basic R8G8B8A8 pixel format.
189 fuchsia::sysmem2::BufferCollectionConstraints constraints;
190 constraints.set_min_buffer_count(1);
191 constraints.mutable_usage()->set_cpu(fuchsia::sysmem2::CPU_USAGE_WRITE |
192 fuchsia::sysmem2::CPU_USAGE_WRITE_OFTEN);
193 auto& bmc = *constraints.mutable_buffer_memory_constraints();
194 bmc.set_physically_contiguous_required(false);
195 bmc.set_secure_required(false);
196 bmc.set_ram_domain_supported(true);
197 bmc.set_cpu_domain_supported(true);
198 bmc.set_inaccessible_domain_supported(false);
199 auto& ifc = constraints.mutable_image_format_constraints()->emplace_back();
200 ifc.set_min_size(fuchsia::math::SizeU{static_cast<uint32_t>(size.fWidth),
201 static_cast<uint32_t>(size.fHeight)});
202 ifc.set_min_bytes_per_row(static_cast<uint32_t>(size.fWidth) * 4);
203 ifc.set_pixel_format(fuchsia::images2::PixelFormat::R8G8B8A8);
204 ifc.mutable_color_spaces()->emplace_back(fuchsia::images2::ColorSpace::SRGB);
205 ifc.set_pixel_format_modifier(fuchsia::images2::PixelFormatModifier::LINEAR);
206 zx_status_t set_constraints_status = buffer_collection->SetConstraints(
207 std::move(fuchsia::sysmem2::BufferCollectionSetConstraintsRequest{}
208 .set_constraints(std::move(constraints))));
209 if (set_constraints_status != ZX_OK) {
210 FML_LOG(ERROR) << "Failed to set constraints: "
211 << zx_status_get_string(set_constraints_status);
212 return false;
213 }
214
215 // Wait for sysmem to allocate, now that constraints are set.
216 fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result
217 wait_result;
218 zx_status_t wait_for_allocated_status =
219 buffer_collection->WaitForAllBuffersAllocated(&wait_result);
220 if (wait_for_allocated_status != ZX_OK) {
221 FML_LOG(ERROR) << "Failed to wait for allocate: "
222 << zx_status_get_string(wait_for_allocated_status);
223 return false;
224 }
225 if (!wait_result.is_response()) {
226 if (wait_result.is_framework_err()) {
227 FML_LOG(ERROR) << "Failed to allocate (framework_err): "
228 << fidl::ToUnderlying(wait_result.framework_err());
229 } else {
230 FML_DCHECK(wait_result.is_err());
231 FML_LOG(ERROR) << "Failed to allocate (err): "
232 << static_cast<uint32_t>(wait_result.err());
233 }
234 return false;
235 }
236 auto buffer_collection_info =
237 std::move(*wait_result.response().mutable_buffer_collection_info());
238
239 // Cache the allocated surface VMO and metadata.
240 FML_CHECK(buffer_collection_info.settings().buffer_settings().size_bytes() !=
241 0);
242 FML_CHECK(buffer_collection_info.buffers()[0].vmo().is_valid());
243 surface_vmo_ =
244 std::move(*buffer_collection_info.mutable_buffers()->at(0).mutable_vmo());
245 surface_size_bytes_ =
246 buffer_collection_info.settings().buffer_settings().size_bytes();
247 if (buffer_collection_info.settings().buffer_settings().coherency_domain() ==
248 fuchsia::sysmem2::CoherencyDomain::RAM) {
249 // RAM coherency domain requires a cache clean when writes are finished.
250 needs_cache_clean_ = true;
251 }
252
253 // Map the allocated buffer to the CPU.
254 uint8_t* vmo_base = nullptr;
255 zx_status_t buffer_map_status = zx::vmar::root_self()->map(
256 ZX_VM_PERM_WRITE | ZX_VM_PERM_READ, 0, surface_vmo_, 0,
257 surface_size_bytes_, reinterpret_cast<uintptr_t*>(&vmo_base));
258 if (buffer_map_status != ZX_OK) {
259 FML_LOG(ERROR) << "Failed to map buffer memory: "
260 << zx_status_get_string(buffer_map_status);
261 return false;
262 }
263
264 // Now that the buffer is CPU-readable, it's safe to discard flutter's
265 // connection to sysmem.
266 zx_status_t close_status = buffer_collection->Release();
267 if (close_status != ZX_OK) {
268 FML_LOG(ERROR) << "Failed to close buffer: "
269 << zx_status_get_string(close_status);
270 return false;
271 }
272
273 // Wrap the buffer in a software-rendered Skia surface.
274 const uint64_t vmo_offset =
275 buffer_collection_info.buffers()[0].vmo_usable_start();
276 const size_t vmo_stride =
277 BytesPerRow(buffer_collection_info.settings(), 4u, size.width());
278 SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry);
279 sk_surface_ = SkSurfaces::WrapPixels(
280 SkImageInfo::Make(size, kSkiaColorType, kPremul_SkAlphaType,
281 SkColorSpace::MakeSRGB()),
282 vmo_base + vmo_offset, vmo_stride, &sk_surface_props);
283 if (!sk_surface_ || sk_surface_->getCanvas() == nullptr) {
284 FML_LOG(ERROR) << "SkSurfaces::WrapPixels failed.";
285 return false;
286 }
287
288 return true;
289}
290
291void SoftwareSurface::SetImageId(uint32_t image_id) {
292 FML_CHECK(image_id_ == 0);
293 image_id_ = image_id;
294}
295
297 return image_id_;
298}
299
300sk_sp<SkSurface> SoftwareSurface::GetSkiaSurface() const {
301 return valid_ ? sk_surface_ : nullptr;
302}
303
304fuchsia::ui::composition::BufferCollectionImportToken
306 fuchsia::ui::composition::BufferCollectionImportToken import_dup;
307 import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
308 return import_dup;
309}
310
312 zx::event fence;
313 acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
314 return fence;
315}
316
318 zx::event fence;
319 release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
320 return fence;
321}
323 ReleaseImageCallback release_image_callback) {
324 release_image_callback_ = release_image_callback;
325}
326
328 return ++age_;
329}
330
332 age_ = 0;
333 return true;
334}
335
337 const std::function<void(void)>& on_surface_read_finished) {
338 FML_CHECK(on_surface_read_finished);
339
340 if (!valid_) {
341 on_surface_read_finished();
342 return;
343 }
344
345 FML_CHECK(surface_read_finished_callback_ == nullptr)
346 << "Attempted to signal a write on the surface when the "
347 "previous write has not yet been acknowledged by the "
348 "compositor.";
349 surface_read_finished_callback_ = on_surface_read_finished;
350
351 // Sysmem *may* require the cache to be cleared after writes to the surface
352 // are complete.
353 if (needs_cache_clean_) {
354 surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_,
355 /*buffer*/ nullptr,
356 /*buffer_size*/ 0);
357 }
358
359 // Inform scenic that flutter is finished writing to the surface.
360 zx_status_t signal_status = acquire_event_.signal(0u, ZX_EVENT_SIGNALED);
361 if (signal_status != ZX_OK) {
362 FML_LOG(ERROR) << "Failed to signal acquire event; "
363 << zx_status_get_string(signal_status);
364 }
365}
366
367void SoftwareSurface::Reset() {
368 if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
369 release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
370 valid_ = false;
371 FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid.";
372 }
373
374 wait_for_surface_read_finished_.Begin(async_get_default_dispatcher());
375
376 // It is safe for the caller to collect the surface in the callback.
377 auto callback = surface_read_finished_callback_;
378 surface_read_finished_callback_ = nullptr;
379 if (callback) {
380 callback();
381 }
382}
383
384void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
385 async::WaitBase* wait,
386 zx_status_t status,
387 const zx_packet_signal_t* signal) {
388 if (status != ZX_OK) {
389 return;
390 }
391 FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
392
393 Reset();
394}
395
396} // 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