blob: ffcd548d3cf46559a833fea3e7671a5ae89e66b3 [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.
#pragma once
#include <ddk/protocol/display/controller.h>
#include <ddktl/device.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/intrusive_hash_table.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/receiver.h>
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/wait.h>
#include <lib/fidl/cpp/builder.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <zircon/device/display-controller.h>
#include <zircon/listnode.h>
#include <map>
#include "controller.h"
#include "fence.h"
#include "fuchsia/hardware/display/c/fidl.h"
#include "id-map.h"
#include "image.h"
namespace display {
class Layer;
class Client;
typedef struct layer_node : public fbl::SinglyLinkedListable<layer_node*> {
Layer* layer;
} layer_node_t;
// Almost-POD used by Client to manage layer state. Public state is used by Controller.
class Layer : public IdMappable<fbl::unique_ptr<Layer>> {
public:
fbl::RefPtr<Image> current_image() const { return displayed_image_; }
uint32_t z_order() const { return current_layer_.z_index; }
bool is_skipped() const { return is_skipped_; }
private:
layer_t pending_layer_;
layer_t current_layer_;
// flag indicating that there are changes in pending_layer that
// need to be applied to current_layer.
bool config_change_;
// Event ids passed to SetLayerImage which haven't been applied yet.
uint64_t pending_wait_event_id_;
uint64_t pending_signal_event_id_;
// The image given to SetLayerImage which hasn't been applied yet.
fbl::RefPtr<Image> pending_image_;
// Image which are waiting to be displayed
list_node_t waiting_images_ = LIST_INITIAL_VALUE(waiting_images_);
// The image which has most recently been sent to the display controller impl
fbl::RefPtr<Image> displayed_image_;
// Counters used for keeping track of when the layer's images need to be dropped.
uint64_t pending_image_config_gen_ = 0;
uint64_t current_image_config_gen_ = 0;
int32_t pending_cursor_x_;
int32_t pending_cursor_y_;
int32_t current_cursor_x_;
int32_t current_cursor_y_;
// Storage for a color layer's color data bytes.
uint8_t pending_color_bytes_[4];
uint8_t current_color_bytes_[4];
layer_node_t pending_node_;
layer_node_t current_node_;
// The display this layer was most recently displayed on
uint64_t current_display_id_;
bool is_skipped_;
friend Client;
};
// Almost-POD used by Client to manage display configuration. Public state is used by Controller.
class DisplayConfig : public IdMappable<fbl::unique_ptr<DisplayConfig>> {
public:
bool apply_layer_change() {
bool ret = pending_apply_layer_change_;
pending_apply_layer_change_ = false;
return ret;
}
uint32_t vsync_layer_count() const { return vsync_layer_count_; }
const display_config_t* current_config() const { return &current_; }
const fbl::SinglyLinkedList<layer_node_t*>& 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::SinglyLinkedList<layer_node_t*> pending_layers_;
fbl::SinglyLinkedList<layer_node_t*> current_layers_;
fbl::Array<zx_pixel_format_t> pixel_formats_;
fbl::Array<cursor_info_t> cursor_infos_;
uint32_t vsync_layer_count_;
bool display_config_change_ = false;
friend Client;
friend ClientProxy;
};
// The Client class manages all state associated with an open display client
// connection. Over than initialization, all methods of this class execute on
// on the controller's looper, so no synchronization is necessary.
class Client : private FenceCallback {
public:
Client(Controller* controller, ClientProxy* proxy, bool is_vc);
// This is used for testing
Client(Controller* controller, ClientProxy* proxy, bool is_vc, zx_handle_t server_handle);
~Client();
zx_status_t Init(zx_handle_t server_handle);
void OnDisplaysChanged(const uint64_t* displays_added,
size_t added_count,
const uint64_t* displays_removed,
size_t removed_count);
void SetOwnership(bool is_owner);
void ApplyConfig();
void OnFenceFired(FenceReference* fence) override;
void OnRefForFenceDead(Fence* fence) override;
void TearDown();
// This is used for testing
void TearDownTest();
bool IsValid() { return server_handle_ != ZX_HANDLE_INVALID; }
private:
void HandleImportVmoImage(const fuchsia_hardware_display_ControllerImportVmoImageRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleImportImage(const fuchsia_hardware_display_ControllerImportImageRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleReleaseImage(const fuchsia_hardware_display_ControllerReleaseImageRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleImportEvent(const fuchsia_hardware_display_ControllerImportEventRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleReleaseEvent(const fuchsia_hardware_display_ControllerReleaseEventRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleCreateLayer(const fuchsia_hardware_display_ControllerCreateLayerRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleDestroyLayer(const fuchsia_hardware_display_ControllerDestroyLayerRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetDisplayMode(const fuchsia_hardware_display_ControllerSetDisplayModeRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetDisplayColorConversion(
const fuchsia_hardware_display_ControllerSetDisplayColorConversionRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void
HandleSetDisplayLayers(const fuchsia_hardware_display_ControllerSetDisplayLayersRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerPrimaryConfig(
const fuchsia_hardware_display_ControllerSetLayerPrimaryConfigRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerPrimaryPosition(
const fuchsia_hardware_display_ControllerSetLayerPrimaryPositionRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerPrimaryAlpha(
const fuchsia_hardware_display_ControllerSetLayerPrimaryAlphaRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerCursorConfig(
const fuchsia_hardware_display_ControllerSetLayerCursorConfigRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerCursorPosition(
const fuchsia_hardware_display_ControllerSetLayerCursorPositionRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerColorConfig(
const fuchsia_hardware_display_ControllerSetLayerColorConfigRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetLayerImage(const fuchsia_hardware_display_ControllerSetLayerImageRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleCheckConfig(const fuchsia_hardware_display_ControllerCheckConfigRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleApplyConfig(const fuchsia_hardware_display_ControllerApplyConfigRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleEnableVsync(const fuchsia_hardware_display_ControllerEnableVsyncRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetVirtconMode(const fuchsia_hardware_display_ControllerSetVirtconModeRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleComputeLinearImageStride(
const fuchsia_hardware_display_ControllerComputeLinearImageStrideRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleAllocateVmo(const fuchsia_hardware_display_ControllerAllocateVmoRequest* req,
fidl::Builder* resp_builder, zx_handle_t* handle_out,
bool* has_handle_out, const fidl_type_t** resp_table);
void HandleGetSingleBufferFramebuffer(
const fuchsia_hardware_display_ControllerGetSingleBufferFramebufferRequest* req,
fidl::Builder* resp_builder, zx_handle_t* handle_out, bool* has_handle_out,
const fidl_type_t** resp_table);
void HandleImportBufferCollection(
const fuchsia_hardware_display_ControllerImportBufferCollectionRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleSetBufferCollectionConstraints(
const fuchsia_hardware_display_ControllerSetBufferCollectionConstraintsRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
void HandleReleaseBufferCollection(
const fuchsia_hardware_display_ControllerReleaseBufferCollectionRequest* req,
fidl::Builder* resp_builder, const fidl_type_t** resp_table);
// Cleans up layer state associated with an image. If image == nullptr, then
// cleans up all image state. Return true if a current layer was modified.
bool CleanUpImage(Image* image);
Controller* controller_;
ClientProxy* proxy_;
bool is_vc_;
uint64_t console_fb_display_id_ = -1;
zx_handle_t server_handle_;
uint64_t next_image_id_ = 1; // Only INVALID_ID == 0 is invalid
Image::Map 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;
zx::channel sysmem_allocator_;
struct Collections {
// Sent to the hardware driver.
zx::channel driver;
// If the VC is using this, |kernel| is the collection used for setting
// it as kernel framebuffer.
zx::channel kernel;
};
std::map<uint64_t, Collections> collection_map_;
Fence::Map fences_ __TA_GUARDED(fence_mtx_);
// Mutex held when creating or destroying fences.
mtx_t fence_mtx_;
Layer::Map layers_;
uint64_t next_layer_id = 1;
// TODO(stevensd): Delete this when client stop using SetDisplayImage
uint64_t display_image_layer_ = INVALID_ID;
void HandleControllerApi(async_dispatcher_t* dispatcher, async::WaitBase* self,
zx_status_t status, const zx_packet_signal_t* signal);
async::WaitMethod<Client, &Client::HandleControllerApi> api_wait_{this};
void NotifyDisplaysChanged(const int32_t* displays_added, uint32_t added_count,
const int32_t* displays_removed, uint32_t removed_count);
bool CheckConfig(fidl::Builder* resp_builder);
fbl::RefPtr<FenceReference> GetFence(uint64_t id);
};
// ClientProxy manages interactions between its Client instance and the ddk and the
// controller. Methods on this class are thread safe.
using ClientParent = ddk::Device<ClientProxy, ddk::Closable>;
class ClientProxy : public ClientParent {
public:
ClientProxy(Controller* controller, bool is_vc);
// This is used for testing
ClientProxy(Controller* controller, bool is_vc, zx::channel server_channel);
~ClientProxy();
zx_status_t Init(zx::channel server_channel);
zx_status_t DdkClose(uint32_t flags);
void DdkRelease();
// Requires holding controller_->mtx() lock
zx_status_t OnDisplayVsync(uint64_t display_id, zx_time_t timestamp,
uint64_t* image_ids, size_t count);
void OnDisplaysChanged(const uint64_t* displays_added, size_t added_count,
const uint64_t* displays_removed, size_t removed_count);
void SetOwnership(bool is_owner);
void ReapplyConfig();
// Requires holding controller_->mtx() lock
void EnableVsync(bool enable) {
ZX_DEBUG_ASSERT(mtx_trylock(controller_->mtx()) == thrd_busy);
enable_vsync_ = enable;
}
void OnClientDead();
void Close();
// This is used for testing
void CloseTest();
private:
Controller* controller_;
bool is_vc_;
zx::channel server_channel_;
Client handler_;
bool enable_vsync_ = false;
// 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;
};
} // namespace display