|
|
|
/*
|
|
|
|
* 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 <errno.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "drm-internal.h"
|
|
|
|
#include "renderer-gl/gl-renderer.h"
|
|
|
|
|
|
|
|
#define POISON_PTR ((void *)8)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a drm_crtc for virtual output
|
|
|
|
*
|
|
|
|
* It will leave its ID and pipe zeroed, as virtual outputs should not use real
|
|
|
|
* CRTC's. Also, as this is a fake CRTC, it will not try to populate props.
|
|
|
|
*/
|
|
|
|
static struct drm_crtc *
|
|
|
|
drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
|
|
|
|
{
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
|
|
|
|
crtc = zalloc(sizeof(*crtc));
|
|
|
|
if (!crtc)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
crtc->backend = b;
|
|
|
|
crtc->output = output;
|
|
|
|
|
|
|
|
crtc->crtc_id = 0;
|
|
|
|
crtc->pipe = 0;
|
|
|
|
|
|
|
|
/* Poisoning the pointers as CRTC's of virtual outputs should not be
|
|
|
|
* added to the DRM-backend CRTC list. With this we can assure (in
|
|
|
|
* function drm_virtual_crtc_destroy()) that this did not happen. */
|
|
|
|
crtc->link.prev = POISON_PTR;
|
|
|
|
crtc->link.next = POISON_PTR;
|
|
|
|
|
|
|
|
return crtc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy drm_crtc created by drm_virtual_crtc_create()
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
drm_virtual_crtc_destroy(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
assert(crtc->link.prev == POISON_PTR);
|
|
|
|
assert(crtc->link.next == POISON_PTR);
|
|
|
|
free(crtc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
struct weston_drm_format *fmt;
|
|
|
|
uint64_t mod;
|
|
|
|
|
|
|
|
plane = zalloc(sizeof(*plane));
|
|
|
|
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;
|
|
|
|
|
|
|
|
weston_drm_format_array_init(&plane->formats);
|
|
|
|
fmt = weston_drm_format_array_add_format(&plane->formats, output->gbm_format);
|
|
|
|
if (!fmt)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* If output supports linear modifier, we add it to the plane.
|
|
|
|
* Otherwise we add DRM_FORMAT_MOD_INVALID, as explicit modifiers
|
|
|
|
* are not supported. */
|
|
|
|
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers)
|
|
|
|
mod = DRM_FORMAT_MOD_LINEAR;
|
|
|
|
else
|
|
|
|
mod = DRM_FORMAT_MOD_INVALID;
|
|
|
|
|
|
|
|
if (weston_drm_format_add_modifier(fmt, mod) < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
weston_plane_init(&plane->base, b->compositor, 0, 0);
|
|
|
|
wl_list_insert(&b->plane_list, &plane->link);
|
|
|
|
|
|
|
|
return plane;
|
|
|
|
|
|
|
|
err:
|
|
|
|
drm_plane_state_free(plane->state_cur, true);
|
|
|
|
weston_drm_format_array_fini(&plane->formats);
|
|
|
|
free(plane);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
weston_drm_format_array_fini(&plane->formats);
|
|
|
|
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,
|
compositor: remove repaint_data from compositor
The repaint_data is entirely backend specific. Moreover, it is only used by the
drm backend, while other backends ignore the repaint data.
There will always be only one repaint active, thus, there is no need to pass the
repaint data from the outside.
The repaint_data breaks with the multi-backend series, which calls repaint begin
for all backends to get the repaint_data. The repaint_data of the last backend
will then be passed to all other backend. At the moment, this works, because the
drm backend is the only backend that implements the begin_repaint call.
Another option would be to track the repaint data per backend in the compositor,
but actually, it the backend needs to track state across the calls, it's its own
responsibility.
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
3 years ago
|
|
|
pixman_region32_t *damage)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
struct drm_pending_state *pending_state;
|
|
|
|
struct drm_backend *backend;
|
|
|
|
|
|
|
|
assert(output->virtual);
|
|
|
|
|
|
|
|
backend = output->backend;
|
|
|
|
pending_state = backend->repaint_data;
|
|
|
|
|
|
|
|
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);
|
|
|
|
drm_virtual_crtc_destroy(output->crtc);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
struct drm_backend *b = to_drm_backend(c);
|
|
|
|
|
|
|
|
output = zalloc(sizeof *output);
|
|
|
|
if (!output)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
output->crtc = drm_virtual_crtc_create(b, output);
|
|
|
|
if (!output->crtc) {
|
|
|
|
free(output);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
output->virtual = true;
|
|
|
|
output->backend = b;
|
|
|
|
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));
|
|
|
|
}
|