diff --git a/.builds/alpine.yml b/.builds/alpine.yml index affd85411..f8d75d8b1 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -35,6 +35,9 @@ tasks: cd wlroots ninja -C build sudo ninja -C build install + - test: | + cd wlroots + meson test -C build --verbose - build-features-disabled: | cd wlroots meson setup build --reconfigure -Dauto_features=disabled diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index fae04ab31..84e04eda7 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -37,6 +37,10 @@ tasks: - clang: | cd wlroots/build-clang ninja + - test: | + cd wlroots/build-gcc + meson test --verbose + meson test --benchmark --verbose - smoke-test: | cd wlroots/build-gcc/tinywl sudo modprobe vkms diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index a99e9c911..3bfe37489 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -32,6 +32,9 @@ tasks: meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dallocators=gbm ninja -C build sudo ninja -C build install + - test: | + cd wlroots + meson test -C build --verbose - tinywl: | cd wlroots/tinywl make diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index faa535d44..daa8ba9bf 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -475,6 +476,62 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, atomic_add(atom, id, props->crtc_h, dst_box->height); } +static void set_color_encoding_and_range(struct atomic *atom, + struct wlr_drm_backend *drm, struct wlr_drm_plane *plane, + enum wlr_color_encoding encoding, enum wlr_color_range range) { + uint32_t id = plane->id; + const struct wlr_drm_plane_props *props = &plane->props; + + uint32_t color_encoding; + switch (encoding) { + case WLR_COLOR_ENCODING_NONE: + case WLR_COLOR_ENCODING_BT601: + color_encoding = WLR_DRM_COLOR_YCBCR_BT601; + break; + case WLR_COLOR_ENCODING_BT709: + color_encoding = WLR_DRM_COLOR_YCBCR_BT709; + break; + case WLR_COLOR_ENCODING_BT2020: + color_encoding = WLR_DRM_COLOR_YCBCR_BT2020; + break; + default: + wlr_log(WLR_DEBUG, "Unsupported color encoding %d", encoding); + atom->failed = true; + return; + } + + if (props->color_encoding) { + atomic_add(atom, id, props->color_encoding, color_encoding); + } else { + wlr_log(WLR_DEBUG, "Plane %"PRIu32" is missing the COLOR_ENCODING property", + id); + atom->failed = true; + return; + } + + uint32_t color_range; + switch (range) { + case WLR_COLOR_RANGE_NONE: + case WLR_COLOR_RANGE_LIMITED: + color_range = WLR_DRM_COLOR_YCBCR_LIMITED_RANGE; + break; + case WLR_COLOR_RANGE_FULL: + color_range = WLR_DRM_COLOR_YCBCR_FULL_RANGE; + break; + default: + assert(0); // Unreachable + } + + if (props->color_range) { + atomic_add(atom, id, props->color_range, color_range); + } else { + wlr_log(WLR_DEBUG, "Plane %"PRIu32" is missing the COLOR_RANGE property", + id); + atom->failed = true; + return; + } +} + static bool supports_cursor_hotspots(const struct wlr_drm_plane *plane) { return plane->props.hotspot_x && plane->props.hotspot_y; } @@ -528,6 +585,10 @@ static void atomic_connector_add(struct atomic *atom, set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, &state->primary_viewport.dst_box, &state->primary_viewport.src_box); + if (state->base->committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) { + set_color_encoding_and_range(atom, drm, crtc->primary, + state->base->color_encoding, state->base->color_range); + } if (crtc->primary->props.fb_damage_clips != 0) { atomic_add(atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, state->fb_damage_clips); diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 5a545b192..c2806152b 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -95,7 +95,7 @@ static const struct wlr_backend_impl backend_impl = { .commit = backend_commit, }; -bool wlr_backend_is_drm(struct wlr_backend *b) { +bool wlr_backend_is_drm(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index bd29872fb..d2f75f71f 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -43,7 +43,8 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_COLOR_TRANSFORM | - WLR_OUTPUT_STATE_IMAGE_DESCRIPTION; + WLR_OUTPUT_STATE_IMAGE_DESCRIPTION | + WLR_OUTPUT_STATE_COLOR_REPRESENTATION; static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; @@ -1320,7 +1321,7 @@ static const struct wlr_output_impl output_impl = { .get_primary_formats = drm_connector_get_primary_formats, }; -bool wlr_output_is_drm(struct wlr_output *output) { +bool wlr_output_is_drm(const struct wlr_output *output) { return output->impl == &output_impl; } diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 314023954..4c6bdcb36 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -50,6 +50,8 @@ static const struct prop_info crtc_info[] = { static const struct prop_info plane_info[] = { #define INDEX(name) (offsetof(struct wlr_drm_plane_props, name) / sizeof(uint32_t)) + { "COLOR_ENCODING", INDEX(color_encoding) }, + { "COLOR_RANGE", INDEX(color_range) }, { "CRTC_H", INDEX(crtc_h) }, { "CRTC_ID", INDEX(crtc_id) }, { "CRTC_W", INDEX(crtc_w) }, diff --git a/backend/headless/backend.c b/backend/headless/backend.c index d03f520b8..0f21b53f4 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -81,6 +81,6 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop) { return &backend->backend; } -bool wlr_backend_is_headless(struct wlr_backend *backend) { +bool wlr_backend_is_headless(const struct wlr_backend *backend) { return backend->impl == &backend_impl; } diff --git a/backend/headless/output.c b/backend/headless/output.c index a4cdb17c3..0464e0d1b 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -106,7 +106,7 @@ static const struct wlr_output_impl output_impl = { .move_cursor = output_move_cursor, }; -bool wlr_output_is_headless(struct wlr_output *wlr_output) { +bool wlr_output_is_headless(const struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 121731b31..0c72c6849 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -155,7 +155,7 @@ static const struct wlr_backend_impl backend_impl = { .destroy = backend_destroy, }; -bool wlr_backend_is_libinput(struct wlr_backend *b) { +bool wlr_backend_is_libinput(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 3d8fb96f5..3a9aca7e6 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -173,7 +173,7 @@ struct wlr_backend *wlr_multi_backend_create(struct wl_event_loop *loop) { return &backend->backend; } -bool wlr_backend_is_multi(struct wlr_backend *b) { +bool wlr_backend_is_multi(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 14a783b67..0d294b543 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -577,7 +577,7 @@ static const struct wlr_backend_impl backend_impl = { .get_drm_fd = backend_get_drm_fd, }; -bool wlr_backend_is_wl(struct wlr_backend *b) { +bool wlr_backend_is_wl(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/wayland/output.c b/backend/wayland/output.c index fb4d1f914..c3442f411 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -1027,7 +1027,7 @@ static const struct wlr_output_impl output_impl = { .get_primary_formats = output_get_formats, }; -bool wlr_output_is_wl(struct wlr_output *wlr_output) { +bool wlr_output_is_wl(const struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 4dc4bf16d..de22faa0e 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -219,7 +219,7 @@ static const struct wlr_backend_impl backend_impl = { .get_drm_fd = backend_get_drm_fd, }; -bool wlr_backend_is_x11(struct wlr_backend *backend) { +bool wlr_backend_is_x11(const struct wlr_backend *backend) { return backend->impl == &backend_impl; } diff --git a/backend/x11/output.c b/backend/x11/output.c index 51a9c1441..40e601f0b 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -711,7 +711,7 @@ void handle_x11_configure_notify(struct wlr_x11_output *output, wlr_output_state_finish(&state); } -bool wlr_output_is_x11(struct wlr_output *wlr_output) { +bool wlr_output_is_x11(const struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index c02d655ba..20e0a5bff 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -65,6 +65,22 @@ struct wlr_drm_plane_props { uint32_t hotspot_x; uint32_t hotspot_y; uint32_t in_fence_fd; + + uint32_t color_encoding; // Not guaranteed to exist + uint32_t color_range; // Not guaranteed to exist +}; + +// Equivalent to wlr_drm_color_encoding defined in the kernel (but not exported) +enum wlr_drm_color_encoding { + WLR_DRM_COLOR_YCBCR_BT601, + WLR_DRM_COLOR_YCBCR_BT709, + WLR_DRM_COLOR_YCBCR_BT2020, +}; + +// Equivalent to wlr_drm_color_range defined in the kernel (but not exported) +enum wlr_drm_color_range { + WLR_DRM_COLOR_YCBCR_FULL_RANGE, + WLR_DRM_COLOR_YCBCR_LIMITED_RANGE, }; bool get_drm_connector_props(int fd, uint32_t id, diff --git a/include/render/vulkan.h b/include/render/vulkan.h index f96619f2c..82d3e40fd 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -56,6 +56,10 @@ struct wlr_vk_device { PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; PFN_vkQueueSubmit2KHR vkQueueSubmit2KHR; + PFN_vkBindImageMemory2KHR vkBindImageMemory2KHR; + PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR; + PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR; + PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; } api; uint32_t format_prop_count; @@ -406,7 +410,7 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer); // Submits the current stage command buffer and waits until it has // finished execution. -bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer); +bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd); struct wlr_vk_render_pass_texture { struct wlr_vk_texture *texture; @@ -472,6 +476,8 @@ uint64_t vulkan_end_command_buffer(struct wlr_vk_command_buffer *cb, void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb); bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer); +VkSemaphore vulkan_command_buffer_wait_sync_file(struct wlr_vk_renderer *renderer, + struct wlr_vk_command_buffer *render_cb, size_t sem_index, int sync_file_fd); bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer, struct wlr_vk_render_pass *pass); @@ -484,7 +490,8 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, VkFormat src_format, VkImage src_image, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, - uint32_t dst_x, uint32_t dst_y, void *data); + uint32_t dst_x, uint32_t dst_y, void *data, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point); // State (e.g. image texture) associated with a surface. struct wlr_vk_texture { diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index 3ca6390ab..233bbcb74 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -39,8 +39,8 @@ struct wlr_drm_lease { struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, struct wlr_device *dev, struct wlr_backend *parent); -bool wlr_backend_is_drm(struct wlr_backend *backend); -bool wlr_output_is_drm(struct wlr_output *output); +bool wlr_backend_is_drm(const struct wlr_backend *backend); +bool wlr_output_is_drm(const struct wlr_output *output); /** * Get the parent DRM backend, if any. diff --git a/include/wlr/backend/headless.h b/include/wlr/backend/headless.h index 126f1ff21..5487c6085 100644 --- a/include/wlr/backend/headless.h +++ b/include/wlr/backend/headless.h @@ -25,7 +25,7 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop); struct wlr_output *wlr_headless_add_output(struct wlr_backend *backend, unsigned int width, unsigned int height); -bool wlr_backend_is_headless(struct wlr_backend *backend); -bool wlr_output_is_headless(struct wlr_output *output); +bool wlr_backend_is_headless(const struct wlr_backend *backend); +bool wlr_output_is_headless(const struct wlr_output *output); #endif diff --git a/include/wlr/backend/libinput.h b/include/wlr/backend/libinput.h index 2a4e1996e..cb5ef824d 100644 --- a/include/wlr/backend/libinput.h +++ b/include/wlr/backend/libinput.h @@ -29,7 +29,7 @@ struct libinput_device *wlr_libinput_get_device_handle( struct libinput_tablet_tool *wlr_libinput_get_tablet_tool_handle( struct wlr_tablet_tool *wlr_tablet_tool); -bool wlr_backend_is_libinput(struct wlr_backend *backend); +bool wlr_backend_is_libinput(const struct wlr_backend *backend); bool wlr_input_device_is_libinput(struct wlr_input_device *device); #endif diff --git a/include/wlr/backend/multi.h b/include/wlr/backend/multi.h index c4322d98b..cc3e41b4e 100644 --- a/include/wlr/backend/multi.h +++ b/include/wlr/backend/multi.h @@ -26,7 +26,7 @@ bool wlr_multi_backend_add(struct wlr_backend *multi, void wlr_multi_backend_remove(struct wlr_backend *multi, struct wlr_backend *backend); -bool wlr_backend_is_multi(struct wlr_backend *backend); +bool wlr_backend_is_multi(const struct wlr_backend *backend); bool wlr_multi_is_empty(struct wlr_backend *backend); void wlr_multi_for_each_backend(struct wlr_backend *backend, diff --git a/include/wlr/backend/wayland.h b/include/wlr/backend/wayland.h index c732de68c..7a41b3d09 100644 --- a/include/wlr/backend/wayland.h +++ b/include/wlr/backend/wayland.h @@ -46,7 +46,7 @@ struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *backend /** * Check whether the provided backend is a Wayland backend. */ -bool wlr_backend_is_wl(struct wlr_backend *backend); +bool wlr_backend_is_wl(const struct wlr_backend *backend); /** * Check whether the provided input device is a Wayland input device. @@ -56,7 +56,7 @@ bool wlr_input_device_is_wl(struct wlr_input_device *device); /** * Check whether the provided output device is a Wayland output device. */ -bool wlr_output_is_wl(struct wlr_output *output); +bool wlr_output_is_wl(const struct wlr_output *output); /** * Sets the title of a struct wlr_output which is a Wayland toplevel. diff --git a/include/wlr/backend/x11.h b/include/wlr/backend/x11.h index 1791041ce..8e8bc9870 100644 --- a/include/wlr/backend/x11.h +++ b/include/wlr/backend/x11.h @@ -31,7 +31,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend); /** * Check whether this backend is an X11 backend. */ -bool wlr_backend_is_x11(struct wlr_backend *backend); +bool wlr_backend_is_x11(const struct wlr_backend *backend); /** * Check whether this input device is an X11 input device. @@ -41,7 +41,7 @@ bool wlr_input_device_is_x11(struct wlr_input_device *device); /** * Check whether this output device is an X11 output device. */ -bool wlr_output_is_x11(struct wlr_output *output); +bool wlr_output_is_x11(const struct wlr_output *output); /** * Sets the title of a struct wlr_output which is an X11 window. diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index fabe23d1c..c7dd3b34c 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -97,7 +97,11 @@ bool wlr_drm_syncobj_timeline_signal(struct wlr_drm_syncobj_timeline *timeline, /** * Asynchronously wait for a timeline point. * - * See wlr_drm_syncobj_timeline_check() for a definition of flags. + * Flags can be: + * + * - 0 to wait for the point to be signalled + * - DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE to only wait for a fence to + * materialize * * A callback must be provided that will be invoked when the waiter has finished. */ diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index 454e7eb0e..87a57fa54 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -42,9 +42,9 @@ struct wlr_gles2_texture_attribs { bool has_alpha; }; -bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer); -bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer); -bool wlr_texture_is_gles2(struct wlr_texture *texture); +bool wlr_renderer_is_gles2(const struct wlr_renderer *wlr_renderer); +bool wlr_render_timer_is_gles2(const struct wlr_render_timer *timer); +bool wlr_texture_is_gles2(const struct wlr_texture *texture); void wlr_gles2_texture_get_attribs(struct wlr_texture *texture, struct wlr_gles2_texture_attribs *attribs); diff --git a/include/wlr/render/pixman.h b/include/wlr/render/pixman.h index 206dd78c6..2951f5e9e 100644 --- a/include/wlr/render/pixman.h +++ b/include/wlr/render/pixman.h @@ -14,8 +14,8 @@ struct wlr_renderer *wlr_pixman_renderer_create(void); -bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer); -bool wlr_texture_is_pixman(struct wlr_texture *texture); +bool wlr_renderer_is_pixman(const struct wlr_renderer *wlr_renderer); +bool wlr_texture_is_pixman(const struct wlr_texture *texture); pixman_image_t *wlr_pixman_renderer_get_buffer_image( struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer); diff --git a/include/wlr/render/vulkan.h b/include/wlr/render/vulkan.h index 50f8c558d..170665dd0 100644 --- a/include/wlr/render/vulkan.h +++ b/include/wlr/render/vulkan.h @@ -25,8 +25,8 @@ VkPhysicalDevice wlr_vk_renderer_get_physical_device(struct wlr_renderer *render VkDevice wlr_vk_renderer_get_device(struct wlr_renderer *renderer); uint32_t wlr_vk_renderer_get_queue_family(struct wlr_renderer *renderer); -bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer); -bool wlr_texture_is_vk(struct wlr_texture *texture); +bool wlr_renderer_is_vk(const struct wlr_renderer *wlr_renderer); +bool wlr_texture_is_vk(const struct wlr_texture *texture); void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture, struct wlr_vk_image_attribs *attribs); diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h index 1e352c6e6..b7dcb143b 100644 --- a/include/wlr/render/wlr_texture.h +++ b/include/wlr/render/wlr_texture.h @@ -37,6 +37,8 @@ struct wlr_texture_read_pixels_options { uint32_t dst_x, dst_y; /** Source box of the texture to read from. If empty, the full texture is assumed. */ const struct wlr_box src_box; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; }; bool wlr_texture_read_pixels(struct wlr_texture *texture, diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 4485b3694..c8e44b0e6 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -77,6 +77,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 11, WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, + WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14, }; enum wlr_output_state_mode_type { @@ -142,6 +143,10 @@ struct wlr_output_state { * regular page-flip at the next wlr_output.frame event. */ bool tearing_page_flip; + // Set if (committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) + enum wlr_color_encoding color_encoding; + enum wlr_color_range color_range; + enum wlr_output_state_mode_type mode_type; struct wlr_output_mode *mode; struct { @@ -205,6 +210,8 @@ struct wlr_output { enum wl_output_transform transform; enum wlr_output_adaptive_sync_status adaptive_sync_status; uint32_t render_format; + enum wlr_color_encoding color_encoding; + enum wlr_color_range color_range; const struct wlr_output_image_description *image_description; // Indicates whether making changes to adaptive sync status is supported. @@ -625,6 +632,15 @@ void wlr_output_state_set_color_transform(struct wlr_output_state *state, bool wlr_output_state_set_image_description(struct wlr_output_state *state, const struct wlr_output_image_description *image_desc); +/** + * Set the color encoding and range of the primary scanout buffer. + * + * Pass WLR_COLOR_ENCODING_NONE / WLR_COLOR_RANGE_NONE to reset to defaults. + */ +void wlr_output_state_set_color_encoding_and_range( + struct wlr_output_state *state, + enum wlr_color_encoding encoding, enum wlr_color_range range); + /** * Copies the output state from src to dst. It is safe to then * wlr_output_state_finish() src and have dst still be valid. diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 6c627b1f9..f6f97cfea 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -171,8 +171,6 @@ struct wlr_scene_buffer { struct { struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event - struct wl_signal output_enter; // struct wlr_scene_output - struct wl_signal output_leave; // struct wlr_scene_output struct wl_signal output_sample; // struct wlr_scene_output_sample_event struct wl_signal frame_done; // struct wlr_scene_frame_done_event } events; diff --git a/include/wlr/util/box.h b/include/wlr/util/box.h index 64dcbc5cf..f6809e0c3 100644 --- a/include/wlr/util/box.h +++ b/include/wlr/util/box.h @@ -107,6 +107,13 @@ void wlr_fbox_transform(struct wlr_fbox *dest, const struct wlr_fbox *box, #ifdef WLR_USE_UNSTABLE +/** + * Checks whether two boxes intersect. + * + * Returns false if either box is empty. + */ +bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b); + /** * Returns true if the two boxes are equal, false otherwise. */ diff --git a/include/xwayland/selection.h b/include/xwayland/selection.h index 308310232..2f333f8e0 100644 --- a/include/xwayland/selection.h +++ b/include/xwayland/selection.h @@ -82,9 +82,9 @@ void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_selection_notify_event_t *event); int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_xfixes_selection_notify_event_t *event); -bool data_source_is_xwayland(struct wlr_data_source *wlr_source); +bool data_source_is_xwayland(const struct wlr_data_source *wlr_source); bool primary_selection_source_is_xwayland( - struct wlr_primary_selection_source *wlr_source); + const struct wlr_primary_selection_source *wlr_source); void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag); diff --git a/meson.build b/meson.build index 9d6b21222..6d31bdc41 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.20.0', + version: '0.21.0-dev', license: 'MIT', meson_version: '>=1.3', default_options: [ @@ -178,6 +178,10 @@ if get_option('examples') subdir('tinywl') endif +if get_option('tests') + subdir('test') +endif + pkgconfig = import('pkgconfig') pkgconfig.generate( lib_wlr, diff --git a/meson.options b/meson.options index 5863764aa..d8a8ca940 100644 --- a/meson.options +++ b/meson.options @@ -7,5 +7,6 @@ option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], v option('allocators', type: 'array', choices: ['auto', 'gbm', 'udmabuf'], value: ['auto'], description: 'Select built-in allocators') option('session', type: 'feature', value: 'auto', description: 'Enable session support') +option('tests', type: 'boolean', value: true, description: 'Build tests and benchmarks') option('color-management', type: 'feature', value: 'auto', description: 'Enable support for color management') option('libliftoff', type: 'feature', value: 'auto', description: 'Enable support for libliftoff') diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index 2abe2cff6..15f71c536 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -222,14 +222,8 @@ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter return false; } - struct drm_syncobj_eventfd syncobj_eventfd = { - .handle = timeline->handle, - .flags = flags, - .point = point, - .fd = ev_fd, - }; - if (drmIoctl(timeline->drm_fd, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobj_eventfd) != 0) { - wlr_log_errno(WLR_ERROR, "DRM_IOCTL_SYNCOBJ_EVENTFD failed"); + if (drmSyncobjEventfd(timeline->drm_fd, timeline->handle, point, ev_fd, flags) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjEventfd() failed"); close(ev_fd); return false; } diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index e362daee8..7f01b8acd 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -29,7 +29,7 @@ static const struct wlr_renderer_impl renderer_impl; static const struct wlr_render_timer_impl render_timer_impl; -bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer) { +bool wlr_renderer_is_gles2(const struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } @@ -40,7 +40,7 @@ struct wlr_gles2_renderer *gles2_get_renderer( return renderer; } -bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer) { +bool wlr_render_timer_is_gles2(const struct wlr_render_timer *timer) { return timer->impl == &render_timer_impl; } diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 9a967ebdb..575a11fca 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -16,7 +18,7 @@ static const struct wlr_texture_impl texture_impl; -bool wlr_texture_is_gles2(struct wlr_texture *wlr_texture) { +bool wlr_texture_is_gles2(const struct wlr_texture *wlr_texture) { return wlr_texture->impl == &texture_impl; } @@ -201,6 +203,27 @@ static bool gles2_texture_read_pixels(struct wlr_texture *wlr_texture, return false; } + if (options->wait_timeline != NULL) { + int sync_file_fd = + wlr_drm_syncobj_timeline_export_sync_file(options->wait_timeline, options->wait_point); + if (sync_file_fd < 0) { + return false; + } + + struct wlr_gles2_renderer *renderer = texture->renderer; + EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, sync_file_fd); + close(sync_file_fd); + if (sync == EGL_NO_SYNC_KHR) { + return false; + } + + bool ok = wlr_egl_wait_sync(renderer->egl, sync); + wlr_egl_destroy_sync(renderer->egl, sync); + if (!ok) { + return false; + } + } + // Make sure any pending drawing is finished before we try to read it glFinish(); diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index 4631a33ab..25c558a9d 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -12,7 +12,7 @@ static const struct wlr_renderer_impl renderer_impl; -bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer) { +bool wlr_renderer_is_pixman(const struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } @@ -69,7 +69,7 @@ static struct wlr_pixman_buffer *get_buffer( static const struct wlr_texture_impl texture_impl; -bool wlr_texture_is_pixman(struct wlr_texture *texture) { +bool wlr_texture_is_pixman(const struct wlr_texture *texture) { return texture->impl == &texture_impl; } diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 503e37c07..becd060f4 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -99,53 +99,6 @@ static void render_pass_destroy(struct wlr_vk_render_pass *pass) { free(pass); } -static VkSemaphore render_pass_wait_sync_file(struct wlr_vk_render_pass *pass, - size_t sem_index, int sync_file_fd) { - struct wlr_vk_renderer *renderer = pass->renderer; - struct wlr_vk_command_buffer *render_cb = pass->command_buffer; - VkResult res; - - VkSemaphore *wait_semaphores = render_cb->wait_semaphores.data; - size_t wait_semaphores_len = render_cb->wait_semaphores.size / sizeof(wait_semaphores[0]); - - VkSemaphore *sem_ptr; - if (sem_index >= wait_semaphores_len) { - sem_ptr = wl_array_add(&render_cb->wait_semaphores, sizeof(*sem_ptr)); - if (sem_ptr == NULL) { - return VK_NULL_HANDLE; - } - *sem_ptr = VK_NULL_HANDLE; - } else { - sem_ptr = &wait_semaphores[sem_index]; - } - - if (*sem_ptr == VK_NULL_HANDLE) { - VkSemaphoreCreateInfo semaphore_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - }; - res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, sem_ptr); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateSemaphore", res); - return VK_NULL_HANDLE; - } - } - - VkImportSemaphoreFdInfoKHR import_info = { - .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, - .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, - .semaphore = *sem_ptr, - .fd = sync_file_fd, - }; - res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info); - if (res != VK_SUCCESS) { - wlr_vk_error("vkImportSemaphoreFdKHR", res); - return VK_NULL_HANDLE; - } - - return *sem_ptr; -} - static bool render_pass_wait_render_buffer(struct wlr_vk_render_pass *pass, VkSemaphoreSubmitInfoKHR *render_wait, uint32_t *render_wait_len_ptr) { int sync_file_fds[WLR_DMABUF_MAX_PLANES]; @@ -162,7 +115,8 @@ static bool render_pass_wait_render_buffer(struct wlr_vk_render_pass *pass, continue; } - VkSemaphore sem = render_pass_wait_sync_file(pass, *render_wait_len_ptr, sync_file_fds[i]); + VkSemaphore sem = vulkan_command_buffer_wait_sync_file(pass->renderer, + pass->command_buffer, *render_wait_len_ptr, sync_file_fds[i]); if (sem == VK_NULL_HANDLE) { close(sync_file_fds[i]); continue; @@ -431,7 +385,8 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { continue; } - VkSemaphore sem = render_pass_wait_sync_file(pass, render_wait_len, sync_file_fds[i]); + VkSemaphore sem = vulkan_command_buffer_wait_sync_file(renderer, render_cb, + render_wait_len, sync_file_fds[i]); if (sem == VK_NULL_HANDLE) { close(sync_file_fds[i]); continue; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index da17a4703..e04f95a0a 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -46,7 +46,7 @@ static bool default_debug = true; static const struct wlr_renderer_impl renderer_impl; -bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer) { +bool wlr_renderer_is_vk(const struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } @@ -379,7 +379,52 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer) { return renderer->stage.cb->vk; } -bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) { +VkSemaphore vulkan_command_buffer_wait_sync_file(struct wlr_vk_renderer *renderer, + struct wlr_vk_command_buffer *render_cb, size_t sem_index, int sync_file_fd) { + VkResult res; + + VkSemaphore *wait_semaphores = render_cb->wait_semaphores.data; + size_t wait_semaphores_len = render_cb->wait_semaphores.size / sizeof(wait_semaphores[0]); + + VkSemaphore *sem_ptr; + if (sem_index >= wait_semaphores_len) { + sem_ptr = wl_array_add(&render_cb->wait_semaphores, sizeof(*sem_ptr)); + if (sem_ptr == NULL) { + return VK_NULL_HANDLE; + } + *sem_ptr = VK_NULL_HANDLE; + } else { + sem_ptr = &wait_semaphores[sem_index]; + } + + if (*sem_ptr == VK_NULL_HANDLE) { + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, sem_ptr); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateSemaphore", res); + return VK_NULL_HANDLE; + } + } + + VkImportSemaphoreFdInfoKHR import_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, + .semaphore = *sem_ptr, + .fd = sync_file_fd, + }; + res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info); + if (res != VK_SUCCESS) { + wlr_vk_error("vkImportSemaphoreFdKHR", res); + return VK_NULL_HANDLE; + } + + return *sem_ptr; +} + +bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd) { if (renderer->stage.cb == NULL) { return false; } @@ -389,9 +434,12 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) { uint64_t timeline_point = vulkan_end_command_buffer(cb, renderer); if (timeline_point == 0) { + close(wait_sync_file_fd); return false; } + VkSemaphore wait_semaphore; + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info = { .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, .signalSemaphoreValueCount = 1, @@ -405,6 +453,18 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) { .signalSemaphoreCount = 1, .pSignalSemaphores = &renderer->timeline_semaphore, }; + + if (wait_sync_file_fd != -1) { + wait_semaphore = vulkan_command_buffer_wait_sync_file(renderer, cb, 0, wait_sync_file_fd); + if (wait_semaphore == VK_NULL_HANDLE) { + close(wait_sync_file_fd); + return false; + } + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &wait_semaphore; + submit_info.pWaitDstStageMask = &wait_stage; + } + VkResult res = vkQueueSubmit(renderer->dev->queue, 1, &submit_info, VK_NULL_HANDLE); if (res != VK_SUCCESS) { wlr_vk_error("vkQueueSubmit", res); @@ -1188,7 +1248,7 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { vkDestroyPipelineLayout(dev->dev, pipeline_layout->vk, NULL); vkDestroyDescriptorSetLayout(dev->dev, pipeline_layout->ds, NULL); vkDestroySampler(dev->dev, pipeline_layout->sampler, NULL); - vkDestroySamplerYcbcrConversion(dev->dev, pipeline_layout->ycbcr.conversion, NULL); + renderer->dev->api.vkDestroySamplerYcbcrConversionKHR(dev->dev, pipeline_layout->ycbcr.conversion, NULL); free(pipeline_layout); } @@ -1218,7 +1278,8 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, VkFormat src_format, VkImage src_image, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, - uint32_t dst_x, uint32_t dst_y, void *data) { + uint32_t dst_x, uint32_t dst_y, void *data, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { VkDevice dev = vk_renderer->dev->dev; const struct wlr_pixel_format_info *pixel_format_info = drm_get_pixel_format_info(drm_format); @@ -1404,7 +1465,17 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT); - if (!vulkan_submit_stage_wait(vk_renderer)) { + int wait_sync_file_fd = -1; + if (wait_timeline != NULL) { + wait_sync_file_fd = wlr_drm_syncobj_timeline_export_sync_file(wait_timeline, wait_point); + if (wait_sync_file_fd < 0) { + wlr_log(WLR_ERROR, "Failed to export wait timeline point as sync_file"); + return false; + } + } + + if (!vulkan_submit_stage_wait(vk_renderer, wait_sync_file_fd)) { + close(wait_sync_file_fd); return false; } @@ -2049,10 +2120,10 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( .yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT, .chromaFilter = VK_FILTER_LINEAR, }; - res = vkCreateSamplerYcbcrConversion(renderer->dev->dev, + res = renderer->dev->api.vkCreateSamplerYcbcrConversionKHR(renderer->dev->dev, &conversion_create_info, NULL, &pipeline_layout->ycbcr.conversion); if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateSamplerYcbcrConversion", res); + wlr_vk_error("vkCreateSamplerYcbcrConversionKHR", res); free(pipeline_layout); return NULL; } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 57db97a56..c6365c90b 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -15,7 +15,7 @@ static const struct wlr_texture_impl texture_impl; -bool wlr_texture_is_vk(struct wlr_texture *wlr_texture) { +bool wlr_texture_is_vk(const struct wlr_texture *wlr_texture) { return wlr_texture->impl == &texture_impl; } @@ -238,7 +238,8 @@ static bool vulkan_texture_read_pixels(struct wlr_texture *wlr_texture, void *p = wlr_texture_read_pixel_options_get_data(options); return vulkan_read_pixels(texture->renderer, texture->format->vk, texture->image, - options->format, options->stride, src.width, src.height, src.x, src.y, 0, 0, p); + options->format, options->stride, src.width, src.height, src.x, src.y, 0, 0, p, + options->wait_timeline, options->wait_point); } static uint32_t vulkan_texture_preferred_read_format(struct wlr_texture *wlr_texture) { @@ -653,7 +654,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, }; - vkGetImageMemoryRequirements2(dev, &memri, &memr); + renderer->dev->api.vkGetImageMemoryRequirements2KHR(dev, &memri, &memr); int mem = vulkan_find_mem_type(renderer->dev, 0, memr.memoryRequirements.memoryTypeBits & fdp.memoryTypeBits); if (mem < 0) { @@ -712,7 +713,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, } } - res = vkBindImageMemory2(dev, mem_count, bindi); + res = renderer->dev->api.vkBindImageMemory2KHR(dev, mem_count, bindi); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error_image; diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 0c0445d80..b4a5e0e11 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -81,21 +81,6 @@ static VKAPI_ATTR VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT } struct wlr_vk_instance *vulkan_instance_create(bool debug) { - // we require vulkan 1.1 - PFN_vkEnumerateInstanceVersion pfEnumInstanceVersion = - (PFN_vkEnumerateInstanceVersion) - vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"); - if (!pfEnumInstanceVersion) { - wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available"); - return NULL; - } - - uint32_t ini_version; - if (pfEnumInstanceVersion(&ini_version) != VK_SUCCESS || - ini_version < VK_API_VERSION_1_1) { - wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available"); - return NULL; - } uint32_t avail_extc = 0; VkResult res; @@ -125,7 +110,18 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { } size_t extensions_len = 0; - const char *extensions[1] = {0}; + const char *extensions[8] = {0}; + extensions[extensions_len++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME; + extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME; + + for (size_t i = 0; i < extensions_len; i++) { + if (!check_extension(avail_ext_props, avail_extc, extensions[i])) { + wlr_log(WLR_ERROR, "vulkan: required instance extension %s not found", + extensions[i]); + goto error; + } + } bool debug_utils_found = false; if (debug && check_extension(avail_ext_props, avail_extc, @@ -140,7 +136,7 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pEngineName = "wlroots", .engineVersion = WLR_VERSION_NUM, - .apiVersion = VK_API_VERSION_1_1, + .apiVersion = VK_API_VERSION_1_0, }; VkInstanceCreateInfo instance_info = { @@ -282,20 +278,10 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) for (uint32_t i = 0; i < num_phdevs; ++i) { VkPhysicalDevice phdev = phdevs[i]; - // check whether device supports vulkan 1.1, needed for - // vkGetPhysicalDeviceProperties2 VkPhysicalDeviceProperties phdev_props; vkGetPhysicalDeviceProperties(phdev, &phdev_props); - log_phdev(&phdev_props); - if (phdev_props.apiVersion < VK_API_VERSION_1_1) { - // NOTE: we could additionally check whether the - // VkPhysicalDeviceProperties2KHR extension is supported but - // implementations not supporting 1.1 are unlikely in future - continue; - } - // check for extensions uint32_t avail_extc = 0; res = vkEnumerateDeviceExtensionProperties(phdev, NULL, @@ -474,6 +460,12 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, extensions[extensions_len++] = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME; extensions[extensions_len++] = VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME; // or vulkan 1.2 extensions[extensions_len++] = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME; // or vulkan 1.3 + extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_BIND_MEMORY_2_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_MAINTENANCE_1_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME; // or vulkan 1.1 for (size_t i = 0; i < extensions_len; i++) { if (!check_extension(avail_ext_props, avail_extc, extensions[i])) { @@ -630,6 +622,10 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, load_device_proc(dev, "vkGetSemaphoreCounterValueKHR", &dev->api.vkGetSemaphoreCounterValueKHR); load_device_proc(dev, "vkQueueSubmit2KHR", &dev->api.vkQueueSubmit2KHR); + load_device_proc(dev, "vkBindImageMemory2KHR", &dev->api.vkBindImageMemory2KHR); + load_device_proc(dev, "vkCreateSamplerYcbcrConversionKHR", &dev->api.vkCreateSamplerYcbcrConversionKHR); + load_device_proc(dev, "vkDestroySamplerYcbcrConversionKHR", &dev->api.vkDestroySamplerYcbcrConversionKHR); + load_device_proc(dev, "vkGetImageMemoryRequirements2KHR", &dev->api.vkGetImageMemoryRequirements2KHR); if (has_external_semaphore_fd) { load_device_proc(dev, "vkGetSemaphoreFdKHR", &dev->api.vkGetSemaphoreFdKHR); diff --git a/test/bench_scene.c b/test/bench_scene.c new file mode 100644 index 000000000..6d6b270f0 --- /dev/null +++ b/test/bench_scene.c @@ -0,0 +1,154 @@ +#include +#include +#include + +struct tree_spec { + // Parameters for the tree we'll construct + int depth; + int branching; + int rect_size; + int spread; + + // Stats around the tree we built + int tree_count; + int rect_count; + int max_x; + int max_y; +}; + +static int max(int a, int b) { + return a > b ? a : b; +} + +static double timespec_diff_msec(struct timespec *start, struct timespec *end) { + return (double)(end->tv_sec - start->tv_sec) * 1e3 + + (double)(end->tv_nsec - start->tv_nsec) / 1e6; +} + +static bool build_tree(struct wlr_scene_tree *parent, struct tree_spec *spec, + int depth, int x, int y) { + + if (depth == spec->depth) { + float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + struct wlr_scene_rect *rect = + wlr_scene_rect_create(parent, spec->rect_size, spec->rect_size, color); + if (rect == NULL) { + fprintf(stderr, "wlr_scene_rect_create failed\n"); + return false; + } + wlr_scene_node_set_position(&rect->node, x, y); + spec->max_x = max(spec->max_x, x + spec->rect_size); + spec->max_y = max(spec->max_y, y + spec->rect_size); + spec->rect_count++; + return true; + } + + for (int i = 0; i < spec->branching; i++) { + struct wlr_scene_tree *child = wlr_scene_tree_create(parent); + if (child == NULL) { + fprintf(stderr, "wlr_scene_tree_create failed\n"); + return false; + } + spec->tree_count++; + int offset = i * spec->spread; + wlr_scene_node_set_position(&child->node, offset, offset); + if (!build_tree(child, spec, depth + 1, x + offset, y + offset)) { + return false; + } + } + return true; +} + +static bool bench_create_tree(struct wlr_scene *scene, struct tree_spec *spec) { + struct timespec start, end; + clock_gettime(CLOCK_MONOTONIC, &start); + if (!build_tree(&scene->tree, spec, 0, 0, 0)) { + fprintf(stderr, "build_tree failed\n"); + return false; + } + clock_gettime(CLOCK_MONOTONIC, &end); + + printf("Built tree with %d tree nodes, %d rect nodes\n\n", + spec->tree_count, spec->rect_count); + + double elapsed = timespec_diff_msec(&start, &end); + int nodes = spec->tree_count + spec->rect_count; + printf("create test tree: %d nodes, %.3f ms, %.0f nodes/ms\n", + nodes, elapsed, nodes / elapsed); + return true; +} + +static void bench_scene_node_at(struct wlr_scene *scene, struct tree_spec *spec) { + struct timespec start, end; + int iters = 10000; + int hits = 0; + + clock_gettime(CLOCK_MONOTONIC, &start); + for (int i = 0; i < iters; i++) { + // Spread lookups across the tree extent + double lx = (double)(i * 97 % spec->max_x); + double ly = (double)(i * 53 % spec->max_y); + double nx, ny; + struct wlr_scene_node *node = + wlr_scene_node_at(&scene->tree.node, lx, ly, &nx, &ny); + if (node != NULL) { + hits++; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + + double elapsed = timespec_diff_msec(&start, &end); + int nodes = (spec->tree_count + spec->rect_count) * iters; + printf("wlr_scene_node_at: %d iters, %.3f ms, %.0f nodes/ms (hits: %d/%d)\n", + iters, elapsed, nodes / elapsed, hits, iters); +} + +static void noop_iterator(struct wlr_scene_buffer *buffer, + int sx, int sy, void *user_data) { + (void)buffer; + (void)sx; + (void)sy; + int *cnt = user_data; + (*cnt)++; +} + +static void bench_scene_node_for_each_buffer(struct wlr_scene *scene, struct tree_spec *spec) { + struct timespec start, end; + int iters = 10000; + int hits = 0; + + clock_gettime(CLOCK_MONOTONIC, &start); + for (int i = 0; i < iters; i++) { + wlr_scene_node_for_each_buffer(&scene->tree.node, + noop_iterator, &hits); + } + clock_gettime(CLOCK_MONOTONIC, &end); + + double elapsed = timespec_diff_msec(&start, &end); + int nodes = (spec->tree_count + spec->rect_count) * iters; + printf("wlr_scene_node_for_each_buffer: %d iters, %.3f ms, %.0f nodes/ms (hits: %d/%d)\n", + iters, elapsed, nodes / elapsed, hits, iters); +} + +int main(void) { + struct wlr_scene *scene = wlr_scene_create(); + if (scene == NULL) { + fprintf(stderr, "wlr_scene_create failed\n"); + return 99; + } + + struct tree_spec spec = { + .depth = 5, + .branching = 5, + .rect_size = 10, + .spread = 100, + }; + if (!bench_create_tree(scene, &spec)) { + return 99; + } + bench_scene_node_at(scene, &spec); + bench_scene_node_for_each_buffer(scene, &spec); + + wlr_scene_node_destroy(&scene->tree.node); + return 0; +} diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 000000000..f51b2c02c --- /dev/null +++ b/test/meson.build @@ -0,0 +1,10 @@ +test( + 'box', + executable('test-box', 'test_box.c', dependencies: wlroots), +) + +benchmark( + 'scene', + executable('bench-scene', 'bench_scene.c', dependencies: wlroots), + timeout: 30, +) diff --git a/test/test_box.c b/test/test_box.c new file mode 100644 index 000000000..fcd95a8e2 --- /dev/null +++ b/test/test_box.c @@ -0,0 +1,149 @@ +#include +#include +#include + +static void test_box_empty(void) { + // NULL is empty + assert(wlr_box_empty(NULL)); + + // Zero width/height + struct wlr_box box = { .x = 0, .y = 0, .width = 0, .height = 10 }; + assert(wlr_box_empty(&box)); + box = (struct wlr_box){ .x = 0, .y = 0, .width = 10, .height = 0 }; + assert(wlr_box_empty(&box)); + + // Negative width/height + box = (struct wlr_box){ .x = 0, .y = 0, .width = -1, .height = 10 }; + assert(wlr_box_empty(&box)); + box = (struct wlr_box){ .x = 0, .y = 0, .width = 10, .height = -1 }; + assert(wlr_box_empty(&box)); + + // Valid box + box = (struct wlr_box){ .x = 0, .y = 0, .width = 10, .height = 10 }; + assert(!wlr_box_empty(&box)); +} + +static void test_box_intersection(void) { + struct wlr_box dest; + + // Overlapping + struct wlr_box a = { .x = 0, .y = 0, .width = 100, .height = 100 }; + struct wlr_box b = { .x = 50, .y = 50, .width = 100, .height = 100 }; + assert(wlr_box_intersection(&dest, &a, &b)); + assert(dest.x == 50 && dest.y == 50 && + dest.width == 50 && dest.height == 50); + + // Non-overlapping + b = (struct wlr_box){ .x = 200, .y = 200, .width = 50, .height = 50 }; + assert(!wlr_box_intersection(&dest, &a, &b)); + assert(dest.width == 0 && dest.height == 0); + + // Touching edges + b = (struct wlr_box){ .x = 100, .y = 0, .width = 50, .height = 50 }; + assert(!wlr_box_intersection(&dest, &a, &b)); + + // Self-intersection + assert(wlr_box_intersection(&dest, &a, &a)); + assert(dest.x == a.x && dest.y == a.y && + dest.width == a.width && dest.height == a.height); + + // Empty input + struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 }; + assert(!wlr_box_intersection(&dest, &a, &empty)); + + // NULL input + assert(!wlr_box_intersection(&dest, &a, NULL)); + assert(!wlr_box_intersection(&dest, NULL, &a)); +} + +static void test_box_intersects_box(void) { + // Overlapping + struct wlr_box a = { .x = 0, .y = 0, .width = 100, .height = 100 }; + struct wlr_box b = { .x = 50, .y = 50, .width = 100, .height = 100 }; + assert(wlr_box_intersects(&a, &b)); + + // Non-overlapping + b = (struct wlr_box){ .x = 200, .y = 200, .width = 50, .height = 50 }; + assert(!wlr_box_intersects(&a, &b)); + + // Touching edges + b = (struct wlr_box){ .x = 100, .y = 0, .width = 50, .height = 50 }; + assert(!wlr_box_intersects(&a, &b)); + + // Self-intersection + assert(wlr_box_intersects(&a, &a)); + + // Empty input + struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 }; + assert(!wlr_box_intersects(&a, &empty)); + + // NULL input + assert(!wlr_box_intersects(&a, NULL)); + assert(!wlr_box_intersects(NULL, &a)); +} + +static void test_box_contains_point(void) { + struct wlr_box box = { .x = 10, .y = 20, .width = 100, .height = 50 }; + + // Interior point + assert(wlr_box_contains_point(&box, 50, 40)); + + // Inclusive lower bound + assert(wlr_box_contains_point(&box, 10, 20)); + + // Exclusive upper bound + assert(!wlr_box_contains_point(&box, 110, 70)); + assert(!wlr_box_contains_point(&box, 110, 40)); + assert(!wlr_box_contains_point(&box, 50, 70)); + + // Outside + assert(!wlr_box_contains_point(&box, 5, 40)); + assert(!wlr_box_contains_point(&box, 50, 15)); + + // Empty box + struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 }; + assert(!wlr_box_contains_point(&empty, 0, 0)); + + // NULL + assert(!wlr_box_contains_point(NULL, 0, 0)); +} + +static void test_box_contains_box(void) { + struct wlr_box outer = { .x = 0, .y = 0, .width = 100, .height = 100 }; + + // Fully contained + struct wlr_box inner = { .x = 10, .y = 10, .width = 50, .height = 50 }; + assert(wlr_box_contains_box(&outer, &inner)); + + // Self-containment + assert(wlr_box_contains_box(&outer, &outer)); + + // Partial overlap — not contained + struct wlr_box partial = { .x = 50, .y = 50, .width = 100, .height = 100 }; + assert(!wlr_box_contains_box(&outer, &partial)); + + // Empty inner + struct wlr_box empty = { .x = 0, .y = 0, .width = 0, .height = 0 }; + assert(!wlr_box_contains_box(&outer, &empty)); + + // Empty outer + assert(!wlr_box_contains_box(&empty, &inner)); + + // NULL + assert(!wlr_box_contains_box(&outer, NULL)); + assert(!wlr_box_contains_box(NULL, &outer)); +} + +int main(void) { +#ifdef NDEBUG + fprintf(stderr, "NDEBUG must be disabled for tests\n"); + return 1; +#endif + + test_box_empty(); + test_box_intersection(); + test_box_intersects_box(); + test_box_contains_point(); + test_box_contains_box(); + return 0; +} diff --git a/tinywl/Makefile b/tinywl/Makefile index 2efe4a436..0f1c4cb13 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -1,6 +1,6 @@ PKG_CONFIG?=pkg-config -PKGS="wlroots-0.20" wayland-server xkbcommon +PKGS="wlroots-0.21" wayland-server xkbcommon CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS) CFLAGS+=$(CFLAGS_PKG_CONFIG) LIBS!=$(PKG_CONFIG) --libs $(PKGS) diff --git a/types/output/cursor.c b/types/output/cursor.c index 5e93b0b2e..4a823dab1 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -159,9 +159,8 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { struct wlr_box cursor_box; output_cursor_get_box(cursor, &cursor_box); - struct wlr_box intersection; cursor->visible = - wlr_box_intersection(&intersection, &output_box, &cursor_box); + wlr_box_intersects(&output_box, &cursor_box); } static bool output_pick_cursor_format(struct wlr_output *output, diff --git a/types/output/output.c b/types/output/output.c index 3715950ce..46da1f425 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -233,6 +233,11 @@ static void output_apply_state(struct wlr_output *output, output->transform = state->transform; } + if (state->committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) { + output->color_encoding = state->color_encoding; + output->color_range = state->color_range; + } + if (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { if (state->image_description != NULL) { output->image_description_value = *state->image_description; @@ -580,6 +585,11 @@ static uint32_t output_compare_state(struct wlr_output *output, output->color_transform == state->color_transform) { fields |= WLR_OUTPUT_STATE_COLOR_TRANSFORM; } + if ((state->committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) && + output->color_encoding == state->color_encoding && + output->color_range == state->color_range) { + fields |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION; + } return fields; } @@ -615,7 +625,7 @@ static bool output_basic_test(struct wlr_output *output, }; struct wlr_box dst_box; output_state_get_buffer_dst_box(state, &dst_box); - if (!wlr_box_intersection(&output_box, &output_box, &dst_box)) { + if (!wlr_box_intersects(&output_box, &dst_box)) { wlr_log(WLR_ERROR, "Primary buffer is entirely off-screen or 0-sized"); return false; } @@ -632,6 +642,10 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Tried to set signal timeline without a buffer"); return false; } + if (state->committed & WLR_OUTPUT_STATE_COLOR_REPRESENTATION) { + wlr_log(WLR_DEBUG, "Tried to set color representation without a buffer"); + return false; + } } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { diff --git a/types/output/state.c b/types/output/state.c index f2c655e21..5f44c18e8 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -141,6 +141,14 @@ bool wlr_output_state_set_image_description(struct wlr_output_state *state, return true; } +void wlr_output_state_set_color_encoding_and_range( + struct wlr_output_state *state, + enum wlr_color_encoding encoding, enum wlr_color_range range) { + state->committed |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION; + state->color_encoding = encoding; + state->color_range = range; +} + bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 54c09bbfe..7231422e8 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -113,24 +113,11 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - uint64_t active = scene_buffer->active_outputs; - if (active) { - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - if (active & (1ull << scene_output->index)) { - wl_signal_emit_mutable(&scene_buffer->events.output_leave, - scene_output); - } - } - } - scene_buffer_set_buffer(scene_buffer, NULL); scene_buffer_set_texture(scene_buffer, NULL); pixman_region32_fini(&scene_buffer->opaque_region); wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); - assert(wl_list_empty(&scene_buffer->events.output_leave.listener_list)); - assert(wl_list_empty(&scene_buffer->events.output_enter.listener_list)); assert(wl_list_empty(&scene_buffer->events.outputs_update.listener_list)); assert(wl_list_empty(&scene_buffer->events.output_sample.listener_list)); assert(wl_list_empty(&scene_buffer->events.frame_done.listener_list)); @@ -238,7 +225,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); - if (wlr_box_intersection(&node_box, &node_box, box) && + if (wlr_box_intersects(&node_box, box) && iterator(node, lx, ly, user_data)) { return true; } @@ -481,28 +468,12 @@ static void update_node_update_outputs(struct wlr_scene_node *node, (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; } - uint64_t old_active = scene_buffer->active_outputs; - scene_buffer->active_outputs = active_outputs; - - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, outputs, link) { - uint64_t mask = 1ull << scene_output->index; - bool intersects = active_outputs & mask; - bool intersects_before = old_active & mask; - - if (intersects && !intersects_before) { - wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output); - } else if (!intersects && intersects_before) { - wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output); - } - } - // if there are active outputs on this node, we should always have a primary // output - assert(!scene_buffer->active_outputs || scene_buffer->primary_output); + assert(!active_outputs || scene_buffer->primary_output); // Skip output update event if nothing was updated - if (old_active == active_outputs && + if (scene_buffer->active_outputs == active_outputs && (!force || ((1ull << force->index) & ~active_outputs)) && old_primary_output == scene_buffer->primary_output) { return; @@ -515,6 +486,7 @@ static void update_node_update_outputs(struct wlr_scene_node *node, }; size_t i = 0; + struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, outputs, link) { if (~active_outputs & (1ull << scene_output->index)) { continue; @@ -524,6 +496,7 @@ static void update_node_update_outputs(struct wlr_scene_node *node, outputs_array[i++] = scene_output; } + scene_buffer->active_outputs = active_outputs; wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); } @@ -869,8 +842,6 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); wl_signal_init(&scene_buffer->events.outputs_update); - wl_signal_init(&scene_buffer->events.output_enter); - wl_signal_init(&scene_buffer->events.output_leave); wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); @@ -2098,15 +2069,6 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( return SCANOUT_INELIGIBLE; } - bool is_color_repr_none = buffer->color_encoding == WLR_COLOR_ENCODING_NONE && - buffer->color_range == WLR_COLOR_RANGE_NONE; - bool is_color_repr_identity_full = buffer->color_encoding == WLR_COLOR_ENCODING_IDENTITY && - buffer->color_range == WLR_COLOR_RANGE_FULL; - - if (!(is_color_repr_none || is_color_repr_identity_full)) { - return SCANOUT_INELIGIBLE; - } - // We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled // on a frame-by-frame basis, we wait for a few frames to send the new format recommendations. // Maybe we should only send feedback in this case if tests fail. @@ -2149,11 +2111,22 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); } - if (scene_output->out_timeline) { + if (scene_output->out_timeline) { scene_output->out_point++; wlr_output_state_set_signal_timeline(&pending, scene_output->out_timeline, scene_output->out_point); } + if (buffer->color_encoding == WLR_COLOR_ENCODING_IDENTITY && + buffer->color_range == WLR_COLOR_RANGE_FULL) { + // IDENTITY+FULL (used for RGB formats) is equivalent to no color + // representation being set at all. + wlr_output_state_set_color_encoding_and_range(&pending, + WLR_COLOR_ENCODING_NONE, WLR_COLOR_RANGE_NONE); + } else { + wlr_output_state_set_color_encoding_and_range(&pending, + buffer->color_encoding, buffer->color_range); + } + if (!wlr_output_test_state(scene_output->output, &pending)) { wlr_output_state_finish(&pending); return SCANOUT_CANDIDATE; @@ -2696,8 +2669,7 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); - struct wlr_box intersection; - if (wlr_box_intersection(&intersection, output_box, &node_box)) { + if (wlr_box_intersects(output_box, &node_box)) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); user_iterator(scene_buffer, lx, ly, user_data); diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index ef2751179..7226809e7 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -260,14 +260,12 @@ bool wlr_output_layout_contains_point(struct wlr_output_layout *layout, bool wlr_output_layout_intersects(struct wlr_output_layout *layout, struct wlr_output *reference, const struct wlr_box *target_lbox) { - struct wlr_box out_box; - if (reference == NULL) { struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); - if (wlr_box_intersection(&out_box, &output_box, target_lbox)) { + if (wlr_box_intersects(&output_box, target_lbox)) { return true; } } @@ -281,7 +279,7 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout, struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); - return wlr_box_intersection(&out_box, &output_box, target_lbox); + return wlr_box_intersects(&output_box, target_lbox); } } diff --git a/util/box.c b/util/box.c index 489c5ad2e..62d405488 100644 --- a/util/box.c +++ b/util/box.c @@ -102,6 +102,15 @@ bool wlr_box_contains_box(const struct wlr_box *bigger, const struct wlr_box *sm smaller->y + smaller->height <= bigger->y + bigger->height; } +bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b) { + if (wlr_box_empty(a) || wlr_box_empty(b)) { + return false; + } + + return a->x < b->x + b->width && b->x < a->x + a->width && + a->y < b->y + b->height && b->y < a->y + a->height; +} + void wlr_box_transform(struct wlr_box *dest, const struct wlr_box *box, enum wl_output_transform transform, int width, int height) { struct wlr_box src = {0}; diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index 85d7775cd..4fa851b2f 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -249,7 +249,7 @@ struct x11_data_source { static const struct wlr_data_source_impl data_source_impl; bool data_source_is_xwayland( - struct wlr_data_source *wlr_source) { + const struct wlr_data_source *wlr_source) { return wlr_source->impl == &data_source_impl; } @@ -292,7 +292,7 @@ static const struct wlr_primary_selection_source_impl primary_selection_source_impl; bool primary_selection_source_is_xwayland( - struct wlr_primary_selection_source *wlr_source) { + const struct wlr_primary_selection_source *wlr_source) { return wlr_source->impl == &primary_selection_source_impl; }