#include "config.h"
#include "gskvulkanimageprivate.h"
#include "gskgpuutilsprivate.h"
#include "gskvulkanbufferprivate.h"
#include "gskvulkanframeprivate.h"
#include "gskvulkanmemoryprivate.h"
#include "gskvulkanycbcrprivate.h"
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdkdmabuftextureprivate.h"
#include "gdk/gdkvulkancontextprivate.h"
#include "gdk/gdkmemoryformatprivate.h"
#include "gdk/gdkvulkancontextprivate.h"
#include <fcntl.h>
#include <string.h>
#ifdef HAVE_DMABUF
#include <linux/dma-buf.h>
#endif
struct _GskVulkanImage
{
GskGpuImage parent_instance;
GskVulkanDevice *device;
VkFormat vk_format;
VkImageTiling vk_tiling;
VkImageUsageFlags vk_usage;
VkImage vk_image;
VkImageView vk_image_view;
VkFramebuffer vk_framebuffer;
VkImageView vk_framebuffer_image_view;
GskVulkanYcbcr *ycbcr;
VkSemaphore vk_semaphore;
uint64_t vk_semaphore_wait;
struct {
VkDescriptorSet vk_descriptor_set;
gsize pool_id;
} descriptor_sets[GSK_GPU_SAMPLER_N_SAMPLERS][2 ];
VkPipelineStageFlags vk_pipeline_stage;
VkImageLayout vk_image_layout;
VkAccessFlags vk_access;
GskVulkanAllocator *allocator;
GskVulkanAllocation allocation;
};
G_DEFINE_TYPE (GskVulkanImage, gsk_vulkan_image, GSK_TYPE_GPU_IMAGE)
static gboolean
gsk_component_mapping_is_framebuffer_compatible (const VkComponentMapping *components)
{
if ((components->r != VK_COMPONENT_SWIZZLE_R && components->r != VK_COMPONENT_SWIZZLE_IDENTITY) ||
(components->g != VK_COMPONENT_SWIZZLE_G && components->g != VK_COMPONENT_SWIZZLE_IDENTITY) ||
(components->b != VK_COMPONENT_SWIZZLE_B && components->b != VK_COMPONENT_SWIZZLE_IDENTITY) ||
(components->a != VK_COMPONENT_SWIZZLE_A && components->a != VK_COMPONENT_SWIZZLE_IDENTITY))
return FALSE ;
return TRUE ;
}
static gboolean
gsk_vulkan_get_ycbcr_flags (GskGpuConversion conv,
VkSamplerYcbcrModelConversion *out_model,
VkSamplerYcbcrRange *out_range)
{
switch (conv)
{
case GSK_GPU_CONVERSION_NONE:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
return FALSE ;
case GSK_GPU_CONVERSION_SRGB:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
return FALSE ;
case GSK_GPU_CONVERSION_BT601:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
return TRUE ;
case GSK_GPU_CONVERSION_BT601_NARROW:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
return TRUE ;
case GSK_GPU_CONVERSION_BT709:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
return TRUE ;
case GSK_GPU_CONVERSION_BT709_NARROW:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
return TRUE ;
case GSK_GPU_CONVERSION_BT2020:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
return TRUE ;
case GSK_GPU_CONVERSION_BT2020_NARROW:
*out_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
*out_range = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
return TRUE ;
default :
g_assert_not_reached ();
return FALSE ;
}
}
static VkFormatFeatureFlags
gsk_vulkan_device_get_format_features (GskVulkanDevice *device,
VkFormat format,
uint64_t modifier,
guint n_planes,
VkImageTiling tiling,
VkImageUsageFlags usage)
{
VkDrmFormatModifierPropertiesEXT drm_mod_properties[100 ];
VkDrmFormatModifierPropertiesListEXT drm_properties;
VkPhysicalDevice vk_phys_device;
VkFormatProperties2 properties;
gsize i;
vk_phys_device = gsk_vulkan_device_get_vk_physical_device (device);
drm_properties = (VkDrmFormatModifierPropertiesListEXT) {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
.drmFormatModifierCount = G_N_ELEMENTS (drm_mod_properties),
.pDrmFormatModifierProperties = drm_mod_properties,
};
properties = (VkFormatProperties2) {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
.pNext = (tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) ? NULL : &drm_properties
};
vkGetPhysicalDeviceFormatProperties2 (vk_phys_device,
format,
&properties);
switch ((int ) tiling)
{
case VK_IMAGE_TILING_OPTIMAL:
return properties.formatProperties.optimalTilingFeatures;
case VK_IMAGE_TILING_LINEAR:
return properties.formatProperties.linearTilingFeatures;
case VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT:
for (i = 0 ; i < drm_properties.drmFormatModifierCount; i++)
{
if (drm_mod_properties[i].drmFormatModifier == modifier &&
drm_mod_properties[i].drmFormatModifierPlaneCount == n_planes)
return drm_mod_properties[i].drmFormatModifierTilingFeatures;
}
return 0 ;
default :
return 0 ;
}
}
static GskGpuImageFlags
gsk_vulkan_image_flags_for_features (VkFormatFeatureFlags features)
{
GskGpuImageFlags result;
result = 0 ;
if (features & VK_FORMAT_FEATURE_BLIT_SRC_BIT)
result |= GSK_GPU_IMAGE_BLIT;
if (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)
result |= GSK_GPU_IMAGE_FILTERABLE;
if (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT)
result |= GSK_GPU_IMAGE_RENDERABLE;
if (features & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)
result |= GSK_GPU_IMAGE_DOWNLOADABLE;
return result;
}
static VkFormatFeatureFlags
gsk_vulkan_device_supports_format (GskVulkanDevice *device,
VkFormat format,
uint64_t modifier,
guint n_planes,
gboolean needs_ycbcr_conversion,
VkImageTiling tiling,
VkImageUsageFlags usage,
gsize width,
gsize height,
GskGpuImageFlags *out_flags)
{
VkPhysicalDevice vk_phys_device;
VkImageFormatProperties2 image_properties;
VkFormatFeatureFlags features;
VkResult res;
vk_phys_device = gsk_vulkan_device_get_vk_physical_device (device);
features = gsk_vulkan_device_get_format_features (device, format, modifier, n_planes, tiling, usage);
if (!(features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
return 0 ;
if (needs_ycbcr_conversion && !gsk_vulkan_ycbcr_is_supported (features))
return 0 ;
image_properties = (VkImageFormatProperties2) {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
};
res = vkGetPhysicalDeviceImageFormatProperties2 (vk_phys_device,
&(VkPhysicalDeviceImageFormatInfo2) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
.format = format,
.type = VK_IMAGE_TYPE_2D,
.tiling = tiling,
.usage = usage,
.flags = 0 ,
.pNext = (tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) ? NULL : &(VkPhysicalDeviceImageDrmFormatModifierInfoEXT) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
.drmFormatModifier = modifier,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 1 ,
.pQueueFamilyIndices = (uint32_t[1 ]) { gsk_vulkan_device_get_vk_queue_family_index (device) },
}
},
&image_properties);
if (res != VK_SUCCESS)
return 0 ;
if (image_properties.imageFormatProperties.maxExtent.width < width ||
image_properties.imageFormatProperties.maxExtent.height < height)
return 0 ;
*out_flags = gsk_vulkan_image_flags_for_features (features);
if (image_properties.imageFormatProperties.maxMipLevels >= gsk_gpu_mipmap_levels (width, height) &&
(*out_flags & (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_FILTERABLE | GSK_GPU_IMAGE_RENDERABLE)) == (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_FILTERABLE | GSK_GPU_IMAGE_RENDERABLE))
*out_flags |= GSK_GPU_IMAGE_CAN_MIPMAP;
return features;
}
static void
gsk_vulkan_image_create_view (GskVulkanImage *self,
VkFormat vk_format,
const VkComponentMapping *vk_components,
VkSamplerYcbcrConversion vk_conversion)
{
GSK_VK_CHECK (vkCreateImageView, gsk_vulkan_device_get_vk_device (self->device),
&(VkImageViewCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = self->vk_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = vk_format,
.components = vk_conversion ? (VkComponentMapping) { 0 , }
: *vk_components,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0 ,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0 ,
.layerCount = 1 ,
},
.pNext = vk_conversion == VK_NULL_HANDLE ? NULL : &(VkSamplerYcbcrConversionInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = vk_conversion
}
},
NULL,
&self->vk_image_view);
}
static VkFormatFeatureFlags
gsk_vulkan_device_check_format (GskVulkanDevice *device,
VkFormat vk_format,
const VkComponentMapping *vk_components,
GskGpuImageFlags required_flags,
gboolean needs_ycbcr_conversion,
VkImageTiling vk_tiling,
VkImageUsageFlags vk_usage,
gsize width,
gsize height,
VkImageTiling *out_tiling,
GskGpuImageFlags *out_flags)
{
#define CHECK_FLAGS (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_FILTERABLE | GSK_GPU_IMAGE_RENDERABLE | GSK_GPU_IMAGE_CAN_MIPMAP)
VkFormatFeatureFlags vk_features;
GskGpuImageFlags flags;
if (vk_format == VK_FORMAT_UNDEFINED)
return 0 ;
if (vk_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT &&
!gsk_component_mapping_is_framebuffer_compatible (vk_components))
return 0 ;
vk_features = gsk_vulkan_device_supports_format (device,
vk_format,
0 , 1 ,
needs_ycbcr_conversion,
vk_tiling, vk_usage,
width, height,
&flags);
if (vk_features != 0 && ((flags & required_flags & CHECK_FLAGS) == (required_flags & CHECK_FLAGS)))
{
*out_tiling = vk_tiling;
*out_flags = flags;
return vk_features;
}
if (vk_tiling == VK_IMAGE_TILING_LINEAR)
{
vk_features = gsk_vulkan_device_supports_format (device,
vk_format,
0 , 1 ,
needs_ycbcr_conversion,
VK_IMAGE_TILING_OPTIMAL, vk_usage,
width, height,
&flags);
if (vk_features != 0 && ((flags & required_flags & CHECK_FLAGS) == (required_flags & CHECK_FLAGS)))
{
*out_tiling = VK_IMAGE_TILING_OPTIMAL;
*out_flags = flags;
return vk_features;
}
}
return 0 ;
#undef CHECK_FLAGS
}
static gboolean
gsk_vulkan_device_supports_conversion (GskVulkanDevice *device,
GskGpuConversion conv)
{
switch (conv)
{
case GSK_GPU_CONVERSION_SRGB:
case GSK_GPU_CONVERSION_NONE:
break ;
case GSK_GPU_CONVERSION_BT601:
case GSK_GPU_CONVERSION_BT601_NARROW:
case GSK_GPU_CONVERSION_BT709:
case GSK_GPU_CONVERSION_BT709_NARROW:
case GSK_GPU_CONVERSION_BT2020:
case GSK_GPU_CONVERSION_BT2020_NARROW:
if (!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_YCBCR))
return FALSE ;
break ;
default :
break ;
}
return TRUE ;
}
static GskVulkanImage *
gsk_vulkan_image_new (GskVulkanDevice *device,
GdkMemoryFormat format,
GskGpuConversion conv,
GskGpuImageFlags required_flags,
gsize width,
gsize height,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkPipelineStageFlags stage,
VkImageLayout layout,
VkAccessFlags access,
VkMemoryPropertyFlags memory)
{
VkMemoryRequirements requirements;
GskVulkanImage *self;
VkDevice vk_device;
GskGpuImageFlags flags;
gboolean try_srgb;
VkFormat vk_format, vk_srgb_format;
VkFormatFeatureFlags vk_features;
VkComponentMapping vk_components;
VkSamplerYcbcrConversion vk_conversion;
VkSamplerYcbcrModelConversion vk_model;
VkSamplerYcbcrRange vk_range;
GdkShaderOp shader_op;
gboolean needs_conversion;
gsize memory_index;
g_assert (width > 0 && height > 0 );
if (!gsk_vulkan_device_supports_conversion (device, conv))
conv = GSK_GPU_CONVERSION_NONE;
vk_srgb_format = VK_FORMAT_UNDEFINED;
try_srgb = conv == GSK_GPU_CONVERSION_SRGB;
/* First, try the actual format */
vk_format = gdk_memory_format_vk_format (format, &vk_components, &needs_conversion);
if (try_srgb)
vk_srgb_format = gdk_memory_format_vk_srgb_format (format);
if ((vk_features = gsk_vulkan_device_check_format (device, vk_srgb_format, &vk_components,
required_flags, needs_conversion,
tiling, usage, width, height,
&tiling, &flags)))
{
vk_format = vk_srgb_format;
}
else if ((vk_features = gsk_vulkan_device_check_format (device, vk_format, &vk_components,
required_flags, needs_conversion,
tiling, usage, width, height,
&tiling, &flags)))
{
/* nothing to do */
}
else
{
GdkMemoryFormat rgba_format;
GdkSwizzle rgba_swizzle;
/* Second, try the potential RGBA format */
if (gdk_memory_format_get_rgba_format (format, &rgba_format, &rgba_swizzle))
{
vk_format = gdk_memory_format_vk_format (rgba_format, &vk_components, &needs_conversion);
gdk_swizzle_to_vk_component_mapping (rgba_swizzle, &vk_components);
}
else
vk_format = VK_FORMAT_UNDEFINED;
if (try_srgb && vk_format != VK_FORMAT_UNDEFINED)
vk_srgb_format = gdk_memory_format_vk_srgb_format (rgba_format);
else
vk_srgb_format = VK_FORMAT_UNDEFINED;
if ((vk_features = gsk_vulkan_device_check_format (device, vk_srgb_format, &vk_components,
required_flags, needs_conversion,
tiling, usage, width, height,
&tiling, &flags)))
{
vk_format = vk_srgb_format;
}
else if ((vk_features = gsk_vulkan_device_check_format (device, vk_format, &vk_components,
required_flags, needs_conversion,
tiling, usage, width, height,
&tiling, &flags)))
{
/* nothing to do */
}
else
{
const GdkMemoryFormat *fallbacks;
gsize i;
/* Next, try the fallbacks */
fallbacks = gdk_memory_format_get_fallbacks (format);
for (i = 0 ; fallbacks[i] != -1 ; i++)
{
vk_format = gdk_memory_format_vk_format (fallbacks[i], &vk_components, &needs_conversion);
if (try_srgb)
vk_srgb_format = gdk_memory_format_vk_srgb_format (fallbacks[i]);
if ((vk_features = gsk_vulkan_device_check_format (device, vk_srgb_format, &vk_components,
required_flags, needs_conversion,
tiling, usage, width, height,
&tiling, &flags)))
{
vk_format = vk_srgb_format;
format = fallbacks[i];
break ;
}
else if ((vk_features = gsk_vulkan_device_check_format (device, vk_format, &vk_components,
required_flags, needs_conversion,
tiling, usage, width, height,
&tiling, &flags)))
{
format = fallbacks[i];
break ;
}
}
/* No format found. Likely, the width/height are too big */
if (fallbacks[i] == -1 )
return NULL;
}
}
shader_op = gdk_memory_format_get_default_shader_op (format);
if (vk_format != vk_srgb_format && conv == GSK_GPU_CONVERSION_SRGB)
conv = GSK_GPU_CONVERSION_NONE;
if (((flags & (GSK_GPU_IMAGE_FILTERABLE | GSK_GPU_IMAGE_RENDERABLE | GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_CAN_MIPMAP)) !=
(GSK_GPU_IMAGE_FILTERABLE | GSK_GPU_IMAGE_RENDERABLE | GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_CAN_MIPMAP)) ||
shader_op != GDK_SHADER_DEFAULT ||
!(required_flags & GSK_GPU_IMAGE_CAN_MIPMAP))
flags &= ~GSK_GPU_IMAGE_CAN_MIPMAP;
if (!gsk_component_mapping_is_framebuffer_compatible (&vk_components) || needs_conversion)
flags &= ~GSK_GPU_IMAGE_BLIT;
vk_device = gsk_vulkan_device_get_vk_device (device);
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->device = g_object_ref (device);
self->vk_format = vk_format;
self->vk_tiling = tiling;
self->vk_usage = usage;
self->vk_pipeline_stage = stage;
self->vk_image_layout = layout;
self->vk_access = access;
if (gsk_vulkan_ycbcr_is_supported (vk_features) &&
(gsk_vulkan_get_ycbcr_flags (conv, &vk_model, &vk_range) || needs_conversion))
{
self->ycbcr = gsk_vulkan_ycbcr_get (device,
&(GskVulkanYcbcrInfo) {
.vk_format = vk_format,
.vk_features = vk_features,
.vk_components = vk_components,
.vk_ycbcr_model = vk_model,
.vk_ycbcr_range = vk_range,
});
gsk_vulkan_ycbcr_ref (self->ycbcr);
vk_conversion = gsk_vulkan_ycbcr_get_vk_conversion (self->ycbcr);
flags |= GSK_GPU_IMAGE_EXTERNAL;
if (!gsk_vulkan_ycbcr_is_filterable (self->ycbcr))
flags &= ~(GSK_GPU_IMAGE_FILTERABLE);
shader_op = GDK_SHADER_DEFAULT;
}
else
{
/* Something is broken if this happens, we checked for this above */
g_warn_if_fail (!needs_conversion);
vk_conversion = VK_NULL_HANDLE;
conv = GSK_GPU_CONVERSION_NONE;
}
gsk_gpu_image_setup (GSK_GPU_IMAGE (self), flags, conv, shader_op, format, width, height);
GSK_VK_CHECK (vkCreateImage, vk_device,
&(VkImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = 0 ,
.imageType = VK_IMAGE_TYPE_2D,
.format = vk_format,
.extent = { width, height, 1 },
.mipLevels = (flags & GSK_GPU_IMAGE_CAN_MIPMAP) ? gsk_gpu_mipmap_levels (width, height) : 1 ,
.arrayLayers = 1 ,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = tiling,
.usage = usage |
(flags & (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_DOWNLOADABLE) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0 ),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = self->vk_image_layout,
},
NULL,
&self->vk_image);
vkGetImageMemoryRequirements (vk_device,
self->vk_image,
&requirements);
memory_index = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
0 ,
tiling == VK_IMAGE_TILING_LINEAR ? GSK_VULKAN_MEMORY_MAPPABLE : 0 );
self->allocator = gsk_vulkan_device_get_allocator (device, memory_index);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.size,
requirements.alignment,
&self->allocation);
GSK_VK_CHECK (vkBindImageMemory, vk_device,
self->vk_image,
self->allocation.vk_memory,
self->allocation.offset);
gsk_vulkan_image_create_view (self, vk_format, &vk_components, vk_conversion);
return self;
}
GskGpuImage *
gsk_vulkan_image_new_for_upload (GskVulkanDevice *device,
gboolean with_mipmap,
GdkMemoryFormat format,
GskGpuConversion conv,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (device,
format,
conv,
0 , //with_mipmap ? (GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_RENDERABLE | GSK_GPU_IMAGE_FILTERABLE) : 0,
width,
height,
VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
return GSK_GPU_IMAGE (self);
}
static gboolean
gsk_vulkan_image_can_map (GskVulkanImage *self)
{
if (GSK_DEBUG_CHECK (STAGING))
return FALSE ;
if (self->vk_tiling != VK_IMAGE_TILING_LINEAR)
return FALSE ;
if (self->vk_image_layout != VK_IMAGE_LAYOUT_PREINITIALIZED &&
self->vk_image_layout != VK_IMAGE_LAYOUT_GENERAL)
return FALSE ;
if ((self->allocation.memory_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) == 0 )
return FALSE ;
return self->allocation.map != NULL;
}
guchar *
gsk_vulkan_image_get_data (GskVulkanImage *self,
GdkMemoryLayout *out_layout)
{
const VkImageAspectFlags aspect_flags[3 ] = { VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT, VK_IMAGE_ASPECT_PLANE_2_BIT };
GskGpuImage *image = GSK_GPU_IMAGE (self);
VkSubresourceLayout image_layout;
VkDevice vk_device;
gsize i, n_planes;
if (!gsk_vulkan_image_can_map (self))
return NULL;
out_layout->format = gsk_gpu_image_get_format (image);
out_layout->width = gsk_gpu_image_get_width (image);
out_layout->height = gsk_gpu_image_get_height (image);
out_layout->size = self->allocation.size;
n_planes = gdk_memory_format_get_n_planes (out_layout->format);
vk_device = gsk_vulkan_device_get_vk_device (self->device);
for (i = 0 ; i < n_planes; i++)
{
vkGetImageSubresourceLayout (vk_device,
self->vk_image,
&(VkImageSubresource) {
.aspectMask = n_planes == 1 ? VK_IMAGE_ASPECT_COLOR_BIT : aspect_flags[i],
.mipLevel = 0 ,
.arrayLayer = 0
},
&image_layout);
out_layout->planes[i].offset = image_layout.offset;
out_layout->planes[i].stride = image_layout.rowPitch;
}
return self->allocation.map;
}
GskGpuImage *
gsk_vulkan_image_new_for_swapchain (GskVulkanDevice *device,
VkImage image,
VkFormat format,
GdkMemoryFormat memory_format,
gsize width,
gsize height)
{
GskVulkanImage *self;
GskGpuImageFlags flags = 0 ;
GskGpuConversion conv;
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->device = g_object_ref (device);
self->vk_tiling = VK_IMAGE_TILING_OPTIMAL;
self->vk_image = image;
self->vk_format = format;
self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
self->vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
self->vk_access = 0 ;
if (format == gdk_memory_format_vk_srgb_format (memory_format))
conv = GSK_GPU_CONVERSION_SRGB;
else
conv = GSK_GPU_CONVERSION_NONE;
/* FIXME: The flags here are very suboptimal */
gsk_gpu_image_setup (GSK_GPU_IMAGE (self), flags, conv, GDK_SHADER_DEFAULT, memory_format, width, height);
gsk_vulkan_image_create_view (self,
format,
&(VkComponentMapping) {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A
},
VK_NULL_HANDLE);
return GSK_GPU_IMAGE (self);
}
GskGpuImage *
gsk_vulkan_image_new_for_atlas (GskVulkanDevice *device,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (device,
GDK_MEMORY_DEFAULT,
GSK_GPU_CONVERSION_NONE,
GSK_GPU_IMAGE_FILTERABLE | GSK_GPU_IMAGE_RENDERABLE,
width,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
0 ,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
return GSK_GPU_IMAGE (self);
}
GskGpuImage *
gsk_vulkan_image_new_for_offscreen (GskVulkanDevice *device,
gboolean with_mipmap,
GdkMemoryFormat preferred_format,
gboolean try_srgb,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (device,
preferred_format,
try_srgb ? GSK_GPU_CONVERSION_SRGB : GSK_GPU_CONVERSION_NONE,
GSK_GPU_IMAGE_RENDERABLE |
(with_mipmap ? GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_FILTERABLE : 0 ),
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_USAGE_TRANSFER_DST_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
return GSK_GPU_IMAGE (self);
}
#ifdef HAVE_DMABUF
static VkFormatFeatureFlags
gsk_vulkan_device_check_dmabuf_format (GskVulkanDevice *device,
VkFormat vk_format,
const VkComponentMapping *vk_components,
gsize width,
gsize height,
uint64_t modifiers[100 ],
gsize *out_n_modifiers)
{
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
VkDrmFormatModifierPropertiesEXT drm_mod_properties[100 ];
VkDrmFormatModifierPropertiesListEXT drm_properties;
VkPhysicalDevice vk_phys_device;
VkFormatProperties2 properties;
VkImageFormatProperties2 image_properties;
VkFormatFeatureFlags vk_features;
gsize i, n_modifiers;
VkResult res;
drm_properties = (VkDrmFormatModifierPropertiesListEXT) {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
.drmFormatModifierCount = G_N_ELEMENTS (drm_mod_properties),
.pDrmFormatModifierProperties = drm_mod_properties,
};
properties = (VkFormatProperties2) {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
.pNext = &drm_properties
};
if (vk_format == VK_FORMAT_UNDEFINED)
return FALSE ;
if (!gsk_component_mapping_is_framebuffer_compatible (vk_components))
return FALSE ;
vk_phys_device = gsk_vulkan_device_get_vk_physical_device (device);
vkGetPhysicalDeviceFormatProperties2 (vk_phys_device,
vk_format,
&properties);
n_modifiers = 0 ;
vk_features = -1 ;
for (i = 0 ; i < drm_properties.drmFormatModifierCount; i++)
{
if ((drm_mod_properties[i].drmFormatModifierTilingFeatures & required) != required)
continue ;
image_properties = (VkImageFormatProperties2) {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
};
res = vkGetPhysicalDeviceImageFormatProperties2 (vk_phys_device,
&(VkPhysicalDeviceImageFormatInfo2) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
.format = vk_format,
.type = VK_IMAGE_TYPE_2D,
.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.flags = 0 ,
.pNext = &(VkPhysicalDeviceImageDrmFormatModifierInfoEXT) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
.drmFormatModifier = drm_mod_properties[i].drmFormatModifier,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 1 ,
.pQueueFamilyIndices = (uint32_t[1 ]) { gsk_vulkan_device_get_vk_queue_family_index (device) },
}
},
&image_properties);
if (res != VK_SUCCESS)
continue ;
if (image_properties.imageFormatProperties.maxExtent.width < width ||
image_properties.imageFormatProperties.maxExtent.height < height)
continue ;
modifiers[n_modifiers++] = drm_mod_properties[i].drmFormatModifier;
vk_features &= drm_mod_properties[i].drmFormatModifierTilingFeatures;
}
if (n_modifiers == 0 )
return 0 ;
*out_n_modifiers = n_modifiers;
return vk_features;
}
GskGpuImage *
gsk_vulkan_image_new_dmabuf (GskVulkanDevice *device,
GdkMemoryFormat format,
gboolean try_srgb,
gsize width,
gsize height)
{
uint64_t modifiers[100 ];
VkDevice vk_device;
VkFormat vk_format, vk_srgb_format;
VkFormatFeatureFlags vk_features;
VkComponentMapping vk_components;
VkMemoryRequirements requirements;
VkSamplerYcbcrConversion vk_conversion;
gsize memory_index;
GskVulkanImage *self;
VkResult res;
gsize n_modifiers;
GskGpuImageFlags flags;
GskGpuConversion conv;
GdkShaderOp shader_op;
gboolean needs_conversion;
if (!gdk_has_feature (GDK_FEATURE_DMABUF) ||
!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_DMABUF))
return NULL;
vk_srgb_format = VK_FORMAT_UNDEFINED;
vk_device = gsk_vulkan_device_get_vk_device (device);
/* First, try the actual format */
vk_format = gdk_memory_format_vk_format (format, &vk_components, &needs_conversion);
if (try_srgb)
vk_srgb_format = gdk_memory_format_vk_srgb_format (format);
if ((vk_features = gsk_vulkan_device_check_dmabuf_format (device, vk_srgb_format, &vk_components,
width, height,
modifiers, &n_modifiers)))
{
vk_format = vk_srgb_format;
}
else if ((vk_features = gsk_vulkan_device_check_dmabuf_format (device, vk_format, &vk_components,
width, height,
modifiers, &n_modifiers)))
{
/* nothing to do */
}
else
{
/* Second, try the potential RGBA format, but as a fallback */
GdkMemoryFormat rgba_format;
GdkSwizzle rgba_swizzle;
if (gdk_memory_format_get_rgba_format (format, &rgba_format, &rgba_swizzle))
{
vk_format = gdk_memory_format_vk_format (rgba_format, &vk_components, &needs_conversion);
gdk_swizzle_to_vk_component_mapping (rgba_swizzle, &vk_components);
if (try_srgb)
vk_srgb_format = gdk_memory_format_vk_srgb_format (format);
}
else
vk_format = vk_srgb_format = VK_FORMAT_UNDEFINED;
if ((vk_features = gsk_vulkan_device_check_dmabuf_format (device, vk_srgb_format, &vk_components,
width, height,
modifiers, &n_modifiers)))
{
vk_format = vk_srgb_format;
format = rgba_format;
}
else if ((vk_features = gsk_vulkan_device_check_dmabuf_format (device, vk_format, &vk_components,
width, height,
modifiers, &n_modifiers)))
{
format = rgba_format;
}
else
{
const GdkMemoryFormat *fallbacks;
gsize i;
/* Next, try the fallbacks */
fallbacks = gdk_memory_format_get_fallbacks (format);
for (i = 0 ; fallbacks[i] != -1 ; i++)
{
vk_format = gdk_memory_format_vk_format (fallbacks[i], &vk_components, &needs_conversion);
if (try_srgb)
vk_srgb_format = gdk_memory_format_vk_srgb_format (format);
if ((vk_features = gsk_vulkan_device_check_dmabuf_format (device, vk_srgb_format, &vk_components,
width, height,
modifiers, &n_modifiers)))
{
vk_format = vk_srgb_format;
format = fallbacks[i];
break ;
}
else if ((vk_features = gsk_vulkan_device_check_dmabuf_format (device, vk_format, &vk_components,
width, height,
modifiers, &n_modifiers)))
{
format = fallbacks[i];
break ;
}
}
/* No format found. Likely, the width/height are too big */
if (fallbacks[i] == -1 )
return NULL;
}
}
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->device = g_object_ref (device);
self->vk_tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
self->vk_format = vk_format;
self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
self->vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
self->vk_access = 0 ;
flags = gsk_vulkan_image_flags_for_features (vk_features) | GSK_GPU_IMAGE_EXTERNAL;
if (vk_format == vk_srgb_format)
conv = GSK_GPU_CONVERSION_SRGB;
else
conv = GSK_GPU_CONVERSION_NONE;
if (!gsk_component_mapping_is_framebuffer_compatible (&vk_components) || needs_conversion)
flags &= ~GSK_GPU_IMAGE_BLIT;
if (needs_conversion)
shader_op = GDK_SHADER_DEFAULT;
else
shader_op = gdk_memory_format_get_default_shader_op (format);
res = vkCreateImage (vk_device,
&(VkImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = 0 ,
.imageType = VK_IMAGE_TYPE_2D,
.format = vk_format,
.extent = { width, height, 1 },
.mipLevels = 1 ,
.arrayLayers = 1 ,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
(flags & (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_DOWNLOADABLE) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0 ),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = self->vk_image_layout,
.pNext = &(VkExternalMemoryImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
.pNext = &(VkImageDrmFormatModifierListCreateInfoEXT) {
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
.drmFormatModifierCount = n_modifiers,
.pDrmFormatModifiers = modifiers
}
},
},
NULL,
&self->vk_image);
if (res != VK_SUCCESS)
{
gsk_vulkan_handle_result (res, "vkCreateImage" );
g_object_unref (self);
return NULL;
}
vkGetImageMemoryRequirements (vk_device,
self->vk_image,
&requirements);
memory_index = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
0 ,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
self->allocator = gsk_vulkan_device_get_external_allocator (device);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.size,
requirements.alignment,
&self->allocation);
res = vkAllocateMemory (vk_device,
&(VkMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.size,
.memoryTypeIndex = memory_index,
.pNext = &(VkExportMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
.pNext = &(VkMemoryDedicatedAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.image = self->vk_image,
},
},
},
NULL,
&self->allocation.vk_memory);
if (res != VK_SUCCESS)
{
gsk_vulkan_handle_result (res, "vkAllocateMemory" );
g_object_unref (self);
return NULL;
}
GSK_VK_CHECK (vkBindImageMemory, vk_device,
self->vk_image,
self->allocation.vk_memory,
self->allocation.offset);
if (needs_conversion)
{
self->ycbcr = gsk_vulkan_ycbcr_get (device,
&(GskVulkanYcbcrInfo) {
.vk_format = vk_format,
.vk_features = vk_features,
.vk_components = vk_components,
.vk_ycbcr_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY,
.vk_ycbcr_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL,
});
gsk_vulkan_ycbcr_ref (self->ycbcr);
if (!gsk_vulkan_ycbcr_is_filterable (self->ycbcr))
flags &= ~(GSK_GPU_IMAGE_FILTERABLE);
vk_conversion = gsk_vulkan_ycbcr_get_vk_conversion (self->ycbcr);
}
else
vk_conversion = VK_NULL_HANDLE;
gsk_gpu_image_setup (GSK_GPU_IMAGE (self),
flags,
conv,
shader_op,
format,
width, height);
gsk_vulkan_image_create_view (self, vk_format, &vk_components, vk_conversion);
return GSK_GPU_IMAGE (self);
}
GskGpuImage *
gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
gsize width,
gsize height,
const GdkDmabuf *dmabuf,
gboolean premultiplied,
GskGpuConversion conv)
{
GskVulkanImage *self;
VkDevice vk_device;
VkFormat vk_format;
VkComponentMapping vk_components;
VkSamplerYcbcrConversion vk_conversion;
VkSamplerYcbcrModelConversion model;
VkSamplerYcbcrRange range;
PFN_vkGetMemoryFdPropertiesKHR func_vkGetMemoryFdPropertiesKHR;
VkFormatFeatureFlags vk_features;
VkMemoryFdPropertiesKHR fd_props = {
.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR,
};
VkMemoryRequirements2 requirements = {
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
};
gsize memory_index;
int fd;
VkResult res;
GdkMemoryFormat format;
GskGpuImageFlags flags;
GdkShaderOp shader_op;
gboolean is_yuv, needs_conversion;
if (!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_DMABUF))
{
GDK_DEBUG (DMABUF, "Vulkan does not support dmabufs" );
return NULL;
}
if (gdk_memory_format_find_by_dmabuf_fourcc (dmabuf->fourcc, premultiplied, &format, &is_yuv))
{
vk_format = gdk_memory_format_vk_format (format, &vk_components, &needs_conversion);
}
else
{
GDK_DEBUG (DMABUF, "GTK's Vulkan doesn't support fourcc %.4s" , (char *) &dmabuf->fourcc);
return NULL;
}
vk_device = gsk_vulkan_device_get_vk_device (device);
func_vkGetMemoryFdPropertiesKHR = (PFN_vkGetMemoryFdPropertiesKHR) vkGetDeviceProcAddr (vk_device, "vkGetMemoryFdPropertiesKHR" );
/* FIXME: Add support for disjoint images */
if (gdk_dmabuf_is_disjoint (dmabuf))
{
GDK_DEBUG (DMABUF, "FIXME: Add support for disjoint dmabufs to Vulkan" );
return NULL;
}
vk_features = gsk_vulkan_device_supports_format (device,
vk_format,
dmabuf->modifier,
dmabuf->n_planes,
needs_conversion,
VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
VK_IMAGE_USAGE_SAMPLED_BIT,
width,
height,
&flags);
if (!(vk_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
{
GDK_DEBUG (DMABUF, "Vulkan driver does not support format %.4s::%016llx with %u planes" ,
(char *) &dmabuf->fourcc, (unsigned long long ) dmabuf->modifier, dmabuf->n_planes);
return NULL;
}
if (!gsk_vulkan_ycbcr_is_supported (vk_features))
{
if (needs_conversion)
{
GDK_DEBUG (DMABUF, "Vulkan driver broken: Can't do YCbcrConversion for YCbcr format %.4s::%016llx with %u planes" ,
(char *) &dmabuf->fourcc, (unsigned long long ) dmabuf->modifier, dmabuf->n_planes);
return NULL;
}
conv = GSK_GPU_CONVERSION_NONE;
}
flags &= ~(GSK_GPU_IMAGE_RENDERABLE | GSK_GPU_IMAGE_CAN_MIPMAP);
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->device = g_object_ref (device);
self->vk_tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
self->vk_format = vk_format;
self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
self->vk_image_layout = VK_IMAGE_LAYOUT_GENERAL;
self->vk_access = 0 ;
res = vkCreateImage (vk_device,
&(VkImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = 0 , //disjoint ? VK_IMAGE_CREATE_DISJOINT_BIT : 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = vk_format,
.extent = { width, height, 1 },
.mipLevels = 1 ,
.arrayLayers = 1 ,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
(flags & (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_DOWNLOADABLE) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0 ),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = self->vk_image_layout,
.pNext = &(VkExternalMemoryImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
.pNext = &(VkImageDrmFormatModifierExplicitCreateInfoEXT) {
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
.drmFormatModifier = dmabuf->modifier,
.drmFormatModifierPlaneCount = dmabuf->n_planes,
.pPlaneLayouts = (VkSubresourceLayout[4 ]) {
{
.offset = dmabuf->planes[0 ].offset,
.rowPitch = dmabuf->planes[0 ].stride,
},
{
.offset = dmabuf->planes[1 ].offset,
.rowPitch = dmabuf->planes[1 ].stride,
},
{
.offset = dmabuf->planes[2 ].offset,
.rowPitch = dmabuf->planes[2 ].stride,
},
{
.offset = dmabuf->planes[3 ].offset,
.rowPitch = dmabuf->planes[3 ].stride,
},
},
}
},
},
NULL,
&self->vk_image);
if (res != VK_SUCCESS)
{
GDK_DEBUG (DMABUF, "vkCreateImage() failed: %s" , gdk_vulkan_strerror (res));
g_object_unref (self);
return NULL;
}
if (needs_conversion)
flags |= GSK_GPU_IMAGE_EXTERNAL;
if (needs_conversion || !gsk_component_mapping_is_framebuffer_compatible (&vk_components))
flags &= ~GSK_GPU_IMAGE_BLIT;
if (gsk_vulkan_get_ycbcr_flags (conv, &model, &range) || needs_conversion)
{
self->ycbcr = gsk_vulkan_ycbcr_get (device,
&(GskVulkanYcbcrInfo) {
.vk_format = vk_format,
.vk_features = vk_features,
.vk_components = vk_components,
.vk_ycbcr_model = model,
.vk_ycbcr_range = range,
});
gsk_vulkan_ycbcr_ref (self->ycbcr);
if (!gsk_vulkan_ycbcr_is_filterable (self->ycbcr))
flags &= ~(GSK_GPU_IMAGE_FILTERABLE);
vk_conversion = gsk_vulkan_ycbcr_get_vk_conversion (self->ycbcr);
shader_op = GDK_SHADER_DEFAULT;
}
else
{
vk_conversion = VK_NULL_HANDLE;
shader_op = gdk_memory_format_get_default_shader_op (format);
}
gsk_gpu_image_setup (GSK_GPU_IMAGE (self),
flags,
conv,
shader_op,
format,
width, height);
fd = fcntl (dmabuf->planes[0 ].fd, F_DUPFD_CLOEXEC, (int ) 3 );
if (fd < 0 )
{
GDK_DEBUG (DMABUF, "Vulkan failed to dup() fd: %s" , g_strerror (errno));
vkDestroyImage (vk_device, self->vk_image, NULL);
self->vk_image = VK_NULL_HANDLE;
g_object_unref (self);
return NULL;
}
GSK_VK_CHECK (func_vkGetMemoryFdPropertiesKHR, vk_device,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
fd,
&fd_props);
vkGetImageMemoryRequirements2 (vk_device,
&(VkImageMemoryRequirementsInfo2) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2,
.image = self->vk_image,
},
&requirements);
if (gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT))
{
int sync_file_fd = gdk_dmabuf_export_sync_file (fd, DMA_BUF_SYNC_READ);
if (sync_file_fd >= 0 )
{
PFN_vkImportSemaphoreFdKHR func_vkImportSemaphoreFdKHR;
func_vkImportSemaphoreFdKHR = (PFN_vkImportSemaphoreFdKHR) vkGetDeviceProcAddr (vk_device, "vkImportSemaphoreFdKHR" );
GSK_VK_CHECK (vkCreateSemaphore, vk_device,
&(VkSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
},
NULL,
&self->vk_semaphore);
GSK_VK_CHECK (func_vkImportSemaphoreFdKHR, vk_device,
&(VkImportSemaphoreFdInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,
.semaphore = self->vk_semaphore,
.fd = sync_file_fd,
});
}
}
memory_index = gsk_vulkan_device_find_allocator (device,
fd_props.memoryTypeBits,
0 ,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
self->allocator = gsk_vulkan_device_get_external_allocator (device);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.memoryRequirements.size,
requirements.memoryRequirements.alignment,
&self->allocation);
res = vkAllocateMemory (vk_device,
&(VkMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.memoryRequirements.size,
.memoryTypeIndex = memory_index,
.pNext = &(VkImportMemoryFdInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
.fd = fd,
.pNext = &(VkMemoryDedicatedAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.image = self->vk_image,
}
}
},
NULL,
&self->allocation.vk_memory);
if (res != VK_SUCCESS)
{
GDK_DEBUG (DMABUF, "vkAllocateMemory() failed: %s" , gdk_vulkan_strerror (res));
g_object_unref (self);
return NULL;
}
GSK_VK_CHECK (vkBindImageMemory2, gsk_vulkan_device_get_vk_device (self->device),
1 ,
&(VkBindImageMemoryInfo) {
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
.image = self->vk_image,
.memory = self->allocation.vk_memory,
.memoryOffset = self->allocation.offset,
});
gsk_vulkan_image_create_view (self, vk_format, &vk_components, vk_conversion);
GDK_DEBUG (DMABUF, "Vulkan uploaded %zux%zu %.4s:%016llx %sdmabuf" ,
width, height,
(char *) &dmabuf->fourcc, (unsigned long long ) dmabuf->modifier,
is_yuv ? "YUV " : "" );
return GSK_GPU_IMAGE (self);
}
static void
close_the_fd (gpointer the_fd)
{
close (GPOINTER_TO_INT (the_fd));
}
static guint
gsk_vulkan_image_get_n_planes (GskVulkanImage *self,
guint64 modifier)
{
VkDrmFormatModifierPropertiesEXT drm_mod_properties[100 ];
VkDrmFormatModifierPropertiesListEXT drm_properties = {
.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
.drmFormatModifierCount = G_N_ELEMENTS (drm_mod_properties),
.pDrmFormatModifierProperties = drm_mod_properties,
};
VkFormatProperties2 properties = {
.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
.pNext = &drm_properties
};
gsize i;
vkGetPhysicalDeviceFormatProperties2 (gsk_vulkan_device_get_vk_physical_device (self->device),
self->vk_format,
&properties);
for (i = 0 ; i < drm_properties.drmFormatModifierCount; i++)
{
if (drm_mod_properties[i].drmFormatModifier == modifier)
return drm_mod_properties[i].drmFormatModifierPlaneCount;
}
g_return_val_if_reached (0 );
}
GdkTexture *
gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self,
GdkColorState *color_state)
{
GskGpuImage *image = GSK_GPU_IMAGE (self);
GdkDmabufTextureBuilder *builder;
GError *error = NULL;
PFN_vkGetImageDrmFormatModifierPropertiesEXT func_vkGetImageDrmFormatModifierPropertiesEXT;
PFN_vkGetMemoryFdKHR func_vkGetMemoryFdKHR;
VkImageDrmFormatModifierPropertiesEXT properties = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
};
VkSubresourceLayout layout;
VkDevice vk_device;
GdkTexture *texture;
GdkMemoryFormat format;
VkResult res;
guint32 fourcc;
int fd;
guint plane, n_planes;
if (!(gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_EXTERNAL))
return FALSE ;
format = gsk_gpu_image_get_format (image);
fourcc = gdk_memory_format_get_dmabuf_rgb_fourcc (format);
if (fourcc == 0 )
fourcc = gdk_memory_format_get_dmabuf_yuv_fourcc (format);
if (fourcc == 0 )
return FALSE ;
vk_device = gsk_vulkan_device_get_vk_device (self->device);
func_vkGetImageDrmFormatModifierPropertiesEXT = (PFN_vkGetImageDrmFormatModifierPropertiesEXT)
vkGetDeviceProcAddr (vk_device, "vkGetImageDrmFormatModifierPropertiesEXT" );
func_vkGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR) vkGetDeviceProcAddr (vk_device, "vkGetMemoryFdKHR" );
res = GSK_VK_CHECK (func_vkGetImageDrmFormatModifierPropertiesEXT, vk_device, self->vk_image, &properties);
if (res != VK_SUCCESS)
return FALSE ;
n_planes = gsk_vulkan_image_get_n_planes (self, properties.drmFormatModifier);
if (n_planes == 0 || n_planes > GDK_DMABUF_MAX_PLANES)
return FALSE ;
res = GSK_VK_CHECK (func_vkGetMemoryFdKHR, vk_device,
&(VkMemoryGetFdInfoKHR) {
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
.memory = self->allocation.vk_memory,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
},
&fd);
if (res != VK_SUCCESS)
return FALSE ;
builder = gdk_dmabuf_texture_builder_new ();
gdk_dmabuf_texture_builder_set_display (builder, gsk_gpu_device_get_display (GSK_GPU_DEVICE (self->device)));
gdk_dmabuf_texture_builder_set_width (builder, gsk_gpu_image_get_width (image));
gdk_dmabuf_texture_builder_set_height (builder, gsk_gpu_image_get_height (image));
gdk_dmabuf_texture_builder_set_fourcc (builder, fourcc);
gdk_dmabuf_texture_builder_set_modifier (builder, properties.drmFormatModifier);
gdk_dmabuf_texture_builder_set_premultiplied (builder, gdk_memory_format_alpha (format) != GDK_MEMORY_ALPHA_STRAIGHT);
gdk_dmabuf_texture_builder_set_n_planes (builder, n_planes);
gdk_dmabuf_texture_builder_set_color_state (builder, color_state);
gdk_dmabuf_texture_builder_set_fd (builder, 0 , fd);
for (plane = 0 ; plane < n_planes; plane++)
{
static const VkImageAspectFlagBits aspect[GDK_DMABUF_MAX_PLANES] = {
VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT,
VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT,
VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT,
VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT
};
vkGetImageSubresourceLayout (vk_device,
self->vk_image,
&(VkImageSubresource) {
.aspectMask = aspect[plane],
.mipLevel = 0 ,
.arrayLayer = 0
},
&layout);
gdk_dmabuf_texture_builder_set_stride (builder, plane, layout.rowPitch);
gdk_dmabuf_texture_builder_set_offset (builder, plane, layout.offset);
}
texture = gdk_dmabuf_texture_builder_build (builder, close_the_fd, GINT_TO_POINTER (fd), &error);
g_object_unref (builder);
if (texture == NULL)
{
GDK_DEBUG (VULKAN, "Failed to create dmabuf texture: %s" , error->message);
g_clear_error (&error);
close (fd);
return NULL;
}
gsk_gpu_image_toggle_ref_texture (GSK_GPU_IMAGE (self), texture);
return texture;
}
#endif
#ifdef GDK_WINDOWING_WIN32
static gboolean
gsk_vulkan_is_same_device (VkDevice vk_device,
ID3D12DeviceChild *child)
{
/* FIXME: implement */
return TRUE ;
}
GskGpuImage *
gsk_vulkan_image_new_for_d3d12resource (GskVulkanDevice *device,
ID3D12Resource *resource,
HANDLE resource_handle,
ID3D12Fence *fence,
HANDLE fence_handle,
guint64 fence_wait,
gboolean premultiplied)
{
GskVulkanImage *self;
VkDevice vk_device;
VkFormat vk_format;
VkFormatFeatureFlags vk_features;
VkComponentMapping vk_components;
VkSamplerYcbcrConversion vk_conversion;
PFN_vkGetMemoryWin32HandlePropertiesKHR func_vkGetMemoryWin32HandlePropertiesKHR;
VkResult res;
GdkMemoryFormat format;
GdkShaderOp shader_op;
GskGpuImageFlags flags;
D3D12_RESOURCE_DESC desc;
gsize memory_index;
gboolean needs_conversion;
VkMemoryRequirements2 requirements = {
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
};
VkMemoryWin32HandlePropertiesKHR handle_properties = {
.sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR,
};
if (!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_WIN32))
{
GDK_DEBUG (D3D12, "Vulkan does not support D3D12Resource import" );
return NULL;
}
vk_device = gsk_vulkan_device_get_vk_device (device);
func_vkGetMemoryWin32HandlePropertiesKHR = (PFN_vkGetMemoryWin32HandlePropertiesKHR) vkGetDeviceProcAddr (vk_device, "vkGetMemoryWin32HandlePropertiesKHR" );
ID3D12Resource_GetDesc (resource, &desc);
if (!gsk_vulkan_is_same_device (vk_device, (ID3D12DeviceChild *) resource))
{
GDK_DEBUG (D3D12, "Resource is from a different device" );
return NULL;
}
if (!gdk_memory_format_find_by_dxgi_format (desc.Format, premultiplied, &format))
{
GDK_DEBUG (D3D12, "Unsupported DXGI format %u" , desc.Format);
return NULL;
}
vk_format = gdk_memory_format_vk_format (format, &vk_components, &needs_conversion);
if (vk_format == VK_FORMAT_UNDEFINED)
{
GDK_DEBUG (D3D12, "GTK's Vulkan doesn't support DXGI format %u" , desc.Format);
return NULL;
}
if (needs_conversion && !gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_YCBCR))
{
GDK_DEBUG (D3D12, "Vulkan driver cannot import DXGI format %u because it lacks YCbCr support" , desc.Format);
return NULL;
}
vk_features = gsk_vulkan_device_supports_format (device,
vk_format,
0 , 1 ,
needs_conversion,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT,
desc.Width, desc.Height,
&flags);
if (vk_features == 0 )
{
GDK_DEBUG (D3D12, "Vulkan driver does not support DXGI format %u" , desc.Format);
return NULL;
}
if (!gsk_vulkan_ycbcr_is_supported (vk_features))
{
if (needs_conversion)
{
GDK_DEBUG (D3D12, "Vulkan driver broken: Can't do YCbcrConversion for YCbcr format %u" , desc.Format);
return NULL;
}
}
if (needs_conversion)
shader_op = GDK_SHADER_DEFAULT;
else
shader_op = gdk_memory_format_get_default_shader_op (format);
flags &= ~(GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_RENDERABLE);
if (desc.MipLevels)
flags |= GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP;
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->device = g_object_ref (device);
self->vk_tiling = VK_IMAGE_TILING_OPTIMAL;
self->vk_format = vk_format;
self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
self->vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
self->vk_access = 0 ;
res = vkCreateImage (vk_device,
&(VkImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = 0 ,
.imageType = VK_IMAGE_TYPE_2D,
.format = vk_format,
.extent = { desc.Width, desc.Height, 1 },
.mipLevels = desc.MipLevels,
.arrayLayers = 1 ,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = self->vk_tiling,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
(flags & (GSK_GPU_IMAGE_BLIT | GSK_GPU_IMAGE_DOWNLOADABLE) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : 0 ),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = self->vk_image_layout,
.pNext = &(VkExternalMemoryImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
},
},
NULL,
&self->vk_image);
if (res != VK_SUCCESS)
{
GDK_DEBUG (D3D12, "vkCreateImage() failed: %s" , gdk_vulkan_strerror (res));
return NULL;
}
vkGetImageMemoryRequirements2 (vk_device,
&(VkImageMemoryRequirementsInfo2) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2,
.image = self->vk_image,
},
&requirements);
// Vulkan memory import
GSK_VK_CHECK (func_vkGetMemoryWin32HandlePropertiesKHR, vk_device,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
resource_handle,
&handle_properties);
memory_index = gsk_vulkan_device_find_allocator (device,
handle_properties.memoryTypeBits,
0 ,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
self->allocator = gsk_vulkan_device_get_external_allocator (device);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.memoryRequirements.size,
requirements.memoryRequirements.alignment,
&self->allocation);
GSK_VK_CHECK (vkAllocateMemory, vk_device,
&(VkMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.memoryRequirements.size,
.memoryTypeIndex = memory_index,
.pNext = &(VkImportMemoryWin32HandleInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
.handle = resource_handle,
.pNext = &(VkMemoryDedicatedAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.image = self->vk_image,
}
}
},
NULL,
&self->allocation.vk_memory);
GSK_VK_CHECK (vkBindImageMemory2, gsk_vulkan_device_get_vk_device (self->device),
1 ,
&(VkBindImageMemoryInfo) {
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
.image = self->vk_image,
.memory = self->allocation.vk_memory,
.memoryOffset = self->allocation.offset,
});
if (gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_WIN32_SEMAPHORE) && fence)
{
PFN_vkImportSemaphoreWin32HandleKHR func_vkImportSemaphoreWin32HandleKHR;
func_vkImportSemaphoreWin32HandleKHR = (PFN_vkImportSemaphoreWin32HandleKHR) vkGetDeviceProcAddr (vk_device, "vkImportSemaphoreWin32HandleKHR" );
GSK_VK_CHECK (vkCreateSemaphore, vk_device,
&(VkSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &(VkSemaphoreTypeCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
},
},
NULL,
&self->vk_semaphore);
GSK_VK_CHECK (func_vkImportSemaphoreWin32HandleKHR, vk_device,
&(VkImportSemaphoreWin32HandleInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT,
.semaphore = self->vk_semaphore,
.handle = fence_handle,
});
self->vk_semaphore_wait = fence_wait;
}
if (needs_conversion)
{
self->ycbcr = gsk_vulkan_ycbcr_get (device,
&(GskVulkanYcbcrInfo) {
.vk_format = vk_format,
.vk_features = vk_features,
.vk_components = vk_components,
.vk_ycbcr_model = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY,
.vk_ycbcr_range = VK_SAMPLER_YCBCR_RANGE_ITU_FULL,
});
gsk_vulkan_ycbcr_ref (self->ycbcr);
if (!gsk_vulkan_ycbcr_is_filterable (self->ycbcr))
flags &= ~(GSK_GPU_IMAGE_FILTERABLE);
vk_conversion = gsk_vulkan_ycbcr_get_vk_conversion (self->ycbcr);
}
else
vk_conversion = VK_NULL_HANDLE;
gsk_gpu_image_setup (GSK_GPU_IMAGE (self),
flags |
(needs_conversion ? GSK_GPU_IMAGE_EXTERNAL : 0 ) |
(desc.MipLevels > 1 ? GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP : 0 ),
GSK_GPU_CONVERSION_NONE,
shader_op,
format,
desc.Width, desc.Height);
gsk_vulkan_image_create_view (self,
vk_format,
&vk_components,
vk_conversion);
GDK_DEBUG (D3D12, "Vulkan uploaded %ux%u resource of %sformat %u" ,
(guint) desc.Width, (guint) desc.Height,
needs_conversion ? "YUV " : "" ,
desc.Format);
return GSK_GPU_IMAGE (self);
}
#endif
static void
gsk_vulkan_image_get_projection_matrix (GskGpuImage *image,
graphene_matrix_t *out_projection)
{
graphene_matrix_t scale_z;
GSK_GPU_IMAGE_CLASS (gsk_vulkan_image_parent_class)->get_projection_matrix (image, out_projection);
graphene_matrix_init_from_float (&scale_z,
(float [16 ]) {
1 , 0 , 0 , 0 ,
0 , 1 , 0 , 0 ,
0 , 0 , 0 .5 , 0 ,
0 , 0 , 0 .5 , 1
});
graphene_matrix_multiply (out_projection, &scale_z, out_projection);
}
static void
gsk_vulkan_image_finalize (GObject *object)
{
GskVulkanImage *self = GSK_VULKAN_IMAGE (object);
VkDevice vk_device;
gsize i;
vk_device = gsk_vulkan_device_get_vk_device (self->device);
g_clear_pointer (&self->ycbcr, gsk_vulkan_ycbcr_unref);
for (i = 0 ; i < GSK_GPU_SAMPLER_N_SAMPLERS; i++)
{
if (self->descriptor_sets[i][0 ].vk_descriptor_set)
gsk_vulkan_device_free_descriptor (self->device,
self->descriptor_sets[i][0 ].pool_id,
self->descriptor_sets[i][0 ].vk_descriptor_set);
if (self->descriptor_sets[i][1 ].vk_descriptor_set)
gsk_vulkan_device_free_descriptor (self->device,
self->descriptor_sets[i][1 ].pool_id,
self->descriptor_sets[i][1 ].vk_descriptor_set);
}
if (self->vk_framebuffer != VK_NULL_HANDLE)
vkDestroyFramebuffer (vk_device, self->vk_framebuffer, NULL);
if (self->vk_framebuffer_image_view != VK_NULL_HANDLE &&
self->vk_framebuffer_image_view != self->vk_image_view)
vkDestroyImageView (vk_device, self->vk_framebuffer_image_view, NULL);
if (self->vk_image_view != VK_NULL_HANDLE)
vkDestroyImageView (vk_device, self->vk_image_view, NULL);
if (self->vk_semaphore != VK_NULL_HANDLE)
vkDestroySemaphore (vk_device, self->vk_semaphore, NULL);
/* memory is NULL for for_swapchain() images, where we don't own
* the VkImage */
if (self->allocator)
{
vkDestroyImage (vk_device, self->vk_image, NULL);
gsk_vulkan_free (self->allocator, &self->allocation);
gsk_vulkan_allocator_unref (self->allocator);
}
g_object_unref (self->device);
G_OBJECT_CLASS (gsk_vulkan_image_parent_class)->finalize (object);
}
static void
gsk_vulkan_image_class_init (GskVulkanImageClass *klass)
{
GskGpuImageClass *image_class = GSK_GPU_IMAGE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
image_class->get_projection_matrix = gsk_vulkan_image_get_projection_matrix;
object_class->finalize = gsk_vulkan_image_finalize;
}
static void
gsk_vulkan_image_init (GskVulkanImage *self)
{
}
VkFramebuffer
gsk_vulkan_image_get_vk_framebuffer (GskVulkanImage *self,
VkRenderPass render_pass)
{
VkDevice vk_device;
if (self->vk_framebuffer)
return self->vk_framebuffer;
vk_device = gsk_vulkan_device_get_vk_device (self->device);
if (gsk_gpu_image_get_flags (GSK_GPU_IMAGE (self)) & GSK_GPU_IMAGE_CAN_MIPMAP)
{
GSK_VK_CHECK (vkCreateImageView, vk_device,
&(VkImageViewCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = self->vk_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = self->vk_format,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0 ,
.levelCount = 1 ,
.baseArrayLayer = 0 ,
.layerCount = 1 ,
}
},
NULL,
&self->vk_framebuffer_image_view);
}
else
{
self->vk_framebuffer_image_view = self->vk_image_view;
}
GSK_VK_CHECK (vkCreateFramebuffer, vk_device,
&(VkFramebufferCreateInfo) {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = render_pass,
.attachmentCount = 1 ,
.pAttachments = (VkImageView[1 ]) {
self->vk_framebuffer_image_view,
},
.width = gsk_gpu_image_get_width (GSK_GPU_IMAGE (self)),
.height = gsk_gpu_image_get_height (GSK_GPU_IMAGE (self)),
.layers = 1
},
NULL,
&self->vk_framebuffer);
return self->vk_framebuffer;
}
VkDescriptorSet
gsk_vulkan_image_get_vk_descriptor_set (GskVulkanImage *self,
GskGpuSampler sampler,
gboolean as_mask)
{
guint id = as_mask ? 1 : 0 ;
if (!self->descriptor_sets[sampler][id].vk_descriptor_set)
{
VkDescriptorSetLayout desc_layout;
uint32_t n_descriptors;
if (self->ycbcr)
{
g_assert (!as_mask);
desc_layout = gsk_vulkan_ycbcr_get_vk_descriptor_set_layout (self->ycbcr);
n_descriptors = 3 ;
}
else if (as_mask)
{
desc_layout = gsk_vulkan_device_get_vk_mask_set_layout (self->device);
n_descriptors = 1 ;
}
else
{
desc_layout = gsk_vulkan_device_get_vk_image_set_layout (self->device);
n_descriptors = 3 ;
}
self->descriptor_sets[sampler][id].vk_descriptor_set =
gsk_vulkan_device_allocate_descriptor (self->device,
desc_layout,
&self->descriptor_sets[sampler][id].pool_id);
vkUpdateDescriptorSets (gsk_vulkan_device_get_vk_device (self->device),
1 ,
&(VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self->descriptor_sets[sampler][id].vk_descriptor_set,
.dstBinding = 0 ,
.dstArrayElement = 0 ,
.descriptorCount = n_descriptors,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = (VkDescriptorImageInfo[3 ]) {
{
.sampler = self->ycbcr ? gsk_vulkan_ycbcr_get_vk_sampler (self->ycbcr)
: gsk_vulkan_device_get_vk_sampler (self->device, sampler),
.imageView = self->vk_image_view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
},
{
.sampler = self->ycbcr ? gsk_vulkan_ycbcr_get_vk_sampler (self->ycbcr)
: gsk_vulkan_device_get_vk_sampler (self->device, sampler),
.imageView = self->vk_image_view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
},
{
.sampler = self->ycbcr ? gsk_vulkan_ycbcr_get_vk_sampler (self->ycbcr)
: gsk_vulkan_device_get_vk_sampler (self->device, sampler),
.imageView = self->vk_image_view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
},
},
0 ,
NULL);
}
return self->descriptor_sets[sampler][id].vk_descriptor_set;
}
GskVulkanYcbcr *
gsk_vulkan_image_get_ycbcr (GskVulkanImage *self)
{
return self->ycbcr;
}
VkImage
gsk_vulkan_image_get_vk_image (GskVulkanImage *self)
{
return self->vk_image;
}
VkImageView
gsk_vulkan_image_get_vk_image_view (GskVulkanImage *self)
{
return self->vk_image_view;
}
VkPipelineStageFlags
gsk_vulkan_image_get_vk_pipeline_stage (GskVulkanImage *self)
{
return self->vk_pipeline_stage;
}
VkImageLayout
gsk_vulkan_image_get_vk_image_layout (GskVulkanImage *self)
{
return self->vk_image_layout;
}
VkAccessFlags
gsk_vulkan_image_get_vk_access (GskVulkanImage *self)
{
return self->vk_access;
}
void
gsk_vulkan_image_set_vk_image_layout (GskVulkanImage *self,
VkPipelineStageFlags stage,
VkImageLayout image_layout,
VkAccessFlags access)
{
self->vk_pipeline_stage = stage;
self->vk_image_layout = image_layout;
self->vk_access = access;
}
void
gsk_vulkan_image_transition (GskVulkanImage *self,
GskVulkanSemaphores *semaphores,
VkCommandBuffer command_buffer,
VkPipelineStageFlags stage,
VkImageLayout image_layout,
VkAccessFlags access)
{
if (self->vk_pipeline_stage == stage &&
self->vk_image_layout == image_layout &&
self->vk_access == access)
return ;
if (self->vk_pipeline_stage == VK_IMAGE_LAYOUT_GENERAL &&
self->vk_semaphore)
{
gsk_vulkan_semaphores_add_wait (semaphores,
self->vk_semaphore,
self->vk_semaphore_wait,
stage);
}
vkCmdPipelineBarrier (command_buffer,
self->vk_pipeline_stage,
stage,
0 ,
0 , NULL,
0 , NULL,
1 , &(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = self->vk_access,
.dstAccessMask = access,
.oldLayout = self->vk_image_layout,
.newLayout = image_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0 ,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0 ,
.layerCount = 1
},
});
gsk_vulkan_image_set_vk_image_layout (self, stage, image_layout, access);
}
VkFormat
gsk_vulkan_image_get_vk_format (GskVulkanImage *self)
{
return self->vk_format;
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland