Compare commits

...

11 commits

Author SHA1 Message Date
Simon Ser
fcff51a68a Merge branch 'dmabuf-wait-helper' into 'master'
Add helper to wait for DMA-BUFs to be ready on surface commit

Closes #3026

See merge request wlroots/wlroots!4040
2025-10-26 08:31:08 +00:00
David Turner
879243e370 xwm: Fix double-close
When an FD is passed to xcb_connect_to_fd(), xcb takes ownership of that
FD and is responsible for closing it, which it does when
xcb_disconnect() is called.  But the xwayland handler code also keeps a
copy of the FD and closes it via safe_close() in
server_finish_process().

This double-close can cause all sorts of problems if another part of
wlroots allocates another FD between the two closes - the latter close
will close the wrong FD and things go horribly wrong (in my case leading
to use-after-free and segfaults).

Fix this by setting wm_fd[0]=-1 after calling xwm_create(), and ensuring
that xwm_create() closes the FD if startup errors occur.
2025-10-20 14:02:29 +01:00
Simon Ser
989cffe70d scene: add software fallback for gamma LUT 2025-10-18 20:36:01 +02:00
Simon Ser
3e08e3be4a gamma_control_v1: introduce fallback_gamma_size 2025-10-18 20:36:01 +02:00
Simon Ser
91f4890ec2 gamma_control_v1: add wlr_gamma_control_v1_get_color_transform() 2025-10-18 20:36:01 +02:00
Simon Ser
74ce6c22a5 output: check for color transform no-op changes
This allows callers to always set this state and not care whether
the output currently has the same color transform applied.
2025-10-18 20:36:01 +02:00
Simon Ser
0b58bddf13 render/color: add wlr_color_transform_pipeline
Useful to apply multiple transforms in sequence, e.g. sRGB inverse
EOTF followed by gamma LUTs.
2025-10-18 20:36:01 +02:00
Simon Ser
3d36ab9211 render/color: add wlr_color_transform_eval()
Makes it so the Vulkan renderer can handle arbitrary color
transforms, and doesn't need to be updated each time a new one is
added.
2025-10-18 20:35:02 +02:00
Furkan Sahin
d786e07899 backend/session: use device boot_display
shouldn't need to check for `boot_vga` if newer, more general
sysfs `boot_display` is set.
closes https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4016
2025-10-17 17:35:51 +00:00
Simon Ser
d3b461c0d2 linux-dmabuf-v1: add wlr_compositor_dmabuf_waiter_create() 2023-07-06 12:00:04 +02:00
Simon Ser
cacf9c6713 Add helper to wait for DMA-BUFs to be ready on surface commit
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3026
2023-07-06 12:00:04 +02:00
16 changed files with 525 additions and 49 deletions

View file

@ -519,8 +519,6 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
break;
}
bool is_boot_vga = false;
const char *path = udev_list_entry_get_name(entry);
struct udev_device *dev = udev_device_new_from_syspath(session->udev, path);
if (!dev) {
@ -536,6 +534,11 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
continue;
}
bool is_primary = false;
const char *boot_display = udev_device_get_sysattr_value(dev, "boot_display");
if (boot_display && strcmp(boot_display, "1") == 0) {
is_primary = true;
} else {
// This is owned by 'dev', so we don't need to free it
struct udev_device *pci =
udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
@ -543,7 +546,8 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
if (pci) {
const char *id = udev_device_get_sysattr_value(pci, "boot_vga");
if (id && strcmp(id, "1") == 0) {
is_boot_vga = true;
is_primary = true;
}
}
}
@ -557,7 +561,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
udev_device_unref(dev);
ret[i] = wlr_dev;
if (is_boot_vga) {
if (is_primary) {
struct wlr_device *tmp = ret[0];
ret[0] = ret[i];
ret[i] = tmp;

View file

@ -9,6 +9,7 @@ enum wlr_color_transform_type {
COLOR_TRANSFORM_INVERSE_EOTF,
COLOR_TRANSFORM_LCMS2,
COLOR_TRANSFORM_LUT_3X1D,
COLOR_TRANSFORM_PIPELINE,
};
struct wlr_color_transform {
@ -39,6 +40,13 @@ struct wlr_color_transform_lut_3x1d {
size_t dim;
};
struct wlr_color_transform_pipeline {
struct wlr_color_transform base;
struct wlr_color_transform **transforms;
size_t len;
};
void wlr_color_transform_init(struct wlr_color_transform *tr,
enum wlr_color_transform_type type);
@ -72,12 +80,6 @@ struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_b
struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
struct wlr_color_transform *tr);
/**
* Evaluate a 3x1D LUT color transform for a given RGB triplet.
*/
void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
float out[static 3], const float in[static 3]);
/**
* Obtain primaries values from a well-known primaries name.
*/

View file

@ -141,6 +141,13 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
const uint16_t *r, const uint16_t *g, const uint16_t *b);
/**
* Initialize a color transformation to apply a sequence of color transforms
* one after another.
*/
struct wlr_color_transform *wlr_color_transform_init_pipeline(
struct wlr_color_transform **transforms, size_t len);
/**
* Increase the reference count of the color transform by 1.
*/
@ -152,4 +159,10 @@ struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *
*/
void wlr_color_transform_unref(struct wlr_color_transform *tr);
/**
* Evaluate a color transform for a given RGB triplet.
*/
void wlr_color_transform_eval(struct wlr_color_transform *tr,
float out[static 3], const float in[static 3]);
#endif

