/* render.c Vulkan render manager Copyright (C) 2023 Bill Currie 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 #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include "QF/hash.h" #include "QF/Vulkan/command.h" #include "QF/Vulkan/debug.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/render.h" #include "QF/Vulkan/pipeline.h" #include "vid_vulkan.h" #include "vkparse.h" static void run_pipeline (qfv_pipeline_t *pipeline, VkCommandBuffer cmd, vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; dfunc->vkCmdBindPipeline (cmd, pipeline->bindPoint, pipeline->pipeline); dfunc->vkCmdSetViewport (cmd, 0, 1, &pipeline->viewport); dfunc->vkCmdSetScissor (cmd, 0, 1, &pipeline->scissor); if (pipeline->num_descriptor_sets) { dfunc->vkCmdBindDescriptorSets (cmd, pipeline->bindPoint, pipeline->layout, pipeline->first_descriptor_set, pipeline->num_descriptor_sets, pipeline->descriptor_sets, 0, 0); } if (pipeline->num_push_constants) { QFV_PushConstants (device, cmd, pipeline->layout, pipeline->num_push_constants, pipeline->push_constants); } } // https://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/ static void run_subpass (qfv_subpass_t_ *sp, vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; __auto_type cmd = sp->cmd; dfunc->vkResetCommandBuffer (cmd, 0); dfunc->vkBeginCommandBuffer (cmd, &sp->beginInfo); QFV_duCmdBeginLabel (device, cmd, sp->label.name, {VEC4_EXP (sp->label.color)}); for (uint32_t i = 0; i < sp->pipline_count; i++) { __auto_type pipeline = &sp->pipelines[i]; run_pipeline (pipeline, cmd, ctx); } QFV_duCmdEndLabel (device, cmd); dfunc->vkEndCommandBuffer (cmd); } void QFV_RunRenderPass (qfv_renderpass_t_ *rp, vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; __auto_type cmd = rp->cmd; VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; dfunc->vkResetCommandBuffer (cmd, 0); dfunc->vkBeginCommandBuffer (cmd, &beginInfo); QFV_duCmdBeginLabel (device, cmd, rp->label.name, {VEC4_EXP (rp->label.color)}); dfunc->vkCmdBeginRenderPass (cmd, &rp->beginInfo, rp->subpassContents); for (uint32_t i = 0; i < rp->subpass_count; i++) { __auto_type sp = &rp->subpasses[i]; run_subpass (sp, ctx); dfunc->vkCmdExecuteCommands (cmd, 1, &sp->cmd); //FIXME comment is a bit off as exactly one buffer is always submitted // //Regardless of whether any commands were submitted for this //subpass, must step through each and every subpass, otherwise //the attachments won't be transitioned correctly. //However, only if not the last (or only) subpass. if (i < rp->subpass_count - 1) { dfunc->vkCmdNextSubpass (cmd, rp->subpassContents); } } QFV_CmdEndLabel (device, cmd); } void QFV_LoadRenderPass (vulkan_ctx_t *ctx) { __auto_type rctx = ctx->render_context; plitem_t *item = Vulkan_GetConfig (ctx, "main_def"); rctx->renderinfo = QFV_ParseRenderInfo (ctx, item, rctx); } void QFV_Render_Init (vulkan_ctx_t *ctx) { qfv_renderctx_t *rctx = calloc (1, sizeof (*rctx)); ctx->render_context = rctx; exprctx_t ectx = { .hashctx = &rctx->hashctx }; exprsym_t syms[] = { {} }; rctx->task_functions.symbols = syms; cexpr_init_symtab (&rctx->task_functions, &ectx); rctx->task_functions.symbols = 0; } void QFV_Render_AddTasks (vulkan_ctx_t *ctx, exprsym_t *task_syms) { __auto_type rctx = ctx->render_context; exprctx_t ectx = { .hashctx = &rctx->hashctx }; for (exprsym_t *sym = task_syms; sym->name; sym++) { Hash_Add (rctx->task_functions.tab, sym); for (exprfunc_t *f = sym->value; f->func; f++) { for (int i = 0; i < f->num_params; i++) { exprenum_t *e = f->param_types[i]->data; if (e && !e->symtab->tab) { cexpr_init_symtab (e->symtab, &ectx); } } } } }