From 33a27b055cf5ac6bd610864afd51ef3e33cd98b1 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 13 Mar 2026 10:49:41 +0800 Subject: [PATCH] render: introduce wlr_render_rect_pass Split rectangle rendering out of wlr_render_pass and add a dedicated wlr_render_rect_pass interface. Remove the add_rect hook from wlr_render_pass_impl and implement rectangle rendering separately in the pixman, GLES2 and Vulkan renderers. --- include/render/gles2.h | 8 ++++ include/render/pixman.h | 8 ++++ include/render/vulkan.h | 8 ++++ include/wlr/render/interface.h | 29 +++++++++++-- include/wlr/render/wlr_renderer.h | 4 ++ include/wlr/types/wlr_output.h | 2 + render/gles2/pass.c | 42 +++++++++++++++++- render/pass.c | 72 ++++++++++++++++++++++++++++++- render/pixman/pass.c | 44 ++++++++++++++++++- render/vulkan/pass.c | 42 +++++++++++++++++- render/wlr_renderer.c | 1 + types/output/output.c | 2 + types/output/render.c | 5 +++ 13 files changed, 258 insertions(+), 9 deletions(-) diff --git a/include/render/gles2.h b/include/render/gles2.h index 6b852dcb7..d1a8b0aba 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -173,4 +173,12 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer, struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point); +struct wlr_gles2_render_rect_pass { + struct wlr_render_rect_pass base; +}; + +bool wlr_render_rect_pass_is_gles2(const struct wlr_render_rect_pass *rect_pass); +struct wlr_gles2_render_rect_pass *wlr_gles2_render_rect_pass_from_pass( + struct wlr_render_rect_pass *rect_pass); + #endif diff --git a/include/render/pixman.h b/include/render/pixman.h index 098421447..6d58ed0b6 100644 --- a/include/render/pixman.h +++ b/include/render/pixman.h @@ -61,4 +61,12 @@ bool begin_pixman_data_ptr_access(struct wlr_buffer *buffer, pixman_image_t **im struct wlr_pixman_render_pass *begin_pixman_render_pass( struct wlr_pixman_buffer *buffer); +struct wlr_pixman_render_rect_pass { + struct wlr_render_rect_pass base; +}; + +bool wlr_render_rect_pass_is_pixman(const struct wlr_render_rect_pass *rect_pass); +struct wlr_pixman_render_rect_pass *wlr_pixman_render_rect_pass_from_pass( + struct wlr_render_rect_pass *rect_pass); + #endif diff --git a/include/render/vulkan.h b/include/render/vulkan.h index c5d571ef7..b4e1a28bd 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -592,4 +592,12 @@ void vulkan_change_layout(VkCommandBuffer cb, VkImage img, #endif +struct wlr_vk_render_rect_pass { + struct wlr_render_rect_pass base; +}; + +bool wlr_render_rect_pass_is_vk(const struct wlr_render_rect_pass *rect_pass); +struct wlr_vk_render_rect_pass *wlr_vk_render_rect_pass_from_pass( + struct wlr_render_rect_pass *rect_pass); + #endif // RENDER_VULKAN_H diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 89f6de970..45b15f0e0 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -50,6 +50,7 @@ void wlr_texture_init(struct wlr_texture *texture, struct wlr_renderer *rendener struct wlr_render_pass { const struct wlr_render_pass_impl *impl; + struct wlr_renderer *renderer; }; void wlr_render_pass_init(struct wlr_render_pass *pass, @@ -59,9 +60,6 @@ struct wlr_render_pass_impl { bool (*submit)(struct wlr_render_pass *pass); void (*add_texture)(struct wlr_render_pass *pass, const struct wlr_render_texture_options *options); - /* Implementers are also guaranteed that options->box is nonempty */ - void (*add_rect)(struct wlr_render_pass *pass, - const struct wlr_render_rect_options *options); }; struct wlr_render_timer { @@ -87,4 +85,29 @@ void wlr_texture_read_pixels_options_get_src_box( void *wlr_texture_read_pixel_options_get_data( const struct wlr_texture_read_pixels_options *options); +struct wlr_render_rect_pass; + +struct wlr_render_rect_pass_impl { + void (*destroy)(struct wlr_render_rect_pass *pass); + void (*render)(struct wlr_render_pass *pass, + const struct wlr_render_rect_options *options); +}; + +struct wlr_render_rect_pass { + const struct wlr_render_rect_pass_impl *impl; + struct { + struct wl_signal destroy; + } events; +}; + +void wlr_render_rect_pass_init(struct wlr_render_rect_pass *pass, + const struct wlr_render_rect_pass_impl *impl); +void wlr_render_rect_pass_destroy(struct wlr_render_rect_pass *pass); + +struct wlr_render_rect_pass *get_or_create_render_rect_pass( + struct wlr_renderer *renderer); +struct wlr_render_rect_pass *wlr_pixman_render_rect_pass_create(void); +struct wlr_render_rect_pass *wlr_gles2_render_rect_pass_create(void); +struct wlr_render_rect_pass *wlr_vk_render_rect_pass_create(void); + #endif diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 902d7564d..f1d761e5a 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -26,6 +26,10 @@ struct wlr_fbox; * A renderer for basic 2D operations. */ struct wlr_renderer { + struct wlr_render_rect_pass *rect_pass; + + void *data; + // Capabilities required for the buffer used as a render target (bitmask of // enum wlr_buffer_cap) uint32_t render_buffer_caps; diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c8e44b0e6..d8a8e9965 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -250,6 +250,8 @@ struct wlr_output { struct wl_signal description; struct wl_signal request_state; // struct wlr_output_event_request_state struct wl_signal destroy; + // Emitted when the output's rendering subsystem is initialized or reinitialized + struct wl_signal render_inited; } events; struct wl_event_source *idle_frame; diff --git a/render/gles2/pass.c b/render/gles2/pass.c index a70ea1320..e207743ce 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -287,7 +287,6 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, static const struct wlr_render_pass_impl render_pass_impl = { .submit = render_pass_submit, .add_texture = render_pass_add_texture, - .add_rect = render_pass_add_rect, }; static const char *reset_status_str(GLenum status) { @@ -330,6 +329,7 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b wlr_render_pass_init(&pass->base, &render_pass_impl); wlr_buffer_lock(wlr_buffer); + pass->base.renderer = &renderer->wlr_renderer; pass->buffer = buffer; pass->timer = timer; pass->prev_ctx = *prev_ctx; @@ -351,3 +351,43 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b return pass; } + +static void render_rect_pass_destroy(struct wlr_render_rect_pass *pass) { + struct wlr_gles2_render_rect_pass *gles2_pass = + wlr_gles2_render_rect_pass_from_pass(pass); + free(gles2_pass); +} + +static const struct wlr_render_rect_pass_impl render_rect_pass_impl = { + .destroy = render_rect_pass_destroy, + .render = render_pass_add_rect, +}; + +struct wlr_render_rect_pass *wlr_gles2_render_rect_pass_create(void) { + struct wlr_gles2_render_rect_pass *pass = calloc(1, sizeof(*pass)); + if (pass == NULL) { + wlr_log_errno(WLR_ERROR, "failed to allocate wlr_gles2_render_rect_pass"); + return NULL; + } + + wlr_render_rect_pass_init(&pass->base, &render_rect_pass_impl); + + return &pass->base; +} + +bool wlr_render_rect_pass_is_gles2(const struct wlr_render_rect_pass *rect_pass) { + return rect_pass != NULL && rect_pass->impl == &render_rect_pass_impl; +} + +struct wlr_gles2_render_rect_pass *wlr_gles2_render_rect_pass_from_pass( + struct wlr_render_rect_pass *rect_pass) { + if (!wlr_render_rect_pass_is_gles2(rect_pass)) { + return NULL; + } + + struct wlr_gles2_render_rect_pass *pass = + wl_container_of(rect_pass, pass, base); + + return pass; +} + diff --git a/render/pass.c b/render/pass.c index 23bdf96dd..ef51c3b66 100644 --- a/render/pass.c +++ b/render/pass.c @@ -1,10 +1,23 @@ #include #include +#include #include +#include +#include + +#include + +#if WLR_HAS_GLES2_RENDERER +#include +#endif + +#if WLR_HAS_VULKAN_RENDERER +#include +#endif void wlr_render_pass_init(struct wlr_render_pass *render_pass, const struct wlr_render_pass_impl *impl) { - assert(impl->submit && impl->add_texture && impl->add_rect); + assert(impl->submit && impl->add_texture); *render_pass = (struct wlr_render_pass){ .impl = impl, }; @@ -31,7 +44,7 @@ void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass, void wlr_render_pass_add_rect(struct wlr_render_pass *render_pass, const struct wlr_render_rect_options *options) { assert(options->box.width >= 0 && options->box.height >= 0); - render_pass->impl->add_rect(render_pass, options); + render_pass->renderer->rect_pass->impl->render(render_pass, options); } void wlr_render_texture_options_get_src_box(const struct wlr_render_texture_options *options, @@ -74,3 +87,58 @@ void wlr_render_rect_options_get_box(const struct wlr_render_rect_options *optio *box = options->box; } + +void wlr_render_rect_pass_init(struct wlr_render_rect_pass *render_pass, + const struct wlr_render_rect_pass_impl *impl) { + assert(impl->render); + *render_pass = (struct wlr_render_rect_pass){ + .impl = impl, + }; + wl_signal_init(&render_pass->events.destroy); +} + +void wlr_render_rect_pass_destroy(struct wlr_render_rect_pass *render_pass) { + if (render_pass == NULL) { + return; + } + + wl_signal_emit_mutable(&render_pass->events.destroy, NULL); + assert(wl_list_empty(&render_pass->events.destroy.listener_list)); + + if (render_pass->impl->destroy != NULL) { + render_pass->impl->destroy(render_pass); + } else { + free(render_pass); + } +} + +struct wlr_render_rect_pass *get_or_create_render_rect_pass( + struct wlr_renderer *renderer) { + if (renderer == NULL) { + return NULL; + } + + if (renderer->rect_pass == NULL) { + struct wlr_render_rect_pass *pass = NULL; + if (wlr_renderer_is_pixman(renderer)) { + pass = wlr_pixman_render_rect_pass_create(); + } + +#if WLR_HAS_GLES2_RENDERER + else if (wlr_renderer_is_gles2(renderer)) { + pass = wlr_gles2_render_rect_pass_create(); + } +#endif + +#if WLR_HAS_VULKAN_RENDERER + else if (wlr_renderer_is_vk(renderer)) { + pass = wlr_vk_render_rect_pass_create(); + } +#endif + + renderer->rect_pass = pass; + return pass; + } else { + return renderer->rect_pass; + } +} diff --git a/render/pixman/pass.c b/render/pixman/pass.c index d3ee17dca..f5c55a577 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -1,5 +1,8 @@ #include #include + +#include + #include "render/pixman.h" static const struct wlr_render_pass_impl render_pass_impl; @@ -228,9 +231,47 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, static const struct wlr_render_pass_impl render_pass_impl = { .submit = render_pass_submit, .add_texture = render_pass_add_texture, - .add_rect = render_pass_add_rect, }; +static void render_rect_pass_destroy(struct wlr_render_rect_pass *pass) { + struct wlr_pixman_render_rect_pass *pixman_pass = + wlr_pixman_render_rect_pass_from_pass(pass); + free(pixman_pass); +} + +static const struct wlr_render_rect_pass_impl render_rect_pass_impl = { + .destroy = render_rect_pass_destroy, + .render = render_pass_add_rect, +}; + +struct wlr_render_rect_pass *wlr_pixman_render_rect_pass_create(void) { + struct wlr_pixman_render_rect_pass *pass = malloc(sizeof(*pass)); + if (pass == NULL) { + wlr_log_errno(WLR_ERROR, "failed to allocate wlr_pixman_render_rect_pass"); + return NULL; + } + + wlr_render_rect_pass_init(&pass->base, &render_rect_pass_impl); + + return &pass->base; +} + +bool wlr_render_rect_pass_is_pixman(const struct wlr_render_rect_pass *rect_pass) { + return rect_pass->impl == &render_rect_pass_impl; +} + +struct wlr_pixman_render_rect_pass *wlr_pixman_render_rect_pass_from_pass( + struct wlr_render_rect_pass *rect_pass) { + if (!wlr_render_rect_pass_is_pixman(rect_pass)) { + return NULL; + } + + struct wlr_pixman_render_rect_pass *pass = + wl_container_of(rect_pass, pass, base); + + return pass; +} + struct wlr_pixman_render_pass *begin_pixman_render_pass( struct wlr_pixman_buffer *buffer) { struct wlr_pixman_render_pass *pass = calloc(1, sizeof(*pass)); @@ -247,6 +288,7 @@ struct wlr_pixman_render_pass *begin_pixman_render_pass( } wlr_buffer_lock(buffer->buffer); + pass->base.renderer = &buffer->renderer->wlr_renderer; pass->buffer = buffer; return pass; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 503e37c07..6cd06f343 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -983,11 +983,9 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, static const struct wlr_render_pass_impl render_pass_impl = { .submit = render_pass_submit, - .add_rect = render_pass_add_rect, .add_texture = render_pass_add_texture, }; - void vk_color_transform_destroy(struct wlr_addon *addon) { struct wlr_vk_renderer *renderer = (struct wlr_vk_renderer *)addon->owner; struct wlr_vk_color_transform *transform = wl_container_of(addon, transform, addon); @@ -1289,6 +1287,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend wlr_render_pass_init(&pass->base, &render_pass_impl); pass->renderer = renderer; + pass->base.renderer = &renderer->wlr_renderer; pass->two_pass = using_two_pass_pathway; if (options != NULL && options->color_transform != NULL) { pass->color_transform = wlr_color_transform_ref(options->color_transform); @@ -1355,3 +1354,42 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend pass->command_buffer = cb; return pass; } + +static void render_rect_pass_destroy(struct wlr_render_rect_pass *pass) { + struct wlr_vk_render_rect_pass *vk_pass = + wlr_vk_render_rect_pass_from_pass(pass); + free(vk_pass); +} + +static const struct wlr_render_rect_pass_impl render_rect_pass_impl = { + .destroy = render_rect_pass_destroy, + .render = render_pass_add_rect, +}; + +struct wlr_render_rect_pass *wlr_vk_render_rect_pass_create(void) { + struct wlr_vk_render_rect_pass *pass = calloc(1, sizeof(*pass)); + if (pass == NULL) { + wlr_log_errno(WLR_ERROR, "failed to allocate wlr_vk_render_rect_pass"); + return NULL; + } + + wlr_render_rect_pass_init(&pass->base, &render_rect_pass_impl); + + return &pass->base; +} + +bool wlr_render_rect_pass_is_vk(const struct wlr_render_rect_pass *rect_pass) { + return rect_pass->impl == &render_rect_pass_impl; +} + +struct wlr_vk_render_rect_pass *wlr_vk_render_rect_pass_from_pass( + struct wlr_render_rect_pass *rect_pass) { + if (!wlr_render_rect_pass_is_vk(rect_pass)) { + return NULL; + } + + struct wlr_vk_render_rect_pass *pass = + wl_container_of(rect_pass, pass, base); + + return pass; +} diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index e65314ccc..493add6b3 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -53,6 +53,7 @@ void wlr_renderer_destroy(struct wlr_renderer *r) { assert(wl_list_empty(&r->events.destroy.listener_list)); assert(wl_list_empty(&r->events.lost.listener_list)); + wlr_render_rect_pass_destroy(r->rect_pass); if (r->impl && r->impl->destroy) { r->impl->destroy(r); } else { diff --git a/types/output/output.c b/types/output/output.c index 46da1f425..80fc0680b 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -377,6 +377,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.description); wl_signal_init(&output->events.request_state); wl_signal_init(&output->events.destroy); + wl_signal_init(&output->events.render_inited); output->software_cursor_locks = env_parse_bool("WLR_NO_HARDWARE_CURSORS"); if (output->software_cursor_locks) { @@ -407,6 +408,7 @@ void wlr_output_finish(struct wlr_output *output) { assert(wl_list_empty(&output->events.description.listener_list)); assert(wl_list_empty(&output->events.request_state.listener_list)); assert(wl_list_empty(&output->events.destroy.listener_list)); + assert(wl_list_empty(&output->events.render_inited.listener_list)); wlr_output_destroy_global(output); diff --git a/types/output/render.c b/types/output/render.c index 155da5cf8..bbba26718 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include "render/drm_format_set.h" @@ -35,6 +36,10 @@ bool wlr_output_init_render(struct wlr_output *output, output->allocator = allocator; output->renderer = renderer; + get_or_create_render_rect_pass(renderer); + + wl_signal_emit_mutable(&output->events.render_inited, output); + return true; }