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>
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"
28constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
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;
42 return unrounded_bytes_per_row + roundup_bytes;
48 fuchsia::sysmem2::AllocatorSyncPtr& sysmem_allocator,
49 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
51 : wait_for_surface_read_finished_(this) {
54 if (!SetupSkiaSurface(sysmem_allocator, flatland_allocator, size)) {
55 FML_LOG(ERROR) <<
"Could not create render surface.";
59 if (!CreateFences()) {
60 FML_LOG(ERROR) <<
"Could not create signal fences.";
64 wait_for_surface_read_finished_.set_object(release_event_.get());
65 wait_for_surface_read_finished_.set_trigger(ZX_EVENT_SIGNALED);
72 release_image_callback_();
73 wait_for_surface_read_finished_.Cancel();
74 wait_for_surface_read_finished_.set_object(ZX_HANDLE_INVALID);
83 return SkISize::Make(0, 0);
86 return SkISize::Make(sk_surface_->width(), sk_surface_->height());
89bool SoftwareSurface::CreateFences() {
90 if (zx::event::create(0, &acquire_event_) != ZX_OK) {
91 FML_LOG(ERROR) <<
"Failed to create acquire event.";
95 if (zx::event::create(0, &release_event_) != ZX_OK) {
96 FML_LOG(ERROR) <<
"Failed to create release event.";
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.";
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);
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})),
132 if (duplicate_status != ZX_OK) {
133 FML_LOG(ERROR) <<
"Failed to duplicate collection token: "
134 << zx_status_get_string(duplicate_status);
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.";
144 auto scenic_token = std::move(duplicate_tokens[0]);
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);
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));
163 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
164 flatland_allocator->RegisterBufferCollection(
166 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
168 if (result.is_err()) {
170 <<
"RegisterBufferCollection call to Scenic Allocator failed.";
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);
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);
215 fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_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);
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());
230 FML_LOG(ERROR) <<
"Failed to allocate (err): "
231 <<
static_cast<uint32_t
>(wait_result.err());
235 auto buffer_collection_info =
236 std::move(*wait_result.response().mutable_buffer_collection_info());
239 FML_CHECK(buffer_collection_info.settings().buffer_settings().size_bytes() !=
241 FML_CHECK(buffer_collection_info.buffers()[0].vmo().is_valid());
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) {
249 needs_cache_clean_ =
true;
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);
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);
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.";
292 image_id_ = image_id;
300 return valid_ ? sk_surface_ :
nullptr;
303fuchsia::ui::composition::BufferCollectionImportToken
305 fuchsia::ui::composition::BufferCollectionImportToken import_dup;
306 import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
312 acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
318 release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
323 release_image_callback_ = release_image_callback;
336 const std::function<
void(
void)>& on_surface_read_finished) {
340 on_surface_read_finished();
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 "
348 surface_read_finished_callback_ = on_surface_read_finished;
352 if (needs_cache_clean_) {
353 surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_,
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);
366void SoftwareSurface::Reset() {
367 if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
368 release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
370 FML_LOG(ERROR) <<
"Could not reset fences. The surface is no longer valid.";
373 wait_for_surface_read_finished_.Begin(async_get_default_dispatcher());
376 auto callback = surface_read_finished_callback_;
377 surface_read_finished_callback_ =
nullptr;
383void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
384 async::WaitBase* wait,
386 const zx_packet_signal_t* signal) {
387 if (status != ZX_OK) {
390 FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
bool FlushSessionAcquireAndReleaseEvents() override
bool IsValid() const override
uint32_t GetImageId() override
fuchsia::ui::composition::BufferCollectionImportToken GetBufferCollectionImportToken() override
void SetReleaseImageCallback(ReleaseImageCallback release_image_callback) override
zx::event GetReleaseFence() override
~SoftwareSurface() override
zx::event GetAcquireFence() override
size_t AdvanceAndGetAge() 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)
#define FML_CHECK(condition)
#define FML_DCHECK(condition)
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