| // 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/graphics/display/drivers/coordinator/image.h" |
| |
| #include <fuchsia/hardware/display/controller/c/banjo.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/ddk/trace/event.h> |
| #include <lib/zx/vmo.h> |
| #include <threads.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <atomic> |
| #include <utility> |
| |
| #include <fbl/ref_ptr.h> |
| #include <fbl/string_printf.h> |
| |
| #include "src/graphics/display/drivers/coordinator/client-id.h" |
| #include "src/graphics/display/drivers/coordinator/controller.h" |
| #include "src/graphics/display/drivers/coordinator/fence.h" |
| #include "src/graphics/display/lib/api-types-cpp/driver-image-id.h" |
| #include "src/graphics/display/lib/api-types-cpp/image-metadata.h" |
| #include "src/graphics/display/lib/api-types-cpp/image-tiling-type.h" |
| |
| namespace display { |
| |
| Image::Image(Controller* controller, const ImageMetadata& metadata, DriverImageId driver_id, |
| zx::vmo vmo, inspect::Node* parent_node, ClientId client_id) |
| : driver_id_(driver_id), |
| metadata_(metadata), |
| controller_(controller), |
| client_id_(client_id), |
| vmo_(std::move(vmo)) { |
| ZX_DEBUG_ASSERT(metadata.tiling_type() != kImageTilingTypeCapture); |
| InitializeInspect(parent_node); |
| } |
| Image::~Image() { |
| ZX_ASSERT(!std::atomic_load(&in_use_)); |
| ZX_ASSERT(!InDoublyLinkedList()); |
| controller_->ReleaseImage(driver_id_); |
| } |
| |
| void Image::InitializeInspect(inspect::Node* parent_node) { |
| if (!parent_node) |
| return; |
| node_ = parent_node->CreateChild(fbl::StringPrintf("image-%p", this).c_str()); |
| node_.CreateInt("width", metadata_.width(), &properties_); |
| node_.CreateInt("height", metadata_.height(), &properties_); |
| node_.CreateUint("tiling_type", metadata_.tiling_type().ValueForLogging(), &properties_); |
| presenting_property_ = node_.CreateBool("presenting", false); |
| retiring_property_ = node_.CreateBool("retiring", false); |
| } |
| |
| mtx_t* Image::mtx() const { return controller_->mtx(); } |
| |
| bool Image::InDoublyLinkedList() const { return doubly_linked_list_node_state_.InContainer(); } |
| |
| fbl::RefPtr<Image> Image::RemoveFromDoublyLinkedList() { |
| return doubly_linked_list_node_state_.RemoveFromContainer<DefaultDoublyLinkedListTraits>(); |
| } |
| |
| void Image::PrepareFences(fbl::RefPtr<FenceReference>&& wait, |
| fbl::RefPtr<FenceReference>&& retire) { |
| wait_fence_ = std::move(wait); |
| retire_fence_ = std::move(retire); |
| |
| if (wait_fence_) { |
| zx_status_t status = wait_fence_->StartReadyWait(); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to start waiting %d", status); |
| // Mark the image as ready. Displaying garbage is better than hanging or crashing. |
| wait_fence_ = nullptr; |
| } |
| } |
| } |
| |
| bool Image::OnFenceReady(FenceReference* fence) { |
| if (wait_fence_.get() == fence) { |
| wait_fence_ = nullptr; |
| } |
| return wait_fence_ == nullptr; |
| } |
| |
| void Image::StartPresent() { |
| ZX_DEBUG_ASSERT(wait_fence_ == nullptr); |
| ZX_DEBUG_ASSERT(mtx_trylock(mtx()) == thrd_busy); |
| TRACE_DURATION("gfx", "Image::StartPresent", "id", id.value()); |
| TRACE_FLOW_BEGIN("gfx", "present_image", id.value()); |
| |
| presenting_ = true; |
| presenting_property_.Set(true); |
| } |
| |
| void Image::EarlyRetire() { |
| // A client may re-use an image as soon as retire_fence_ fires. Set in_use_ first. |
| std::atomic_store(&in_use_, false); |
| if (wait_fence_) { |
| wait_fence_->SetImmediateRelease(std::move(retire_fence_)); |
| wait_fence_ = nullptr; |
| } else if (retire_fence_) { |
| retire_fence_->Signal(); |
| retire_fence_ = nullptr; |
| } |
| } |
| |
| void Image::RetireWithFence(fbl::RefPtr<FenceReference>&& fence) { |
| // Retire and acquire are not synchronized, so set in_use_ before signaling so |
| // that the image can be reused as soon as the event is signaled. We don't have |
| // to worry about the armed signal fence being overwritten on reuse since it is |
| // on set in StartRetire, which is called under the same lock as OnRetire. |
| std::atomic_store(&in_use_, false); |
| if (fence) { |
| fence->Signal(); |
| } |
| } |
| |
| void Image::StartRetire() { |
| ZX_DEBUG_ASSERT(wait_fence_ == nullptr); |
| ZX_DEBUG_ASSERT(mtx_trylock(mtx()) == thrd_busy); |
| |
| if (!presenting_) { |
| RetireWithFence(std::move(retire_fence_)); |
| } else { |
| retiring_ = true; |
| retiring_property_.Set(true); |
| armed_retire_fence_ = std::move(retire_fence_); |
| } |
| } |
| |
| void Image::OnRetire() { |
| ZX_DEBUG_ASSERT(mtx_trylock(mtx()) == thrd_busy); |
| |
| presenting_ = false; |
| presenting_property_.Set(false); |
| |
| if (retiring_) { |
| RetireWithFence(std::move(armed_retire_fence_)); |
| retiring_ = false; |
| retiring_property_.Set(false); |
| } |
| } |
| |
| void Image::DiscardAcquire() { |
| ZX_DEBUG_ASSERT(wait_fence_ == nullptr); |
| |
| std::atomic_store(&in_use_, false); |
| } |
| |
| bool Image::Acquire() { return !std::atomic_exchange(&in_use_, true); } |
| |
| void Image::ResetFences() { |
| if (wait_fence_) { |
| wait_fence_->ResetReadyWait(); |
| } |
| |
| wait_fence_ = nullptr; |
| armed_retire_fence_ = nullptr; |
| retire_fence_ = nullptr; |
| } |
| |
| bool Image::HasSameDisplayPropertiesAsLayer(const image_t& layer_config) const { |
| // TODO(https://fxbug.dev/42076907): Currently this function only compares size and |
| // usage type between current Image and a given Layer's accepted |
| // configuration. |
| // |
| // We don't set the pixel format a Layer can accept, and we don't compare the |
| // Image's pixel format against any accepted pixel format, assuming that all |
| // image buffers allocated by sysmem can always be used for scanout in any |
| // Layer. Currently, this assumption works for all our existing display engine |
| // drivers. However, switching pixel formats in a Layer may cause performance |
| // reduction, or might be not supported by new display engines / new display |
| // formats. |
| // |
| // We should figure out a mechanism to indicate pixel format / modifiers |
| // support for a Layer's image configuration (as opposed of using image_t), |
| // and compare this Image's sysmem buffer collection information against the |
| // Layer's format support. |
| |
| // The casts will not result in UB, because ImageMetadata's width and height |
| // are guaranteed to be non-negative. |
| return static_cast<uint32_t>(metadata_.width()) == layer_config.width && |
| static_cast<uint32_t>(metadata_.height()) == layer_config.height && |
| metadata_.tiling_type().ToBanjo() == layer_config.tiling_type; |
| } |
| |
| } // namespace display |