backend-drm: Support tearing

Allow tearing for clients that request it, provided we can put their
content on a plane.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
This commit is contained in:
Derek Foreman 2022-11-24 14:15:14 -06:00
parent 9203d98f8b
commit 8057f28b98
4 changed files with 72 additions and 7 deletions

View File

@ -427,6 +427,7 @@ struct drm_output_state {
enum dpms_enum dpms;
enum weston_hdcp_protection protection;
struct wl_list plane_list;
bool tear;
};
/**

View File

@ -319,6 +319,9 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
return;
}
if (output->state_cur->tear)
flags |= WESTON_FINISH_FRAME_TEARING;
ts.tv_sec = sec;
ts.tv_nsec = usec * 1000;
@ -528,6 +531,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
struct timespec ts, tnow;
struct timespec vbl2now;
int64_t refresh_nsec;
uint32_t flags = WP_PRESENTATION_FEEDBACK_INVALID;
int ret;
drmVBlank vbl = {
.request.type = DRM_VBLANK_RELATIVE,
@ -551,6 +555,16 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
assert(scanout_plane->state_cur->output == output);
/* If we're tearing, we've been generating timestamps from the
* presentation clock that don't line up with the msc timestamps,
* and could be more recent than the latest msc, which would cause
* an assert() later.
*/
if (output->state_cur->tear) {
flags |= WESTON_FINISH_FRAME_TEARING;
goto finish_frame;
}
/* Try to get current msc and timestamp via instant query */
vbl.request.type |= drm_waitvblank_pipe(output->crtc);
ret = drmWaitVBlank(device->drm.fd, &vbl);
@ -571,8 +585,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
millihz_to_nsec(output->base.current_mode->refresh);
if (timespec_to_nsec(&vbl2now) < refresh_nsec) {
drm_output_update_msc(output, vbl.reply.sequence);
weston_output_finish_frame(output_base, &ts,
WP_PRESENTATION_FEEDBACK_INVALID);
weston_output_finish_frame(output_base, &ts, flags);
return 0;
}
}
@ -601,8 +614,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
finish_frame:
/* if we cannot page-flip, immediately finish frame */
weston_output_finish_frame(output_base, NULL,
WP_PRESENTATION_FEEDBACK_INVALID);
weston_output_finish_frame(output_base, NULL, flags);
return 0;
}

View File

@ -1189,6 +1189,18 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
return 0;
}
static void
drm_pending_state_clear_tearing(struct drm_pending_state *pending_state)
{
struct drm_output_state *output_state;
wl_list_for_each(output_state, &pending_state->output_list, link) {
if (output_state->output->virtual)
continue;
output_state->tear = false;
}
}
/**
* Helper function used only by drm_pending_state_apply, with the same
* guarantees and constraints as that function.
@ -1202,7 +1214,8 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
struct drm_output_state *output_state, *tmp;
struct drm_plane *plane;
drmModeAtomicReq *req = drmModeAtomicAlloc();
uint32_t flags;
uint32_t flags, tear_flag = 0;
bool may_tear = true;
int ret = 0;
if (!req)
@ -1312,6 +1325,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
continue;
if (mode == DRM_STATE_APPLY_SYNC)
assert(output_state->dpms == WESTON_DPMS_OFF);
may_tear &= output_state->tear;
ret |= drm_output_apply_state_atomic(output_state, req, &flags);
}
@ -1319,10 +1333,22 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
weston_log("atomic: couldn't compile atomic state\n");
goto out;
}
if (may_tear)
tear_flag = DRM_MODE_PAGE_FLIP_ASYNC;
ret = drmModeAtomicCommit(device->drm.fd, req, flags, device);
ret = drmModeAtomicCommit(device->drm.fd, req, flags | tear_flag,
device);
drm_debug(b, "[atomic] drmModeAtomicCommit\n");
if (ret != 0 && may_tear && mode == DRM_STATE_TEST_ONLY) {
/* If we failed trying to set up a tearing commit, try again
* without tearing. If that succeeds, knock the tearing flag
* out of our state in case we were testing for a later commit.
*/
drm_debug(b, "[atomic] drmModeAtomicCommit (no tear fallback)\n");
ret = drmModeAtomicCommit(device->drm.fd, req, flags, device);
if (ret == 0)
drm_pending_state_clear_tearing(pending_state);
}
/* Test commits do not take ownership of the state; return
* without freeing here. */
if (mode == DRM_STATE_TEST_ONLY) {
@ -1539,8 +1565,10 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
{
struct drm_device *device = data;
struct drm_backend *b = device->backend;
struct weston_compositor *ec = b->compositor;
struct drm_crtc *crtc;
struct drm_output *output;
struct timespec now;
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
@ -1558,6 +1586,17 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
drm_output_update_msc(output, frame);
if (output->state_cur->tear) {
/* When tearing we might not get accurate timestamps from
* the driver, so just use whatever time it is now.
* Note: This could actually be after a vblank that occured
* after entering this function.
*/
weston_compositor_read_presentation_clock(ec, &now);
sec = now.tv_sec;
usec = now.tv_nsec / 1000;
}
drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
assert(device->atomic_modeset);
assert(output->atomic_complete_pending);

View File

@ -660,6 +660,14 @@ drm_output_propose_state(struct weston_output *output_base,
pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES);
/* Start with the assumption that we're going to do a tearing commit,
* if the hardware supports it and we're not compositing with the
* renderer.
* As soon as anything in the scene graph wants to be presented without
* tearing, or a test fails, drop the tear flag. */
state->tear = device->tearing_supported &&
mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
/* We implement mixed mode by progressively creating and testing
* incremental states, of scanout + overlay + cursor. Since we
* walk our views top to bottom, the scanout plane is last, however
@ -830,6 +838,11 @@ drm_output_propose_state(struct weston_output *output_base,
force_renderer = true;
}
if (pnode->view->surface->tear_control)
state->tear &= pnode->view->surface->tear_control->may_tear;
else
state->tear = 0;
/* Now try to place it on a plane if we can. */
if (!force_renderer) {
drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",