quakeforge/libs/video/renderer/vulkan/render_load.c
Bill Currie 387051fedf [vulkan] Split up render job loading and running
The creation of the render jobs doesn't really belong with the running
of those jobs. This makes the code a little easier to navigate as it was
too easy to lost between load-time and run-time code.
2023-06-15 17:29:41 +09:00

1088 lines
32 KiB
C

/*
render_load.c
Vulkan render manager loading/creation
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
#ifdef HAVE_MATH_H
# include <math.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cmem.h"
#include "QF/hash.h"
#include "QF/va.h"
#include "QF/Vulkan/command.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/resource.h"
#include "QF/Vulkan/pipeline.h"
#include "vid_vulkan.h"
#include "vkparse.h"
static qfv_output_t
get_output (vulkan_ctx_t *ctx, plitem_t *item)
{
qfv_output_t output = {};
Vulkan_ConfigOutput (ctx, &output);
plitem_t *output_def = PL_ObjectForKey (item, "output");
if (output_def) {
// QFV_ParseOutput clears the structure, but extent and frames need to
// be preserved
qfv_output_t o = output;
QFV_ParseOutput (ctx, &o, output_def, item);
output.format = o.format;
output.finalLayout = o.finalLayout;
}
return output;
}
void
QFV_LoadRenderInfo (vulkan_ctx_t *ctx)
{
__auto_type rctx = ctx->render_context;
plitem_t *item = Vulkan_GetConfig (ctx, "main_def");
__auto_type output = get_output (ctx, item);
Vulkan_Script_SetOutput (ctx, &output);
rctx->jobinfo = QFV_ParseJobInfo (ctx, item, rctx);
if (rctx->jobinfo) {
rctx->jobinfo->plitem = item;
}
}
typedef struct {
uint32_t num_images;
uint32_t num_imageviews;
uint32_t num_buffers;
uint32_t num_bufferviews;
uint32_t num_layouts;
uint32_t num_steps;
uint32_t num_render;
uint32_t num_compute;
uint32_t num_process;
uint32_t num_tasks;
uint32_t num_renderpasses;
uint32_t num_attachments;
uint32_t num_subpasses;
uint32_t num_dependencies;
uint32_t num_attachmentrefs;
uint32_t num_colorblend;
uint32_t num_preserve;
uint32_t num_graph_pipelines;
uint32_t num_comp_pipelines;
uint32_t num_descriptorsets;
} objcount_t;
static void
count_as_stuff (qfv_attachmentsetinfo_t *as, objcount_t *counts)
{
counts->num_attachmentrefs += as->num_input;
counts->num_attachmentrefs += as->num_color;
counts->num_colorblend += as->num_color;
if (as->resolve) {
counts->num_attachmentrefs += as->num_color;
}
if (as->depth) {
counts->num_attachmentrefs += 1;
}
counts->num_preserve += as->num_preserve;
}
static void
count_sp_stuff (qfv_subpassinfo_t *spi, objcount_t *counts)
{
counts->num_dependencies += spi->num_dependencies;
if (spi->attachments) {
count_as_stuff (spi->attachments, counts);
}
for (uint32_t i = 0; i < spi->num_pipelines; i++) {
__auto_type pli = &spi->pipelines[i];
if (pli->num_graph_stages && !pli->compute_stage) {
counts->num_graph_pipelines++;
counts->num_tasks += pli->num_tasks;
} else {
Sys_Error ("%s:%s: invalid graphics pipeline",
spi->name, pli->name);
}
}
}
static void
count_rp_stuff (qfv_renderpassinfo_t *rpi, objcount_t *counts)
{
counts->num_attachments += rpi->num_attachments;
counts->num_subpasses += rpi->num_subpasses;
for (uint32_t i = 0; i < rpi->num_subpasses; i++) {
count_sp_stuff (&rpi->subpasses[i], counts);
}
}
static void
count_comp_stuff (qfv_computeinfo_t *ci, objcount_t *counts)
{
for (uint32_t i = 0; i < ci->num_pipelines; i++) {
__auto_type pli = &ci->pipelines[i];
if (!pli->num_graph_stages && pli->compute_stage) {
counts->num_comp_pipelines++;
counts->num_tasks += pli->num_tasks;
} else {
Sys_Error ("%s:%s: invalid compute pipeline",
ci->name, pli->name);
}
}
}
static void
count_step_stuff (qfv_stepinfo_t *step, objcount_t *counts)
{
if ((step->render && step->compute)
|| (step->render && step->process)
|| (step->compute && step->process)) {
Sys_Error ("%s: invalid step: must be one of render/compute/process",
step->name);
}
if (step->render) {
__auto_type rinfo = step->render;
counts->num_renderpasses += rinfo->num_renderpasses;
for (uint32_t i = 0; i < rinfo->num_renderpasses; i++) {
count_rp_stuff (&rinfo->renderpasses[i], counts);
}
counts->num_render++;
}
if (step->compute) {
count_comp_stuff (step->compute, counts);
counts->num_compute++;
}
if (step->process) {
counts->num_process++;
counts->num_tasks += step->process->num_tasks;
}
counts->num_steps++;
}
static void
count_stuff (qfv_jobinfo_t *jobinfo, objcount_t *counts)
{
counts->num_images += jobinfo->num_images;
counts->num_imageviews += jobinfo->num_imageviews;
counts->num_buffers += jobinfo->num_buffers;
counts->num_bufferviews += jobinfo->num_bufferviews;
for (uint32_t i = 0; i < jobinfo->num_steps; i++) {
count_step_stuff (&jobinfo->steps[i], counts);
}
}
static void
create_resources (vulkan_ctx_t *ctx, objcount_t *counts)
{
__auto_type rctx = ctx->render_context;
__auto_type jinfo = rctx->jobinfo;
__auto_type job = rctx->job;
job->resources = malloc (sizeof(qfv_resource_t)
+ counts->num_images * sizeof (qfv_resobj_t)
+ counts->num_imageviews * sizeof (qfv_resobj_t));
job->images = (qfv_resobj_t *) &job->resources[1];
job->image_views = &job->images[counts->num_images];
job->resources[0] = (qfv_resource_t) {
.name = "render",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = counts->num_images + counts->num_imageviews,
.objects = job->images,
};
for (uint32_t i = 0; i < counts->num_images; i++) {
__auto_type img = &jinfo->images[i];
job->images[i] = (qfv_resobj_t) {
.name = img->name,
.type = qfv_res_image,
.image = {
.flags = img->flags,
.type = img->imageType,
.format = img->format,
.extent = img->extent,
.num_mipmaps = img->mipLevels,
.num_layers = img->arrayLayers,
.samples = img->samples,
.tiling = img->tiling,
.usage = img->usage,
.initialLayout = img->initialLayout,
},
};
}
int error = 0;
for (uint32_t i = 0; i < counts->num_imageviews; i++) {
__auto_type view = &jinfo->imageviews[i];
job->image_views[i] = (qfv_resobj_t) {
.name = view->name,
.type = qfv_res_image_view,
.image_view = {
.flags = view->flags,
.type = view->viewType,
.format = view->format,
.components = view->components,
.subresourceRange = view->subresourceRange,
},
};
if (strcmp (view->image.name, "$output.image") == 0) {
//__auto_type image = jinfo->output.image;
//job->image_views[i].image_view.external_image = image;
//job->image_views[i].image_view.image = -1;
} else {
qfv_resobj_t *img = 0;
for (uint32_t j = 0; j < jinfo->num_images; j++) {
if (strcmp (view->image.name, jinfo->images[j].name) == 0) {
img = &job->images[j];
}
}
if (img) {
uint32_t ind = img - job->resources->objects;
job->image_views[i].image_view.image = ind;
} else {
Sys_Printf ("%d: unknown image reference: %s\n",
view->image.line, view->image.name);
error = 1;
}
}
}
if (error) {
free (job->resources);
job->resources = 0;
return;
}
QFV_CreateResource (ctx->device, job->resources);
}
typedef struct {
VkRenderPassCreateInfo *rpCreate;
VkAttachmentDescription *attach;
VkClearValue *clear;
VkSubpassDescription *subpass;
VkSubpassDependency *depend;
VkAttachmentReference *attachref;
VkPipelineColorBlendAttachmentState *cbAttach;
uint32_t *preserve;
const char **rpName;
const char **plName;
VkComputePipelineCreateInfo *cplCreate;
VkGraphicsPipelineCreateInfo *gplCreate;
VkPipelineColorBlendStateCreateInfo *cbState;
qfv_layoutinfo_t *layouts;
uint32_t *pl_counts;
VkPipeline *gpl;
VkPipeline *cpl;
VkRenderPass *rp;
} objptr_t;
typedef struct {
objcount_t inds;
objptr_t ptr;
vulkan_ctx_t *ctx;
qfv_jobinfo_t *jinfo;
exprtab_t *symtab;
qfv_renderpassinfo_t *rpi;
VkRenderPassCreateInfo *rpc;
qfv_subpassinfo_t *spi;
VkSubpassDescription *spc;
qfv_pipelineinfo_t *pli;
VkGraphicsPipelineCreateInfo *plc;
} objstate_t;
static uint32_t __attribute__((pure))
find_subpass (qfv_dependencyinfo_t *d, uint32_t spind,
qfv_subpassinfo_t *subpasses)
{
if (strcmp (d->name, "$external") == 0) {
return VK_SUBPASS_EXTERNAL;
}
for (uint32_t i = 0; i <= spind; i++) {
__auto_type s = &subpasses[i];
if (strcmp (d->name, s->name) == 0) {
return i;
}
}
Sys_Error ("invalid dependency: [%d] %s", spind, d->name);
}
static VkDescriptorSetLayout
find_descriptorSet (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];
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;
}
}
Sys_Error ("%s.%s:%d: invalid descriptor set layout: %s",
s->rpi->name, s->spi->name, ref->line, ref->name);
}
static qfv_layoutinfo_t *
find_layout (const qfv_reference_t *ref, objstate_t *s)
{
for (uint32_t i = 0; i < s->inds.num_layouts; i++) {
if (strcmp (s->ptr.layouts[i].name, ref->name) == 0) {
return &s->ptr.layouts[i];
}
}
if (!QFV_ParseLayoutInfo (s->ctx, s->jinfo->memsuper, s->symtab, ref->name,
&s->ptr.layouts[s->inds.num_layouts])) {
Sys_Error ("%s.%s:%d: invalid layout: %s",
s->rpi->name, s->spi->name, ref->line, ref->name);
}
__auto_type li = &s->ptr.layouts[s->inds.num_layouts++];
li->name = ref->name;
VkDescriptorSetLayout sets[li->num_sets];
for (uint32_t i = 0; i < li->num_sets; i++) {
sets[i] = find_descriptorSet (&li->sets[i], s);
}
VkPipelineLayoutCreateInfo cInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = li->num_sets,
.pSetLayouts = sets,
.pushConstantRangeCount = li->num_ranges,
.pPushConstantRanges = li->ranges,
};
qfv_device_t *device = s->ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
dfunc->vkCreatePipelineLayout (device->dev, &cInfo, 0, &li->layout);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_PIPELINE_LAYOUT, li->layout,
va (s->ctx->va_ctx, "layout:%s", li->name));
return li;
}
static void
init_plCreate (VkGraphicsPipelineCreateInfo *plc, const qfv_pipelineinfo_t *pli,
objstate_t *s)
{
if (pli->num_graph_stages) {
plc->stageCount = pli->num_graph_stages;
}
if (pli->graph_stages) {
plc->pStages = pli->graph_stages;
}
if (pli->vertexInput) {
plc->pVertexInputState = pli->vertexInput;
}
if (pli->inputAssembly) {
plc->pInputAssemblyState = pli->inputAssembly;
}
if (pli->tessellation) {
plc->pTessellationState = pli->tessellation;
}
if (pli->viewport) {
plc->pViewportState = pli->viewport;
}
if (pli->rasterization) {
plc->pRasterizationState = pli->rasterization;
}
if (pli->multisample) {
plc->pMultisampleState = pli->multisample;
}
if (pli->depthStencil) {
plc->pDepthStencilState = pli->depthStencil;
}
if (pli->colorBlend) {
VkPipelineColorBlendStateCreateInfo *cb;
cb = (VkPipelineColorBlendStateCreateInfo *) plc->pColorBlendState;
*cb = *pli->colorBlend;
}
if (pli->dynamic) {
plc->pDynamicState = pli->dynamic;
}
if (pli->layout.name) {
__auto_type li = find_layout (&pli->layout, s);
plc->layout = li->layout;
s->inds.num_descriptorsets += li->num_sets;
}
}
static uint32_t __attribute__((pure))
find_attachment (qfv_reference_t *ref, objstate_t *s)
{
for (uint32_t i = 0; i < s->rpi->num_attachments; i++) {
__auto_type a = &s->rpi->attachments[i];
if (strcmp (ref->name, a->name) == 0) {
return i;
}
}
Sys_Error ("%s.%s:%d: invalid attachment: %s",
s->rpi->name, s->spi->name, ref->line, ref->name);
}
static void
init_arCreate (const qfv_attachmentrefinfo_t *ari, objstate_t *s)
{
__auto_type arc = &s->ptr.attachref[s->inds.num_attachmentrefs];
qfv_reference_t ref = {
.name = ari->name,
.line = ari->line,
};
*arc = (VkAttachmentReference) {
.attachment = find_attachment (&ref, s),
.layout = ari->layout,
};
}
static void
init_cbCreate (const qfv_attachmentrefinfo_t *ari, objstate_t *s)
{
__auto_type cbc = &s->ptr.cbAttach[s->inds.num_colorblend];
*cbc = ari->blend;
}
static void
init_spCreate (uint32_t index, qfv_subpassinfo_t *sub, objstate_t *s)
{
s->spi = &sub[index];
s->plc = &s->ptr.gplCreate[s->inds.num_graph_pipelines];
s->spc = &s->ptr.subpass[s->inds.num_subpasses];
__auto_type pln = &s->ptr.plName[s->inds.num_graph_pipelines];
__auto_type cbs = &s->ptr.cbState[s->inds.num_graph_pipelines];
s->ptr.pl_counts[s->inds.num_renderpasses] += s->spi->num_pipelines;
*s->spc = (VkSubpassDescription) {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
};
for (uint32_t i = 0; i < s->spi->num_dependencies; i++) {
__auto_type d = &s->spi->dependencies[i];
__auto_type dep = &s->ptr.depend[s->inds.num_dependencies++];
*dep = (VkSubpassDependency) {
.srcSubpass = find_subpass (d, index, s->rpi->subpasses),
.dstSubpass = index,
.srcStageMask = d->src.stage,
.dstStageMask = d->dst.stage,
.srcAccessMask = d->src.access,
.dstAccessMask = d->dst.access,
.dependencyFlags = d->flags,
};
}
for (uint32_t i = 0; i < s->spi->num_pipelines; i++) {
s->plc[i] = (VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pColorBlendState = &cbs[i],
.subpass = index,
};
if (s->spi->base_pipeline) {
init_plCreate (&s->plc[i], s->spi->base_pipeline, s);
}
init_plCreate (&s->plc[i], &s->spi->pipelines[i], s);
pln[i] = s->spi->name;
s->inds.num_graph_pipelines++;
}
__auto_type att = s->spi->attachments;
if (!att) {
return;
}
for (uint32_t i = 0; i < s->spi->num_pipelines; i++) {
cbs[i].attachmentCount = att->num_color;
cbs[i].pAttachments = &s->ptr.cbAttach[s->inds.num_colorblend];
}
s->spc->inputAttachmentCount = att->num_input;
s->spc->pInputAttachments = &s->ptr.attachref[s->inds.num_attachmentrefs];
for (uint32_t i = 0; i < att->num_input; i++) {
init_arCreate (&att->input[i], s);
s->inds.num_attachmentrefs++;
}
s->spc->colorAttachmentCount = att->num_color;
s->spc->pColorAttachments = &s->ptr.attachref[s->inds.num_attachmentrefs];
for (uint32_t i = 0; i < att->num_color; i++) {
init_arCreate (&att->color[i], s);
s->inds.num_attachmentrefs++;
init_cbCreate (&att->color[i], s);
s->inds.num_colorblend++;
}
if (att->resolve) {
s->spc->pResolveAttachments
= &s->ptr.attachref[s->inds.num_attachmentrefs];
for (uint32_t i = 0; i < att->num_color; i++) {
init_arCreate (&att->resolve[i], s);
s->inds.num_attachmentrefs++;
}
}
if (att->depth) {
s->spc->pDepthStencilAttachment
= &s->ptr.attachref[s->inds.num_attachmentrefs];
init_arCreate (att->depth, s);
s->inds.num_attachmentrefs++;
}
s->spc->preserveAttachmentCount = att->num_preserve;
s->spc->pPreserveAttachments = &s->ptr.preserve[s->inds.num_preserve];
for (uint32_t i = 0; i < att->num_preserve; i++) {
s->ptr.preserve[s->inds.num_preserve]
= find_attachment (&att->preserve[i], s);
s->inds.num_preserve++;
}
}
static void
init_atCreate (uint32_t index, qfv_attachmentinfo_t *attachments, objstate_t *s)
{
__auto_type ati = &attachments[index];
__auto_type atc = &s->ptr.attach[s->inds.num_attachments];
__auto_type cvc = &s->ptr.clear[s->inds.num_attachments];
*atc = (VkAttachmentDescription) {
.flags = ati->flags,
.format = ati->format,
.samples = ati->samples,
.loadOp = ati->loadOp,
.storeOp = ati->storeOp,
.stencilLoadOp = ati->stencilLoadOp,
.stencilStoreOp = ati->stencilStoreOp,
.initialLayout = ati->initialLayout,
.finalLayout = ati->finalLayout,
};
*cvc = ati->clearValue;
}
static void
init_rpCreate (uint32_t index, const qfv_renderinfo_t *rinfo, objstate_t *s)
{
s->rpi = &rinfo->renderpasses[index];
s->rpc = &s->ptr.rpCreate[s->inds.num_renderpasses];
s->ptr.rpName[s->inds.num_renderpasses] = s->rpi->name;
__auto_type attachments = &s->ptr.attach[s->inds.num_attachments];
__auto_type subpasses = &s->ptr.subpass[s->inds.num_subpasses];
__auto_type dependencies = &s->ptr.depend[s->inds.num_dependencies];
for (uint32_t i = 0; i < s->rpi->num_attachments; i++) {
init_atCreate (i, s->rpi->attachments, s);
s->inds.num_attachments++;
}
uint32_t num_dependencies = s->inds.num_dependencies;
for (uint32_t i = 0; i < s->rpi->num_subpasses; i++) {
init_spCreate (i, s->rpi->subpasses, s);
s->inds.num_subpasses++;
}
num_dependencies = s->inds.num_dependencies - num_dependencies;
*s->rpc = (VkRenderPassCreateInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = s->rpi->num_attachments,
.pAttachments = attachments,
.subpassCount = s->rpi->num_subpasses,
.pSubpasses = subpasses,
.dependencyCount = num_dependencies,
.pDependencies = dependencies,
};
}
typedef struct {
qfv_step_t *steps;
qfv_render_t *renders;
qfv_compute_t *computes;
qfv_process_t *processes;
qfv_renderpass_t *renderpasses;
VkClearValue *clearvalues;
qfv_subpass_t *subpasses;
qfv_pipeline_t *pipelines;
qfv_taskinfo_t *tasks;
VkDescriptorSet *descriptorsets;
} jobptr_t;
static void
init_pipeline (qfv_pipeline_t *pl, qfv_pipelineinfo_t *plinfo,
jobptr_t *jp, objstate_t *s, int is_compute)
{
__auto_type li = find_layout (&plinfo->layout, s);
*pl = (qfv_pipeline_t) {
.label = {
.name = plinfo->name,
.color = plinfo->color,
},
.bindPoint = is_compute ? VK_PIPELINE_BIND_POINT_COMPUTE
: VK_PIPELINE_BIND_POINT_GRAPHICS,
.pipeline = is_compute ? s->ptr.cpl[s->inds.num_comp_pipelines]
: s->ptr.gpl[s->inds.num_graph_pipelines],
.layout = li->layout,
.task_count = plinfo->num_tasks,
.tasks = &jp->tasks[s->inds.num_tasks],
.descriptorsets = &jp->descriptorsets[s->inds.num_descriptorsets],
};
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;
}
for (uint32_t i = 0; i < pl->task_count; i++) {
pl->tasks[i] = plinfo->tasks[i];
}
}
static void
init_subpass (qfv_subpass_t *sp, qfv_subpassinfo_t *isp,
jobptr_t *jp, objstate_t *s)
{
uint32_t np = s->inds.num_graph_pipelines + s->inds.num_comp_pipelines;
*sp = (qfv_subpass_t) {
.label = {
.name = isp->name,
.color = isp->color,
},
.inherit = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
},
.beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
| VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT,
.pInheritanceInfo = &sp->inherit,
},
.pipeline_count = isp->num_pipelines,
.pipelines = &jp->pipelines[np],
};
for (uint32_t i = 0; i < isp->num_pipelines; i++) {
init_pipeline (&sp->pipelines[i], &isp->pipelines[i], jp, s, 0);
s->inds.num_graph_pipelines++;
}
}
static void
init_renderpass (qfv_renderpass_t *rp, qfv_renderpassinfo_t *rpinfo,
jobptr_t *jp, objstate_t *s)
{
*rp = (qfv_renderpass_t) {
.vulkan_ctx = s->ctx,
.label.name = rpinfo->name,
.label.color = rpinfo->color,
.subpass_count = rpinfo->num_subpasses,
.subpasses = &jp->subpasses[s->inds.num_subpasses],
.beginInfo = (VkRenderPassBeginInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = s->ptr.rp[s->inds.num_renderpasses],
.clearValueCount = rpinfo->num_attachments,
.pClearValues = &jp->clearvalues[s->inds.num_attachments],
},
.subpassContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS,
};
s->inds.num_attachments += rpinfo->num_attachments;
for (uint32_t i = 0; i < rpinfo->num_subpasses; i++) {
init_subpass (&rp->subpasses[i], &rpinfo->subpasses[i], jp, s);
rp->subpasses[i].inherit.renderPass = rp->beginInfo.renderPass;
rp->subpasses[i].inherit.subpass = i;
s->inds.num_subpasses++;
}
}
static void
init_render (qfv_render_t *rend, qfv_renderinfo_t *rinfo,
jobptr_t *jp, objstate_t *s)
{
*rend = (qfv_render_t) {
.label.color = rinfo->color,
.label.name = rinfo->name,
.num_renderpasses = rinfo->num_renderpasses,
.renderpasses = &jp->renderpasses[s->inds.num_renderpasses],
};
s->inds.num_renderpasses += rinfo->num_renderpasses;
for (uint32_t i = 0; i < rend->num_renderpasses; i++) {
init_renderpass (&rend->renderpasses[i], &rinfo->renderpasses[i],
jp, s);
}
rend->active = &rend->renderpasses[0];
}
static void
init_compute (qfv_compute_t *comp, qfv_computeinfo_t *cinfo,
jobptr_t *jp, objstate_t *s)
{
uint32_t np = s->inds.num_graph_pipelines + s->inds.num_comp_pipelines;
*comp = (qfv_compute_t) {
.label.color = cinfo->color,
.label.name = cinfo->name,
.pipelines = &jp->pipelines[np],
.pipeline_count = cinfo->num_pipelines,
};
for (uint32_t i = 0; i < cinfo->num_pipelines; i++) {
init_pipeline (&comp->pipelines[i], &cinfo->pipelines[i], jp, s, 1);
s->inds.num_comp_pipelines++;
}
}
static void
init_process (qfv_process_t *proc, qfv_processinfo_t *pinfo,
jobptr_t *jp, objstate_t *s)
{
*proc = (qfv_process_t) {
.label.color = pinfo->color,
.label.name = pinfo->name,
.tasks = &jp->tasks[s->inds.num_tasks],
.task_count = pinfo->num_tasks,
};
s->inds.num_tasks += pinfo->num_tasks;
for (uint32_t i = 0; i < proc->task_count; i++) {
proc->tasks[i] = pinfo->tasks[i];
}
}
static void
init_step (uint32_t ind, jobptr_t *jp, objstate_t *s)
{
__auto_type step = &jp->steps[s->inds.num_steps++];
__auto_type sinfo = &s->jinfo->steps[ind];
*step = (qfv_step_t) {
.label.name = sinfo->name,
.label.color = sinfo->color,
};
if (sinfo->render) {
step->render = &jp->renders[s->inds.num_render++];
init_render (step->render, sinfo->render, jp, s);
}
if (sinfo->compute) {
step->compute = &jp->computes[s->inds.num_compute++];
init_compute (step->compute, sinfo->compute, jp, s);
}
if (sinfo->process) {
step->process = &jp->processes[s->inds.num_process++];
init_process (step->process, sinfo->process, jp, s);
}
}
static void
init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
{
__auto_type rctx = ctx->render_context;
size_t size = sizeof (qfv_job_t);
size += counts->num_renderpasses * sizeof (VkRenderPass);
size += counts->num_graph_pipelines * sizeof (VkPipeline);
size += counts->num_comp_pipelines * sizeof (VkPipeline);
size += s.inds.num_layouts * sizeof (VkPipelineLayout);
size += counts->num_steps * sizeof (qfv_step_t);
size += counts->num_render * sizeof (qfv_render_t);
size += counts->num_compute * sizeof (qfv_compute_t);
size += counts->num_process * sizeof (qfv_process_t);
size += counts->num_renderpasses * sizeof (qfv_renderpass_t);
size += counts->num_attachments * sizeof (VkClearValue);
size += counts->num_subpasses * sizeof (qfv_subpass_t);
size += counts->num_graph_pipelines * sizeof (qfv_pipeline_t);
size += counts->num_comp_pipelines * sizeof (qfv_pipeline_t);
size += counts->num_tasks * sizeof (qfv_taskinfo_t);
size += counts->num_descriptorsets * sizeof (VkDescriptorSet);
rctx->job = malloc (size);
__auto_type job = rctx->job;
*job = (qfv_job_t) {
.num_renderpasses = counts->num_renderpasses,
.num_pipelines = counts->num_graph_pipelines
+ counts->num_comp_pipelines,
.num_layouts = s.inds.num_layouts,
.num_steps = counts->num_steps,
.commands = DARRAY_STATIC_INIT (16),
};
job->renderpasses = (VkRenderPass *) &job[1];
job->pipelines = (VkPipeline *) &job->renderpasses[job->num_renderpasses];
job->layouts = (VkPipelineLayout *) &job->pipelines[job->num_pipelines];
job->steps = (qfv_step_t *) &job->layouts[job->num_layouts];
__auto_type rn = (qfv_render_t *) &job->steps[job->num_steps];
__auto_type cp = (qfv_compute_t *) &rn[counts->num_render];
__auto_type pr = (qfv_process_t *) &cp[counts->num_compute];
__auto_type rp = (qfv_renderpass_t *) &pr[counts->num_process];
__auto_type cv = (VkClearValue *) &rp[counts->num_renderpasses];
__auto_type sp = (qfv_subpass_t *) &cv[counts->num_attachments];
__auto_type pl = (qfv_pipeline_t *) &sp[counts->num_subpasses];
__auto_type ti = (qfv_taskinfo_t *) &pl[job->num_pipelines];
__auto_type ds = (VkDescriptorSet *) &ti[counts->num_tasks];
jobptr_t jp = {
.steps = job->steps,
.renders = rn,
.computes = cp,
.processes = pr,
.renderpasses = rp,
.clearvalues = cv,
.subpasses = sp,
.pipelines = pl,
.tasks = ti,
.descriptorsets = ds,
};
for (uint32_t i = 0; i < job->num_renderpasses; i++) {
job->renderpasses[i] = s.ptr.rp[i];
}
for (uint32_t i = 0; i < job->num_pipelines; i++) {
// compute pipelines come immediately after the graphics pipelines
job->pipelines[i] = s.ptr.gpl[i];
}
for (uint32_t i = 0; i < s.inds.num_layouts; i++) {
job->layouts[i] = s.ptr.layouts[i].layout;
}
memcpy (cv, s.ptr.clear, counts->num_attachments * sizeof (VkClearValue));
uint32_t num_layouts = s.inds.num_layouts;
s.inds = (objcount_t) {};
s.inds.num_layouts = num_layouts;
for (uint32_t i = 0; i < job->num_steps; i++) {
init_step (i, &jp, &s);
}
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
VkCommandPoolCreateInfo poolCInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.queueFamilyIndex = device->queue.queueFamily,
};
dfunc->vkCreateCommandPool (device->dev, &poolCInfo, 0, &job->command_pool);
}
static void
create_step_render_objects (uint32_t index, const qfv_stepinfo_t *step,
objstate_t *s)
{
__auto_type rinfo = step->render;
if (!rinfo) {
return;
}
for (uint32_t i = 0; i < rinfo->num_renderpasses; i++) {
s->ptr.pl_counts[s->inds.num_renderpasses] = 0;
init_rpCreate (i, rinfo, s);
s->inds.num_renderpasses++;
}
}
static void
create_step_compute_objects (uint32_t index, const qfv_stepinfo_t *step,
objstate_t *s)
{
__auto_type cinfo = step->compute;
if (!cinfo) {
return;
}
uint32_t base = s->inds.num_graph_pipelines;
for (uint32_t i = 0; i < cinfo->num_pipelines; i++) {
__auto_type pli = &cinfo->pipelines[i];
__auto_type plc = &s->ptr.cplCreate[s->inds.num_comp_pipelines];
__auto_type li = find_layout (&pli->layout, s);
s->ptr.plName[base + s->inds.num_comp_pipelines] = pli->name;
*plc = (VkComputePipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.flags = pli->flags,
.stage = *pli->compute_stage,
.layout = li->layout,
};
plc->stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
s->inds.num_comp_pipelines++;
s->inds.num_descriptorsets += li->num_sets;
}
}
static void
create_step_process_objects (uint32_t index, const qfv_stepinfo_t *step,
objstate_t *s)
{
__auto_type pinfo = step->process;
if (!pinfo) {
return;
}
// nothing to create at this stage
}
static void
create_objects (vulkan_ctx_t *ctx, objcount_t *counts)
{
__auto_type rctx = ctx->render_context;
__auto_type jinfo = rctx->jobinfo;
VkRenderPass renderpasses[counts->num_renderpasses];
VkPipeline pipelines[counts->num_graph_pipelines
+ counts->num_comp_pipelines];
VkRenderPassCreateInfo rpCreate[counts->num_renderpasses];
VkAttachmentDescription attach[counts->num_attachments];
VkClearValue clear[counts->num_attachments];
VkSubpassDescription subpass[counts->num_subpasses];
VkSubpassDependency depend[counts->num_dependencies];
VkAttachmentReference attachref[counts->num_attachmentrefs];
VkPipelineColorBlendAttachmentState cbAttach[counts->num_colorblend];
uint32_t preserve[counts->num_preserve];
const char *rpName[counts->num_renderpasses];
const char *plName[counts->num_graph_pipelines
+ counts->num_comp_pipelines];
VkComputePipelineCreateInfo cplCreate[counts->num_comp_pipelines];
VkGraphicsPipelineCreateInfo gplCreate[counts->num_graph_pipelines];
VkPipelineColorBlendStateCreateInfo cbState[counts->num_graph_pipelines];
qfv_layoutinfo_t layouts[counts->num_graph_pipelines
+ counts->num_comp_pipelines];
uint32_t pl_counts[counts->num_renderpasses];
exprctx_t ectx = { .hashctx = &ctx->script_context->hashctx };
objstate_t s = {
.ptr = {
.rpCreate = rpCreate,
.attach = attach,
.clear = clear,
.subpass = subpass,
.depend = depend,
.attachref = attachref,
.cbAttach = cbAttach,
.preserve = preserve,
.rpName = rpName,
.plName = plName,
.cplCreate = cplCreate,
.gplCreate = gplCreate,
.cbState = cbState,
.layouts = layouts,
.pl_counts = pl_counts,
.rp = renderpasses,
.gpl = pipelines,
.cpl = pipelines + counts->num_graph_pipelines,
},
.ctx = ctx,
.jinfo = jinfo,
.symtab = QFV_CreateSymtab (jinfo->plitem, "properties", 0, 0, &ectx),
};
for (uint32_t i = 0; i < jinfo->num_steps; i++) {
create_step_render_objects (i, &jinfo->steps[i], &s);
}
for (uint32_t i = 0; i < jinfo->num_steps; i++) {
create_step_compute_objects (i, &jinfo->steps[i], &s);
}
for (uint32_t i = 0; i < jinfo->num_steps; i++) {
create_step_process_objects (i, &jinfo->steps[i], &s);
}
if (s.inds.num_renderpasses != counts->num_renderpasses
|| s.inds.num_attachments != counts->num_attachments
|| s.inds.num_subpasses != counts->num_subpasses
|| s.inds.num_dependencies != counts->num_dependencies
|| s.inds.num_attachmentrefs != counts->num_attachmentrefs
|| s.inds.num_colorblend != counts->num_colorblend
|| s.inds.num_preserve != counts->num_preserve
|| s.inds.num_graph_pipelines != counts->num_graph_pipelines
|| s.inds.num_comp_pipelines != counts->num_comp_pipelines) {
Sys_Error ("create_objects: something was missed");
}
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
uint32_t plInd = 0;
for (uint32_t i = 0; i < counts->num_renderpasses; i++) {
dfunc->vkCreateRenderPass (device->dev, &s.ptr.rpCreate[i], 0,
&renderpasses[i]);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_RENDER_PASS,
renderpasses[i],
va (ctx->va_ctx, "renderpass:%s", rpName[i]));
for (uint32_t j = 0; j < pl_counts[i]; j++) {
s.ptr.gplCreate[plInd++].renderPass = renderpasses[i];
}
}
if (s.inds.num_graph_pipelines) {
dfunc->vkCreateGraphicsPipelines (device->dev, 0,
s.inds.num_graph_pipelines,
s.ptr.gplCreate, 0, pipelines);
}
if (s.inds.num_comp_pipelines) {
__auto_type p = &pipelines[s.inds.num_graph_pipelines];
dfunc->vkCreateComputePipelines (device->dev, 0,
s.inds.num_comp_pipelines,
s.ptr.cplCreate, 0, p);
}
for (uint32_t i = 0;
i < s.inds.num_graph_pipelines + s.inds.num_comp_pipelines; i++) {
QFV_duSetObjectName (device, VK_OBJECT_TYPE_PIPELINE, pipelines[i],
va (ctx->va_ctx, "pipeline:%s", plName[i]));
}
counts->num_descriptorsets = s.inds.num_descriptorsets;
init_job (ctx, counts, s);
}
void
QFV_BuildRender (vulkan_ctx_t *ctx)
{
__auto_type rctx = ctx->render_context;
objcount_t counts = {};
count_stuff (rctx->jobinfo, &counts);
create_objects (ctx, &counts);
create_resources (ctx, &counts);
}
static VkImageView __attribute__((pure, used))
find_imageview (qfv_reference_t *ref, qfv_renderctx_t *rctx)
{
__auto_type jinfo = rctx->jobinfo;
__auto_type job = rctx->job;
const char *name = ref->name;
if (strncmp (name, "$imageviews.", 7) == 0) {
name += 7;
}
for (uint32_t i = 0; i < jinfo->num_imageviews; i++) {
__auto_type vi = &jinfo->imageviews[i];
__auto_type vo = &job->image_views[i];
if (strcmp (name, vi->name) == 0) {
return vo->image_view.view;
}
}
Sys_Error ("%d:invalid imageview: %s", ref->line, ref->name);
}