render/vulkan: Delay shm texture updates till render

In order to optimize rendering of shm-backed buffers, we copy the buffer
into a properly allocated texture. However, some clients might send
rapid updates for large surfaces, or may not even be visible, making the
copy wasteful.

Move the copy to the point where the texture is added to the render
pass, ensuring that we only perform the copy if texture is being used
and only once per render. This means that the shm optimization is
effectively disabled for clients that are not visible.
This commit is contained in:
Kenny Levinsen 2024-07-04 00:57:17 +02:00
parent 67b88e46b0
commit 6ca84bf64d
3 changed files with 51 additions and 15 deletions

View file

@ -11,6 +11,7 @@
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
#include <wlr/util/addon.h> #include <wlr/util/addon.h>
#include "util/rect_union.h" #include "util/rect_union.h"
#include <pixman.h>
struct wlr_vk_descriptor_pool; struct wlr_vk_descriptor_pool;
struct wlr_vk_texture; struct wlr_vk_texture;
@ -454,6 +455,9 @@ struct wlr_vk_texture {
VkSemaphore foreign_semaphores[WLR_DMABUF_MAX_PLANES]; VkSemaphore foreign_semaphores[WLR_DMABUF_MAX_PLANES];
struct wl_list views; // struct wlr_vk_texture_ds.link struct wl_list views; // struct wlr_vk_texture_ds.link
// For SHM buffers
pixman_region32_t invalidated;
}; };
struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture); struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture);
@ -464,6 +468,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
struct wlr_texture *vulkan_texture_from_buffer( struct wlr_texture *vulkan_texture_from_buffer(
struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer); struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer);
void vulkan_texture_destroy(struct wlr_vk_texture *texture); void vulkan_texture_destroy(struct wlr_vk_texture *texture);
void vulkan_texture_update(struct wlr_vk_texture *texture);
struct wlr_vk_descriptor_pool { struct wlr_vk_descriptor_pool {
VkDescriptorPool pool; VkDescriptorPool pool;

View file

@ -612,6 +612,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
wl_list_insert(&renderer->foreign_textures, &texture->foreign_link); wl_list_insert(&renderer->foreign_textures, &texture->foreign_link);
} }
vulkan_texture_update(texture);
struct wlr_fbox src_box; struct wlr_fbox src_box;
wlr_render_texture_options_get_src_box(options, &src_box); wlr_render_texture_options_get_src_box(options, &src_box);
struct wlr_box dst_box; struct wlr_box dst_box;

View file

