quakeforge/libs/video/renderer/vulkan/vulkan_output.c
Bill Currie 31cf3ed248 [vulkan] Register external attachments
This is along the lines of what I originally intended, but now I need
more than just the swapchain (which is now registered by vulkan_output).
2023-07-24 19:11:18 +09:00

382 lines
11 KiB
C

/*
vulkan_main.c
Vulkan output
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2022/11/21
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_STRING_H
# include "string.h"
#endif
#ifdef HAVE_STRINGS_H
# include "strings.h"
#endif
#include "QF/cvar.h"
#include "QF/render.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/Vulkan/qf_draw.h"
#include "QF/Vulkan/qf_matrices.h"
#include "QF/Vulkan/qf_output.h"
#include "QF/Vulkan/qf_vid.h"
#include "QF/Vulkan/capture.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/descriptor.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/dsmanager.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/swapchain.h"
#include "r_local.h"
#include "r_internal.h"
#include "vid_vulkan.h"
#include "vkparse.h"//FIXME
static void
acquire_output (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto device = ctx->device;
auto dfunc = device->funcs;
auto rctx = ctx->render_context;
auto frame = &rctx->frames.a[ctx->curFrame];
auto octx = ctx->output_context;
auto sc = ctx->swapchain;
uint32_t imageIndex = 0;
while (!QFV_AcquireNextImage (sc, frame->imageAvailableSemaphore,
0, &imageIndex)) {
QFV_DeviceWaitIdle (device);
for (uint32_t i = 0; i < sc->imageViews->size; i++) {
dfunc->vkDestroyFramebuffer (device->dev,
octx->framebuffers[i], 0);
}
octx->framebuffers = 0;
Vulkan_CreateSwapchain (ctx);
sc = ctx->swapchain;
QFV_Capture_Renew (ctx);
dfunc->vkDestroySemaphore (device->dev, frame->imageAvailableSemaphore,
0);
frame->imageAvailableSemaphore = QFV_CreateSemaphore (device);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_SEMAPHORE,
frame->imageAvailableSemaphore,
va (ctx->va_ctx, "sc image:%d", ctx->curFrame));
}
//FIXME clean this up
auto step = QFV_GetStep (params[0], ctx->render_context->job);
auto render = step->render;
auto rp = &render->renderpasses[0];
if (!octx->framebuffers) {
uint32_t count = ctx->swapchain->imageViews->size;
octx->framebuffers = malloc (sizeof (VkFramebuffer [count]));
for (uint32_t i = 0; i < count; i++) {
rp->beginInfo.framebuffer = 0;
//FIXME come up with a better mechanism
ctx->swapImageIndex = i;
QFV_CreateFramebuffer (ctx, rp, sc->extent);
octx->framebuffers[i] = rp->beginInfo.framebuffer;
QFV_duSetObjectName (device, VK_OBJECT_TYPE_FRAMEBUFFER,
octx->framebuffers[i],
va (ctx->va_ctx, "sc fb:%d", i));
}
rp->beginInfo.renderArea.extent = sc->extent;
for (uint32_t i = 0; i < rp->subpass_count; i++) {
auto sp = &rp->subpasses[i];
for (uint32_t j = 0; j < sp->pipeline_count; j++) {
auto pl = &sp->pipelines[j];
pl->viewport.width = sc->extent.width;
pl->viewport.height = sc->extent.height;
pl->scissor.extent = sc->extent;
}
}
rctx->size_time = Sys_LongTime ();
}
ctx->swapImageIndex = imageIndex;
rp->beginInfo.framebuffer = octx->framebuffers[imageIndex];
for (uint32_t i = 0; i < rp->subpass_count; i++) {
auto sp = &rp->subpasses[i];
sp->inherit.framebuffer = rp->beginInfo.framebuffer;
}
}
static void
update_input (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto device = ctx->device;
auto dfunc = device->funcs;
auto octx = ctx->output_context;
auto oframe = &octx->frames.a[ctx->curFrame];
auto input = QFV_GetStep (params[0], ctx->render_context->job);
if (oframe->input == input->render->active->output) {
return;
}
oframe->input = input->render->active->output;
VkDescriptorImageInfo imageInfo = {
octx->sampler, oframe->input,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
VkWriteDescriptorSet write[] = {
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0,
oframe->set, 0, 0, 1,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
&imageInfo, 0, 0 }
};
dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0);
}
static void
output_select_pipeline (const exprval_t **params, exprval_t *result,
exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto output = QFV_GetStep (params[0], ctx->render_context->job);
// FIXME the output render pass has only one subpass
auto sp = output->render->active->subpasses;
// FIXME the output render pass pipelines are in the order
// output, waterwarp, fisheye, followed by any additional pipelines
if (scr_fisheye) {
sp->pipelines[0].disabled = true;
sp->pipelines[1].disabled = true;
sp->pipelines[2].disabled = false;
} else if (r_dowarp) {
sp->pipelines[0].disabled = true;
sp->pipelines[1].disabled = false;
sp->pipelines[2].disabled = true;
} else {
sp->pipelines[0].disabled = false;
sp->pipelines[1].disabled = true;
sp->pipelines[2].disabled = true;
}
}
static void
output_select_renderpass (const exprval_t **params, exprval_t *result,
exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto main = QFV_GetStep (params[0], ctx->render_context->job);
// FIXME the main render step has only two renderpasses
auto render = main->render;
if (scr_fisheye) {
render->active = &render->renderpasses[1];
} else {
render->active = &render->renderpasses[0];
}
}
static void
output_draw (qfv_taskctx_t *taskctx,
int num_push_constants, qfv_push_constants_t *push_constants)
{
auto ctx = taskctx->ctx;
auto device = ctx->device;
auto dfunc = device->funcs;
auto octx = ctx->output_context;
auto oframe = &octx->frames.a[ctx->curFrame];
auto layout = taskctx->pipeline->layout;
auto cmd = taskctx->cmd;
VkDescriptorSet set[] = {
Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
oframe->set,
};
dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
layout, 0, 2, set, 0, 0);
if (num_push_constants) {
QFV_PushConstants (device, cmd, layout,
num_push_constants, push_constants);
}
dfunc->vkCmdDraw (cmd, 3, 1, 0, 0);
}
static void
output_draw_flat (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
output_draw (taskctx, 0, 0);
}
static void
output_draw_waterwarp (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
float time = vr_data.realtime;
qfv_push_constants_t push_constants[] = {
{ VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof (float), &time },
};
output_draw (taskctx, 1, push_constants);
}
static void
output_draw_fisheye (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
float width = r_refdef.vrect.width;
float height = r_refdef.vrect.height;
float ffov = scr_ffov * M_PI / 360;
float aspect = height / width;
qfv_push_constants_t push_constants[] = {
{ VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof (float), &ffov },
{ VK_SHADER_STAGE_FRAGMENT_BIT, 4, sizeof (float), &aspect },
};
output_draw (taskctx, 2, push_constants);
}
static exprtype_t *stepref_param[] = {
&cexpr_string,
};
static exprfunc_t acquire_output_func[] = {
{ .func = acquire_output, .num_params = 1, .param_types = stepref_param },
{}
};
static exprfunc_t update_input_func[] = {
{ .func = update_input, .num_params = 1, .param_types = stepref_param },
{}
};
static exprfunc_t output_select_pipeline_func[] = {
{ .func = output_select_pipeline,
.num_params = 1, .param_types = stepref_param },
{}
};
static exprfunc_t output_select_renderpass_func[] = {
{ .func = output_select_renderpass,
.num_params = 1, .param_types = stepref_param },
{}
};
static exprfunc_t output_draw_flat_func[] = {
{ .func = output_draw_flat },
{}
};
static exprfunc_t output_draw_waterwarp_func[] = {
{ .func = output_draw_waterwarp },
{}
};
static exprfunc_t output_draw_fisheye_func[] = {
{ .func = output_draw_fisheye },
{}
};
static exprsym_t output_task_syms[] = {
{ "acquire_output", &cexpr_function, acquire_output_func },
{ "update_input", &cexpr_function, update_input_func },
{ "output_select_pipeline", &cexpr_function, output_select_pipeline_func },
{ "output_select_renderpass", &cexpr_function,
output_select_renderpass_func },
{ "output_draw_flat", &cexpr_function, output_draw_flat_func },
{ "output_draw_waterwarp", &cexpr_function, output_draw_waterwarp_func },
{ "output_draw_fisheye", &cexpr_function, output_draw_fisheye_func },
{}
};
void
Vulkan_Output_Init (vulkan_ctx_t *ctx)
{
outputctx_t *octx = calloc (1, sizeof (outputctx_t));
ctx->output_context = octx;
octx->swapchain_info = (qfv_attachmentinfo_t) {
.name = "$swapchain",
.format = ctx->swapchain->format,
.samples = 1,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
};
QFV_Render_AddTasks (ctx, output_task_syms);
qfv_attachmentinfo_t *attachments[] = {
&octx->swapchain_info,
};
QFV_Render_AddAttachments (ctx, 1, attachments);
}
void
Vulkan_Output_Setup (vulkan_ctx_t *ctx)
{
qfvPushDebug (ctx, "output init");
auto octx = ctx->output_context;
auto rctx = ctx->render_context;
size_t frames = rctx->frames.size;
DARRAY_INIT (&octx->frames, frames);
DARRAY_RESIZE (&octx->frames, frames);
octx->frames.grow = 0;
octx->sampler = QFV_Render_Sampler (ctx, "linear");
auto dsmanager = QFV_Render_DSManager (ctx, "output_set");
for (size_t i = 0; i < frames; i++) {
auto oframe = &octx->frames.a[i];
oframe->input = 0;
oframe->set = QFV_DSManager_AllocSet (dsmanager);
}
qfvPopDebug (ctx);
}
void
Vulkan_Output_Shutdown (vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
outputctx_t *octx = ctx->output_context;
for (uint32_t i = 0; i < ctx->swapchain->imageViews->size; i++) {
dfunc->vkDestroyFramebuffer (device->dev, octx->framebuffers[i], 0);
}
free (octx->framebuffers);
auto step = QFV_FindStep ("output", ctx->render_context->job);
auto render = step->render;
auto rp = &render->renderpasses[0];
rp->beginInfo.framebuffer = 0;
free (octx->frames.a);
free (octx);
}