]> git.tdb.fi Git - libs/gl.git/blobdiff - source/backends/vulkan/texture_backend.cpp
Convert RGB pixel data to RGBA when staging
[libs/gl.git] / source / backends / vulkan / texture_backend.cpp
index ff1afad9b1130933136f4970992f700ce1245765..dd19c63664b88658fe06917798742bbcbb4fd2f5 100644 (file)
@@ -1,5 +1,6 @@
 #include "device.h"
 #include "error.h"
+#include "synchronizer.h"
 #include "texture.h"
 #include "texture_backend.h"
 #include "vulkan.h"
@@ -18,17 +19,23 @@ VulkanTexture::VulkanTexture(VulkanTexture &&other):
        device(other.device),
        handle(other.handle),
        view_handle(other.view_handle),
+       memory_id(other.memory_id),
        view_type(other.view_type),
        debug_name(move(other.debug_name))
 {
        other.handle = 0;
        other.view_handle = 0;
+       other.memory_id = 0;
 }
 
 VulkanTexture::~VulkanTexture()
 {
+       DestroyQueue &dq = device.get_destroy_queue();
+
        if(view_handle)
-               device.get_destroy_queue().destroy(view_handle);
+               dq.destroy(view_handle);
+       if(handle)
+               dq.destroy(handle, memory_id);
 }
 
 void VulkanTexture::allocate()
@@ -36,14 +43,44 @@ void VulkanTexture::allocate()
        const Texture &self = *static_cast<const Texture *>(this);
        const VulkanFunctions &vk = device.get_functions();
 
+       VkImageCreateInfo image_info = { };
+       image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+       image_info.format = static_cast<VkFormat>(get_vulkan_pixelformat(self.storage_fmt));
+       image_info.extent.width = 1;
+       image_info.extent.height = 1;
+       image_info.extent.depth = 1;
+       image_info.mipLevels = 1;
+       image_info.arrayLayers = 1;
+       image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+       image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+       image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+       image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+       image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;
+       PixelComponents comp = get_components(self.storage_fmt);
+       if(comp==DEPTH_COMPONENT || comp==STENCIL_INDEX)
+               image_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+       else
+               image_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+       fill_image_info(&image_info);
+
+       /* SwapChainTexture may have already provided the image.  Create_info is
+       filled anyway because some of its fields are used for view_info. */
        if(!handle)
-               throw logic_error("Texture image allocation is unimplemented");
+       {
+               vk.CreateImage(image_info, handle);
+               memory_id = device.get_allocator().allocate(handle, DEVICE_MEMORY);
+
+               // Trigger a layout transition if the image is used before uploading data.
+               change_layout(0, -1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true);
+       }
 
        VkImageViewCreateInfo view_info = { };
        view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
        view_info.image = handle_cast<::VkImage>(handle);
        view_info.viewType = static_cast<VkImageViewType>(view_type);
-       view_info.format = static_cast<VkFormat>(get_vulkan_pixelformat(self.storage_fmt));
+       view_info.format = image_info.format;
 
        const unsigned *swizzle_order = get_vulkan_swizzle(self.swizzle);
        view_info.components.r = static_cast<VkComponentSwizzle>(swizzle_order[0]);
@@ -53,9 +90,9 @@ void VulkanTexture::allocate()
 
        view_info.subresourceRange.aspectMask = get_vulkan_aspect(get_components(self.storage_fmt));
        view_info.subresourceRange.baseMipLevel = 0;
-       view_info.subresourceRange.levelCount = 1;
+       view_info.subresourceRange.levelCount = image_info.mipLevels;
        view_info.subresourceRange.baseArrayLayer = 0;
-       view_info.subresourceRange.layerCount = 1;
+       view_info.subresourceRange.layerCount = image_info.arrayLayers;
 
        vk.CreateImageView(view_info, view_handle);
 
@@ -63,9 +100,78 @@ void VulkanTexture::allocate()
                set_vulkan_object_names();
 }
 
