vulkan-renderer: fix read pixels for sub regions

The captured buffer cannot be just memcpy'ed to the destination as
it might overwrite existing pixels outside of the capture region.
For now switch it to a pixman composition.

Signed-off-by: Erico Nunes <nunes.erico@gmail.com>
This commit is contained in:
Erico Nunes 2025-05-27 10:35:14 +02:00
parent 4e4d7993ba
commit f8af0dbf72

View file

@ -1023,21 +1023,27 @@ create_image_view(VkDevice device, VkImage image, VkFormat format, VkImageView *
}
static void
copy_image_to_buffer(VkCommandBuffer cmd_buffer,
VkImage image, VkBuffer buffer,
uint32_t image_width, uint32_t image_height,
uint32_t buffer_pitch)
copy_sub_image_to_buffer(VkCommandBuffer cmd_buffer,
VkBuffer buffer, VkImage image,
uint32_t buffer_width, uint32_t buffer_height,
uint32_t pitch,
uint32_t bpp,
uint32_t xoff, uint32_t yoff,
uint32_t xcopy, uint32_t ycopy)
{
const VkExtent3D image_extent = { image_width, image_height, 1 };
const VkOffset3D image_offset = { xoff, yoff };
const VkExtent3D image_extent = { xcopy, ycopy, 1 };
const VkBufferImageCopy region = {
.bufferOffset = ((buffer_width * yoff) + xoff) * (bpp/8),
.bufferRowLength = pitch,
.bufferImageHeight = buffer_height,
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.imageSubresource.mipLevel = 0,
.imageSubresource.baseArrayLayer = 0,
.imageSubresource.layerCount = 1,
.imageOffset = image_offset,
.imageExtent = image_extent,
.bufferOffset = 0,
.bufferRowLength = buffer_pitch,
.bufferImageHeight = image_height,
};
vkCmdCopyImageToBuffer(cmd_buffer,
@ -1104,7 +1110,7 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
{
VkBuffer dst_buffer;
VkDeviceMemory dst_memory;
VkDeviceSize buffer_size = stride * rect->height;
VkDeviceSize buffer_size = stride * vo->fb_size.height;
VkResult result;
create_buffer(vr, buffer_size,
@ -1121,10 +1127,13 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, VK_ACCESS_TRANSFER_WRITE_BIT);
copy_image_to_buffer(cmd_buffer,
color_attachment, dst_buffer,
rect->width, rect->height,
stride / (pixel_format->bpp/8));
copy_sub_image_to_buffer(cmd_buffer,
dst_buffer, color_attachment,
vo->fb_size.width, vo->fb_size.height,
(stride / (pixel_format->bpp/8)),
pixel_format->bpp,
rect->x, rect->y,
rect->width, rect->height);
transition_image_layout(cmd_buffer, color_attachment,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
@ -1139,7 +1148,30 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
result = vkMapMemory(vr->dev, dst_memory, 0, VK_WHOLE_SIZE, 0, &buffer_map);
check_vk_success(result, "vkMapMemory");
memcpy(pixels, buffer_map, buffer_size);
/* The captured buffer cannot be just memcpy'ed to the destination as
* it might overwrite existing pixels outside of the capture region,
* so use a pixman composition. */
pixman_image_t *image_src;
image_src = pixman_image_create_bits_no_clear(pixel_format->pixman_format,
vo->fb_size.width, vo->fb_size.height,
buffer_map, stride);
pixman_image_t *image_dst;
image_dst = pixman_image_create_bits_no_clear(pixel_format->pixman_format,
vo->fb_size.width, vo->fb_size.height,
pixels, stride);
pixman_image_composite32(PIXMAN_OP_SRC,
image_src, /* src */
NULL, /* mask */
image_dst, /* dest */
rect->x, rect->y, /* src x,y */
0, 0, /* mask x,y */
rect->x, rect->y, /* dest x,y */
rect->width, rect->height);
pixman_image_unref(image_src);
pixman_image_unref(image_dst);
destroy_buffer(vr->dev, dst_buffer, dst_memory);