View file

@ -10,6 +10,11 @@ struct wlr_gamma_control_manager_v1 {
struct wl_global *global;
struct wl_list controls; // wlr_gamma_control_v1.link
// Fallback to use when an struct wlr_output doesn't support gamma LUTs.
// Can be used to apply gamma LUTs via a struct wlr_renderer. Leave zero to
// indicate that the fallback is unsupported.
size_t fallback_gamma_size;
struct {
struct wl_signal destroy;
struct wl_signal set_gamma; // struct wlr_gamma_control_manager_v1_set_gamma_event
@ -49,6 +54,8 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control(
struct wlr_gamma_control_manager_v1 *manager, struct wlr_output *output);
bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control,
struct wlr_output_state *output_state);
struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform(
struct wlr_gamma_control_v1 *gamma_control);
void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control);
#endif

View file

@ -16,6 +16,7 @@
#include <wlr/render/dmabuf.h>
#include <wlr/render/drm_format_set.h>
struct wlr_compositor;
struct wlr_surface;
struct wlr_dmabuf_v1_buffer {
@ -134,4 +135,40 @@ struct wlr_linux_dmabuf_feedback_v1_init_options {
bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feedback_v1 *feedback,
const struct wlr_linux_dmabuf_feedback_v1_init_options *options);
/**
* A helper to wait for client DMA-BUFs to be ready.
*
* When attached to a surface, this helper will delay commits until the GPU
* work is done. In other words, wlr_surface.events.commit will only fire when
* GPU buffers attached to that commit are ready.
*/
struct wlr_surface_dmabuf_waiter {
struct wlr_surface *surface;
// private state
struct wl_list commits; // wlr_surface_dmabuf_waiter_commit.link
struct wl_listener client_commit;
};
/**
* Initialize a DMA-BUF waiter for a surface.
*
* Callers must call wlr_surface_dmabuf_waiter_finish() to unregister the waiter.
*/
void wlr_surface_dmabuf_waiter_init(struct wlr_surface_dmabuf_waiter *waiter,
struct wlr_surface *surface);
/**
* Clean up a DMA-BUF waiter.
*
* Any pending commit waiting on GPU work to complete will be applied
* immediately.
*/
void wlr_surface_dmabuf_waiter_finish(struct wlr_surface_dmabuf_waiter *waiter);
/**
* Initialize a compositor-wide DMA-BUF waiter, which will listen for new
* surfaces and attach DMA-BUF surface waiters to them.
*/
void wlr_compositor_dmabuf_waiter_create(struct wlr_compositor *compositor);
#endif

View file

@ -267,6 +267,7 @@ struct wlr_output {
struct {
struct wl_listener display_destroy;
struct wlr_output_image_description image_description_value;
struct wlr_color_transform *color_transform;
} WLR_PRIVATE;
};

View file

@ -252,6 +252,11 @@ struct wlr_scene_output {
bool gamma_lut_changed;
struct wlr_gamma_control_v1 *gamma_lut;
struct wlr_color_transform *gamma_lut_color_transform;
struct wlr_color_transform *prev_gamma_lut_color_transform;
struct wlr_color_transform *prev_supplied_color_transform;
struct wlr_color_transform *prev_combined_color_transform;
struct wl_listener output_commit;
struct wl_listener output_damage;

View file

@ -164,6 +164,7 @@ struct wlr_xwm {
struct wl_listener drop_focus_destroy;
};
// xwm_create takes ownership of wm_fd and will close it under all circumstances.
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd);
void xwm_destroy(struct wlr_xwm *xwm);

View file

@ -62,6 +62,33 @@ struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
return &tx->base;
}
struct wlr_color_transform *wlr_color_transform_init_pipeline(
struct wlr_color_transform **transforms, size_t len) {
assert(len > 0);
struct wlr_color_transform **copy = calloc(len, sizeof(copy[0]));
if (copy == NULL) {
return NULL;
}
struct wlr_color_transform_pipeline *tx = calloc(1, sizeof(*tx));
if (!tx) {
free(copy);
return NULL;
}
wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_PIPELINE);
// TODO: flatten nested pipeline transforms
for (size_t i = 0; i < len; i++) {
copy[i] = wlr_color_transform_ref(transforms[i]);
}
tx->transforms = copy;
tx->len = len;
return &tx->base;
}
static void color_transform_destroy(struct wlr_color_transform *tr) {
switch (tr->type) {
case COLOR_TRANSFORM_INVERSE_EOTF:
@ -73,6 +100,14 @@ static void color_transform_destroy(struct wlr_color_transform *tr) {
struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr);
free(lut_3x1d->lut_3x1d);
break;
case COLOR_TRANSFORM_PIPELINE:;
struct wlr_color_transform_pipeline *pipeline =
wl_container_of(tr, pipeline, base);
for (size_t i = 0; i < pipeline->len; i++) {
wlr_color_transform_unref(pipeline->transforms[i]);
}
free(pipeline->transforms);
break;
}
wlr_addon_set_finish(&tr->addons);
free(tr);
@ -108,6 +143,65 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
return lut_3x1d;
}
static float srgb_eval_inverse_eotf(float x) {
// See https://www.w3.org/Graphics/Color/srgb
if (x <= 0.0031308) {
return 12.92 * x;
} else {
return 1.055 * powf(x, 1.0 / 2.4) - 0.055;
}
}
static float st2084_pq_eval_inverse_eotf(float x) {
// H.273 TransferCharacteristics code point 16
float c1 = 0.8359375;
float c2 = 18.8515625;
float c3 = 18.6875;
float m = 78.84375;
float n = 0.1593017578125;
if (x < 0) {
x = 0;
}
if (x > 1) {
x = 1;
}
float pow_n = powf(x, n);
return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m);
}
static float bt1886_eval_inverse_eotf(float x) {
float lb = powf(0.0001, 1.0 / 2.4);
float lw = powf(1.0, 1.0 / 2.4);
float a = powf(lw - lb, 2.4);
float b = lb / (lw - lb);
return powf(x / a, 1.0 / 2.4) - b;
}
static float transfer_function_eval_inverse_eotf(
enum wlr_color_transfer_function tf, float x) {
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
return srgb_eval_inverse_eotf(x);
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
return st2084_pq_eval_inverse_eotf(x);
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
return x;
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
return powf(x, 1.0 / 2.2);
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
return bt1886_eval_inverse_eotf(x);
}
abort(); // unreachable
}
static void color_transform_inverse_eotf_eval(
struct wlr_color_transform_inverse_eotf *tr,
float out[static 3], const float in[static 3]) {
for (size_t i = 0; i < 3; i++) {
out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]);
}
}
static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) {
if (i >= len) {
i = len - 1;
@ -125,13 +219,38 @@ static float lut_1d_eval(const uint16_t *lut, size_t len, float x) {
return a * (1 - frac_part) + b * frac_part;
}
void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
float out[static 3], const float in[static 3]) {
for (size_t i = 0; i < 3; i++) {
out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]);
}
}
void wlr_color_transform_eval(struct wlr_color_transform *tr,
float out[static 3], const float in[static 3]) {
switch (tr->type) {
case COLOR_TRANSFORM_INVERSE_EOTF:
color_transform_inverse_eotf_eval(wlr_color_transform_inverse_eotf_from_base(tr), out, in);
break;
case COLOR_TRANSFORM_LCMS2:
color_transform_lcms2_eval(color_transform_lcms2_from_base(tr), out, in);
break;
case COLOR_TRANSFORM_LUT_3X1D:
color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in);
break;
case COLOR_TRANSFORM_PIPELINE:;
struct wlr_color_transform_pipeline *pipeline =
wl_container_of(tr, pipeline, base);
float color[3];
memcpy(color, in, sizeof(color));
for (size_t i = 0; i < pipeline->len; i++) {
wlr_color_transform_eval(pipeline->transforms[i], color, color);
}
memcpy(out, color, sizeof(color));
break;
}
}
void wlr_color_primaries_from_named(struct wlr_color_primaries *out,
enum wlr_color_named_primaries named) {
switch (named) {

View file

@ -964,19 +964,6 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer,
*ds = VK_NULL_HANDLE;
*ds_pool = NULL;
struct wlr_color_transform_lcms2 *tr_lcms2 = NULL;
struct wlr_color_transform_lut_3x1d *tr_lut_3x1d = NULL;
switch (tr->type) {
case COLOR_TRANSFORM_INVERSE_EOTF:
abort(); // unreachable
case COLOR_TRANSFORM_LCMS2:
tr_lcms2 = color_transform_lcms2_from_base(tr);
break;
case COLOR_TRANSFORM_LUT_3X1D:
tr_lut_3x1d = color_transform_lut_3x1d_from_base(tr);
break;
}
// R32G32B32 is not a required Vulkan format
// TODO: use it when available
VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT;
@ -1074,11 +1061,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer,
b_index * sample_range,
};
float rgb_out[3];
if (tr_lcms2 != NULL) {
color_transform_lcms2_eval(tr_lcms2, rgb_out, rgb_in);
} else {
color_transform_lut_3x1d_eval(tr_lut_3x1d, rgb_out, rgb_in);
}
wlr_color_transform_eval(tr, rgb_out, rgb_in);
dst[dst_offset] = rgb_out[0];
dst[dst_offset + 1] = rgb_out[1];

View file

@ -242,6 +242,15 @@ static void output_apply_state(struct wlr_output *output,
}
}
if (state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) {
wlr_color_transform_unref(output->color_transform);
if (state->color_transform != NULL) {
output->color_transform = wlr_color_transform_ref(state->color_transform);
} else {
output->color_transform = NULL;
}
}
bool geometry_updated = state->committed &
(WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM |
WLR_OUTPUT_STATE_SUBPIXEL);
@ -407,6 +416,7 @@ void wlr_output_finish(struct wlr_output *output) {
wlr_swapchain_destroy(output->cursor_swapchain);
wlr_buffer_unlock(output->cursor_front_buffer);
wlr_color_transform_unref(output->color_transform);
wlr_swapchain_destroy(output->swapchain);
@ -515,8 +525,7 @@ const struct wlr_output_image_description *output_pending_image_description(
* Returns a bitfield of the unchanged fields.
*
* Some fields are not checked: damage always changes in-between frames, the
* gamma LUT is too expensive to check, the contents of the buffer might have
* changed, etc.
* contents of the buffer might have changed, etc.
*/
static uint32_t output_compare_state(struct wlr_output *output,
const struct wlr_output_state *state) {
@ -562,6 +571,10 @@ static uint32_t output_compare_state(struct wlr_output *output,
output->subpixel == state->subpixel) {
fields |= WLR_OUTPUT_STATE_SUBPIXEL;
}
if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) &&
output->color_transform == state->color_transform) {
fields |= WLR_OUTPUT_STATE_COLOR_TRANSFORM;
}
return fields;
}

