7#include <lib/async/default.h>
8#include <zircon/rights.h>
9#include <zircon/status.h>
10#include <zircon/types.h>
16#include "fuchsia/sysmem/cpp/fidl.h"
17#include "include/core/SkImageInfo.h"
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"
23#include "../runtime/dart/utils/inlines.h"
29constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
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;
43 return unrounded_bytes_per_row + roundup_bytes;
49 fuchsia::sysmem2::AllocatorSyncPtr& sysmem_allocator,
50 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
52 : wait_for_surface_read_finished_(this) {
55 if (!SetupSkiaSurface(sysmem_allocator, flatland_allocator, size)) {
56 FML_LOG(ERROR) <<
"Could not create render surface.";
60 if (!CreateFences()) {
61 FML_LOG(ERROR) <<
"Could not create signal fences.";
65 wait_for_surface_read_finished_.set_object(release_event_.get());
66 wait_for_surface_read_finished_.set_trigger(ZX_EVENT_SIGNALED);
73 release_image_callback_();
74 wait_for_surface_read_finished_.Cancel();
75 wait_for_surface_read_finished_.set_object(ZX_HANDLE_INVALID);
84 return SkISize::Make(0, 0);
87 return SkISize::Make(sk_surface_->width(), sk_surface_->height());
90bool SoftwareSurface::CreateFences() {
91 if (zx::event::create(0, &acquire_event_) != ZX_OK) {
92 FML_LOG(ERROR) <<
"Failed to create acquire event.";
96 if (zx::event::create(0, &release_event_) != ZX_OK) {
97 FML_LOG(ERROR) <<
"Failed to create release event.";
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.";
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);
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})),
133 if (duplicate_status != ZX_OK) {
134 FML_LOG(ERROR) <<
"Failed to duplicate collection token: "
135 << zx_status_get_string(duplicate_status);
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.";
145 auto scenic_token = std::move(duplicate_tokens[0]);
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);
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));
164 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
165 flatland_allocator->RegisterBufferCollection(
167 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
169 if (result.is_err()) {
171 <<
"RegisterBufferCollection call to Scenic Allocator failed.";
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);
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);
216 fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_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);
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());
231 FML_LOG(ERROR) <<
"Failed to allocate (err): "
232 <<
static_cast<uint32_t
>(wait_result.err());
236 auto buffer_collection_info =
237 std::move(*wait_result.response().mutable_buffer_collection_info());
240 FML_CHECK(buffer_collection_info.settings().buffer_settings().size_bytes() !=
242 FML_CHECK(buffer_collection_info.buffers()[0].vmo().is_valid());
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) {
250 needs_cache_clean_ =
true;
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);
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);
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.";
293 image_id_ = image_id;
301 return valid_ ? sk_surface_ :
nullptr;
304fuchsia::ui::composition::BufferCollectionImportToken
306 fuchsia::ui::composition::BufferCollectionImportToken import_dup;
307 import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
313 acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
319 release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
324 release_image_callback_ = release_image_callback;
337 const std::function<
void(
void)>& on_surface_read_finished) {
341 on_surface_read_finished();
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 "
349 surface_read_finished_callback_ = on_surface_read_finished;
353 if (needs_cache_clean_) {
354 surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_,
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);
367void SoftwareSurface::Reset() {
368 if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
369 release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
371 FML_LOG(ERROR) <<
"Could not reset fences. The surface is no longer valid.";
374 wait_for_surface_read_finished_.Begin(async_get_default_dispatcher());
377 auto callback = surface_read_finished_callback_;
378 surface_read_finished_callback_ =
nullptr;
384void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
385 async::WaitBase* wait,
387 const zx_packet_signal_t* signal) {
388 if (status != ZX_OK) {
391 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