blob: d45c5ada460887e13a575b05d2b0117de2b07629 [file] [edit]
// Copyright 2019 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/graphics/display/drivers/goldfish-display/display-engine.h"
#include <fidl/fuchsia.hardware.goldfish/cpp/wire.h>
#include <fidl/fuchsia.images2/cpp/fidl.h>
#include <fidl/fuchsia.math/cpp/fidl.h>
#include <fidl/fuchsia.sysmem2/cpp/wire.h>
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/time.h>
#include <lib/async/cpp/wait.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/image-format/image_format.h>
#include <lib/trace/event.h>
#include <lib/zx/result.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include <bind/fuchsia/goldfish/platform/sysmem/heap/cpp/bind.h>
#include <bind/fuchsia/sysmem/heap/cpp/bind.h>
#include <fbl/algorithm.h>
#include <fbl/auto_lock.h>
#include "src/graphics/display/drivers/goldfish-display/render_control.h"
#include "src/graphics/display/lib/api-types/cpp/color-conversion.h"
#include "src/graphics/display/lib/api-types/cpp/display-id.h"
#include "src/graphics/display/lib/api-types/cpp/driver-buffer-collection-id.h"
#include "src/graphics/display/lib/api-types/cpp/driver-config-stamp.h"
#include "src/graphics/display/lib/api-types/cpp/driver-image-id.h"
#include "src/graphics/display/lib/api-types/cpp/mode-and-id.h"
#include "src/graphics/display/lib/api-types/cpp/mode-id.h"
#include "src/graphics/display/lib/api-types/cpp/mode.h"
#include "src/graphics/display/lib/api-types/cpp/pixel-format.h"
#include "src/graphics/display/lib/api-types/cpp/power-mode.h"
namespace goldfish {
namespace {
constexpr display::DisplayId kPrimaryDisplayId(1);
constexpr uint32_t FB_WIDTH = 1;
constexpr uint32_t FB_HEIGHT = 2;
constexpr uint32_t FB_FPS = 5;
constexpr uint32_t GL_RGBA = 0x1908;
constexpr uint32_t GL_BGRA_EXT = 0x80E1;
// TODO(https://fxbug.dev/42072949): The `Mode` struct does not have an ID field.
// The display coordinator requires each mode to have a unique ID. For now,
// we use a placeholder ID 1.
constexpr display::ModeId kModeId(1);
constexpr display::EngineInfo kEngineInfo({
.max_layer_count = 1,
.max_connected_display_count = 1,
.is_capture_supported = false,
});
} // namespace
DisplayEngine::DisplayEngine(fidl::ClientEnd<fuchsia_hardware_goldfish::ControlDevice> control,
fidl::ClientEnd<fuchsia_hardware_goldfish_pipe::Bus> pipe_bus,
fidl::ClientEnd<fuchsia_sysmem2::Allocator> sysmem_allocator,
std::unique_ptr<RenderControl> render_control,
async_dispatcher_t* display_event_dispatcher,
display::DisplayEngineEventsInterface* engine_events)
: control_(std::move(control)),
pipe_bus_(std::move(pipe_bus)),
sysmem_allocator_client_(std::move(sysmem_allocator)),
rc_(std::move(render_control)),
display_event_dispatcher_(display_event_dispatcher),
engine_events_(engine_events) {
ZX_DEBUG_ASSERT(control_.is_valid());
ZX_DEBUG_ASSERT(pipe_bus_.is_valid());
ZX_DEBUG_ASSERT(sysmem_allocator_client_.is_valid());
ZX_DEBUG_ASSERT(rc_ != nullptr);
ZX_DEBUG_ASSERT(engine_events_ != nullptr);
}
DisplayEngine::~DisplayEngine() {}
zx::result<> DisplayEngine::Initialize() {
fbl::AutoLock lock(&lock_);
// Create primary display device.
static constexpr int32_t kFallbackWidthPx = 1024;
static constexpr int32_t kFallbackHeightPx = 768;
static constexpr int32_t kFallbackRefreshRateHz = 60;
primary_display_device_ = DisplayState{
.width_px = rc_->GetFbParam(FB_WIDTH, kFallbackWidthPx),
.height_px = rc_->GetFbParam(FB_HEIGHT, kFallbackHeightPx),
.refresh_rate_hz = rc_->GetFbParam(FB_FPS, kFallbackRefreshRateHz),
};
// Set up display and set up flush task for each device.
zx_status_t status = SetupPrimaryDisplay();
if (status != ZX_OK) {
fdf::error("Failed to set up the primary display: {}", zx::make_result(status));
return zx::error(status);
}
status = async::PostTask(display_event_dispatcher_,
[this] { FlushPrimaryDisplay(display_event_dispatcher_); });
if (status != ZX_OK) {
fdf::error("Failed to post display flush task on the display event loop: {}",
zx::make_result(status));
return zx::error(status);
}
return zx::ok();
}
display::EngineInfo DisplayEngine::CompleteCoordinatorConnection() {
const int32_t width = primary_display_device_.width_px;
const int32_t height = primary_display_device_.height_px;
const int32_t refresh_rate_hz = primary_display_device_.refresh_rate_hz;
const display::Mode mode = {{
.active_width = width,
.active_height = height,
.refresh_rate_millihertz = refresh_rate_hz * 1'000,
}};
const display::ModeAndId preferred_mode = {{
.id = kModeId,
.mode = mode,
}};
static constexpr std::array<display::PixelFormat, 2> pixel_formats = {
display::PixelFormat::kB8G8R8A8,
display::PixelFormat::kR8G8B8A8,
};
engine_events_->OnDisplayAdded(kPrimaryDisplayId, std::span(&preferred_mode, 1), pixel_formats);
return kEngineInfo;
}
namespace {
uint32_t GetColorBufferFormatFromSysmemPixelFormat(
const fuchsia_images2::PixelFormat& pixel_format) {
switch (pixel_format) {
case fuchsia_images2::PixelFormat::kR8G8B8A8:
return GL_RGBA;
case fuchsia_images2::PixelFormat::kB8G8R8A8:
return GL_BGRA_EXT;
default:
// This should not happen. The sysmem-negotiated pixel format must be supported.
ZX_ASSERT_MSG(false, "Import unsupported image: %u", static_cast<uint32_t>(pixel_format));
}
}
} // namespace
zx::result<display::DriverImageId> DisplayEngine::ImportVmoImage(
const display::ImageMetadata& image_metadata, const fuchsia_images2::PixelFormat& pixel_format,
zx::vmo vmo, size_t offset) {
auto color_buffer = std::make_unique<ColorBuffer>();
color_buffer->is_linear_format =
image_metadata.tiling_type() == display::ImageTilingType::kLinear;
const uint32_t color_buffer_format = GetColorBufferFormatFromSysmemPixelFormat(pixel_format);
fidl::Arena unused_arena;
const uint32_t bytes_per_pixel = ImageFormatStrideBytesPerWidthPixel(
PixelFormatAndModifier(pixel_format,
/* ignored */
fuchsia_images2::PixelFormatModifier::kLinear));
color_buffer->size =
fbl::round_up(image_metadata.width() * image_metadata.height() * bytes_per_pixel,
zx_system_get_page_size());
// Linear images must be pinned.
color_buffer->pinned_vmo =
rc_->pipe_io()->PinVmo(vmo, ZX_BTI_PERM_READ | ZX_BTI_CONTIGUOUS, offset, color_buffer->size);
color_buffer->vmo = std::move(vmo);
color_buffer->width = image_metadata.width();
color_buffer->height = image_metadata.height();
color_buffer->format = color_buffer_format;
zx::result<HostColorBufferId> create_result =
rc_->CreateColorBuffer(image_metadata.width(), image_metadata.height(), color_buffer_format);
if (create_result.is_error()) {
fdf::error("failed to create color buffer: {}", create_result);
return create_result.take_error();
}
color_buffer->host_color_buffer_id = create_result.value();
// TODO(https://fxbug.dev/434967502): DisplayEngine should not directly use
// ColorBuffer raw addresses as keys.
const display::DriverImageId image_id(reinterpret_cast<uint64_t>(color_buffer.release()));
return zx::ok(image_id);
}
zx::result<> DisplayEngine::ImportBufferCollection(
display::DriverBufferCollectionId buffer_collection_id,
fidl::ClientEnd<fuchsia_sysmem2::BufferCollectionToken> buffer_collection_token) {
if (buffer_collections_.find(buffer_collection_id) != buffer_collections_.end()) {
fdf::error("Buffer Collection (id={}) already exists", buffer_collection_id.value());
return zx::error(ZX_ERR_ALREADY_EXISTS);
}
ZX_DEBUG_ASSERT_MSG(sysmem_allocator_client_.is_valid(), "sysmem allocator is not initialized");
auto [collection_client_endpoint, collection_server_endpoint] =
fidl::Endpoints<::fuchsia_sysmem2::BufferCollection>::Create();
fidl::Arena arena;
auto bind_result = sysmem_allocator_client_->BindSharedCollection(
fuchsia_sysmem2::wire::AllocatorBindSharedCollectionRequest::Builder(arena)
.buffer_collection_request(std::move(collection_server_endpoint))
.token(std::move(buffer_collection_token))
.Build());
if (!bind_result.ok()) {
fdf::error("Cannot complete FIDL call BindSharedCollection: {}", bind_result.status_string());
return zx::error(ZX_ERR_INTERNAL);
}
buffer_collections_[buffer_collection_id] =
fidl::SyncClient(std::move(collection_client_endpoint));
return zx::ok();
}
zx::result<> DisplayEngine::ReleaseBufferCollection(
display::DriverBufferCollectionId buffer_collection_id) {
if (buffer_collections_.find(buffer_collection_id) == buffer_collections_.end()) {
fdf::error("Cannot release buffer collection {}: buffer collection doesn't exist",
buffer_collection_id);
return zx::error(ZX_ERR_NOT_FOUND);
}
buffer_collections_.erase(buffer_collection_id);
return zx::ok();
}
zx::result<display::DriverImageId> DisplayEngine::ImportImage(
const display::ImageMetadata& image_metadata,
display::DriverBufferCollectionId buffer_collection_id, uint32_t buffer_index) {
const auto it = buffer_collections_.find(buffer_collection_id);
if (it == buffer_collections_.end()) {
fdf::error("ImportImage: Cannot find imported buffer collection (id={})", buffer_collection_id);
return zx::error(ZX_ERR_NOT_FOUND);
}
const fidl::SyncClient<fuchsia_sysmem2::BufferCollection>& collection_client = it->second;
fidl::Result check_result = collection_client->CheckAllBuffersAllocated();
// TODO(https://fxbug.dev/42072690): The sysmem FIDL error logging patterns are
// inconsistent across drivers. The FIDL error handling and logging should be
// unified.
if (check_result.is_error()) {
const auto& error = check_result.error_value();
if (error.is_domain_error() && error.domain_error() == fuchsia_sysmem2::Error::kPending) {
return zx::error(ZX_ERR_SHOULD_WAIT);
}
return zx::error(ZX_ERR_UNAVAILABLE);
}
fidl::Result wait_result = collection_client->WaitForAllBuffersAllocated();
// TODO(https://fxbug.dev/42072690): The sysmem FIDL error logging patterns are
// inconsistent across drivers. The FIDL error handling and logging should be
// unified.
if (wait_result.is_error()) {
const auto& error = wait_result.error_value();
if (error.is_domain_error() && error.domain_error() == fuchsia_sysmem2::Error::kPending) {
return zx::error(ZX_ERR_SHOULD_WAIT);
}
return zx::error(ZX_ERR_UNAVAILABLE);
}
auto& wait_response = wait_result.value();
auto& collection_info = wait_response.buffer_collection_info();
zx::vmo vmo;
if (buffer_index < collection_info->buffers()->size()) {
vmo = std::move(collection_info->buffers()->at(buffer_index).vmo().value());
ZX_DEBUG_ASSERT(!collection_info->buffers()->at(buffer_index).vmo()->is_valid());
}
if (!vmo.is_valid()) {
fdf::error("invalid index: {}", buffer_index);
return zx::error(ZX_ERR_OUT_OF_RANGE);
}
if (!collection_info->settings()->image_format_constraints()) {
fdf::error("Buffer collection doesn't have valid image format constraints");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
uint64_t offset = collection_info->buffers()->at(buffer_index).vmo_usable_start().value();
if (collection_info->settings()->buffer_settings()->heap().value().heap_type() !=
bind_fuchsia_goldfish_platform_sysmem_heap::HEAP_TYPE_DEVICE_LOCAL) {
const auto& pixel_format =
collection_info->settings()->image_format_constraints()->pixel_format();
return ImportVmoImage(image_metadata, pixel_format.value(), std::move(vmo), offset);
}
if (offset != 0) {
fdf::error("VMO offset ({}) not supported for Goldfish device local color buffers", offset);
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
auto color_buffer = std::make_unique<ColorBuffer>();
color_buffer->is_linear_format =
image_metadata.tiling_type() == display::ImageTilingType::kLinear;
color_buffer->vmo = std::move(vmo);
const display::DriverImageId image_id(reinterpret_cast<uint64_t>(color_buffer.release()));
return zx::ok(image_id);
}
zx::result<display::DriverCaptureImageId> DisplayEngine::ImportImageForCapture(
display::DriverBufferCollectionId buffer_collection_id, uint32_t buffer_index) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
void DisplayEngine::ReleaseImage(display::DriverImageId image_id) {
auto color_buffer = reinterpret_cast<ColorBuffer*>(image_id.value());
// Color buffer is owned by image in the linear case.
if (color_buffer->is_linear_format) {
rc_->CloseColorBuffer(color_buffer->host_color_buffer_id);
}
async::PostTask(display_event_dispatcher_, [this, color_buffer] {
if (primary_display_device_.incoming_config.has_value() &&
primary_display_device_.incoming_config->color_buffer == color_buffer) {
primary_display_device_.incoming_config = std::nullopt;
}
delete color_buffer;
});
}
display::ConfigCheckResult DisplayEngine::CheckConfiguration(
display::DisplayId display_id, display::ModeId display_mode_id,
display::ColorConversion color_conversion, std::span<const display::DriverLayer> layers) {
// The display coordinator currently uses zero-display configs to blank a
// display. We'll remove this eventually.
if (layers.empty()) {
return display::ConfigCheckResult::kOk;
}
ZX_DEBUG_ASSERT(display_id == kPrimaryDisplayId);
if (display_mode_id != kModeId) {
return display::ConfigCheckResult::kUnsupportedDisplayModes;
}
if (color_conversion != display::ColorConversion::kIdentity) {
// Color Correction is not supported, but we will pretend we do.
// TODO(https://fxbug.dev/435550805): Returning error will cause blank screen if scenic
// requests color correction. For now, lets pretend we support it, until a proper fix is
// done (either from scenic or from core display)
fdf::warn("Color Correction not supported.");
}
// If the number of layers exceeds the maximum supported, the rest need to be
// merged into the base layer.
if (layers.size() > kEngineInfo.max_layer_count()) {
return display::ConfigCheckResult::kUnsupportedConfig;
}
for (const display::DriverLayer& layer : layers) {
if (layer.image_source().width() == 0 || layer.image_source().height() == 0) {
// Solid color fill layers are not yet supported.
// TODO(https://fxbug.dev/406525464): add support.
return display::ConfigCheckResult::kUnsupportedConfig;
}
// Scaling is allowed if destination frame match display and
// source frame match image.
const display::Rectangle display_area = {{
.x = 0,
.y = 0,
.width = primary_display_device_.width_px,
.height = primary_display_device_.height_px,
}};
const display::Rectangle image_area = {{
.x = 0,
.y = 0,
.width = layer.image_metadata().width(),
.height = layer.image_metadata().height(),
}};
if (layer.display_destination() != display_area) {
// TODO(https://fxbug.dev/42111727): Need to provide proper flag to indicate driver only
// accepts full screen dest frame.
return display::ConfigCheckResult::kUnsupportedConfig;
}
if (layer.image_source() != image_area) {
return display::ConfigCheckResult::kUnsupportedConfig;
}
if (layer.alpha_mode() != display::AlphaMode::kDisable) {
// Alpha is not supported.
return display::ConfigCheckResult::kUnsupportedConfig;
}
if (layer.image_source_transformation() != display::CoordinateTransformation::kIdentity) {
// Transformation is not supported.
return display::ConfigCheckResult::kUnsupportedConfig;
}
}
return display::ConfigCheckResult::kOk;
}
zx_status_t DisplayEngine::PresentPrimaryDisplayConfig(const DisplayConfig& display_config) {
ColorBuffer* color_buffer = display_config.color_buffer;
if (!color_buffer) {
return ZX_OK;
}
zx::eventpair event_display, event_sync_device;
zx_status_t status = zx::eventpair::create(0u, &event_display, &event_sync_device);
if (status != ZX_OK) {
fdf::error("zx_eventpair_create failed: {}", zx::make_result(status));
return status;
}
// Set up async wait for the goldfish sync event. The zx::eventpair will be
// stored in the async wait callback, which will be destroyed only when the
// event is signaled or the wait is cancelled.
primary_display_device_.pending_config_waits.emplace_back(event_display.get(),
ZX_EVENTPAIR_SIGNALED, 0);
async::WaitOnce& wait = primary_display_device_.pending_config_waits.back();
wait.Begin(
display_event_dispatcher_,
[this, event = std::move(event_display), pending_config_stamp = display_config.config_stamp](
async_dispatcher_t* dispatcher, async::WaitOnce* current_wait, zx_status_t status,
const zx_packet_signal_t*) {
TRACE_DURATION("gfx", "DisplayEngine::SyncEventHandler", "config_stamp",
pending_config_stamp.value());
if (status == ZX_ERR_CANCELED) {
fdf::info("Wait for config stamp {} cancelled.", pending_config_stamp);
return;
}
ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "Invalid wait status: %d", status);
// When the eventpair in |current_wait| is signalled, all the pending waits
// that are queued earlier than that eventpair will be removed from the list
// and the async WaitOnce will be cancelled.
// Note that the cancelled waits will return early and will not reach here.
ZX_DEBUG_ASSERT(std::any_of(primary_display_device_.pending_config_waits.begin(),
primary_display_device_.pending_config_waits.end(),
[current_wait](const async::WaitOnce& wait) {
return wait.object() == current_wait->object();
}));
// Remove all the pending waits that are queued earlier than the current
// wait, and the current wait itself. In WaitOnce, the callback is moved to
// stack before current wait is removed, so it's safe to remove any item in
// the list.
for (auto it = primary_display_device_.pending_config_waits.begin();
it != primary_display_device_.pending_config_waits.end();) {
if (it->object() == current_wait->object()) {
primary_display_device_.pending_config_waits.erase(it);
break;
}
it = primary_display_device_.pending_config_waits.erase(it);
}
primary_display_device_.latest_config_stamp =
std::max(primary_display_device_.latest_config_stamp, pending_config_stamp);
});
// Update host-writeable display buffers before presenting.
if (color_buffer->pinned_vmo.region_count() > 0) {
auto status = rc_->UpdateColorBuffer(
color_buffer->host_color_buffer_id, color_buffer->pinned_vmo, color_buffer->width,
color_buffer->height, color_buffer->format, color_buffer->size);
if (status.is_error() || status.value()) {
fdf::error("color buffer update failed: {}:{}", zx::make_result(status.status_value()),
status.value_or(0));
return status.is_error() ? status.status_value() : ZX_ERR_INTERNAL;
}
}
// Present the buffer.
{
zx_status_t status = rc_->FbPost(color_buffer->host_color_buffer_id);
if (status != ZX_OK) {
fdf::error("Failed to call render control command FbPost: {}", zx::make_result(status));
return status;
}
fbl::AutoLock lock(&lock_);
fidl::WireResult result = control_->CreateSyncFence(std::move(event_sync_device));
if (!result.ok()) {
fdf::error("Failed to call FIDL CreateSyncFence: {}", result.error().FormatDescription());
return status;
}
if (result.value().is_error()) {
fdf::error("Failed to create SyncFence: {}", zx::make_result(result.value().error_value()));
return result.value().error_value();
}
}
return ZX_OK;
}
void DisplayEngine::SubmitConfiguration(display::DisplayId display_id,
display::ModeId display_mode_id,
display::ColorConversion color_conversion,
std::span<const display::DriverLayer> layers,
display::DriverConfigStamp config_stamp) {
ZX_DEBUG_ASSERT_MSG(layers.size() == kEngineInfo.max_layer_count(), "Invalid layer size: %zu",
layers.size());
display::DriverImageId driver_image_id = display::kInvalidDriverImageId;
if (display_id == kPrimaryDisplayId) {
if (!layers.empty()) {
driver_image_id = layers[0].image_id();
}
}
ZX_DEBUG_ASSERT(display_mode_id == kModeId);
if (driver_image_id == display::kInvalidDriverImageId) {
// The display doesn't have any active layers right now. For layers that
// previously existed, we should cancel waiting events on the pending
// color buffer and remove references to both pending and current color
// buffers.
async::PostTask(display_event_dispatcher_, [this, config_stamp] {
primary_display_device_.pending_config_waits.clear();
primary_display_device_.incoming_config = std::nullopt;
primary_display_device_.latest_config_stamp =
std::max(primary_display_device_.latest_config_stamp, config_stamp);
});
return;
}
ColorBuffer* color_buffer = reinterpret_cast<ColorBuffer*>(driver_image_id.value());
ZX_DEBUG_ASSERT(color_buffer != nullptr);
if (color_buffer->host_color_buffer_id == kInvalidHostColorBufferId) {
zx::vmo vmo;
zx_status_t status = color_buffer->vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo);
if (status != ZX_OK) {
fdf::error("Failed to duplicate vmo: {}", zx::make_result(status));
return;
}
{
fbl::AutoLock lock(&lock_);
fidl::WireResult result = control_->GetBufferHandle(std::move(vmo));
if (!result.ok()) {
fdf::error("Failed to call FIDL GetBufferHandle: {}", result.error().FormatDescription());
return;
}
if (result.value().res != ZX_OK) {
fdf::error("Failed to get ColorBuffer handle: {}", zx::make_result(result.value().res));
return;
}
if (result.value().type != fuchsia_hardware_goldfish::BufferHandleType::kColorBuffer) {
fdf::error("Buffer handle type invalid. Expected ColorBuffer, actual {}",
static_cast<uint32_t>(result.value().type));
return;
}
uint32_t render_control_encoded_color_buffer_id = result.value().id;
color_buffer->host_color_buffer_id =
ToHostColorBufferId(render_control_encoded_color_buffer_id);
// Color buffers are in vulkan-only mode by default as that avoids
// unnecessary copies on the host in some cases. The color buffer
// needs to be moved out of vulkan-only mode before being used for
// presentation.
if (color_buffer->host_color_buffer_id != kInvalidHostColorBufferId) {
static constexpr uint32_t kVulkanGlSharedMode = 0;
zx::result<RenderControl::RcResult> status = rc_->SetColorBufferVulkanMode(
color_buffer->host_color_buffer_id, /*mode=*/kVulkanGlSharedMode);
if (status.is_error()) {
fdf::error("Failed to call render control SetColorBufferVulkanMode: {}", status);
}
if (status.value() != 0) {
fdf::error("Render control host failed to set vulkan mode: {}", status.value());
}
}
}
}
async::PostTask(display_event_dispatcher_, [this, config_stamp, color_buffer] {
primary_display_device_.incoming_config = {
.color_buffer = color_buffer,
.config_stamp = config_stamp,
};
});
}
zx::result<> DisplayEngine::SetBufferCollectionConstraints(
const display::ImageBufferUsage& image_buffer_usage,
display::DriverBufferCollectionId buffer_collection_id) {
const auto it = buffer_collections_.find(buffer_collection_id);
if (it == buffer_collections_.end()) {
fdf::error("ImportImage: Cannot find imported buffer collection (id={})", buffer_collection_id);
return zx::error(ZX_ERR_NOT_FOUND);
}
const fidl::SyncClient<fuchsia_sysmem2::BufferCollection>& collection = it->second;
auto constraints =
fuchsia_sysmem2::BufferCollectionConstraints()
.usage(fuchsia_sysmem2::BufferUsage().display(fuchsia_sysmem2::kDisplayUsageLayer))
.buffer_memory_constraints(
fuchsia_sysmem2::BufferMemoryConstraints()
.min_size_bytes(0)
.max_size_bytes(0xFFFFFFFF)
.physically_contiguous_required(true)
.secure_required(false)
.ram_domain_supported(true)
.cpu_domain_supported(true)
.inaccessible_domain_supported(true)
.permitted_heaps(std::vector{
fuchsia_sysmem2::Heap()
.heap_type(bind_fuchsia_sysmem_heap::HEAP_TYPE_SYSTEM_RAM)
.id(0),
fuchsia_sysmem2::Heap()
.heap_type(
bind_fuchsia_goldfish_platform_sysmem_heap::HEAP_TYPE_DEVICE_LOCAL)
.id(0),
}));
std::vector<fuchsia_sysmem2::ImageFormatConstraints> image_constraints_vec;
for (uint32_t i = 0; i < 4; i++) {
auto image_constraints =
fuchsia_sysmem2::ImageFormatConstraints()
.pixel_format(i & 0b01 ? fuchsia_images2::PixelFormat::kR8G8B8A8
: fuchsia_images2::PixelFormat::kB8G8R8A8)
.pixel_format_modifier(
i & 0b10 ? fuchsia_images2::PixelFormatModifier::kLinear
: fuchsia_images2::PixelFormatModifier::kGoogleGoldfishOptimal)
.color_spaces(std::vector{fuchsia_images2::ColorSpace::kSrgb})
.min_size(fuchsia_math::SizeU().width(0).height(0))
.max_size(fuchsia_math::SizeU().width(0xFFFFFFFF).height(0xFFFFFFFF))
.min_bytes_per_row(0)
.max_bytes_per_row(0xFFFFFFFF)
.max_width_times_height(0xFFFFFFFF)
.bytes_per_row_divisor(1)
.start_offset_divisor(1)
.display_rect_alignment(fuchsia_math::SizeU().width(1).height(1));
image_constraints_vec.push_back(std::move(image_constraints));
}
constraints.image_format_constraints(std::move(image_constraints_vec));
fuchsia_sysmem2::BufferCollectionSetConstraintsRequest request;
request.constraints(std::move(constraints));
auto set_result = collection->SetConstraints(std::move(request));
if (set_result.is_error()) {
fdf::error("failed to set constraints: {}", set_result.error_value().status_string());
return zx::error(set_result.error_value().status());
}
return zx::ok();
}
zx::result<> DisplayEngine::SetDisplayPowerMode(display::DisplayId display_id,
display::PowerMode power_mode) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::result<> DisplayEngine::StartCapture(display::DriverCaptureImageId capture_image_id) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::result<> DisplayEngine::ReleaseCapture(display::DriverCaptureImageId capture_image_id) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::result<> DisplayEngine::SetMinimumRgb(uint8_t minimum_rgb) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx_status_t DisplayEngine::SetupPrimaryDisplay() {
// On the host render control protocol, the "invalid" host display ID is used
// to configure the primary display device.
const HostDisplayId kPrimaryHostDisplayId = kInvalidHostDisplayId;
zx::result<RenderControl::RcResult> status =
rc_->SetDisplayPose(kPrimaryHostDisplayId, /*x=*/0, /*y=*/0, primary_display_device_.width_px,
primary_display_device_.height_px);
if (status.is_error()) {
fdf::error("Failed to call render control SetDisplayPose command: {}", status);
return status.error_value();
}
if (status.value() != 0) {
fdf::error("Render control host failed to set display pose: {}", status.value());
return ZX_ERR_INTERNAL;
}
primary_display_device_.expected_next_flush = async::Now(display_event_dispatcher_);
return ZX_OK;
}
void DisplayEngine::FlushPrimaryDisplay(async_dispatcher_t* dispatcher) {
zx::duration period = zx::sec(1) / primary_display_device_.refresh_rate_hz;
zx::time_monotonic expected_next_flush = primary_display_device_.expected_next_flush + period;
if (primary_display_device_.incoming_config.has_value()) {
zx_status_t status = PresentPrimaryDisplayConfig(*primary_display_device_.incoming_config);
ZX_DEBUG_ASSERT(status == ZX_OK || status == ZX_ERR_SHOULD_WAIT);
}
{
fbl::AutoLock lock(&flush_lock_);
if (primary_display_device_.latest_config_stamp != display::kInvalidDriverConfigStamp) {
zx::time_monotonic now = async::Now(dispatcher);
engine_events_->OnDisplayVsync(kPrimaryDisplayId, now,
primary_display_device_.latest_config_stamp);
}
}
// If we've already passed the |expected_next_flush| deadline, skip the
// Vsync and adjust the deadline to the earliest next available frame.
zx::time_monotonic now = async::Now(dispatcher);
if (now > expected_next_flush) {
expected_next_flush +=
period * (((now - expected_next_flush + period).get() - 1L) / period.get());
}
primary_display_device_.expected_next_flush = expected_next_flush;
async::PostTaskForTime(
dispatcher, [this, dispatcher] { FlushPrimaryDisplay(dispatcher); }, expected_next_flush);
}
void DisplayEngine::SetupPrimaryDisplayForTesting(int32_t width_px, int32_t height_px,
int32_t refresh_rate_hz) {
primary_display_device_ = {
.width_px = width_px,
.height_px = height_px,
.refresh_rate_hz = refresh_rate_hz,
};
}
} // namespace goldfish