vulkan: Allow uploading in different formats
authorBenjamin Otte <otte@redhat.com>
Wed, 14 Jun 2023 18:37:58 +0000 (20:37 +0200)
committerBenjamin Otte <otte@redhat.com>
Mon, 19 Jun 2023 13:08:00 +0000 (15:08 +0200)
This requires quite some code because Vulkan may not support all the
formats and then we need to detect that and fallback properly.

gsk/vulkan/gskvulkanimage.c
gsk/vulkan/gskvulkanimageprivate.h
gsk/vulkan/gskvulkanrenderpass.c

index 64d5512733ffb5b957ec1d5b2aabd18c8c3ecfac..42bb3ff653f4393a2324d38c3a88a77c1835f4fc 100644 (file)
@@ -30,6 +30,7 @@ struct _GskVulkanImage
 
   GdkVulkanContext *vulkan;
 
+  GdkMemoryFormat format;
   gsize width;
   gsize height;
   VkImageUsageFlags vk_usage;
@@ -196,22 +197,261 @@ gsk_vulkan_uploader_reset (GskVulkanUploader *self)
   self->staging_buffer_free_list = NULL;
 }
 
+typedef struct _GskMemoryFormatInfo GskMemoryFormatInfo;
+
+struct _GskMemoryFormatInfo
+{
+  VkFormat format;
+  VkComponentMapping components;
+};
+
+static const GskMemoryFormatInfo default_format_info = {
+  VK_FORMAT_B8G8R8A8_UNORM,
+  { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }
+};
+
+static const GskMemoryFormatInfo *
+gsk_memory_format_get_vk_format_infos (GdkMemoryFormat format)
+{
+#define SWIZZLE(a, b, c, d) { VK_COMPONENT_SWIZZLE_ ## a, VK_COMPONENT_SWIZZLE_ ## b, VK_COMPONENT_SWIZZLE_ ## c, VK_COMPONENT_SWIZZLE_ ## d }
+#define DEFAULT_SWIZZLE SWIZZLE (R, G, B, A)
+  switch (format)
+    {
+    case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_B8G8R8A8_UNORM, DEFAULT_SWIZZLE },
+          { VK_FORMAT_R8G8B8A8_UNORM, SWIZZLE(B, G, R, A) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R8G8B8A8_UNORM, SWIZZLE(G, B, A, R) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R8G8B8A8_UNORM, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+#if 0
+  GDK_MEMORY_B8G8R8A8,
+  GDK_MEMORY_A8R8G8B8,
+  GDK_MEMORY_R8G8B8A8,
+  GDK_MEMORY_A8B8G8R8,
+#endif
+
+    case GDK_MEMORY_R8G8B8:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R8G8B8_UNORM, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_B8G8R8:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_B8G8R8_UNORM, DEFAULT_SWIZZLE },
+          { VK_FORMAT_R8G8B8_UNORM, SWIZZLE(B, G, R, A) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_R16G16B16:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16G16B16_UNORM, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16G16B16A16_UNORM, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+#if 0
+  GDK_MEMORY_R16G16B16A16,
+#endif
+
+    case GDK_MEMORY_R16G16B16_FLOAT:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16G16B16_SFLOAT, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16G16B16A16_SFLOAT, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+#if 0
+  GDK_MEMORY_R16G16B16A16_FLOAT,
+#endif
+
+    case GDK_MEMORY_R32G32B32_FLOAT:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R32G32B32_SFLOAT, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R32G32B32A32_SFLOAT, DEFAULT_SWIZZLE },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+#if 0
+  GDK_MEMORY_R32G32B32A32_FLOAT,
+#endif
+
+    case GDK_MEMORY_G8A8_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R8G8_UNORM, SWIZZLE (R, R, R, G) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+#if 0
+  GDK_MEMORY_G8A8,
+#endif
+
+    case GDK_MEMORY_G8:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R8_UNORM, SWIZZLE (R, R, R, ONE) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_G16A16_PREMULTIPLIED:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16G16_UNORM, SWIZZLE (R, R, R, G) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+#if 0
+  GDK_MEMORY_G16A16
+#endif
+
+    case GDK_MEMORY_G16:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16_UNORM, SWIZZLE (R, R, R, ONE) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_A8:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R8_UNORM, SWIZZLE (R, R, R, R) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_A16:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_R16_UNORM, SWIZZLE (R, R, R, R) },
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_B8G8R8A8:
+    case GDK_MEMORY_A8R8G8B8:
+    case GDK_MEMORY_R8G8B8A8:
+    case GDK_MEMORY_A8B8G8R8:
+    case GDK_MEMORY_R16G16B16A16:
+    case GDK_MEMORY_R16G16B16A16_FLOAT:
+    case GDK_MEMORY_R32G32B32A32_FLOAT:
+    case GDK_MEMORY_G8A8:
+    case GDK_MEMORY_G16A16:
+      {
+        static const GskMemoryFormatInfo info[] = {
+          { VK_FORMAT_UNDEFINED }
+        };
+        return info;
+      }
+
+    case GDK_MEMORY_N_FORMATS:
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+#undef DEFAULT_SWIZZLE
+#undef SWIZZLE
+}
+
+static gboolean
+gsk_vulkan_context_supports_format (GdkVulkanContext *context,
+                                    VkFormat          format)
+{
+  VkFormatProperties properties;
+
+  vkGetPhysicalDeviceFormatProperties (gdk_vulkan_context_get_physical_device (context),
+                                       format,
+                                       &properties);
+
+  if ((properties.linearTilingFeatures & (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT)) &&
+      (properties.optimalTilingFeatures & (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT)))
+    return TRUE;
+
+  return FALSE;
+}
+
 static void