-void VulkanTexture::generate_mipmap()
+void VulkanTexture::stage_pixels(void *staging, const void *data, size_t count)
+{
+       const Texture &self = *static_cast<const Texture *>(this);
+
+       if(self.swizzle==RGBA_TO_RGB)
+       {
+               const uint32_t *src = static_cast<const uint32_t *>(data);
+               uint32_t *dst = static_cast<uint32_t *>(staging);
+               size_t i = 0;
+               for(; i+3<count; i+=4)
+               {
+                       dst[0] = src[0]|0xFF000000;
+                       dst[1] = (src[0]>>24)|(src[1]<<8)|0xFF000000;
+                       dst[2] = (src[1]>>16)|(src[2]<<16)|0xFF000000;
+                       dst[3] = (src[2]>>8)|0xFF000000;
+                       src += 3;
+                       dst += 4;
+               }
+
+               if(i<count)
+               {
+                       const uint8_t *src_bytes = reinterpret_cast<const uint8_t *>(src);
+                       for(; i<count; ++i)
+                       {
+                               *dst++ = src_bytes[0]|(src_bytes[1]<<8)|(src_bytes[2]<<16)|0xFF000000;
+                               src_bytes += 3;
+                       }
+               }
+       }
+       else
+       {
+               const char *src = static_cast<const char *>(data);
+               size_t data_size = count*get_pixel_size(self.storage_fmt);
+               copy(src, src+data_size, static_cast<char *>(staging));
+       }
+}
+
+void VulkanTexture::generate_mipmap_levels(unsigned n_levels)
 {
-       throw logic_error("VulkanTexture::generate_mipmap is unimplemented");
+       TransferQueue &tq = device.get_transfer_queue();
+       for(unsigned i=0; i+1<n_levels; ++i)
+       {
+               tq.prepare_transfer(this, true, 0,
+                       [this, n_levels, i](){
+                               change_layout((i==0 ? n_levels : 0), i, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, false);
+                               change_layout(0, i+1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true);
+                       },
+                       [this, i](VkCommandBuffer cmd_buf, VkBuffer, size_t){
+                               const VulkanFunctions &vk = device.get_functions();
+
+                               VkImageBlit region = { };
+                               region.srcSubresource.aspectMask = get_vulkan_aspect(get_components(static_cast<const Texture *>(this)->storage_fmt));
+                               region.srcSubresource.mipLevel = i;
+                               region.srcSubresource.baseArrayLayer = 0;
+                               region.srcSubresource.layerCount = 1;
+                               region.dstSubresource = region.srcSubresource;
+                               ++region.dstSubresource.mipLevel;
+
+                               fill_mipmap_blit(i, &region);
+
+                               vk.CmdBlitImage(cmd_buf, handle, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                                       1, &region, VK_FILTER_LINEAR);
+                       });
+       }
+}
+
+void VulkanTexture::change_layout(unsigned n_levels, int level, unsigned layout, bool discard) const
+{
+       unsigned aspect = get_vulkan_aspect(get_components(static_cast<const Texture *>(this)->storage_fmt));
+       if(n_levels>0)
+               device.get_synchronizer().split_image_mipmap(handle, aspect, n_levels);
+       device.get_synchronizer().change_image_layout(handle, aspect, level, layout, discard);
 }
 
 void VulkanTexture::set_debug_name(const string &name)
@@ -84,9 +190,14 @@ void VulkanTexture::set_vulkan_object_names() const
 #ifdef DEBUG
        const VulkanFunctions &vk = device.get_functions();
 
-       string view_name = debug_name+"/view";
        VkDebugUtilsObjectNameInfoEXT name_info = { };
        name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
+       name_info.objectType = VK_OBJECT_TYPE_IMAGE;
+       name_info.objectHandle = reinterpret_cast<uint64_t>(handle);
+       name_info.pObjectName = debug_name.c_str();
+       vk.SetDebugUtilsObjectName(name_info);
+
+       string view_name = debug_name+"/view";
        name_info.objectType = VK_OBJECT_TYPE_IMAGE_VIEW;
        name_info.objectHandle = reinterpret_cast<uint64_t>(view_handle);
        name_info.pObjectName = view_name.c_str();