View file

@ -1530,6 +1530,8 @@ static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener *
output->gamma_lut_changed = true;
output->gamma_lut = event->control;
wlr_color_transform_unref(output->gamma_lut_color_transform);
output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control);
wlr_output_schedule_frame(output->output);
}
@ -1547,6 +1549,8 @@ static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *li
wl_list_for_each(output, &scene->outputs, link) {
output->gamma_lut_changed = false;
output->gamma_lut = NULL;
wlr_color_transform_unref(output->gamma_lut_color_transform);
output->gamma_lut_color_transform = NULL;
}
}
@ -1766,6 +1770,10 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link);
wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
wlr_color_transform_unref(scene_output->prev_combined_color_transform);
wl_array_release(&scene_output->render_list);
free(scene_output);
}
@ -2104,16 +2112,15 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp
return;
}
if (!wlr_gamma_control_v1_apply(scene_output->gamma_lut, &gamma_pending)) {
wlr_output_state_finish(&gamma_pending);
return;
}
wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform);
scene_output->gamma_lut_changed = false;
if (!wlr_output_test_state(scene_output->output, &gamma_pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut);
scene_output->gamma_lut = NULL;
wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
scene_output->gamma_lut_color_transform = NULL;
wlr_output_state_finish(&gamma_pending);
return;
}
@ -2122,6 +2129,41 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp
wlr_output_state_finish(&gamma_pending);
}
static struct wlr_color_transform *scene_output_combine_color_transforms(
struct wlr_scene_output *scene_output, struct wlr_color_transform *supplied) {
struct wlr_color_transform *gamma_lut = scene_output->gamma_lut_color_transform;
assert(gamma_lut != NULL);
if (gamma_lut == scene_output->prev_gamma_lut_color_transform &&
supplied == scene_output->prev_supplied_color_transform) {
return wlr_color_transform_ref(scene_output->prev_combined_color_transform);
}
struct wlr_color_transform *combined;
if (supplied == NULL) {
combined = wlr_color_transform_ref(gamma_lut);
} else {
struct wlr_color_transform *transforms[] = {
gamma_lut,
supplied,
};
size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]);
combined = wlr_color_transform_init_pipeline(transforms, transforms_len);
if (combined == NULL) {
return NULL;
}
}
wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
scene_output->prev_gamma_lut_color_transform = wlr_color_transform_ref(gamma_lut);
wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
scene_output->prev_supplied_color_transform = supplied ? wlr_color_transform_ref(supplied) : NULL;
wlr_color_transform_unref(scene_output->prev_combined_color_transform);
scene_output->prev_combined_color_transform = wlr_color_transform_ref(combined);
return combined;
}
bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) {
struct wlr_scene_output_state_options default_options = {0};
@ -2145,6 +2187,16 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
enum wlr_scene_debug_damage_option debug_damage =
scene_output->scene->debug_damage_option;
bool render_gamma_lut = false;
if (wlr_output_get_gamma_size(output) == 0 && output->renderer->features.output_color_transform) {
if (scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform) {
scene_output_damage_whole(scene_output);
}
if (scene_output->gamma_lut_color_transform != NULL) {
render_gamma_lut = true;
}
}
struct render_data render_data = {
.transform = output->transform,
.scale = output->scale,
@ -2245,7 +2297,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
// - There are no color transforms that need to be applied
// - Damage highlight debugging is not enabled
enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE;
if (options->color_transform == NULL && list_len == 1
if (options->color_transform == NULL && !render_gamma_lut && list_len == 1
&& debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
}
@ -2319,6 +2371,17 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
color_transform = wlr_color_transform_ref(options->color_transform);
}
if (render_gamma_lut) {
struct wlr_color_transform *combined =
scene_output_combine_color_transforms(scene_output, color_transform);
wlr_color_transform_unref(color_transform);
if (combined == NULL) {
wlr_buffer_unlock(buffer);
return false;
}
color_transform = combined;
}
scene_output->in_point++;
struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer,
&(struct wlr_buffer_pass_options){
@ -2441,7 +2504,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
scene_output->in_point);
}
if (!render_gamma_lut) {
scene_output_state_attempt_gamma(scene_output, state);
}
return true;
}

