/* renderpass.c Vulkan render pass and frame buffer functions Copyright (C) 2020 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 #include "QF/cvar.h" #include "QF/hash.h" #include "QF/plist.h" #include "QF/va.h" #include "QF/Vulkan/command.h" #include "QF/Vulkan/debug.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/image.h" #include "QF/Vulkan/qf_renderpass.h" #include "vid_vulkan.h" #include "vkparse.h" static plitem_t * get_rp_item (vulkan_ctx_t *ctx, qfv_renderpass_t *rp, const char *name) { if (!rp->renderpassDef) { rp->renderpassDef = Vulkan_GetConfig (ctx, rp->name); } plitem_t *item = rp->renderpassDef; if (!item) { Sys_Printf ("error loading %s\n", rp->name); } else if ((item = PL_ObjectForKey (item, name))) { Sys_MaskPrintf (SYS_vulkan_parse, "Found %s def\n", name); } return item; } static size_t get_image_size (VkImage image, qfv_device_t *device) { qfv_devfuncs_t *dfunc = device->funcs; size_t size; size_t align; VkMemoryRequirements requirements; dfunc->vkGetImageMemoryRequirements (device->dev, image, &requirements); size = requirements.size; align = requirements.alignment - 1; size = (size + align) & ~(align); return size; } static void destroy_framebuffers (vulkan_ctx_t *ctx, qfv_renderpass_t *rp) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; for (size_t i = 0; i < rp->framebuffers->size; i++) { dfunc->vkDestroyFramebuffer (device->dev, rp->framebuffers->a[i], 0); } free (rp->framebuffers); rp->framebuffers = 0; } void Vulkan_CreateAttachments (vulkan_ctx_t *ctx, qfv_renderpass_t *renderpass) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; __auto_type rp = renderpass; scriptctx_t *sctx = ctx->script_context; plitem_t *item = get_rp_item (ctx, rp, "images"); if (!item) { return; } if (renderpass->framebuffers) { destroy_framebuffers (ctx, renderpass); } if (rp->attachment_views) { for (size_t i = 0; i < rp->attachment_views->size; i++) { dfunc->vkDestroyImageView (device->dev, rp->attachment_views->a[i], 0); } free (rp->attachment_views); rp->attachment_views = 0; } if (rp->attachment_images) { for (size_t i = 0; i < rp->attachment_images->size; i++) { dfunc->vkDestroyImage (device->dev, rp->attachment_images->a[i], 0); } free (rp->attachment_images); rp->attachment_images = 0; } __auto_type images = QFV_ParseImageSet (ctx, item, rp->renderpassDef); rp->attachment_images = images; size_t memSize = 0; for (size_t i = 0; i < images->size; i++) { memSize += get_image_size (images->a[i], device); } VkDeviceMemory mem = rp->attachmentMemory; if (memSize > rp->attachmentMemory_size) { if (rp->attachmentMemory) { dfunc->vkFreeMemory (device->dev, rp->attachmentMemory, 0); } rp->attachmentMemory_size = memSize; mem = QFV_AllocImageMemory (device, images->a[0], VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memSize, 0); rp->attachmentMemory = mem; QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, mem, "memory:framebuffers"); } size_t offset = 0; for (size_t i = 0; i < images->size; i++) { QFV_BindImageMemory (device, images->a[i], mem, offset); offset += get_image_size (images->a[i], device); } item = get_rp_item (ctx, rp, "imageViews"); if (!item) { return; } __auto_type views = QFV_ParseImageViewSet (ctx, item, rp->renderpassDef); rp->attachment_views = views; item = get_rp_item (ctx, rp, "framebuffer"); if (!item) { return; } rp->framebuffers = QFV_AllocFrameBuffers (sctx->output.frames, malloc); for (size_t i = 0; i < rp->framebuffers->size; i++) { sctx->output.view = sctx->output.view_list[i]; rp->framebuffers->a[i] = QFV_ParseFramebuffer (ctx, item, rp->renderpassDef); } } static void init_renderframe (vulkan_ctx_t *ctx, qfv_renderpass_t *rp, qfv_renderframe_t *rFrame) { rFrame->subpassContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS; rFrame->vulkan_ctx = ctx; rFrame->renderpass = rp; rFrame->subpassCount = rp->subpassCount; if (rp->subpass_info) { rFrame->subpassInfo = rp->subpass_info->a; } rFrame->subpassCmdSets = malloc (rp->subpassCount * sizeof (qfv_cmdbufferset_t)); for (size_t j = 0; j < rp->subpassCount; j++) { DARRAY_INIT (&rFrame->subpassCmdSets[j], 4); } } static void destroy_attachments (vulkan_ctx_t *ctx, qfv_renderpass_t *rp) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; if (rp->attachment_views) { for (size_t i = 0; i < rp->attachment_views->size; i++) { dfunc->vkDestroyImageView (device->dev, rp->attachment_views->a[i], 0); } } if (rp->attachment_images) { for (size_t i = 0; i < rp->attachment_images->size; i++) { dfunc->vkDestroyImage (device->dev, rp->attachment_images->a[i], 0); } } dfunc->vkFreeMemory (device->dev, rp->attachmentMemory, 0); free (rp->attachment_images); free (rp->attachment_views); } static void destroy_renderframes (vulkan_ctx_t *ctx, qfv_renderpass_t *rp) { for (size_t i = 0; i < rp->frames.size; i++) { __auto_type rFrame = &rp->frames.a[i]; for (int j = 0; j < rFrame->subpassCount; j++) { DARRAY_CLEAR (&rFrame->subpassCmdSets[j]); } free (rFrame->subpassCmdSets); } } qfv_renderpass_t * Vulkan_CreateRenderPass (vulkan_ctx_t *ctx, const char *name, qfv_output_t *output, qfv_draw_t draw) { plitem_t *item; qfv_renderpass_t *rp = calloc (1, sizeof (qfv_renderpass_t)); rp->name = name; plitem_t *rp_cfg = get_rp_item (ctx, rp, "renderpass"); if (rp_cfg) { hashtab_t *tab = ctx->script_context->renderpasses; const char *path; path = va (ctx->va_ctx, "$"QFV_PROPERTIES".%s", name); __auto_type renderpass = (VkRenderPass) QFV_GetHandle (tab, path); if (renderpass) { rp->renderpass = renderpass; } else { Vulkan_Script_SetOutput (ctx, output); rp->renderpass = QFV_ParseRenderPass (ctx, rp_cfg, rp->renderpassDef); QFV_AddHandle (tab, path, (uint64_t) rp->renderpass); QFV_duSetObjectName (ctx->device, VK_OBJECT_TYPE_RENDER_PASS, rp->renderpass, va (ctx->va_ctx, "renderpass:%s", name)); } rp->subpassCount = PL_A_NumObjects (PL_ObjectForKey (rp_cfg, "subpasses")); } plitem_t *rp_info = get_rp_item (ctx, rp, "info"); if (rp_info) { plitem_t *subpass_info = PL_ObjectForKey (rp_info, "subpass_info"); if (subpass_info) { rp->subpass_info = QFV_ParseSubpasses (ctx, subpass_info, rp->renderpassDef); if (rp->subpass_info->size < rp->subpassCount) { Sys_Printf ("warning:%s:%d: insufficient entries in " "subpass_info\n", name, PL_Line (subpass_info)); } if (!rp->subpassCount) { rp->subpassCount = rp->subpass_info->size; } } plitem_t *color = PL_ObjectForKey (rp_info, "color"); if (color) { QFV_ParseRGBA (ctx, (float *)&rp->color, color, rp->renderpassDef); } } int width = output->extent.width; int height = output->extent.height; rp->viewport = (VkViewport) { 0, 0, width, height, 0, 1 }; rp->scissor = (VkRect2D) { {0, 0}, {width, height} }; DARRAY_INIT (&rp->frames, 4); DARRAY_RESIZE (&rp->frames, ctx->frames.size); for (size_t i = 0; i < rp->frames.size; i++) { init_renderframe (ctx, rp, &rp->frames.a[i]); } item = get_rp_item (ctx, rp, "clearValues"); rp->clearValues = QFV_ParseClearValues (ctx, item, rp->renderpassDef); rp->draw = draw; Vulkan_CreateAttachments (ctx, rp); return rp; } qfv_renderpass_t * Vulkan_CreateFunctionPass (vulkan_ctx_t *ctx, const char *name, qfv_draw_t function) { qfv_renderpass_t *rp = calloc (1, sizeof (qfv_renderpass_t)); rp->name = name; rp->draw = function; DARRAY_INIT (&rp->frames, 4); DARRAY_RESIZE (&rp->frames, ctx->frames.size); for (size_t i = 0; i < rp->frames.size; i++) { init_renderframe (ctx, rp, &rp->frames.a[i]); } return rp; } void Vulkan_DestroyRenderPass (vulkan_ctx_t *ctx, qfv_renderpass_t *renderpass) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; PL_Free (renderpass->renderpassDef); destroy_attachments (ctx, renderpass); dfunc->vkDestroyRenderPass (device->dev, renderpass->renderpass, 0); destroy_renderframes (ctx, renderpass); if (renderpass->framebuffers) { destroy_framebuffers (ctx, renderpass); } DARRAY_CLEAR (&renderpass->frames); free (renderpass->clearValues); free (renderpass); }