From 827ea06c443c60a8697255b375880ca3f6181252 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 7 Jul 2024 13:31:56 +0200 Subject: [PATCH 1/3] render/swapchain: Add-on to get swapchain from buffer This can be used to cache things shared between render buffers (e.g., blending buffers) on the swapchain instance itself. --- include/wlr/render/swapchain.h | 6 +++++ render/swapchain.c | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/wlr/render/swapchain.h b/include/wlr/render/swapchain.h index 2317f1b62..5537efc73 100644 --- a/include/wlr/render/swapchain.h +++ b/include/wlr/render/swapchain.h @@ -4,6 +4,7 @@ #include #include #include +#include #define WLR_SWAPCHAIN_CAP 4 @@ -23,6 +24,8 @@ struct wlr_swapchain { struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP]; struct wl_listener allocator_destroy; + + struct wlr_addon_set addons; }; struct wlr_swapchain *wlr_swapchain_create( @@ -43,4 +46,7 @@ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain); bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer); +struct wlr_swapchain *wlr_swapchain_try_from_wlr_buffer( + struct wlr_buffer *buffer); + #endif diff --git a/render/swapchain.c b/render/swapchain.c index 233d85eb0..0fecba1ce 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -6,6 +6,36 @@ #include "render/allocator/allocator.h" #include "render/drm_format_set.h" +struct wlr_swapchain_buffer { + struct wlr_addon buffer_addon; + struct wlr_swapchain *swapchain; +}; + +static void wlr_swapchain_buffer_destroy(struct wlr_addon *addon) { + struct wlr_swapchain_buffer *swapchain_buffer = + wl_container_of(addon, swapchain_buffer, buffer_addon); + wlr_addon_finish(&swapchain_buffer->buffer_addon); + swapchain_buffer->swapchain = NULL; + free(swapchain_buffer); +} + +static const struct wlr_addon_interface buffer_addon_impl = { + .name = "wlr_swapchain_buffer", + .destroy = wlr_swapchain_buffer_destroy, +}; + +struct wlr_swapchain *wlr_swapchain_try_from_wlr_buffer(struct wlr_buffer *buffer) { + struct wlr_addon *addon = + wlr_addon_find(&buffer->addons, NULL, &buffer_addon_impl); + if (addon == NULL) { + return NULL; + } + + struct wlr_swapchain_buffer *swapchain_buffer = + wl_container_of(addon, swapchain_buffer, buffer_addon); + return swapchain_buffer->swapchain; +} + static void swapchain_handle_allocator_destroy(struct wl_listener *listener, void *data) { struct wlr_swapchain *swapchain = @@ -33,6 +63,7 @@ struct wlr_swapchain *wlr_swapchain_create( swapchain->allocator_destroy.notify = swapchain_handle_allocator_destroy; wl_signal_add(&alloc->events.destroy, &swapchain->allocator_destroy); + wlr_addon_set_init(&swapchain->addons); return swapchain; } @@ -54,6 +85,7 @@ void wlr_swapchain_destroy(struct wlr_swapchain *swapchain) { } wl_list_remove(&swapchain->allocator_destroy.link); wlr_drm_format_finish(&swapchain->format); + wlr_addon_set_finish(&swapchain->addons); free(swapchain); } @@ -98,13 +130,25 @@ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain) { return NULL; } + struct wlr_swapchain_buffer *swapchain_buffer = + calloc(1, sizeof(*swapchain_buffer)); + if (swapchain_buffer == NULL) { + return NULL; + } + swapchain_buffer->swapchain = swapchain; + wlr_log(WLR_DEBUG, "Allocating new swapchain buffer"); free_slot->buffer = wlr_allocator_create_buffer(swapchain->allocator, swapchain->width, swapchain->height, &swapchain->format); if (free_slot->buffer == NULL) { wlr_log(WLR_ERROR, "Failed to allocate buffer"); + free(swapchain_buffer); return NULL; } + + wlr_addon_init(&swapchain_buffer->buffer_addon, &free_slot->buffer->addons, + NULL, &buffer_addon_impl); + return slot_acquire(swapchain, free_slot); } From 752d9f232ca6dfa4462abc62cbc083e3cf781cb8 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 7 Jul 2024 16:06:09 +0200 Subject: [PATCH 2/3] render/vulkan: Reference count blending buffer This will be used to share the blending buffer in a later commit. --- include/render/vulkan.h | 14 ++- render/vulkan/pass.c | 2 +- render/vulkan/renderer.c | 211 ++++++++++++++++++++++++--------------- 3 files changed, 144 insertions(+), 83 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 29403f01f..75b5ec526 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -197,6 +197,15 @@ struct wlr_vk_render_format_setup { struct wl_list pipelines; // struct wlr_vk_pipeline.link }; +// Internal blending buffer shraed between render buffers +struct wlr_vk_blend_buffer { + struct wlr_vk_renderer *renderer; + VkImageView blend_image_view; + VkImage blend_image; + VkDeviceMemory blend_memory; + int refcnt; +}; + // Renderer-internal represenation of an wlr_buffer imported for rendering. struct wlr_vk_render_buffer { struct wlr_buffer *wlr_buffer; @@ -228,9 +237,8 @@ struct wlr_vk_render_buffer { VkFramebuffer framebuffer; bool transitioned; - VkImage blend_image; - VkImageView blend_image_view; - VkDeviceMemory blend_memory; + struct wlr_vk_blend_buffer *blend_buffer; + VkDescriptorSet blend_descriptor_set; struct wlr_vk_descriptor_pool *blend_attachment_pool; bool blend_transitioned; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 3697dcda6..0cfb3e0e0 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -267,7 +267,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = render_buffer->plain.blend_image, + .image = render_buffer->plain.blend_buffer->blend_image, .oldLayout = blend_src_layout, .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 32effb5a7..7adcc46da 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -576,6 +576,26 @@ void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb) { } } +static void vulkan_blend_buffer_unref(struct wlr_vk_blend_buffer *bb) { + if (bb == NULL || --bb->refcnt > 0) { + return; + } + + VkDevice dev = bb->renderer->dev->dev; + + vkDestroyImageView(dev, bb->blend_image_view, NULL); + vkDestroyImage(dev, bb->blend_image, NULL); + vkFreeMemory(dev, bb->blend_memory, NULL); + + free(bb); +} + +static struct wlr_vk_blend_buffer *vulkan_blend_buffer_ref( + struct wlr_vk_blend_buffer *bb) { + bb->refcnt++; + return bb; +} + static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wl_list_remove(&buffer->link); wlr_addon_finish(&buffer->addon); @@ -594,9 +614,7 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { vkDestroyFramebuffer(dev, buffer->plain.framebuffer, NULL); vkDestroyImageView(dev, buffer->plain.image_view, NULL); - vkDestroyImage(dev, buffer->plain.blend_image, NULL); - vkFreeMemory(dev, buffer->plain.blend_memory, NULL); - vkDestroyImageView(dev, buffer->plain.blend_image_view, NULL); + vulkan_blend_buffer_unref(buffer->plain.blend_buffer); if (buffer->plain.blend_attachment_pool) { vulkan_free_ds(buffer->renderer, buffer->plain.blend_attachment_pool, buffer->plain.blend_descriptor_set); @@ -620,12 +638,121 @@ static struct wlr_addon_interface render_buffer_addon_impl = { .destroy = handle_render_buffer_destroy, }; +static struct wlr_vk_blend_buffer *vulkan_create_blend_buffer( + struct wlr_vk_renderer *renderer, int width, int height) { + VkResult res; + VkDevice dev = renderer->dev->dev; + + struct wlr_vk_blend_buffer *bb = calloc(1, sizeof(*bb)); + if (!bb) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + bb->renderer = renderer; + + // Set up an extra 16F buffer on which to do linear blending, + // and afterwards to render onto the target + VkImageCreateInfo img_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_R16G16B16A16_SFLOAT, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .extent = (VkExtent3D) { width, height, 1 }, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, + }; + + res = vkCreateImage(dev, &img_info, NULL, &bb->blend_image); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImage failed", res); + goto error; + } + + VkMemoryRequirements mem_reqs; + vkGetImageMemoryRequirements(dev, bb->blend_image, &mem_reqs); + + int mem_type_index = vulkan_find_mem_type(renderer->dev, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); + if (mem_type_index == -1) { + wlr_log(WLR_ERROR, "failed to find suitable vulkan memory type"); + goto error; + } + + VkMemoryAllocateInfo mem_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = mem_type_index, + }; + + res = vkAllocateMemory(dev, &mem_info, NULL, &bb->blend_memory); + if (res != VK_SUCCESS) { + wlr_vk_error("vkAllocatorMemory failed", res); + goto error; + } + + res = vkBindImageMemory(dev, bb->blend_image, bb->blend_memory, 0); + if (res != VK_SUCCESS) { + wlr_vk_error("vkBindMemory failed", res); + goto error; + } + + VkImageViewCreateInfo blend_view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = bb->blend_image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = img_info.format, + .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, + .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + res = vkCreateImageView(dev, &blend_view_info, NULL, &bb->blend_image_view); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImageView failed", res); + goto error; + } + + return bb; + +error: + if (bb->blend_image_view) { + vkDestroyImageView(dev, bb->blend_image_view, NULL); + } + if (bb->blend_image) { + vkDestroyImage(dev, bb->blend_image, NULL); + } + if (bb->blend_memory) { + vkFreeMemory(dev, bb->blend_memory, NULL); + } + + return NULL; +} + bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, const struct wlr_dmabuf_attributes *dmabuf) { struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; VkDevice dev = renderer->dev->dev; + struct wlr_vk_blend_buffer *bb = vulkan_create_blend_buffer(renderer, + dmabuf->width, dmabuf->height); + if (!bb) { + return false; + } + buffer->plain.blend_buffer = vulkan_blend_buffer_ref(bb); + const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( renderer->dev, dmabuf->format); assert(fmt); @@ -660,80 +787,6 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, goto error; } - // Set up an extra 16F buffer on which to do linear blending, - // and afterwards to render onto the target - VkImageCreateInfo img_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .format = VK_FORMAT_R16G16B16A16_SFLOAT, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .extent = (VkExtent3D) { dmabuf->width, dmabuf->height, 1 }, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, - }; - - res = vkCreateImage(dev, &img_info, NULL, &buffer->plain.blend_image); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateImage failed", res); - goto error; - } - - VkMemoryRequirements mem_reqs; - vkGetImageMemoryRequirements(dev, buffer->plain.blend_image, &mem_reqs); - - int mem_type_index = vulkan_find_mem_type(renderer->dev, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); - if (mem_type_index == -1) { - wlr_log(WLR_ERROR, "failed to find suitable vulkan memory type"); - goto error; - } - - VkMemoryAllocateInfo mem_info = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = mem_reqs.size, - .memoryTypeIndex = mem_type_index, - }; - - res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->plain.blend_memory); - if (res != VK_SUCCESS) { - wlr_vk_error("vkAllocatorMemory failed", res); - goto error; - } - - res = vkBindImageMemory(dev, buffer->plain.blend_image, buffer->plain.blend_memory, 0); - if (res != VK_SUCCESS) { - wlr_vk_error("vkBindMemory failed", res); - goto error; - } - - VkImageViewCreateInfo blend_view_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = buffer->plain.blend_image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = img_info.format, - .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->plain.blend_image_view); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateImageView failed", res); - goto error; - } - buffer->plain.blend_attachment_pool = vulkan_alloc_blend_ds(renderer, &buffer->plain.blend_descriptor_set); if (!buffer->plain.blend_attachment_pool) { @@ -743,7 +796,7 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, VkDescriptorImageInfo ds_attach_info = { .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = buffer->plain.blend_image_view, + .imageView = bb->blend_image_view, .sampler = VK_NULL_HANDLE, }; VkWriteDescriptorSet ds_write = { @@ -757,7 +810,7 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); VkImageView attachments[2] = { - buffer->plain.blend_image_view, + bb->blend_image_view, buffer->plain.image_view }; VkFramebufferCreateInfo fb_info = { From fb8921f69c20f04bd2ca9412eddfc1f2b3143897 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 7 Jul 2024 16:07:18 +0200 Subject: [PATCH 3/3] render/vulkan: Cache blending buffer on swapchain Our blending buffer is 64 bits per pixel, and triple the amount of memory needed for each swapchain buffer. To save some memory, keep a single blending buffer per swapchain. --- render/vulkan/renderer.c | 62 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 7adcc46da..b0668283e 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -638,6 +639,23 @@ static struct wlr_addon_interface render_buffer_addon_impl = { .destroy = handle_render_buffer_destroy, }; +struct vulkan_swapchain_blend_buffer { + struct wlr_addon swapchain_addon; + struct wlr_vk_blend_buffer *blend_buffer; +}; + +static void handle_swapchain_destroy(struct wlr_addon *addon) { + struct vulkan_swapchain_blend_buffer *bb = + wl_container_of(addon, bb, swapchain_addon); + vulkan_blend_buffer_unref(bb->blend_buffer); + wlr_addon_finish(&bb->swapchain_addon); +} + +static struct wlr_addon_interface vulkan_swapchain_blend_buffer_addon_impl = { + .name = "vulkan_swapchain_blend_buffer", + .destroy = handle_swapchain_destroy, +}; + static struct wlr_vk_blend_buffer *vulkan_create_blend_buffer( struct wlr_vk_renderer *renderer, int width, int height) { VkResult res; @@ -740,14 +758,54 @@ error: return NULL; } +static struct wlr_vk_blend_buffer *vulkan_get_swapchain_blend_buffer( + struct wlr_vk_renderer *renderer, struct wlr_swapchain *swapchain, + int width, int height) { + + if (!swapchain) { + wlr_log(WLR_DEBUG, "No swapchain for blending buffer"); + return vulkan_create_blend_buffer(renderer, width, height); + } + + struct vulkan_swapchain_blend_buffer *sbb; + struct wlr_addon *addon = wlr_addon_find(&swapchain->addons, renderer, + &vulkan_swapchain_blend_buffer_addon_impl); + if (addon) { + wlr_log(WLR_DEBUG, "Reusing blending buffer"); + sbb = wl_container_of(addon, sbb, swapchain_addon); + return sbb->blend_buffer; + } + + sbb = calloc(1, sizeof(*sbb)); + if (!sbb) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + struct wlr_vk_blend_buffer *bb = vulkan_create_blend_buffer(renderer, width, height); + if (!bb) { + free(sbb); + return NULL; + } + sbb->blend_buffer = vulkan_blend_buffer_ref(bb); + + wlr_log(WLR_DEBUG, "New swapchain blending buffer"); + + wlr_addon_init(&sbb->swapchain_addon, &swapchain->addons, renderer, + &vulkan_swapchain_blend_buffer_addon_impl); + return sbb->blend_buffer; +} + bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, const struct wlr_dmabuf_attributes *dmabuf) { struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; VkDevice dev = renderer->dev->dev; - struct wlr_vk_blend_buffer *bb = vulkan_create_blend_buffer(renderer, - dmabuf->width, dmabuf->height); + struct wlr_swapchain *swapchain = + wlr_swapchain_try_from_wlr_buffer(buffer->wlr_buffer); + struct wlr_vk_blend_buffer *bb = vulkan_get_swapchain_blend_buffer(renderer, + swapchain, dmabuf->width, dmabuf->height); if (!bb) { return false; }