mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 08:00:51 +00:00
d673887bf1
Things are a bit of a mess with interdependence between sub-module initialization and render pass initialization, and window resizing is broken, but the main render pass rendering to an image that is then post-processed (currently just blitted) is working. This will make it possible to implement fisheye and water warp (and other effects, of course).
326 lines
9 KiB
C
326 lines
9 KiB
C
/*
|
|
renderpass.c
|
|
|
|
Vulkan render pass and frame buffer functions
|
|
|
|
Copyright (C) 2020 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 "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);
|
|
}
|