[vulkan] Create a descriptor set manager

The manager allows recycling of descriptor sets and takes care of
creating pools as needed.
This commit is contained in:
Bill Currie 2023-06-24 14:55:46 +09:00
parent 92368eafb2
commit 8470ae5a28
7 changed files with 274 additions and 69 deletions

View file

@ -0,0 +1,37 @@
#ifndef __QF_Vulkan_dsmanager_h
#define __QF_Vulkan_dsmanager_h
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>
#include "QF/darray.h"
#include "QF/darray.h"
typedef struct qfv_descriptorpoolset_s
DARRAY_TYPE (VkDescriptorPool) qfv_descriptorpoolset_t;
typedef struct qfv_descriptorsetset_s
DARRAY_TYPE (VkDescriptorSet) qfv_descriptorsetset_t;
typedef struct qfv_dsmanager_s {
const char *name;
struct qfv_device_s *device;
VkDescriptorPoolCreateInfo poolCreateInfo;
VkDescriptorPool activePool;
qfv_descriptorpoolset_t freePools;
qfv_descriptorpoolset_t usedPools;
qfv_descriptorsetset_t freeSets;
VkDescriptorSetLayout layout;
} qfv_dsmanager_t;
struct qfv_descriptorsetlayoutinfo_s;
qfv_dsmanager_t *
QFV_DSManager_Create (const struct qfv_descriptorsetlayoutinfo_s *setLayoutInfo,
uint32_t maxSets, struct vulkan_ctx_s *ctx);
void QFV_DSManager_Destroy (qfv_dsmanager_t *setManager);
VkDescriptorSet QFV_DSManager_AllocSet (qfv_dsmanager_t *setManager);
void QFV_DSManager_FreeSet (qfv_dsmanager_t *setManager, VkDescriptorSet set);
#endif//__QF_Vulkan_dsmanager_h

View file

