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:
Tomohito Esaki 2018-01-31 17:50:48 +09:00
parent 718a40b49c
commit b1fb00dbcd
2 changed files with 433 additions and 1 deletions

View File

@ -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:

View File

@ -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