vulkan: Allow mapping images as "read" and/or "write"

This way, we unify the code paths for memory access to textures.

We also technically gain the ability to modify images, though I have no
use case for this.
This commit is contained in:
Benjamin Otte 2023-06-15 14:29:15 +02:00
parent 7b4846bc25
commit ba28971a18
5 changed files with 146 additions and 111 deletions

View File

@ -72,18 +72,16 @@ gsk_vulkan_buffer_new_storage (GdkVulkanContext *context,
}
GskVulkanBuffer *
gsk_vulkan_buffer_new_staging (GdkVulkanContext *context,
gsize size)
gsk_vulkan_buffer_new_map (GdkVulkanContext *context,
gsize size,
GskVulkanMapMode mode)
{
return gsk_vulkan_buffer_new_internal (context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
return gsk_vulkan_buffer_new_internal (context,
size,
(mode & GSK_VULKAN_READ ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : 0) |
(mode & GSK_VULKAN_WRITE ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0));
}
GskVulkanBuffer *
gsk_vulkan_buffer_new_download (GdkVulkanContext *context,
gsize size)
{
return gsk_vulkan_buffer_new_internal (context, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
}
void
gsk_vulkan_buffer_free (GskVulkanBuffer *self)
{

View File

@ -6,14 +6,20 @@ G_BEGIN_DECLS
typedef struct _GskVulkanBuffer GskVulkanBuffer;
typedef enum
{
GSK_VULKAN_READ = (1 << 0),
GSK_VULKAN_WRITE = (1 << 1),
GSK_VULKAN_READWRITE = GSK_VULKAN_READ | GSK_VULKAN_WRITE
} GskVulkanMapMode;
GskVulkanBuffer * gsk_vulkan_buffer_new (GdkVulkanContext *context,
gsize size);
GskVulkanBuffer * gsk_vulkan_buffer_new_storage (GdkVulkanContext *context,
gsize size);
GskVulkanBuffer * gsk_vulkan_buffer_new_staging (GdkVulkanContext *context,
gsize size);
GskVulkanBuffer * gsk_vulkan_buffer_new_download (GdkVulkanContext *context,
gsize size);
GskVulkanBuffer * gsk_vulkan_buffer_new_map (GdkVulkanContext *context,
gsize size,
GskVulkanMapMode mode);
void gsk_vulkan_buffer_free (GskVulkanBuffer *buffer);
VkBuffer gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self);

View File

@ -6,6 +6,8 @@
#include "gskvulkanmemoryprivate.h"
#include "gskvulkanpipelineprivate.h"
#include "gdk/gdkmemoryformatprivate.h"
#include <string.h>
struct _GskVulkanUploader
@ -556,7 +558,7 @@ gsk_vulkan_image_new_from_texture (GskVulkanUploader *uploader,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
gdk_texture_downloader_set_format (downloader, result->format);
gsk_vulkan_image_map_memory (result, uploader, &map);
gsk_vulkan_image_map_memory (result, uploader, GSK_VULKAN_WRITE, &map);
gdk_texture_downloader_download_into (downloader, map.data, map.stride);
gsk_vulkan_image_unmap_memory (result, uploader, &map);
gdk_texture_downloader_free (downloader);
@ -588,11 +590,28 @@ gsk_vulkan_image_new_for_upload (GskVulkanUploader *uploader,
static void
gsk_vulkan_image_map_memory_direct (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanMapMode mode,
GskVulkanImageMap *map)
{
VkImageSubresource image_res;
VkSubresourceLayout image_layout;
if (self->vk_image_layout != VK_IMAGE_LAYOUT_PREINITIALIZED)
{
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
self,
VK_IMAGE_LAYOUT_GENERAL,
(mode & GSK_VULKAN_READ ? VK_ACCESS_MEMORY_READ_BIT : 0) |
(mode & GSK_VULKAN_WRITE ? VK_ACCESS_MEMORY_WRITE_BIT : 0));
if (mode & GSK_VULKAN_READ)
{
gsk_vulkan_uploader_upload (uploader);
GSK_VK_CHECK (vkQueueWaitIdle, gdk_vulkan_context_get_queue (self->vulkan));
}
}
image_res.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_res.mipLevel = 0;
image_res.arrayLayer = 0;
@ -600,6 +619,7 @@ gsk_vulkan_image_map_memory_direct (GskVulkanImage *self,
vkGetImageSubresourceLayout (gdk_vulkan_context_get_device (self->vulkan),
self->vk_image, &image_res, &image_layout);
map->mode = mode;
map->staging_buffer = NULL;
map->data = gsk_vulkan_memory_map (self->memory) + image_layout.offset;
map->stride = image_layout.rowPitch;
@ -622,13 +642,52 @@ gsk_vulkan_image_unmap_memory_direct (GskVulkanImage *self,
static void
gsk_vulkan_image_map_memory_indirect (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanMapMode mode,
GskVulkanImageMap *map)
{
gsize buffer_size = self->width * self->height * 4;
map->mode = mode;
map->stride = self->width * gdk_memory_format_bytes_per_pixel (self->format);
map->staging_buffer = gsk_vulkan_buffer_new_map (uploader->vulkan, self->height * map->stride, mode);
if (self->vk_image_layout != VK_IMAGE_LAYOUT_PREINITIALIZED)
{
if (mode & GSK_VULKAN_READ)
{
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
self,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT);
vkCmdCopyImageToBuffer (gsk_vulkan_uploader_get_copy_buffer (uploader),
self->vk_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
gsk_vulkan_buffer_get_buffer (map->staging_buffer),
1,
(VkBufferImageCopy[1]) {
{
.bufferOffset = 0,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = { 0, 0, 0 },
.imageExtent = {
.width = self->width,
.height = self->height,
.depth = 1
}
}
});
gsk_vulkan_uploader_upload (uploader);
GSK_VK_CHECK (vkQueueWaitIdle, gdk_vulkan_context_get_queue (self->vulkan));
}
}
map->staging_buffer = gsk_vulkan_buffer_new_staging (uploader->vulkan, buffer_size);
map->data = gsk_vulkan_buffer_map (map->staging_buffer);
map->stride = self->width * 4;
}
static void
@ -638,70 +697,75 @@ gsk_vulkan_image_unmap_memory_indirect (GskVulkanImage *self,
{
gsk_vulkan_buffer_unmap (map->staging_buffer);
gsk_vulkan_uploader_add_buffer_barrier (uploader,
FALSE,
&(VkBufferMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = gsk_vulkan_buffer_get_buffer (map->staging_buffer),
.offset = 0,
.size = VK_WHOLE_SIZE,
});
if (map->mode & GSK_VULKAN_WRITE)
{
gsk_vulkan_uploader_add_buffer_barrier (uploader,
FALSE,
&(VkBufferMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = gsk_vulkan_buffer_get_buffer (map->staging_buffer),
.offset = 0,
.size = VK_WHOLE_SIZE,
});
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
self,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT);
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
self,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT);
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
gsk_vulkan_buffer_get_buffer (map->staging_buffer),
self->vk_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
(VkBufferImageCopy[1]) {
{
.bufferOffset = 0,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = { 0, 0, 0 },
.imageExtent = {
.width = self->width,
.height = self->height,
.depth = 1
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
gsk_vulkan_buffer_get_buffer (map->staging_buffer),
self->vk_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
(VkBufferImageCopy[1]) {
{
.bufferOffset = 0,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = { 0, 0, 0 },
.imageExtent = {
.width = self->width,
.height = self->height,
.depth = 1
}
}
}
});
});
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list,
map->staging_buffer);
}
else
{
gsk_vulkan_buffer_free (map->staging_buffer);
}
gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE,
self,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT);
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list,
map->staging_buffer);
}
void
gsk_vulkan_image_map_memory (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanMapMode mode,
GskVulkanImageMap *map)
{
g_assert (self->vk_image_layout == VK_IMAGE_LAYOUT_UNDEFINED ||
self->vk_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED);
if (!GSK_DEBUG_CHECK (STAGING) && gsk_vulkan_memory_can_map (self->memory, TRUE))
gsk_vulkan_image_map_memory_direct (self, uploader, map);
gsk_vulkan_image_map_memory_direct (self, uploader, mode, map);
else
gsk_vulkan_image_map_memory_indirect (self, uploader, map);
gsk_vulkan_image_map_memory_indirect (self, uploader, mode, map);
}
void
@ -776,7 +840,7 @@ gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
preferred_format,
width,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
@ -791,54 +855,18 @@ GdkTexture *
gsk_vulkan_image_download (GskVulkanImage *self,
GskVulkanUploader *uploader)
{
GskVulkanBuffer *buffer;
GskVulkanImageMap map;
GdkTexture *texture;
GBytes *bytes;
guchar *mem;
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
self,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT);
buffer = gsk_vulkan_buffer_new_download (self->vulkan, self->width * self->height * 4);
vkCmdCopyImageToBuffer (gsk_vulkan_uploader_get_copy_buffer (uploader),
self->vk_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
gsk_vulkan_buffer_get_buffer (buffer),
1,
(VkBufferImageCopy[1]) {
{
.bufferOffset = 0,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = { 0, 0, 0 },
.imageExtent = {
.width = self->width,
.height = self->height,
.depth = 1
}
}
});
gsk_vulkan_uploader_upload (uploader);
GSK_VK_CHECK (vkQueueWaitIdle, gdk_vulkan_context_get_queue (self->vulkan));
mem = gsk_vulkan_buffer_map (buffer);
bytes = g_bytes_new (mem, self->width * self->height * 4);
gsk_vulkan_image_map_memory (self, uploader, GSK_VULKAN_READ, &map);
bytes = g_bytes_new (map.data, map.stride * self->height);
texture = gdk_memory_texture_new (self->width, self->height,
self->format,
bytes,
self->width * 4);
gsk_vulkan_buffer_unmap (buffer);
gsk_vulkan_buffer_free (buffer);
map.stride);
g_bytes_unref (bytes);
gsk_vulkan_image_unmap_memory (self, uploader, &map);
return texture;
}
@ -860,7 +888,7 @@ gsk_vulkan_image_upload_regions (GskVulkanImage *self,
for (int i = 0; i < num_regions; i++)
size += regions[i].width * regions[i].height * 4;
staging = gsk_vulkan_buffer_new_staging (uploader->vulkan, size);
staging = gsk_vulkan_buffer_new_map (uploader->vulkan, size, GSK_VULKAN_WRITE);
mem = gsk_vulkan_buffer_map (staging);
bufferImageCopy = alloca (sizeof (VkBufferImageCopy) * num_regions);

View File

@ -2,6 +2,7 @@
#include <gdk/gdk.h>
#include "gskvulkanbufferprivate.h"
#include "gskvulkancommandpoolprivate.h"
G_BEGIN_DECLS
@ -56,10 +57,11 @@ typedef struct _GskVulkanImageMap GskVulkanImageMap;
struct _GskVulkanImageMap
{
guchar *data;
gsize stride;
gsize stride;
GskVulkanMapMode mode;
/* private */
gpointer staging_buffer;
GskVulkanBuffer *staging_buffer;
};
GskVulkanImage * gsk_vulkan_image_new_for_upload (GskVulkanUploader *uploader,
@ -68,6 +70,7 @@ GskVulkanImage * gsk_vulkan_image_new_for_upload (GskVulk
gsize height);
void gsk_vulkan_image_map_memory (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanMapMode mode,
GskVulkanImageMap *map);
void gsk_vulkan_image_unmap_memory (GskVulkanImage *self,
GskVulkanUploader *uploader,

View File

@ -1370,7 +1370,7 @@ gsk_vulkan_render_pass_get_node_as_texture (GskVulkanRenderPass *self,
height = ceil (node->bounds.size.height * graphene_vec2_get_y (scale));
result = gsk_vulkan_image_new_for_upload (uploader, GDK_MEMORY_DEFAULT, width, height);
gsk_vulkan_image_map_memory (result, uploader, &map);
gsk_vulkan_image_map_memory (result, uploader, GSK_VULKAN_WRITE, &map);
surface = cairo_image_surface_create_for_data (map.data,
CAIRO_FORMAT_ARGB32,
width, height,
@ -1431,7 +1431,7 @@ gsk_vulkan_render_pass_upload_fallback (GskVulkanRenderPass *self,
height = ceil (node->bounds.size.height * graphene_vec2_get_y (&self->scale));
op->source = gsk_vulkan_image_new_for_upload (uploader, GDK_MEMORY_DEFAULT, width, height);
gsk_vulkan_image_map_memory (op->source, uploader, &map);
gsk_vulkan_image_map_memory (op->source, uploader, GSK_VULKAN_WRITE, &map);
surface = cairo_image_surface_create_for_data (map.data,
CAIRO_FORMAT_ARGB32,
width, height,