compositor-drm: Add Support virtual output
Add support virtual output for streaming image to remote output by remoting-plugin which will be added by the patch: "Add remoting plugin for output streaming." The gbm bo of virtual output is the linear format. Virtual output is implemented based on a patch by Grigory Kletsko <grigory.kletsko@cogentembedded.com>. Signed-off-by: Tomohito Esaki <etom@igel.co.jp>
This commit is contained in:
parent
718a40b49c
commit
b1fb00dbcd
|
@ -74,6 +74,10 @@
|
|||
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
|
||||
#endif
|
||||
|
||||
#ifndef GBM_BO_USE_LINEAR
|
||||
#define GBM_BO_USE_LINEAR (1 << 4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A small wrapper to print information into the 'drm-backend' debug scope.
|
||||
*
|
||||
|
@ -547,6 +551,10 @@ struct drm_output {
|
|||
struct wl_listener recorder_frame_listener;
|
||||
|
||||
struct wl_event_source *pageflip_timer;
|
||||
|
||||
bool virtual;
|
||||
|
||||
submit_frame_cb virtual_submit_frame;
|
||||
};
|
||||
|
||||
static const char *const aspect_ratio_as_string[] = {
|
||||
|
@ -859,6 +867,9 @@ drm_output_update_msc(struct drm_output *output, unsigned int seq);
|
|||
static void
|
||||
drm_output_destroy(struct weston_output *output_base);
|
||||
|
||||
static void
|
||||
drm_virtual_output_destroy(struct weston_output *output_base);
|
||||
|
||||
/**
|
||||
* Returns true if the plane can be used on the given output for its current
|
||||
* repaint cycle.
|
||||
|
@ -868,6 +879,9 @@ drm_plane_is_available(struct drm_plane *plane, struct drm_output *output)
|
|||
{
|
||||
assert(plane->state_cur);
|
||||
|
||||
if (output->virtual)
|
||||
return false;
|
||||
|
||||
/* The plane still has a request not yet completed by the kernel. */
|
||||
if (!plane->state_cur->complete)
|
||||
return false;
|
||||
|
@ -2668,6 +2682,8 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
|||
}
|
||||
|
||||
wl_list_for_each(output_state, &pending_state->output_list, link) {
|
||||
if (output_state->output->virtual)
|
||||
continue;
|
||||
if (mode == DRM_STATE_APPLY_SYNC)
|
||||
assert(output_state->dpms == WESTON_DPMS_OFF);
|
||||
ret |= drm_output_apply_state_atomic(output_state, req, &flags);
|
||||
|
@ -2777,6 +2793,12 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
|||
struct drm_output *output = output_state->output;
|
||||
int ret;
|
||||
|
||||
if (output->virtual) {
|
||||
drm_output_assign_state(output_state,
|
||||
DRM_STATE_APPLY_ASYNC);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = drm_output_apply_state_legacy(output_state);
|
||||
if (ret != 0) {
|
||||
weston_log("Couldn't apply state for output %s\n",
|
||||
|
@ -2855,6 +2877,8 @@ drm_output_repaint(struct weston_output *output_base,
|
|||
struct drm_output_state *state = NULL;
|
||||
struct drm_plane_state *scanout_state;
|
||||
|
||||
assert(!output->virtual);
|
||||
|
||||
if (output->disable_pending || output->destroy_pending)
|
||||
goto err;
|
||||
|
||||
|
@ -3729,7 +3753,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
|
|||
drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
|
||||
output_base->name, (unsigned long) output_base->id);
|
||||
|
||||
if (!b->sprites_are_broken) {
|
||||
if (!b->sprites_are_broken && !output->virtual) {
|
||||
state = drm_output_propose_state(output_base, pending_state,
|
||||
DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
|
||||
if (!state) {
|
||||
|
@ -4448,6 +4472,63 @@ drm_plane_destroy(struct drm_plane *plane)
|
|||
free(plane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a drm_plane for virtual output
|
||||
*
|
||||
* Call drm_virtual_plane_destroy to clean up the plane.
|
||||
*
|
||||
* @param b DRM compositor backend
|
||||
* @param output Output to create internal plane for
|
||||
*/
|
||||
static struct drm_plane *
|
||||
drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
{
|
||||
struct drm_plane *plane;
|
||||
|
||||
/* num of formats is one */
|
||||
plane = zalloc(sizeof(*plane) + sizeof(plane->formats[0]));
|
||||
if (!plane) {
|
||||
weston_log("%s: out of memory\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plane->type = WDRM_PLANE_TYPE_PRIMARY;
|
||||
plane->backend = b;
|
||||
plane->state_cur = drm_plane_state_alloc(NULL, plane);
|
||||
plane->state_cur->complete = true;
|
||||
plane->formats[0].format = output->gbm_format;
|
||||
plane->count_formats = 1;
|
||||
if (output->gbm_bo_flags & GBM_BO_USE_LINEAR) {
|
||||
uint64_t *modifiers = zalloc(sizeof *modifiers);
|
||||
if (modifiers) {
|
||||
*modifiers = DRM_FORMAT_MOD_LINEAR;
|
||||
plane->formats[0].modifiers = modifiers;
|
||||
plane->formats[0].count_modifiers = 1;
|
||||
}
|
||||
}
|
||||
|
||||
weston_plane_init(&plane->base, b->compositor, 0, 0);
|
||||
wl_list_insert(&b->plane_list, &plane->link);
|
||||
|
||||
return plane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy one DRM plane
|
||||
*
|
||||
* @param plane Plane to deallocate (will be freed)
|
||||
*/
|
||||
static void
|
||||
drm_virtual_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
drm_plane_state_free(plane->state_cur, true);
|
||||
weston_plane_release(&plane->base);
|
||||
wl_list_remove(&plane->link);
|
||||
if (plane->formats[0].modifiers)
|
||||
free(plane->formats[0].modifiers);
|
||||
free(plane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise sprites (overlay planes)
|
||||
*
|
||||
|
@ -4681,6 +4762,8 @@ drm_output_init_backlight(struct drm_output *output)
|
|||
*
|
||||
* If we are called as part of repaint, we simply set the relevant bit in
|
||||
* state and return.
|
||||
*
|
||||
* This function is never called on a virtual output.
|
||||
*/
|
||||
static void
|
||||
drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
|
||||
|
@ -4691,6 +4774,8 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
|
|||
struct drm_output_state *state;
|
||||
int ret;
|
||||
|
||||
assert(!output->virtual);
|
||||
|
||||
if (output->state_cur->dpms == level)
|
||||
return;
|
||||
|
||||
|
@ -5591,6 +5676,9 @@ drm_output_set_mode(struct weston_output *base,
|
|||
|
||||
struct drm_mode *current;
|
||||
|
||||
if (output->virtual)
|
||||
return -1;
|
||||
|
||||
if (drm_output_update_modelist_from_heads(output) < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -5948,6 +6036,8 @@ drm_output_enable(struct weston_output *base)
|
|||
drmModeRes *resources;
|
||||
int ret;
|
||||
|
||||
assert(!output->virtual);
|
||||
|
||||
resources = drmModeGetResources(b->drm.fd);
|
||||
if (!resources) {
|
||||
weston_log("drmModeGetResources failed\n");
|
||||
|
@ -6045,6 +6135,8 @@ drm_output_destroy(struct weston_output *base)
|
|||
struct drm_output *output = to_drm_output(base);
|
||||
struct drm_backend *b = to_drm_backend(base->compositor);
|
||||
|
||||
assert(!output->virtual);
|
||||
|
||||
if (output->page_flip_pending || output->vblank_pending ||
|
||||
output->atomic_complete_pending) {
|
||||
output->destroy_pending = 1;
|
||||
|
@ -6073,6 +6165,8 @@ drm_output_disable(struct weston_output *base)
|
|||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
|
||||
assert(!output->virtual);
|
||||
|
||||
if (output->page_flip_pending || output->vblank_pending ||
|
||||
output->atomic_complete_pending) {
|
||||
output->disable_pending = 1;
|
||||
|
@ -6909,12 +7003,269 @@ renderer_switch_binding(struct weston_keyboard *keyboard,
|
|||
switch_to_gl_renderer(b);
|
||||
}
|
||||
|
||||
static void
|
||||
drm_virtual_output_start_repaint_loop(struct weston_output *output_base)
|
||||
{
|
||||
weston_output_finish_frame(output_base, NULL,
|
||||
WP_PRESENTATION_FEEDBACK_INVALID);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_virtual_output_submit_frame(struct drm_output *output,
|
||||
struct drm_fb *fb)
|
||||
{
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
int fd, ret;
|
||||
|
||||
assert(fb->num_planes == 1);
|
||||
ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd);
|
||||
if (ret) {
|
||||
weston_log("drmPrimeHandleFD failed, errno=%d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
drm_fb_ref(fb);
|
||||
ret = output->virtual_submit_frame(&output->base, fd, fb->strides[0],
|
||||
fb);
|
||||
if (ret < 0) {
|
||||
drm_fb_unref(fb);
|
||||
close(fd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
drm_virtual_output_repaint(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
{
|
||||
struct drm_pending_state *pending_state = repaint_data;
|
||||
struct drm_output_state *state = NULL;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_plane_state *scanout_state;
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
if (output->disable_pending || output->destroy_pending)
|
||||
goto err;
|
||||
|
||||
/* Drop frame if there isn't free buffers */
|
||||
if (!gbm_surface_has_free_buffers(output->gbm_surface)) {
|
||||
weston_log("%s: Drop frame!!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(!output->state_last);
|
||||
|
||||
/* If planes have been disabled in the core, we might not have
|
||||
* hit assign_planes at all, so might not have valid output state
|
||||
* here. */
|
||||
state = drm_pending_state_get_output(pending_state, output);
|
||||
if (!state)
|
||||
state = drm_output_state_duplicate(output->state_cur,
|
||||
pending_state,
|
||||
DRM_OUTPUT_STATE_CLEAR_PLANES);
|
||||
|
||||
drm_output_render(state, damage);
|
||||
scanout_state = drm_output_state_get_plane(state, scanout_plane);
|
||||
if (!scanout_state || !scanout_state->fb)
|
||||
goto err;
|
||||
|
||||
if (drm_virtual_output_submit_frame(output, scanout_state->fb) < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
drm_output_state_free(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
drm_virtual_output_deinit(struct weston_output *base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
|
||||
drm_output_fini_egl(output);
|
||||
|
||||
drm_virtual_plane_destroy(output->scanout_plane);
|
||||
}
|
||||
|
||||
static void
|
||||
drm_virtual_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
if (output->base.enabled)
|
||||
drm_virtual_output_deinit(&output->base);
|
||||
|
||||
weston_output_release(&output->base);
|
||||
|
||||
drm_output_state_free(output->state_cur);
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_virtual_output_enable(struct weston_output *output_base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *b = to_drm_backend(output_base->compositor);
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
if (b->use_pixman) {
|
||||
weston_log("Not support pixman renderer on Virtual output\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!output->virtual_submit_frame) {
|
||||
weston_log("The virtual_submit_frame hook is not set\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
output->scanout_plane = drm_virtual_plane_create(b, output);
|
||||
if (!output->scanout_plane) {
|
||||
weston_log("Failed to find primary plane for output %s\n",
|
||||
output->base.name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (drm_output_init_egl(output, b) < 0) {
|
||||
weston_log("Failed to init output gl state\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
output->base.start_repaint_loop = drm_virtual_output_start_repaint_loop;
|
||||
output->base.repaint = drm_virtual_output_repaint;
|
||||
output->base.assign_planes = drm_assign_planes;
|
||||
output->base.set_dpms = NULL;
|
||||
output->base.switch_mode = NULL;
|
||||
output->base.gamma_size = 0;
|
||||
output->base.set_gamma = NULL;
|
||||
|
||||
weston_compositor_stack_plane(b->compositor,
|
||||
&output->scanout_plane->base,
|
||||
&b->compositor->primary_plane);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
drm_virtual_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
if (output->base.enabled)
|
||||
drm_virtual_output_deinit(&output->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct weston_output *
|
||||
drm_virtual_output_create(struct weston_compositor *c, char *name)
|
||||
{
|
||||
struct drm_output *output;
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
output->virtual = true;
|
||||
output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING;
|
||||
|
||||
weston_output_init(&output->base, c, name);
|
||||
|
||||
output->base.enable = drm_virtual_output_enable;
|
||||
output->base.destroy = drm_virtual_output_destroy;
|
||||
output->base.disable = drm_virtual_output_disable;
|
||||
output->base.attach_head = NULL;
|
||||
|
||||
output->state_cur = drm_output_state_alloc(output, NULL);
|
||||
|
||||
weston_compositor_add_pending_output(&output->base, c);
|
||||
|
||||
return &output->base;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
drm_virtual_output_set_gbm_format(struct weston_output *base,
|
||||
const char *gbm_format)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
struct drm_backend *b = to_drm_backend(base->compositor);
|
||||
|
||||
if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
|
||||
output->gbm_format = b->gbm_format;
|
||||
|
||||
return output->gbm_format;
|
||||
}
|
||||
|
||||
static void
|
||||
drm_virtual_output_set_submit_frame_cb(struct weston_output *output_base,
|
||||
submit_frame_cb cb)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
|
||||
output->virtual_submit_frame = cb;
|
||||
}
|
||||
|
||||
static int
|
||||
drm_virtual_output_get_fence_fd(struct weston_output *output_base)
|
||||
{
|
||||
return gl_renderer->create_fence_fd(output_base);
|
||||
}
|
||||
|
||||
static void
|
||||
drm_virtual_output_buffer_released(struct drm_fb *fb)
|
||||
{
|
||||
drm_fb_unref(fb);
|
||||
}
|
||||
|
||||
static void
|
||||
drm_virtual_output_finish_frame(struct weston_output *output_base,
|
||||
struct timespec *stamp,
|
||||
uint32_t presented_flags)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_plane_state *ps;
|
||||
|
||||
wl_list_for_each(ps, &output->state_cur->plane_list, link)
|
||||
ps->complete = true;
|
||||
|
||||
drm_output_state_free(output->state_last);
|
||||
output->state_last = NULL;
|
||||
|
||||
weston_output_finish_frame(&output->base, stamp, presented_flags);
|
||||
|
||||
/* We can't call this from frame_notify, because the output's
|
||||
* repaint needed flag is cleared just after that */
|
||||
if (output->recorder)
|
||||
weston_output_schedule_repaint(&output->base);
|
||||
}
|
||||
|
||||
static const struct weston_drm_output_api api = {
|
||||
drm_output_set_mode,
|
||||
drm_output_set_gbm_format,
|
||||
drm_output_set_seat,
|
||||
};
|
||||
|
||||
static const struct weston_drm_virtual_output_api virt_api = {
|
||||
drm_virtual_output_create,
|
||||
drm_virtual_output_set_gbm_format,
|
||||
drm_virtual_output_set_submit_frame_cb,
|
||||
drm_virtual_output_get_fence_fd,
|
||||
drm_virtual_output_buffer_released,
|
||||
drm_virtual_output_finish_frame
|
||||
};
|
||||
|
||||
static struct drm_backend *
|
||||
drm_backend_create(struct weston_compositor *compositor,
|
||||
struct weston_drm_backend_config *config)
|
||||
|
@ -7079,6 +7430,14 @@ drm_backend_create(struct weston_compositor *compositor,
|
|||
goto err_udev_monitor;
|
||||
}
|
||||
|
||||
ret = weston_plugin_api_register(compositor,
|
||||
WESTON_DRM_VIRTUAL_OUTPUT_API_NAME,
|
||||
&virt_api, sizeof(virt_api));
|
||||
if (ret < 0) {
|
||||
weston_log("Failed to register virtual output API.\n");
|
||||
goto err_udev_monitor;
|
||||
}
|
||||
|
||||
return b;
|
||||
|
||||
err_udev_monitor:
|
||||
|
|
|
@ -90,6 +90,79 @@ weston_drm_output_get_api(struct weston_compositor *compositor)
|
|||
return (const struct weston_drm_output_api *)api;
|
||||
}
|
||||
|
||||
#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v1"
|
||||
|
||||
struct drm_fb;
|
||||
typedef int (*submit_frame_cb)(struct weston_output *output, int fd,
|
||||
int stride, struct drm_fb *buffer);
|
||||
|
||||
struct weston_drm_virtual_output_api {
|
||||
/** Create virtual output.
|
||||
* This is a low-level function, where the caller is expected to wrap
|
||||
* the weston_output function pointers as necessary to make the virtual
|
||||
* output useful. The caller must set up output make, model, serial,
|
||||
* physical size, the mode list and current mode.
|
||||
*
|
||||
* Returns output on success, NULL on failure.
|
||||
*/
|
||||
struct weston_output* (*create_output)(struct weston_compositor *c,
|
||||
char *name);
|
||||
|
||||
/** Set pixel format same as drm_output set_gbm_format().
|
||||
*
|
||||
* Returns the set format.
|
||||
*/
|
||||
uint32_t (*set_gbm_format)(struct weston_output *output,
|
||||
const char *gbm_format);
|
||||
|
||||
/** Set a callback to be called when the DRM-backend has drawn a new
|
||||
* frame and submits it for display.
|
||||
* The callback will deliver a buffer to the virtual output's the
|
||||
* owner and assumes the buffer is now reserved for the owner. The
|
||||
* callback is called in virtual output repaint function.
|
||||
* The caller must call buffer_released() and finish_frame().
|
||||
*
|
||||
* The callback parameters are output, FD and stride (bytes) of dmabuf,
|
||||
* and buffer (drm_fb) pointer.
|
||||
* The callback returns 0 on success, -1 on failure.
|
||||
*
|
||||
* The submit_frame_cb callback hook is responsible for closing the fd
|
||||
* if it returns success. One needs to call the buffer release and
|
||||
* finish frame functions if and only if this hook returns success.
|
||||
*/
|
||||
void (*set_submit_frame_cb)(struct weston_output *output,
|
||||
submit_frame_cb cb);
|
||||
|
||||
/** Get fd for renderer fence.
|
||||
* The returned fence signals when the renderer job has completed and
|
||||
* the buffer is fully drawn.
|
||||
*
|
||||
* Returns fd on success, -1 on failure.
|
||||
*/
|
||||
int (*get_fence_sync_fd)(struct weston_output *output);
|
||||
|
||||
/** Notify that the caller has finished using buffer */
|
||||
void (*buffer_released)(struct drm_fb *fb);
|
||||
|
||||
/** Notify finish frame
|
||||
* This function allows the output repainting mechanism to advance to
|
||||
* the next frame.
|
||||
*/
|
||||
void (*finish_frame)(struct weston_output *output,
|
||||
struct timespec *stamp,
|
||||
uint32_t presented_flags);
|
||||
};
|
||||
|
||||
static inline const struct weston_drm_virtual_output_api *
|
||||
weston_drm_virtual_output_get_api(struct weston_compositor *compositor)
|
||||
{
|
||||
const void *api;
|
||||
api = weston_plugin_api_get(compositor,
|
||||
WESTON_DRM_VIRTUAL_OUTPUT_API_NAME,
|
||||
sizeof(struct weston_drm_virtual_output_api));
|
||||
return (const struct weston_drm_virtual_output_api *)api;
|
||||
}
|
||||
|
||||
/** The backend configuration struct.
|
||||
*
|
||||
* weston_drm_backend_config contains the configuration used by a DRM
|
||||
|
|
Loading…
Reference in New Issue