Mesa (main): venus: reland wsi image ownership transfer for Android
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Thu Jun 3 06:13:17 UTC 2021
Module: Mesa
Branch: main
Commit: 676abf12dc6f1d03dbf73cf5370f78578576e0ed
URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=676abf12dc6f1d03dbf73cf5370f78578576e0ed
Author: Chia-I Wu <olvaffe at gmail.com>
Date: Wed May 5 15:58:39 2021 -0700
venus: reland wsi image ownership transfer for Android
This takes a different approach. It turns out we can patch
vkCmd{Begin,End}RenderPass, vkCmdWaitEvents, and vkCmdPipelineBarrier to
do queue family ownership transfers without tracking image ownerships.
v2: check the value of VN_PRESENT_SRC_INTERNAL_LAYOUT and keep this
Android-only
Signed-off-by: Chia-I Wu <olvaffe at gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei at chromium.org> (v1)
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10709>
---
src/virtio/vulkan/vn_command_buffer.c | 326 ++++++++++++++++++++++++++++++++--
1 file changed, 312 insertions(+), 14 deletions(-)
diff --git a/src/virtio/vulkan/vn_command_buffer.c b/src/virtio/vulkan/vn_command_buffer.c
index 73f10f6c504..655a071d7fb 100644
--- a/src/virtio/vulkan/vn_command_buffer.c
+++ b/src/virtio/vulkan/vn_command_buffer.c
@@ -50,31 +50,211 @@ vn_cmd_get_image_memory_barriers(struct vn_command_buffer *cmd,
return cmd->builder.image_barriers;
}
+/* About VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, the spec says
+ *
+ * VK_IMAGE_LAYOUT_PRESENT_SRC_KHR must only be used for presenting a
+ * presentable image for display. A swapchain's image must be transitioned
+ * to this layout before calling vkQueuePresentKHR, and must be
+ * transitioned away from this layout after calling vkAcquireNextImageKHR.
+ *
+ * That allows us to treat the layout internally as
+ *
+ * - VK_IMAGE_LAYOUT_GENERAL
+ * - VK_QUEUE_FAMILY_FOREIGN_EXT has the ownership, if the image is not a
+ * prime blit source
+ *
+ * while staying performant.
+ *
+ * About queue family ownerships, the spec says
+ *
+ * A queue family can take ownership of an image subresource or buffer
+ * range of a resource created with VK_SHARING_MODE_EXCLUSIVE, without an
+ * ownership transfer, in the same way as for a resource that was just
+ * created; however, taking ownership in this way has the effect that the
+ * contents of the image subresource or buffer range are undefined.
+ *
+ * It is unclear if that is applicable to external resources, which supposedly
+ * have the same semantics
+ *
+ * Binding a resource to a memory object shared between multiple Vulkan
+ * instances or other APIs does not change the ownership of the underlying
+ * memory. The first entity to access the resource implicitly acquires
+ * ownership. Accessing a resource backed by memory that is owned by a
+ * particular instance or API has the same semantics as accessing a
+ * VK_SHARING_MODE_EXCLUSIVE resource[...]
+ *
+ * We should get the spec clarified, or get rid of this completely broken code
+ * (TODO).
+ *
+ * Assuming a queue family can acquire the ownership implicitly when the
+ * contents are not needed, we do not need to worry about
+ * VK_IMAGE_LAYOUT_UNDEFINED. We can use VK_IMAGE_LAYOUT_PRESENT_SRC_KHR as
+ * the sole signal to trigger queue family ownership transfers.
+ *
+ * When the image has VK_SHARING_MODE_CONCURRENT, we can, and are required to,
+ * use VK_QUEUE_FAMILY_IGNORED as the other queue family whether we are
+ * transitioning to or from VK_IMAGE_LAYOUT_PRESENT_SRC_KHR.
+ *
+ * When the image has VK_SHARING_MODE_EXCLUSIVE, we have to work out who the
+ * other queue family is. It is easier when the barrier does not also define
+ * a queue family ownership transfer (i.e., srcQueueFamilyIndex equals to
+ * dstQueueFamilyIndex). The other queue family must be the queue family the
+ * command buffer was allocated for.
+ *
+ * When the barrier also defines a queue family ownership transfer, it is
+ * submitted both to the source queue family to release the ownership and to
+ * the destination queue family to acquire the ownership. Depending on
+ * whether the barrier transitions to or from VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ * we are only interested in the ownership release or acquire respectively and
+ * should be careful to avoid double releases/acquires.
+ *
+ * I haven't followed all transition paths mentally to verify the correctness.
+ * I likely also violate some VUs or miss some cases below. They are
+ * hopefully fixable and are left as TODOs.
+ */
static void
vn_cmd_fix_image_memory_barrier(const struct vn_command_buffer *cmd,
const VkImageMemoryBarrier *src_barrier,
VkImageMemoryBarrier *out_barrier)
{
+ const struct vn_image *img = vn_image_from_handle(src_barrier->image);
+
*out_barrier = *src_barrier;
- /* XXX drop the #ifdef after fixing common wsi */
-#ifdef ANDROID
- for (uint32_t i = 0; i < count; i++) {
+ /* no fix needed */
+ if (out_barrier->oldLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
+ out_barrier->newLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
+ return;
+
+ assert(img->is_wsi);
+
+ if (VN_PRESENT_SRC_INTERNAL_LAYOUT == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
+ return;
+
+ /* prime blit src or no layout transition */
+ if (img->prime_blit_buffer != VK_NULL_HANDLE ||
+ out_barrier->oldLayout == out_barrier->newLayout) {
if (out_barrier->oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
- out_barrier->oldLayout = VK_IMAGE_LAYOUT_GENERAL;
+ out_barrier->oldLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT;
if (out_barrier->newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
- out_barrier->newLayout = VK_IMAGE_LAYOUT_GENERAL;
+ out_barrier->newLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT;
+ return;
+ }
+
+ if (out_barrier->oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
+ out_barrier->oldLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT;
+
+ /* no availability operation needed */
+ out_barrier->srcAccessMask = 0;
+
+ const uint32_t dst_qfi = out_barrier->dstQueueFamilyIndex;
+ if (img->sharing_mode == VK_SHARING_MODE_CONCURRENT) {
+ out_barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
+ out_barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ } else if (dst_qfi == out_barrier->srcQueueFamilyIndex ||
+ dst_qfi == cmd->queue_family_index) {
+ out_barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
+ out_barrier->dstQueueFamilyIndex = cmd->queue_family_index;
+ } else {
+ /* The barrier also defines a queue family ownership transfer, and
+ * this is the one that gets submitted to the source queue family to
+ * release the ownership. Skip both the transfer and the transition.
+ */
+ out_barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ out_barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ out_barrier->newLayout = out_barrier->oldLayout;
+ }
+ } else {
+ out_barrier->newLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT;
+
+ /* no visibility operation needed */
+ out_barrier->dstAccessMask = 0;
+
+ const uint32_t src_qfi = out_barrier->srcQueueFamilyIndex;
+ if (img->sharing_mode == VK_SHARING_MODE_CONCURRENT) {
+ out_barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ out_barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
+ } else if (src_qfi == out_barrier->dstQueueFamilyIndex ||
+ src_qfi == cmd->queue_family_index) {
+ out_barrier->srcQueueFamilyIndex = cmd->queue_family_index;
+ out_barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
+ } else {
+ /* The barrier also defines a queue family ownership transfer, and
+ * this is the one that gets submitted to the destination queue
+ * family to acquire the ownership. Skip both the transfer and the
+ * transition.
+ */
+ out_barrier->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ out_barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ out_barrier->oldLayout = out_barrier->newLayout;
+ }
}
-#endif
}
static const VkImageMemoryBarrier *
vn_cmd_wait_events_fix_image_memory_barriers(
+ struct vn_command_buffer *cmd,
+ const VkImageMemoryBarrier *src_barriers,
+ uint32_t count,
+ uint32_t *out_transfer_count)
+{
+ *out_transfer_count = 0;
+
+ if (cmd->builder.render_pass ||
+ !vn_image_memory_barrier_has_present_src(src_barriers, count))
+ return src_barriers;
+
+ VkImageMemoryBarrier *img_barriers =
+ vn_cmd_get_image_memory_barriers(cmd, count * 2);
+ if (!img_barriers) {
+ cmd->state = VN_COMMAND_BUFFER_STATE_INVALID;
+ return src_barriers;
+ }
+
+ /* vkCmdWaitEvents cannot be used for queue family ownership transfers.
+ * Nothing appears to be said about the submission order of image memory
+ * barriers in the same array. We take the liberty to move queue family
+ * ownership transfers to the tail.
+ */
+ VkImageMemoryBarrier *transfer_barriers = img_barriers + count;
+ uint32_t transfer_count = 0;
+ uint32_t valid_count = 0;
+ for (uint32_t i = 0; i < count; i++) {
+ VkImageMemoryBarrier *img_barrier = &img_barriers[valid_count];
+ vn_cmd_fix_image_memory_barrier(cmd, &src_barriers[i], img_barrier);
+
+ if (VN_PRESENT_SRC_INTERNAL_LAYOUT == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
+ valid_count++;
+ continue;
+ }
+
+ if (img_barrier->srcQueueFamilyIndex ==
+ img_barrier->dstQueueFamilyIndex) {
+ valid_count++;
+ } else {
+ transfer_barriers[transfer_count++] = *img_barrier;
+ }
+ }
+
+ assert(valid_count + transfer_count == count);
+ if (transfer_count) {
+ /* copy back to the tail */
+ memcpy(&img_barriers[valid_count], transfer_barriers,
+ sizeof(*transfer_barriers) * transfer_count);
+ *out_transfer_count = transfer_count;
+ }
+
+ return img_barriers;
+}
+
+static const VkImageMemoryBarrier *
+vn_cmd_pipeline_barrier_fix_image_memory_barriers(
struct vn_command_buffer *cmd,
const VkImageMemoryBarrier *src_barriers,
uint32_t count)
{
- if (!vn_image_memory_barrier_has_present_src(src_barriers, count))
+ if (cmd->builder.render_pass ||
+ !vn_image_memory_barrier_has_present_src(src_barriers, count))
return src_barriers;
VkImageMemoryBarrier *img_barriers =
@@ -92,14 +272,85 @@ vn_cmd_wait_events_fix_image_memory_barriers(
return img_barriers;
}
-static const VkImageMemoryBarrier *
-vn_cmd_pipeline_barrier_fix_image_memory_barriers(
+static void
+vn_cmd_encode_memory_barriers(struct vn_command_buffer *cmd,
+ VkPipelineStageFlags src_stage_mask,
+ VkPipelineStageFlags dst_stage_mask,
+ uint32_t buf_barrier_count,
+ const VkBufferMemoryBarrier *buf_barriers,
+ uint32_t img_barrier_count,
+ const VkImageMemoryBarrier *img_barriers)
+{
+ const VkCommandBuffer cmd_handle = vn_command_buffer_to_handle(cmd);
+
+ const size_t cmd_size = vn_sizeof_vkCmdPipelineBarrier(
+ cmd_handle, src_stage_mask, dst_stage_mask, 0, 0, NULL,
+ buf_barrier_count, buf_barriers, img_barrier_count, img_barriers);
+ if (!vn_cs_encoder_reserve(&cmd->cs, cmd_size)) {
+ cmd->state = VN_COMMAND_BUFFER_STATE_INVALID;
+ return;
+ }
+
+ vn_encode_vkCmdPipelineBarrier(
+ &cmd->cs, 0, cmd_handle, src_stage_mask, dst_stage_mask, 0, 0, NULL,
+ buf_barrier_count, buf_barriers, img_barrier_count, img_barriers);
+}
+
+static void
+vn_present_src_attachment_to_image_memory_barrier(
+ const struct vn_image *img,
+ const struct vn_present_src_attachment *att,
+ VkImageMemoryBarrier *img_barrier)
+{
+ *img_barrier = (VkImageMemoryBarrier)
+ {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .srcAccessMask = att->src_access_mask,
+ .dstAccessMask = att->dst_access_mask,
+ .oldLayout = att->acquire ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
+ : VN_PRESENT_SRC_INTERNAL_LAYOUT,
+ .newLayout = att->acquire ? VN_PRESENT_SRC_INTERNAL_LAYOUT
+ : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ .image = vn_image_to_handle((struct vn_image *)img),
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .levelCount = 1,
+ .layerCount = 1,
+ },
+ };
+}
+
+static void
+vn_cmd_transfer_present_src_images(
struct vn_command_buffer *cmd,
- const VkImageMemoryBarrier *src_barriers,
+ const struct vn_image *const *images,
+ const struct vn_present_src_attachment *atts,
uint32_t count)
{
- return vn_cmd_wait_events_fix_image_memory_barriers(cmd, src_barriers,
- count);
+ VkImageMemoryBarrier *img_barriers =
+ vn_cmd_get_image_memory_barriers(cmd, count);
+ if (!img_barriers) {
+ cmd->state = VN_COMMAND_BUFFER_STATE_INVALID;
+ return;
+ }
+
+ VkPipelineStageFlags src_stage_mask = 0;
+ VkPipelineStageFlags dst_stage_mask = 0;
+ for (uint32_t i = 0; i < count; i++) {
+ src_stage_mask |= atts[i].src_stage_mask;
+ dst_stage_mask |= atts[i].dst_stage_mask;
+
+ vn_present_src_attachment_to_image_memory_barrier(images[i], &atts[i],
+ &img_barriers[i]);
+ vn_cmd_fix_image_memory_barrier(cmd, &img_barriers[i],
+ &img_barriers[i]);
+ }
+
+ if (VN_PRESENT_SRC_INTERNAL_LAYOUT == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
+ return;
+
+ vn_cmd_encode_memory_barriers(cmd, src_stage_mask, dst_stage_mask, 0, NULL,
+ count, img_barriers);
}
static void
@@ -144,6 +395,11 @@ vn_cmd_begin_render_pass(struct vn_command_buffer *cmd,
images[i] = vn_image_view_from_handle(views[index])->image;
}
+ if (pass->acquire_count) {
+ vn_cmd_transfer_present_src_images(
+ cmd, images, pass->present_src_attachments, pass->acquire_count);
+ }
+
cmd->builder.present_src_images = images;
}
@@ -158,7 +414,17 @@ vn_cmd_end_render_pass(struct vn_command_buffer *cmd)
if (!pass->present_src_count || !cmd->builder.present_src_images)
return;
- vk_free(&cmd->allocator, cmd->builder.present_src_images);
+ const struct vn_image **images = cmd->builder.present_src_images;
+ cmd->builder.present_src_images = NULL;
+
+ if (pass->release_count) {
+ vn_cmd_transfer_present_src_images(
+ cmd, images + pass->acquire_count,
+ pass->present_src_attachments + pass->acquire_count,
+ pass->release_count);
+ }
+
+ vk_free(&cmd->allocator, images);
}
/* command pool commands */
@@ -942,6 +1208,16 @@ vn_CmdCopyImageToBuffer(VkCommandBuffer commandBuffer,
vn_command_buffer_from_handle(commandBuffer);
size_t cmd_size;
+ bool prime_blit = false;
+ if (srcImageLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
+ VN_PRESENT_SRC_INTERNAL_LAYOUT != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
+ srcImageLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT;
+
+ const struct vn_image *img = vn_image_from_handle(srcImage);
+ prime_blit = img->is_wsi && img->prime_blit_buffer == dstBuffer;
+ assert(prime_blit);
+ }
+
cmd_size = vn_sizeof_vkCmdCopyImageToBuffer(commandBuffer, srcImage,
srcImageLayout, dstBuffer,
regionCount, pRegions);
@@ -951,6 +1227,20 @@ vn_CmdCopyImageToBuffer(VkCommandBuffer commandBuffer,
vn_encode_vkCmdCopyImageToBuffer(&cmd->cs, 0, commandBuffer, srcImage,
srcImageLayout, dstBuffer, regionCount,
pRegions);
+
+ if (prime_blit) {
+ const VkBufferMemoryBarrier buf_barrier = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .srcQueueFamilyIndex = cmd->queue_family_index,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
+ .buffer = dstBuffer,
+ .size = VK_WHOLE_SIZE,
+ };
+ vn_cmd_encode_memory_barriers(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 1,
+ &buf_barrier, 0, NULL);
+ }
}
void
@@ -1130,8 +1420,10 @@ vn_CmdWaitEvents(VkCommandBuffer commandBuffer,
vn_command_buffer_from_handle(commandBuffer);
size_t cmd_size;
+ uint32_t transfer_count;
pImageMemoryBarriers = vn_cmd_wait_events_fix_image_memory_barriers(
- cmd, pImageMemoryBarriers, imageMemoryBarrierCount);
+ cmd, pImageMemoryBarriers, imageMemoryBarrierCount, &transfer_count);
+ imageMemoryBarrierCount -= transfer_count;
cmd_size = vn_sizeof_vkCmdWaitEvents(
commandBuffer, eventCount, pEvents, srcStageMask, dstStageMask,
@@ -1145,6 +1437,12 @@ vn_CmdWaitEvents(VkCommandBuffer commandBuffer,
pMemoryBarriers, bufferMemoryBarrierCount,
pBufferMemoryBarriers, imageMemoryBarrierCount,
pImageMemoryBarriers);
+
+ if (transfer_count) {
+ pImageMemoryBarriers += imageMemoryBarrierCount;
+ vn_cmd_encode_memory_barriers(cmd, srcStageMask, dstStageMask, 0, NULL,
+ transfer_count, pImageMemoryBarriers);
+ }
}
void
More information about the mesa-commit
mailing list