compositor-drm: Use drm_plane for scanout plane

Use a real drm_plane to back the scanout plane, displacing
output->fb_{last,cur,pending} to their plane-tracked equivalents.

Signed-off-by: Daniel Stone <daniels@collabora.com>
Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
dev
Daniel Stone 7 years ago
parent 2ba17f4de5
commit e2e8013633
  1. 222
      libweston/compositor-drm.c

@ -362,17 +362,8 @@ struct drm_output {
struct gbm_surface *gbm_surface; struct gbm_surface *gbm_surface;
uint32_t gbm_format; uint32_t gbm_format;
/* Plane for a fullscreen direct scanout view */ /* Plane being displayed directly on the CRTC */
struct weston_plane scanout_plane; struct drm_plane *scanout_plane;
/* The last framebuffer submitted to the kernel for this CRTC. */
struct drm_fb *fb_current;
/* The previously-submitted framebuffer, where the hardware has not
* yet acknowledged display of fb_current. */
struct drm_fb *fb_last;
/* Framebuffer we are going to submit to the kernel when the current
* repaint is flushed. */
struct drm_fb *fb_pending;
/* The last state submitted to the kernel for this CRTC. */ /* The last state submitted to the kernel for this CRTC. */
struct drm_output_state *state_cur; struct drm_output_state *state_cur;
@ -1369,6 +1360,8 @@ drm_output_assign_state(struct drm_output_state *state,
if (plane->type == WDRM_PLANE_TYPE_OVERLAY) if (plane->type == WDRM_PLANE_TYPE_OVERLAY)
output->vblank_pending++; output->vblank_pending++;
else if (plane->type == WDRM_PLANE_TYPE_PRIMARY)
output->page_flip_pending = 1;
} }
} }
@ -1416,6 +1409,8 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
{ {
struct drm_output *output = output_state->output; struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor); struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport; struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct gbm_bo *bo; struct gbm_bo *bo;
@ -1456,6 +1451,15 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
if (ev->alpha != 1.0f) if (ev->alpha != 1.0f)
return NULL; return NULL;
state = drm_output_state_get_plane(output_state, scanout_plane);
if (state->fb) {
/* If there is already a framebuffer on the scanout plane,
* a client view has already been placed on the scanout
* view. In that case, do not free or put back the state,
* but just leave it in place and quietly exit. */
return NULL;
}
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT); buffer->resource, GBM_BO_USE_SCANOUT);
@ -1465,19 +1469,33 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
format = drm_output_check_scanout_format(output, ev->surface, bo); format = drm_output_check_scanout_format(output, ev->surface, bo);
if (format == 0) { if (format == 0) {
drm_plane_state_put_back(state);
gbm_bo_destroy(bo); gbm_bo_destroy(bo);
return NULL; return NULL;
} }
output->fb_pending = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT); state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
if (!output->fb_pending) { if (!state->fb) {
drm_plane_state_put_back(state);
gbm_bo_destroy(bo); gbm_bo_destroy(bo);
return NULL; return NULL;
} }
drm_fb_set_buffer(output->fb_pending, buffer); drm_fb_set_buffer(state->fb, buffer);
return &output->scanout_plane; state->output = output;
state->src_x = 0;
state->src_y = 0;
state->src_w = state->fb->width << 16;
state->src_h = state->fb->height << 16;
state->dest_x = 0;
state->dest_y = 0;
state->dest_w = output->base.current_mode->width;
state->dest_h = output->base.current_mode->height;
return &scanout_plane->base;
} }
static struct drm_fb * static struct drm_fb *
@ -1542,12 +1560,15 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
{ {
struct drm_output *output = state->output; struct drm_output *output = state->output;
struct weston_compositor *c = output->base.compositor; struct weston_compositor *c = output->base.compositor;
struct drm_plane_state *scanout_state;
struct drm_backend *b = to_drm_backend(c); struct drm_backend *b = to_drm_backend(c);
struct drm_fb *fb; struct drm_fb *fb;
/* If we already have a client buffer promoted to scanout, then we don't /* If we already have a client buffer promoted to scanout, then we don't
* want to render. */ * want to render. */
if (output->fb_pending) scanout_state = drm_output_state_get_plane(state,
output->scanout_plane);
if (scanout_state->fb)
return; return;
if (b->use_pixman) if (b->use_pixman)
@ -1555,9 +1576,24 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
else else
fb = drm_output_render_gl(state, damage); fb = drm_output_render_gl(state, damage);
if (!fb) if (!fb) {
drm_plane_state_put_back(scanout_state);
return; return;
output->fb_pending = fb; }
scanout_state->fb = fb;
scanout_state->output = output;
scanout_state->src_x = 0;
scanout_state->src_y = 0;
scanout_state->src_w = output->base.current_mode->width << 16;
scanout_state->src_h = output->base.current_mode->height << 16;
scanout_state->dest_x = 0;
scanout_state->dest_y = 0;
scanout_state->dest_w = scanout_state->src_w >> 16;
scanout_state->dest_h = scanout_state->src_h >> 16;
pixman_region32_subtract(&c->primary_plane.damage, pixman_region32_subtract(&c->primary_plane.damage,
&c->primary_plane.damage, damage); &c->primary_plane.damage, damage);
@ -1620,6 +1656,8 @@ drm_output_repaint(struct weston_output *output_base,
struct drm_output *output = to_drm_output(output_base); struct drm_output *output = to_drm_output(output_base);
struct drm_backend *backend = struct drm_backend *backend =
to_drm_backend(output->base.compositor); to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps; struct drm_plane_state *ps;
struct drm_plane *p; struct drm_plane *p;
struct drm_mode *mode; struct drm_mode *mode;
@ -1639,7 +1677,6 @@ drm_output_repaint(struct weston_output *output_base,
pending_state, pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES); DRM_OUTPUT_STATE_CLEAR_PLANES);
assert(!output->fb_last);
/* If disable_planes is set then assign_planes() wasn't /* If disable_planes is set then assign_planes() wasn't
* called for this render, so we could still have a stale * called for this render, so we could still have a stale
@ -1652,14 +1689,29 @@ drm_output_repaint(struct weston_output *output_base,
} }
drm_output_render(state, damage); drm_output_render(state, damage);
if (!output->fb_pending) scanout_state = drm_output_state_get_plane(state, scanout_plane);
if (!scanout_state || !scanout_state->fb)
goto err; goto err;
/* The legacy SetCrtc API doesn't allow us to do scaling, and the
* legacy PageFlip API doesn't allow us to do clipping either. */
assert(scanout_state->src_x == 0);
assert(scanout_state->src_y == 0);
assert(scanout_state->src_w ==
(unsigned) (output->base.current_mode->width << 16));
assert(scanout_state->src_h ==
(unsigned) (output->base.current_mode->height << 16));
assert(scanout_state->dest_x == 0);
assert(scanout_state->dest_y == 0);
assert(scanout_state->dest_w == scanout_state->src_w >> 16);
assert(scanout_state->dest_h == scanout_state->src_h >> 16);
mode = container_of(output->base.current_mode, struct drm_mode, base); mode = container_of(output->base.current_mode, struct drm_mode, base);
if (output->state_invalid || !output->fb_current || if (output->state_invalid || !scanout_plane->state_cur->fb ||
output->fb_current->stride != output->fb_pending->stride) { scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
output->fb_pending->fb_id, 0, 0, scanout_state->fb->fb_id,
0, 0,
&output->connector_id, 1, &output->connector_id, 1,
&mode->mode_info); &mode->mode_info);
if (ret) { if (ret) {
@ -1672,18 +1724,13 @@ drm_output_repaint(struct weston_output *output_base,
} }
if (drmModePageFlip(backend->drm.fd, output->crtc_id, if (drmModePageFlip(backend->drm.fd, output->crtc_id,
output->fb_pending->fb_id, scanout_state->fb->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
weston_log("queueing pageflip failed: %m\n"); weston_log("queueing pageflip failed: %m\n");
goto err; goto err;
} }
output->fb_last = output->fb_current;
output->fb_current = output->fb_pending;
output->fb_pending = NULL;
assert(!output->page_flip_pending); assert(!output->page_flip_pending);
output->page_flip_pending = 1;
if (output->pageflip_timer) if (output->pageflip_timer)
wl_event_source_timer_update(output->pageflip_timer, wl_event_source_timer_update(output->pageflip_timer,
@ -1743,10 +1790,7 @@ drm_output_repaint(struct weston_output *output_base,
err: err:
output->cursor_view = NULL; output->cursor_view = NULL;
if (output->fb_pending) {
drm_fb_unref(output->fb_pending);
output->fb_pending = NULL;
}
drm_output_state_free(state); drm_output_state_free(state);
return -1; return -1;
@ -1759,6 +1803,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
struct drm_pending_state *pending_state = NULL; struct drm_pending_state *pending_state = NULL;
struct drm_output_state *state; struct drm_output_state *state;
struct drm_plane_state *plane_state; struct drm_plane_state *plane_state;
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_backend *backend = struct drm_backend *backend =
to_drm_backend(output_base->compositor); to_drm_backend(output_base->compositor);
uint32_t fb_id; uint32_t fb_id;
@ -1775,7 +1820,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
if (output->disable_pending || output->destroy_pending) if (output->disable_pending || output->destroy_pending)
return; return;
if (!output->fb_current) { if (!output->scanout_plane->state_cur->fb) {
/* We can't page flip if there's no mode set */ /* We can't page flip if there's no mode set */
goto finish_frame; goto finish_frame;
} }
@ -1786,6 +1831,8 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
if (output->state_invalid) if (output->state_invalid)
goto finish_frame; goto finish_frame;
assert(scanout_plane->state_cur->output == output);
/* Try to get current msc and timestamp via instant query */ /* Try to get current msc and timestamp via instant query */
vbl.request.type |= drm_waitvblank_pipe(output); vbl.request.type |= drm_waitvblank_pipe(output);
ret = drmWaitVBlank(backend->drm.fd, &vbl); ret = drmWaitVBlank(backend->drm.fd, &vbl);
@ -1815,10 +1862,9 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
/* Immediate query didn't provide valid timestamp. /* Immediate query didn't provide valid timestamp.
* Use pageflip fallback. * Use pageflip fallback.
*/ */
fb_id = output->fb_current->fb_id; fb_id = scanout_plane->state_cur->fb->fb_id;
assert(!output->page_flip_pending); assert(!output->page_flip_pending);
assert(!output->fb_last);
assert(!output->state_last); assert(!output->state_last);
pending_state = drm_pending_state_alloc(backend); pending_state = drm_pending_state_alloc(backend);
@ -1835,9 +1881,6 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
wl_event_source_timer_update(output->pageflip_timer, wl_event_source_timer_update(output->pageflip_timer,
backend->pageflip_timeout); backend->pageflip_timeout);
output->fb_last = drm_fb_ref(output->fb_current);
output->page_flip_pending = 1;
wl_list_for_each(plane_state, &state->plane_list, link) { wl_list_for_each(plane_state, &state->plane_list, link) {
if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY) if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY)
continue; continue;
@ -1909,9 +1952,6 @@ page_flip_handler(int fd, unsigned int frame,
assert(output->page_flip_pending); assert(output->page_flip_pending);
output->page_flip_pending = 0; output->page_flip_pending = 0;
drm_fb_unref(output->fb_last);
output->fb_last = NULL;
if (output->vblank_pending) if (output->vblank_pending)
return; return;
@ -2591,10 +2631,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
* sledgehammer modeswitch first, and only later showing new * sledgehammer modeswitch first, and only later showing new
* content. * content.
*/ */
drm_fb_unref(output->fb_current); output->state_invalid = true;
assert(!output->fb_last);
assert(!output->fb_pending);
output->fb_last = output->fb_current = NULL;
if (b->use_pixman) { if (b->use_pixman) {
drm_output_fini_pixman(output); drm_output_fini_pixman(output);
@ -2917,6 +2954,13 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
case WDRM_PLANE_TYPE_CURSOR: case WDRM_PLANE_TYPE_CURSOR:
format = GBM_FORMAT_ARGB8888; format = GBM_FORMAT_ARGB8888;
break; break;
case WDRM_PLANE_TYPE_PRIMARY:
/* We don't know what formats the primary plane supports
* before universal planes, so we just assume that the
* GBM format works; however, this isn't set until after
* the output is created. */
format = 0;
break;
default: default:
assert(!"invalid type in drm_output_find_special_plane"); assert(!"invalid type in drm_output_find_special_plane");
break; break;
@ -2939,14 +2983,16 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
* same plane for two outputs. */ * same plane for two outputs. */
wl_list_for_each(tmp, &b->compositor->pending_output_list, wl_list_for_each(tmp, &b->compositor->pending_output_list,
base.link) { base.link) {
if (tmp->cursor_plane == plane) { if (tmp->cursor_plane == plane ||
tmp->scanout_plane == plane) {
found_elsewhere = true; found_elsewhere = true;
break; break;
} }
} }
wl_list_for_each(tmp, &b->compositor->output_list, wl_list_for_each(tmp, &b->compositor->output_list,
base.link) { base.link) {
if (tmp->cursor_plane == plane) { if (tmp->cursor_plane == plane ||
tmp->scanout_plane == plane) {
found_elsewhere = true; found_elsewhere = true;
break; break;
} }
@ -3352,6 +3398,19 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
static void static void
drm_output_fini_egl(struct drm_output *output) drm_output_fini_egl(struct drm_output *output)
{ {
struct drm_backend *b = to_drm_backend(output->base.compositor);
/* Destroying the GBM surface will destroy all our GBM buffers,
* regardless of refcount. Ensure we destroy them here. */
if (!b->shutting_down &&
output->scanout_plane->state_cur->fb &&
output->scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE) {
drm_plane_state_free(output->scanout_plane->state_cur, true);
output->scanout_plane->state_cur =
drm_plane_state_alloc(NULL, output->scanout_plane);
output->scanout_plane->state_cur->complete = true;
}
gl_renderer->output_destroy(&output->base); gl_renderer->output_destroy(&output->base);
gbm_surface_destroy(output->gbm_surface); gbm_surface_destroy(output->gbm_surface);
drm_output_fini_cursor_egl(output); drm_output_fini_cursor_egl(output);
@ -3417,8 +3476,20 @@ err:
static void static void
drm_output_fini_pixman(struct drm_output *output) drm_output_fini_pixman(struct drm_output *output)
{ {
struct drm_backend *b = to_drm_backend(output->base.compositor);
unsigned int i; unsigned int i;
/* Destroying the Pixman surface will destroy all our buffers,
* regardless of refcount. Ensure we destroy them here. */
if (!b->shutting_down &&
output->scanout_plane->state_cur->fb &&
output->scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB) {
drm_plane_state_free(output->scanout_plane->state_cur, true);
output->scanout_plane->state_cur =
drm_plane_state_alloc(NULL, output->scanout_plane);
output->scanout_plane->state_cur->complete = true;
}
pixman_renderer_output_destroy(&output->base); pixman_renderer_output_destroy(&output->base);
pixman_region32_fini(&output->previous_damage); pixman_region32_fini(&output->previous_damage);
@ -3821,6 +3892,12 @@ drm_output_set_gbm_format(struct weston_output *base,
if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1) if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
output->gbm_format = b->gbm_format; output->gbm_format = b->gbm_format;
/* Without universal planes, we can't discover which formats are
* supported by the primary plane; we just hope that the GBM format
* works. */
if (!b->universal_planes)
output->scanout_plane->formats[0] = output->gbm_format;
} }
static void static void
@ -3872,8 +3949,6 @@ drm_output_enable(struct weston_output *base)
output->base.gamma_size = output->original_crtc->gamma_size; output->base.gamma_size = output->original_crtc->gamma_size;
output->base.set_gamma = drm_output_set_gamma; output->base.set_gamma = drm_output_set_gamma;
weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);
if (output->cursor_plane) if (output->cursor_plane)
weston_compositor_stack_plane(b->compositor, weston_compositor_stack_plane(b->compositor,
&output->cursor_plane->base, &output->cursor_plane->base,
@ -3881,7 +3956,8 @@ drm_output_enable(struct weston_output *base)
else else
b->cursors_are_broken = 1; b->cursors_are_broken = 1;
weston_compositor_stack_plane(b->compositor, &output->scanout_plane, weston_compositor_stack_plane(b->compositor,
&output->scanout_plane->base,
&b->compositor->primary_plane); &b->compositor->primary_plane);
weston_log("Output %s, (connector %d, crtc %d)\n", weston_log("Output %s, (connector %d, crtc %d)\n",
@ -3910,29 +3986,25 @@ drm_output_deinit(struct weston_output *base)
struct drm_output *output = to_drm_output(base); struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor); struct drm_backend *b = to_drm_backend(base->compositor);
/* output->fb_last and output->fb_pending must not be set here;
* destroy_pending/disable_pending exist to guarantee exactly this. */
assert(!output->fb_last);
assert(!output->fb_pending);
drm_fb_unref(output->fb_current);
output->fb_current = NULL;
if (b->use_pixman) if (b->use_pixman)
drm_output_fini_pixman(output); drm_output_fini_pixman(output);
else else
drm_output_fini_egl(output); drm_output_fini_egl(output);
weston_plane_release(&output->scanout_plane);
/* Since our planes are no longer in use anywhere, remove their base /* Since our planes are no longer in use anywhere, remove their base
* weston_plane's link from the plane stacking list, unless we're * weston_plane's link from the plane stacking list, unless we're
* shutting down, in which case the plane has already been * shutting down, in which case the plane has already been
* destroyed. */ * destroyed. */
if (output->cursor_plane && !b->shutting_down) { if (!b->shutting_down) {
wl_list_remove(&output->cursor_plane->base.link); wl_list_remove(&output->scanout_plane->base.link);
wl_list_init(&output->cursor_plane->base.link); wl_list_init(&output->scanout_plane->base.link);
/* Turn off hardware cursor */
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); if (output->cursor_plane) {
wl_list_remove(&output->cursor_plane->base.link);
wl_list_init(&output->cursor_plane->base.link);
/* Turn off hardware cursor */
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
}
} }
} }
@ -3968,6 +4040,7 @@ drm_output_destroy(struct weston_output *base)
*/ */
if (output->cursor_plane) if (output->cursor_plane)
drm_plane_destroy(output->cursor_plane); drm_plane_destroy(output->cursor_plane);
drm_plane_destroy(output->scanout_plane);
} }
wl_list_for_each_safe(drm_mode, next, &output->base.mode_list, wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
@ -4016,10 +4089,6 @@ drm_output_disable(struct weston_output *base)
if (output->base.enabled) if (output->base.enabled)
drm_output_deinit(&output->base); drm_output_deinit(&output->base);
assert(!output->fb_last);
assert(!output->fb_current);
assert(!output->fb_pending);
output->disable_pending = 0; output->disable_pending = 0;
weston_log("Disabling output %s\n", output->base.name); weston_log("Disabling output %s\n", output->base.name);
@ -4130,6 +4199,16 @@ create_output_for_connector(struct drm_backend *b,
} }
} }
output->scanout_plane =
drm_output_find_special_plane(b, output,
WDRM_PLANE_TYPE_PRIMARY);
if (!output->scanout_plane) {
weston_log("Failed to find primary plane for output %s\n",
output->base.name);
drm_output_destroy(&output->base);
return -1;
}
/* Failing to find a cursor plane is not fatal, as we'll fall back /* Failing to find a cursor plane is not fatal, as we'll fall back
* to software cursor. */ * to software cursor. */
output->cursor_plane = output->cursor_plane =
@ -4610,7 +4689,8 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
if (!output->recorder) if (!output->recorder)
return; return;
ret = drmPrimeHandleToFD(b->drm.fd, output->fb_current->handle, ret = drmPrimeHandleToFD(b->drm.fd,
output->scanout_plane->state_cur->fb->handle,
DRM_CLOEXEC, &fd); DRM_CLOEXEC, &fd);
if (ret) { if (ret) {
weston_log("[libva recorder] " weston_log("[libva recorder] "
@ -4619,7 +4699,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
} }
ret = vaapi_recorder_frame(output->recorder, fd, ret = vaapi_recorder_frame(output->recorder, fd,
output->fb_current->stride); output->scanout_plane->state_cur->fb->stride);
if (ret < 0) { if (ret < 0) {
weston_log("[libva recorder] aborted: %m\n"); weston_log("[libva recorder] aborted: %m\n");
recorder_destroy(output); recorder_destroy(output);

Loading…
Cancel
Save