| // 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_COORDINATOR_CLIENT_H_ |
| #define SRC_GRAPHICS_DISPLAY_DRIVERS_COORDINATOR_CLIENT_H_ |
| |
| #include <fidl/fuchsia.hardware.display.types/cpp/wire.h> |
| #include <fidl/fuchsia.hardware.display/cpp/wire.h> |
| #include <fidl/fuchsia.sysmem/cpp/wire.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/fit/function.h> |
| #include <lib/inspect/cpp/inspect.h> |
| #include <lib/sync/completion.h> |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #include <cstdint> |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <type_traits> |
| #include <variant> |
| #include <vector> |
| |
| #include <fbl/array.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/ring_buffer.h> |
| #include <fbl/vector.h> |
| |
| #include "src/graphics/display/drivers/coordinator/capture-image.h" |
| #include "src/graphics/display/drivers/coordinator/client-id.h" |
| #include "src/graphics/display/drivers/coordinator/client-priority.h" |
| #include "src/graphics/display/drivers/coordinator/controller.h" |
| #include "src/graphics/display/drivers/coordinator/fence.h" |
| #include "src/graphics/display/drivers/coordinator/id-map.h" |
| #include "src/graphics/display/drivers/coordinator/image.h" |
| #include "src/graphics/display/drivers/coordinator/layer.h" |
| #include "src/graphics/display/drivers/coordinator/migration-util.h" |
| #include "src/graphics/display/lib/api-types-cpp/buffer-collection-id.h" |
| #include "src/graphics/display/lib/api-types-cpp/buffer-id.h" |
| #include "src/graphics/display/lib/api-types-cpp/config-stamp.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-layer-id.h" |
| #include "src/graphics/display/lib/api-types-cpp/event-id.h" |
| #include "src/graphics/display/lib/api-types-cpp/image-id.h" |
| #include "src/graphics/display/lib/api-types-cpp/vsync-ack-cookie.h" |
| |
| namespace display { |
| |
| // Almost-POD used by Client to manage display configuration. Public state is used by Controller. |
| class DisplayConfig : public IdMappable<std::unique_ptr<DisplayConfig>, DisplayId> { |
| public: |
| void InitializeInspect(inspect::Node* parent); |
| |
| bool apply_layer_change() { |
| bool ret = pending_apply_layer_change_; |
| pending_apply_layer_change_ = false; |
| pending_apply_layer_change_property_.Set(false); |
| return ret; |
| } |
| |
| // Discards all the pending config (except for pending layers lists) |
| // of a Display's `config`. |
| // |
| // The display pending layers' pending config must be discarded before |
| // `DiscardNonLayerPendingConfig()` is called. |
| void DiscardNonLayerPendingConfig(); |
| |
| int current_layer_count() const { return static_cast<int>(current_.layer_count); } |
| const display_config_t* current_config() const { return ¤t_; } |
| const fbl::DoublyLinkedList<LayerNode*>& get_current_layers() const { return current_layers_; } |
| |
| private: |
| display_config_t current_; |
| display_config_t pending_; |
| |
| bool pending_layer_change_; |
| bool pending_apply_layer_change_; |
| fbl::DoublyLinkedList<LayerNode*> pending_layers_; |
| fbl::DoublyLinkedList<LayerNode*> current_layers_; |
| |
| fbl::Array<CoordinatorPixelFormat> pixel_formats_; |
| |
| bool display_config_change_ = false; |
| |
| friend Client; |
| friend ClientProxy; |
| |
| inspect::Node node_; |
| inspect::BoolProperty pending_layer_change_property_; |
| inspect::BoolProperty pending_apply_layer_change_property_; |
| }; |
| |
| // Helper class for sending events using the same API, regardless if |Client| is |
| // bound to a FIDL connection. This object either holds a binding reference or a |
| // |ServerEnd| that owns the channel, both of which allows sending events |
| // without unsafe channel borrowing. |
| class DisplayControllerBindingState { |
| using Protocol = fuchsia_hardware_display::Coordinator; |
| |
| public: |
| // Constructs an invalid binding state. The user must populate it with an |
| // active binding reference or event sender before events could be sent. |
| DisplayControllerBindingState() = default; |
| |
| explicit DisplayControllerBindingState(fidl::ServerEnd<Protocol> server_end) |
| : binding_state_(std::move(server_end)) {} |
| |
| // Invokes |fn| with an polymorphic object that may be used to send events |
| // in |Protocol|. |
| // |
| // |fn| must be a templated lambda that calls |
| // |fidl::WireSendEvent(arg)->OnSomeEvent| to send |OnSomeEvent| event, and |
| // returns a |fidl::Status|. |
| template <typename EventSenderConsumer> |
| fidl::Status SendEvents(EventSenderConsumer&& fn) { |
| return std::visit( |
| [&](auto&& arg) -> fidl::Status { |
| using T = std::decay_t<decltype(arg)>; |
| if constexpr (std::is_same_v<T, fidl::ServerBindingRef<Protocol>>) { |
| return fn(arg); |
| } |
| if constexpr (std::is_same_v<T, fidl::ServerEnd<Protocol>>) { |
| return fn(arg); |
| } |
| ZX_PANIC("Invalid display controller binding state"); |
| }, |
| binding_state_); |
| } |
| |
| // Sets this object into the bound state, i.e. the server is handling FIDL |
| // messages, and the connect may be managed through |binding|. |
| void SetBound(fidl::ServerBindingRef<Protocol> binding) { binding_state_ = std::move(binding); } |
| |
| // If the object is in the bound state, schedules it to be unbound. |
| void Unbind() { |
| if (auto ref = std::get_if<fidl::ServerBindingRef<Protocol>>(&binding_state_)) { |
| // Note that |binding_state_| will remain in the |
| // |fidl::ServerBindingRef<Protocol>| variant, and future attempts to |
| // send events will fail at runtime. This should be okay since the client |
| // is shutting down when unbinding happens. |
| ref->Unbind(); |
| } |
| } |
| |
| private: |
| std::variant<std::monostate, fidl::ServerBindingRef<Protocol>, fidl::ServerEnd<Protocol>> |
| binding_state_; |
| }; |
| |
| // Manages the state associated with a display coordinator client connection. |
| // |
| // This class is not thread-safe. After initialization, all methods must be |
| // executed on the same thread. |
| class Client final : public fidl::WireServer<fuchsia_hardware_display::Coordinator> { |
| public: |
| // |controller| must outlive this and |proxy|. |
| Client(Controller* controller, ClientProxy* proxy, ClientPriority priority, ClientId client_id); |
| |
| // This is used for testing |
| Client(Controller* controller, ClientProxy* proxy, ClientPriority priority, ClientId client_id, |
| fidl::ServerEnd<fuchsia_hardware_display::Coordinator> server_end); |
| |
| Client(const Client&) = delete; |
| Client& operator=(const Client&) = delete; |
| |
| ~Client() override; |
| |
| fpromise::result<fidl::ServerBindingRef<fuchsia_hardware_display::Coordinator>, zx_status_t> Init( |
| fidl::ServerEnd<fuchsia_hardware_display::Coordinator> server_end); |
| |
| void OnDisplaysChanged(cpp20::span<const DisplayId> added_display_ids, |
| cpp20::span<const DisplayId> removed_display_ids); |
| void SetOwnership(bool is_owner); |
| void ApplyConfig(); |
| |
| void OnFenceFired(FenceReference* fence); |
| |
| void TearDown(); |
| // This is used for testing |
| void TearDownTest(); |
| |
| bool IsValid() const { return running_; } |
| ClientId id() const { return id_; } |
| ClientPriority priority() const { return priority_; } |
| void CaptureCompleted(); |
| |
| uint8_t GetMinimumRgb() const { return client_minimum_rgb_; } |
| |
| // Test helpers |
| size_t TEST_imported_images_count() const { return images_.size(); } |
| |
| void CancelFidlBind() { binding_state_.Unbind(); } |
| |
| DisplayControllerBindingState& binding_state() { return binding_state_; } |
| |
| // Used for testing |
| sync_completion_t* fidl_unbound() { return &fidl_unbound_; } |
| VsyncAckCookie LatestAckedCookie() const { return acked_cookie_; } |
| |
| // fidl::WireServer<fuchsia_hardware_display::Coordinator> overrides: |
| void ImportImage(ImportImageRequestView request, ImportImageCompleter::Sync& _completer) override; |
| void ReleaseImage(ReleaseImageRequestView request, |
| ReleaseImageCompleter::Sync& _completer) override; |
| void ImportEvent(ImportEventRequestView request, ImportEventCompleter::Sync& _completer) override; |
| void ReleaseEvent(ReleaseEventRequestView request, |
| ReleaseEventCompleter::Sync& _completer) override; |
| void CreateLayer(CreateLayerCompleter::Sync& _completer) override; |
| void DestroyLayer(DestroyLayerRequestView request, |
| DestroyLayerCompleter::Sync& _completer) override; |
| void SetDisplayMode(SetDisplayModeRequestView request, |
| SetDisplayModeCompleter::Sync& _completer) override; |
| void SetDisplayColorConversion(SetDisplayColorConversionRequestView request, |
| SetDisplayColorConversionCompleter::Sync& _completer) override; |
| void SetDisplayLayers(SetDisplayLayersRequestView request, |
| SetDisplayLayersCompleter::Sync& _completer) override; |
| void SetLayerPrimaryConfig(SetLayerPrimaryConfigRequestView request, |
| SetLayerPrimaryConfigCompleter::Sync& _completer) override; |
| void SetLayerPrimaryPosition(SetLayerPrimaryPositionRequestView request, |
| SetLayerPrimaryPositionCompleter::Sync& _completer) override; |
| void SetLayerPrimaryAlpha(SetLayerPrimaryAlphaRequestView request, |
| SetLayerPrimaryAlphaCompleter::Sync& _completer) override; |
| void SetLayerColorConfig(SetLayerColorConfigRequestView request, |
| SetLayerColorConfigCompleter::Sync& _completer) override; |
| void SetLayerImage(SetLayerImageRequestView request, |
| SetLayerImageCompleter::Sync& _completer) override; |
| void CheckConfig(CheckConfigRequestView request, CheckConfigCompleter::Sync& _completer) override; |
| void ApplyConfig(ApplyConfigCompleter::Sync& _completer) override; |
| void ApplyConfig2(ApplyConfig2RequestView request, |
| ApplyConfigCompleter::Sync& _completer) override { |
| ZX_PANIC("Not Implemented"); |
| } |
| void GetLatestAppliedConfigStamp(GetLatestAppliedConfigStampCompleter::Sync& _completer) override; |
| void EnableVsync(EnableVsyncRequestView request, EnableVsyncCompleter::Sync& _completer) override; |
| void SetVirtconMode(SetVirtconModeRequestView request, |
| SetVirtconModeCompleter::Sync& _completer) override; |
| void ImportBufferCollection(ImportBufferCollectionRequestView request, |
| ImportBufferCollectionCompleter::Sync& _completer) override; |
| void SetBufferCollectionConstraints( |
| SetBufferCollectionConstraintsRequestView request, |
| SetBufferCollectionConstraintsCompleter::Sync& _completer) override; |
| void ReleaseBufferCollection(ReleaseBufferCollectionRequestView request, |
| ReleaseBufferCollectionCompleter::Sync& _completer) override; |
| |
| void IsCaptureSupported(IsCaptureSupportedCompleter::Sync& _completer) override; |
| |
| void StartCapture(StartCaptureRequestView request, |
| StartCaptureCompleter::Sync& _completer) override; |
| |
| void AcknowledgeVsync(AcknowledgeVsyncRequestView request, |
| AcknowledgeVsyncCompleter::Sync& _completer) override; |
| |
| void SetMinimumRgb(SetMinimumRgbRequestView request, |
| SetMinimumRgbCompleter::Sync& _completer) override; |
| |
| void SetDisplayPower(SetDisplayPowerRequestView request, |
| SetDisplayPowerCompleter::Sync& _completer) override; |
| |
| private: |
| // Cleans up states of all current Images. |
| // Returns true if any current layer has been modified. |
| bool CleanUpAllImages(); |
| |
| // Cleans up layer state associated with an Image. `image` must be valid. |
| // Returns true if a current layer has been modified. |
| bool CleanUpImage(Image& image); |
| void CleanUpCaptureImage(ImageId id); |
| |
| // Displays' pending layers list may have been changed by pending |
| // SetDisplayLayers() operations. |
| // |
| // Restores the pending layer lists of all the Displays to their current |
| // (applied) layer list state respectively, undoing all pending changes to |
| // the layer lists. |
| void SetAllConfigPendingLayersToCurrentLayers(); |
| |
| // `fuchsia.hardware.display/Coordinator.ImportImage()` helper for display |
| // images. |
| // |
| // `image_id` must be unused and `image_metadata` contains metadata for an |
| // image used for display. |
| zx_status_t ImportImageForDisplay(const ImageMetadata& image_metadata, BufferId buffer_id, |
| ImageId image_id); |
| |
| // `fuchsia.hardware.display/Coordinator.ImportImage()` helper for capture |
| // images. |
| // |
| // `image_id` must be unused and `image_metadata` contains metadata for an |
| // image used for capture. |
| zx_status_t ImportImageForCapture(const ImageMetadata& image_metadata, BufferId buffer_id, |
| ImageId image_id); |
| |
| // Discards all the pending config on all Displays and Layers. |
| void DiscardConfig(); |
| |
| Controller* const controller_; |
| ClientProxy* const proxy_; |
| const ClientPriority priority_; |
| const ClientId id_; |
| bool running_; |
| |
| Image::Map images_; |
| CaptureImage::Map capture_images_; |
| |
| DisplayConfig::Map configs_; |
| bool pending_config_valid_ = false; |
| bool is_owner_ = false; |
| |
| // A counter for the number of times the client has successfully applied |
| // a configuration. This does not account for changes due to waiting images. |
| uint32_t client_apply_count_ = 0; |
| ConfigStamp latest_config_stamp_ = kInvalidConfigStamp; |
| |
| // This is the client's clamped RGB value. |
| uint8_t client_minimum_rgb_ = 0; |
| sync_completion_t fidl_unbound_; |
| |
| struct Collections { |
| // The BufferCollection ID used in fuchsia.hardware.display.Controller |
| // protocol. |
| display::DriverBufferCollectionId driver_buffer_collection_id; |
| }; |
| std::map<display::BufferCollectionId, Collections> collection_map_; |
| |
| FenceCollection fences_; |
| |
| Layer::Map layers_; |
| |
| // TODO(fxbug.com/129082): Move to Controller, so values issued using this |
| // counter are globally unique. Do not pass to DriverLayerId values to drivers |
| // until this issue is fixed. |
| DriverLayerId next_driver_layer_id = DriverLayerId(1); |
| |
| void NotifyDisplaysChanged(const int32_t* displays_added, uint32_t added_count, |
| const int32_t* displays_removed, uint32_t removed_count); |
| bool CheckConfig(fuchsia_hardware_display_types::wire::ConfigResult* res, |
| std::vector<fuchsia_hardware_display::wire::ClientCompositionOp>* ops); |
| |
| // The state of the FIDL binding. See comments on |
| // |DisplayControllerBindingState|. |
| DisplayControllerBindingState binding_state_; |
| |
| // Capture related book keeping |
| EventId capture_fence_id_ = kInvalidEventId; |
| |
| // Points to the image whose contents is modified by the current capture. |
| // |
| // Invalid when no is capture in progress. |
| ImageId current_capture_image_id_ = kInvalidImageId; |
| |
| // Tracks an image released by the client while used by a capture. |
| // |
| // The coordinator must ensure that an image remains valid while a display |
| // engine is writing to it. If a client attempts to release the image used by |
| // an in-progress capture, we defer the release operation until the capture |
| // completes. The deferred release is tracked here. |
| ImageId pending_release_capture_image_id_ = kInvalidImageId; |
| |
| VsyncAckCookie acked_cookie_ = kInvalidVsyncAckCookie; |
| }; |
| |
| // ClientProxy manages interactions between its Client instance and the |
| // controller. Methods on this class are thread safe. |
| class ClientProxy { |
| public: |
| // `client_id` is assigned by the Controller to distinguish clients. |
| ClientProxy(Controller* controller, ClientPriority client_priority, ClientId client_id, |
| fit::function<void()> on_client_dead); |
| |
| // This is used for testing |
| ClientProxy(Controller* controller, ClientPriority client_priority, ClientId client_id, |
| fidl::ServerEnd<fuchsia_hardware_display::Coordinator> server_end); |
| |
| ~ClientProxy(); |
| zx_status_t Init(inspect::Node* parent_node, |
| fidl::ServerEnd<fuchsia_hardware_display::Coordinator> server_end); |
| |
| // Schedule a task on the controller loop to close this ClientProxy and |
| // have it be freed. |
| void CloseOnControllerLoop(); |
| |
| // Requires holding controller_->mtx() lock |
| zx_status_t OnDisplayVsync(DisplayId display_id, zx_time_t timestamp, |
| ConfigStamp controller_stamp); |
| void OnDisplaysChanged(cpp20::span<const DisplayId> added_display_ids, |
| cpp20::span<const DisplayId> removed_display_ids); |
| void SetOwnership(bool is_owner); |
| void ReapplyConfig(); |
| zx_status_t OnCaptureComplete(); |
| |
| void EnableVsync(bool enable) { |
| fbl::AutoLock lock(&mtx_); |
| enable_vsync_ = enable; |
| } |
| |
| void EnableCapture(bool enable) { |
| fbl::AutoLock lock(&mtx_); |
| enable_capture_ = enable; |
| } |
| void OnClientDead(); |
| |
| // This function restores client configurations that are not part of |
| // the standard configuration. These configurations are typically one-time |
| // settings that need to get restored once client takes control again. |
| void ReapplySpecialConfigs(); |
| |
| ClientId client_id() const { return handler_.id(); } |
| |
| inspect::Node& node() { return node_; } |
| |
| struct ConfigStampPair { |
| ConfigStamp controller_stamp; |
| ConfigStamp client_stamp; |
| }; |
| std::list<ConfigStampPair>& pending_applied_config_stamps() { |
| return pending_applied_config_stamps_; |
| } |
| |
| // Add a new mapping entry from |stamps.controller_stamp| to |stamp.config_stamp|. |
| // Controller should guarantee that |stamps.controller_stamp| is strictly |
| // greater than existing pending controller stamps. |
| void UpdateConfigStampMapping(ConfigStampPair stamps); |
| |
| // This is used for testing |
| void CloseTest(); |
| |
| // Test helpers |
| size_t TEST_imported_images_count() const { return handler_.TEST_imported_images_count(); } |
| |
| // Define these constants here so we can access it for test |
| |
| static constexpr uint32_t kVsyncBufferSize = 10; |
| |
| // Maximum number of vsync messages sent before an acknowledgement is required. |
| // Half of this limit is provided to clients as part of display info. Assuming a |
| // frame rate of 60hz, clients will be required to acknowledge at least once a second |
| // and driver will stop sending messages after 2 seconds of no acknowledgement |
| static constexpr uint32_t kMaxVsyncMessages = 120; |
| static constexpr uint32_t kVsyncMessagesWatermark = (kMaxVsyncMessages / 2); |
| // At the moment, maximum image handles returned by any driver is 4 which is |
| // equal to number of hardware layers. 8 should be more than enough to allow for |
| // a simple statically allocated array of image_ids for vsync events that are being |
| // stored due to client non-acknowledgement. |
| static constexpr uint32_t kMaxImageHandles = 8; |
| |
| private: |
| friend IntegrationTest; |
| |
| mtx_t mtx_; |
| Controller* const controller_; |
| |
| Client handler_; |
| bool enable_vsync_ __TA_GUARDED(&mtx_) = false; |
| bool enable_capture_ __TA_GUARDED(&mtx_) = false; |
| |
| mtx_t task_mtx_; |
| std::vector<std::unique_ptr<async::Task>> client_scheduled_tasks_ __TA_GUARDED(task_mtx_); |
| |
| // This variable is used to limit the number of errors logged in case of channel oom error |
| static constexpr uint32_t kChannelOomPrintFreq = 600; // 1 per 10 seconds (assuming 60fps) |
| uint32_t chn_oom_print_freq_ = 0; |
| uint64_t total_oom_errors_ = 0; |
| |
| struct VsyncMessageData { |
| DisplayId display_id; |
| zx_time_t timestamp; |
| ConfigStamp config_stamp; |
| }; |
| |
| fbl::RingBuffer<VsyncMessageData, kVsyncBufferSize> buffered_vsync_messages_; |
| VsyncAckCookie initial_cookie_ = VsyncAckCookie(0); |
| VsyncAckCookie cookie_sequence_ = VsyncAckCookie(0); |
| |
| uint64_t number_of_vsyncs_sent_ = 0; |
| VsyncAckCookie last_cookie_sent_ = kInvalidVsyncAckCookie; |
| bool acknowledge_request_sent_ = false; |
| |
| fit::function<void()> on_client_dead_; |
| |
| // Mapping from controller_stamp to client_stamp for all configurations that |
| // are already applied and pending to be presented on the display. |
| // Ordered by |controller_stamp_| in increasing order. |
| std::list<ConfigStampPair> pending_applied_config_stamps_; |
| |
| inspect::Node node_; |
| inspect::BoolProperty is_owner_property_; |
| }; |
| |
| } // namespace display |
| |
| #endif // SRC_GRAPHICS_DISPLAY_DRIVERS_COORDINATOR_CLIENT_H_ |