gtk/gsk/vulkan/gskvulkanimage.c
Benjamin Otte 9836389fde vulkan: Repurpose debug flags for image uploads
Now that we don't use the old environment variables anymore to force
staging buffer/image uploads, we don't need them.

However, we do autodetect the fast path for avoiding a staging buffer
now, and we might want to be able to turn that off for testing.

So add GSK_DEBUG=staging that does exactly that.
2023-06-14 03:34:07 +02:00

741 lines
27 KiB
C

#include "config.h"
#include "gskvulkanimageprivate.h"
#include "gskvulkanbufferprivate.h"
#include "gskvulkanmemoryprivate.h"
#include "gskvulkanpipelineprivate.h"
#include <string.h>
struct _GskVulkanUploader
{
GdkVulkanContext *vulkan;
GskVulkanCommandPool *command_pool;
GArray *before_buffer_barriers;
GArray *before_image_barriers;
VkCommandBuffer copy_buffer;
GArray *after_buffer_barriers;
GArray *after_image_barriers;
GSList *staging_image_free_list;
GSList *staging_buffer_free_list;
};
struct _GskVulkanImage
{
GObject parent_instance;
GdkVulkanContext *vulkan;
gsize width;
gsize height;
VkImageUsageFlags vk_usage;
VkImage vk_image;
VkImageView vk_image_view;
VkImageLayout vk_image_layout;
VkAccessFlags vk_access;
GskVulkanMemory *memory;
};
G_DEFINE_TYPE (GskVulkanImage, gsk_vulkan_image, G_TYPE_OBJECT)
GskVulkanUploader *
gsk_vulkan_uploader_new (GdkVulkanContext *context,
GskVulkanCommandPool *command_pool)
{
GskVulkanUploader *self;
self = g_new0 (GskVulkanUploader, 1);
self->vulkan = g_object_ref (context);
self->command_pool = command_pool;
self->before_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
self->after_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
self->before_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
self->after_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
return self;
}
void
gsk_vulkan_uploader_free (GskVulkanUploader *self)
{
gsk_vulkan_uploader_reset (self);
g_array_unref (self->after_buffer_barriers);
g_array_unref (self->before_buffer_barriers);
g_array_unref (self->after_image_barriers);
g_array_unref (self->before_image_barriers);
g_object_unref (self->vulkan);
g_free (self);
}
static void
gsk_vulkan_uploader_add_image_barrier (GskVulkanUploader *self,
gboolean after,
GskVulkanImage *image,
VkImageLayout new_layout,
VkAccessFlags new_access)
{
GArray *array;
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = image->vk_access,
.dstAccessMask = new_access,
.oldLayout = image->vk_image_layout,
.newLayout = new_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};
if (after)
array = self->after_image_barriers;
else
array = self->before_image_barriers;
g_array_append_val (array, barrier);
image->vk_image_layout = new_layout;
image->vk_access = new_access;
}
static void
gsk_vulkan_uploader_add_buffer_barrier (GskVulkanUploader *self,
gboolean after,
const VkBufferMemoryBarrier *barrier)
{
GArray *array;
if (after)
array = self->after_buffer_barriers;
else
array = self->before_buffer_barriers;
g_array_append_val (array, *barrier);
}
static VkCommandBuffer
gsk_vulkan_uploader_get_copy_buffer (GskVulkanUploader *self)
{
if (self->copy_buffer == VK_NULL_HANDLE)
self->copy_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
return self->copy_buffer;
}
void
gsk_vulkan_uploader_upload (GskVulkanUploader *self)
{
VkPipelineStageFlagBits host_and_transfer_bits = VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
if (self->before_buffer_barriers->len > 0 || self->before_image_barriers->len > 0)
{
VkCommandBuffer command_buffer;
command_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
vkCmdPipelineBarrier (command_buffer,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | host_and_transfer_bits,
VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, NULL,
self->before_buffer_barriers->len, (VkBufferMemoryBarrier *) self->before_buffer_barriers->data,
self->before_image_barriers->len, (VkImageMemoryBarrier *) self->before_image_barriers->data);
gsk_vulkan_command_pool_submit_buffer (self->command_pool, command_buffer, 0, NULL, 0, NULL, VK_NULL_HANDLE);
g_array_set_size (self->before_buffer_barriers, 0);
g_array_set_size (self->before_image_barriers, 0);
}
/* append these to existing buffer */
if (self->after_buffer_barriers->len > 0 || self->after_image_barriers->len > 0)
{
VkCommandBuffer command_buffer = gsk_vulkan_uploader_get_copy_buffer (self);
vkCmdPipelineBarrier (command_buffer,
host_and_transfer_bits,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0, NULL,
self->after_buffer_barriers->len, (VkBufferMemoryBarrier *) self->after_buffer_barriers->data,
self->after_image_barriers->len, (VkImageMemoryBarrier *) self->after_image_barriers->data);
g_array_set_size (self->after_buffer_barriers, 0);
g_array_set_size (self->after_image_barriers, 0);
}
if (self->copy_buffer != VK_NULL_HANDLE)
{
gsk_vulkan_command_pool_submit_buffer (self->command_pool, self->copy_buffer, 0, NULL, 0, NULL, VK_NULL_HANDLE);
self->copy_buffer = VK_NULL_HANDLE;
}
}
void
gsk_vulkan_uploader_reset (GskVulkanUploader *self)
{
g_array_set_size (self->before_image_barriers, 0);
self->copy_buffer = VK_NULL_HANDLE;
g_array_set_size (self->after_image_barriers, 0);
g_slist_free_full (self->staging_image_free_list, g_object_unref);
self->staging_image_free_list = NULL;
g_slist_free_full (self->staging_buffer_free_list, (GDestroyNotify) gsk_vulkan_buffer_free);
self->staging_buffer_free_list = NULL;
}
static GskVulkanImage *
gsk_vulkan_image_new (GdkVulkanContext *context,
gsize width,
gsize height,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkImageLayout layout,
VkAccessFlags access,
VkMemoryPropertyFlags memory)
{
VkMemoryRequirements requirements;
GskVulkanImage *self;
g_assert (width > 0 && height > 0);
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->vulkan = g_object_ref (context);
self->width = width;
self->height = height;
self->vk_usage = usage;
self->vk_image_layout = layout;
self->vk_access = access;
GSK_VK_CHECK (vkCreateImage, gdk_vulkan_context_get_device (context),
&(VkImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = VK_FORMAT_B8G8R8A8_UNORM,
.extent = { width, height, 1 },
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = tiling,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = self->vk_image_layout,
},
NULL,
&self->vk_image);
vkGetImageMemoryRequirements (gdk_vulkan_context_get_device (context),
self->vk_image,
&requirements);
self->memory = gsk_vulkan_memory_new (context,
requirements.memoryTypeBits,
memory,
requirements.size);
GSK_VK_CHECK (vkBindImageMemory, gdk_vulkan_context_get_device (context),
self->vk_image,
gsk_vulkan_memory_get_device_memory (self->memory),
0);
return self;
}
static void
gsk_vulkan_image_ensure_view (GskVulkanImage *self,
VkFormat format)
{
if (self->vk_image_view == VK_NULL_HANDLE)
GSK_VK_CHECK (vkCreateImageView, gdk_vulkan_context_get_device (self->vulkan),
&(VkImageViewCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = self->vk_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.components = {
.r = VK_COMPONENT_SWIZZLE_R,
.g = VK_COMPONENT_SWIZZLE_G,
.b = VK_COMPONENT_SWIZZLE_B,
.a = VK_COMPONENT_SWIZZLE_A,
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
NULL,
&self->vk_image_view);
}
GskVulkanImage *
gsk_vulkan_image_new_from_texture (GskVulkanUploader *uploader,
GdkTexture *texture)
{
GdkTextureDownloader *downloader;
GskVulkanImage *result;
GskVulkanImageMap map;
downloader = gdk_texture_downloader_new (texture);
result = gsk_vulkan_image_new_for_upload (uploader,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
gsk_vulkan_image_map_memory (result, uploader, &map);
gdk_texture_downloader_download_into (downloader, map.data, map.stride);
gsk_vulkan_image_unmap_memory (result, uploader, &map);
gdk_texture_downloader_free (downloader);
return result;
}
GskVulkanImage *
gsk_vulkan_image_new_for_upload (GskVulkanUploader *uploader,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (uploader->vulkan,
width,
height,
VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
return self;
}
static void
gsk_vulkan_image_map_memory_direct (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanImageMap *map)
{
VkImageSubresource image_res;
VkSubresourceLayout image_layout;
image_res.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_res.mipLevel = 0;
image_res.arrayLayer = 0;
vkGetImageSubresourceLayout (gdk_vulkan_context_get_device (self->vulkan),
self->vk_image, &image_res, &image_layout);
map->staging_buffer = NULL;
map->data = gsk_vulkan_memory_map (self->memory) + image_layout.offset;
map->stride = image_layout.rowPitch;
}
static void
gsk_vulkan_image_unmap_memory_direct (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanImageMap *map)
{
gsk_vulkan_memory_unmap (self->memory);
gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE,
self,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT);
}
static void
gsk_vulkan_image_map_memory_indirect (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanImageMap *map)
{
gsize buffer_size = self->width * self->height * 4;
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
gsk_vulkan_image_unmap_memory_indirect (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanImageMap *map)
{
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,
});
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
}
}
});
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,
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);
else
gsk_vulkan_image_map_memory_indirect (self, uploader, map);
}
void
gsk_vulkan_image_unmap_memory (GskVulkanImage *self,
GskVulkanUploader *uploader,
GskVulkanImageMap *map)
{
if (map->staging_buffer)
gsk_vulkan_image_unmap_memory_indirect (self, uploader, map);
else
gsk_vulkan_image_unmap_memory_direct (self, uploader, map);
}
GskVulkanImage *
gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
VkImage image,
VkFormat format,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->vulkan = g_object_ref (context);
self->width = width;
self->height = height;
self->vk_image = image;
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
return self;
}
GskVulkanImage *
gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (context,
width,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
return self;
}
GskVulkanImage *
gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (context,
width,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
0,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
return self;
}
GskVulkanImage *
gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (context,
width,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
return self;
}
GdkTexture *
gsk_vulkan_image_download (GskVulkanImage *self,
GskVulkanUploader *uploader)
{
GskVulkanBuffer *buffer;
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);
texture = gdk_memory_texture_new (self->width, self->height,
GDK_MEMORY_DEFAULT,
bytes,
self->width * 4);
gsk_vulkan_buffer_unmap (buffer);
gsk_vulkan_buffer_free (buffer);
return texture;
}
void
gsk_vulkan_image_upload_regions (GskVulkanImage *self,
GskVulkanUploader *uploader,
guint num_regions,
GskImageRegion *regions)
{
GskVulkanBuffer *staging;
guchar *mem;
guchar *m;
gsize size;
gsize offset;
VkBufferImageCopy *bufferImageCopy;
size = 0;
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);
mem = gsk_vulkan_buffer_map (staging);
bufferImageCopy = alloca (sizeof (VkBufferImageCopy) * num_regions);
memset (bufferImageCopy, 0, sizeof (VkBufferImageCopy) * num_regions);
offset = 0;
for (int i = 0; i < num_regions; i++)
{
m = mem + offset;
if (regions[i].stride == regions[i].width * 4)
{
memcpy (m, regions[i].data, regions[i].stride * regions[i].height);
}
else
{
for (gsize r = 0; r < regions[i].height; r++)
memcpy (m + r * regions[i].width * 4, regions[i].data + r * regions[i].stride, regions[i].width * 4);
}
bufferImageCopy[i].bufferOffset = offset;
bufferImageCopy[i].bufferRowLength = regions[i].width;
bufferImageCopy[i].bufferImageHeight = regions[i].height;
bufferImageCopy[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
bufferImageCopy[i].imageSubresource.mipLevel = 0;
bufferImageCopy[i].imageSubresource.baseArrayLayer = 0;
bufferImageCopy[i].imageSubresource.layerCount = 1;
bufferImageCopy[i].imageOffset.x = regions[i].x;
bufferImageCopy[i].imageOffset.y = regions[i].y;
bufferImageCopy[i].imageOffset.z = 0;
bufferImageCopy[i].imageExtent.width = regions[i].width;
bufferImageCopy[i].imageExtent.height = regions[i].height;
bufferImageCopy[i].imageExtent.depth = 1;
offset += regions[i].width * regions[i].height * 4;
}
gsk_vulkan_buffer_unmap (staging);
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 (staging),
self->vk_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
num_regions,
bufferImageCopy);
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, staging);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
}
static void
gsk_vulkan_image_finalize (GObject *object)
{
GskVulkanImage *self = GSK_VULKAN_IMAGE (object);
if (self->vk_image_view != VK_NULL_HANDLE)
{
vkDestroyImageView (gdk_vulkan_context_get_device (self->vulkan),
self->vk_image_view,
NULL);
}
/* memory is NULL for for_swapchain() images, where we don't own
* the VkImage */
if (self->memory)
{
vkDestroyImage (gdk_vulkan_context_get_device (self->vulkan),
self->vk_image,
NULL);
gsk_vulkan_memory_free (self->memory);
}
g_object_unref (self->vulkan);
G_OBJECT_CLASS (gsk_vulkan_image_parent_class)->finalize (object);
}
static void
gsk_vulkan_image_class_init (GskVulkanImageClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_image_finalize;
}
static void
gsk_vulkan_image_init (GskVulkanImage *self)
{
}
gsize
gsk_vulkan_image_get_width (GskVulkanImage *self)
{
return self->width;
}
gsize
gsk_vulkan_image_get_height (GskVulkanImage *self)
{
return self->height;
}
VkImage
gsk_vulkan_image_get_image (GskVulkanImage *self)
{
return self->vk_image;
}
VkImageView
gsk_vulkan_image_get_image_view (GskVulkanImage *self)
{
return self->vk_image_view;
}