mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-12-03 12:32:20 +00:00
backend/wayland: add support for explicit sync
wl_buffer.release event delivery becomes undefined when using the linux-drm-syncobj-v1 protocol, so we need to wait for buffer release via a timeline point instead. The protocol requires both wait and signal timelines to be set, so we need to create one when the compositor only supplies a wait timeline.
This commit is contained in:
parent
ca29f43a54
commit
e9a6b3b85d
@ -21,6 +21,7 @@
|
||||
|
||||
#include "drm-client-protocol.h"
|
||||
#include "linux-dmabuf-v1-client-protocol.h"
|
||||
#include "linux-drm-syncobj-v1-client-protocol.h"
|
||||
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
||||
#include "presentation-time-client-protocol.h"
|
||||
#include "xdg-activation-v1-client-protocol.h"
|
||||
@ -411,6 +412,9 @@ static void registry_global(void *data, struct wl_registry *registry,
|
||||
} else if (strcmp(iface, wp_viewporter_interface.name) == 0) {
|
||||
wl->viewporter = wl_registry_bind(registry, name,
|
||||
&wp_viewporter_interface, 1);
|
||||
} else if (strcmp(iface, wp_linux_drm_syncobj_manager_v1_interface.name) == 0) {
|
||||
wl->drm_syncobj_manager_v1 = wl_registry_bind(registry, name,
|
||||
&wp_linux_drm_syncobj_manager_v1_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,6 +488,11 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||
destroy_wl_buffer(buffer);
|
||||
}
|
||||
|
||||
struct wlr_wl_drm_syncobj_timeline *timeline, *tmp_timeline;
|
||||
wl_list_for_each_safe(timeline, tmp_timeline, &wl->drm_syncobj_timelines, link) {
|
||||
destroy_wl_drm_syncobj_timeline(timeline);
|
||||
}
|
||||
|
||||
wlr_backend_finish(backend);
|
||||
|
||||
wl_list_remove(&wl->event_loop_destroy.link);
|
||||
@ -518,6 +527,9 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||
if (wl->zwp_linux_dmabuf_v1) {
|
||||
zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1);
|
||||
}
|
||||
if (wl->drm_syncobj_manager_v1) {
|
||||
wp_linux_drm_syncobj_manager_v1_destroy(wl->drm_syncobj_manager_v1);
|
||||
}
|
||||
if (wl->legacy_drm != NULL) {
|
||||
wl_drm_destroy(wl->legacy_drm);
|
||||
}
|
||||
@ -592,6 +604,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop,
|
||||
wl_list_init(&wl->outputs);
|
||||
wl_list_init(&wl->seats);
|
||||
wl_list_init(&wl->buffers);
|
||||
wl_list_init(&wl->drm_syncobj_timelines);
|
||||
|
||||
if (remote_display != NULL) {
|
||||
wl->remote_display = remote_display;
|
||||
@ -624,6 +637,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop,
|
||||
goto error_registry;
|
||||
}
|
||||
|
||||
wl->backend.features.timeline = wl->drm_syncobj_manager_v1 != NULL;
|
||||
|
||||
wl_display_roundtrip(wl->remote_display); // process initial event bursts
|
||||
|
||||
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL;
|
||||
|
@ -15,6 +15,7 @@ wlr_files += files(
|
||||
client_protos = [
|
||||
'drm',
|
||||
'linux-dmabuf-v1',
|
||||
'linux-drm-syncobj-v1',
|
||||
'pointer-gestures-unstable-v1',
|
||||
'presentation-time',
|
||||
'relative-pointer-unstable-v1',
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <wayland-client-protocol.h>
|
||||
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/drm_syncobj.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_output_layer.h>
|
||||
#include <wlr/util/log.h>
|
||||
@ -20,6 +21,7 @@
|
||||
#include "types/wlr_output.h"
|
||||
|
||||
#include "linux-dmabuf-v1-client-protocol.h"
|
||||
#include "linux-drm-syncobj-v1-client-protocol.h"
|
||||
#include "presentation-time-client-protocol.h"
|
||||
#include "viewporter-client-protocol.h"
|
||||
#include "xdg-activation-v1-client-protocol.h"
|
||||
@ -31,7 +33,9 @@ static const uint32_t SUPPORTED_OUTPUT_STATE =
|
||||
WLR_OUTPUT_STATE_BUFFER |
|
||||
WLR_OUTPUT_STATE_ENABLED |
|
||||
WLR_OUTPUT_STATE_MODE |
|
||||
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
|
||||
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED |
|
||||
WLR_OUTPUT_STATE_WAIT_TIMELINE |
|
||||
WLR_OUTPUT_STATE_SIGNAL_TIMELINE;
|
||||
|
||||
static size_t last_output_num = 0;
|
||||
|
||||
@ -140,19 +144,40 @@ void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
||||
if (!buffer->released) {
|
||||
wlr_buffer_unlock(buffer->buffer);
|
||||
}
|
||||
wlr_drm_syncobj_timeline_unref(buffer->fallback_signal_timeline);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void buffer_release(struct wlr_wl_buffer *buffer) {
|
||||
if (buffer->released) {
|
||||
return;
|
||||
}
|
||||
buffer->released = true;
|
||||
wlr_buffer_unlock(buffer->buffer); // might free buffer
|
||||
}
|
||||
|
||||
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
|
||||
struct wlr_wl_buffer *buffer = data;
|
||||
buffer->released = true;
|
||||
wlr_buffer_unlock(buffer->buffer); // might free buffer
|
||||
if (buffer->has_drm_syncobj_waiter) {
|
||||
return;
|
||||
}
|
||||
buffer_release(buffer);
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
.release = buffer_handle_release,
|
||||
};
|
||||
|
||||
static void buffer_handle_drm_syncobj_ready(struct wl_listener *listener, void *data) {
|
||||
struct wlr_wl_buffer *buffer = wl_container_of(listener, buffer, drm_syncobj_ready);
|
||||
|
||||
wl_list_remove(&buffer->drm_syncobj_ready.link);
|
||||
wlr_drm_syncobj_timeline_waiter_finish(&buffer->drm_syncobj_waiter);
|
||||
buffer->has_drm_syncobj_waiter = false;
|
||||
|
||||
buffer_release(buffer);
|
||||
}
|
||||
|
||||
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_wl_buffer *buffer =
|
||||
@ -293,6 +318,58 @@ static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl,
|
||||
return create_wl_buffer(wl, wlr_buffer);
|
||||
}
|
||||
|
||||
void destroy_wl_drm_syncobj_timeline(struct wlr_wl_drm_syncobj_timeline *timeline) {
|
||||
wp_linux_drm_syncobj_timeline_v1_destroy(timeline->wl);
|
||||
wlr_addon_finish(&timeline->addon);
|
||||
wl_list_remove(&timeline->link);
|
||||
free(timeline);
|
||||
}
|
||||
|
||||
static void drm_syncobj_timeline_addon_destroy(struct wlr_addon *addon) {
|
||||
struct wlr_wl_drm_syncobj_timeline *timeline = wl_container_of(addon, timeline, addon);
|
||||
destroy_wl_drm_syncobj_timeline(timeline);
|
||||
}
|
||||
|
||||
static const struct wlr_addon_interface drm_syncobj_timeline_addon_impl = {
|
||||
.name = "wlr_wl_drm_syncobj_timeline",
|
||||
.destroy = drm_syncobj_timeline_addon_destroy,
|
||||
};
|
||||
|
||||
static struct wlr_wl_drm_syncobj_timeline *get_or_create_drm_syncobj_timeline(
|
||||
struct wlr_wl_backend *wl, struct wlr_drm_syncobj_timeline *wlr_timeline) {
|
||||
struct wlr_addon *addon =
|
||||
wlr_addon_find(&wlr_timeline->addons, wl, &drm_syncobj_timeline_addon_impl);
|
||||
if (addon != NULL) {
|
||||
struct wlr_wl_drm_syncobj_timeline *timeline = wl_container_of(addon, timeline, addon);
|
||||
return timeline;
|
||||
}
|
||||
|
||||
struct wlr_wl_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline));
|
||||
if (timeline == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
timeline->base = wlr_timeline;
|
||||
|
||||
int fd = wlr_drm_syncobj_timeline_export(wlr_timeline);
|
||||
if (fd < 0) {
|
||||
free(timeline);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
timeline->wl = wp_linux_drm_syncobj_manager_v1_import_timeline(wl->drm_syncobj_manager_v1, fd);
|
||||
close(fd);
|
||||
if (timeline->wl == NULL) {
|
||||
free(timeline);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wlr_addon_init(&timeline->addon, &wlr_timeline->addons, wl, &drm_syncobj_timeline_addon_impl);
|
||||
wl_list_insert(&wl->drm_syncobj_timelines, &timeline->link);
|
||||
|
||||
return timeline;
|
||||
}
|
||||
|
||||
static bool update_title(struct wlr_wl_output *output, const char *title) {
|
||||
struct wlr_output *wlr_output = &output->wlr_output;
|
||||
|
||||
@ -387,6 +464,21 @@ static bool output_test(struct wlr_output *wlr_output,
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) &&
|
||||
!(state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE)) {
|
||||
wlr_log(WLR_DEBUG, "Signal timeline requires a wait timeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) ||
|
||||
(state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE)) {
|
||||
struct wlr_dmabuf_attributes dmabuf;
|
||||
if (!wlr_buffer_get_dmabuf(state->buffer, &dmabuf)) {
|
||||
wlr_log(WLR_DEBUG, "Wait/signal timelines require DMA-BUFs");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->committed & WLR_OUTPUT_STATE_LAYERS) {
|
||||
// If we can't use a sub-surface for a layer, then we can't use a
|
||||
// sub-surface for any layer underneath
|
||||
@ -660,6 +752,7 @@ static bool output_commit(struct wlr_output *wlr_output,
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_wl_buffer *buffer = NULL;
|
||||
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||
const pixman_region32_t *damage = NULL;
|
||||
if (state->committed & WLR_OUTPUT_STATE_DAMAGE) {
|
||||
@ -667,8 +760,7 @@ static bool output_commit(struct wlr_output *wlr_output,
|
||||
}
|
||||
|
||||
struct wlr_buffer *wlr_buffer = state->buffer;
|
||||
struct wlr_wl_buffer *buffer =
|
||||
get_or_create_wl_buffer(output->backend, wlr_buffer);
|
||||
buffer = get_or_create_wl_buffer(output->backend, wlr_buffer);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
@ -677,6 +769,66 @@ static bool output_commit(struct wlr_output *wlr_output,
|
||||
damage_surface(output->surface, damage);
|
||||
}
|
||||
|
||||
if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) {
|
||||
struct wlr_wl_drm_syncobj_timeline *wait_timeline =
|
||||
get_or_create_drm_syncobj_timeline(output->backend, state->wait_timeline);
|
||||
|
||||
struct wlr_wl_drm_syncobj_timeline *signal_timeline;
|
||||
uint64_t signal_point;
|
||||
if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) {
|
||||
signal_timeline = get_or_create_drm_syncobj_timeline(output->backend, state->signal_timeline);
|
||||
signal_point = state->signal_point;
|
||||
} else {
|
||||
if (buffer->fallback_signal_timeline == NULL) {
|
||||
buffer->fallback_signal_timeline =
|
||||
wlr_drm_syncobj_timeline_create(output->backend->drm_fd);
|
||||
if (buffer->fallback_signal_timeline == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
signal_timeline =
|
||||
get_or_create_drm_syncobj_timeline(output->backend, buffer->fallback_signal_timeline);
|
||||
signal_point = ++buffer->fallback_signal_point;
|
||||
}
|
||||
|
||||
if (wait_timeline == NULL || signal_timeline == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output->drm_syncobj_surface_v1 == NULL) {
|
||||
output->drm_syncobj_surface_v1 = wp_linux_drm_syncobj_manager_v1_get_surface(
|
||||
output->backend->drm_syncobj_manager_v1, output->surface);
|
||||
if (output->drm_syncobj_surface_v1 == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t wait_point_hi = state->wait_point >> 32;
|
||||
uint32_t wait_point_lo = state->wait_point & UINT32_MAX;
|
||||
uint32_t signal_point_hi = signal_point >> 32;
|
||||
uint32_t signal_point_lo = signal_point & UINT32_MAX;
|
||||
|
||||
wp_linux_drm_syncobj_surface_v1_set_acquire_point(output->drm_syncobj_surface_v1,
|
||||
wait_timeline->wl, wait_point_hi, wait_point_lo);
|
||||
wp_linux_drm_syncobj_surface_v1_set_release_point(output->drm_syncobj_surface_v1,
|
||||
signal_timeline->wl, signal_point_hi, signal_point_lo);
|
||||
|
||||
if (!wlr_drm_syncobj_timeline_waiter_init(&buffer->drm_syncobj_waiter,
|
||||
signal_timeline->base, signal_point, 0, output->backend->event_loop)) {
|
||||
return false;
|
||||
}
|
||||
buffer->has_drm_syncobj_waiter = true;
|
||||
|
||||
buffer->drm_syncobj_ready.notify = buffer_handle_drm_syncobj_ready;
|
||||
wl_signal_add(&buffer->drm_syncobj_waiter.events.ready,
|
||||
&buffer->drm_syncobj_ready);
|
||||
} else {
|
||||
if (output->drm_syncobj_surface_v1 != NULL) {
|
||||
wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1);
|
||||
output->drm_syncobj_surface_v1 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((state->committed & WLR_OUTPUT_STATE_LAYERS) &&
|
||||
!commit_layers(output, state->layers, state->layers_len)) {
|
||||
return false;
|
||||
@ -801,6 +953,9 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
||||
wl_callback_destroy(output->unmap_callback);
|
||||
}
|
||||
|
||||
if (output->drm_syncobj_surface_v1) {
|
||||
wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1);
|
||||
}
|
||||
if (output->zxdg_toplevel_decoration_v1) {
|
||||
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <wlr/types/wlr_tablet_tool.h>
|
||||
#include <wlr/types/wlr_touch.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
#include <wlr/render/drm_syncobj.h>
|
||||
|
||||
struct wlr_wl_backend {
|
||||
struct wlr_backend backend;
|
||||
@ -40,6 +41,8 @@ struct wlr_wl_backend {
|
||||
struct wp_presentation *presentation;
|
||||
struct wl_shm *shm;
|
||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
|
||||
struct wp_linux_drm_syncobj_manager_v1 *drm_syncobj_manager_v1;
|
||||
struct wl_list drm_syncobj_timelines; // wlr_wl_drm_syncobj_timeline.link
|
||||
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
|
||||
struct wl_list seats; // wlr_wl_seat.link
|
||||
struct zwp_tablet_manager_v2 *tablet_manager;
|
||||
@ -58,6 +61,20 @@ struct wlr_wl_buffer {
|
||||
bool released;
|
||||
struct wl_list link; // wlr_wl_backend.buffers
|
||||
struct wl_listener buffer_destroy;
|
||||
|
||||
bool has_drm_syncobj_waiter;
|
||||
struct wlr_drm_syncobj_timeline_waiter drm_syncobj_waiter;
|
||||
struct wl_listener drm_syncobj_ready;
|
||||
|
||||
struct wlr_drm_syncobj_timeline *fallback_signal_timeline;
|
||||
uint64_t fallback_signal_point;
|
||||
};
|
||||
|
||||
struct wlr_wl_drm_syncobj_timeline {
|
||||
struct wlr_drm_syncobj_timeline *base;
|
||||
struct wlr_addon addon;
|
||||
struct wl_list link; // wlr_wl_backend.drm_syncobj_timelines
|
||||
struct wp_linux_drm_syncobj_timeline_v1 *wl;
|
||||
};
|
||||
|
||||
struct wlr_wl_presentation_feedback {
|
||||
@ -88,6 +105,7 @@ struct wlr_wl_output {
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
|
||||
struct wp_linux_drm_syncobj_surface_v1 *drm_syncobj_surface_v1;
|
||||
struct wl_list presentation_feedbacks;
|
||||
|
||||
char *title;
|
||||
@ -190,6 +208,7 @@ bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl,
|
||||
uint32_t global_name);
|
||||
void destroy_wl_seat(struct wlr_wl_seat *seat);
|
||||
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
|
||||
void destroy_wl_drm_syncobj_timeline(struct wlr_wl_drm_syncobj_timeline *timeline);
|
||||
|
||||
extern const struct wlr_pointer_impl wl_pointer_impl;
|
||||
extern const struct wlr_tablet_pad_impl wl_tablet_pad_impl;
|
||||
|
Loading…
Reference in New Issue
Block a user