blob: 9190c4bf82c6eacc48ea6d114ce29344dd0ae636 [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.
#ifndef SRC_GRAPHICS_DISPLAY_DRIVERS_FAKE_FAKE_DISPLAY_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_FAKE_FAKE_DISPLAY_H_
#include <fidl/fuchsia.sysmem/cpp/fidl.h>
#include <fuchsia/hardware/display/controller/cpp/banjo.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/inspector.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <threads.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <unordered_map>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include "src/graphics/display/drivers/fake/image-info.h"
#include "src/graphics/display/lib/api-types-cpp/config-stamp.h"
#include "src/graphics/display/lib/api-types-cpp/driver-buffer-collection-id.h"
#include "src/graphics/display/lib/api-types-cpp/driver-capture-image-id.h"
#include "src/graphics/display/lib/api-types-cpp/driver-image-id.h"
namespace fake_display {
struct FakeDisplayDeviceConfig {
// If enabled, the fake display device will not automatically emit Vsync
// events. `SendVsync()` must be called to emit a Vsync event manually.
bool manual_vsync_trigger = false;
// If true, the fake display device will never access imported image buffers,
// and it will not add extra image format constraints to the imported buffer
// collection.
// Otherwise, it may add extra BufferCollection constraints to ensure that the
// allocated image buffers support CPU access, and may access the imported
// image buffers for capturing.
// Display capture is supported iff this field is false.
//
// TODO(https://fxbug.dev/42079320): This is a temporary workaround to support fake
// display device for GPU devices that cannot render into CPU-accessible
// formats directly. Remove this option when we have a fake Vulkan
// implementation.
bool no_buffer_access = false;
};
class FakeDisplay : public ddk::DisplayControllerImplProtocol<FakeDisplay> {
public:
explicit FakeDisplay(FakeDisplayDeviceConfig device_config,
fidl::ClientEnd<fuchsia_sysmem::Allocator> sysmem_allocator,
inspect::Inspector inspector);
FakeDisplay(const FakeDisplay&) = delete;
FakeDisplay& operator=(const FakeDisplay&) = delete;
~FakeDisplay();
// Initialization work that is not suitable for the constructor.
//
// Must be called exactly once for each FakeDisplay instance.
zx_status_t Initialize();
// This method is idempotent.
void Deinitialize();
// DisplayControllerImplProtocol implementation:
void DisplayControllerImplSetDisplayControllerInterface(
const display_controller_interface_protocol_t* intf);
void DisplayControllerImplResetDisplayControllerInterface();
zx_status_t DisplayControllerImplImportBufferCollection(
uint64_t banjo_driver_buffer_collection_id, zx::channel collection_token);
zx_status_t DisplayControllerImplReleaseBufferCollection(
uint64_t banjo_driver_buffer_collection_id);
zx_status_t DisplayControllerImplImportImage(const image_metadata_t* image_metadata,
uint64_t banjo_driver_buffer_collection_id,
uint32_t index, uint64_t* out_image_handle);
void DisplayControllerImplReleaseImage(uint64_t image_handle);
config_check_result_t DisplayControllerImplCheckConfiguration(
const display_config_t* display_configs, size_t display_count,
client_composition_opcode_t* out_client_composition_opcodes_list,
size_t client_composition_opcodes_count, size_t* out_client_composition_opcodes_actual);
void DisplayControllerImplApplyConfiguration(const display_config_t* display_configs,
size_t display_count,
const config_stamp_t* banjo_config_stamp);
void DisplayControllerImplSetEld(uint64_t display_id, const uint8_t* raw_eld_list,
size_t raw_eld_count);
zx_status_t DisplayControllerImplSetBufferCollectionConstraints(
const image_buffer_usage_t* usage, uint64_t banjo_driver_buffer_collection_id);
zx_status_t DisplayControllerImplSetDisplayPower(uint64_t display_id, bool power_on);
zx_status_t DisplayControllerImplImportImageForCapture(uint64_t banjo_driver_buffer_collection_id,
uint32_t index,
uint64_t* out_capture_handle)
__TA_EXCLUDES(capture_mutex_);
bool DisplayControllerImplIsCaptureSupported();
zx_status_t DisplayControllerImplStartCapture(uint64_t capture_handle)
__TA_EXCLUDES(capture_mutex_);
zx_status_t DisplayControllerImplReleaseCapture(uint64_t capture_handle)
__TA_EXCLUDES(capture_mutex_);
bool DisplayControllerImplIsCaptureCompleted() __TA_EXCLUDES(capture_mutex_);
zx_status_t DisplayControllerImplSetMinimumRgb(uint8_t minimum_rgb);
const display_controller_impl_protocol_t* display_controller_impl_banjo_protocol() const {
return &display_controller_impl_banjo_protocol_;
}
bool IsCaptureSupported() const;
void SendVsync();
// Just for display core unittests.
zx::result<display::DriverImageId> ImportVmoImageForTesting(zx::vmo vmo, size_t offset);
size_t TEST_imported_images_count() const {
fbl::AutoLock lock(&image_mutex_);
return imported_images_.size();
}
uint8_t GetClampRgbValue() const {
fbl::AutoLock lock(&capture_mutex_);
return clamp_rgb_value_;
}
const inspect::Inspector& inspector() const { return inspector_; }
private:
enum class BufferCollectionUsage : int32_t;
zx_status_t SetupDisplayInterface();
int VSyncThread();
int CaptureThread() __TA_EXCLUDES(capture_mutex_, image_mutex_);
void PopulateAddedDisplayArgs(added_display_args_t* args);
// Initializes the sysmem Allocator client used to import incoming buffer
// collection tokens.
//
// On success, returns ZX_OK and the sysmem allocator client will be open
// until the device is released.
zx_status_t InitSysmemAllocatorClient();
fuchsia_sysmem::BufferCollectionConstraints CreateBufferCollectionConstraints(
BufferCollectionUsage usage);
// Constraints applicable to all buffers used for display images.
void SetBufferMemoryConstraints(fuchsia_sysmem::BufferMemoryConstraints& constraints);
// Constraints applicable to all image buffers used in Display.
void SetCommonImageFormatConstraints(fuchsia_sysmem::PixelFormatType pixel_format_type,
fuchsia_sysmem::FormatModifier format_modifier,
fuchsia_sysmem::ImageFormatConstraints& constraints);
// Constraints applicable to images buffers used in image capture.
void SetCaptureImageFormatConstraints(fuchsia_sysmem::ImageFormatConstraints& constraints);
// Constraints applicable to image buffers that will be bound to layers.
void SetLayerImageFormatConstraints(fuchsia_sysmem::ImageFormatConstraints& constraints);
// Records the display config to the inspector's root node. The root node must
// be already initialized.
void RecordDisplayConfigToInspectRootNode();
// Banjo vtable for fuchsia.hardware.display.controller.DisplayControllerImpl.
const display_controller_impl_protocol_t display_controller_impl_banjo_protocol_;
FakeDisplayDeviceConfig device_config_;
std::atomic_bool vsync_shutdown_flag_ = false;
std::atomic_bool capture_shutdown_flag_ = false;
// Thread handles. Only used on the thread that starts/stops us.
bool vsync_thread_running_ = false;
thrd_t vsync_thread_;
thrd_t capture_thread_;
// Guards display coordinator interface.
mutable fbl::Mutex interface_mutex_;
// Guards imported images and references to imported images.
mutable fbl::Mutex image_mutex_;
// Guards imported capture buffers, capture interface and state.
// `capture_mutex_` must never be acquired when `image_mutex_` is already
// held.
mutable fbl::Mutex capture_mutex_;
// The sysmem allocator client used to bind incoming buffer collection tokens.
fidl::SyncClient<fuchsia_sysmem::Allocator> sysmem_;
// Imported sysmem buffer collections.
std::unordered_map<display::DriverBufferCollectionId,
fidl::SyncClient<fuchsia_sysmem::BufferCollection>>
buffer_collections_;
// Imported display images, keyed by image ID.
DisplayImageInfo::HashTable imported_images_ TA_GUARDED(image_mutex_);
// ID of the current image to be displayed and captured.
// Stores `kInvalidDriverImageId` if there is no image displaying on the fake
// display.
display::DriverImageId current_image_to_capture_id_ TA_GUARDED(image_mutex_) =
display::kInvalidDriverImageId;
// The driver image ID for the next display image to be imported to the
// device.
// Note: we cannot use std::atomic here, since std::atomic only allows
// built-in integral and floating types to do atomic arithmetics.
display::DriverImageId next_imported_display_driver_image_id_ TA_GUARDED(image_mutex_) =
display::DriverImageId(1);
// Imported capture images, keyed by image ID.
CaptureImageInfo::HashTable imported_captures_ TA_GUARDED(capture_mutex_);
// ID of the next capture target image imported to the fake display device
// to capture displayed contents into.
// Stores `kInvalidDriverCaptureImageId` if capture is not going to be
// performed.
display::DriverCaptureImageId current_capture_target_image_id_ TA_GUARDED(capture_mutex_) =
display::kInvalidDriverCaptureImageId;
// The driver capture image ID for the next image to be imported to the
// device.
display::DriverCaptureImageId next_imported_driver_capture_image_id_ TA_GUARDED(capture_mutex_) =
display::DriverCaptureImageId(1);
// The most recently applied config stamp.
std::atomic<display::ConfigStamp> current_config_stamp_ = display::kInvalidConfigStamp;
// Capture complete is signaled at vsync time. This counter introduces a bit of delay
// for signal capture complete
uint64_t capture_complete_signal_count_ TA_GUARDED(capture_mutex_) = 0;
// Minimum value of RGB channels, via the SetMinimumRgb() method.
//
// This is associated with the display capture lock so we have the option to
// reflect the clamping when we simulate display capture.
uint8_t clamp_rgb_value_ TA_GUARDED(capture_mutex_) = 0;
// Display controller related data
ddk::DisplayControllerInterfaceProtocolClient controller_interface_client_
TA_GUARDED(interface_mutex_);
inspect::Inspector inspector_;
bool initialized_ = false;
};
} // namespace fake_display
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_FAKE_FAKE_DISPLAY_H_