#include "config.h"
#include "gskvulkanycbcrprivate.h"
#include "gskgpucacheprivate.h"
#include "gskgpucachedprivate.h"
struct _GskVulkanYcbcr
{
GskGpuCached parent;
int ref_count;
GskVulkanYcbcrInfo info;
VkSamplerYcbcrConversion vk_conversion;
VkSampler vk_sampler;
VkDescriptorSetLayout vk_descriptor_set_layout;
VkPipelineLayout vk_pipeline_layouts[2 ];
};
static guint
gsk_vulkan_ycbcr_info_hash (gconstpointer info_)
{
const GskVulkanYcbcrInfo *info = info_;
return ((info->vk_components.r << 29 ) |
(info->vk_components.g << 26 ) |
(info->vk_components.b << 23 ) |
(info->vk_components.a << 20 ) |
(info->vk_ycbcr_model << 17 ) |
(info->vk_ycbcr_range << 16 )) ^
info->vk_features ^
info->vk_format;
}
static gboolean
gsk_vulkan_ycbcr_info_equal (gconstpointer info1_,
gconstpointer info2_)
{
const GskVulkanYcbcrInfo *info1 = info1_;
const GskVulkanYcbcrInfo *info2 = info2_;
return info1->vk_format == info2->vk_format &&
info1->vk_features == info2->vk_features &&
info1->vk_components.r == info2->vk_components.r &&
info1->vk_components.g == info2->vk_components.g &&
info1->vk_components.b == info2->vk_components.b &&
info1->vk_components.a == info2->vk_components.a &&
info1->vk_ycbcr_model == info2->vk_ycbcr_model &&
info1->vk_ycbcr_range == info2->vk_ycbcr_range;
}
static void
gsk_vulkan_ycbcr_finalize (GskGpuCached *cached)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cached->cache);
GskVulkanYcbcr *self = (GskVulkanYcbcr *) cached;
GskGpuCache *cache = cached->cache;
GskVulkanDevice *device;
VkDevice vk_device;
device = GSK_VULKAN_DEVICE (gsk_gpu_cache_get_device (cache));
vk_device = gsk_vulkan_device_get_vk_device (device);
g_assert (self->ref_count == 0 );
g_hash_table_remove (priv->ycbcr_cache, &self->info);
vkDestroySampler (vk_device, self->vk_sampler, NULL);
vkDestroySamplerYcbcrConversion (vk_device, self->vk_conversion, NULL);
vkDestroyDescriptorSetLayout (vk_device, self->vk_descriptor_set_layout, NULL);
vkDestroyPipelineLayout (vk_device, self->vk_pipeline_layouts[0 ], NULL);
vkDestroyPipelineLayout (vk_device, self->vk_pipeline_layouts[1 ], NULL);
}
static gboolean
gsk_vulkan_ycbcr_should_collect (GskGpuCached *cached,
gint64 cache_timeout,
gint64 timestamp)
{
GskVulkanYcbcr *self = (GskVulkanYcbcr *) cached;
if (self->ref_count > 0 )
return FALSE ;
return gsk_gpu_cached_is_old (cached, cache_timeout, timestamp);
}
static const GskGpuCachedClass GSK_VULKAN_YCBCR_CLASS =
{
sizeof (GskVulkanYcbcr),
"Vulkan Ycbcr" ,
FALSE ,
gsk_gpu_cached_print_no_stats,
gsk_vulkan_ycbcr_finalize,
gsk_vulkan_ycbcr_should_collect
};
void
gsk_vulkan_ycbcr_init_cache (GskGpuCache *cache)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
priv->ycbcr_cache = g_hash_table_new (gsk_vulkan_ycbcr_info_hash, gsk_vulkan_ycbcr_info_equal);
}
void
gsk_vulkan_ycbcr_finish_cache (GskGpuCache *cache)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
g_assert (g_hash_table_size (priv->ycbcr_cache) == 0 );
g_hash_table_unref (priv->ycbcr_cache);
}
gboolean
gsk_vulkan_ycbcr_is_supported (VkFormatFeatureFlags vk_features)
{
/* If neither VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT nor VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT is set,
* the application must not define a sampler Y ′ CBCR conversion using this format as a source .
*/
if (!(vk_features & (VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT | VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT)))
return FALSE ;
return TRUE ;
}
static VkChromaLocation
gsk_vulkan_ycbcr_get_best_vk_chroma (VkFormatFeatureFlags vk_features)
{
if (vk_features & VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT)
{
return VK_CHROMA_LOCATION_COSITED_EVEN;
}
else if (vk_features & VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT)
{
return VK_CHROMA_LOCATION_MIDPOINT;
}
else
{
/* Checked via gsk_vulkan_ycbcr_is_supported () */
g_return_val_if_reached (VK_CHROMA_LOCATION_COSITED_EVEN);
}
}
GskVulkanYcbcr *
gsk_vulkan_ycbcr_get (GskVulkanDevice *device,
const GskVulkanYcbcrInfo *info)
{
GskGpuCache *cache = gsk_gpu_device_get_cache (GSK_GPU_DEVICE (device));
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
VkDevice vk_device = gsk_vulkan_device_get_vk_device (device);
VkDescriptorSetLayout vk_image_set_layout;
VkChromaLocation vk_chroma;
GskVulkanYcbcr *self;
self = g_hash_table_lookup (priv->ycbcr_cache, info);
if (self)
return self;
/* We expect calling code to check this */
g_assert (gsk_vulkan_ycbcr_is_supported (info->vk_features));
self = gsk_gpu_cached_new (cache, &GSK_VULKAN_YCBCR_CLASS);
self->info = *info;
vk_chroma = gsk_vulkan_ycbcr_get_best_vk_chroma (info->vk_features);
GSK_VK_CHECK (vkCreateSamplerYcbcrConversion, vk_device,
&(VkSamplerYcbcrConversionCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.format = self->info.vk_format,
.ycbcrModel = self->info.vk_ycbcr_model,
.ycbcrRange = self->info.vk_ycbcr_range,
.components = self->info.vk_components,
.xChromaOffset = vk_chroma,
.yChromaOffset = vk_chroma,
.chromaFilter = info->vk_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT
? VK_FILTER_LINEAR : VK_FILTER_NEAREST,
.forceExplicitReconstruction = VK_FALSE
},
NULL,
&self->vk_conversion);
GSK_VK_CHECK (vkCreateSampler, vk_device,
&(VkSamplerCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = gsk_vulkan_ycbcr_is_filterable (self) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST,
.minFilter = gsk_vulkan_ycbcr_is_filterable (self) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
.unnormalizedCoordinates = VK_FALSE,
.maxAnisotropy = 1 .0 ,
.minLod = 0 .0 ,
.maxLod = 0 .0 f,
.pNext = &(VkSamplerYcbcrConversionInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = self->vk_conversion
}
},
NULL,
&self->vk_sampler);
GSK_VK_CHECK (vkCreateDescriptorSetLayout, vk_device,
&(VkDescriptorSetLayoutCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1 ,
.flags = 0 ,
.pBindings = (VkDescriptorSetLayoutBinding[1 ]) {
{
.binding = 0 ,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 3 ,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = (VkSampler[3 ]) {
self->vk_sampler,
self->vk_sampler,
self->vk_sampler,
},
}
},
},
NULL,
&self->vk_descriptor_set_layout);
vk_image_set_layout = gsk_vulkan_device_get_vk_image_set_layout (device);
self->vk_pipeline_layouts[0 ] = gsk_vulkan_device_create_vk_pipeline_layout (device,
self->vk_descriptor_set_layout,
vk_image_set_layout);
self->vk_pipeline_layouts[1 ] = gsk_vulkan_device_create_vk_pipeline_layout (device,
vk_image_set_layout,
self->vk_descriptor_set_layout);
g_hash_table_insert (priv->ycbcr_cache, &self->info, self);
return self;
}
GskVulkanYcbcr *
gsk_vulkan_ycbcr_ref (GskVulkanYcbcr *self)
{
self->ref_count++;
return self;
}
void
gsk_vulkan_ycbcr_unref (GskVulkanYcbcr *self)
{
self->ref_count--;
gsk_gpu_cached_use ((GskGpuCached *) self);
}
gboolean
gsk_vulkan_ycbcr_is_filterable (GskVulkanYcbcr *self)
{
/* VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT specifies that
* the format can have different chroma , min , and mag filters .
*/
if (self->info.vk_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT)
return TRUE ;
/* VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT specifies that an application
* can define a sampler Y ′ CBCR conversion using this format as a source with chromaFilter set to VK_FILTER_LINEAR .
*/
if (self->info.vk_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT)
return TRUE ;
return FALSE ;
}
VkSamplerYcbcrConversion
gsk_vulkan_ycbcr_get_vk_conversion (GskVulkanYcbcr *self)
{
return self->vk_conversion;
}
VkSampler
gsk_vulkan_ycbcr_get_vk_sampler (GskVulkanYcbcr *self)
{
return self->vk_sampler;
}
VkDescriptorSetLayout
gsk_vulkan_ycbcr_get_vk_descriptor_set_layout (GskVulkanYcbcr *self)
{
return self->vk_descriptor_set_layout;
}
VkPipelineLayout
gsk_vulkan_ycbcr_get_vk_pipeline_layout (GskVulkanYcbcr *self,
gsize id)
{
g_assert (id < 2 );
return self->vk_pipeline_layouts[id];
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland