On Tue, 18 Jul 2017 14:14:32 +0100 Daniel Stone <dani...@collabora.com> wrote:
> 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 <dani...@collabora.com> > --- > libweston/compositor-drm.c | 157 > ++++++++++++++++++++++++++------------------- > 1 file changed, 91 insertions(+), 66 deletions(-) Hi, the biggest issue with this patch seems to be the missing drm_plane teardown. There's also some wrong output/buffer sizes being used. > > diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c > index 2ede76b6..bf3273ba 100644 > --- a/libweston/compositor-drm.c > +++ b/libweston/compositor-drm.c > @@ -356,17 +356,8 @@ struct drm_output { > struct gbm_surface *gbm_surface; > uint32_t gbm_format; > > - /* Plane for a fullscreen direct scanout view */ > - struct weston_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; > + /* Plane being displayed directly on the CRTC */ > + struct drm_plane *scanout_plane; > > /* The last state submitted to the kernel for this CRTC. */ > struct drm_output_state *state_cur; > @@ -1324,6 +1315,8 @@ drm_output_assign_state(struct drm_output_state *state, > > if (plane->type == WDRM_PLANE_TYPE_OVERLAY) > output->vblank_pending++; > + else if (plane->type == WDRM_PLANE_TYPE_PRIMARY) > + output->page_flip_pending = 1; > } > } > > @@ -1371,6 +1364,8 @@ drm_output_prepare_scanout_view(struct drm_output_state > *output_state, > { > struct drm_output *output = output_state->output; > 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_viewport *viewport = &ev->surface->buffer_viewport; > struct gbm_bo *bo; > @@ -1424,15 +1419,29 @@ drm_output_prepare_scanout_view(struct > drm_output_state *output_state, > return NULL; > } > > - output->fb_pending = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT); > - if (!output->fb_pending) { > + state = drm_output_state_get_plane(output_state, scanout_plane); Is it not possible that state->fb is already non-NULL? I suppose not, because drm_output_state_duplicate() is always called with DRM_OUTPUT_STATE_CLEAR_PLANES in any path that may lead here. Maybe an assert just in case? > + state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT); > + if (!state->fb) { > + drm_plane_state_put_back(state); > gbm_bo_destroy(bo); > return NULL; > } > > - drm_fb_set_buffer(output->fb_pending, buffer); > + drm_fb_set_buffer(state->fb, buffer); > + > + state->output = output; > + > + state->src_x = 0; > + state->src_y = 0; > + state->src_w = ev->surface->width << 16; > + state->src_h = ev->surface->height << 16; You need to use buffer size here, not the surface size. We allow non-identity scale and transform, as long as they are the same between the buffer and the output. Surface size would be incorrect. > > - return &output->scanout_plane; > + state->dest_x = 0; > + state->dest_y = 0; > + state->dest_w = output->base.width; > + state->dest_h = output->base.height; Destination size should probably be taken from current_mode, in case we are temporarily in a non-native video mode. That would make the output size different from the mode. And the output size is wrong here anyway, if output transform and scale are not identity. > + > + return &scanout_plane->base; > } > > static struct drm_fb * > @@ -1497,12 +1506,15 @@ drm_output_render(struct drm_output_state *state, > pixman_region32_t *damage) > { > struct drm_output *output = state->output; > struct weston_compositor *c = output->base.compositor; > + struct drm_plane_state *scanout_state; > struct drm_backend *b = to_drm_backend(c); > struct drm_fb *fb; > > /* If we already have a client buffer promoted to scanout, then we don't > * want to render. */ > - if (output->fb_pending) > + scanout_state = drm_output_state_get_plane(state, > + output->scanout_plane); > + if (scanout_state->fb) > return; > > if (b->use_pixman) > @@ -1510,9 +1522,24 @@ drm_output_render(struct drm_output_state *state, > pixman_region32_t *damage) > else > fb = drm_output_render_gl(state, damage); > > - if (!fb) > + if (!fb) { > + drm_plane_state_put_back(scanout_state); > 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, > &c->primary_plane.damage, damage); > @@ -1575,6 +1602,8 @@ drm_output_repaint(struct weston_output *output_base, > struct drm_output *output = to_drm_output(output_base); > struct drm_backend *backend = > 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 *p; > struct drm_mode *mode; > @@ -1594,7 +1623,6 @@ drm_output_repaint(struct weston_output *output_base, > pending_state, > > DRM_OUTPUT_STATE_CLEAR_PLANES); > > - assert(!output->fb_last); > > /* If disable_planes is set then assign_planes() wasn't > * called for this render, so we could still have a stale > @@ -1607,14 +1635,29 @@ drm_output_repaint(struct weston_output *output_base, > } > > 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; > > + /* 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->src_w == scanout_state->dest_w << 16); > + assert(scanout_state->src_h == scanout_state->dest_h << 16); The two asserts you said you already fixed. Technically I think the code was correct, but definitely not easy to read right. :-D > + > mode = container_of(output->base.current_mode, struct drm_mode, base); > - if (!output->fb_current || > - output->fb_current->stride != output->fb_pending->stride) { > + if (!scanout_plane->state_cur->fb || > + scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) { > 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, > &mode->mode_info); > if (ret) { > @@ -1625,18 +1668,13 @@ drm_output_repaint(struct weston_output *output_base, > } > > 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) { > weston_log("queueing pageflip failed: %m\n"); > goto err; > } > > - output->fb_last = output->fb_current; > - output->fb_current = output->fb_pending; > - output->fb_pending = NULL; > - > assert(!output->page_flip_pending); > - output->page_flip_pending = 1; > > if (output->pageflip_timer) > wl_event_source_timer_update(output->pageflip_timer, > @@ -1696,10 +1734,7 @@ drm_output_repaint(struct weston_output *output_base, Heh, twice I have wondered why does drm_output_repaint() not call drm_output_assign_state(), and then I remember it is done in drm_repaint_flush(), and the reason for that is that with atomic modesetting that really is where it will happen. > > err: > output->cursor_view = NULL; > - if (output->fb_pending) { > - drm_fb_unref(output->fb_pending); > - output->fb_pending = NULL; > - } > + > drm_output_state_free(state); > > return -1; > @@ -1712,6 +1747,7 @@ drm_output_start_repaint_loop(struct weston_output > *output_base) > struct drm_pending_state *pending_state; > struct drm_output_state *state; > struct drm_plane_state *plane_state; > + struct drm_plane *scanout_plane = output->scanout_plane; > struct drm_backend *backend = > to_drm_backend(output_base->compositor); > uint32_t fb_id; > @@ -1728,11 +1764,13 @@ drm_output_start_repaint_loop(struct weston_output > *output_base) > if (output->disable_pending || output->destroy_pending) > return; > > - if (!output->fb_current) { > + if (!output->scanout_plane->state_cur->fb) { > /* We can't page flip if there's no mode set */ > goto finish_frame; > } > > + assert(scanout_plane->state_cur->output == output); > + > /* Try to get current msc and timestamp via instant query */ > vbl.request.type |= drm_waitvblank_pipe(output); > ret = drmWaitVBlank(backend->drm.fd, &vbl); > @@ -1762,10 +1800,9 @@ drm_output_start_repaint_loop(struct weston_output > *output_base) > /* Immediate query didn't provide valid timestamp. > * 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->fb_last); > assert(!output->state_last); > > pending_state = drm_pending_state_alloc(backend); > @@ -1783,9 +1820,6 @@ drm_output_start_repaint_loop(struct weston_output > *output_base) > wl_event_source_timer_update(output->pageflip_timer, > 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) { > if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY) > continue; > @@ -1858,9 +1892,6 @@ page_flip_handler(int fd, unsigned int frame, > assert(output->page_flip_pending); > output->page_flip_pending = 0; > > - drm_fb_unref(output->fb_last); > - output->fb_last = NULL; > - > if (output->vblank_pending) > return; > > @@ -2536,10 +2567,6 @@ drm_output_switch_mode(struct weston_output > *output_base, struct weston_mode *mo > * sledgehammer modeswitch first, and only later showing new > * content. > */ > - drm_fb_unref(output->fb_current); > - assert(!output->fb_last); > - assert(!output->fb_pending); > - output->fb_last = output->fb_current = NULL; I think the comment above might need updating, but I suppose it's safe to say the switch_mode hook is just plain broken for now, to be fixed in the future. Or is it broken? We choose a new mode, we reinitialize the renderer... maybe it works most of the time by luck? OTOH, not sure we even have a way to trigger this to begin with. > > if (b->use_pixman) { > drm_output_fini_pixman(output); > @@ -2870,14 +2897,16 @@ drm_output_find_special_plane(struct drm_backend *b, > struct drm_output *output, > * same plane for two outputs. */ > wl_list_for_each(tmp, &b->compositor->pending_output_list, > base.link) { > - if (tmp->cursor_plane == plane) { > + if (tmp->cursor_plane == plane || > + tmp->scanout_plane == plane) { > found_elsewhere = true; > break; > } > } > wl_list_for_each(tmp, &b->compositor->output_list, > base.link) { > - if (tmp->cursor_plane == plane) { > + if (tmp->cursor_plane == plane || > + tmp->scanout_plane == plane) { Ah, here they are. > found_elsewhere = true; > break; > } > @@ -3755,6 +3784,15 @@ drm_output_enable(struct weston_output *base) > if (b->pageflip_timeout) > drm_output_pageflip_timer_create(output); > > + 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); > + goto err; The error path (not this jump but the later ones) forgets to clean up scanout_plane. > + } > + > /* Failing to find a cursor plane is not fatal, as we'll fall back > * to software cursor. */ > output->cursor_plane = > @@ -3795,8 +3833,6 @@ drm_output_enable(struct weston_output *base) > output->connector->connector_type == DRM_MODE_CONNECTOR_eDP) > output->base.connection_internal = 1; > > - weston_plane_init(&output->scanout_plane, b->compositor, 0, 0); > - > if (output->cursor_plane) > weston_compositor_stack_plane(b->compositor, > &output->cursor_plane->base, > @@ -3804,7 +3840,8 @@ drm_output_enable(struct weston_output *base) > else > 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); The scanout plane is a little strange. It is used for both fullscreen direct scanout as well as the renderer output for the primary plane. But, I guess that makes no harm. It's probably just another rabbit hole. > > weston_log("Output %s, (connector %d, crtc %d)\n", > @@ -3831,20 +3868,11 @@ drm_output_deinit(struct weston_output *base) > struct drm_output *output = to_drm_output(base); > 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) > drm_output_fini_pixman(output); > else > drm_output_fini_egl(output); > > - weston_plane_release(&output->scanout_plane); I believe Philipp is right, the plane teardown is missing, just like for the cursor plane. > - > if (output->cursor_plane) { > /* Turn off hardware cursor */ > drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); Thanks, pq
pgpQuaLzeouve.pgp
Description: OpenPGP digital signature
_______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel