fuchsia: Add a SoftwareSurfaceProducer for debug (flutter/engine#29657)
This commit is contained in:
@@ -1504,6 +1504,10 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface_producer.h
|
||||
|
||||
@@ -95,6 +95,10 @@ template("runner_sources") {
|
||||
"program_metadata.h",
|
||||
"runner.cc",
|
||||
"runner.h",
|
||||
"software_surface.cc",
|
||||
"software_surface.h",
|
||||
"software_surface_producer.cc",
|
||||
"software_surface_producer.h",
|
||||
"surface.cc",
|
||||
"surface.h",
|
||||
"surface_producer.h",
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#include "focus_delegate.h"
|
||||
#include "fuchsia_intl.h"
|
||||
#include "gfx_platform_view.h"
|
||||
#include "software_surface_producer.h"
|
||||
#include "surface.h"
|
||||
#include "vsync_waiter.h"
|
||||
#include "vulkan_surface_producer.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
namespace {
|
||||
@@ -252,13 +254,22 @@ void Engine::Initialize(
|
||||
request = parent_viewport_watcher.NewRequest(),
|
||||
view_ref_pair = std::move(view_ref_pair),
|
||||
max_frames_in_flight = product_config.get_max_frames_in_flight(),
|
||||
vsync_offset = product_config.get_vsync_offset()]() mutable {
|
||||
vsync_offset = product_config.get_vsync_offset(),
|
||||
software_rendering = product_config.software_rendering()]() mutable {
|
||||
if (use_flatland) {
|
||||
if (software_rendering) {
|
||||
surface_producer_ = std::make_shared<SoftwareSurfaceProducer>(
|
||||
/*scenic_session=*/nullptr);
|
||||
} else {
|
||||
surface_producer_ = std::make_shared<VulkanSurfaceProducer>(
|
||||
/*scenic_session=*/nullptr);
|
||||
}
|
||||
|
||||
flatland_connection_ = std::make_shared<FlatlandConnection>(
|
||||
thread_label_, std::move(flatland),
|
||||
std::move(session_error_callback), [](auto) {},
|
||||
max_frames_in_flight, vsync_offset);
|
||||
surface_producer_.emplace(/*scenic_session=*/nullptr);
|
||||
|
||||
fuchsia::ui::views::ViewIdentityOnCreation view_identity = {
|
||||
.view_ref = std::move(view_ref_pair.view_ref),
|
||||
.view_ref_control = std::move(view_ref_pair.control_ref)};
|
||||
@@ -266,18 +277,25 @@ void Engine::Initialize(
|
||||
std::make_shared<FlatlandExternalViewEmbedder>(
|
||||
std::move(view_creation_token), std::move(view_identity),
|
||||
std::move(flatland_view_protocols), std::move(request),
|
||||
*flatland_connection_.get(), surface_producer_.value(),
|
||||
flatland_connection_, surface_producer_,
|
||||
intercept_all_input_);
|
||||
} else {
|
||||
session_connection_ = std::make_shared<GfxSessionConnection>(
|
||||
thread_label_, std::move(session_inspect_node),
|
||||
std::move(session), std::move(session_error_callback),
|
||||
[](auto) {}, max_frames_in_flight, vsync_offset);
|
||||
surface_producer_.emplace(session_connection_->get());
|
||||
|
||||
if (software_rendering) {
|
||||
surface_producer_ = std::make_shared<SoftwareSurfaceProducer>(
|
||||
session_connection_->get());
|
||||
} else {
|
||||
surface_producer_ = std::make_shared<VulkanSurfaceProducer>(
|
||||
session_connection_->get());
|
||||
}
|
||||
|
||||
external_view_embedder_ = std::make_shared<GfxExternalViewEmbedder>(
|
||||
thread_label_, std::move(view_token), std::move(view_ref_pair),
|
||||
*session_connection_.get(), surface_producer_.value(),
|
||||
intercept_all_input_);
|
||||
session_connection_, surface_producer_, intercept_all_input_);
|
||||
}
|
||||
view_embedder_latch.Signal();
|
||||
}));
|
||||
@@ -432,7 +450,7 @@ void Engine::Initialize(
|
||||
->GetTaskRunner()
|
||||
.get(),
|
||||
shell.GetTaskRunners().GetRasterTaskRunner().get(),
|
||||
surface_producer_.value(), width, height,
|
||||
surface_producer_, SkISize::Make(width, height),
|
||||
flutter::PersistentCache::GetCacheForProcess()
|
||||
->asset_manager(),
|
||||
skp_names, completion_callback);
|
||||
@@ -443,7 +461,7 @@ void Engine::Initialize(
|
||||
->GetTaskRunner()
|
||||
.get(),
|
||||
shell.GetTaskRunners().GetRasterTaskRunner().get(),
|
||||
surface_producer_.value(), 1024, 600,
|
||||
surface_producer_, SkISize::Make(1024, 600),
|
||||
flutter::PersistentCache::GetCacheForProcess()
|
||||
->asset_manager(),
|
||||
std::nullopt, std::nullopt);
|
||||
@@ -658,12 +676,15 @@ Engine::~Engine() {
|
||||
fml::AutoResetWaitableEvent view_embedder_latch;
|
||||
thread_host_.raster_thread->GetTaskRunner()->PostTask(
|
||||
fml::MakeCopyable([this, &view_embedder_latch]() mutable {
|
||||
flatland_view_embedder_.reset();
|
||||
external_view_embedder_.reset();
|
||||
surface_producer_.reset();
|
||||
flatland_connection_.reset();
|
||||
session_connection_.reset();
|
||||
|
||||
if (flatland_view_embedder_ != nullptr) {
|
||||
flatland_view_embedder_.reset();
|
||||
flatland_connection_.reset();
|
||||
surface_producer_.reset();
|
||||
} else {
|
||||
external_view_embedder_.reset();
|
||||
surface_producer_.reset();
|
||||
session_connection_.reset();
|
||||
}
|
||||
view_embedder_latch.Signal();
|
||||
}));
|
||||
view_embedder_latch.Wait();
|
||||
@@ -835,19 +856,16 @@ void Engine::WriteProfileToTrace() const {
|
||||
void Engine::WarmupSkps(
|
||||
fml::BasicTaskRunner* concurrent_task_runner,
|
||||
fml::BasicTaskRunner* raster_task_runner,
|
||||
VulkanSurfaceProducer& surface_producer,
|
||||
uint64_t width,
|
||||
uint64_t height,
|
||||
std::shared_ptr<SurfaceProducer> surface_producer,
|
||||
SkISize size,
|
||||
std::shared_ptr<flutter::AssetManager> asset_manager,
|
||||
std::optional<const std::vector<std::string>> skp_names,
|
||||
std::optional<std::function<void(uint32_t)>> completion_callback) {
|
||||
SkISize size = SkISize::Make(width, height);
|
||||
|
||||
// We use a raw pointer here because we want to keep this alive until all gpu
|
||||
// work is done and the callbacks skia takes for this are function pointers
|
||||
// so we are unable to use a lambda that captures the smart pointer.
|
||||
SurfaceProducerSurface* skp_warmup_surface =
|
||||
surface_producer.ProduceOffscreenSurface(size).release();
|
||||
surface_producer->ProduceOffscreenSurface(size).release();
|
||||
if (!skp_warmup_surface) {
|
||||
FML_LOG(ERROR) << "Failed to create offscreen warmup surface";
|
||||
// Tell client that zero shaders were warmed up because warmup failed.
|
||||
@@ -860,7 +878,7 @@ void Engine::WarmupSkps(
|
||||
// tell concurrent task runner to deserialize all skps available from
|
||||
// the asset manager
|
||||
concurrent_task_runner->PostTask([raster_task_runner, skp_warmup_surface,
|
||||
&surface_producer, asset_manager, skp_names,
|
||||
surface_producer, asset_manager, skp_names,
|
||||
completion_callback]() {
|
||||
TRACE_DURATION("flutter", "DeserializeSkps");
|
||||
std::vector<std::unique_ptr<fml::Mapping>> skp_mappings;
|
||||
@@ -903,27 +921,13 @@ void Engine::WarmupSkps(
|
||||
// Tell raster task runner to warmup have the compositor
|
||||
// context warm up the newly deserialized picture
|
||||
raster_task_runner->PostTask([skp_warmup_surface, picture,
|
||||
&surface_producer, completion_callback, i,
|
||||
surface_producer, completion_callback, i,
|
||||
count = skp_mappings.size()] {
|
||||
TRACE_DURATION("flutter", "WarmupSkp");
|
||||
skp_warmup_surface->GetSkiaSurface()->getCanvas()->drawPicture(picture);
|
||||
|
||||
if (i < count - 1) {
|
||||
// For all but the last skp we fire and forget
|
||||
surface_producer.gr_context()->flushAndSubmit();
|
||||
} else {
|
||||
// For the last skp we provide a callback that frees the warmup
|
||||
// surface and calls the completion callback
|
||||
struct GrFlushInfo flush_info;
|
||||
flush_info.fFinishedContext = skp_warmup_surface;
|
||||
flush_info.fFinishedProc = [](void* skp_warmup_surface) {
|
||||
delete static_cast<SurfaceProducerSurface*>(skp_warmup_surface);
|
||||
};
|
||||
|
||||
surface_producer.gr_context()->flush(flush_info);
|
||||
surface_producer.gr_context()->submit();
|
||||
|
||||
// We call this here instead of inside fFinishedProc above because
|
||||
if (i == count - 1) {
|
||||
// We call this here instead of inside fFinishedProc below because
|
||||
// we want to unblock the dart animation code as soon as the raster
|
||||
// thread is free to enque work, rather than waiting for the GPU work
|
||||
// itself to finish.
|
||||
@@ -931,6 +935,28 @@ void Engine::WarmupSkps(
|
||||
completion_callback.value()(count);
|
||||
}
|
||||
}
|
||||
|
||||
if (surface_producer->gr_context()) {
|
||||
if (i < count - 1) {
|
||||
// For all but the last skp we fire and forget
|
||||
surface_producer->gr_context()->flushAndSubmit();
|
||||
} else {
|
||||
// For the last skp we provide a callback that frees the warmup
|
||||
// surface and calls the completion callback
|
||||
struct GrFlushInfo flush_info;
|
||||
flush_info.fFinishedContext = skp_warmup_surface;
|
||||
flush_info.fFinishedProc = [](void* skp_warmup_surface) {
|
||||
delete static_cast<SurfaceProducerSurface*>(skp_warmup_surface);
|
||||
};
|
||||
|
||||
surface_producer->gr_context()->flush(flush_info);
|
||||
surface_producer->gr_context()->submit();
|
||||
}
|
||||
} else {
|
||||
if (i == count - 1) {
|
||||
delete skp_warmup_surface;
|
||||
}
|
||||
}
|
||||
});
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "gfx_external_view_embedder.h"
|
||||
#include "gfx_session_connection.h"
|
||||
#include "isolate_configurator.h"
|
||||
#include "vulkan_surface_producer.h"
|
||||
#include "surface_producer.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
@@ -104,9 +104,8 @@ class Engine final : public fuchsia::memorypressure::Watcher {
|
||||
static void WarmupSkps(
|
||||
fml::BasicTaskRunner* concurrent_task_runner,
|
||||
fml::BasicTaskRunner* raster_task_runner,
|
||||
VulkanSurfaceProducer& surface_producer,
|
||||
uint64_t width,
|
||||
uint64_t height,
|
||||
std::shared_ptr<SurfaceProducer> surface_producer,
|
||||
SkISize size,
|
||||
std::shared_ptr<flutter::AssetManager> asset_manager,
|
||||
std::optional<const std::vector<std::string>> skp_names,
|
||||
std::optional<std::function<void(uint32_t)>> completion_callback);
|
||||
@@ -157,7 +156,7 @@ class Engine final : public fuchsia::memorypressure::Watcher {
|
||||
session_connection_; // Must come before surface_producer_
|
||||
std::shared_ptr<FlatlandConnection>
|
||||
flatland_connection_; // Must come before surface_producer_
|
||||
std::optional<VulkanSurfaceProducer> surface_producer_;
|
||||
std::shared_ptr<SurfaceProducer> surface_producer_;
|
||||
std::shared_ptr<GfxExternalViewEmbedder> external_view_embedder_;
|
||||
std::shared_ptr<FlatlandExternalViewEmbedder> flatland_view_embedder_;
|
||||
|
||||
|
||||
@@ -17,17 +17,17 @@ FlatlandExternalViewEmbedder::FlatlandExternalViewEmbedder(
|
||||
fuchsia::ui::composition::ViewBoundProtocols view_protocols,
|
||||
fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
|
||||
parent_viewport_watcher_request,
|
||||
FlatlandConnection& flatland,
|
||||
SurfaceProducer& surface_producer,
|
||||
std::shared_ptr<FlatlandConnection> flatland,
|
||||
std::shared_ptr<SurfaceProducer> surface_producer,
|
||||
bool intercept_all_input)
|
||||
: flatland_(flatland), surface_producer_(surface_producer) {
|
||||
flatland_.flatland()->CreateView2(
|
||||
flatland_->flatland()->CreateView2(
|
||||
std::move(view_creation_token), std::move(view_identity),
|
||||
std::move(view_protocols), std::move(parent_viewport_watcher_request));
|
||||
|
||||
root_transform_id_ = flatland_.NextTransformId();
|
||||
flatland_.flatland()->CreateTransform(root_transform_id_);
|
||||
flatland_.flatland()->SetRootTransform(root_transform_id_);
|
||||
root_transform_id_ = flatland_->NextTransformId();
|
||||
flatland_->flatland()->CreateTransform(root_transform_id_);
|
||||
flatland_->flatland()->SetRootTransform(root_transform_id_);
|
||||
}
|
||||
|
||||
FlatlandExternalViewEmbedder::~FlatlandExternalViewEmbedder() = default;
|
||||
@@ -122,7 +122,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
}
|
||||
|
||||
auto surface =
|
||||
surface_producer_.ProduceSurface(layer.second.surface_size);
|
||||
surface_producer_->ProduceSurface(layer.second.surface_size);
|
||||
if (!surface) {
|
||||
const std::string layer_id_str =
|
||||
layer.first.has_value() ? std::to_string(layer.first.value())
|
||||
@@ -137,24 +137,24 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
// If we receive an unitialized surface, we need to first create flatland
|
||||
// resource.
|
||||
if (surface->GetImageId() == 0) {
|
||||
auto image_id = flatland_.NextContentId().value;
|
||||
auto image_id = flatland_->NextContentId().value;
|
||||
const auto& size = surface->GetSize();
|
||||
fuchsia::ui::composition::ImageProperties image_properties;
|
||||
image_properties.set_size({static_cast<uint32_t>(size.width()),
|
||||
static_cast<uint32_t>(size.height())});
|
||||
flatland_.flatland()->CreateImage(
|
||||
flatland_->flatland()->CreateImage(
|
||||
{image_id}, surface->GetBufferCollectionImportToken(), 0,
|
||||
std::move(image_properties));
|
||||
|
||||
surface->SetImageId(image_id);
|
||||
surface->SetReleaseImageCallback([flatland = &flatland_, image_id]() {
|
||||
surface->SetReleaseImageCallback([flatland = flatland_, image_id]() {
|
||||
flatland->flatland()->ReleaseImage({image_id});
|
||||
});
|
||||
}
|
||||
|
||||
// Enqueue fences for the next present.
|
||||
flatland_.EnqueueAcquireFence(surface->GetAcquireFence());
|
||||
flatland_.EnqueueReleaseFence(surface->GetReleaseFence());
|
||||
flatland_->EnqueueAcquireFence(surface->GetAcquireFence());
|
||||
flatland_->EnqueueReleaseFence(surface->GetReleaseFence());
|
||||
|
||||
frame_surface_indices.emplace(
|
||||
std::make_pair(layer.first, frame_surfaces.size()));
|
||||
@@ -193,7 +193,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
// Set transform for the viewport.
|
||||
// TODO(fxbug.dev/64201): Handle scaling.
|
||||
if (view_mutators.transform != viewport.mutators.transform) {
|
||||
flatland_.flatland()->SetTranslation(
|
||||
flatland_->flatland()->SetTranslation(
|
||||
viewport.transform_id,
|
||||
{static_cast<int32_t>(view_mutators.transform.getTranslateX()),
|
||||
static_cast<int32_t>(view_mutators.transform.getTranslateY())});
|
||||
@@ -210,14 +210,14 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
properties.set_logical_size(
|
||||
{static_cast<uint32_t>(view_size.fWidth),
|
||||
static_cast<uint32_t>(view_size.fHeight)});
|
||||
flatland_.flatland()->SetViewportProperties(viewport.viewport_id,
|
||||
std::move(properties));
|
||||
flatland_->flatland()->SetViewportProperties(viewport.viewport_id,
|
||||
std::move(properties));
|
||||
viewport.size = view_size;
|
||||
}
|
||||
|
||||
// Attach the FlatlandView to the main scene graph.
|
||||
flatland_.flatland()->AddChild(root_transform_id_,
|
||||
viewport.transform_id);
|
||||
flatland_->flatland()->AddChild(root_transform_id_,
|
||||
viewport.transform_id);
|
||||
child_transforms_.emplace_back(viewport.transform_id);
|
||||
}
|
||||
|
||||
@@ -244,22 +244,22 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
// Create a new layer if needed for the surface.
|
||||
FML_CHECK(flatland_layer_index <= flatland_layers_.size());
|
||||
if (flatland_layer_index == flatland_layers_.size()) {
|
||||
FlatlandLayer new_layer{.transform_id = flatland_.NextTransformId()};
|
||||
flatland_.flatland()->CreateTransform(new_layer.transform_id);
|
||||
FlatlandLayer new_layer{.transform_id = flatland_->NextTransformId()};
|
||||
flatland_->flatland()->CreateTransform(new_layer.transform_id);
|
||||
flatland_layers_.emplace_back(std::move(new_layer));
|
||||
}
|
||||
|
||||
// Update the image content and set size.
|
||||
flatland_.flatland()->SetContent(
|
||||
flatland_->flatland()->SetContent(
|
||||
flatland_layers_[flatland_layer_index].transform_id,
|
||||
{surface_for_layer->GetImageId()});
|
||||
flatland_.flatland()->SetImageDestinationSize(
|
||||
flatland_->flatland()->SetImageDestinationSize(
|
||||
{surface_for_layer->GetImageId()},
|
||||
{static_cast<uint32_t>(surface_for_layer->GetSize().width()),
|
||||
static_cast<uint32_t>(surface_for_layer->GetSize().height())});
|
||||
|
||||
// Attach the FlatlandLayer to the main scene graph.
|
||||
flatland_.flatland()->AddChild(
|
||||
flatland_->flatland()->AddChild(
|
||||
root_transform_id_,
|
||||
flatland_layers_[flatland_layer_index].transform_id);
|
||||
child_transforms_.emplace_back(
|
||||
@@ -276,7 +276,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
{
|
||||
TRACE_EVENT0("flutter", "SessionPresent");
|
||||
|
||||
flatland_.Present();
|
||||
flatland_->Present();
|
||||
}
|
||||
|
||||
// Render the recorded SkPictures into the surfaces.
|
||||
@@ -315,7 +315,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
|
||||
{
|
||||
TRACE_EVENT0("flutter", "PresentSurfaces");
|
||||
|
||||
surface_producer_.SubmitSurfaces(std::move(frame_surfaces));
|
||||
surface_producer_->SubmitSurfaces(std::move(frame_surfaces));
|
||||
}
|
||||
|
||||
// Submit the underlying render-backend-specific frame for processing.
|
||||
@@ -328,19 +328,20 @@ void FlatlandExternalViewEmbedder::CreateView(
|
||||
FlatlandViewCreatedCallback on_view_bound) {
|
||||
FML_CHECK(flatland_views_.find(view_id) == flatland_views_.end());
|
||||
|
||||
FlatlandView new_view = {.transform_id = flatland_.NextTransformId(),
|
||||
.viewport_id = flatland_.NextContentId()};
|
||||
flatland_.flatland()->CreateTransform(new_view.transform_id);
|
||||
FlatlandView new_view = {.transform_id = flatland_->NextTransformId(),
|
||||
.viewport_id = flatland_->NextContentId()};
|
||||
flatland_->flatland()->CreateTransform(new_view.transform_id);
|
||||
fuchsia::ui::composition::ViewportProperties properties;
|
||||
// TODO(fxbug.dev/64201): Investigate if it is possible to avoid using a
|
||||
// default size by finding the size before creation.
|
||||
properties.set_logical_size(
|
||||
{kFlatlandDefaultViewportSize, kFlatlandDefaultViewportSize});
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr child_view_watcher;
|
||||
flatland_.flatland()->CreateViewport(
|
||||
flatland_->flatland()->CreateViewport(
|
||||
new_view.viewport_id, {zx::channel((zx_handle_t)view_id)},
|
||||
std::move(properties), child_view_watcher.NewRequest());
|
||||
flatland_.flatland()->SetContent(new_view.transform_id, new_view.viewport_id);
|
||||
flatland_->flatland()->SetContent(new_view.transform_id,
|
||||
new_view.viewport_id);
|
||||
|
||||
on_view_created();
|
||||
on_view_bound(new_view.viewport_id, std::move(child_view_watcher));
|
||||
@@ -355,17 +356,17 @@ void FlatlandExternalViewEmbedder::DestroyView(
|
||||
|
||||
auto viewport_id = flatland_view->second.viewport_id;
|
||||
auto transform_id = flatland_view->second.transform_id;
|
||||
flatland_.flatland()->ReleaseViewport(viewport_id, [](auto) {});
|
||||
flatland_->flatland()->ReleaseViewport(viewport_id, [](auto) {});
|
||||
auto itr =
|
||||
std::find_if(child_transforms_.begin(), child_transforms_.end(),
|
||||
[transform_id](fuchsia::ui::composition::TransformId id) {
|
||||
return id.value == transform_id.value;
|
||||
});
|
||||
if (itr != child_transforms_.end()) {
|
||||
flatland_.flatland()->RemoveChild(root_transform_id_, transform_id);
|
||||
flatland_->flatland()->RemoveChild(root_transform_id_, transform_id);
|
||||
child_transforms_.erase(itr);
|
||||
}
|
||||
flatland_.flatland()->ReleaseTransform(transform_id);
|
||||
flatland_->flatland()->ReleaseTransform(transform_id);
|
||||
|
||||
flatland_views_.erase(flatland_view);
|
||||
on_view_unbound(viewport_id);
|
||||
@@ -389,13 +390,13 @@ void FlatlandExternalViewEmbedder::Reset() {
|
||||
|
||||
// Clear all children from root.
|
||||
for (const auto& transform : child_transforms_) {
|
||||
flatland_.flatland()->RemoveChild(root_transform_id_, transform);
|
||||
flatland_->flatland()->RemoveChild(root_transform_id_, transform);
|
||||
}
|
||||
child_transforms_.clear();
|
||||
|
||||
// Clear images on all layers so they aren't cached unnecessarily.
|
||||
for (const auto& layer : flatland_layers_) {
|
||||
flatland_.flatland()->SetContent(layer.transform_id, {0});
|
||||
flatland_->flatland()->SetContent(layer.transform_id, {0});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ class FlatlandExternalViewEmbedder final
|
||||
fuchsia::ui::composition::ViewBoundProtocols endpoints,
|
||||
fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
|
||||
parent_viewport_watcher_request,
|
||||
FlatlandConnection& flatland,
|
||||
SurfaceProducer& surface_producer,
|
||||
std::shared_ptr<FlatlandConnection> flatland,
|
||||
std::shared_ptr<SurfaceProducer> surface_producer,
|
||||
bool intercept_all_input = false);
|
||||
~FlatlandExternalViewEmbedder();
|
||||
|
||||
@@ -168,8 +168,9 @@ class FlatlandExternalViewEmbedder final
|
||||
fuchsia::ui::composition::TransformId transform_id;
|
||||
};
|
||||
|
||||
FlatlandConnection& flatland_;
|
||||
SurfaceProducer& surface_producer_;
|
||||
std::shared_ptr<FlatlandConnection> flatland_;
|
||||
std::shared_ptr<SurfaceProducer> surface_producer_;
|
||||
|
||||
fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher_;
|
||||
|
||||
fuchsia::ui::composition::TransformId root_transform_id_;
|
||||
|
||||
@@ -25,28 +25,39 @@ FlutterRunnerProductConfiguration::FlutterRunnerProductConfiguration(
|
||||
// Parse out all values we're expecting.
|
||||
if (document.HasMember("vsync_offset_in_us")) {
|
||||
auto& val = document["vsync_offset_in_us"];
|
||||
if (val.IsInt())
|
||||
if (val.IsInt()) {
|
||||
vsync_offset_ = fml::TimeDelta::FromMicroseconds(val.GetInt());
|
||||
}
|
||||
}
|
||||
if (document.HasMember("max_frames_in_flight")) {
|
||||
auto& val = document["max_frames_in_flight"];
|
||||
if (val.IsInt())
|
||||
if (val.IsInt()) {
|
||||
max_frames_in_flight_ = val.GetInt();
|
||||
}
|
||||
}
|
||||
if (document.HasMember("intercept_all_input")) {
|
||||
auto& val = document["intercept_all_input"];
|
||||
if (val.IsBool())
|
||||
if (val.IsBool()) {
|
||||
intercept_all_input_ = val.GetBool();
|
||||
}
|
||||
}
|
||||
if (document.HasMember("software_rendering")) {
|
||||
auto& val = document["software_rendering"];
|
||||
if (val.IsBool()) {
|
||||
software_rendering_ = val.GetBool();
|
||||
}
|
||||
}
|
||||
if (document.HasMember("enable_shader_warmup")) {
|
||||
auto& val = document["enable_shader_warmup"];
|
||||
if (val.IsBool())
|
||||
if (val.IsBool()) {
|
||||
enable_shader_warmup_ = val.GetBool();
|
||||
}
|
||||
}
|
||||
if (document.HasMember("enable_shader_warmup_dart_hooks")) {
|
||||
auto& val = document["enable_shader_warmup_dart_hooks"];
|
||||
if (val.IsBool())
|
||||
if (val.IsBool()) {
|
||||
enable_shader_warmup_dart_hooks_ = val.GetBool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "flutter/fml/time/time_delta.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
@@ -12,11 +14,12 @@ namespace flutter_runner {
|
||||
class FlutterRunnerProductConfiguration {
|
||||
public:
|
||||
FlutterRunnerProductConfiguration() {}
|
||||
FlutterRunnerProductConfiguration(std::string path);
|
||||
explicit FlutterRunnerProductConfiguration(std::string json_string);
|
||||
|
||||
fml::TimeDelta get_vsync_offset() { return vsync_offset_; }
|
||||
uint64_t get_max_frames_in_flight() { return max_frames_in_flight_; }
|
||||
bool get_intercept_all_input() { return intercept_all_input_; }
|
||||
bool software_rendering() { return software_rendering_; }
|
||||
bool enable_shader_warmup() { return enable_shader_warmup_; }
|
||||
bool enable_shader_warmup_dart_hooks() {
|
||||
return enable_shader_warmup_dart_hooks_;
|
||||
@@ -26,6 +29,7 @@ class FlutterRunnerProductConfiguration {
|
||||
fml::TimeDelta vsync_offset_ = fml::TimeDelta::Zero();
|
||||
uint64_t max_frames_in_flight_ = 3;
|
||||
bool intercept_all_input_ = false;
|
||||
bool software_rendering_ = false;
|
||||
bool enable_shader_warmup_ = false;
|
||||
bool enable_shader_warmup_dart_hooks_ = true;
|
||||
};
|
||||
|
||||
@@ -103,18 +103,18 @@ GfxExternalViewEmbedder::GfxExternalViewEmbedder(
|
||||
std::string debug_label,
|
||||
fuchsia::ui::views::ViewToken view_token,
|
||||
scenic::ViewRefPair view_ref_pair,
|
||||
GfxSessionConnection& session,
|
||||
SurfaceProducer& surface_producer,
|
||||
std::shared_ptr<GfxSessionConnection> session,
|
||||
std::shared_ptr<SurfaceProducer> surface_producer,
|
||||
bool intercept_all_input)
|
||||
: session_(session),
|
||||
surface_producer_(surface_producer),
|
||||
root_view_(session_.get(),
|
||||
root_view_(session_->get(),
|
||||
std::move(view_token),
|
||||
std::move(view_ref_pair.control_ref),
|
||||
std::move(view_ref_pair.view_ref),
|
||||
debug_label),
|
||||
metrics_node_(session_.get()),
|
||||
layer_tree_node_(session_.get()) {
|
||||
metrics_node_(session_->get()),
|
||||
layer_tree_node_(session_->get()) {
|
||||
layer_tree_node_.SetLabel("Flutter::LayerTree");
|
||||
metrics_node_.SetLabel("Flutter::MetricsWatcher");
|
||||
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
|
||||
@@ -125,7 +125,7 @@ GfxExternalViewEmbedder::GfxExternalViewEmbedder(
|
||||
// will capture all input, and any unwanted input will be reinjected into
|
||||
// embedded views.
|
||||
if (intercept_all_input) {
|
||||
input_interceptor_node_.emplace(session_.get());
|
||||
input_interceptor_node_.emplace(session_->get());
|
||||
input_interceptor_node_->SetLabel("Flutter::InputInterceptor");
|
||||
input_interceptor_node_->SetHitTestBehavior(
|
||||
fuchsia::ui::gfx::HitTestBehavior::kDefault);
|
||||
@@ -134,7 +134,7 @@ GfxExternalViewEmbedder::GfxExternalViewEmbedder(
|
||||
metrics_node_.AddChild(input_interceptor_node_.value());
|
||||
}
|
||||
|
||||
session_.Present();
|
||||
session_->Present();
|
||||
}
|
||||
|
||||
GfxExternalViewEmbedder::~GfxExternalViewEmbedder() = default;
|
||||
@@ -215,7 +215,7 @@ void GfxExternalViewEmbedder::BeginFrame(
|
||||
if (found_rect == scenic_interceptor_rects_.end()) {
|
||||
auto [emplaced_rect, success] =
|
||||
scenic_interceptor_rects_.emplace(std::make_pair(
|
||||
rect_hash, scenic::Rectangle(session_.get(), frame_size_.width(),
|
||||
rect_hash, scenic::Rectangle(session_->get(), frame_size_.width(),
|
||||
frame_size_.height())));
|
||||
FML_CHECK(success);
|
||||
|
||||
@@ -253,7 +253,7 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
}
|
||||
|
||||
auto surface =
|
||||
surface_producer_.ProduceSurface(layer.second.surface_size);
|
||||
surface_producer_->ProduceSurface(layer.second.surface_size);
|
||||
if (!surface) {
|
||||
const std::string layer_id_str =
|
||||
layer.first.has_value() ? std::to_string(layer.first.value())
|
||||
@@ -313,7 +313,7 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
// Expand the clip_nodes array to fit any new nodes.
|
||||
while (view_holder.clip_nodes.size() < view_mutators.clips.size()) {
|
||||
view_holder.clip_nodes.emplace_back(
|
||||
scenic::EntityNode(session_.get()));
|
||||
scenic::EntityNode(session_->get()));
|
||||
}
|
||||
FML_CHECK(view_holder.clip_nodes.size() >=
|
||||
view_mutators.clips.size());
|
||||
@@ -437,8 +437,8 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
FML_CHECK(scenic_layer_index <= scenic_layers_.size());
|
||||
if (scenic_layer_index == scenic_layers_.size()) {
|
||||
ScenicLayer new_layer{
|
||||
.shape_node = scenic::ShapeNode(session_.get()),
|
||||
.material = scenic::Material(session_.get()),
|
||||
.shape_node = scenic::ShapeNode(session_->get()),
|
||||
.material = scenic::Material(session_->get()),
|
||||
};
|
||||
new_layer.shape_node.SetMaterial(new_layer.material);
|
||||
scenic_layers_.emplace_back(std::move(new_layer));
|
||||
@@ -469,7 +469,7 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
FML_CHECK(rect_index <= found_rects->second.size());
|
||||
if (rect_index == found_rects->second.size()) {
|
||||
found_rects->second.emplace_back(scenic::Rectangle(
|
||||
session_.get(), layer->second.surface_size.width(),
|
||||
session_->get(), layer->second.surface_size.width(),
|
||||
layer->second.surface_size.height()));
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
{
|
||||
TRACE_EVENT0("flutter", "SessionPresent");
|
||||
|
||||
session_.Present();
|
||||
session_->Present();
|
||||
}
|
||||
|
||||
// Render the recorded SkPictures into the surfaces.
|
||||
@@ -563,7 +563,7 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
{
|
||||
TRACE_EVENT0("flutter", "PresentSurfaces");
|
||||
|
||||
surface_producer_.SubmitSurfaces(std::move(frame_surfaces));
|
||||
surface_producer_->SubmitSurfaces(std::move(frame_surfaces));
|
||||
}
|
||||
|
||||
// Submit the underlying render-backend-specific frame for processing.
|
||||
@@ -571,9 +571,9 @@ void GfxExternalViewEmbedder::SubmitFrame(
|
||||
}
|
||||
|
||||
void GfxExternalViewEmbedder::EnableWireframe(bool enable) {
|
||||
session_.get()->Enqueue(
|
||||
session_->get()->Enqueue(
|
||||
scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable));
|
||||
session_.Present();
|
||||
session_->Present();
|
||||
}
|
||||
|
||||
void GfxExternalViewEmbedder::CreateView(int64_t view_id,
|
||||
@@ -582,10 +582,10 @@ void GfxExternalViewEmbedder::CreateView(int64_t view_id,
|
||||
FML_CHECK(scenic_views_.find(view_id) == scenic_views_.end());
|
||||
|
||||
ScenicView new_view = {
|
||||
.opacity_node = scenic::OpacityNodeHACK(session_.get()),
|
||||
.transform_node = scenic::EntityNode(session_.get()),
|
||||
.opacity_node = scenic::OpacityNodeHACK(session_->get()),
|
||||
.transform_node = scenic::EntityNode(session_->get()),
|
||||
.view_holder = scenic::ViewHolder(
|
||||
session_.get(),
|
||||
session_->get(),
|
||||
scenic::ToViewHolderToken(zx::eventpair((zx_handle_t)view_id)),
|
||||
"Flutter::PlatformView"),
|
||||
};
|
||||
|
||||
@@ -75,8 +75,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
|
||||
GfxExternalViewEmbedder(std::string debug_label,
|
||||
fuchsia::ui::views::ViewToken view_token,
|
||||
scenic::ViewRefPair view_ref_pair,
|
||||
GfxSessionConnection& session,
|
||||
SurfaceProducer& surface_producer,
|
||||
std::shared_ptr<GfxSessionConnection> session,
|
||||
std::shared_ptr<SurfaceProducer> surface_producer,
|
||||
bool intercept_all_input = false);
|
||||
~GfxExternalViewEmbedder();
|
||||
|
||||
@@ -177,8 +177,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
|
||||
scenic::Material material;
|
||||
};
|
||||
|
||||
GfxSessionConnection& session_;
|
||||
SurfaceProducer& surface_producer_;
|
||||
std::shared_ptr<GfxSessionConnection> session_;
|
||||
std::shared_ptr<SurfaceProducer> surface_producer_;
|
||||
|
||||
scenic::View root_view_;
|
||||
scenic::EntityNode metrics_node_;
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "software_surface.h"
|
||||
|
||||
#include <lib/async/default.h>
|
||||
#include <lib/ui/scenic/cpp/commands.h>
|
||||
#include <zircon/status.h>
|
||||
#include <zircon/types.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "fuchsia/sysmem/cpp/fidl.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "third_party/skia/include/core/SkColorSpace.h"
|
||||
#include "third_party/skia/include/core/SkImageInfo.h"
|
||||
|
||||
#include "../runtime/dart/utils/inlines.h"
|
||||
#include "zx/cpp/fidl.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
|
||||
|
||||
uint32_t BytesPerRow(const fuchsia::sysmem::SingleBufferSettings& settings,
|
||||
uint32_t bytes_per_pixel,
|
||||
uint32_t image_width) {
|
||||
const uint32_t bytes_per_row_divisor =
|
||||
settings.image_format_constraints.bytes_per_row_divisor;
|
||||
const uint32_t min_bytes_per_row =
|
||||
settings.image_format_constraints.min_bytes_per_row;
|
||||
const uint32_t unrounded_bytes_per_row =
|
||||
std::max(image_width * bytes_per_pixel, min_bytes_per_row);
|
||||
const uint32_t roundup_bytes =
|
||||
unrounded_bytes_per_row % bytes_per_row_divisor;
|
||||
|
||||
return unrounded_bytes_per_row + roundup_bytes;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
uint32_t SoftwareSurface::sNextBufferId = 1; // 0 is invalid; start at 1.
|
||||
|
||||
SoftwareSurface::SoftwareSurface(
|
||||
fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
|
||||
fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
|
||||
scenic::Session* session,
|
||||
const SkISize& size)
|
||||
: session_(session), wait_for_surface_read_finished_(this) {
|
||||
FML_CHECK((session_ || flatland_allocator.is_bound()) &&
|
||||
!(session_ && flatland_allocator.is_bound()));
|
||||
|
||||
if (!SetupSkiaSurface(sysmem_allocator, flatland_allocator, size)) {
|
||||
FML_LOG(ERROR) << "Could not create render surface.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CreateFences()) {
|
||||
FML_LOG(ERROR) << "Could not create signal fences.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (session) {
|
||||
if (image_id_ == 0) {
|
||||
image_id_ = session->AllocResourceId();
|
||||
}
|
||||
session->Enqueue(scenic::NewCreateImage2Cmd(
|
||||
image_id_, sk_surface_->width(), sk_surface_->height(), buffer_id_, 0));
|
||||
}
|
||||
|
||||
wait_for_surface_read_finished_.set_object(release_event_.get());
|
||||
wait_for_surface_read_finished_.set_trigger(ZX_EVENT_SIGNALED);
|
||||
Reset();
|
||||
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
SoftwareSurface::~SoftwareSurface() {
|
||||
if (session_) {
|
||||
if (image_id_) {
|
||||
session_->Enqueue(scenic::NewReleaseResourceCmd(image_id_));
|
||||
}
|
||||
if (buffer_id_) {
|
||||
session_->DeregisterBufferCollection(buffer_id_);
|
||||
}
|
||||
} else {
|
||||
release_image_callback_();
|
||||
}
|
||||
wait_for_surface_read_finished_.Cancel();
|
||||
wait_for_surface_read_finished_.set_object(ZX_HANDLE_INVALID);
|
||||
}
|
||||
|
||||
bool SoftwareSurface::IsValid() const {
|
||||
return valid_;
|
||||
}
|
||||
|
||||
SkISize SoftwareSurface::GetSize() const {
|
||||
if (!valid_) {
|
||||
return SkISize::Make(0, 0);
|
||||
}
|
||||
|
||||
return SkISize::Make(sk_surface_->width(), sk_surface_->height());
|
||||
}
|
||||
|
||||
bool SoftwareSurface::CreateFences() {
|
||||
if (zx::event::create(0, &acquire_event_) != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to create acquire event.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (zx::event::create(0, &release_event_) != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to create release event.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoftwareSurface::SetupSkiaSurface(
|
||||
fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
|
||||
fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
|
||||
const SkISize& size) {
|
||||
if (size.isEmpty()) {
|
||||
FML_LOG(ERROR) << "Failed to allocate surface, size is empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate a "local" sysmem token to represent flutter's handle to the
|
||||
// sysmem buffer.
|
||||
fuchsia::sysmem::BufferCollectionTokenSyncPtr local_token;
|
||||
zx_status_t allocate_status =
|
||||
sysmem_allocator->AllocateSharedCollection(local_token.NewRequest());
|
||||
if (allocate_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to allocate collection: "
|
||||
<< zx_status_get_string(allocate_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a single Duplicate of the token and Sync it; the single duplicate
|
||||
// token represents scenic's handle to the sysmem buffer.
|
||||
std::vector<fuchsia::sysmem::BufferCollectionTokenHandle> duplicate_tokens;
|
||||
zx_status_t duplicate_status = local_token->DuplicateSync(
|
||||
std::vector<zx::rights>{zx::rights::SAME_RIGHTS}, &duplicate_tokens);
|
||||
if (duplicate_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to duplicate collection token: "
|
||||
<< zx_status_get_string(duplicate_status);
|
||||
return false;
|
||||
}
|
||||
if (duplicate_tokens.size() != 1u) {
|
||||
FML_LOG(ERROR) << "Failed to duplicate collection token: Incorrect number "
|
||||
"of tokens returned.";
|
||||
return false;
|
||||
}
|
||||
auto scenic_token = std::move(duplicate_tokens[0]);
|
||||
|
||||
// Register the sysmem token with flatland (or scenic's legacy gfx interface).
|
||||
//
|
||||
// This binds the sysmem token to a composition token, which is used later
|
||||
// to associate the rendering surface with a specific flatland Image.
|
||||
//
|
||||
// Under gfx, scenic uses an integral `buffer_id` instead of the composition
|
||||
// token.
|
||||
if (session_) {
|
||||
buffer_id_ = sNextBufferId++;
|
||||
session_->RegisterBufferCollection(buffer_id_, std::move(scenic_token));
|
||||
} else {
|
||||
fuchsia::ui::composition::BufferCollectionExportToken export_token;
|
||||
zx_status_t token_create_status =
|
||||
zx::eventpair::create(0, &export_token.value, &import_token_.value);
|
||||
if (token_create_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to create flatland export token: "
|
||||
<< zx_status_get_string(token_create_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
fuchsia::ui::composition::RegisterBufferCollectionArgs args;
|
||||
args.set_export_token(std::move(export_token));
|
||||
args.set_buffer_collection_token(std::move(scenic_token));
|
||||
args.set_usage(
|
||||
fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
|
||||
flatland_allocator->RegisterBufferCollection(
|
||||
std::move(args),
|
||||
[](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
|
||||
result) {
|
||||
if (result.is_err()) {
|
||||
FML_LOG(ERROR)
|
||||
<< "RegisterBufferCollection call to Scenic Allocator failed.";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Acquire flutter's local handle to the sysmem buffer.
|
||||
fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
|
||||
zx_status_t bind_status = sysmem_allocator->BindSharedCollection(
|
||||
std::move(local_token), buffer_collection.NewRequest());
|
||||
if (bind_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to bind collection token: "
|
||||
<< zx_status_get_string(bind_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set flutter's constraints on the sysmem buffer. Software rendering only
|
||||
// requires CPU access to the surface and a basic R8G8B8A8 pixel format.
|
||||
fuchsia::sysmem::BufferCollectionConstraints constraints;
|
||||
constraints.min_buffer_count = 1;
|
||||
constraints.usage.cpu =
|
||||
fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften;
|
||||
constraints.has_buffer_memory_constraints = true;
|
||||
constraints.buffer_memory_constraints.physically_contiguous_required = false;
|
||||
constraints.buffer_memory_constraints.secure_required = false;
|
||||
constraints.buffer_memory_constraints.ram_domain_supported = true;
|
||||
constraints.buffer_memory_constraints.cpu_domain_supported = true;
|
||||
constraints.buffer_memory_constraints.inaccessible_domain_supported = false;
|
||||
constraints.image_format_constraints_count = 1;
|
||||
fuchsia::sysmem::ImageFormatConstraints& image_constraints =
|
||||
constraints.image_format_constraints[0];
|
||||
image_constraints = fuchsia::sysmem::ImageFormatConstraints();
|
||||
image_constraints.min_coded_width = static_cast<uint32_t>(size.fWidth);
|
||||
image_constraints.min_coded_height = static_cast<uint32_t>(size.fHeight);
|
||||
image_constraints.min_bytes_per_row = static_cast<uint32_t>(size.fWidth) * 4;
|
||||
image_constraints.pixel_format.type =
|
||||
fuchsia::sysmem::PixelFormatType::R8G8B8A8;
|
||||
image_constraints.color_spaces_count = 1;
|
||||
image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB;
|
||||
image_constraints.pixel_format.has_format_modifier = true;
|
||||
image_constraints.pixel_format.format_modifier.value =
|
||||
fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
|
||||
zx_status_t set_constraints_status =
|
||||
buffer_collection->SetConstraints(true, constraints);
|
||||
if (set_constraints_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to set constraints: "
|
||||
<< zx_status_get_string(set_constraints_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for sysmem to allocate, now that constraints are set.
|
||||
fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info;
|
||||
zx_status_t allocation_status = ZX_OK;
|
||||
zx_status_t wait_for_allocated_status =
|
||||
buffer_collection->WaitForBuffersAllocated(&allocation_status,
|
||||
&buffer_collection_info);
|
||||
if (allocation_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to allocate: "
|
||||
<< zx_status_get_string(allocation_status);
|
||||
return false;
|
||||
}
|
||||
if (wait_for_allocated_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to wait for allocate: "
|
||||
<< zx_status_get_string(wait_for_allocated_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache the allocated surface VMO and metadata.
|
||||
FML_CHECK(buffer_collection_info.settings.buffer_settings.size_bytes != 0);
|
||||
FML_CHECK(buffer_collection_info.buffers[0].vmo != ZX_HANDLE_INVALID);
|
||||
surface_vmo_ = std::move(buffer_collection_info.buffers[0].vmo);
|
||||
surface_size_bytes_ =
|
||||
buffer_collection_info.settings.buffer_settings.size_bytes;
|
||||
if (buffer_collection_info.settings.buffer_settings.coherency_domain ==
|
||||
fuchsia::sysmem::CoherencyDomain::RAM) {
|
||||
// RAM coherency domain requires a cache clean when writes are finished.
|
||||
needs_cache_clean_ = true;
|
||||
}
|
||||
|
||||
// Map the allocated buffer to the CPU.
|
||||
uint8_t* vmo_base = nullptr;
|
||||
zx_status_t buffer_map_status = zx::vmar::root_self()->map(
|
||||
ZX_VM_PERM_WRITE | ZX_VM_PERM_READ, 0, surface_vmo_, 0,
|
||||
surface_size_bytes_, reinterpret_cast<uintptr_t*>(&vmo_base));
|
||||
if (buffer_map_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to map buffer memory: "
|
||||
<< zx_status_get_string(buffer_map_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now that the buffer is CPU-readable, it's safe to discard flutter's
|
||||
// connection to sysmem.
|
||||
zx_status_t close_status = buffer_collection->Close();
|
||||
if (close_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to close buffer: "
|
||||
<< zx_status_get_string(close_status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wrap the buffer in a software-rendered Skia surface.
|
||||
const uint64_t vmo_offset =
|
||||
buffer_collection_info.buffers[0].vmo_usable_start;
|
||||
const size_t vmo_stride =
|
||||
BytesPerRow(buffer_collection_info.settings, 4u, size.width());
|
||||
SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry);
|
||||
sk_surface_ = SkSurface::MakeRasterDirect(
|
||||
SkImageInfo::Make(size, kSkiaColorType, kPremul_SkAlphaType,
|
||||
SkColorSpace::MakeSRGB()),
|
||||
vmo_base + vmo_offset, vmo_stride, &sk_surface_props);
|
||||
if (!sk_surface_ || sk_surface_->getCanvas() == nullptr) {
|
||||
FML_LOG(ERROR) << "SkSurface::MakeRasterDirect failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoftwareSurface::SetImageId(uint32_t image_id) {
|
||||
FML_CHECK(image_id_ == 0);
|
||||
FML_CHECK(!session_);
|
||||
image_id_ = image_id;
|
||||
}
|
||||
|
||||
uint32_t SoftwareSurface::GetImageId() {
|
||||
return image_id_;
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> SoftwareSurface::GetSkiaSurface() const {
|
||||
return valid_ ? sk_surface_ : nullptr;
|
||||
}
|
||||
|
||||
fuchsia::ui::composition::BufferCollectionImportToken
|
||||
SoftwareSurface::GetBufferCollectionImportToken() {
|
||||
FML_CHECK(!session_);
|
||||
fuchsia::ui::composition::BufferCollectionImportToken import_dup;
|
||||
import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
|
||||
return import_dup;
|
||||
}
|
||||
|
||||
zx::event SoftwareSurface::GetAcquireFence() {
|
||||
FML_CHECK(!session_);
|
||||
zx::event fence;
|
||||
acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
|
||||
return fence;
|
||||
}
|
||||
|
||||
zx::event SoftwareSurface::GetReleaseFence() {
|
||||
FML_CHECK(!session_);
|
||||
zx::event fence;
|
||||
release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
|
||||
return fence;
|
||||
}
|
||||
void SoftwareSurface::SetReleaseImageCallback(
|
||||
ReleaseImageCallback release_image_callback) {
|
||||
FML_CHECK(!session_);
|
||||
release_image_callback_ = release_image_callback;
|
||||
}
|
||||
|
||||
size_t SoftwareSurface::AdvanceAndGetAge() {
|
||||
return ++age_;
|
||||
}
|
||||
|
||||
bool SoftwareSurface::FlushSessionAcquireAndReleaseEvents() {
|
||||
if (session_) {
|
||||
zx::event acquire, release;
|
||||
if (acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &acquire) != ZX_OK ||
|
||||
release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &release) != ZX_OK) {
|
||||
return false;
|
||||
}
|
||||
session_->EnqueueAcquireFence(std::move(acquire));
|
||||
session_->EnqueueReleaseFence(std::move(release));
|
||||
}
|
||||
|
||||
age_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoftwareSurface::SignalWritesFinished(
|
||||
const std::function<void(void)>& on_surface_read_finished) {
|
||||
FML_CHECK(on_surface_read_finished);
|
||||
|
||||
if (!valid_) {
|
||||
on_surface_read_finished();
|
||||
return;
|
||||
}
|
||||
|
||||
dart_utils::Check(surface_read_finished_callback_ == nullptr,
|
||||
"Attempted to signal a write on the surface when the "
|
||||
"previous write has not yet been acknowledged by the "
|
||||
"compositor.");
|
||||
surface_read_finished_callback_ = on_surface_read_finished;
|
||||
|
||||
// Sysmem *may* require the cache to be cleared after writes to the surface
|
||||
// are complete.
|
||||
if (needs_cache_clean_) {
|
||||
surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_,
|
||||
/*buffer*/ nullptr,
|
||||
/*buffer_size*/ 0);
|
||||
}
|
||||
|
||||
// Inform scenic that flutter is finished writing to the surface.
|
||||
zx_status_t signal_status = acquire_event_.signal(0u, ZX_EVENT_SIGNALED);
|
||||
if (signal_status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to signal acquire event; "
|
||||
<< zx_status_get_string(signal_status);
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareSurface::Reset() {
|
||||
if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
|
||||
release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
|
||||
valid_ = false;
|
||||
FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid.";
|
||||
}
|
||||
|
||||
wait_for_surface_read_finished_.Begin(async_get_default_dispatcher());
|
||||
|
||||
// It is safe for the caller to collect the surface in the callback.
|
||||
auto callback = surface_read_finished_callback_;
|
||||
surface_read_finished_callback_ = nullptr;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
|
||||
async::WaitBase* wait,
|
||||
zx_status_t status,
|
||||
const zx_packet_signal_t* signal) {
|
||||
if (status != ZX_OK) {
|
||||
return;
|
||||
}
|
||||
FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fuchsia/sysmem/cpp/fidl.h>
|
||||
#include <fuchsia/ui/composition/cpp/fidl.h>
|
||||
#include <lib/async/cpp/wait.h>
|
||||
#include <lib/ui/scenic/cpp/id.h>
|
||||
#include <lib/ui/scenic/cpp/resources.h>
|
||||
#include <lib/zx/event.h>
|
||||
#include <lib/zx/vmo.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
|
||||
#include "surface_producer.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
class SoftwareSurface final : public SurfaceProducerSurface {
|
||||
public:
|
||||
SoftwareSurface(fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
|
||||
fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
|
||||
scenic::Session* session,
|
||||
const SkISize& size);
|
||||
|
||||
~SoftwareSurface() override;
|
||||
|
||||
size_t GetAllocationSize() const { return surface_size_bytes_; }
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
size_t AdvanceAndGetAge() override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
bool FlushSessionAcquireAndReleaseEvents() override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
bool IsValid() const override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
SkISize GetSize() const override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
void SignalWritesFinished(
|
||||
const std::function<void(void)>& on_surface_read_finished) override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
void SetImageId(uint32_t image_id) override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
uint32_t GetImageId() override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
sk_sp<SkSurface> GetSkiaSurface() const override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
fuchsia::ui::composition::BufferCollectionImportToken
|
||||
GetBufferCollectionImportToken() override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
zx::event GetAcquireFence() override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
zx::event GetReleaseFence() override;
|
||||
|
||||
// |SurfaceProducerSurface|
|
||||
void SetReleaseImageCallback(
|
||||
ReleaseImageCallback release_image_callback) override;
|
||||
|
||||
private:
|
||||
void OnSurfaceReadFinished(async_dispatcher_t* dispatcher,
|
||||
async::WaitBase* wait,
|
||||
zx_status_t status,
|
||||
const zx_packet_signal_t* signal);
|
||||
|
||||
bool SetupSkiaSurface(
|
||||
fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
|
||||
fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
|
||||
const SkISize& size);
|
||||
|
||||
bool CreateFences();
|
||||
|
||||
void Reset();
|
||||
|
||||
static uint32_t sNextBufferId; // For legacy gfx; counter for `buffer_id_`.
|
||||
|
||||
scenic::Session* session_; // Legacy gfx API endpoint.
|
||||
scenic::ResourceId image_id_{0}; // Legacy gfx image ID.
|
||||
|
||||
sk_sp<SkSurface> sk_surface_;
|
||||
|
||||
// This is associated with `release_event_` and allows detection of when
|
||||
// scenic has finished reading from the surface (and thus it is safe to re-use
|
||||
// for writing).
|
||||
async::WaitMethod<SoftwareSurface, &SoftwareSurface::OnSurfaceReadFinished>
|
||||
wait_for_surface_read_finished_;
|
||||
// Called when scenic has finished reading from the surface, to allow
|
||||
// `SoftwareSurfaceProducer` to re-use the surface.
|
||||
std::function<void()> surface_read_finished_callback_;
|
||||
// Called when the surface is destroyed, to allow
|
||||
// `FlatlandExternalViewEmbedder` to release the associated Flatland image.
|
||||
ReleaseImageCallback release_image_callback_;
|
||||
|
||||
// Allows Flatland to associate this surface with a Flatland Image.
|
||||
fuchsia::ui::composition::BufferCollectionImportToken import_token_;
|
||||
uint32_t buffer_id_{0}; // Legacy gfx version of the import_token_.
|
||||
zx::event acquire_event_; // Signals to scenic that writing is finished.
|
||||
zx::event release_event_; // Signalled by scenic that reading is finished.
|
||||
zx::vmo surface_vmo_; // VMO that is backing the surface memory.
|
||||
|
||||
uint32_t surface_size_bytes_; // Size of the surface memory, in bytes.
|
||||
size_t age_{0}; // Number of frames since surface was last written to.
|
||||
|
||||
bool needs_cache_clean_{false};
|
||||
bool valid_{false};
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(SoftwareSurface);
|
||||
};
|
||||
|
||||
} // namespace flutter_runner
|
||||
@@ -0,0 +1,245 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "software_surface_producer.h"
|
||||
|
||||
#include <lib/fdio/directory.h>
|
||||
#include <lib/zx/process.h>
|
||||
|
||||
#include <algorithm> // Foor std::remove_if
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string GetCurrentProcessName() {
|
||||
char name[ZX_MAX_NAME_LEN];
|
||||
zx_status_t status =
|
||||
zx::process::self()->get_property(ZX_PROP_NAME, name, sizeof(name));
|
||||
if (status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to get process name for sysmem; using \"\".";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return std::string(name);
|
||||
}
|
||||
|
||||
zx_koid_t GetCurrentProcessId() {
|
||||
zx_info_handle_basic_t info;
|
||||
zx_status_t status = zx::process::self()->get_info(
|
||||
ZX_INFO_HANDLE_BASIC, &info, sizeof(info), /*actual_count*/ nullptr,
|
||||
/*avail_count*/ nullptr);
|
||||
if (status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Failed to get process ID for sysmem; using 0.";
|
||||
return ZX_KOID_INVALID;
|
||||
}
|
||||
|
||||
return info.koid;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SoftwareSurfaceProducer::SoftwareSurfaceProducer(
|
||||
scenic::Session* scenic_session)
|
||||
: scenic_session_(scenic_session) {
|
||||
zx_status_t status = fdio_service_connect(
|
||||
"/svc/fuchsia.sysmem.Allocator",
|
||||
sysmem_allocator_.NewRequest().TakeChannel().release());
|
||||
sysmem_allocator_->SetDebugClientInfo(GetCurrentProcessName(),
|
||||
GetCurrentProcessId());
|
||||
FML_DCHECK(status != ZX_OK);
|
||||
|
||||
if (!scenic_session_) {
|
||||
status = fdio_service_connect(
|
||||
"/svc/fuchsia.ui.composition.Allocator",
|
||||
flatland_allocator_.NewRequest().TakeChannel().release());
|
||||
FML_DCHECK(status != ZX_OK);
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
SoftwareSurfaceProducer::~SoftwareSurfaceProducer() = default;
|
||||
|
||||
std::unique_ptr<SurfaceProducerSurface>
|
||||
SoftwareSurfaceProducer::ProduceOffscreenSurface(const SkISize& size) {
|
||||
FML_CHECK(valid_);
|
||||
|
||||
return CreateSurface(size);
|
||||
}
|
||||
|
||||
std::unique_ptr<SurfaceProducerSurface> SoftwareSurfaceProducer::ProduceSurface(
|
||||
const SkISize& size) {
|
||||
TRACE_EVENT2("flutter", "SoftwareSurfacePool::ProduceSurface", "width",
|
||||
size.width(), "height", size.height());
|
||||
FML_CHECK(valid_);
|
||||
|
||||
std::unique_ptr<SurfaceProducerSurface> surface;
|
||||
auto exact_match_it =
|
||||
std::find_if(available_surfaces_.begin(), available_surfaces_.end(),
|
||||
[&size](const auto& surface) {
|
||||
return surface->IsValid() && surface->GetSize() == size;
|
||||
});
|
||||
if (exact_match_it != available_surfaces_.end()) {
|
||||
TRACE_EVENT_INSTANT0("flutter", "Exact match found");
|
||||
surface = std::move(*exact_match_it);
|
||||
available_surfaces_.erase(exact_match_it);
|
||||
} else {
|
||||
surface = CreateSurface(size);
|
||||
}
|
||||
|
||||
if (surface == nullptr) {
|
||||
FML_LOG(ERROR) << "Could not acquire surface.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!surface->FlushSessionAcquireAndReleaseEvents()) {
|
||||
FML_LOG(ERROR) << "Could not flush acquire/release events for buffer.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void SoftwareSurfaceProducer::SubmitSurfaces(
|
||||
std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) {
|
||||
TRACE_EVENT0("flutter", "SoftwareSurfaceProducer::SubmitSurfaces");
|
||||
|
||||
// Submit surface
|
||||
for (auto& surface : surfaces) {
|
||||
SubmitSurface(std::move(surface));
|
||||
}
|
||||
|
||||
// Buffer management.
|
||||
AgeAndCollectOldBuffers();
|
||||
}
|
||||
|
||||
void SoftwareSurfaceProducer::SubmitSurface(
|
||||
std::unique_ptr<SurfaceProducerSurface> surface) {
|
||||
TRACE_EVENT0("flutter", "SoftwareSurfacePool::SubmitSurface");
|
||||
FML_CHECK(valid_);
|
||||
|
||||
// This cast is safe because |SoftwareSurface| is the only implementation of
|
||||
// |SurfaceProducerSurface| for Flutter on Fuchsia. Additionally, it is
|
||||
// required, because we need to access |SoftwareSurface| specific information
|
||||
// of the surface (such as the amount of memory it contains).
|
||||
auto software_surface = std::unique_ptr<SoftwareSurface>(
|
||||
static_cast<SoftwareSurface*>(surface.release()));
|
||||
if (!software_surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t surface_key = reinterpret_cast<uintptr_t>(software_surface.get());
|
||||
auto insert_iterator = pending_surfaces_.insert(std::make_pair(
|
||||
surface_key, // key
|
||||
std::move(software_surface) // value
|
||||
));
|
||||
if (insert_iterator.second) {
|
||||
insert_iterator.first->second->SignalWritesFinished(std::bind(
|
||||
&SoftwareSurfaceProducer::RecyclePendingSurface, this, surface_key));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SoftwareSurface> SoftwareSurfaceProducer::CreateSurface(
|
||||
const SkISize& size) {
|
||||
TRACE_EVENT2("flutter", "SoftwareSurfacePool::CreateSurface", "width",
|
||||
size.width(), "height", size.height());
|
||||
auto surface = std::make_unique<SoftwareSurface>(
|
||||
sysmem_allocator_, flatland_allocator_, scenic_session_, size);
|
||||
if (!surface->IsValid()) {
|
||||
FML_LOG(ERROR) << "Created surface is invalid.";
|
||||
return nullptr;
|
||||
}
|
||||
trace_surfaces_created_++;
|
||||
return surface;
|
||||
}
|
||||
|
||||
void SoftwareSurfaceProducer::RecycleSurface(
|
||||
std::unique_ptr<SoftwareSurface> surface) {
|
||||
// The surface may have become invalid (for example if the fences could
|
||||
// not be reset).
|
||||
if (!surface->IsValid()) {
|
||||
FML_LOG(ERROR) << "Attempted to recycle invalid surface.";
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_EVENT0("flutter", "SoftwareSurfacePool::RecycleSurface");
|
||||
// Recycle the buffer by putting it in the list of available surfaces if we
|
||||
// have not reached the maximum amount of cached surfaces.
|
||||
if (available_surfaces_.size() < kMaxSurfaces) {
|
||||
available_surfaces_.push_back(std::move(surface));
|
||||
} else {
|
||||
TRACE_EVENT_INSTANT0("flutter", "Too many surfaces in pool, dropping");
|
||||
}
|
||||
TraceStats();
|
||||
}
|
||||
|
||||
void SoftwareSurfaceProducer::RecyclePendingSurface(uintptr_t surface_key) {
|
||||
// Before we do anything, we must clear the surface from the collection of
|
||||
// pending surfaces.
|
||||
auto found_in_pending = pending_surfaces_.find(surface_key);
|
||||
if (found_in_pending == pending_surfaces_.end()) {
|
||||
FML_LOG(ERROR) << "Attempted to recycle a surface that wasn't pending.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab a hold of the surface to recycle and clear the entry in the pending
|
||||
// surfaces collection.
|
||||
auto surface_to_recycle = std::move(found_in_pending->second);
|
||||
pending_surfaces_.erase(found_in_pending);
|
||||
|
||||
RecycleSurface(std::move(surface_to_recycle));
|
||||
}
|
||||
|
||||
void SoftwareSurfaceProducer::AgeAndCollectOldBuffers() {
|
||||
TRACE_EVENT0("flutter", "SoftwareSurfacePool::AgeAndCollectOldBuffers");
|
||||
|
||||
// Remove all surfaces that are no longer valid or are too old.
|
||||
size_t size_before = available_surfaces_.size();
|
||||
available_surfaces_.erase(
|
||||
std::remove_if(available_surfaces_.begin(), available_surfaces_.end(),
|
||||
[&](auto& surface) {
|
||||
return !surface->IsValid() ||
|
||||
surface->AdvanceAndGetAge() >= kMaxSurfaceAge;
|
||||
}),
|
||||
available_surfaces_.end());
|
||||
TRACE_EVENT1("flutter", "AgeAndCollect", "aged surfaces",
|
||||
(size_before - available_surfaces_.size()));
|
||||
|
||||
TraceStats();
|
||||
}
|
||||
|
||||
void SoftwareSurfaceProducer::TraceStats() {
|
||||
// Resources held in cached buffers.
|
||||
size_t cached_surfaces_bytes = 0;
|
||||
for (const auto& surface : available_surfaces_) {
|
||||
cached_surfaces_bytes += surface->GetAllocationSize();
|
||||
}
|
||||
|
||||
TRACE_COUNTER("flutter", "SurfacePoolCounts", 0u, "CachedCount",
|
||||
available_surfaces_.size(), //
|
||||
"Created", trace_surfaces_created_, //
|
||||
"Reused", trace_surfaces_reused_, //
|
||||
"PendingInCompositor", pending_surfaces_.size(), //
|
||||
"Retained", 0 //
|
||||
);
|
||||
|
||||
TRACE_COUNTER("flutter", "SurfacePoolBytes", 0u, //
|
||||
"CachedBytes", cached_surfaces_bytes, //
|
||||
"RetainedBytes", 0 //
|
||||
);
|
||||
|
||||
// Reset per present/frame stats.
|
||||
trace_surfaces_created_ = 0;
|
||||
trace_surfaces_reused_ = 0;
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fuchsia/sysmem/cpp/fidl.h>
|
||||
#include <fuchsia/ui/composition/cpp/fidl.h>
|
||||
#include <lib/ui/scenic/cpp/resources.h>
|
||||
#include <lib/ui/scenic/cpp/session.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
|
||||
#include "software_surface.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
class SoftwareSurfaceProducer final : public SurfaceProducer {
|
||||
public:
|
||||
// Only keep 12 surfaces at a time.
|
||||
static constexpr int kMaxSurfaces = 12;
|
||||
// If a surface doesn't get used for 3 or more generations, we discard it.
|
||||
static constexpr int kMaxSurfaceAge = 3;
|
||||
|
||||
explicit SoftwareSurfaceProducer(scenic::Session* scenic_session);
|
||||
~SoftwareSurfaceProducer() override;
|
||||
|
||||
bool IsValid() const { return valid_; }
|
||||
|
||||
// |SurfaceProducer|
|
||||
GrDirectContext* gr_context() const override { return nullptr; }
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceOffscreenSurface(
|
||||
const SkISize& size) override;
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
|
||||
const SkISize& size) override;
|
||||
|
||||
// |SurfaceProducer|
|
||||
void SubmitSurfaces(
|
||||
std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) override;
|
||||
|
||||
private:
|
||||
void SubmitSurface(std::unique_ptr<SurfaceProducerSurface> surface);
|
||||
std::unique_ptr<SoftwareSurface> CreateSurface(const SkISize& size);
|
||||
void RecycleSurface(std::unique_ptr<SoftwareSurface> surface);
|
||||
|
||||
void RecyclePendingSurface(uintptr_t surface_key);
|
||||
|
||||
void AgeAndCollectOldBuffers();
|
||||
|
||||
void TraceStats();
|
||||
|
||||
scenic::Session* scenic_session_; // Legacy gfx API endpoint.
|
||||
fuchsia::sysmem::AllocatorSyncPtr sysmem_allocator_;
|
||||
fuchsia::ui::composition::AllocatorPtr flatland_allocator_;
|
||||
|
||||
// These surfaces are available for re-use.
|
||||
std::vector<std::unique_ptr<SoftwareSurface>> available_surfaces_;
|
||||
// These surfaces have been written to, but scenic is not finished reading
|
||||
// from them yet.
|
||||
std::unordered_map<uintptr_t, std::unique_ptr<SoftwareSurface>>
|
||||
pending_surfaces_;
|
||||
|
||||
size_t trace_surfaces_created_ = 0;
|
||||
size_t trace_surfaces_reused_ = 0;
|
||||
|
||||
bool valid_ = false;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(SoftwareSurfaceProducer);
|
||||
};
|
||||
|
||||
} // namespace flutter_runner
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
@@ -66,6 +67,10 @@ class SurfaceProducer {
|
||||
public:
|
||||
virtual ~SurfaceProducer() = default;
|
||||
|
||||
virtual GrDirectContext* gr_context() const = 0;
|
||||
|
||||
virtual std::unique_ptr<SurfaceProducerSurface> ProduceOffscreenSurface(
|
||||
const SkISize& size) = 0;
|
||||
virtual std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
|
||||
const SkISize& size) = 0;
|
||||
|
||||
|
||||
@@ -6,22 +6,23 @@
|
||||
#include <lib/async-loop/default.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
|
||||
#include "assets/directory_asset_bundle.h"
|
||||
#include "flutter/assets/directory_asset_bundle.h"
|
||||
#include "flutter/common/graphics/persistent_cache.h"
|
||||
#include "flutter/fml/memory/ref_ptr.h"
|
||||
#include "flutter/fml/message_loop_impl.h"
|
||||
#include "flutter/fml/task_runner.h"
|
||||
#include "flutter/shell/common/serialization_callbacks.h"
|
||||
#include "flutter/shell/common/thread_host.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/gfx_session_connection.h"
|
||||
#include "third_party/skia/include/core/SkPicture.h"
|
||||
#include "third_party/skia/include/core/SkPictureRecorder.h"
|
||||
#include "third_party/skia/include/core/SkSerialProcs.h"
|
||||
|
||||
#include "flutter/shell/platform/fuchsia/flutter/logging.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/runner.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "include/core/SkPicture.h"
|
||||
#include "include/core/SkPictureRecorder.h"
|
||||
#include "include/core/SkSerialProcs.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace flutter_runner;
|
||||
using namespace flutter;
|
||||
|
||||
namespace flutter_runner {
|
||||
@@ -74,17 +75,17 @@ class EngineTest : public ::testing::Test {
|
||||
scenic_ = context_->svc()->Connect<fuchsia::ui::scenic::Scenic>();
|
||||
session_.emplace(scenic_.get());
|
||||
surface_producer_ =
|
||||
std::make_unique<VulkanSurfaceProducer>(&session_.value());
|
||||
std::make_shared<VulkanSurfaceProducer>(&session_.value());
|
||||
|
||||
Engine::WarmupSkps(&concurrent_task_runner_, &raster_task_runner_,
|
||||
*surface_producer_, width, height, asset_manager,
|
||||
std::nullopt, std::nullopt);
|
||||
surface_producer_, SkISize::Make(width, height),
|
||||
asset_manager, std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
protected:
|
||||
MockTaskRunner concurrent_task_runner_;
|
||||
MockTaskRunner raster_task_runner_;
|
||||
std::unique_ptr<VulkanSurfaceProducer> surface_producer_;
|
||||
std::shared_ptr<VulkanSurfaceProducer> surface_producer_;
|
||||
|
||||
std::unique_ptr<sys::ComponentContext> context_;
|
||||
fuchsia::ui::scenic::ScenicPtr scenic_;
|
||||
|
||||
@@ -128,6 +128,16 @@ class FakeSurfaceProducer : public SurfaceProducer {
|
||||
: flatland_allocator_(flatland_allocator.Bind()) {}
|
||||
~FakeSurfaceProducer() override = default;
|
||||
|
||||
// |SurfaceProducer|
|
||||
GrDirectContext* gr_context() const override { return nullptr; }
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceOffscreenSurface(
|
||||
const SkISize& size) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
|
||||
const SkISize& size) override {
|
||||
auto [buffer_export_token, buffer_import_token] =
|
||||
@@ -156,6 +166,7 @@ class FakeSurfaceProducer : public SurfaceProducer {
|
||||
std::move(sysmem_token_request), std::move(buffer_import_token), size);
|
||||
}
|
||||
|
||||
// |SurfaceProducer|
|
||||
void SubmitSurfaces(
|
||||
std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) override {}
|
||||
|
||||
@@ -305,19 +316,22 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test {
|
||||
protected:
|
||||
FlatlandExternalViewEmbedderTest()
|
||||
: session_subloop_(loop_.StartNewLoop()),
|
||||
fake_surface_producer_(CreateFlatlandAllocator()),
|
||||
flatland_connection_(CreateFlatlandConnection()) {}
|
||||
flatland_connection_(CreateFlatlandConnection()),
|
||||
fake_surface_producer_(
|
||||
std::make_shared<FakeSurfaceProducer>(CreateFlatlandAllocator())) {}
|
||||
~FlatlandExternalViewEmbedderTest() override = default;
|
||||
|
||||
async::TestLoop& loop() { return loop_; }
|
||||
|
||||
FakeSurfaceProducer& fake_surface_producer() {
|
||||
std::shared_ptr<FakeSurfaceProducer> fake_surface_producer() {
|
||||
return fake_surface_producer_;
|
||||
}
|
||||
|
||||
FakeFlatland& fake_flatland() { return fake_flatland_; }
|
||||
|
||||
FlatlandConnection& flatland_connection() { return flatland_connection_; }
|
||||
std::shared_ptr<FlatlandConnection> flatland_connection() {
|
||||
return flatland_connection_;
|
||||
}
|
||||
|
||||
private:
|
||||
fuchsia::ui::composition::AllocatorHandle CreateFlatlandAllocator() {
|
||||
@@ -328,14 +342,14 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test {
|
||||
return flatland_allocator;
|
||||
}
|
||||
|
||||
FlatlandConnection CreateFlatlandConnection() {
|
||||
std::shared_ptr<FlatlandConnection> CreateFlatlandConnection() {
|
||||
FML_CHECK(!fake_flatland_.is_flatland_connected());
|
||||
fuchsia::ui::composition::FlatlandHandle flatland =
|
||||
fake_flatland_.ConnectFlatland(session_subloop_->dispatcher());
|
||||
|
||||
auto test_name =
|
||||
::testing::UnitTest::GetInstance()->current_test_info()->name();
|
||||
return FlatlandConnection(
|
||||
return std::make_shared<FlatlandConnection>(
|
||||
std::move(test_name), std::move(flatland), []() { FAIL(); },
|
||||
[](auto...) {}, 1, fml::TimeDelta::Zero());
|
||||
}
|
||||
@@ -349,9 +363,9 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test {
|
||||
std::unique_ptr<async::LoopInterface> session_subloop_;
|
||||
|
||||
FakeFlatland fake_flatland_;
|
||||
FakeSurfaceProducer fake_surface_producer_;
|
||||
|
||||
FlatlandConnection flatland_connection_;
|
||||
std::shared_ptr<FlatlandConnection> flatland_connection_;
|
||||
std::shared_ptr<FakeSurfaceProducer> fake_surface_producer_;
|
||||
};
|
||||
|
||||
TEST_F(FlatlandExternalViewEmbedderTest, RootScene) {
|
||||
@@ -382,7 +396,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, RootScene) {
|
||||
EXPECT_THAT(fake_flatland().graph(), IsEmptyGraph());
|
||||
|
||||
// Pump the loop; the contents of the initial `Present` should be processed.
|
||||
flatland_connection().Present();
|
||||
flatland_connection()->Present();
|
||||
loop().RunUntilIdle();
|
||||
EXPECT_THAT(fake_flatland().graph(),
|
||||
IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
|
||||
@@ -411,7 +425,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, SimpleScene) {
|
||||
fuchsia::ui::composition::ViewBoundProtocols{},
|
||||
parent_viewport_watcher.NewRequest(), flatland_connection(),
|
||||
fake_surface_producer());
|
||||
flatland_connection().Present();
|
||||
flatland_connection()->Present();
|
||||
loop().RunUntilIdle();
|
||||
fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
|
||||
loop().RunUntilIdle();
|
||||
@@ -471,7 +485,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) {
|
||||
fuchsia::ui::composition::ViewBoundProtocols{},
|
||||
parent_viewport_watcher.NewRequest(), flatland_connection(),
|
||||
fake_surface_producer());
|
||||
flatland_connection().Present();
|
||||
flatland_connection()->Present();
|
||||
loop().RunUntilIdle();
|
||||
fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
|
||||
loop().RunUntilIdle();
|
||||
|
||||
@@ -57,25 +57,26 @@ namespace {
|
||||
|
||||
class FakeSurfaceProducerSurface : public SurfaceProducerSurface {
|
||||
public:
|
||||
explicit FakeSurfaceProducerSurface(scenic::Session& session,
|
||||
explicit FakeSurfaceProducerSurface(scenic::Session* session,
|
||||
const SkISize& size,
|
||||
uint32_t buffer_id)
|
||||
: session_(session),
|
||||
surface_(SkSurface::MakeNull(size.width(), size.height())),
|
||||
image_id_(session_.AllocResourceId()),
|
||||
buffer_id_(buffer_id) {
|
||||
FML_CHECK(session_);
|
||||
FML_CHECK(buffer_id_ != 0);
|
||||
|
||||
fuchsia::sysmem::BufferCollectionTokenSyncPtr token;
|
||||
buffer_binding_ = token.NewRequest();
|
||||
|
||||
session_.RegisterBufferCollection(buffer_id_, std::move(token));
|
||||
session_.Enqueue(scenic::NewCreateImage2Cmd(
|
||||
image_id_ = session_->AllocResourceId();
|
||||
session_->RegisterBufferCollection(buffer_id_, std::move(token));
|
||||
session_->Enqueue(scenic::NewCreateImage2Cmd(
|
||||
image_id_, surface_->width(), surface_->height(), buffer_id_, 0));
|
||||
}
|
||||
~FakeSurfaceProducerSurface() override {
|
||||
session_.DeregisterBufferCollection(buffer_id_);
|
||||
session_.Enqueue(scenic::NewReleaseResourceCmd(image_id_));
|
||||
session_->DeregisterBufferCollection(buffer_id_);
|
||||
session_->Enqueue(scenic::NewReleaseResourceCmd(image_id_));
|
||||
}
|
||||
|
||||
bool IsValid() const override { return true; }
|
||||
@@ -107,7 +108,7 @@ class FakeSurfaceProducerSurface : public SurfaceProducerSurface {
|
||||
const std::function<void(void)>& on_writes_committed) override {}
|
||||
|
||||
private:
|
||||
scenic::Session& session_;
|
||||
scenic::Session* session_;
|
||||
|
||||
sk_sp<SkSurface> surface_;
|
||||
|
||||
@@ -119,20 +120,31 @@ class FakeSurfaceProducerSurface : public SurfaceProducerSurface {
|
||||
|
||||
class FakeSurfaceProducer : public SurfaceProducer {
|
||||
public:
|
||||
explicit FakeSurfaceProducer(scenic::Session& session) : session_(session) {}
|
||||
explicit FakeSurfaceProducer(scenic::Session* session) : session_(session) {}
|
||||
~FakeSurfaceProducer() override = default;
|
||||
|
||||
// |SurfaceProducer|
|
||||
GrDirectContext* gr_context() const override { return nullptr; }
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceOffscreenSurface(
|
||||
const SkISize& size) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
|
||||
const SkISize& size) override {
|
||||
return std::make_unique<FakeSurfaceProducerSurface>(session_, size,
|
||||
buffer_id_++);
|
||||
}
|
||||
|
||||
// |SurfaceProducer|
|
||||
void SubmitSurfaces(
|
||||
std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) override {}
|
||||
|
||||
private:
|
||||
scenic::Session& session_;
|
||||
scenic::Session* session_;
|
||||
|
||||
uint32_t buffer_id_{1};
|
||||
};
|
||||
@@ -458,21 +470,25 @@ class GfxExternalViewEmbedderTest
|
||||
public fuchsia::ui::scenic::SessionListener {
|
||||
protected:
|
||||
GfxExternalViewEmbedderTest()
|
||||
: session_listener_(this),
|
||||
session_subloop_(loop_.StartNewLoop()),
|
||||
: session_subloop_(loop_.StartNewLoop()),
|
||||
session_listener_(this),
|
||||
session_connection_(CreateSessionConnection()),
|
||||
fake_surface_producer_(*session_connection_.get()) {}
|
||||
fake_surface_producer_(
|
||||
std::make_shared<FakeSurfaceProducer>(session_connection_->get())) {
|
||||
}
|
||||
~GfxExternalViewEmbedderTest() override = default;
|
||||
|
||||
async::TestLoop& loop() { return loop_; }
|
||||
|
||||
FakeSession& fake_session() { return fake_session_; }
|
||||
|
||||
FakeSurfaceProducer& fake_surface_producer() {
|
||||
std::shared_ptr<FakeSurfaceProducer> fake_surface_producer() {
|
||||
return fake_surface_producer_;
|
||||
}
|
||||
|
||||
GfxSessionConnection& session_connection() { return session_connection_; }
|
||||
std::shared_ptr<GfxSessionConnection> session_connection() {
|
||||
return session_connection_;
|
||||
}
|
||||
|
||||
private:
|
||||
// |fuchsia::ui::scenic::SessionListener|
|
||||
@@ -483,7 +499,7 @@ class GfxExternalViewEmbedderTest
|
||||
FAIL();
|
||||
}
|
||||
|
||||
GfxSessionConnection CreateSessionConnection() {
|
||||
std::shared_ptr<GfxSessionConnection> CreateSessionConnection() {
|
||||
FML_CHECK(!fake_session_.is_bound());
|
||||
FML_CHECK(!session_listener_.is_bound());
|
||||
|
||||
@@ -494,22 +510,22 @@ class GfxExternalViewEmbedderTest
|
||||
fake_session_.Bind(session_subloop_->dispatcher());
|
||||
session_listener_.Bind(std::move(session_listener));
|
||||
|
||||
return GfxSessionConnection(
|
||||
return std::make_shared<GfxSessionConnection>(
|
||||
GetCurrentTestName(), std::move(inspect_node), std::move(session),
|
||||
[]() { FAIL(); }, [](auto...) {}, 1, fml::TimeDelta::Zero());
|
||||
}
|
||||
|
||||
async::TestLoop loop_; // Must come before FIDL bindings.
|
||||
|
||||
inspect::Inspector inspector_;
|
||||
std::unique_ptr<async::LoopInterface> session_subloop_;
|
||||
|
||||
fidl::Binding<fuchsia::ui::scenic::SessionListener> session_listener_;
|
||||
|
||||
std::unique_ptr<async::LoopInterface> session_subloop_;
|
||||
FakeSession fake_session_;
|
||||
GfxSessionConnection session_connection_;
|
||||
inspect::Inspector inspector_;
|
||||
|
||||
FakeSurfaceProducer fake_surface_producer_;
|
||||
FakeSession fake_session_;
|
||||
|
||||
std::shared_ptr<GfxSessionConnection> session_connection_;
|
||||
std::shared_ptr<FakeSurfaceProducer> fake_surface_producer_;
|
||||
};
|
||||
|
||||
TEST_F(GfxExternalViewEmbedderTest, RootScene) {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
|
||||
#include "third_party/skia/include/gpu/GrBackendSurface.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
#include "third_party/skia/include/gpu/vk/GrVkBackendContext.h"
|
||||
#include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
|
||||
#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <lib/async/cpp/time.h>
|
||||
#include <lib/async/default.h>
|
||||
#include <lib/syslog/global.h>
|
||||
#include <lib/ui/scenic/cpp/resources.h>
|
||||
#include <lib/ui/scenic/cpp/session.h>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
@@ -14,8 +16,8 @@
|
||||
#include "flutter/vulkan/vulkan_device.h"
|
||||
#include "flutter/vulkan/vulkan_proc_table.h"
|
||||
#include "flutter/vulkan/vulkan_provider.h"
|
||||
#include "lib/ui/scenic/cpp/resources.h"
|
||||
#include "lib/ui/scenic/cpp/session.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "vulkan_surface.h"
|
||||
#include "vulkan_surface_pool.h"
|
||||
@@ -30,10 +32,12 @@ class VulkanSurfaceProducer final : public SurfaceProducer,
|
||||
|
||||
bool IsValid() const { return valid_; }
|
||||
|
||||
GrDirectContext* gr_context() const { return context_.get(); }
|
||||
// |SurfaceProducer|
|
||||
GrDirectContext* gr_context() const override { return context_.get(); }
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceOffscreenSurface(
|
||||
const SkISize& size);
|
||||
const SkISize& size) override;
|
||||
|
||||
// |SurfaceProducer|
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
|
||||
|
||||
Reference in New Issue
Block a user