vulkan-playground/vulkan_texture_utl.h

296 lines
10 KiB
C

/*
* Copyright (c) 2017-2019 Collabora Ltd.
* Copyright (c) 2024 mittorn
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef VULKAN_TEXTURE_UTL_H
#define VULKAN_TEXTURE_UTL_H
#include "vulkan_utl.h"
struct VulkanTexture
{
// keep device for destroy
VkDevice owning_device = NULL;
VkImage image = NULL;
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkDeviceMemory device_memory = NULL;
VkSampler sampler = NULL;
VkImageView view = NULL;
uint32_t width = 0, height = 0;
uint32_t mip_levels = 0;
uint32_t layer_count = 0;
bool created_from_image = false;
static VkAccessFlags GetAccessFlags(VkImageLayout layout)
{
switch (layout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
case VK_IMAGE_LAYOUT_GENERAL: return 0;
case VK_IMAGE_LAYOUT_PREINITIALIZED: return VK_ACCESS_HOST_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
default: printf("Unhandled access mask case for layout %d.\n", layout);
}
return 0;
}
static void SetImageLayout(VkCommandBuffer cmd_buffer, VkImage image, VkImageLayout src_layout, VkImageLayout dst_layout, VkImageSubresourceRange subresource_range)
{
VkImageMemoryBarrier imageMemoryBarrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = GetAccessFlags(src_layout),
.dstAccessMask = GetAccessFlags(dst_layout),
.oldLayout = src_layout,
.newLayout = dst_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = subresource_range,
};
vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL, 0, NULL,
1, &imageMemoryBarrier);
}
VkDescriptorImageInfo GetDescriptor()
{
VkDescriptorImageInfo descriptor = {
.sampler = sampler,
.imageView = view,
.imageLayout = image_layout,
};
return descriptor;
}
void Destroy()
{
if (view)
vkDestroyImageView(owning_device, view, NULL);
view = NULL;
if (!created_from_image && image)
vkDestroyImage(owning_device, image, NULL);
image = NULL;
if (sampler)
vkDestroySampler(owning_device, sampler, NULL);
sampler = NULL;
if (device_memory)
vkFreeMemory(owning_device, device_memory, NULL);
device_memory = 0;
}
void CreateImage(VkDevice device, VkFormat format)
{
VkImageCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = { .width = width, .height = height, .depth = 1 },
.mipLevels = mip_levels,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
};
owning_device = device;
VK_CHECK_RESULT(vkCreateImage(device, &info, NULL, &image));
}
static void AllocateImageMemory(VkImage image,
VulkanDevice dev,
VkMemoryRequirements *mem_reqs,
VkMemoryAllocateInfo *mem_info,
VkDeviceMemory *out_memory)
{
vkGetImageMemoryRequirements(dev.device, image, mem_reqs);
mem_info->allocationSize = mem_reqs->size;
dev.GetMemoryType(mem_reqs->memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_info->memoryTypeIndex);
VK_CHECK_RESULT(vkAllocateMemory(dev.device, mem_info, NULL, out_memory));
VK_CHECK_RESULT(vkBindImageMemory(dev.device, image, *out_memory, 0));
}
void TransferImage(VulkanDevice &dev, VkQueue copy_queue, VkBuffer staging_buffer, VkImageLayout dest_layout, VkBufferImageCopy *buffer_image_copies)
{
VkImageSubresourceRange subresource_range = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = mip_levels,
.layerCount = 1,
};
VkCommandBuffer copy_cmd = dev.CreateCommandBuffer();
SetImageLayout(copy_cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresource_range);
vkCmdCopyBufferToImage(copy_cmd, staging_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
mip_levels, buffer_image_copies);
image_layout = dest_layout;
SetImageLayout(copy_cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dest_layout, subresource_range);
dev.FlushCommandBuffer(copy_cmd, copy_queue);
}
uint8_t *BeginUpload(VulkanDevice &dev, VkMemoryRequirements &mem_reqs, VkMemoryAllocateInfo &mem_info, VkBuffer &staging_buffer, VkDeviceMemory &staging_memory, VkDeviceSize size)
{
VkBufferCreateInfo bufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VK_CHECK_RESULT(vkCreateBuffer(dev.device, &bufferCreateInfo, NULL, &staging_buffer));
vkGetBufferMemoryRequirements(dev.device, staging_buffer, &mem_reqs);
uint32_t type_index;
dev.GetMemoryType(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &type_index);
mem_info = VkMemoryAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = mem_reqs.size,
.memoryTypeIndex = type_index,
};
VK_CHECK_RESULT(vkAllocateMemory(dev.device, &mem_info, NULL, &staging_memory));
VK_CHECK_RESULT(vkBindBufferMemory(dev.device, staging_buffer, staging_memory, 0));
uint8_t *data;
VK_CHECK_RESULT(vkMapMemory(dev.device, staging_memory, 0, mem_reqs.size, 0, (void **)&data));
return data;
}
void EndUpload(VulkanDevice &dev, VkQueue copy_queue, bool alloc_mem, VkImageLayout dest_layout, VkMemoryRequirements &mem_reqs, VkMemoryAllocateInfo &mem_info, VkBuffer &staging_buffer, VkDeviceMemory &staging_memory, unsigned int depth, unsigned int *mip_offsets)
{
VkBufferImageCopy buffer_image_copies[mip_levels];
vkUnmapMemory(dev.device, staging_memory);
unsigned int offset = 0;
if(!mip_offsets)
mip_offsets = &offset;
for (uint32_t i = 0; i < mip_levels; i++) {
buffer_image_copies[i] = VkBufferImageCopy {
.bufferOffset = mip_offsets[i],
.imageSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = i,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageExtent = {
.width = width >> i,
.height = height >> i,
.depth = depth >> i,
},
};
}
if (alloc_mem)
AllocateImageMemory(image, dev, &mem_reqs, &mem_info, &device_memory);
TransferImage(dev, copy_queue, staging_buffer, dest_layout, buffer_image_copies);
vkFreeMemory(dev.device, staging_memory, NULL);
vkDestroyBuffer(dev.device, staging_buffer, NULL);
}
void FromBuffer(VulkanDevice &dev, int w, int h, int mips, VkFormat format, VkQueue copy_queue, VkImageLayout dest_layout, uint8_t *buffer, VkDeviceSize size)
{
VkMemoryRequirements mem_reqs;
VkMemoryAllocateInfo mem_info;
VkBuffer staging_buffer;
VkDeviceMemory staging_memory;
width = w;
height = h;
mip_levels = mips;
CreateImage(dev.device, format);
uint8_t *data = BeginUpload(dev, mem_reqs, mem_info,staging_buffer, staging_memory, size);
memcpy(data, buffer, size);
EndUpload(dev, copy_queue, true, dest_layout, mem_reqs, mem_info, staging_buffer, staging_memory, 1, NULL);
CreateSampler(dev.device);
CreateImageView(dev.device, format);
created_from_image = false;
}
void CreateSampler(VkDevice device)
{
VkSamplerCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mipLodBias = 0.0f,
.anisotropyEnable = VK_TRUE,
.maxAnisotropy = 8,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 0.0f,
.maxLod = (float)mip_levels,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
};
owning_device = device;
VK_CHECK_RESULT(vkCreateSampler(device, &info, NULL, &sampler));
}
void CreateImageView(VkDevice device, VkFormat format)
{
VkImageViewCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = 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 = mip_levels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
owning_device = device;
VK_CHECK_RESULT(vkCreateImageView(device, &info, NULL, &view));
}
};
#endif // VULKAN_TEXTURE_UTL_H