@ -153,6 +153,26 @@ static bool write_pixels(struct wlr_vk_texture *texture,
return true; return true;
} }
void vulkan_texture_update(struct wlr_vk_texture *texture) {
if (pixman_region32_empty(&texture->invalidated)) {
return;
}
void *data;
uint32_t format;
size_t stride;
if (wlr_buffer_begin_data_ptr_access(texture->buffer,
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
write_pixels(texture, stride, &texture->invalidated, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
wlr_buffer_end_data_ptr_access(texture->buffer);
}
pixman_region32_clear(&texture->invalidated);
wlr_buffer_unlock(texture->buffer);
texture->buffer = NULL;
}
static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture, static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture,
struct wlr_buffer *buffer, const pixman_region32_t *damage) { struct wlr_buffer *buffer, const pixman_region32_t *damage) {
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
@ -172,8 +192,12 @@ static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture,
goto out; goto out;
} }
ok = write_pixels(texture, stride, damage, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, if (texture->buffer) {
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); wlr_buffer_unlock(texture->buffer);
}
texture->buffer = wlr_buffer_lock(buffer);
pixman_region32_union(&texture->invalidated, &texture->invalidated, damage);
out: out:
wlr_buffer_end_data_ptr_access(buffer); wlr_buffer_end_data_ptr_access(buffer);
@ -198,6 +222,7 @@ void vulkan_texture_destroy(struct wlr_vk_texture *texture) {
} }
wl_list_remove(&texture->link); wl_list_remove(&texture->link);
pixman_region32_fini(&texture->invalidated);
VkDevice dev = texture->renderer->dev->dev; VkDevice dev = texture->renderer->dev->dev;
@ -225,13 +250,18 @@ void vulkan_texture_destroy(struct wlr_vk_texture *texture) {
static void vulkan_texture_unref(struct wlr_texture *wlr_texture) { static void vulkan_texture_unref(struct wlr_texture *wlr_texture) {
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
if (texture->buffer != NULL) { if (texture->buffer) {
// Keep the texture around, in case the buffer is re-used later. We're
// still listening to the buffer's destroy event.
wlr_buffer_unlock(texture->buffer); wlr_buffer_unlock(texture->buffer);
} else { if (texture->dmabuf_imported) {
vulkan_texture_destroy(texture); // Keep the texture around, in case the buffer is re-used later. We're
// still listening to the buffer's destroy event.
return;
}
// The buffer is temporary and has no add-on, clear it before destroy
texture->buffer = NULL;
} }
vulkan_texture_destroy(texture);
} }
static bool vulkan_texture_read_pixels(struct wlr_texture *wlr_texture, static bool vulkan_texture_read_pixels(struct wlr_texture *wlr_texture,
@ -271,6 +301,7 @@ static struct wlr_vk_texture *vulkan_texture_create(
texture->renderer = renderer; texture->renderer = renderer;
wl_list_insert(&renderer->textures, &texture->link); wl_list_insert(&renderer->textures, &texture->link);
wl_list_init(&texture->views); wl_list_init(&texture->views);
pixman_region32_init(&texture->invalidated);
return texture; return texture;
} }
@ -376,9 +407,11 @@ static void texture_set_format(struct wlr_vk_texture *texture,
static struct wlr_texture *vulkan_texture_from_pixels( static struct wlr_texture *vulkan_texture_from_pixels(
struct wlr_vk_renderer *renderer, uint32_t drm_fmt, uint32_t stride, struct wlr_vk_renderer *renderer, uint32_t drm_fmt, uint32_t stride,
uint32_t width, uint32_t height, const void *data) { struct wlr_buffer *buffer) {
VkResult res; VkResult res;
VkDevice dev = renderer->dev->dev; VkDevice dev = renderer->dev->dev;
int32_t width = buffer->width;
int32_t height = buffer->height;
const struct wlr_vk_format_props *fmt = const struct wlr_vk_format_props *fmt =
vulkan_format_props_from_drm(renderer->dev, drm_fmt); vulkan_format_props_from_drm(renderer->dev, drm_fmt);
@ -459,12 +492,8 @@ static struct wlr_texture *vulkan_texture_from_pixels(
goto error; goto error;
} }
pixman_region32_t region; pixman_region32_init_rect(&texture->invalidated, 0, 0, width, height);
pixman_region32_init_rect(&region, 0, 0, width, height); texture->buffer = wlr_buffer_lock(buffer);
if (!write_pixels(texture, stride, &region, data, VK_IMAGE_LAYOUT_UNDEFINED,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0)) {
goto error;
}
return &texture->wlr_texture; return &texture->wlr_texture;
@ -814,7 +843,7 @@ struct wlr_texture *vulkan_texture_from_buffer(struct wlr_renderer *wlr_renderer
} else if (wlr_buffer_begin_data_ptr_access(buffer, } else if (wlr_buffer_begin_data_ptr_access(buffer,
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
struct wlr_texture *tex = vulkan_texture_from_pixels(renderer, struct wlr_texture *tex = vulkan_texture_from_pixels(renderer,
format, stride, buffer->width, buffer->height, data); format, stride, buffer);
wlr_buffer_end_data_ptr_access(buffer); wlr_buffer_end_data_ptr_access(buffer);
return tex; return tex;
} else { } else {