[vulkan] Pass validation for the first frame

The new render system now passes validation for the first frame (but
no drawing is done by the various subsystems yet). Something is wrong
with how swap chain semaphores are handled thus the second frame fails.

Frame buffer attachments can now be defined externally, with
"$swapchain" supported for now (in which case, the swap chain defines
the size of the frame buffer).

Also, render pass render areas and pipeline viewport and scissor rects
are updated when necessary.
This commit is contained in:
Bill Currie 2023-06-20 15:18:58 +09:00
parent 38453f3d2f
commit 7da8399220
6 changed files with 240 additions and 87 deletions

View file

@ -122,6 +122,7 @@ typedef struct qfv_dependencyinfo_s {
typedef struct qfv_attachmentinfo_s {
const char *name;
int line;
VkAttachmentDescriptionFlags flags;
VkFormat format;
VkSampleCountFlagBits samples;
@ -133,6 +134,7 @@ typedef struct qfv_attachmentinfo_s {
VkImageLayout finalLayout;
VkClearValue clearValue;
qfv_reference_t view;
const char *external;
} qfv_attachmentinfo_t;
typedef struct qfv_taskinfo_s {
@ -225,7 +227,6 @@ typedef struct qfv_renderinfo_s {
uint32_t num_renderpasses;
qfv_renderpassinfo_t *renderpasses;
qfv_output_t output;
} qfv_renderinfo_t;
typedef struct qfv_processinfo_s {
@ -274,13 +275,6 @@ typedef struct qfv_label_s {
const char *name;
} qfv_label_t;
typedef struct qfv_bar_s {
VkBuffer *buffers;
VkDeviceSize *offsets;
uint32_t firstBinding;
uint32_t bindingCount;
} qfv_bar_t;
typedef struct qfv_pipeline_s {
qfv_label_t label;
VkPipelineBindPoint bindPoint;
@ -314,7 +308,6 @@ typedef struct qfv_renderpass_s {
VkSubpassContents subpassContents;
qfv_framebufferinfo_t *framebufferinfo;
//qfv_output_t output;
uint32_t subpass_count;
qfv_subpass_t *subpasses;
@ -325,6 +318,7 @@ typedef struct qfv_render_s {
qfv_renderpass_t *active;
qfv_renderpass_t *renderpasses;
uint32_t num_renderpasses;
qfv_output_t output;
} qfv_render_t;
typedef struct qfv_compute_s {
@ -386,6 +380,8 @@ void QFV_BuildRender (struct vulkan_ctx_s *ctx);
void QFV_Render_Init (struct vulkan_ctx_s *ctx);
void QFV_Render_Shutdown (struct vulkan_ctx_s *ctx);
void QFV_Render_AddTasks (struct vulkan_ctx_s *ctx, exprsym_t *task_sys);
qfv_step_t *QFV_GetStep (const exprval_t *param, qfv_job_t *job);
#endif//__QFCC__
#endif//__QF_Vulkan_render_h

View file

@ -44,9 +44,11 @@
#include "QF/Vulkan/command.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/pipeline.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/resource.h"
#include "QF/Vulkan/pipeline.h"
#include "QF/Vulkan/swapchain.h"
#include "vid_vulkan.h"
#include "vkparse.h"
@ -119,7 +121,6 @@ run_subpass (qfv_subpass_t *sp, VkCommandBuffer cmd, vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
dfunc->vkResetCommandBuffer (cmd, 0);
dfunc->vkBeginCommandBuffer (cmd, &sp->beginInfo);
QFV_duCmdBeginLabel (device, cmd, sp->label.name,
{VEC4_EXP (sp->label.color)});
@ -308,7 +309,17 @@ QFV_CreateFramebuffer (vulkan_ctx_t *ctx, qfv_renderpass_t *rp)
.layers = fb->layers,
};
for (uint32_t i = 0; i < fb->num_attachments; i++) {
attachments[i] = find_imageview (&fb->attachments[i].view, rctx);
if (fb->attachments[i].external) {
attachments[i] = 0;
if (!strcmp (fb->attachments[i].external, "$swapchain")) {
auto sc = ctx->swapchain;
attachments[i] = sc->imageViews->a[ctx->swapImageIndex];
cInfo.width = sc->extent.width;
cInfo.height = sc->extent.height;
}
} else {
attachments[i] = find_imageview (&fb->attachments[i].view, rctx);
}
}
qfv_device_t *device = ctx->device;
@ -339,36 +350,49 @@ wait_on_fence (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
dfunc->vkWaitForFences (dev, 1, &frame->fence, VK_TRUE, 2000000000);
}
static void
renderpass_update_viewper_sissor (qfv_renderpass_t *rp,
const qfv_output_t *output)
{
rp->beginInfo.renderArea.extent = output->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 = output->extent.width;
pl->viewport.height = output->extent.height;
pl->scissor.extent = output->extent;
}
}
}
static void
update_viewport_scissor (qfv_render_t *render, const qfv_output_t *output)
{
for (uint32_t i = 0; i < render->num_renderpasses; i++) {
renderpass_update_viewper_sissor (&render->renderpasses[i], output);
}
}
static void
update_framebuffer (const exprval_t **params, exprval_t *result,
exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto stepref = params[0];
// cache the render step referenced, using the parameter type as a flag
// for whether the caching has been performed.
if (stepref->type == &cexpr_string) {
if (cexpr_string.size != cexpr_voidptr.size) {
Sys_Error ("string and voidptr incompatible sizes");
}
auto name = *(const char **)stepref->value;
((exprval_t *) stepref)->type = &cexpr_voidptr;
*(void **)stepref->value = 0;
auto job = ctx->render_context->job;
for (uint32_t i = 0; i < job->num_steps; i++) {
auto step = &job->steps[i];
if (!strcmp (step->label.name, name)) {
*(void **)stepref->value = step;
break;
}
}
}
auto step = *(qfv_step_t **)stepref->value;
auto job = ctx->render_context->job;
auto step = QFV_GetStep (params[0], job);
auto render = step->render;
auto rp = render->active;
qfv_output_t output = {};
Vulkan_ConfigOutput (ctx, &output);
if (output.extent.width != render->output.extent.width
|| output.extent.height != render->output.extent.height) {
//FIXME framebuffer image creation here
update_viewport_scissor (render, &output);
}
if (!rp->beginInfo.framebuffer) {
QFV_CreateFramebuffer (ctx, rp);
}
@ -466,3 +490,30 @@ QFV_Render_AddTasks (vulkan_ctx_t *ctx, exprsym_t *task_syms)
}
}
}
qfv_step_t *
QFV_GetStep (const exprval_t *param, qfv_job_t *job)
{
// this is a little evil, but need to update the type after resolving
// the step name
auto stepref = (exprval_t *) param;
// cache the render step referenced, using the parameter type as a flag
// for whether the caching has been performed.
if (stepref->type == &cexpr_string) {
if (cexpr_string.size != cexpr_voidptr.size) {
Sys_Error ("string and voidptr incompatible sizes");
}
auto name = *(const char **)stepref->value;
stepref->type = &cexpr_voidptr;
*(void **)stepref->value = 0;
for (uint32_t i = 0; i < job->num_steps; i++) {
auto step = &job->steps[i];
if (!strcmp (step->label.name, name)) {
*(void **)stepref->value = step;
break;
}
}
}
return *(qfv_step_t **)stepref->value;
}

View file

@ -44,9 +44,10 @@
#include "QF/Vulkan/command.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/pipeline.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/resource.h"
#include "QF/Vulkan/pipeline.h"
#include "QF/Vulkan/swapchain.h"
#include "vid_vulkan.h"
#include "vkparse.h"
@ -211,13 +212,15 @@ count_stuff (qfv_jobinfo_t *jobinfo, objcount_t *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;
auto rctx = ctx->render_context;
auto jinfo = rctx->jobinfo;
auto job = rctx->job;
job->resources = malloc (sizeof(qfv_resource_t)
+ counts->num_images * sizeof (qfv_resobj_t)
+ counts->num_imageviews * sizeof (qfv_resobj_t));
size_t size = sizeof (qfv_resource_t);
size += sizeof (qfv_resobj_t [counts->num_images]);
size += sizeof (qfv_resobj_t [counts->num_imageviews]);
job->resources = malloc (size);
job->images = (qfv_resobj_t *) &job->resources[1];
job->image_views = &job->images[counts->num_images];
@ -646,17 +649,32 @@ init_atCreate (uint32_t index, qfv_attachmentinfo_t *attachments, objstate_t *s)
__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,
};
if (ati->external) {
if (!strcmp (ati->external, "$swapchain")) {
*atc = (VkAttachmentDescription) {
.format = s->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,
};
}
} else {
*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;
}
@ -937,7 +955,7 @@ init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s)
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));
memcpy (cv, s.ptr.clear, sizeof (VkClearValue [counts->num_attachments ]));
uint32_t num_layouts = s.inds.num_layouts;
s.inds = (objcount_t) {};

