diff --git a/content_handler/vulkan_rasterizer.cc b/content_handler/vulkan_rasterizer.cc index a7ebed68bc3f7..5f3aa1c9e396f 100644 --- a/content_handler/vulkan_rasterizer.cc +++ b/content_handler/vulkan_rasterizer.cc @@ -7,6 +7,9 @@ #include #include +#include +#include + #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/skia/include/gpu/vk/GrVkTypes.h" #include "third_party/skia/src/gpu/vk/GrVkUtil.h" @@ -16,21 +19,12 @@ namespace flutter_runner { VulkanRasterizer::VulkanSurfaceProducer::VulkanSurfaceProducer() { valid_ = Initialize(); if (!valid_) - FTL_DLOG(INFO) << "VulkanSurfaceProducer failed to initialize"; + FTL_LOG(ERROR) << "VulkanSurfaceProducer failed to initialize"; } -sk_sp VulkanRasterizer::VulkanSurfaceProducer::ProduceSurface( - SkISize size, - mozart::ImagePtr* out_image) { - if (size.isEmpty()) { - FTL_DLOG(INFO) << "Attempting to create surface with empty size"; - return nullptr; - } - - // these casts are safe because of the early out on frame_size.isEmpty() - auto width = static_cast(size.width()); - auto height = static_cast(size.height()); - +std::unique_ptr +VulkanRasterizer::VulkanSurfaceProducer::CreateSurface(uint32_t width, + uint32_t height) { VkResult vk_result; VkImageCreateInfo image_create_info = { @@ -109,7 +103,7 @@ sk_sp VulkanRasterizer::VulkanSurfaceProducer::ProduceSurface( auto sk_surface = SkSurface::MakeFromBackendRenderTarget( context_.get(), desc, SkColorSpace::MakeSRGB(), &props); if (!sk_surface) { - FTL_DLOG(INFO) << "MakeFromBackendRenderTarget Failed"; + FTL_LOG(ERROR) << "MakeFromBackendRenderTarget Failed"; return nullptr; } @@ -119,34 +113,71 @@ sk_sp VulkanRasterizer::VulkanSurfaceProducer::ProduceSurface( if (vk_result) return nullptr; + mx::vmo vmo(vmo_handle); + + size_t vmo_size; + vmo.get_size(&vmo_size); + + FTL_DCHECK(vmo_size >= memory_reqs.size); + mx::eventpair retention_events[2]; auto mx_status = mx::eventpair::create(0, &retention_events[0], &retention_events[1]); if (mx_status) { - FTL_DLOG(INFO) << "Failed to create retention eventpair"; + FTL_LOG(ERROR) << "Failed to create retention eventpair"; return nullptr; } if (!sk_surface || sk_surface->getCanvas() == nullptr) { - FTL_DLOG(INFO) << "surface invalid"; + FTL_LOG(ERROR) << "surface invalid"; return nullptr; } - surfaces_.emplace_back( - sk_surface, // sk_sp sk_surface - mx::vmo(vmo_handle), // mx::vmo vmo - std::move(retention_events[0]), // mx::eventpair local_retention_event; - std::move(retention_events[1]), // mx::eventpair remote_retention_event; - mx::eventpair(), // mx::eventpair fence_event - vk_image, vk_memory); + return std::make_unique(backend_context_, sk_surface, std::move(vmo), + std::move(retention_events[0]), + std::move(retention_events[1]), vk_image, + vk_memory); +} - auto surface = &surfaces_.back(); +sk_sp VulkanRasterizer::VulkanSurfaceProducer::ProduceSurface( + SkISize size, + mozart::ImagePtr* out_image) { + if (size.isEmpty()) { + FTL_LOG(ERROR) << "Attempting to create surface with empty size"; + return nullptr; + } - size_t vmo_size; - surface->vmo.get_size(&vmo_size); + // these casts are safe because of the early out on frame_size.isEmpty() + auto width = static_cast(size.width()); + auto height = static_cast(size.height()); + + std::unique_ptr surface; + // try and find a Swapchain with surfaces of the right size + auto it = available_surfaces_.find(MakeSizeKey(width, height)); + if (it == available_surfaces_.end()) { + // No matching Swapchain exists, create a new surfaces + surface = CreateSurface(width, height); + } else { + auto& swapchain = it->second; + if (swapchain.queue.size() == 0) { + // matching Swapchain exists, but does not have any buffers available in + // it + surface = CreateSurface(width, height); + } else { + surface = std::move(swapchain.queue.front()); + swapchain.queue.pop(); + swapchain.tick_count = 0; + + // Need to do some skia foo here to clear all the canvas state from the + // last frame + surface->sk_surface->getCanvas()->restoreToCount(0); + surface->sk_surface->getCanvas()->save(); + surface->sk_surface->getCanvas()->resetMatrix(); + } + } - if (vmo_size < memory_reqs.size) { - FTL_DLOG(INFO) << "Failed to allocate sufficiently large vmo"; + if (!surface) { + FTL_LOG(ERROR) << "Failed to produce surface"; return nullptr; } @@ -154,22 +185,27 @@ sk_sp VulkanRasterizer::VulkanSurfaceProducer::ProduceSurface( auto buffer = mozart::Buffer::New(); status = surface->vmo.duplicate(MX_RIGHT_SAME_RIGHTS, &buffer->vmo); if (status) { - FTL_DLOG(INFO) << "failed to duplicate vmo"; + FTL_LOG(ERROR) << "failed to duplicate vmo"; return nullptr; } buffer->memory_type = mozart::Buffer::MemoryType::VK_DEVICE_MEMORY; - status = mx::eventpair::create(0, &surface->fence_event, &buffer->fence); + mx::eventpair fence_event; + status = mx::eventpair::create(0, &fence_event, &buffer->fence); if (status) { - FTL_DLOG(INFO) << "failed to create fence eventpair"; + FTL_LOG(ERROR) << "failed to create fence eventpair"; return nullptr; } + mtl::MessageLoop::HandlerKey handler_key = + mtl::MessageLoop::GetCurrent()->AddHandler(this, fence_event.get(), + MX_EPAIR_PEER_CLOSED); + status = surface->remote_retention_event.duplicate(MX_RIGHT_SAME_RIGHTS, &buffer->retention); if (status) { - FTL_DLOG(INFO) << "failed to duplicate retention eventpair"; + FTL_LOG(ERROR) << "failed to duplicate retention eventpair"; return nullptr; } @@ -184,35 +220,85 @@ sk_sp VulkanRasterizer::VulkanSurfaceProducer::ProduceSurface( image->buffer = std::move(buffer); *out_image = std::move(image); + auto sk_surface = surface->sk_surface; + PendingSurfaceInfo info; + info.handler_key = handler_key; + info.surface = std::move(surface); + info.production_fence = std::move(fence_event); + outstanding_surfaces_.push_back(std::move(info)); + return sk_surface; } -bool VulkanRasterizer::VulkanSurfaceProducer::Tick() { +bool VulkanRasterizer::VulkanSurfaceProducer::FinishFrame() { mx_status_t status; - for (auto& surface : surfaces_) { - GrVkImageInfo* image_info = nullptr; - if (!surface.sk_surface->getRenderTargetHandle( - reinterpret_cast(&image_info), - SkSurface::kFlushRead_BackendHandleAccess)) { - FTL_DLOG(INFO) << "Could not get render target handle."; - return false; - } - (void)image_info; + // Finish Rendering + context_->flush(); + VkResult result = + VK_CALL_LOG_ERROR(vkQueueWaitIdle(backend_context_->fQueue)); + if (result) + return false; - status = surface.fence_event.signal_peer(0u, MX_EPAIR_SIGNALED); + for (auto& info : outstanding_surfaces_) { + // info.surface->sk_surface->prepareForExternalIO(); + // Signal the compositor + status = info.production_fence.signal_peer(0u, MX_EPAIR_SIGNALED); if (status) { - FTL_DLOG(INFO) << "failed to signal fence event"; + FTL_LOG(ERROR) << "failed to signal fence event"; return false; } - vkFreeMemory(backend_context_->fDevice, surface.vk_memory, NULL); + pending_surfaces_.insert( + std::make_pair(info.production_fence.get(), std::move(info))); + } + outstanding_surfaces_.clear(); + return true; +} - vkDestroyImage(backend_context_->fDevice, surface.vk_image, NULL); +void VulkanRasterizer::VulkanSurfaceProducer::Tick() { + for (auto it = available_surfaces_.begin(); + it != available_surfaces_.end();) { + auto& swapchain = it->second; + swapchain.tick_count++; + if (swapchain.tick_count > Swapchain::kMaxTickBeforeDiscard) + it = available_surfaces_.erase(it); + else + it++; } +} - surfaces_.clear(); - return true; +void VulkanRasterizer::VulkanSurfaceProducer::OnHandleReady( + mx_handle_t handle, + mx_signals_t pending) { + FTL_DCHECK(pending & MX_EPAIR_PEER_CLOSED); + + auto it = pending_surfaces_.find(handle); + FTL_DCHECK(it != pending_surfaces_.end()); + + // Add the newly available buffer to the swapchain. + PendingSurfaceInfo& info = it->second; + mtl::MessageLoop::GetCurrent()->RemoveHandler(info.handler_key); + + // try and find a Swapchain with surfaces of the right size + size_key_t key = MakeSizeKey(info.surface->sk_surface->width(), + info.surface->sk_surface->height()); + auto swapchain_it = available_surfaces_.find(key); + if (swapchain_it == available_surfaces_.end()) { + // No matching Swapchain exists, create one + Swapchain swapchain; + if (swapchain.queue.size() + 1 <= Swapchain::kMaxSurfaces) { + swapchain.queue.push(std::move(info.surface)); + } + available_surfaces_.insert(std::make_pair(key, std::move(swapchain))); + } else { + auto& swapchain = swapchain_it->second; + if (swapchain.queue.size() + 1 <= Swapchain::kMaxSurfaces) { + swapchain.queue.push(std::move(info.surface)); + } + } + + pending_surfaces_.erase(it); } bool VulkanRasterizer::VulkanSurfaceProducer::Initialize() { @@ -225,7 +311,7 @@ bool VulkanRasterizer::VulkanSurfaceProducer::Initialize() { if (!application_->IsValid() || !vk->AreInstanceProcsSetup()) { // Make certain the application instance was created and it setup the // instance proc table entries. - FTL_DLOG(INFO) << "Instance proc addresses have not been setup."; + FTL_LOG(ERROR) << "Instance proc addresses have not been setup."; return false; } @@ -237,30 +323,30 @@ bool VulkanRasterizer::VulkanSurfaceProducer::Initialize() { !vk->AreDeviceProcsSetup()) { // Make certain the device was created and it setup the device proc table // entries. - FTL_DLOG(INFO) << "Device proc addresses have not been setup."; + FTL_LOG(ERROR) << "Device proc addresses have not been setup."; return false; } if (!vk->HasAcquiredMandatoryProcAddresses()) { - FTL_DLOG(INFO) << "Failed to acquire mandatory proc addresses"; + FTL_LOG(ERROR) << "Failed to acquire mandatory proc addresses"; return false; } if (!vk->IsValid()) { - FTL_DLOG(INFO) << "VulkanProcTable invalid"; + FTL_LOG(ERROR) << "VulkanProcTable invalid"; return false; } auto interface = vk->CreateSkiaInterface(); if (interface == nullptr || !interface->validate(0)) { - FTL_DLOG(INFO) << "interface invalid"; + FTL_LOG(ERROR) << "interface invalid"; return false; } uint32_t skia_features = 0; if (!logical_device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) { - FTL_DLOG(INFO) << "Failed to get physical device features"; + FTL_LOG(ERROR) << "Failed to get physical device features"; return false; } @@ -281,20 +367,26 @@ bool VulkanRasterizer::VulkanSurfaceProducer::Initialize() { kVulkan_GrBackend, reinterpret_cast(backend_context_.get()))); + FTL_DLOG(INFO) << "Successfully initialized VulkanRasterizer"; return true; } -VulkanRasterizer::VulkanRasterizer() : compositor_context_(nullptr) {} +VulkanRasterizer::VulkanRasterizer() : compositor_context_(nullptr) { + // todo(SY-88) We need to not create this surface producer until + // the graphics driver finishes coming up. Not sure where thats going to + // happen eventually but for now we paper over the race by sleeping for 10 ms + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + surface_producer_.reset(new VulkanSurfaceProducer()); +} VulkanRasterizer::~VulkanRasterizer() = default; bool VulkanRasterizer::IsValid() const { - return !surface_producer_ || surface_producer_->IsValid(); + return surface_producer_ && surface_producer_->IsValid(); } void VulkanRasterizer::SetScene(fidl::InterfaceHandle scene) { scene_.Bind(std::move(scene)); - surface_producer_.reset(new VulkanSurfaceProducer()); } void VulkanRasterizer::Draw(std::unique_ptr layer_tree, @@ -305,12 +397,12 @@ void VulkanRasterizer::Draw(std::unique_ptr layer_tree, bool VulkanRasterizer::Draw(std::unique_ptr layer_tree) { if (layer_tree == nullptr) { - FTL_DLOG(INFO) << "Layer tree was not valid."; + FTL_LOG(ERROR) << "Layer tree was not valid."; return false; } if (!scene_) { - FTL_DLOG(INFO) << "Scene was not valid."; + FTL_LOG(ERROR) << "Scene was not valid."; return false; } @@ -329,7 +421,7 @@ bool VulkanRasterizer::Draw(std::unique_ptr layer_tree) { auto metadata = mozart::SceneMetadata::New(); metadata->version = layer_tree->scene_version(); scene_->Publish(std::move(metadata)); - FTL_DLOG(INFO) << "Publishing empty node"; + FTL_LOG(ERROR) << "Publishing empty node"; return false; } @@ -355,10 +447,11 @@ bool VulkanRasterizer::Draw(std::unique_ptr layer_tree) { // Draw the contents of the scene to a surface. // We do this after publishing to take advantage of pipelining. context.ExecutePaintTasks(frame); - if (!surface_producer_->Tick()) { - FTL_DLOG(INFO) << "Failed to tick surface producer"; + if (!surface_producer_->FinishFrame()) { + FTL_LOG(ERROR) << "Failed to Finish Frame"; return false; } + surface_producer_->Tick(); return true; } diff --git a/content_handler/vulkan_rasterizer.h b/content_handler/vulkan_rasterizer.h index 6a0f389dfb251..4f16adeffa92d 100644 --- a/content_handler/vulkan_rasterizer.h +++ b/content_handler/vulkan_rasterizer.h @@ -6,6 +6,9 @@ #define FLUTTER_CONTENT_HANDLER_VULKAN_RASTERIZER_H_ #include +#include +#include +#include #include "apps/mozart/services/buffers/cpp/buffer_producer.h" #include "flutter/content_handler/rasterizer.h" @@ -32,40 +35,90 @@ class VulkanRasterizer : public Rasterizer { private: class VulkanSurfaceProducer - : public flow::SceneUpdateContext::SurfaceProducer { + : public flow::SceneUpdateContext::SurfaceProducer, + private mtl::MessageLoopHandler { public: VulkanSurfaceProducer(); sk_sp ProduceSurface(SkISize size, mozart::ImagePtr* out_image) override; - bool Tick(); + + void Tick(); + bool FinishFrame(); bool IsValid() const { return valid_; } private: + // |mtl::MessageLoopHandler| + void OnHandleReady(mx_handle_t handle, mx_signals_t pending) override; + struct Surface { + sk_sp backend_context; sk_sp sk_surface; mx::vmo vmo; mx::eventpair local_retention_event; mx::eventpair remote_retention_event; - mx::eventpair fence_event; VkImage vk_image; VkDeviceMemory vk_memory; - Surface(sk_sp sk_surface, + Surface(sk_sp backend_context, + sk_sp sk_surface, mx::vmo vmo, mx::eventpair local_retention_event, mx::eventpair remote_retention_event, - mx::eventpair fence_event, VkImage vk_image, VkDeviceMemory vk_memory) - : sk_surface(std::move(sk_surface)), + : backend_context(std::move(backend_context)), + sk_surface(std::move(sk_surface)), vmo(std::move(vmo)), local_retention_event(std::move(local_retention_event)), remote_retention_event(std::move(remote_retention_event)), - fence_event(std::move(fence_event)), vk_image(vk_image), vk_memory(vk_memory) {} + + ~Surface() { + FTL_DCHECK(backend_context); + vkFreeMemory(backend_context->fDevice, vk_memory, NULL); + vkDestroyImage(backend_context->fDevice, vk_image, NULL); + } + }; + + std::unique_ptr CreateSurface(uint32_t width, uint32_t height); + + struct Swapchain { + std::queue> queue; + uint32_t tick_count = 0; + static constexpr uint32_t kMaxSurfaces = 3; + static constexpr uint32_t kMaxTickBeforeDiscard = 3; + }; + + using size_key_t = uint64_t; + static size_key_t MakeSizeKey(uint32_t width, uint32_t height) { + return (static_cast(width) << 32) | + static_cast(height); + } + + // These three containers hold surfaces in various stages of recycling + + // Buffers exist in available_surfaces_ when they are ready to be recycled + // ProduceSurface will look here for an appropriately sized surface before + // creating a new one + // The Swapchain's tick_count is incremented in Tick and decremented when + // a surface is taken from the queue, when the tick count goes above + // kMaxTickBeforeDiscard the Swapchain is discarded. Newly surfaces are + // added to the queue iff there aar less than kMaxSurfaces already in the + // queue + std::unordered_map available_surfaces_; + + struct PendingSurfaceInfo { + mtl::MessageLoop::HandlerKey handler_key; + std::unique_ptr surface; + mx::eventpair production_fence; }; - std::vector surfaces_; + // Surfaces produced by ProduceSurface live in outstanding_surfaces_ until + // FinishFrame is called, at which point they are moved to pending_surfaces_ + std::vector outstanding_surfaces_; + // Surfaces exist in pendind surfaces until they are released by the buffer + // consumer + std::unordered_map pending_surfaces_; sk_sp context_; sk_sp backend_context_; @@ -78,10 +131,8 @@ class VulkanRasterizer : public Rasterizer { flow::CompositorContext compositor_context_; mozart::ScenePtr scene_; - bool valid_; std::unique_ptr surface_producer_; - bool CreateOrRecreateSurfaces(uint32_t width, uint32_t height); bool Draw(std::unique_ptr layer_tree); FTL_DISALLOW_COPY_AND_ASSIGN(VulkanRasterizer);