blob: d1fd028d707cdd84ce222c6ac48e2ba43564f32d [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "src/lib/vulkan/swapchain/image_pipe_surface_display.h"
#include <dirent.h>
#include <errno.h>
#include <fidl/fuchsia.hardware.display.types/cpp/fidl.h>
#include <fidl/fuchsia.hardware.display/cpp/fidl.h>
#include <fidl/fuchsia.hardware.display/cpp/wire.h>
#include <fidl/fuchsia.images2/cpp/fidl.h>
#include <fidl/fuchsia.math/cpp/fidl.h>
#include <fidl/fuchsia.sysmem2/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/incoming/cpp/service_member_watcher.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <vk_dispatch_table_helper.h>
#include <zircon/rights.h>
#include <zircon/status.h>
#include <deque>
#include <fbl/unique_fd.h>
#include <vulkan/vk_layer.h>
#include <vulkan/vulkan_fuchsia.h>
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/vulkan/swapchain/display_coordinator_listener.h"
#include "src/lib/vulkan/swapchain/vulkan_utils.h"
namespace image_pipe_swapchain {
namespace {
const char* const kTag = "ImagePipeSurfaceDisplay";
using DisplayCoordinator = fuchsia_hardware_display::Coordinator;
using OneWayResult = fit::result<fidl::OneWayStatus>;
zx::result<fidl::ClientEnd<fuchsia_hardware_display::Provider>> ConnectToDisplayProvider() {
component::SyncServiceMemberWatcher<fuchsia_hardware_display::Service::Provider> watcher;
zx::result<fidl::ClientEnd<fuchsia_hardware_display::Provider>> watch_result =
watcher.GetNextInstance(/*stop_at_idle=*/true);
if (watch_result.status_value() == ZX_ERR_STOP) {
fprintf(stderr, "fuchsia.hardware.display.Service is not available");
return zx::error(ZX_ERR_UNAVAILABLE);
}
return watch_result;
}
} // namespace
ImagePipeSurfaceDisplay::ImagePipeSurfaceDisplay()
: client_loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
listener_loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {}
ImagePipeSurfaceDisplay::~ImagePipeSurfaceDisplay() { listener_loop_.Shutdown(); }
bool ImagePipeSurfaceDisplay::Init() {
{
zx::result client_end = component::Connect<fuchsia_sysmem2::Allocator>();
if (!client_end.is_ok()) {
fprintf(stderr, "%s: Couldn't conect to fuchsia.sysmem2.Allocator: %s\n", kTag,
client_end.status_string());
return false;
}
sysmem_allocator_.Bind(std::move(*client_end));
}
{
OneWayResult result = sysmem_allocator_->SetDebugClientInfo(
std::move(fuchsia_sysmem2::AllocatorSetDebugClientInfoRequest()
.name(fsl::GetCurrentProcessName())
.id(fsl::GetCurrentProcessKoid())));
if (!result.is_ok()) {
fprintf(stderr, "%s: Couldn't set debug client info on fuchsia.sysmem2.Allocator: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
zx::result<fidl::ClientEnd<fuchsia_hardware_display::Provider>> provider_result =
ConnectToDisplayProvider();
if (provider_result.is_error()) {
fprintf(stderr, "%s: Failed to create provider channel %d (%s)\n", kTag,
provider_result.error_value(), provider_result.status_string());
}
fidl::ClientEnd<fuchsia_hardware_display::Provider> provider = std::move(provider_result).value();
auto [coordinator_client, coordinator_server] =
fidl::Endpoints<fuchsia_hardware_display::Coordinator>::Create();
auto [listener_client, listener_server] =
fidl::Endpoints<fuchsia_hardware_display::CoordinatorListener>::Create();
fidl::Arena arena;
auto open_coordinator_request =
fuchsia_hardware_display::wire::ProviderOpenCoordinatorWithListenerForPrimaryRequest::Builder(
arena)
.coordinator(std::move(coordinator_server))
.coordinator_listener(std::move(listener_client))
.Build();
fidl::WireResult open_coordinator_result =
fidl::WireCall(provider)->OpenCoordinatorWithListenerForPrimary(
std::move(open_coordinator_request));
if (!open_coordinator_result.ok()) {
fprintf(stderr, "%s: Failed to call display.Provider handle %d (%s)\n", kTag,
open_coordinator_result.status(), open_coordinator_result.FormatDescription().c_str());
return false;
}
if (open_coordinator_result.value().is_error()) {
fprintf(stderr, "%s: Failed to open display.Coordinator %d (%s)\n", kTag,
open_coordinator_result.value().error_value(),
zx_status_get_string(open_coordinator_result.value().error_value()));
return false;
}
display_coordinator_.Bind(std::move(coordinator_client), client_loop_.dispatcher(), this);
display_coordinator_listener_ = std::make_unique<DisplayCoordinatorListener>(
std::move(listener_server),
fit::bind_member(this, &ImagePipeSurfaceDisplay::ControllerOnDisplaysChanged),
fit::bind_member(this, &ImagePipeSurfaceDisplay::ControllerOnVsync),
/* on_client_ownership_change= */ nullptr, *listener_loop_.dispatcher());
while (!have_display_) {
listener_loop_.Run(zx::time::infinite(), true);
if (display_connection_exited_)
return false;
}
listener_loop_.StartThread("ImagePipeSurfaceDisplay-coordinator-listener", nullptr);
return true;
}
bool ImagePipeSurfaceDisplay::WaitForAsyncMessage() {
got_message_response_ = false;
while (!got_message_response_ && !display_connection_exited_) {
client_loop_.Run(zx::time::infinite(), true);
}
return !display_connection_exited_;
}
void ImagePipeSurfaceDisplay::ControllerOnDisplaysChanged(
std::vector<fuchsia_hardware_display::Info> infos,
std::vector<fuchsia_hardware_display_types::DisplayId>) {
if (infos.size() == 0)
return;
const fuchsia_hardware_display::Info& info = infos[0];
width_ = info.modes()[0].active_area().width();
height_ = info.modes()[0].active_area().height();
display_id_ = info.id();
std::deque<VkSurfaceFormatKHR> formats;
for (fuchsia_images2::PixelFormat pixel_format : info.pixel_format()) {
switch (pixel_format) {
case fuchsia_images2::PixelFormat::kB8G8R8A8:
formats.push_back({VK_FORMAT_B8G8R8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR});
formats.push_back({VK_FORMAT_B8G8R8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR});
break;
case fuchsia_images2::PixelFormat::kR8G8B8A8:
// Push front to prefer R8G8B8A8 formats.
formats.push_front({VK_FORMAT_R8G8B8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR});
formats.push_front({VK_FORMAT_R8G8B8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR});
break;
default:
// Ignore unknown formats.
break;
}
}
if (formats.empty()) {
fprintf(stderr, "OnDisplaysChanged: No pixel format available. Cannot use this display.\n");
return;
}
supported_image_properties_ =
SupportedImageProperties{.formats = {formats.begin(), formats.end()}};
have_display_ = true;
}
void ImagePipeSurfaceDisplay::ControllerOnVsync(
fuchsia_hardware_display_types::DisplayId, zx::time timestamp,
fuchsia_hardware_display::ConfigStamp applied_config_stamp,
fuchsia_hardware_display::VsyncAckCookie cookie) {
// Minimize the time spent holding the mutex by gathering fences to signal, but not immediately
// signaling them.
std::vector<zx::event> events_to_signal;
{
std::scoped_lock lock(mutex_);
while (!pending_release_fences_.empty() &&
applied_config_stamp.value() > pending_release_fences_.front().config_stamp.value()) {
events_to_signal.push_back(std::move(pending_release_fences_.front().release_fence));
pending_release_fences_.pop();
}
}
// Signal the events accumulated above.
for (auto& evt : events_to_signal) {
evt.signal(0, ZX_EVENT_SIGNALED);
}
// Non-zero cookies must be acknowledged immediately, others need not be acknowledged.
// See `coordinator.fidl`.
if (cookie.value() != 0) {
OneWayResult result = display_coordinator_->AcknowledgeVsync(cookie.value());
if (result.is_error()) {
fprintf(stderr, "%s: AcknowledgeVsync failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
}
}
}
bool ImagePipeSurfaceDisplay::CreateImage(VkDevice device, VkLayerDispatchTable* pDisp,
VkFormat format, VkImageUsageFlags usage,
VkSwapchainCreateFlagsKHR swapchain_flags,
VkExtent2D extent, uint32_t image_count,
VkCompositeAlphaFlagBitsKHR alpha_flags,
const VkAllocationCallbacks* pAllocator,
std::vector<ImageInfo>* image_info_out) {
// To create BufferCollection, the image must have a valid format.
if (format == VK_FORMAT_UNDEFINED) {
fprintf(stderr, "%s: Invalid format: %d\n", kTag, format);
return false;
}
VkResult result;
auto [local_token_client_end, local_token_server_end] =
fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create();
fidl::SyncClient local_token(std::move(local_token_client_end));
{
fuchsia_sysmem2::AllocatorAllocateSharedCollectionRequest local_allocate_request;
local_allocate_request.token_request(std::move(local_token_server_end));
OneWayResult result =
sysmem_allocator_->AllocateSharedCollection(std::move(local_allocate_request));
if (result.is_error()) {
fprintf(stderr, "%s: AllocateSharedCollection failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
auto [vulkan_token_client, vulkan_token_server] =
fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create();
{
fuchsia_sysmem2::BufferCollectionTokenDuplicateRequest vulkan_duplicate_request;
vulkan_duplicate_request.token_request(std::move(vulkan_token_server));
vulkan_duplicate_request.rights_attenuation_mask(ZX_RIGHT_SAME_RIGHTS);
OneWayResult result = local_token->Duplicate(std::move(vulkan_duplicate_request));
if (result.is_error()) {
fprintf(stderr, "%s: Duplicate failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
auto [display_token_client, display_token_server] =
fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create();
{
fuchsia_sysmem2::BufferCollectionTokenDuplicateRequest display_duplicate_request;
display_duplicate_request.token_request(std::move(display_token_server));
display_duplicate_request.rights_attenuation_mask(ZX_RIGHT_SAME_RIGHTS);
OneWayResult result = local_token->Duplicate(std::move(display_duplicate_request));
if (result.is_error()) {
fprintf(stderr, "%s: Duplicate failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
{
fit::result result = local_token->Sync();
if (result.is_error()) {
fprintf(stderr, "%s: Sync failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
const fuchsia_hardware_display::BufferCollectionId kBufferCollectionId(1);
zx_status_t status = ZX_OK;
display_coordinator_
->ImportBufferCollection({kBufferCollectionId, std::move(display_token_client)})
.ThenExactlyOnce([this,
&status](fidl::Result<DisplayCoordinator::ImportBufferCollection>& result) {
if (result.is_error()) {
auto& err = result.error_value();
status = err.is_framework_error() ? err.framework_error().status() : err.domain_error();
fprintf(stderr, "%s: ImportBufferCollection failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
}
got_message_response_ = true;
});
if (!WaitForAsyncMessage()) {
fprintf(stderr, "%s: Display Disconnected\n", kTag);
return false;
}
if (status != ZX_OK) {
return false;
}
#if defined(__x86_64__)
// Must be consistent with intel-gpu-core.h
static constexpr uint32_t kImageTilingTypeXTiled = 1;
static constexpr uint32_t kImageTilingType = kImageTilingTypeXTiled;
#elif defined(__aarch64__)
static constexpr uint32_t kImageTilingType =
fuchsia_hardware_display_types::kImageTilingTypeLinear;
#else
static constexpr uint32_t kImageTilingType =
fuchsia_hardware_display_types::kImageTilingTypeLinear;
// Unsupported display.
return false;
#endif
const fuchsia_hardware_display_types::ImageBufferUsage image_buffer_usage{
kImageTilingType,
};
const fuchsia_hardware_display_types::ImageMetadata image_metadata({
.dimensions = fuchsia_math::SizeU({.width = extent.width, .height = extent.height}),
.tiling_type = image_buffer_usage.tiling_type(),
});
display_coordinator_->SetBufferCollectionConstraints({kBufferCollectionId, image_buffer_usage})
.ThenExactlyOnce(
[this,
&status](fidl::Result<DisplayCoordinator::SetBufferCollectionConstraints>& result) {
if (result.is_error()) {
auto& err = result.error_value();
status =
err.is_framework_error() ? err.framework_error().status() : err.domain_error();
fprintf(stderr, "%s: SetBufferCollectionConstraints failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
}
got_message_response_ = true;
});
if (!WaitForAsyncMessage()) {
fprintf(stderr, "%s: Display Disconnected\n", kTag);
return false;
}
if (status != ZX_OK) {
return false;
}
uint32_t image_flags = 0;
if (swapchain_flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR)
image_flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
if (swapchain_flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR)
image_flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
VkImageCreateInfo image_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = image_flags,
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = VkExtent3D{extent.width, extent.height, 1},
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, // not used since not sharing
.pQueueFamilyIndices = nullptr, // not used since not sharing
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
const VkSysmemColorSpaceFUCHSIA kSrgbColorSpace = {
.sType = VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA,
.pNext = nullptr,
.colorSpace = static_cast<uint32_t>(fuchsia_images2::ColorSpace::kSrgb)};
const VkSysmemColorSpaceFUCHSIA kYuvColorSpace = {
.sType = VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA,
.pNext = nullptr,
.colorSpace = static_cast<uint32_t>(fuchsia_images2::ColorSpace::kRec709)};
VkImageFormatConstraintsInfoFUCHSIA format_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA,
.pNext = nullptr,
.imageCreateInfo = image_create_info,
.requiredFormatFeatures = GetFormatFeatureFlagsFromUsage(usage),
.sysmemPixelFormat = 0u,
.colorSpaceCount = 1,
.pColorSpaces = IsYuvFormat(format) ? &kYuvColorSpace : &kSrgbColorSpace,
};
VkImageConstraintsInfoFUCHSIA image_constraints_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA,
.pNext = nullptr,
.formatConstraintsCount = 1,
.pFormatConstraints = &format_info,
.bufferCollectionConstraints =
VkBufferCollectionConstraintsInfoFUCHSIA{
.sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA,
.pNext = nullptr,
.minBufferCount = 1,
.maxBufferCount = 0,
.minBufferCountForCamping = 0,
.minBufferCountForDedicatedSlack = 0,
.minBufferCountForSharedSlack = 0,
},
.flags = 0u,
};
VkBufferCollectionCreateInfoFUCHSIA import_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA,
.pNext = nullptr,
.collectionToken = vulkan_token_client.TakeChannel().release(),
};
VkBufferCollectionFUCHSIA collection;
result = pDisp->CreateBufferCollectionFUCHSIA(device, &import_info, pAllocator, &collection);
if (result != VK_SUCCESS) {
fprintf(stderr, "%s: Failed to import buffer collection: %d\n", kTag, result);
return false;
}
result = pDisp->SetBufferCollectionImageConstraintsFUCHSIA(device, collection,
&image_constraints_info);
if (result != VK_SUCCESS) {
fprintf(stderr, "%s: Failed to import buffer collection: %d\n", kTag, result);
return false;
}
auto [sysmem_collection_client, sysmem_collection_server] =
fidl::Endpoints<fuchsia_sysmem2::BufferCollection>::Create();
fidl::SyncClient sysmem_collection(std::move(sysmem_collection_client));
{
fuchsia_sysmem2::AllocatorBindSharedCollectionRequest bind_shared_collection_request;
bind_shared_collection_request.token({local_token.TakeClientEnd()});
bind_shared_collection_request.buffer_collection_request(std::move(sysmem_collection_server));
OneWayResult result =
sysmem_allocator_->BindSharedCollection(std::move(bind_shared_collection_request));
if (result.is_error()) {
fprintf(stderr, "%s: BindSharedCollection failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
// 1000 should override the generic Magma name.
{
constexpr uint32_t kNamePriority = 1000u;
const char* kImageName = "ImagePipeSurfaceDisplay";
fuchsia_sysmem2::NodeSetNameRequest set_name_request;
set_name_request.name(kImageName);
set_name_request.priority(kNamePriority);
OneWayResult result = sysmem_collection->SetName(std::move(set_name_request));
if (result.is_error()) {
fprintf(stderr, "%s: SetName failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
{
fuchsia_sysmem2::BufferCollectionConstraints constraints;
constraints.min_buffer_count(image_count);
// Used because every constraints need to have a usage.
constraints.usage(
std::move(fuchsia_sysmem2::BufferUsage().display(fuchsia_sysmem2::kDisplayUsageLayer)));
fuchsia_sysmem2::BufferCollectionSetConstraintsRequest constraints_request;
constraints_request.constraints(std::move(constraints));
OneWayResult result = sysmem_collection->SetConstraints(std::move(constraints_request));
if (result.is_error()) {
fprintf(stderr, "%s: SetConstraints failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
auto wait_for_all_buffers_allocated_result = sysmem_collection->WaitForAllBuffersAllocated();
if (wait_for_all_buffers_allocated_result.is_error()) {
fprintf(stderr, "%s: WaitForBuffersAllocated failed: %s\n", kTag,
wait_for_all_buffers_allocated_result.error_value().FormatDescription().c_str());
return false;
}
{
OneWayResult result = sysmem_collection->Release();
if (result.is_error()) {
fprintf(stderr, "%s: Release failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
ZX_ASSERT(wait_for_all_buffers_allocated_result.value().buffer_collection_info().has_value());
auto& buffer_collection_info =
wait_for_all_buffers_allocated_result.value().buffer_collection_info().value();
ZX_ASSERT(buffer_collection_info.buffers().has_value());
if (buffer_collection_info.buffers()->size() != image_count) {
fprintf(stderr, "%s: incorrect image count %lu allocated vs. %d requested\n", kTag,
buffer_collection_info.buffers()->size(), image_count);
return false;
}
for (uint32_t i = 0; i < image_count; ++i) {
VkExternalMemoryImageCreateInfo external_image_create_info = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA,
};
VkBufferCollectionImageCreateInfoFUCHSIA image_format_fuchsia = {
.sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA,
.pNext = &external_image_create_info,
.collection = collection,
.index = i};
image_create_info.pNext = &image_format_fuchsia;
VkImage image;
result = pDisp->CreateImage(device, &image_create_info, pAllocator, &image);
if (result != VK_SUCCESS) {
fprintf(stderr, "%s: vkCreateImage failed: %d\n", kTag, result);
return false;
}
VkMemoryRequirements memory_requirements;
pDisp->GetImageMemoryRequirements(device, image, &memory_requirements);
VkBufferCollectionPropertiesFUCHSIA properties = {
.sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA};
result = pDisp->GetBufferCollectionPropertiesFUCHSIA(device, collection, &properties);
if (result != VK_SUCCESS) {
fprintf(stderr, "%s: GetBufferCollectionPropertiesFUCHSIA failed: %d\n", kTag, status);
return false;
}
// Find lowest usable index.
uint32_t memory_type_index =
__builtin_ctz(memory_requirements.memoryTypeBits & properties.memoryTypeBits);
VkMemoryDedicatedAllocateInfoKHR dedicated_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
.image = image,
};
VkImportMemoryBufferCollectionFUCHSIA import_info = {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA,
.pNext = &dedicated_info,
.collection = collection,
.index = i,
};
VkMemoryAllocateInfo alloc_info{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &import_info,
.allocationSize = memory_requirements.size,
.memoryTypeIndex = memory_type_index,
};
VkDeviceMemory memory;
result = pDisp->AllocateMemory(device, &alloc_info, pAllocator, &memory);
if (result != VK_SUCCESS) {
fprintf(stderr, "%s: vkAllocateMemory failed: %d\n", kTag, result);
return result;
}
result = pDisp->BindImageMemory(device, image, memory, 0);
if (result != VK_SUCCESS) {
fprintf(stderr, "%s: vkBindImageMemory failed: %d\n", kTag, result);
return result;
}
uint32_t image_id = next_image_id();
{
const fuchsia_hardware_display::ImageId fidl_image_id(image_id);
display_coordinator_->ImportImage({image_metadata, kBufferCollectionId, i, fidl_image_id})
.ThenExactlyOnce([this, &status](fidl::Result<DisplayCoordinator::ImportImage>& result) {
if (result.is_error()) {
auto& err = result.error_value();
status =
err.is_framework_error() ? err.framework_error().status() : err.domain_error();
fprintf(stderr, "%s: ImportVmoImage failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
}
got_message_response_ = true;
});
}
if (!WaitForAsyncMessage()) {
return false;
}
if (status != ZX_OK) {
return false;
}
ImageInfo info = {.image = image, .memory = memory, .image_id = image_id};
image_info_out->push_back(info);
image_ids.insert(image_id);
}
pDisp->DestroyBufferCollectionFUCHSIA(device, collection, pAllocator);
{
OneWayResult result = display_coordinator_->ReleaseBufferCollection({kBufferCollectionId});
if (result.is_error()) {
fprintf(stderr, "%s: ReleaseBufferCollection failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
// Assert that `CreateImage()` hasn't already been called. If it has, the current implementation
// won't properly clean up resources from previous `CreateImage()` calls.
if (layer_id_.value() != fuchsia_hardware_display_types::kInvalidDispId) {
fprintf(stderr,
"%s: CreateImage() stomping existing layer_id_; see https://fxbug.dev/374201213\n",
kTag);
}
display_coordinator_->CreateLayer().ThenExactlyOnce(
[this, &status](fidl::Result<DisplayCoordinator::CreateLayer>& result) {
if (result.is_ok()) {
layer_id_ = result.value().layer_id();
status = ZX_OK;
} else {
layer_id_ =
fuchsia_hardware_display::LayerId{fuchsia_hardware_display_types::kInvalidDispId};
auto& err = result.error_value();
status = err.is_framework_error() ? err.framework_error().status() : err.domain_error();
fprintf(stderr, "%s: CreateLayer failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
}
got_message_response_ = true;
});
if (!WaitForAsyncMessage()) {
return false;
}
if (status != ZX_OK) {
return false;
}
{
OneWayResult result = display_coordinator_->SetDisplayLayers(
{display_id_, std::vector<fuchsia_hardware_display::LayerId>{layer_id_}});
if (result.is_error()) {
fprintf(stderr, "%s: SetDisplayLayers failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
{
OneWayResult result = display_coordinator_->SetLayerPrimaryConfig({layer_id_, image_metadata});
if (result.is_error()) {
fprintf(stderr, "%s: SetLayerPrimaryConfig failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return false;
}
}
return true;
}
bool ImagePipeSurfaceDisplay::GetSize(uint32_t* width_out, uint32_t* height_out) {
*width_out = width_;
*height_out = height_;
return true;
}
void ImagePipeSurfaceDisplay::RemoveImage(uint32_t image_id) {
auto iter = image_ids.find(image_id);
if (iter != image_ids.end()) {
image_ids.erase(iter);
}
}
void ImagePipeSurfaceDisplay::PresentImage(
bool immediate, uint32_t image_id, std::vector<std::unique_ptr<PlatformEvent>> acquire_fences,
std::vector<std::unique_ptr<PlatformEvent>> release_fences, VkQueue queue) {
ZX_ASSERT(!immediate);
ZX_ASSERT(acquire_fences.size() <= 1);
ZX_ASSERT(release_fences.size() <= 1);
auto iter = image_ids.find(image_id);
if (iter == image_ids.end()) {
fprintf(stderr, "%s::PresentImage: can't find image_id %u\n", kTag, image_id);
return;
}
fuchsia_hardware_display::EventId wait_event_id = {
fuchsia_hardware_display_types::kInvalidDispId};
if (acquire_fences.size()) {
zx::event event = static_cast<FuchsiaEvent*>(acquire_fences[0].get())->Take();
zx_info_handle_basic_t info;
zx_status_t status =
event.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
fprintf(stderr, "%s: failed to get event id: %d\n", kTag, status);
return;
}
wait_event_id.value(info.koid);
OneWayResult result = display_coordinator_->ImportEvent({std::move(event), wait_event_id});
if (result.is_error()) {
fprintf(stderr, "%s: ImportEvent failed for acquire fence: %s\n", kTag,
result.error_value().FormatDescription().c_str());
return;
}
}
// image_id is also used in DisplayController interface.
const fuchsia_hardware_display::ImageId fidl_image_id = {image_id};
{
OneWayResult result = display_coordinator_->SetLayerImage2(
{{.layer_id = layer_id_, .image_id = fidl_image_id, .wait_event_id = wait_event_id}});
if (result.is_error()) {
fprintf(stderr, "%s: SetLayerImage2 failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
// Note: don't return on failure, because we want to release fences afterward.
}
}
const fuchsia_hardware_display::ConfigStamp new_config_stamp = NextConfigStamp();
{
std::scoped_lock lock(mutex_);
// Apply the config while the mutex is locked. This avoids a race condition where the vsync for
// this config could be received before we enqueue the pending release fences below.
fuchsia_hardware_display::CoordinatorApplyConfig3Request request;
request.stamp(new_config_stamp);
OneWayResult result = display_coordinator_->ApplyConfig3(std::move(request));
if (result.is_error()) {
fprintf(stderr, "%s: ApplyConfig failed: %s\n", kTag,
result.error_value().FormatDescription().c_str());
// Note: don't return on failure, because we want to release fences afterward.
}
// If there are unsignaled release fences, we want them to be released when the just-applied
// config is latched.
if (!release_fences.empty()) {
// We only handle 1 fence, so if more are provided then we wouldn't behave as expected.
zx::event release_fence = static_cast<FuchsiaEvent*>(release_fences[0].get())->Take();
ZX_ASSERT(release_fences.size() == 1);
pending_release_fences_.push(
{.config_stamp = new_config_stamp, .release_fence = std::move(release_fence)});
}
}
if (wait_event_id.value() != fuchsia_hardware_display_types::kInvalidDispId) {
OneWayResult result = display_coordinator_->ReleaseEvent(wait_event_id);
if (result.is_error()) {
fprintf(stderr, "%s: ReleaseEvent failed for wait event: %s\n", kTag,
result.error_value().FormatDescription().c_str());
}
}
}
SupportedImageProperties& ImagePipeSurfaceDisplay::GetSupportedImageProperties() {
return supported_image_properties_;
}
void ImagePipeSurfaceDisplay::on_fidl_error(fidl::UnbindInfo error) {
display_connection_exited_ = true;
}
} // namespace image_pipe_swapchain