View file

@ -157,6 +157,9 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client,
}
size_t gamma_size = wlr_output_get_gamma_size(output);
if (gamma_size == 0) {
gamma_size = manager->fallback_gamma_size;
}
if (gamma_size == 0) {
zwlr_gamma_control_v1_send_failed(resource);
return;
@ -262,15 +265,24 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control(
return NULL;
}
bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control,
struct wlr_output_state *output_state) {
struct wlr_color_transform *tr = NULL;
if (gamma_control != NULL && gamma_control->table != NULL) {
struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform(
struct wlr_gamma_control_v1 *gamma_control) {
if (gamma_control == NULL || gamma_control->table == NULL) {
return NULL;
}
const uint16_t *r = gamma_control->table;
const uint16_t *g = gamma_control->table + gamma_control->ramp_size;
const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size;
tr = wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b);
return wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b);
}
bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control,
struct wlr_output_state *output_state) {
struct wlr_color_transform *tr = NULL;
if (gamma_control != NULL && gamma_control->table != NULL) {
tr = wlr_gamma_control_v1_get_color_transform(gamma_control);
if (tr == NULL) {
return false;
}

View file

@ -1,6 +1,7 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <fcntl.h>
#include <poll.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
@ -1173,3 +1174,210 @@ error:
wlr_linux_dmabuf_feedback_v1_finish(feedback);
return false;
}
struct wlr_surface_dmabuf_waiter_commit {
struct wlr_surface_dmabuf_waiter *waiter;
uint32_t surface_lock_seq;
int fds[WLR_DMABUF_MAX_PLANES]; // not owned by us
struct wl_event_source *event_sources[WLR_DMABUF_MAX_PLANES];
struct wl_list link; // wlr_surface_dmabuf_waiter.commits
};
static void surface_dmabuf_waiter_commit_destroy(
struct wlr_surface_dmabuf_waiter_commit *waiter_commit) {
for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) {
if (waiter_commit->event_sources[i] != NULL) {
wl_event_source_remove(waiter_commit->event_sources[i]);
}
}
wlr_surface_unlock_cached(waiter_commit->waiter->surface, waiter_commit->surface_lock_seq);
wl_list_remove(&waiter_commit->link);
free(waiter_commit);
}
static int surface_dmabuf_waiter_handle_fd_event(int fd, uint32_t mask, void *data) {
struct wlr_surface_dmabuf_waiter_commit *waiter_commit = data;
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
wlr_log(WLR_ERROR, "Got hangup/error while polling on DMA-BUF");
}
bool found = false;
bool need_wait = false;
for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) {
if (waiter_commit->fds[i] == fd) {
wl_event_source_remove(waiter_commit->event_sources[i]);
waiter_commit->event_sources[i] = NULL;
found = true;
} else if (waiter_commit->event_sources[i] != NULL) {
need_wait = true;
}
}
if (!found) {
wlr_log(WLR_ERROR, "Got event for unknown DMA-BUF FD");
return 0;
} else if (need_wait) {
return 0;
}
surface_dmabuf_waiter_commit_destroy(waiter_commit);
return 0;
}
static void surface_dmabuf_waiter_handle_client_commit(struct wl_listener *listener, void *data) {
struct wlr_surface_dmabuf_waiter *waiter = wl_container_of(listener, waiter, client_commit);
struct wlr_surface *surface = waiter->surface;
if (!(surface->pending.committed & WLR_SURFACE_STATE_BUFFER)) {
return;
}
struct wlr_buffer *buffer = surface->pending.buffer;
struct wlr_dmabuf_attributes dmabuf = {0};
if (buffer == NULL || !wlr_buffer_get_dmabuf(buffer, &dmabuf)) {
return;
}
// First check whether all DMA-BUF FDs are already ready
bool need_wait = false;
bool ready[WLR_DMABUF_MAX_PLANES] = {0};
for (int i = 0; i < dmabuf.n_planes; i++) {
struct pollfd pollfd = {
.fd = dmabuf.fd[i],
.events = POLLIN,
};
if (poll(&pollfd, 1, 0) < 0) {
wlr_log_errno(WLR_ERROR, "poll() failed");
return;
} else if (pollfd.revents & (POLLHUP | POLLERR)) {
wlr_log(WLR_ERROR, "Got hangup/error while polling on DMA-BUF");
return;
}
if (pollfd.revents & POLLIN) {
ready[i] = true;
} else {
need_wait = true;
}
}
if (!need_wait) {
return;
}
struct wlr_surface_dmabuf_waiter_commit *waiter_commit = calloc(1, sizeof(*waiter_commit));
if (waiter_commit == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return;
}
waiter_commit->waiter = waiter;
struct wl_client *client = wl_resource_get_client(surface->resource);
struct wl_display *display = wl_client_get_display(client);
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
for (int i = 0; i < dmabuf.n_planes; i++) {
if (ready[i]) {
continue;
}
struct wl_event_source *event_source = wl_event_loop_add_fd(event_loop, dmabuf.fd[i],
WL_EVENT_READABLE, surface_dmabuf_waiter_handle_fd_event, waiter_commit);
if (event_source == NULL) {
wlr_log(WLR_ERROR, "wl_event_loop_add_fd() failed");
goto error;
}
waiter_commit->fds[i] = dmabuf.fd[i];
waiter_commit->event_sources[i] = event_source;
}
// wlr_compositor ensures the wlr_buffer will remain alive (IOW, the
// DMA-BUF FDs will remain opened) while we have a lock
waiter_commit->surface_lock_seq = wlr_surface_lock_pending(waiter->surface);
wl_list_insert(&waiter->commits, &waiter_commit->link);
return;
error:
for (int i = 0; i < dmabuf.n_planes; i++) {
if (waiter_commit->event_sources[i] != NULL) {
wl_event_source_remove(waiter_commit->event_sources[i]);
}
}
free(waiter_commit);
}
void wlr_surface_dmabuf_waiter_init(struct wlr_surface_dmabuf_waiter *waiter,
struct wlr_surface *surface) {
*waiter = (struct wlr_surface_dmabuf_waiter){0};
waiter->surface = surface;
wl_list_init(&waiter->commits);
waiter->client_commit.notify = surface_dmabuf_waiter_handle_client_commit;
wl_signal_add(&surface->events.client_commit, &waiter->client_commit);
}
void wlr_surface_dmabuf_waiter_finish(struct wlr_surface_dmabuf_waiter *waiter) {
struct wlr_surface_dmabuf_waiter_commit *waiter_commit, *waiter_commit_tmp;
wl_list_for_each_safe(waiter_commit, waiter_commit_tmp, &waiter->commits, link) {
surface_dmabuf_waiter_commit_destroy(waiter_commit);
}
wl_list_remove(&waiter->commits);
wl_list_remove(&waiter->client_commit.link);
}
struct wlr_compositor_dmabuf_waiter {
struct wl_listener new_surface;
struct wl_listener destroy;
};
struct wlr_compositor_dmabuf_waiter_surface {
struct wlr_surface_dmabuf_waiter base;
struct wl_listener destroy;
};
static void compositor_dmabuf_waiter_surface_handle_destroy(struct wl_listener *listener, void *data) {
struct wlr_compositor_dmabuf_waiter_surface *waiter_surface =
wl_container_of(listener, waiter_surface, destroy);
wlr_surface_dmabuf_waiter_finish(&waiter_surface->base);
wl_list_remove(&waiter_surface->destroy.link);
free(waiter_surface);
}
static void compositor_dmabuf_waiter_handle_new_surface(struct wl_listener *listener, void *data) {
struct wlr_surface *surface = data;
struct wlr_compositor_dmabuf_waiter_surface *waiter_surface = calloc(1, sizeof(*waiter_surface));
if (waiter_surface == NULL) {
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
wlr_surface_dmabuf_waiter_init(&waiter_surface->base, surface);
waiter_surface->destroy.notify = compositor_dmabuf_waiter_surface_handle_destroy;
wl_signal_add(&surface->events.destroy, &waiter_surface->destroy);
}
static void compositor_dmabuf_waiter_handle_destroy(struct wl_listener *listener, void *data) {
struct wlr_compositor_dmabuf_waiter *waiter =
wl_container_of(listener, waiter, destroy);
wl_list_remove(&waiter->new_surface.link);
wl_list_remove(&waiter->destroy.link);
free(waiter);
}
void wlr_compositor_dmabuf_waiter_create(struct wlr_compositor *compositor) {
struct wlr_compositor_dmabuf_waiter *waiter = calloc(1, sizeof(*waiter));
if (waiter == NULL) {
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
waiter->new_surface.notify = compositor_dmabuf_waiter_handle_new_surface;
wl_signal_add(&compositor->events.new_surface, &waiter->new_surface);
waiter->destroy.notify = compositor_dmabuf_waiter_handle_destroy;
wl_signal_add(&compositor->events.destroy, &waiter->destroy);
}

View file

@ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) {
static void xwayland_mark_ready(struct wlr_xwayland *xwayland) {
assert(xwayland->server->wm_fd[0] >= 0);
xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]);
// xwm_create takes ownership of wm_fd[0] under all circumstances
xwayland->server->wm_fd[0] = -1;
if (!xwayland->xwm) {
return;
}

View file

@ -2530,6 +2530,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride,
struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
struct wlr_xwm *xwm = calloc(1, sizeof(*xwm));
if (xwm == NULL) {
close(wm_fd);
return NULL;
}
@ -2544,11 +2545,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
xwm->ping_timeout = 10000;
// xcb_connect_to_fd takes ownership of the FD regardless of success/failure
xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL);
int rc = xcb_connection_has_error(xwm->xcb_conn);
if (rc) {
wlr_log(WLR_ERROR, "xcb connect failed: %d", rc);
xcb_disconnect(xwm->xcb_conn);
free(xwm);
return NULL;
}