diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index dbd5ace4..d43f292c 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -692,3 +692,28 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data); bool drm_plane_is_available(struct drm_plane *plane, struct drm_output *output); + +void +drm_output_render(struct drm_output_state *state, pixman_region32_t *damage); + +int +parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format); + +extern struct gl_renderer_interface *gl_renderer; + +#ifdef BUILD_DRM_VIRTUAL +extern int +drm_backend_init_virtual_output_api(struct weston_compositor *compositor); +#else +inline static int +drm_backend_init_virtual_output_api(struct weston_compositor *compositor) +{ + return 0; +} +#endif + +int +drm_output_init_egl(struct drm_output *output, struct drm_backend *b); +void +drm_output_fini_egl(struct drm_output *output); + diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c new file mode 100644 index 00000000..ebebbbd2 --- /dev/null +++ b/libweston/backend-drm/drm-virtual.c @@ -0,0 +1,362 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2011 Intel Corporation + * Copyright © 2017, 2018 Collabora, Ltd. + * Copyright © 2017, 2018 General Electric Company + * Copyright (c) 2018 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "drm-internal.h" +#include "renderer-gl/gl-renderer.h" + +/** + * 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) && b->fb_modifiers) { + 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); +} + +static int +drm_virtual_output_start_repaint_loop(struct weston_output *output_base) +{ + weston_output_finish_frame(output_base, NULL, + WP_PRESENTATION_FEEDBACK_INVALID); + + return 0; +} + +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_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 +}; + +int drm_backend_init_virtual_output_api(struct weston_compositor *compositor) +{ + return weston_plugin_api_register(compositor, + WESTON_DRM_VIRTUAL_OUTPUT_API_NAME, + &virt_api, sizeof(virt_api)); +} diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index ac4088a9..d37837dc 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -69,7 +69,7 @@ #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "linux-explicit-synchronization.h" -static struct gl_renderer_interface *gl_renderer; +struct gl_renderer_interface *gl_renderer; static const char default_seat[] = "seat0"; @@ -136,9 +136,6 @@ drm_output_pageflip_timer_create(struct drm_output *output) 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. @@ -331,7 +328,7 @@ drm_output_render_pixman(struct drm_output_state *state, return drm_fb_ref(output->dumb[output->current_image]); } -static void +void drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) { struct drm_output *output = state->output; @@ -630,10 +627,6 @@ drm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data) b->repaint_data = NULL; } -static int -drm_output_init_egl(struct drm_output *output, struct drm_backend *b); -static void -drm_output_fini_egl(struct drm_output *output); static int drm_output_init_pixman(struct drm_output *output, struct drm_backend *b); static void @@ -984,63 +977,6 @@ 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) && b->fb_modifiers) { - 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) * @@ -1335,7 +1271,7 @@ err: } /* Init output state that depends on gl or gbm */ -static int +int drm_output_init_egl(struct drm_output *output, struct drm_backend *b) { uint32_t format[2] = { @@ -1406,7 +1342,7 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b) return 0; } -static void +void drm_output_fini_egl(struct drm_output *output) { struct drm_backend *b = to_drm_backend(output->base.compositor); @@ -1588,7 +1524,7 @@ drm_output_detach_head(struct weston_output *output_base, weston_output_schedule_repaint(output_base); } -static int +int parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format) { const struct pixel_format_info *pinfo; @@ -3107,271 +3043,12 @@ renderer_switch_binding(struct weston_keyboard *keyboard, switch_to_gl_renderer(b); } -static int -drm_virtual_output_start_repaint_loop(struct weston_output *output_base) -{ - weston_output_finish_frame(output_base, NULL, - WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -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) @@ -3549,9 +3226,7 @@ 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)); + ret = drm_backend_init_virtual_output_api(compositor); if (ret < 0) { weston_log("Failed to register virtual output API.\n"); goto err_udev_monitor; diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index 6d6a7239..ee92f125 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -70,6 +70,11 @@ if get_option('backend-drm-screencast-vaapi') config_h.set('BUILD_VAAPI_RECORDER', '1') endif +if get_option('remoting') + srcs_drm += 'drm-virtual.c' + config_h.set('BUILD_DRM_VIRTUAL', '1') +endif + if dep_libdrm.version().version_compare('>= 2.4.71') config_h.set('HAVE_DRM_ADDFB2_MODIFIERS', '1') endif