296 lines
10 KiB
C
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
|