vulkan: Cache VkRenderPasses in render object

Instead of recreating the same renderpass object in every frame and for
every offscreen, just reuse it.

Technically, we can save this per-renderer or even per-display (it
should really be cached by VkDevice), but we have no infrastructure for
that.
This commit is contained in:
Benjamin Otte
2023-07-08 20:40:48 +02:00
parent 05c9f3442c
commit 7fa159e94a
3 changed files with 122 additions and 45 deletions

View File

@@ -58,6 +58,7 @@ struct _GskVulkanRender
VkDescriptorPool descriptor_pool;
VkDescriptorSet descriptor_sets[N_DESCRIPTOR_SETS];
GHashTable *pipeline_cache;
GHashTable *render_pass_cache;
GskVulkanImage *target;
@@ -72,6 +73,7 @@ struct _GskVulkanRender
};
typedef struct _PipelineCacheKey PipelineCacheKey;
typedef struct _RenderPassCacheKey RenderPassCacheKey;
struct _PipelineCacheKey
{
@@ -80,6 +82,13 @@ struct _PipelineCacheKey
VkFormat format;
};
struct _RenderPassCacheKey
{
VkFormat format;
VkImageLayout from_layout;
VkImageLayout to_layout;
};
static guint
pipeline_cache_key_hash (gconstpointer data)
{
@@ -102,6 +111,28 @@ pipeline_cache_key_equal (gconstpointer a,
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 void
gsk_vulkan_render_verbose_print (GskVulkanRender *self,
const char *heading)
@@ -310,6 +341,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
self->uploader = gsk_vulkan_uploader_new (self->vulkan, self->command_pool);
self->pipeline_cache = g_hash_table_new (pipeline_cache_key_hash, pipeline_cache_key_equal);
self->render_pass_cache = g_hash_table_new (render_pass_cache_key_hash, render_pass_cache_key_equal);
#ifdef G_ENABLE_DEBUG
self->render_pass_counter = g_quark_from_static_string ("render-passes");
@@ -474,6 +506,69 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
return pipeline;
}
VkRenderPass
gsk_vulkan_render_get_render_pass (GskVulkanRender *self,
VkFormat format,
VkImageLayout from_layout,
VkImageLayout to_layout)
{
RenderPassCacheKey cache_key;
VkRenderPass render_pass;
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;
GSK_VK_CHECK (vkCreateRenderPass, gdk_vulkan_context_get_device (self->vulkan),
&(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;
}
void
gsk_vulkan_render_bind_descriptor_sets (GskVulkanRender *self,
VkCommandBuffer command_buffer)
@@ -793,6 +888,14 @@ gsk_vulkan_render_free (GskVulkanRender *self)
}
g_hash_table_unref (self->pipeline_cache);
g_hash_table_iter_init (&iter, self->render_pass_cache);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_free (key);
vkDestroyRenderPass (device, value, NULL);
}
g_hash_table_unref (self->render_pass_cache);
g_clear_pointer (&self->uploader, gsk_vulkan_uploader_free);

View File

@@ -109,7 +109,6 @@ gsk_vulkan_render_pass_new (GdkVulkanContext *context,
gboolean is_root)
{
GskVulkanRenderPass *self;
VkImageLayout final_layout;
self = g_new0 (GskVulkanRenderPass, 1);
self->vulkan = g_object_ref (context);
@@ -120,49 +119,22 @@ gsk_vulkan_render_pass_new (GdkVulkanContext *context,
self->viewport = *viewport;
graphene_vec2_init_from_vec2 (&self->scale, scale);
if (!is_root) // this is a dependent pass
final_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if (is_root)
{
/* this is a swapchain target */
self->render_pass = gsk_vulkan_render_get_render_pass (render,
gsk_vulkan_image_get_vk_format (target),
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
else
final_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
GSK_VK_CHECK (vkCreateRenderPass, gdk_vulkan_context_get_device (self->vulkan),
&(VkRenderPassCreateInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = (VkAttachmentDescription[]) {
{
.format = gsk_vulkan_image_get_vk_format (target),
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = final_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,
&self->render_pass);
{
/* this is an offscreen */
self->render_pass = gsk_vulkan_render_get_render_pass (render,
gsk_vulkan_image_get_vk_format (target),
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
#ifdef G_ENABLE_DEBUG
if (fallback_pixels_quark == 0)
@@ -182,7 +154,6 @@ gsk_vulkan_render_pass_new (GdkVulkanContext *context,
void
gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
{
VkDevice device = gdk_vulkan_context_get_device (self->vulkan);
GskVulkanOp *op;
gsize i;
@@ -197,7 +168,6 @@ gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
g_object_unref (self->vulkan);
g_object_unref (self->target);
cairo_region_destroy (self->clip);
vkDestroyRenderPass (device, self->render_pass, NULL);
g_free (self);
}

View File

@@ -35,6 +35,10 @@ VkPipeline gsk_vulkan_render_get_pipeline (GskVulk
const char *clip_type,
VkFormat format,
VkRenderPass render_pass);
VkRenderPass gsk_vulkan_render_get_render_pass (GskVulkanRender *self,
VkFormat format,
VkImageLayout from_layout,
VkImageLayout to_layout);
gsize gsk_vulkan_render_get_image_descriptor (GskVulkanRender *self,
GskVulkanImage *source,
GskVulkanRenderSampler render_sampler);