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:
parent
9203d98f8b
commit
8057f28b98
@ -427,6 +427,7 @@ struct drm_output_state {
|
||||
enum dpms_enum dpms;
|
||||
enum weston_hdcp_protection protection;
|
||||
struct wl_list plane_list;
|
||||
bool tear;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user