View file

@ -939,7 +939,7 @@ renderpasses = {
alias:depth = {
color = $color.alias;
tasks = (
{ func = "alias_draw"; },
{ func = alias_draw; },
);
stages = (
@ -964,7 +964,7 @@ renderpasses = {
iqm:depth = {
color = $color.iqm;
tasks = (
{ func = "iqm_draw"; },
{ func = iqm_draw; },
);
stages = (
@ -977,7 +977,7 @@ renderpasses = {
sprite:depth = {
color = $color.sprite;
tasks = (
{ func = "sprite_draw"; },
{ func = sprite_draw; },
);
stages = (
@ -1121,7 +1121,7 @@ renderpasses = {
alias:gbuffer = {
color = $color.alias;
tasks = (
{ func = "alias_draw"; },
{ func = alias_draw; },
);
stages = (
@ -1135,7 +1135,7 @@ renderpasses = {
iqm:gbuffer = {
color = $color.iqm;
tasks = (
{ func = "iqm_draw"; },
{ func = iqm_draw; },
);
stages = (
@ -1149,7 +1149,7 @@ renderpasses = {
sprite:gbuffer = {
color = $color.sprite;
tasks = (
{ func = "sprite_draw"; },
{ func = sprite_draw; },
);
stages = (
@ -1189,7 +1189,7 @@ renderpasses = {
color = $color.lights;
tasks = (
{ func = "lights_draw"; },
{ func = lights_draw; },
);
stages = (
@ -1220,7 +1220,7 @@ renderpasses = {
color = $color.compose;
tasks = (
{ func = "compose_draw"; },
{ func = compose_draw; },
);
stages = (
@ -1243,18 +1243,9 @@ renderpasses = {
output = {
color = "[0, 1, 1, 1]";
framebuffer = {
width = $render_output.extent.width;
height = $render_output.extent.height;
layers = 1;
attachments = {
output = {
@inherit = $attachment_base;
format = $render_output.format;
loadOp = clear;
storeOp = store;
finalLayout = present_src_khr;
view = $render_output.view;
};
output = $swapchain;
};
};
subpasses = {
@ -1271,7 +1262,7 @@ renderpasses = {
color = $color.output;
tasks = (
{ func = "output_draw"; },
{ func = output_draw; },
);
stages = (
@ -1289,7 +1280,7 @@ steps = {
wait_on_fence = {
process = {
tasks = (
{ func = "wait_on_fence"; },
{ func = wait_on_fence; },
);
};
};
@ -1300,7 +1291,7 @@ steps = {
part:update = {
color = "[0.3, 0.8, 0.9]";
tasks = (
{ func = "update_particles"; }
{ func = update_particles; }
);
stage = {
name = main;
@ -1311,7 +1302,7 @@ steps = {
part:physics = {
color = "[0.6, 0.8, 0.9]";
tasks = (
{ func = "particle_physics"; }
{ func = particle_physics; }
);
stage = {
name = main;
@ -1330,14 +1321,14 @@ steps = {
dependencies = (wait_on_fence);
process = {
tasks = (
{ func = "clear_translucent"; },
{ func = clear_translucent; },
);
};
};
setup_main = {
process = {
tasks = (
{ func = "update_framebuffer";
{ func = update_framebuffer;
params = ("\"main\""); },
);
};
@ -1355,8 +1346,11 @@ steps = {
dependencies = (wait_on_fence);
process = {
tasks = (
{ func = "acquire_output"; },
{ func = "update_input"; },
{ func = acquire_output;
params = ("\"output\""); },
{ func = update_framebuffer;
params = ("\"output\""); },
{ func = update_input; },
);
};
};

View file

@ -624,6 +624,31 @@ parse = {
value = base_pipeline;
};
};
qfv_attachmentinfo_s = {
.name = qfv_attachmentinfo_t;
.type = (QFString, QFDictionary);
.string = {
name = $name;
line = $item.line;
external = $item.string;
};
.dictionary = {
.parse = auto;
name = $name;
line = $item.line;
};
flags = auto;
format = auto;
samples = auto;
loadOp = auto;
storeOp = auto;
stencilLoadOp = auto;
stencilStoreOp = auto;
initialLayout = auto;
finalLayout = auto;
clearValue = auto;
view = auto;
};
qfv_framebufferinfo_s = {
.name = qfv_framebufferinfo_t;
width = auto;

View file

@ -103,7 +103,7 @@ acquire_image (qfv_renderframe_t *rFrame)
}
static void
update_input (qfv_renderframe_t *rFrame)
output_update_input (qfv_renderframe_t *rFrame)
{
vulkan_ctx_t *ctx = rFrame->vulkan_ctx;
qfv_device_t *device = ctx->device;
@ -134,7 +134,7 @@ static void
preoutput_draw (qfv_renderframe_t *rFrame)
{
acquire_image (rFrame);
update_input (rFrame);
output_update_input (rFrame);
}
static void
@ -212,7 +212,7 @@ process_input (qfv_renderframe_t *rFrame)
}
static void
output_draw (qfv_renderframe_t *rFrame)
draw_output (qfv_renderframe_t *rFrame)
{
process_input (rFrame);
Vulkan_FlushText (rFrame);
@ -224,7 +224,7 @@ Vulkan_Output_CreateRenderPasses (vulkan_ctx_t *ctx)
outputctx_t *octx = calloc (1, sizeof (outputctx_t));
ctx->output_context = octx;
__auto_type out = QFV_RenderPass_New (ctx, "output", output_draw);
__auto_type out = QFV_RenderPass_New (ctx, "output", draw_output);
out->output = (qfv_output_t) {
.extent = ctx->swapchain->extent,
.format = ctx->swapchain->format,
@ -247,28 +247,97 @@ Vulkan_Output_CreateRenderPasses (vulkan_ctx_t *ctx)
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 frame = &ctx->frames.a[ctx->curFrame];
uint32_t imageIndex = 0;
while (!QFV_AcquireNextImage (ctx->swapchain,
frame->imageAvailableSemaphore,
0, &imageIndex)) {
QFV_DeviceWaitIdle (device);
if (ctx->capture) {
QFV_DestroyCapture (ctx->capture);
}
Vulkan_CreateSwapchain (ctx);
Vulkan_CreateCapture (ctx);
__auto_type out = ctx->output_renderpass;
out->output = (qfv_output_t) {
.extent = ctx->swapchain->extent,
.format = ctx->swapchain->format,
.frames = ctx->swapchain->numImages,
.view_list = ctx->swapchain->imageViews->a,
};
out->viewport.width = out->output.extent.width;
out->viewport.height = out->output.extent.height;
out->scissor.extent = out->output.extent;
auto step = QFV_GetStep (params[0], ctx->render_context->job);
auto render = step->render;
for (uint32_t i = 0; i < render->num_renderpasses; i++) {
auto rp = &render->renderpasses[i];
if (rp->beginInfo.framebuffer) {
dfunc->vkDestroyFramebuffer (device->dev,
rp->beginInfo.framebuffer, 0);
}
rp->beginInfo.framebuffer = 0; // leave for update_framebuffer
}
dfunc->vkDestroySemaphore (device->dev, frame->imageAvailableSemaphore,
0);
frame->imageAvailableSemaphore = QFV_CreateSemaphore (device);
}
ctx->swapImageIndex = imageIndex;
}
static void
_update_input (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
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];
if (oframe->input == octx->input) {
return;
}
oframe->input = octx->input;
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_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
output_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx)
{
}
static exprtype_t *stepref_param[] = {
&cexpr_string,
};
static exprfunc_t acquire_output_func[] = {
{ .func = acquire_output },
{ .func = acquire_output, .num_params = 1, stepref_param },
{}
};
static exprfunc_t update_input_func[] = {
{ .func = _update_input },
{ .func = update_input, .num_params = 1, stepref_param },
{}
};
static exprfunc_t output_draw_func[] = {
{ .func = _output_draw },
{ .func = output_draw },
{}
};
static exprsym_t output_task_syms[] = {