mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-17 06:46:39 -04:00
Merge branch 'x11-shm-readback-fallback' into 'master'
backend/x11: add SHM readback fallback when buffer import fails See merge request wlroots/wlroots!5291
This commit is contained in:
commit
f4bee20858
3 changed files with 176 additions and 5 deletions
|
|
@ -474,9 +474,11 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop,
|
||||||
xcb_shm_query_version_reply_t *shm_reply =
|
xcb_shm_query_version_reply_t *shm_reply =
|
||||||
xcb_shm_query_version_reply(x11->xcb, shm_cookie, NULL);
|
xcb_shm_query_version_reply(x11->xcb, shm_cookie, NULL);
|
||||||
if (shm_reply) {
|
if (shm_reply) {
|
||||||
if (shm_reply->major_version >= 1 || shm_reply->minor_version >= 2) {
|
if (shm_reply->major_version > 1 ||
|
||||||
if (shm_reply->shared_pixmaps) {
|
(shm_reply->major_version == 1 && shm_reply->minor_version >= 2)) {
|
||||||
x11->have_shm = true;
|
x11->have_shm = true;
|
||||||
|
if (shm_reply->shared_pixmaps) {
|
||||||
|
x11->have_shm_pixmaps = true;
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_INFO, "X11 does not support shared pixmaps");
|
wlr_log(WLR_INFO, "X11 does not support shared pixmaps");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <xcb/dri3.h>
|
#include <xcb/dri3.h>
|
||||||
|
|
@ -20,6 +22,7 @@
|
||||||
|
|
||||||
#include "backend/x11.h"
|
#include "backend/x11.h"
|
||||||
#include "render/pixel_format.h"
|
#include "render/pixel_format.h"
|
||||||
|
#include "util/shm.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "types/wlr_buffer.h"
|
#include "types/wlr_buffer.h"
|
||||||
#include "types/wlr_output.h"
|
#include "types/wlr_output.h"
|
||||||
|
|
@ -92,6 +95,29 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
|
|
||||||
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
|
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
|
||||||
|
|
||||||
|
static void readback_cleanup(struct wlr_x11_output *output) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
if (output->readback.seg != XCB_NONE) {
|
||||||
|
xcb_shm_detach(x11->xcb, output->readback.seg);
|
||||||
|
output->readback.seg = XCB_NONE;
|
||||||
|
}
|
||||||
|
if (output->readback.data != NULL) {
|
||||||
|
munmap(output->readback.data, output->readback.size);
|
||||||
|
output->readback.data = NULL;
|
||||||
|
}
|
||||||
|
if (output->readback.gc != XCB_NONE) {
|
||||||
|
xcb_free_gc(x11->xcb, output->readback.gc);
|
||||||
|
output->readback.gc = XCB_NONE;
|
||||||
|
}
|
||||||
|
if (output->readback.pixmap != XCB_PIXMAP_NONE) {
|
||||||
|
xcb_free_pixmap(x11->xcb, output->readback.pixmap);
|
||||||
|
output->readback.pixmap = XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
output->readback.size = 0;
|
||||||
|
output->readback.width = 0;
|
||||||
|
output->readback.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
@ -110,6 +136,8 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
|
|
||||||
|
readback_cleanup(output);
|
||||||
|
|
||||||
if (output->cursor.pic != XCB_NONE) {
|
if (output->cursor.pic != XCB_NONE) {
|
||||||
xcb_render_free_picture(x11->xcb, output->cursor.pic);
|
xcb_render_free_picture(x11->xcb, output->cursor.pic);
|
||||||
}
|
}
|
||||||
|
|
@ -216,6 +244,116 @@ static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
||||||
destroy_x11_buffer(buffer);
|
destroy_x11_buffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool format_set_has_explicit_modifier(
|
||||||
|
const struct wlr_drm_format_set *formats) {
|
||||||
|
for (size_t i = 0; i < formats->len; i++) {
|
||||||
|
const struct wlr_drm_format *fmt = &formats->formats[i];
|
||||||
|
for (size_t j = 0; j < fmt->len; j++) {
|
||||||
|
if (fmt->modifiers[j] != DRM_FORMAT_MOD_INVALID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readback_ensure(struct wlr_x11_output *output,
|
||||||
|
int width, int height) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
int bpp = x11->x11_format->bpp;
|
||||||
|
int stride = width * (bpp / 8);
|
||||||
|
size_t size = (size_t)stride * height;
|
||||||
|
|
||||||
|
if (output->readback.data != NULL &&
|
||||||
|
output->readback.width == width &&
|
||||||
|
output->readback.height == height) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "Using SHM readback fallback for output '%s' (%dx%d)",
|
||||||
|
output->wlr_output.name, width, height);
|
||||||
|
|
||||||
|
readback_cleanup(output);
|
||||||
|
|
||||||
|
int fd = allocate_shm_file(size);
|
||||||
|
if (fd < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to allocate SHM file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *data = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, fd, 0);
|
||||||
|
if (data == MAP_FAILED) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "mmap failed");
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_shm_seg_t seg = xcb_generate_id(x11->xcb);
|
||||||
|
// xcb_shm_attach_fd takes ownership of the fd
|
||||||
|
xcb_shm_attach_fd(x11->xcb, seg, fd, false);
|
||||||
|
|
||||||
|
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
||||||
|
xcb_create_pixmap(x11->xcb, x11->x11_format->depth, pixmap,
|
||||||
|
output->win, width, height);
|
||||||
|
|
||||||
|
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
|
||||||
|
xcb_create_gc(x11->xcb, gc, pixmap, 0, NULL);
|
||||||
|
|
||||||
|
output->readback.seg = seg;
|
||||||
|
output->readback.data = data;
|
||||||
|
output->readback.size = size;
|
||||||
|
output->readback.width = width;
|
||||||
|
output->readback.height = height;
|
||||||
|
output->readback.pixmap = pixmap;
|
||||||
|
output->readback.gc = gc;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readback_commit(struct wlr_x11_output *output,
|
||||||
|
struct wlr_buffer *buffer) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
struct wlr_renderer *renderer = output->wlr_output.renderer;
|
||||||
|
|
||||||
|
if (renderer == NULL || !x11->have_shm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!readback_ensure(output, buffer->width, buffer->height)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer);
|
||||||
|
if (!texture) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create texture from buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bpp = x11->x11_format->bpp;
|
||||||
|
int stride = buffer->width * (bpp / 8);
|
||||||
|
|
||||||
|
bool ok = wlr_texture_read_pixels(texture, &(struct wlr_texture_read_pixels_options) {
|
||||||
|
.format = x11->x11_format->drm,
|
||||||
|
.stride = stride,
|
||||||
|
.data = output->readback.data,
|
||||||
|
});
|
||||||
|
wlr_texture_destroy(texture);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to read pixels from texture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_shm_put_image(x11->xcb, output->readback.pixmap,
|
||||||
|
output->readback.gc, buffer->width, buffer->height, 0, 0,
|
||||||
|
buffer->width, buffer->height, 0, 0,
|
||||||
|
x11->x11_format->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
|
||||||
|
false, output->readback.seg, 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output,
|
static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output,
|
||||||
struct wlr_dmabuf_attributes *dmabuf) {
|
struct wlr_dmabuf_attributes *dmabuf) {
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
@ -265,6 +403,10 @@ static xcb_pixmap_t import_shm(struct wlr_x11_output *output,
|
||||||
struct wlr_shm_attributes *shm) {
|
struct wlr_shm_attributes *shm) {
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
if (!x11->have_shm_pixmaps) {
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
if (shm->format != x11->x11_format->drm) {
|
if (shm->format != x11->x11_format->drm) {
|
||||||
// The pixmap's depth must match the window's depth, otherwise Present
|
// The pixmap's depth must match the window's depth, otherwise Present
|
||||||
// will throw a Match error
|
// will throw a Match error
|
||||||
|
|
@ -349,10 +491,19 @@ static bool output_commit_buffer(struct wlr_x11_output *output,
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
struct wlr_buffer *buffer = state->buffer;
|
struct wlr_buffer *buffer = state->buffer;
|
||||||
|
xcb_pixmap_t present_pixmap;
|
||||||
|
|
||||||
struct wlr_x11_buffer *x11_buffer =
|
struct wlr_x11_buffer *x11_buffer =
|
||||||
get_or_create_x11_buffer(output, buffer);
|
get_or_create_x11_buffer(output, buffer);
|
||||||
if (!x11_buffer) {
|
if (x11_buffer) {
|
||||||
goto error;
|
present_pixmap = x11_buffer->pixmap;
|
||||||
|
} else {
|
||||||
|
// DRI3/SHM import failed (e.g. buffer has a modifier that X11
|
||||||
|
// cannot handle). Fall back to CPU readback via SHM.
|
||||||
|
if (!readback_commit(output, buffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
present_pixmap = output->readback.pixmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb_xfixes_region_t region = XCB_NONE;
|
xcb_xfixes_region_t region = XCB_NONE;
|
||||||
|
|
@ -388,7 +539,7 @@ static bool output_commit_buffer(struct wlr_x11_output *output,
|
||||||
uint32_t serial = output->wlr_output.commit_seq;
|
uint32_t serial = output->wlr_output.commit_seq;
|
||||||
uint32_t options = 0;
|
uint32_t options = 0;
|
||||||
uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0;
|
uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0;
|
||||||
xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial,
|
xcb_present_pixmap(x11->xcb, output->win, present_pixmap, serial,
|
||||||
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc,
|
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc,
|
||||||
0, 0, 0, NULL);
|
0, 0, 0, NULL);
|
||||||
|
|
||||||
|
|
@ -569,6 +720,13 @@ static const struct wlr_drm_format_set *output_get_primary_formats(
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
if (x11->have_dri3 && (buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
|
if (x11->have_dri3 && (buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
|
||||||
|
// When DRI3 has no explicit modifiers, return NULL (no format
|
||||||
|
// constraint) so the renderer can allocate with its preferred
|
||||||
|
// modifier. The commit path will fall back to CPU readback
|
||||||
|
// if DRI3 cannot import the resulting buffer.
|
||||||
|
if (!format_set_has_explicit_modifier(&x11->primary_dri3_formats)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return &output->x11->primary_dri3_formats;
|
return &output->x11->primary_dri3_formats;
|
||||||
} else if (x11->have_shm && (buffer_caps & WLR_BUFFER_CAP_SHM)) {
|
} else if (x11->have_shm && (buffer_caps & WLR_BUFFER_CAP_SHM)) {
|
||||||
return &output->x11->primary_shm_formats;
|
return &output->x11->primary_shm_formats;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/present.h>
|
#include <xcb/present.h>
|
||||||
|
#include <xcb/shm.h>
|
||||||
|
|
||||||
#include <pixman.h>
|
#include <pixman.h>
|
||||||
#include <wlr/backend/x11.h>
|
#include <wlr/backend/x11.h>
|
||||||
|
|
@ -48,6 +49,15 @@ struct wlr_x11_output {
|
||||||
|
|
||||||
uint64_t last_msc;
|
uint64_t last_msc;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
xcb_shm_seg_t seg;
|
||||||
|
uint8_t *data;
|
||||||
|
size_t size;
|
||||||
|
int width, height;
|
||||||
|
xcb_pixmap_t pixmap;
|
||||||
|
xcb_gcontext_t gc;
|
||||||
|
} readback;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wlr_swapchain *swapchain;
|
struct wlr_swapchain *swapchain;
|
||||||
xcb_render_picture_t pic;
|
xcb_render_picture_t pic;
|
||||||
|
|
@ -74,6 +84,7 @@ struct wlr_x11_backend {
|
||||||
xcb_render_pictformat_t argb32;
|
xcb_render_pictformat_t argb32;
|
||||||
|
|
||||||
bool have_shm;
|
bool have_shm;
|
||||||
|
bool have_shm_pixmaps;
|
||||||
bool have_dri3;
|
bool have_dri3;
|
||||||
uint32_t dri3_major_version, dri3_minor_version;
|
uint32_t dri3_major_version, dri3_minor_version;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue