blob: ab2f480f1c51a42c5d3d212a50a399a69902778d [file] [log] [blame]
// Copyright 2020 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/coordinator/layer.h"
#include <fidl/fuchsia.hardware.display.types/cpp/wire.h>
#include <fidl/fuchsia.math/cpp/wire.h>
#include <lib/driver/logging/cpp/logger.h>
#include <zircon/assert.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <optional>
#include <utility>
#include <fbl/auto_lock.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/ref_ptr.h>
#include "src/graphics/display/drivers/coordinator/fence.h"
#include "src/graphics/display/drivers/coordinator/image.h"
#include "src/graphics/display/lib/api-types/cpp/config-stamp.h"
#include "src/graphics/display/lib/api-types/cpp/driver-image-id.h"
#include "src/graphics/display/lib/api-types/cpp/event-id.h"
#include "src/graphics/display/lib/api-types/cpp/image-metadata.h"
#include "src/graphics/display/lib/api-types/cpp/layer-id.h"
#include "src/graphics/display/lib/api-types/cpp/rectangle.h"
namespace display_coordinator {
namespace {
display::DriverLayer CreatePlaceholderDriverLayer() {
return display::DriverLayer({
.display_destination = display::Rectangle({.x = 0, .y = 0, .width = 1, .height = 1}),
.image_source = display::Rectangle({.x = 0, .y = 0, .width = 0, .height = 0}),
.image_id = display::kInvalidDriverImageId,
.image_metadata = display::ImageMetadata({
.width = 0,
.height = 0,
.tiling_type = display::ImageTilingType::kLinear,
}),
.fallback_color = display::Color({
.format = display::PixelFormat::kR8G8B8A8,
.bytes = {{0, 0, 0, 0, 0, 0, 0, 0}},
}),
});
}
} // namespace
static_assert(WaitingImageList::kMaxSize ==
fuchsia_hardware_display::wire::kMaxWaitingImagesPerLayer,
"Violation of fuchsia.hardware.display.Coordinator API contract.");
Layer::Layer(display::LayerId id)
: IdMappable(id),
draft_layer_config_(CreatePlaceholderDriverLayer()),
applied_layer_config_(CreatePlaceholderDriverLayer()) {
draft_layer_config_differs_from_applied_ = false;
draft_display_config_list_node_.layer = this;
applied_display_config_list_node_.layer = this;
is_skipped_ = false;
}
Layer::~Layer() {
ZX_DEBUG_ASSERT(!in_use());
waiting_images_.RemoveAllImages();
}
bool Layer::ResolveDraftLayerProperties() {
// If the layer's image configuration changed, get rid of any current images
if (draft_image_config_gen_ != applied_image_config_gen_) {
applied_image_config_gen_ = draft_image_config_gen_;
if (draft_image_ == nullptr) {
fdf::error("Tried to apply configuration with missing image");
return false;
}
waiting_images_.RemoveAllImages();
applied_image_ = nullptr;
}
return true;
}
bool Layer::ResolveDraftImage(FenceCollection* fences, display::ConfigStamp stamp) {
if (draft_image_ != nullptr) {
auto wait_fence = fences->GetFence(draft_image_wait_event_id_);
draft_image_wait_event_id_ = display::kInvalidEventId;
if (auto result = waiting_images_.PushImage(std::move(draft_image_), std::move(wait_fence));
result.is_error()) {
return false;
}
}
// This relates to the strategy used by `Client::ApplyConfig()` to compute the vsync config stamp
// that will be returned to the client (see more detailed comment there). The subtlety is that we
// cannot set the image's stamp above (within the scope of `if (draft_image_) {`); it must be
// done here.
//
// This is because the same image can appear in multiple configs. If we only set the stamp when
// the image moves from `draft_image_` to `waiting_images_`, then we would improperly compute
// the vsync config stamp sent in `CoordinatorListener.OnVsync`. Consequently, the client would
// improperly compute whether a particular image is free to reuse.
waiting_images_.UpdateLatestClientConfigStamp(stamp);
return true;
}
void Layer::ApplyChanges() {
if (!draft_layer_config_differs_from_applied_) {
return;
}
const display::Rectangle& image_source = draft_layer_config_.image_source();
const bool is_solid_color_fill = image_source.width() == 0 && image_source.height() == 0;
if (is_solid_color_fill) {
applied_image_ = nullptr;
waiting_images_.RemoveAllImages();
}
applied_layer_config_ = display::DriverLayer({
.display_destination = draft_layer_config_.display_destination(),
.image_source = draft_layer_config_.image_source(),
.image_id = applied_image_ ? applied_image_->driver_id() : display::kInvalidDriverImageId,
.image_metadata = draft_layer_config_.image_metadata(),
.fallback_color = draft_layer_config_.fallback_color(),
.alpha_mode = draft_layer_config_.alpha_mode(),
.alpha_coefficient = draft_layer_config_.alpha_coefficient(),
.image_source_transformation = draft_layer_config_.image_source_transformation(),
});
draft_layer_config_differs_from_applied_ = false;
}
void Layer::DiscardChanges() {
draft_image_config_gen_ = applied_image_config_gen_;
draft_image_ = nullptr;
if (draft_layer_config_differs_from_applied_) {
draft_layer_config_ = applied_layer_config_;
draft_layer_config_differs_from_applied_ = false;
}
}
bool Layer::CleanUpAllImages() {
RetireDraftImage();
waiting_images_.RemoveAllImages();
return RetireAppliedImage();
}
bool Layer::CleanUpImage(const Image& image) {
if (draft_image_.get() == &image) {
RetireDraftImage();
}
RetireWaitingImage(image);
if (applied_image_.get() == &image) {
return RetireAppliedImage();
}
return false;
}
std::optional<display::ConfigStamp> Layer::GetCurrentClientConfigStamp() const {
if (applied_image_ != nullptr) {
return applied_image_->latest_client_config_stamp();
}
return std::nullopt;
}
bool Layer::ActivateLatestReadyImage() {
fbl::RefPtr<Image> newest_ready_image = waiting_images_.PopNewestReadyImage();
if (!newest_ready_image) {
return false;
}
ZX_DEBUG_ASSERT(applied_image_ == nullptr || (newest_ready_image->latest_client_config_stamp() >
applied_image_->latest_client_config_stamp()));
applied_image_ = std::move(newest_ready_image);
applied_layer_config_ = display::DriverLayer({
.display_destination = applied_layer_config_.display_destination(),
.image_source = applied_layer_config_.image_source(),
.image_id = applied_image_->driver_id(),
.image_metadata = applied_layer_config_.image_metadata(),
.fallback_color = applied_layer_config_.fallback_color(),
.alpha_mode = applied_layer_config_.alpha_mode(),
.alpha_coefficient = applied_layer_config_.alpha_coefficient(),
.image_source_transformation = applied_layer_config_.image_source_transformation(),
});
// TODO(costan): `applied_layer_config_` is updated without updating
// `draft_layer_config_differs_from_applied_`. Is it guaranteed that the
// draft config has changed enough, or will this cause trouble?
return true;
}
bool Layer::AppendToConfigLayerList(fbl::DoublyLinkedList<LayerNode*>& config_layer_list) {
if (draft_display_config_list_node_.InContainer()) {
return false;
}
config_layer_list.push_back(&draft_display_config_list_node_);
return true;
}
void Layer::SetPrimaryConfig(display::ImageMetadata image_metadata) {
const display::Rectangle image_area = {{.x = 0,
.y = 0,
.width = image_metadata.dimensions().width(),
.height = image_metadata.dimensions().height()}};
draft_layer_config_ = display::DriverLayer({
.display_destination = image_area,
.image_source = image_area,
.image_id = display::kInvalidDriverImageId,
.image_metadata = display::ImageMetadata(image_metadata),
.fallback_color = display::Color({
.format = display::PixelFormat::kR8G8B8A8,
.bytes = {{0, 0, 0, 0, 0, 0, 0, 0}},
}),
.alpha_mode = draft_layer_config_.alpha_mode(),
.alpha_coefficient = draft_layer_config_.alpha_coefficient(),
.image_source_transformation = draft_layer_config_.image_source_transformation(),
});
draft_layer_config_differs_from_applied_ = true;
++draft_image_config_gen_;
draft_image_ = nullptr;
}
void Layer::SetPrimaryPosition(display::CoordinateTransformation image_source_transformation,
display::Rectangle image_source,
display::Rectangle display_destination) {
draft_layer_config_ = display::DriverLayer({
.display_destination = display_destination,
.image_source = image_source,
.image_id = draft_layer_config_.image_id(),
.image_metadata = draft_layer_config_.image_metadata(),
.fallback_color = draft_layer_config_.fallback_color(),
.alpha_mode = draft_layer_config_.alpha_mode(),
.alpha_coefficient = draft_layer_config_.alpha_coefficient(),
.image_source_transformation = image_source_transformation,
});
draft_layer_config_differs_from_applied_ = true;
}
void Layer::SetPrimaryAlpha(display::AlphaMode alpha_mode, float alpha_coefficient) {
draft_layer_config_ = display::DriverLayer({
.display_destination = draft_layer_config_.display_destination(),
.image_source = draft_layer_config_.image_source(),
.image_id = draft_layer_config_.image_id(),
.image_metadata = draft_layer_config_.image_metadata(),
.fallback_color = draft_layer_config_.fallback_color(),
.alpha_mode = alpha_mode,
.alpha_coefficient = alpha_coefficient,
.image_source_transformation = draft_layer_config_.image_source_transformation(),
});
draft_layer_config_differs_from_applied_ = true;
}
void Layer::SetColorConfig(display::Color color, display::Rectangle display_destination) {
draft_layer_config_ = display::DriverLayer({
.display_destination = display_destination,
.image_source = display::Rectangle({.x = 0, .y = 0, .width = 0, .height = 0}),
.image_id = display::kInvalidDriverImageId,
.image_metadata = display::ImageMetadata({
.width = 0,
.height = 0,
.tiling_type = display::ImageTilingType::kLinear,
}),
.fallback_color = color,
.alpha_mode = draft_layer_config_.alpha_mode(),
.alpha_coefficient = draft_layer_config_.alpha_coefficient(),
.image_source_transformation = draft_layer_config_.image_source_transformation(),
});
draft_layer_config_differs_from_applied_ = true;
draft_image_ = nullptr;
}
void Layer::SetImage(fbl::RefPtr<Image> image, display::EventId wait_event_id) {
draft_image_ = std::move(image);
draft_image_wait_event_id_ = wait_event_id;
}
bool Layer::MarkFenceReady(Fence& fence) { return waiting_images_.MarkFenceReady(fence); }
bool Layer::HasWaitingImages() const { return waiting_images_.size() > 0; }
void Layer::RetireDraftImage() { draft_image_ = nullptr; }
void Layer::RetireWaitingImage(const Image& image) { waiting_images_.RemoveImage(image); }
bool Layer::RetireAppliedImage() {
if (applied_image_ == nullptr) {
return false;
}
applied_image_ = nullptr;
return applied_display_config_list_node_.InContainer();
}
} // namespace display_coordinator