diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index 89f6de970..d68766feb 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -62,6 +62,8 @@ struct wlr_render_pass_impl { /* Implementers are also guaranteed that options->box is nonempty */ void (*add_rect)(struct wlr_render_pass *pass, const struct wlr_render_rect_options *options); + void (*clear)(struct wlr_render_pass *pass, + const struct wlr_render_clear_options *options); }; struct wlr_render_timer { diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index 61ab77132..be075e66d 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -157,4 +157,20 @@ struct wlr_render_rect_options { void wlr_render_pass_add_rect(struct wlr_render_pass *render_pass, const struct wlr_render_rect_options *options); +struct wlr_render_clear_options { + /* Clear color */ + struct wlr_render_color color; + /* Clip region, leave NULL to disable clipping */ + const pixman_region32_t *clip; +}; + +/** + * Clear the render target with a solid color. + * + * This is optimized for GPU hardware (e.g., glClear + glScissor) and is more + * efficient than drawing a rectangle. + */ +void wlr_render_pass_clear(struct wlr_render_pass *render_pass, + const struct wlr_render_clear_options *options); + #endif diff --git a/render/gles2/pass.c b/render/gles2/pass.c index b10ac047d..ae4398f31 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -275,10 +275,41 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, pop_gles2_debug(renderer); } +static void render_pass_clear(struct wlr_render_pass *wlr_pass, + const struct wlr_render_clear_options *options) { + struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass); + struct wlr_gles2_renderer *renderer = pass->buffer->renderer; + + push_gles2_debug(renderer); + + const struct wlr_render_color *color = &options->color; + glClearColor(color->r, color->g, color->b, color->a); + + if (options->clip) { + int rects_len; + const pixman_box32_t *rects = pixman_region32_rectangles(options->clip, &rects_len); + + glEnable(GL_SCISSOR_TEST); + for (int i = 0; i < rects_len; i++) { + const pixman_box32_t *rect = &rects[i]; + int height = pass->buffer->buffer->height; + glScissor(rect->x1, height - rect->y2, + rect->x2 - rect->x1, rect->y2 - rect->y1); + glClear(GL_COLOR_BUFFER_BIT); + } + glDisable(GL_SCISSOR_TEST); + } else { + glClear(GL_COLOR_BUFFER_BIT); + } + + pop_gles2_debug(renderer); +} + 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, + .clear = render_pass_clear, }; static const char *reset_status_str(GLenum status) { diff --git a/render/pass.c b/render/pass.c index 23bdf96dd..7daaec243 100644 --- a/render/pass.c +++ b/render/pass.c @@ -4,7 +4,7 @@ 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 && impl->add_rect && impl->clear); *render_pass = (struct wlr_render_pass){ .impl = impl, }; @@ -34,6 +34,11 @@ void wlr_render_pass_add_rect(struct wlr_render_pass *render_pass, render_pass->impl->add_rect(render_pass, options); } +void wlr_render_pass_clear(struct wlr_render_pass *render_pass, + const struct wlr_render_clear_options *options) { + render_pass->impl->clear(render_pass, options); +} + void wlr_render_texture_options_get_src_box(const struct wlr_render_texture_options *options, struct wlr_fbox *box) { *box = options->src_box; diff --git a/render/pixman/pass.c b/render/pixman/pass.c index 4ae742cab..0d04c64d0 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -225,10 +225,23 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, pixman_image_unref(fill); } +static void render_pass_clear(struct wlr_render_pass *wlr_pass, + const struct wlr_render_clear_options *options) { + struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass); + + render_pass_add_rect(wlr_pass, &(struct wlr_render_rect_options){ + .box = { .width = pass->buffer->buffer->width, .height = pass->buffer->buffer->height }, + .color = options->color, + .clip = options->clip, + .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + }); +} + 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, + .clear = render_pass_clear, }; struct wlr_pixman_render_pass *begin_pixman_render_pass( diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 31f5116bd..691bdb84b 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -943,13 +943,60 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } } +static void render_pass_clear(struct wlr_render_pass *wlr_pass, + const struct wlr_render_clear_options *options) { + struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); + VkCommandBuffer cb = pass->command_buffer->vk; + + float linear_color[] = { + color_to_linear_premult(options->color.r, options->color.a), + color_to_linear_premult(options->color.g, options->color.a), + color_to_linear_premult(options->color.b, options->color.a), + options->color.a, + }; + + VkClearAttachment clear_att = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = 0, + .clearValue.color.float32 = { + linear_color[0], + linear_color[1], + linear_color[2], + linear_color[3], + }, + }; + + pixman_region32_t clip; + get_clip_region(pass, options->clip, &clip); + + int clip_rects_len; + const pixman_box32_t *clip_rects = pixman_region32_rectangles(&clip, &clip_rects_len); + + VkClearRect clear_rect = { + .layerCount = 1, + }; + for (int i = 0; i < clip_rects_len; i++) { + convert_pixman_box_to_vk_rect(&clip_rects[i], &clear_rect.rect); + vkCmdClearAttachments(cb, 1, &clear_att, 1, &clear_rect); + struct wlr_box box = { + .x = clip_rects[i].x1, + .y = clip_rects[i].y1, + .width = clip_rects[i].x2 - clip_rects[i].x1, + .height = clip_rects[i].y2 - clip_rects[i].y1, + }; + render_pass_mark_box_updated(pass, &box); + } + + pixman_region32_fini(&clip); +} + 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, + .clear = render_pass_clear, }; - 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); diff --git a/types/output/cursor.c b/types/output/cursor.c index 11f442cdf..098eaa936 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -268,9 +268,8 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) enum wl_output_transform transform = wlr_output_transform_invert(cursor->transform); transform = wlr_output_transform_compose(transform, output->transform); - wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ - .box = { .width = buffer->width, .height = buffer->height }, - .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + wlr_render_pass_clear(pass, &(struct wlr_render_clear_options){ + .color = { 0, 0, 0, 0 }, }); wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = texture, @@ -535,4 +534,4 @@ bool output_cursor_refresh_color_transform(struct wlr_output_cursor *output_curs wlr_color_transform_unref(transforms[0]); wlr_color_transform_unref(transforms[1]); return output_cursor->color_transform != NULL; -} \ No newline at end of file +} diff --git a/types/output/render.c b/types/output/render.c index 155da5cf8..275d74f36 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -63,9 +63,8 @@ static struct wlr_buffer *output_acquire_empty_buffer(struct wlr_output *output, return NULL; } - wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ + wlr_render_pass_clear(pass, &(struct wlr_render_clear_options){ .color = { 0, 0, 0, 0 }, - .blend_mode = WLR_RENDER_BLEND_MODE_NONE, }); if (!wlr_render_pass_submit(pass)) {