-gsk_vulkan_image_create_view (GskVulkanImage *self,
-                              VkFormat        format)
+gsk_vulkan_image_create_view (GskVulkanImage            *self,
+                              const GskMemoryFormatInfo *format)
 {
   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,
-                                     },
+                                     .format = format->format,
+                                     .components = format->components,
                                      .subresourceRange = {
                                          .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                                          .baseMipLevel = 0,
@@ -226,6 +466,7 @@ gsk_vulkan_image_create_view (GskVulkanImage *self,
 
 static GskVulkanImage *
 gsk_vulkan_image_new (GdkVulkanContext      *context,
+                      GdkMemoryFormat        format,
                       gsize                  width,
                       gsize                  height,
                       VkImageTiling          tiling,
@@ -236,12 +477,28 @@ gsk_vulkan_image_new (GdkVulkanContext      *context,
 {
   VkMemoryRequirements requirements;
   GskVulkanImage *self;
+  const GskMemoryFormatInfo *vk_format;
 
   g_assert (width > 0 && height > 0);
 
+  for (vk_format = gsk_memory_format_get_vk_format_infos (format);
+       vk_format->format != VK_FORMAT_UNDEFINED;
+       vk_format++)
+    {
+      if (gsk_vulkan_context_supports_format (context, vk_format->format))
+        break;
+    }
+
+  if (vk_format->format == VK_FORMAT_UNDEFINED)
+    {
+      vk_format = &default_format_info;
+      format = GDK_MEMORY_DEFAULT;
+    }
+
   self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
 
   self->vulkan = g_object_ref (context);
+  self->format = format;
   self->width = width;
   self->height = height;
   self->vk_usage = usage;
@@ -253,7 +510,7 @@ gsk_vulkan_image_new (GdkVulkanContext      *context,
                                     .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
                                     .flags = 0,
                                     .imageType = VK_IMAGE_TYPE_2D,
-                                    .format = VK_FORMAT_B8G8R8A8_UNORM,
+                                    .format = vk_format->format,
                                     .extent = { width, height, 1 },
                                     .mipLevels = 1,
                                     .arrayLayers = 1,
@@ -280,7 +537,7 @@ gsk_vulkan_image_new (GdkVulkanContext      *context,
                                    gsk_vulkan_memory_get_device_memory (self->memory),
                                    0);
 
-  gsk_vulkan_image_create_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+  gsk_vulkan_image_create_view (self, vk_format);
 
   return self;
 }
@@ -295,8 +552,10 @@ gsk_vulkan_image_new_from_texture (GskVulkanUploader *uploader,
 
   downloader = gdk_texture_downloader_new (texture);
   result = gsk_vulkan_image_new_for_upload (uploader,
+                                            gdk_texture_get_format (texture),
                                             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);
   gdk_texture_downloader_download_into (downloader, map.data, map.stride);
   gsk_vulkan_image_unmap_memory (result, uploader, &map);
@@ -306,12 +565,14 @@ gsk_vulkan_image_new_from_texture (GskVulkanUploader *uploader,
 
 GskVulkanImage *
 gsk_vulkan_image_new_for_upload (GskVulkanUploader *uploader,
+                                 GdkMemoryFormat    format,
                                  gsize              width,
                                  gsize              height)
 {
   GskVulkanImage *self;
 
   self = gsk_vulkan_image_new (uploader->vulkan,
+                               format,
                                width,
                                height,
                                VK_IMAGE_TILING_LINEAR,
@@ -470,7 +731,15 @@ gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
   self->height = height;
   self->vk_image = image;
 
-  gsk_vulkan_image_create_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+  gsk_vulkan_image_create_view (self,
+                                &(GskMemoryFormatInfo) {
+                                  format,
+                                  { VK_COMPONENT_SWIZZLE_R,
+                                    VK_COMPONENT_SWIZZLE_G,
+                                    VK_COMPONENT_SWIZZLE_B,
+                                    VK_COMPONENT_SWIZZLE_A
+                                   }
+                                });
 
   return self;
 }
@@ -483,6 +752,7 @@ gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
   GskVulkanImage *self;
 
   self = gsk_vulkan_image_new (context,
+                               GDK_MEMORY_DEFAULT,
                                width,
                                height,
                                VK_IMAGE_TILING_OPTIMAL,
@@ -502,6 +772,7 @@ gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
   GskVulkanImage *self;
 
   self = gsk_vulkan_image_new (context,
+                               GDK_MEMORY_DEFAULT,
                                width,
                                height,
                                VK_IMAGE_TILING_OPTIMAL,
@@ -521,6 +792,7 @@ gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
   GskVulkanImage *self;
 
   self = gsk_vulkan_image_new (context,
+                               GDK_MEMORY_DEFAULT,
                                width,
                                height,
                                VK_IMAGE_TILING_OPTIMAL,
@@ -581,7 +853,7 @@ gsk_vulkan_image_download (GskVulkanImage    *self,
   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,
+                                    self->format,
                                     bytes,
                                     self->width * 4);
   gsk_vulkan_buffer_unmap (buffer);
index e11155564122818135e0ce93bd91d2943ba711c2..51aae186de8318d6c61530dd7ef0467d959370d9 100644 (file)
@@ -65,6 +65,7 @@ struct _GskVulkanImageMap
 };
 
 GskVulkanImage *        gsk_vulkan_image_new_for_upload                 (GskVulkanUploader      *uploader,
+                                                                         GdkMemoryFormat         format,
                                                                          gsize                   width,
                                                                          gsize                   height);
 void                    gsk_vulkan_image_map_memory                     (GskVulkanImage         *self,
index 1057a04a1fa3fa4b125818572c0216c9db2003ab..0a641b6238f89f84445d1b574ccefdd203405f8a 100644 (file)
@@ -1368,7 +1368,7 @@ gsk_vulkan_render_pass_get_node_as_texture (GskVulkanRenderPass    *self,
   width = ceil (node->bounds.size.width * graphene_vec2_get_x (scale));
   height = ceil (node->bounds.size.height * graphene_vec2_get_y (scale));
 
-  result = gsk_vulkan_image_new_for_upload (uploader, width, height);
+  result = gsk_vulkan_image_new_for_upload (uploader, GDK_MEMORY_DEFAULT, width, height);
   gsk_vulkan_image_map_memory (result, uploader, &map);
   surface = cairo_image_surface_create_for_data (map.data,
                                                  CAIRO_FORMAT_ARGB32,
@@ -1429,7 +1429,7 @@ gsk_vulkan_render_pass_upload_fallback (GskVulkanRenderPass  *self,
   width = ceil (node->bounds.size.width * graphene_vec2_get_x (&self->scale));
   height = ceil (node->bounds.size.height * graphene_vec2_get_y (&self->scale));
 
-  op->source = gsk_vulkan_image_new_for_upload (uploader, width, height);
+  op->source = gsk_vulkan_image_new_for_upload (uploader, GDK_MEMORY_DEFAULT, width, height);
   gsk_vulkan_image_map_memory (op->source, uploader, &map);
   surface = cairo_image_surface_create_for_data (map.data,
                                                  CAIRO_FORMAT_ARGB32,