@ -8,7 +8,9 @@
#include "QF/cexpr.h"
#include "QF/simd/types.h"
#ifndef __QFCC__
#include "QF/darray.h"
#include "QF/Vulkan/command.h"
#endif
@ -266,8 +268,8 @@ typedef struct qfv_jobinfo_s {
qfv_imageinfo_t *buffers;
qfv_imageviewinfo_t *bufferviews;
uint32_t num_descriptorsetlayouts;
qfv_descriptorsetlayoutinfo_t *descriptorsetlayouts;
uint32_t num_dslayouts;
qfv_descriptorsetlayoutinfo_t *dslayouts;
} qfv_jobinfo_t;
#ifndef __QFCC__
@ -285,9 +287,8 @@ typedef struct qfv_pipeline_s {
VkViewport viewport;
VkRect2D scissor;
uint32_t num_descriptorsets;
uint32_t first_descriptorset;
VkDescriptorSet *descriptorsets;
uint32_t num_indices;
uint32_t *ds_indices;
uint32_t task_count;
qfv_taskinfo_t *tasks;
@ -367,6 +368,8 @@ typedef struct qfv_job_s {
VkPipelineLayout *layouts;
qfv_step_t *steps;
qfv_cmdbufferset_t commands;
uint32_t num_dsmanagers;
struct qfv_dsmanager_s **dsmanager;
} qfv_job_t;
typedef struct qfv_renderframe_s {
@ -408,6 +411,7 @@ void QFV_CreateFramebuffer (struct vulkan_ctx_s *ctx, qfv_renderpass_t *rp);
qfv_step_t *QFV_GetStep (const exprval_t *param, qfv_job_t *job);
qfv_step_t *QFV_FindStep (const char *step, qfv_job_t *job) __attribute__((pure));
#endif//__QFCC__
#endif//__QF_Vulkan_render_h

View file

@ -228,6 +228,7 @@ libs_video_renderer_librender_vulkan_la_SOURCES = \
libs/video/renderer/vulkan/debug.c \
libs/video/renderer/vulkan/descriptor.c \
libs/video/renderer/vulkan/device.c \
libs/video/renderer/vulkan/dsmanager.c \
libs/video/renderer/vulkan/image.c \
libs/video/renderer/vulkan/instance.c \
libs/video/renderer/vulkan/memory.c \

View file

@ -0,0 +1,166 @@
/*
dsmanager.c
Vulkan descriptor set manager
Copyright (C) 2023 Bill Currie <bill@taniwha.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "QF/hash.h"
#include "QF/va.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/dsmanager.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/resource.h"
#include "vid_vulkan.h"
qfv_dsmanager_t *
QFV_DSManager_Create (const qfv_descriptorsetlayoutinfo_t *setLayoutInfo,
uint32_t maxSets, vulkan_ctx_t *ctx)
{
VkDescriptorPoolCreateFlags poolFlags = setLayoutInfo->flags
& VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT;
size_t size = sizeof (qfv_dsmanager_t);
size += sizeof (VkDescriptorPoolSize[setLayoutInfo->num_bindings]);
qfv_dsmanager_t *setManager = malloc (size);
auto poolSizes = (VkDescriptorPoolSize *) &setManager[1];
*setManager = (qfv_dsmanager_t) {
.name = setLayoutInfo->name,
.device = ctx->device,
.poolCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = poolFlags,
.maxSets = maxSets,
.poolSizeCount = setLayoutInfo->num_bindings,
.pPoolSizes = poolSizes,
},
.freePools = DARRAY_STATIC_INIT (4),
.usedPools = DARRAY_STATIC_INIT (4),
.freeSets = DARRAY_STATIC_INIT (4),
};
for (uint32_t i = 0; i < setLayoutInfo->num_bindings; i++) {
auto binding = setLayoutInfo->bindings[i];
poolSizes[i] = (VkDescriptorPoolSize) {
.type = binding.descriptorType,
.descriptorCount = maxSets * binding.descriptorCount,
};
};
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = setManager->device->funcs;
VkDescriptorSetLayoutCreateInfo cInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.flags = setLayoutInfo->flags,
.bindingCount = setLayoutInfo->num_bindings,
.pBindings = setLayoutInfo->bindings,
};
dfunc->vkCreateDescriptorSetLayout (device->dev, &cInfo, 0,
&setManager->layout);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT,
setManager->layout,
va (ctx->va_ctx, "descriptorSetLayout:%s",
setLayoutInfo->name));
return setManager;
}
void
QFV_DSManager_Destroy (qfv_dsmanager_t *setManager)
{
if (!setManager) {
return;
}
VkDevice dev = setManager->device->dev;
qfv_devfuncs_t *dfunc = setManager->device->funcs;
for (size_t i = 0; i < setManager->freePools.size; i++) {
dfunc->vkDestroyDescriptorPool (dev, setManager->freePools.a[i], 0);
}
for (size_t i = 0; i < setManager->usedPools.size; i++) {
dfunc->vkDestroyDescriptorPool (dev, setManager->usedPools.a[i], 0);
}
if (setManager->activePool) {
dfunc->vkDestroyDescriptorPool (dev, setManager->activePool, 0);
}
DARRAY_CLEAR (&setManager->freePools);
DARRAY_CLEAR (&setManager->usedPools);
DARRAY_CLEAR (&setManager->freeSets);
dfunc->vkDestroyDescriptorSetLayout (dev, setManager->layout, 0);
free (setManager);
}
VkDescriptorSet
QFV_DSManager_AllocSet (qfv_dsmanager_t *setManager)
{
if (setManager->freeSets.size) {
uint32_t ind = --setManager->freeSets.size;
return setManager->freeSets.a[ind];
}
VkDevice dev = setManager->device->dev;
qfv_devfuncs_t *dfunc = setManager->device->funcs;
VkResult res;
retry:
if (setManager->activePool) {
VkDescriptorSet set;
VkDescriptorSetAllocateInfo aInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = setManager->activePool,
.descriptorSetCount = 1,
.pSetLayouts = &setManager->layout,
};
res = dfunc->vkAllocateDescriptorSets (dev, &aInfo, &set);
if (res == VK_SUCCESS) {
return set;
}
if (res != VK_ERROR_OUT_OF_POOL_MEMORY) {
Sys_Error ("failed to allocate descriptor set: %d", res);
}
DARRAY_APPEND (&setManager->usedPools, setManager->activePool);
}
if (setManager->freePools.size) {
uint32_t ind = --setManager->freePools.size;
setManager->activePool = setManager->freePools.a[ind];
goto retry;
}
res = dfunc->vkCreateDescriptorPool (dev, &setManager->poolCreateInfo, 0,
&setManager->activePool);
if (res != VK_SUCCESS) {
Sys_Error ("failed to create descriptor set pool: %d", res);
}
goto retry;
}
void
QFV_DSManager_FreeSet (qfv_dsmanager_t *setManager, VkDescriptorSet set)
{
DARRAY_APPEND (&setManager->freeSets, set);
}

View file

@ -44,6 +44,7 @@
#include "QF/Vulkan/command.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/dsmanager.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/pipeline.h"
#include "QF/Vulkan/render.h"
@ -89,15 +90,6 @@ run_pipeline (qfv_pipeline_t *pipeline, qfv_taskctx_t *taskctx)
taskctx->pipeline = pipeline;
run_tasks (pipeline->task_count, pipeline->tasks, taskctx);
if (pipeline->num_descriptorsets) {
dfunc->vkCmdBindDescriptorSets (cmd, pipeline->bindPoint,
pipeline->layout,
pipeline->first_descriptorset,
pipeline->num_descriptorsets,
pipeline->descriptorsets,
0, 0);
}
}
// https://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/
@ -119,8 +111,6 @@ run_subpass (qfv_subpass_t *sp, qfv_taskctx_t *taskctx)
static void
run_renderpass (qfv_renderpass_t *rp, vulkan_ctx_t *ctx)
{
//printf ("%10.2f run_renderpass: %s\n", Sys_DoubleTime (), rp->label.name);
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
__auto_type rctx = ctx->render_context;
@ -168,8 +158,6 @@ static void
run_compute_pipeline (qfv_pipeline_t *pipeline, VkCommandBuffer cmd,
vulkan_ctx_t *ctx)
{
//printf ("run_compute_pipeline: %s\n", pipeline->label.name);
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
dfunc->vkCmdBindPipeline (cmd, pipeline->bindPoint, pipeline->pipeline);
@ -181,14 +169,6 @@ run_compute_pipeline (qfv_pipeline_t *pipeline, VkCommandBuffer cmd,
};
run_tasks (pipeline->task_count, pipeline->tasks, &taskctx);
if (pipeline->num_descriptorsets) {
dfunc->vkCmdBindDescriptorSets (cmd, pipeline->bindPoint,
pipeline->layout,
pipeline->first_descriptorset,
pipeline->num_descriptorsets,
pipeline->descriptorsets,
0, 0);
}
vec4u_t d = pipeline->dispatch;
if (d[0] && d[1] && d[2]) {
dfunc->vkCmdDispatch (cmd, d[0], d[1], d[2]);
@ -240,7 +220,6 @@ QFV_RunRenderJob (vulkan_ctx_t *ctx)
for (uint32_t i = 0; i < job->num_steps; i++) {
__auto_type step = &job->steps[i];
//printf ("%10.2f run_step: %s\n", Sys_DoubleTime (), step->label.name);
if (step->render) {
run_renderpass (step->render->active, ctx);
}
@ -264,8 +243,6 @@ QFV_RunRenderJob (vulkan_ctx_t *ctx)
job->commands.size, job->commands.a,
1, &frame->renderDoneSemaphore,
};
//printf ("%10.2f submit for frame %d: %zd %p\n", Sys_DoubleTime (),
// ctx->curFrame, job->commands.size, frame->imageAvailableSemaphore);
dfunc->vkResetFences (device->dev, 1, &frame->fence);
dfunc->vkQueueSubmit (queue->queue, 1, &submitInfo, frame->fence);
@ -503,6 +480,9 @@ QFV_Render_Shutdown (vulkan_ctx_t *ctx)
}
}
DARRAY_CLEAR (&job->commands);
for (uint32_t i = 0; i < job->num_dsmanagers; i++) {
QFV_DSManager_Destroy (job->dsmanager[i]);
}
free (rctx->job);
}
@ -519,8 +499,8 @@ QFV_Render_Shutdown (vulkan_ctx_t *ctx)
if (rctx->jobinfo) {
__auto_type jinfo = rctx->jobinfo;
for (uint32_t i = 0; i < jinfo->num_descriptorsetlayouts; i++) {
__auto_type setLayout = jinfo->descriptorsetlayouts[i].setLayout;
for (uint32_t i = 0; i < jinfo->num_dslayouts; i++) {
__auto_type setLayout = jinfo->dslayouts[i].setLayout;
dfunc->vkDestroyDescriptorSetLayout (device->dev, setLayout, 0);
}
delete_memsuper (jinfo->memsuper);

View file

@ -44,6 +44,7 @@
#include "QF/Vulkan/command.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/dsmanager.h"
#include "QF/Vulkan/pipeline.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/resource.h"
@ -107,7 +108,7 @@ typedef struct {
uint32_t num_graph_pipelines;
uint32_t num_comp_pipelines;
uint32_t num_descriptorsets;
uint32_t num_ds_indices;
} objcount_t;
static void
@ -347,36 +348,42 @@ find_subpass (qfv_dependencyinfo_t *d, uint32_t spind,
Sys_Error ("invalid dependency: [%d] %s", spind, d->name);
}
static VkDescriptorSetLayout
find_descriptorSet (const qfv_reference_t *ref, objstate_t *s)
static uint32_t __attribute__((pure))
find_ds_index (const qfv_reference_t *ref, objstate_t *s)
{
for (uint32_t i = 0; i < s->jinfo->num_descriptorsetlayouts; i++) {
__auto_type ds = &s->jinfo->descriptorsetlayouts[i];
for (uint32_t i = 0; i < s->jinfo->num_dslayouts; i++) {
__auto_type ds = &s->jinfo->dslayouts[i];
if (strcmp (ds->name, ref->name) == 0) {
if (!ds->setLayout) {
VkDescriptorSetLayoutCreateInfo cInfo = {
.sType=VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.flags = ds->flags,
.bindingCount = ds->num_bindings,
.pBindings = ds->bindings,
};
qfv_device_t *device = s->ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
dfunc->vkCreateDescriptorSetLayout (device->dev, &cInfo, 0,
&ds->setLayout);
QFV_duSetObjectName (device,
VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT,
ds->setLayout,
va (s->ctx->va_ctx, "descriptorSet:%s",
ds->name));
}
return ds->setLayout;
return i;
}
}
Sys_Error ("%s.%s:%d: invalid descriptor set layout: %s",
s->rpi->name, s->spi->name, ref->line, ref->name);
}
static VkDescriptorSetLayout
find_descriptorSet (const qfv_reference_t *ref, objstate_t *s)
{
auto ds = &s->jinfo->dslayouts[find_ds_index (ref, s)];
if (ds->setLayout) {
return ds->setLayout;
}
VkDescriptorSetLayoutCreateInfo cInfo = {
.sType=VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.flags = ds->flags,
.bindingCount = ds->num_bindings,
.pBindings = ds->bindings,
};
qfv_device_t *device = s->ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
dfunc->vkCreateDescriptorSetLayout (device->dev, &cInfo, 0, &ds->setLayout);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT,
ds->setLayout, va (s->ctx->va_ctx, "descriptorSet:%s",
ds->name));
return ds->setLayout;
}
#define RUP(x,a) (((x) + ((a) - 1)) & ~((a) - 1))
static uint32_t
@ -515,7 +522,7 @@ init_plCreate (VkGraphicsPipelineCreateInfo *plc, const qfv_pipelineinfo_t *pli,
if (pli->layout.name) {
__auto_type li = find_layout (&pli->layout, s);
plc->layout = li->layout;
s->inds.num_descriptorsets += li->num_sets;
s->inds.num_ds_indices += li->num_sets;
}
}
@ -722,7 +729,7 @@ typedef struct {
qfv_subpass_t *subpasses;
qfv_pipeline_t *pipelines;
qfv_taskinfo_t *tasks;
VkDescriptorSet *descriptorsets;
uint32_t *ds_indices;
VkImageView *attachment_views;
} jobptr_t;
@ -743,12 +750,13 @@ init_pipeline (qfv_pipeline_t *pl, qfv_pipelineinfo_t *plinfo,
.layout = li->layout,
.task_count = plinfo->num_tasks,
.tasks = &jp->tasks[s->inds.num_tasks],
.descriptorsets = &jp->descriptorsets[s->inds.num_descriptorsets],
.num_indices = li->num_sets,
.ds_indices = &jp->ds_indices[s->inds.num_ds_indices],
};
s->inds.num_tasks += plinfo->num_tasks;
s->inds.num_descriptorsets += li->num_sets;
for (uint32_t i = 0; i < li->num_sets; i++) {
pl->descriptorsets[i] = 0;
s->inds.num_ds_indices += li->num_sets;
for (uint32_t i = 0; i < pl->num_indices; i++) {
pl->ds_indices[i] = find_ds_index (&li->sets[i], s);
}
for (uint32_t i = 0; i < pl->task_count; i++) {
pl->tasks[i] = plinfo->tasks[i];
@ -894,7 +902,8 @@ init_step (uint32_t ind, jobptr_t *jp, objstate_t *s)
static void
init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
{
__auto_type rctx = ctx->render_context;
auto rctx = ctx->render_context;
auto jobinfo = rctx->jobinfo;
size_t size = sizeof (qfv_job_t);
@ -913,8 +922,9 @@ init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
size += sizeof (VkPipeline [counts->num_graph_pipelines]);
size += sizeof (VkPipeline [counts->num_comp_pipelines]);
size += sizeof (VkPipelineLayout [s.inds.num_layouts]);
size += sizeof (VkDescriptorSet [counts->num_descriptorsets]);
size += sizeof (VkImageView [counts->num_attachments]);
size += sizeof (qfv_dsmanager_t *[jobinfo->num_dslayouts]);
size += sizeof (uint32_t [counts->num_ds_indices]);
rctx->job = malloc (size);
auto job = rctx->job;
@ -925,6 +935,7 @@ init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
.num_layouts = s.inds.num_layouts,
.num_steps = counts->num_steps,
.commands = DARRAY_STATIC_INIT (16),
.num_dsmanagers = jobinfo->num_dslayouts,
};
job->steps = (qfv_step_t *) &job[1];
auto rn = (qfv_render_t *) &job->steps[job->num_steps];
@ -939,8 +950,10 @@ init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
job->renderpasses = (VkRenderPass *) &cv[counts->num_attachments];
job->pipelines = (VkPipeline *) &job->renderpasses[job->num_renderpasses];
job->layouts = (VkPipelineLayout *) &job->pipelines[job->num_pipelines];
auto ds = (VkDescriptorSet *) &job->layouts[job->num_layouts];
auto av = (VkImageView *) &ds[counts->num_descriptorsets];
auto av = (VkImageView *) &job->layouts[s.inds.num_layouts];
job->dsmanager = (qfv_dsmanager_t **) &av[counts->num_attachments];
auto ds = (uint32_t *) &job->dsmanager[jobinfo->num_dslayouts];
jobptr_t jp = {
.steps = job->steps,
.renders = rn,
@ -951,7 +964,7 @@ init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
.subpasses = sp,
.pipelines = pl,
.tasks = ti,
.descriptorsets = ds,
.ds_indices = ds,
.attachment_views = av,
};
@ -973,6 +986,10 @@ init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
for (uint32_t i = 0; i < job->num_steps; i++) {
init_step (i, &jp, &s);
}
for (uint32_t i = 0; i < job->num_dsmanagers; i++) {
auto layoutInfo = &jobinfo->dslayouts[i];
job->dsmanager[i] = QFV_DSManager_Create (layoutInfo, 16, ctx);
}
}
static void
@ -1013,7 +1030,7 @@ create_step_compute_objects (uint32_t index, const qfv_stepinfo_t *step,
};
plc->stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
s->inds.num_comp_pipelines++;
s->inds.num_descriptorsets += li->num_sets;
s->inds.num_ds_indices += li->num_sets;
}
}
@ -1132,7 +1149,7 @@ create_objects (vulkan_ctx_t *ctx, objcount_t *counts)
va (ctx->va_ctx, "pipeline:%s", plName[i]));
}
counts->num_descriptorsets = s.inds.num_descriptorsets;
counts->num_ds_indices = s.inds.num_ds_indices;
init_job (ctx, counts, s);
}

View file

@ -776,8 +776,8 @@ parse = {
};
descriptorSetLayouts = {
type = (labeledarray, qfv_descriptorsetlayoutinfo_t, name);
size = num_descriptorsetlayouts;
values = descriptorsetlayouts;
size = num_dslayouts;
values = dslayouts;
};
};
};