mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-06-13 14:32:57 -04:00
render/vulkan: Use VK_EXT_host_image_copy for shm
When uploading shm buffers to our internal backing texture, we first allocate suitable chunk from our staging buffer accessible from both CPU and GPU, and then we queue a GPU-side copy from the staging buffer to the texture. This is a lot of copying, especially on iGPUs where everything is system memory anyway. Instead, use VK_EXT_host_image_copy when available for formats that are reported as having optimal device access. This allows us to copy directly to the target texture from CPU, eliminating the queued GPU-side copy. To keep things simple we keep this texture in GENERAL for the entire duration for now.
This commit is contained in:
parent
e8c03e9ce9
commit
9a457a3f1d
4 changed files with 199 additions and 17 deletions
|
|
@ -35,9 +35,84 @@ static VkImageAspectFlagBits mem_plane_aspect(unsigned i) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool write_pixels_host(struct wlr_vk_texture *texture,
|
||||
uint32_t stride, const pixman_region32_t *region, const void *vdata) {
|
||||
struct wlr_vk_renderer *renderer = texture->renderer;
|
||||
|
||||
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(texture->format->drm);
|
||||
assert(format_info);
|
||||
|
||||
// If the buffer is in use, we unfortunately need to wait for that to finish
|
||||
if (texture->last_used_cb != NULL &&
|
||||
!vulkan_wait_command_buffer(texture->last_used_cb, renderer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int rects_len = 0;
|
||||
const pixman_box32_t *rects = pixman_region32_rectangles(region, &rects_len);
|
||||
if (rects_len == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VkMemoryToImageCopyEXT *copies = calloc((size_t)rects_len, sizeof(*copies));
|
||||
if (!copies) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate image copy parameters");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rects_len; i++) {
|
||||
pixman_box32_t rect = rects[i];
|
||||
uint32_t width = rect.x2 - rect.x1;
|
||||
uint32_t height = rect.y2 - rect.y1;
|
||||
uint32_t src_x = rect.x1;
|
||||
uint32_t src_y = rect.y1;
|
||||
|
||||
assert((uint32_t)rect.x2 <= texture->wlr_texture.width);
|
||||
assert((uint32_t)rect.y2 <= texture->wlr_texture.height);
|
||||
|
||||
const char *pdata = (const char *)vdata;
|
||||
pdata += stride * src_y;
|
||||
pdata += format_info->bytes_per_block * src_x;
|
||||
|
||||
copies[i] = (VkMemoryToImageCopyEXT) {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT,
|
||||
.pHostPointer = pdata,
|
||||
.memoryRowLength = stride / format_info->bytes_per_block,
|
||||
.memoryImageHeight = height,
|
||||
.imageExtent.width = width,
|
||||
.imageExtent.height = height,
|
||||
.imageExtent.depth = 1,
|
||||
.imageOffset.x = src_x,
|
||||
.imageOffset.y = src_y,
|
||||
.imageOffset.z = 0,
|
||||
.imageSubresource.mipLevel = 0,
|
||||
.imageSubresource.baseArrayLayer = 0,
|
||||
.imageSubresource.layerCount = 1,
|
||||
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
};
|
||||
}
|
||||
|
||||
VkCopyMemoryToImageInfoEXT copy_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT,
|
||||
.dstImage = texture->image,
|
||||
.dstImageLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.regionCount = (uint32_t)rects_len,
|
||||
.pRegions = copies,
|
||||
};
|
||||
|
||||
VkResult res = renderer->dev->api.vkCopyMemoryToImageEXT(renderer->dev->dev, ©_info);
|
||||
free(copies);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCopyMemoryToImageEXT failed", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Will transition the texture to shaderReadOnlyOptimal layout for reading
|
||||
// from fragment shader later on
|
||||
static bool write_pixels(struct wlr_vk_texture *texture,
|
||||
static bool write_pixels_staging(struct wlr_vk_texture *texture,
|
||||
uint32_t stride, const pixman_region32_t *region, const void *vdata,
|
||||
VkImageLayout old_layout, VkPipelineStageFlags src_stage,
|
||||
VkAccessFlags src_access) {
|
||||
|
|
@ -172,8 +247,13 @@ static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ok = write_pixels(texture, stride, damage, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
if (texture->host_image_copy) {
|
||||
ok = write_pixels_host(texture, stride, damage, data);
|
||||
} else {
|
||||
ok = write_pixels_staging(texture, stride, damage, data,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
out:
|
||||
wlr_buffer_end_data_ptr_access(buffer);
|
||||
|
|
@ -337,7 +417,9 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text
|
|||
|
||||
VkDescriptorImageInfo ds_img_info = {
|
||||
.imageView = view->image_view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
.imageLayout = texture->host_image_copy
|
||||
? VK_IMAGE_LAYOUT_GENERAL
|
||||
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet ds_write = {
|
||||
|
|
@ -399,6 +481,12 @@ static struct wlr_texture *vulkan_texture_from_pixels(
|
|||
}
|
||||
|
||||
texture_set_format(texture, &fmt->format, fmt->shm.has_mutable_srgb);
|
||||
texture->host_image_copy = fmt->shm.host_image_copy;
|
||||
|
||||
VkImageUsageFlags usage = vulkan_shm_tex_usage;
|
||||
if (texture->host_image_copy) {
|
||||
usage |= VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
|
||||
}
|
||||
|
||||
VkFormat view_formats[] = {
|
||||
fmt->format.vk,
|
||||
|
|
@ -420,7 +508,7 @@ static struct wlr_texture *vulkan_texture_from_pixels(
|
|||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.extent = (VkExtent3D) { width, height, 1 },
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = vulkan_shm_tex_usage,
|
||||
.usage = usage,
|
||||
.pNext = fmt->shm.has_mutable_srgb ? &list_info : NULL,
|
||||
};
|
||||
if (fmt->shm.has_mutable_srgb) {
|
||||
|
|
@ -462,14 +550,40 @@ static struct wlr_texture *vulkan_texture_from_pixels(
|
|||
goto error;
|
||||
}
|
||||
|
||||
pixman_region32_t region;
|
||||
pixman_region32_init_rect(®ion, 0, 0, width, height);
|
||||
if (!write_pixels(texture, stride, ®ion, data, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0)) {
|
||||
goto error;
|
||||
if (texture->host_image_copy) {
|
||||
VkHostImageLayoutTransitionInfoEXT transition = {
|
||||
.sType = VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO_EXT,
|
||||
.image = texture->image,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
res = renderer->dev->api.vkTransitionImageLayoutEXT(dev, 1, &transition);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkTransitionImageLayoutEXT failed", res);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return &texture->wlr_texture;
|
||||
pixman_region32_t region;
|
||||
pixman_region32_init_rect(®ion, 0, 0, width, height);
|
||||
|
||||
bool ok;
|
||||
if (texture->host_image_copy) {
|
||||
ok = write_pixels_host(texture, stride, ®ion, data);
|
||||
} else {
|
||||
ok = write_pixels_staging(texture, stride, ®ion, data,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0);
|
||||
}
|
||||
if (ok) {
|
||||
return &texture->wlr_texture;
|
||||
}
|
||||
|
||||
error:
|
||||
vulkan_texture_destroy(texture);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue