From b1fb00dbcdc8898d6bebf70040aded01ff54d365 Mon Sep 17 00:00:00 2001 From: Tomohito Esaki Date: Wed, 31 Jan 2018 17:50:48 +0900 Subject: [PATCH] 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 . Signed-off-by: Tomohito Esaki --- libweston/compositor-drm.c | 361 ++++++++++++++++++++++++++++++++++++- libweston/compositor-drm.h | 73 ++++++++ 2 files changed, 433 insertions(+), 1 deletion(-) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 2c1f6046..e024e66f 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -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: diff --git a/libweston/compositor-drm.h b/libweston/compositor-drm.h index 9c37c153..71a306fd 100644 --- a/libweston/compositor-drm.h +++ b/libweston/compositor-drm.h @@ -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