gtk/gsk/gpu/gskvulkandevice.c
Benjamin Otte 5b184a10a9 gpu: Add a CLEAR blend mode for subsurface hole punching
The previous code was ignoring non-scissor clips, which would make it
overeager at punching holes.

It also was not working with fractional coordinates.

Fixes #6375
2024-02-09 04:28:32 +01:00

1253 lines
54 KiB
C

#include "config.h"
#include "gskvulkandeviceprivate.h"
#include "gskgpuglobalsopprivate.h"
#include "gskgpushaderopprivate.h"
#include "gskvulkanbufferprivate.h"
#include "gskvulkanimageprivate.h"
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdkvulkancontextprivate.h"
struct _GskVulkanDevice
{
GskGpuDevice parent_instance;
GskVulkanAllocator *allocators[VK_MAX_MEMORY_TYPES];
GskVulkanAllocator *external_allocator;
GdkVulkanFeatures features;
guint max_immutable_samplers;
guint max_samplers;
guint max_buffers;
GHashTable *conversion_cache;
GHashTable *render_pass_cache;
GHashTable *pipeline_layouts;
GskVulkanPipelineLayout *pipeline_layout_cache;
VkCommandPool vk_command_pool;
VkSampler vk_samplers[GSK_GPU_SAMPLER_N_SAMPLERS];
};
struct _GskVulkanDeviceClass
{
GskGpuDeviceClass parent_class;
};
G_DEFINE_TYPE (GskVulkanDevice, gsk_vulkan_device, GSK_TYPE_GPU_DEVICE)
typedef struct _ConversionCacheEntry ConversionCacheEntry;
typedef struct _PipelineCacheKey PipelineCacheKey;
typedef struct _RenderPassCacheKey RenderPassCacheKey;
typedef struct _GskVulkanPipelineLayoutSetup GskVulkanPipelineLayoutSetup;
struct _GskVulkanPipelineLayoutSetup
{
gsize n_buffers;
gsize n_samplers;
gsize n_immutable_samplers;
VkSampler *immutable_samplers;
};
struct _GskVulkanPipelineLayout
{
gint ref_count;
VkDescriptorSetLayout vk_buffer_set_layout;
VkDescriptorSetLayout vk_image_set_layout;
VkPipelineLayout vk_pipeline_layout;
GHashTable *pipeline_cache;
GskVulkanPipelineLayoutSetup setup;
VkSampler samplers[];
};
struct _ConversionCacheEntry
{
VkFormat vk_format;
/* actual data */
VkSamplerYcbcrConversion vk_conversion;
VkSampler vk_sampler;
};
struct _PipelineCacheKey
{
const GskGpuShaderOpClass *op_class;
guint32 variation;
GskGpuShaderClip clip;
GskGpuBlend blend;
VkFormat format;
};
struct _RenderPassCacheKey
{
VkFormat format;
VkImageLayout from_layout;
VkImageLayout to_layout;
};
static guint
conversion_cache_entry_hash (gconstpointer data)
{
const ConversionCacheEntry *key = data;
return key->vk_format;
}
static gboolean
conversion_cache_entry_equal (gconstpointer a,
gconstpointer b)
{
const ConversionCacheEntry *keya = a;
const ConversionCacheEntry *keyb = b;
return keya->vk_format == keyb->vk_format;
}
static guint
pipeline_cache_key_hash (gconstpointer data)
{
const PipelineCacheKey *key = data;
return GPOINTER_TO_UINT (key->op_class) ^
key->clip ^
(key->variation << 2) ^
(key->blend << 6) ^
(key->format << 8);
}
static gboolean
pipeline_cache_key_equal (gconstpointer a,
gconstpointer b)
{
const PipelineCacheKey *keya = a;
const PipelineCacheKey *keyb = b;
return keya->op_class == keyb->op_class &&
keya->variation == keyb->variation &&
keya->clip == keyb->clip &&
keya->blend == keyb->blend &&
keya->format == keyb->format;
}
static guint
render_pass_cache_key_hash (gconstpointer data)
{
const RenderPassCacheKey *key = data;
return (key->from_layout << 20) ^
(key->to_layout << 16) ^
(key->format);
}
static gboolean
render_pass_cache_key_equal (gconstpointer a,
gconstpointer b)
{
const RenderPassCacheKey *keya = a;
const RenderPassCacheKey *keyb = b;
return keya->from_layout == keyb->from_layout &&
keya->to_layout == keyb->to_layout &&
keya->format == keyb->format;
}
static GskVulkanPipelineLayout *
gsk_vulkan_pipeline_layout_new (GskVulkanDevice *self,
const GskVulkanPipelineLayoutSetup *setup)
{
GskVulkanPipelineLayout *layout;
GdkDisplay *display;
gboolean descriptor_indexing;
descriptor_indexing = gsk_vulkan_device_has_feature (self, GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING);
layout = g_malloc (sizeof (GskVulkanPipelineLayout) + setup->n_immutable_samplers * sizeof (VkSampler));
layout->ref_count = 1;
layout->setup = *setup;
if (setup->n_immutable_samplers)
memcpy (layout->samplers, setup->immutable_samplers, setup->n_immutable_samplers * sizeof (VkSampler));
layout->setup.immutable_samplers = layout->samplers;
layout->pipeline_cache = g_hash_table_new (pipeline_cache_key_hash, pipeline_cache_key_equal);
display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self));
GSK_VK_CHECK (vkCreateDescriptorSetLayout, display->vk_device,
&(VkDescriptorSetLayoutCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 2,
.flags = 0,
.pBindings = (VkDescriptorSetLayoutBinding[2]) {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = MAX (1, layout->setup.n_immutable_samplers),
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = layout->setup.n_immutable_samplers
? layout->setup.immutable_samplers
: (VkSampler[1]) {
gsk_vulkan_device_get_vk_sampler (self, GSK_GPU_SAMPLER_DEFAULT)
},
},
{
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = layout->setup.n_samplers,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
}
},
.pNext = !descriptor_indexing ? NULL : &(VkDescriptorSetLayoutBindingFlagsCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.bindingCount = 2,
.pBindingFlags = (VkDescriptorBindingFlags[2]) {
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT,
},
}
},
NULL,
&layout->vk_image_set_layout);
GSK_VK_CHECK (vkCreateDescriptorSetLayout, display->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_STORAGE_BUFFER,
.descriptorCount = layout->setup.n_buffers,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
},
},
.pNext = !descriptor_indexing ? NULL : &(VkDescriptorSetLayoutBindingFlagsCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.bindingCount = 1,
.pBindingFlags = (VkDescriptorBindingFlags[1]) {
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT,
},
}
},
NULL,
&layout->vk_buffer_set_layout);
GSK_VK_CHECK (vkCreatePipelineLayout, display->vk_device,
&(VkPipelineLayoutCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 2,
.pSetLayouts = (VkDescriptorSetLayout[2]) {
layout->vk_image_set_layout,
layout->vk_buffer_set_layout,
},
.pushConstantRangeCount = 1,
.pPushConstantRanges = (VkPushConstantRange[1]) {
{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = 0,
.size = sizeof (GskGpuGlobalsInstance)
}
}
},
NULL,
&layout->vk_pipeline_layout);
g_hash_table_insert (self->pipeline_layouts, &layout->setup, layout);
return layout;
}
static void
gsk_vulkan_pipeline_layout_unref (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout)
{
GdkDisplay *display;
GHashTableIter iter;
gpointer key, value;
layout->ref_count--;
if (layout->ref_count)
return;
if (!g_hash_table_remove (self->pipeline_layouts, &layout->setup))
{
g_assert_not_reached ();
}
display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self));
vkDestroyDescriptorSetLayout (display->vk_device,
layout->vk_image_set_layout,
NULL);
vkDestroyDescriptorSetLayout (display->vk_device,
layout->vk_buffer_set_layout,
NULL);
vkDestroyPipelineLayout (display->vk_device,
layout->vk_pipeline_layout,
NULL);
g_hash_table_iter_init (&iter, layout->pipeline_cache);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_free (key);
vkDestroyPipeline (display->vk_device, value, NULL);
}
g_hash_table_unref (layout->pipeline_cache);
g_free (layout);
}
static void
gsk_vulkan_pipeline_layout_ref (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout)
{
layout->ref_count++;
}
static guint
gsk_vulkan_pipeline_layout_setup_hash (gconstpointer data)
{
const GskVulkanPipelineLayoutSetup *setup = data;
guint result;
gsize i;
result = (setup->n_buffers << 23) |
(setup->n_samplers << 7) |
setup->n_immutable_samplers;
for (i = 0; i < setup->n_immutable_samplers; i++)
{
result = (result << 13) ^
GPOINTER_TO_SIZE (setup->immutable_samplers[i]) ^
(GPOINTER_TO_SIZE (setup->immutable_samplers[i]) >> 32);
}
return result;
}
static gboolean
gsk_vulkan_pipeline_layout_setup_equal (gconstpointer a,
gconstpointer b)
{
const GskVulkanPipelineLayoutSetup *setupa = a;
const GskVulkanPipelineLayoutSetup *setupb = b;
gsize i;
if (setupa->n_buffers != setupb->n_buffers ||
setupa->n_samplers != setupb->n_samplers ||
setupa->n_immutable_samplers != setupb->n_immutable_samplers)
return FALSE;
for (i = 0; i < setupa->n_immutable_samplers; i++)
{
if (setupa->immutable_samplers[i] != setupb->immutable_samplers[i])
return FALSE;
}
return TRUE;
}
static GskGpuImage *
gsk_vulkan_device_create_offscreen_image (GskGpuDevice *device,
gboolean with_mipmap,
GdkMemoryDepth depth,
gsize width,
gsize height)
{
GskVulkanDevice *self = GSK_VULKAN_DEVICE (device);
return gsk_vulkan_image_new_for_offscreen (self,
with_mipmap,
gdk_memory_depth_get_format (depth),
width,
height);
}
static GskGpuImage *
gsk_vulkan_device_create_atlas_image (GskGpuDevice *device,
gsize width,
gsize height)
{
GskVulkanDevice *self = GSK_VULKAN_DEVICE (device);
return gsk_vulkan_image_new_for_atlas (self,
width,
height);
}
static GskGpuImage *
gsk_vulkan_device_create_upload_image (GskGpuDevice *device,
gboolean with_mipmap,
GdkMemoryFormat format,
gsize width,
gsize height)
{
GskVulkanDevice *self = GSK_VULKAN_DEVICE (device);
return gsk_vulkan_image_new_for_upload (self,
with_mipmap,
format,
width,
height);
}
static GskGpuImage *
gsk_vulkan_device_create_download_image (GskGpuDevice *device,
GdkMemoryDepth depth,
gsize width,
gsize height)
{
GskVulkanDevice *self = GSK_VULKAN_DEVICE (device);
GskGpuImage *image;
#ifdef HAVE_DMABUF
image = gsk_vulkan_image_new_dmabuf (self,
gdk_memory_depth_get_format (depth),
width,
height);
if (image != NULL)
return image;
#endif
image = gsk_vulkan_image_new_for_offscreen (self,
FALSE,
gdk_memory_depth_get_format (depth),
width,
height);
return image;
}
static void
gsk_vulkan_device_make_current (GskGpuDevice *device)
{
}
static void
gsk_vulkan_device_finalize (GObject *object)
{
GskVulkanDevice *self = GSK_VULKAN_DEVICE (object);
GskGpuDevice *device = GSK_GPU_DEVICE (self);
GdkDisplay *display;
GHashTableIter iter;
gpointer key, value;
gsize i;
g_object_steal_data (G_OBJECT (gsk_gpu_device_get_display (device)), "-gsk-vulkan-device");
display = gsk_gpu_device_get_display (device);
if (self->pipeline_layout_cache)
gsk_vulkan_pipeline_layout_unref (self, self->pipeline_layout_cache);
g_assert (g_hash_table_size (self->pipeline_layouts) == 0);
g_hash_table_unref (self->pipeline_layouts);
g_hash_table_iter_init (&iter, self->conversion_cache);
while (g_hash_table_iter_next (&iter, &key, &value))
{
ConversionCacheEntry *entry = key;
vkDestroySamplerYcbcrConversion (display->vk_device, entry->vk_conversion, NULL);
vkDestroySampler (display->vk_device, entry->vk_sampler, NULL);
g_free (key);
}
g_hash_table_unref (self->conversion_cache);
g_hash_table_iter_init (&iter, self->render_pass_cache);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_free (key);
vkDestroyRenderPass (display->vk_device, value, NULL);
}
g_hash_table_unref (self->render_pass_cache);
for (i = 0; i < G_N_ELEMENTS (self->vk_samplers); i++)
{
if (self->vk_samplers[i] != VK_NULL_HANDLE)
vkDestroySampler (display->vk_device,
self->vk_samplers[i],
NULL);
}
vkDestroyCommandPool (display->vk_device,
self->vk_command_pool,
NULL);
for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
g_clear_pointer (&self->allocators[i], gsk_vulkan_allocator_unref);
g_clear_pointer (&self->external_allocator, gsk_vulkan_allocator_unref);
gdk_display_unref_vulkan (display);
G_OBJECT_CLASS (gsk_vulkan_device_parent_class)->finalize (object);
}
static void
gsk_vulkan_device_class_init (GskVulkanDeviceClass *klass)
{
GskGpuDeviceClass *gpu_device_class = GSK_GPU_DEVICE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
gpu_device_class->create_offscreen_image = gsk_vulkan_device_create_offscreen_image;
gpu_device_class->create_atlas_image = gsk_vulkan_device_create_atlas_image;
gpu_device_class->create_upload_image = gsk_vulkan_device_create_upload_image;
gpu_device_class->create_download_image = gsk_vulkan_device_create_download_image;
gpu_device_class->make_current = gsk_vulkan_device_make_current;
object_class->finalize = gsk_vulkan_device_finalize;
}
static void
gsk_vulkan_device_init (GskVulkanDevice *self)
{
self->conversion_cache = g_hash_table_new (conversion_cache_entry_hash, conversion_cache_entry_equal);
self->render_pass_cache = g_hash_table_new (render_pass_cache_key_hash, render_pass_cache_key_equal);
self->pipeline_layouts = g_hash_table_new (gsk_vulkan_pipeline_layout_setup_hash, gsk_vulkan_pipeline_layout_setup_equal);
}
static void
gsk_vulkan_device_create_vk_objects (GskVulkanDevice *self)
{
GdkDisplay *display;
display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self));
GSK_VK_CHECK (vkCreateCommandPool, display->vk_device,
&(const VkCommandPoolCreateInfo) {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.queueFamilyIndex = display->vk_queue_family_index,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
},
NULL,
&self->vk_command_pool);
}
static void
gsk_vulkan_device_setup (GskVulkanDevice *self,
GdkDisplay *display)
{
VkPhysicalDeviceVulkan12Properties vk12_props = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES,
};
VkPhysicalDeviceProperties2 vk_props = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &vk12_props
};
vkGetPhysicalDeviceProperties2 (display->vk_physical_device, &vk_props);
if (gsk_vulkan_device_has_feature (self, GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING))
{
self->max_buffers = vk12_props.maxPerStageDescriptorUpdateAfterBindUniformBuffers;
self->max_samplers = vk12_props.maxPerStageDescriptorUpdateAfterBindSampledImages;
}
else
{
self->max_buffers = vk_props.properties.limits.maxPerStageDescriptorUniformBuffers;
self->max_samplers = vk_props.properties.limits.maxPerStageDescriptorSampledImages;
}
if (!gsk_vulkan_device_has_feature (self, GDK_VULKAN_FEATURE_DYNAMIC_INDEXING) ||
!gsk_vulkan_device_has_feature (self, GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING))
{
/* These numbers can be improved in the shader sources by adding more
* entries to the big if() ladders */
self->max_buffers = MIN (self->max_buffers, 32);
self->max_samplers = MIN (self->max_samplers, 32);
}
self->max_immutable_samplers = MIN (self->max_samplers / 3, 32);
gsk_gpu_device_setup (GSK_GPU_DEVICE (self),
display,
vk_props.properties.limits.maxImageDimension2D);
}
GskGpuDevice *
gsk_vulkan_device_get_for_display (GdkDisplay *display,
GError **error)
{
GskVulkanDevice *self;
self = g_object_get_data (G_OBJECT (display), "-gsk-vulkan-device");
if (self)
return GSK_GPU_DEVICE (g_object_ref (self));
if (!gdk_display_init_vulkan (display, error))
return NULL;
self = g_object_new (GSK_TYPE_VULKAN_DEVICE, NULL);
self->features = display->vulkan_features;
gsk_vulkan_device_setup (self, display);
gsk_vulkan_device_create_vk_objects (self);
g_object_set_data (G_OBJECT (display), "-gsk-vulkan-device", self);
return GSK_GPU_DEVICE (self);
}
gsize
gsk_vulkan_device_get_max_immutable_samplers (GskVulkanDevice *self)
{
return self->max_immutable_samplers;
}
gsize
gsk_vulkan_device_get_max_samplers (GskVulkanDevice *self)
{
return self->max_samplers;
}
gsize
gsk_vulkan_device_get_max_buffers (GskVulkanDevice *self)
{
return self->max_buffers;
}
gboolean
gsk_vulkan_device_has_feature (GskVulkanDevice *self,
GdkVulkanFeatures feature)
{
return (self->features & feature) == feature;
}
VkDevice
gsk_vulkan_device_get_vk_device (GskVulkanDevice *self)
{
return gsk_gpu_device_get_display (GSK_GPU_DEVICE (self))->vk_device;
}
VkPhysicalDevice
gsk_vulkan_device_get_vk_physical_device (GskVulkanDevice *self)
{
return gsk_gpu_device_get_display (GSK_GPU_DEVICE (self))->vk_physical_device;
}
VkQueue
gsk_vulkan_device_get_vk_queue (GskVulkanDevice *self)
{
return gsk_gpu_device_get_display (GSK_GPU_DEVICE (self))->vk_queue;
}
uint32_t
gsk_vulkan_device_get_vk_queue_family_index (GskVulkanDevice *self)
{
return gsk_gpu_device_get_display (GSK_GPU_DEVICE (self))->vk_queue_family_index;
}
VkDescriptorSetLayout
gsk_vulkan_device_get_vk_image_set_layout (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout)
{
return layout->vk_image_set_layout;
}
VkDescriptorSetLayout
gsk_vulkan_device_get_vk_buffer_set_layout (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout)
{
return layout->vk_buffer_set_layout;
}
VkPipelineLayout
gsk_vulkan_device_get_vk_pipeline_layout (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout)
{
return layout->vk_pipeline_layout;
}
VkCommandPool
gsk_vulkan_device_get_vk_command_pool (GskVulkanDevice *self)
{
return self->vk_command_pool;
}
static VkSampler
gsk_vulkan_device_create_sampler (GskVulkanDevice *self,
VkSamplerYcbcrConversion vk_conversion,
VkFilter vk_filter,
VkSamplerAddressMode vk_address_mode,
VkSamplerMipmapMode vk_mipmap_mode,
float max_lod)
{
VkSampler result;
GSK_VK_CHECK (vkCreateSampler, gsk_vulkan_device_get_vk_device (self),
&(VkSamplerCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = vk_filter,
.minFilter = vk_filter,
.mipmapMode = vk_mipmap_mode,
.addressModeU = vk_address_mode,
.addressModeV = vk_address_mode,
.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 = max_lod,
.pNext = vk_conversion == VK_NULL_HANDLE ? NULL : &(VkSamplerYcbcrConversionInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = vk_conversion
}
},
NULL,
&result);
return result;
}
VkSampler
gsk_vulkan_device_get_vk_sampler (GskVulkanDevice *self,
GskGpuSampler sampler)
{
const struct {
VkFilter filter;
VkSamplerAddressMode address_mode;
VkSamplerMipmapMode mipmap_mode;
float max_lod;
} filter_attrs[GSK_GPU_SAMPLER_N_SAMPLERS] = {
[GSK_GPU_SAMPLER_DEFAULT] = {
.filter = VK_FILTER_LINEAR,
.address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.max_lod = 0.0f,
},
[GSK_GPU_SAMPLER_TRANSPARENT] = {
.filter = VK_FILTER_LINEAR,
.address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.max_lod = 0.0f,
},
[GSK_GPU_SAMPLER_REPEAT] = {
.filter = VK_FILTER_LINEAR,
.address_mode = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.max_lod = 0.0f,
},
[GSK_GPU_SAMPLER_NEAREST] = {
.filter = VK_FILTER_NEAREST,
.address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.max_lod = 0.0f,
},
[GSK_GPU_SAMPLER_MIPMAP_DEFAULT] = {
.filter = VK_FILTER_LINEAR,
.address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.max_lod = VK_LOD_CLAMP_NONE,
},
};
if (self->vk_samplers[sampler] == VK_NULL_HANDLE)
{
self->vk_samplers[sampler] = gsk_vulkan_device_create_sampler (self,
VK_NULL_HANDLE,
filter_attrs[sampler].filter,
filter_attrs[sampler].address_mode,
filter_attrs[sampler].mipmap_mode,
filter_attrs[sampler].max_lod);
}
return self->vk_samplers[sampler];
}
VkSamplerYcbcrConversion
gsk_vulkan_device_get_vk_conversion (GskVulkanDevice *self,
VkFormat vk_format,
VkSampler *out_sampler)
{
ConversionCacheEntry lookup;
ConversionCacheEntry *entry;
GdkDisplay *display;
lookup = (ConversionCacheEntry) {
.vk_format = vk_format,
};
entry = g_hash_table_lookup (self->conversion_cache, &lookup);
if (entry)
{
if (out_sampler)
*out_sampler = entry->vk_sampler;
return entry->vk_conversion;
}
display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self));
entry = g_memdup (&lookup, sizeof (ConversionCacheEntry));
GSK_VK_CHECK (vkCreateSamplerYcbcrConversion, display->vk_device,
&(VkSamplerYcbcrConversionCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.format = vk_format,
.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601,
.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL,
.components = (VkComponentMapping) {
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY
},
.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN,
.yChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN,
.chromaFilter = VK_FILTER_LINEAR,
.forceExplicitReconstruction = VK_FALSE
},
NULL,
&entry->vk_conversion);
entry->vk_sampler = gsk_vulkan_device_create_sampler (self,
entry->vk_conversion,
VK_FILTER_LINEAR,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_MIPMAP_MODE_NEAREST,
0.0f);
g_hash_table_insert (self->conversion_cache, entry, entry);
if (out_sampler)
*out_sampler = entry->vk_sampler;
return entry->vk_conversion;
}
VkRenderPass
gsk_vulkan_device_get_vk_render_pass (GskVulkanDevice *self,
VkFormat format,
VkImageLayout from_layout,
VkImageLayout to_layout)
{
RenderPassCacheKey cache_key;
VkRenderPass render_pass;
GdkDisplay *display;
cache_key = (RenderPassCacheKey) {
.format = format,
.from_layout = from_layout,
.to_layout = to_layout,
};
render_pass = g_hash_table_lookup (self->render_pass_cache, &cache_key);
if (render_pass)
return render_pass;
display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self));
GSK_VK_CHECK (vkCreateRenderPass, display->vk_device,
&(VkRenderPassCreateInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = (VkAttachmentDescription[]) {
{
.format = format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.initialLayout = from_layout,
.finalLayout = to_layout
}
},
.subpassCount = 1,
.pSubpasses = (VkSubpassDescription []) {
{
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.colorAttachmentCount = 1,
.pColorAttachments = (VkAttachmentReference []) {
{
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
}
},
.pResolveAttachments = (VkAttachmentReference []) {
{
.attachment = VK_ATTACHMENT_UNUSED,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
}
},
.pDepthStencilAttachment = NULL,
}
},
.dependencyCount = 0,
},
NULL,
&render_pass);
g_hash_table_insert (self->render_pass_cache, g_memdup (&cache_key, sizeof (RenderPassCacheKey)), render_pass);
return render_pass;
}
typedef struct _GskVulkanShaderSpecialization GskVulkanShaderSpecialization;
struct _GskVulkanShaderSpecialization
{
guint32 clip;
guint32 n_immutable_samplers;
guint32 n_samplers;
guint32 n_buffers;
guint32 variation;
};
static VkPipelineColorBlendAttachmentState blend_attachment_states[3] = {
[GSK_GPU_BLEND_OVER] = {
.blendEnable = VK_TRUE,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorWriteMask = VK_COLOR_COMPONENT_A_BIT
| VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
},
[GSK_GPU_BLEND_ADD] = {
.blendEnable = VK_TRUE,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE,
.alphaBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.colorWriteMask = VK_COLOR_COMPONENT_A_BIT
| VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
},
[GSK_GPU_BLEND_CLEAR] = {
.blendEnable = VK_TRUE,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorWriteMask = VK_COLOR_COMPONENT_A_BIT
| VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
},
};
VkPipeline
gsk_vulkan_device_get_vk_pipeline (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout,
const GskGpuShaderOpClass *op_class,
guint32 variation,
GskGpuShaderClip clip,
GskGpuBlend blend,
VkFormat format,
VkRenderPass render_pass)
{
PipelineCacheKey cache_key;
VkPipeline pipeline;
GdkDisplay *display;
const char *version_string;
char *vertex_shader_name, *fragment_shader_name;
cache_key = (PipelineCacheKey) {
.op_class = op_class,
.variation = variation,
.clip = clip,
.blend = blend,
.format = format,
};
pipeline = g_hash_table_lookup (layout->pipeline_cache, &cache_key);
if (pipeline)
return pipeline;
display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self));
if (gsk_vulkan_device_has_feature (self, GDK_VULKAN_FEATURE_DYNAMIC_INDEXING) &&
gsk_vulkan_device_has_feature (self, GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING))
version_string = ".1.2";
else
version_string = ".1.0";
vertex_shader_name = g_strconcat ("/org/gtk/libgsk/shaders/vulkan/",
op_class->shader_name,
version_string,
".vert.spv",
NULL);
fragment_shader_name = g_strconcat ("/org/gtk/libgsk/shaders/vulkan/",
op_class->shader_name,
version_string,
".frag.spv",
NULL);
GSK_VK_CHECK (vkCreateGraphicsPipelines, display->vk_device,
display->vk_pipeline_cache,
1,
&(VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = (VkPipelineShaderStageCreateInfo[2]) {
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = gdk_display_get_vk_shader_module (display, vertex_shader_name),
.pName = "main",
.pSpecializationInfo = &(VkSpecializationInfo) {
.mapEntryCount = 5,
.pMapEntries = (VkSpecializationMapEntry[5]) {
{
.constantID = 0,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, clip),
.size = sizeof (guint32),
},
{
.constantID = 1,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, n_immutable_samplers),
.size = sizeof (guint32),
},
{
.constantID = 2,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, n_samplers),
.size = sizeof (guint32),
},
{
.constantID = 3,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, n_buffers),
.size = sizeof (guint32),
},
{
.constantID = 4,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, variation),
.size = sizeof (guint32),
},
},
.dataSize = sizeof (GskVulkanShaderSpecialization),
.pData = &(GskVulkanShaderSpecialization) {
.clip = clip,
.n_immutable_samplers = MAX (1, layout->setup.n_immutable_samplers),
.n_samplers = layout->setup.n_samplers - MAX (3 * layout->setup.n_immutable_samplers, 1),
.n_buffers = layout->setup.n_buffers,
.variation = variation,
},
},
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = gdk_display_get_vk_shader_module (display, fragment_shader_name),
.pName = "main",
.pSpecializationInfo = &(VkSpecializationInfo) {
.mapEntryCount = 5,
.pMapEntries = (VkSpecializationMapEntry[5]) {
{
.constantID = 0,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, clip),
.size = sizeof (guint32),
},
{
.constantID = 1,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, n_immutable_samplers),
.size = sizeof (guint32),
},
{
.constantID = 2,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, n_samplers),
.size = sizeof (guint32),
},
{
.constantID = 3,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, n_buffers),
.size = sizeof (guint32),
},
{
.constantID = 4,
.offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, variation),
.size = sizeof (guint32),
},
},
.dataSize = sizeof (GskVulkanShaderSpecialization),
.pData = &(GskVulkanShaderSpecialization) {
.clip = clip,
.n_immutable_samplers = MAX (1, layout->setup.n_immutable_samplers),
.n_samplers = layout->setup.n_samplers - MAX (3 * layout->setup.n_immutable_samplers, 1),
.n_buffers = layout->setup.n_buffers,
.variation = variation,
},
},
},
},
.pVertexInputState = op_class->vertex_input_state,
.pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
},
.pTessellationState = NULL,
.pViewportState = &(VkPipelineViewportStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
},
.pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.lineWidth = 1.0f,
},
.pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = 1,
},
.pDepthStencilState = &(VkPipelineDepthStencilStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
},
.pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blend_attachment_states[blend],
},
.pDynamicState = &(VkPipelineDynamicStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = (VkDynamicState[2]) {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
},
},
.layout = layout->vk_pipeline_layout,
.renderPass = render_pass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1,
},
NULL,
&pipeline);
g_free (fragment_shader_name);
g_free (vertex_shader_name);
g_hash_table_insert (layout->pipeline_cache, g_memdup (&cache_key, sizeof (PipelineCacheKey)), pipeline);
gdk_display_vulkan_pipeline_cache_updated (display);
return pipeline;
}
GskVulkanPipelineLayout *
gsk_vulkan_device_acquire_pipeline_layout (GskVulkanDevice *self,
VkSampler *immutable_samplers,
gsize n_immutable_samplers,
gsize n_samplers,
gsize n_buffers)
{
GskVulkanPipelineLayoutSetup setup;
GskVulkanPipelineLayout *layout;
/* round the number of samplers/buffer up a bit, so we don't (re)create
* excessive amounts of layouts */
n_samplers = MAX (n_samplers, 8);
g_assert (n_samplers <= self->max_samplers);
n_buffers = MAX (n_buffers, 8);
g_assert (n_buffers <= self->max_buffers);
setup.n_samplers = MIN (2 << g_bit_nth_msf (n_samplers - 1, -1), self->max_samplers);
setup.n_buffers = MIN (2 << g_bit_nth_msf (n_buffers - 1, -1), self->max_buffers);
setup.n_immutable_samplers = n_immutable_samplers;
setup.immutable_samplers = immutable_samplers;
layout = g_hash_table_lookup (self->pipeline_layouts, &setup);
if (layout)
{
gsk_vulkan_pipeline_layout_ref (self, layout);
return layout;
}
return gsk_vulkan_pipeline_layout_new (self, &setup);
}
void
gsk_vulkan_device_release_pipeline_layout (GskVulkanDevice *self,
GskVulkanPipelineLayout *layout)
{
if (self->pipeline_layout_cache)
gsk_vulkan_pipeline_layout_unref (self, self->pipeline_layout_cache);
self->pipeline_layout_cache = layout;
}
void
gsk_vulkan_device_get_pipeline_sizes (GskVulkanDevice *self,
GskVulkanPipelineLayout*layout,
gsize *n_immutable_samplers,
gsize *n_samplers,
gsize *n_buffers)
{
*n_immutable_samplers = layout->setup.n_immutable_samplers;
*n_samplers = layout->setup.n_samplers;
*n_buffers = layout->setup.n_buffers;
}
static GskVulkanAllocator *
gsk_vulkan_device_get_allocator (GskVulkanDevice *self,
gsize index,
const VkMemoryType *type)
{
if (self->allocators[index] == NULL)
{
self->allocators[index] = gsk_vulkan_direct_allocator_new (gsk_vulkan_device_get_vk_device (self),
index,
type);
self->allocators[index] = gsk_vulkan_buddy_allocator_new (self->allocators[index],
1024 * 1024);
//allocators[index] = gsk_vulkan_stats_allocator_new (allocators[index]);
}
return self->allocators[index];
}
/* following code found in
* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceMemoryProperties.html */
GskVulkanAllocator *
gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
uint32_t allowed_types,
VkMemoryPropertyFlags required_flags,
VkMemoryPropertyFlags desired_flags)
{
VkPhysicalDeviceMemoryProperties properties;
uint32_t i, found;
vkGetPhysicalDeviceMemoryProperties (gsk_vulkan_device_get_vk_physical_device (self),
&properties);
found = properties.memoryTypeCount;
for (i = 0; i < properties.memoryTypeCount; i++)
{
if (!(allowed_types & (1 << i)))
continue;
if ((properties.memoryTypes[i].propertyFlags & required_flags) != required_flags)
continue;
found = MIN (i, found);
if ((properties.memoryTypes[i].propertyFlags & desired_flags) == desired_flags)
break;
}
g_assert (found < properties.memoryTypeCount);
return gsk_vulkan_allocator_ref (gsk_vulkan_device_get_allocator (self, i, &properties.memoryTypes[i]));
}
GskVulkanAllocator *
gsk_vulkan_device_get_external_allocator (GskVulkanDevice *self)
{
if (self->external_allocator == NULL)
{
self->external_allocator = gsk_vulkan_external_allocator_new (gsk_vulkan_device_get_vk_device (self));
//self->external_allocator = gsk_vulkan_stats_allocator_new (self->external_allocator);
}
return self->external_allocator;
}