[vulkan] Implement mouse-picking for light entities

Currently, only light entities get drawn to the entid buffer, and the
ids are simply displayed in a window for now (not very useful yet).
This commit is contained in:
Bill Currie 2023-08-07 17:47:49 +09:00
parent 95b660d7fd
commit 1745d3bccc
17 changed files with 697 additions and 46 deletions

View file

@ -0,0 +1,46 @@
#ifndef __QF_Vulkan_mouse_pick_h
#define __QF_Vulkan_mouse_pick_h
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>
#include "QF/darray.h"
#include "QF/qtypes.h"
#define mousepick_size 5
struct vulkan_ctx_s;
typedef void (*mousepickfunc_t) (const uint32_t *entid, void *data);
typedef struct qfv_mousepick_frame_s {
VkBuffer entid_buffer;
uint32_t *entid_data;
bool initiated;
mousepickfunc_t callback;
void *callback_data;
uint32_t x, y;
VkOffset3D offset;
VkExtent3D extent;
VkImage entid_image;
} qfv_mousepick_frame_t;
typedef struct qfv_mousepick_frame_set_s
DARRAY_TYPE (qfv_mousepick_frame_t) qfv_mousepick_frame_set_t;
typedef struct qfv_mousepickctx_s {
qfv_mousepick_frame_set_t frames;
struct qfv_resobj_s *entid_res;
struct qfv_resource_s *resources;
} qfv_mousepickctx_t;
struct vulkan_ctx_s;
void QFV_MousePick_Init (struct vulkan_ctx_s *ctx);
void QFV_MousePick_Shutdown (struct vulkan_ctx_s *ctx);
void QFV_MousePick_Read (struct vulkan_ctx_s *ctx, uint32_t x, uint32_t y,
mousepickfunc_t callback, void *data);
#endif//__QF_Vulkan_mouse_pick_h

View file

@ -85,6 +85,7 @@ typedef struct lightingframe_s {
VkBuffer render_buffer;
VkBuffer style_buffer;
VkBuffer id_buffer;
VkBuffer entid_buffer;
light_queue_t light_queue[4];
qfv_imageviewset_t views;

View file

@ -433,8 +433,7 @@ typedef struct qfv_renderctx_s {
qfv_job_t *job;
qfv_renderframeset_t frames;
int64_t size_time;
struct imui_window_s *job_timings_window;
struct imui_window_s *job_control_window;
struct qfv_renderdebug_s *debug;
} qfv_renderctx_t;
typedef struct qfv_taskctx_s {
@ -472,6 +471,7 @@ VkSampler QFV_Render_Sampler (struct vulkan_ctx_s *ctx, const char *name);
qfv_step_t *QFV_GetStep (const exprval_t *param, qfv_job_t *job);
qfv_step_t *QFV_FindStep (const char *step, qfv_job_t *job) __attribute__((pure));
struct qfv_resobj_s *QFV_FindResource (const char *name, qfv_renderpass_t *rp) __attribute__((pure));
struct imui_ctx_s;
void QFV_Render_UI (struct vulkan_ctx_s *ctx, struct imui_ctx_s *imui_ctx);

View file

@ -40,6 +40,7 @@ typedef struct vulkan_ctx_s {
struct scriptctx_s *script_context;
struct qfv_renderctx_s *render_context;
struct qfv_capturectx_s *capture_context;
struct qfv_mousepickctx_s *mousepick_context;
struct texturectx_s *texture_context;
struct matrixctx_s *matrix_context;
struct translucentctx_s *translucent_context;

View file

@ -222,6 +222,7 @@ libs_video_renderer_librender_vulkan_la_SOURCES = \
libs/video/renderer/vulkan/dsmanager.c \
libs/video/renderer/vulkan/image.c \
libs/video/renderer/vulkan/instance.c \
libs/video/renderer/vulkan/mouse_pick.c \
libs/video/renderer/vulkan/memory.c \
libs/video/renderer/vulkan/pipeline.c \
libs/video/renderer/vulkan/projection.c \
@ -330,7 +331,11 @@ bsp_turbf_src = $(vkshaderpath)/bsp_turb.frag
bsp_turbf_c = $(vkshaderpath)/bsp_turb.frag.spvc
debug_src = $(vkshaderpath)/debug.frag
debug_c = $(vkshaderpath)/debug.frag.spvc
entid_src = $(vkshaderpath)/entid.frag
entid_c = $(vkshaderpath)/entid.frag.spvc
light_attach_h = $(vkshaderpath)/light_attach.h
light_entid_src = $(vkshaderpath)/light_entid.vert
light_entid_c = $(vkshaderpath)/light_entid.vert.spvc
light_flat_src = $(vkshaderpath)/light_flat.vert
light_flat_c = $(vkshaderpath)/light_flat.vert.spvc
light_splatv_src = $(vkshaderpath)/light_splat.vert
@ -437,6 +442,10 @@ $(bsp_turbf_c): $(bsp_turbf_src) $(oit_store) $(oit_h)
$(debug_c): $(debug_src) $(lighting_h)
$(entid_c): $(entid_src)
$(light_entid_c): $(light_entid_src) $(lighting_h)
$(light_flat_c): $(light_flat_src) $(lighting_h)
$(light_splatv_c): $(light_splatv_src) $(lighting_h)
@ -514,6 +523,8 @@ vkshader_c = \
$(bsp_skyf_c) \
$(bsp_turbf_c) \
$(debug_c) \
$(entid_c) \
$(light_entid_c) \
$(light_flat_c) \
$(light_splatv_c) \
$(light_splatf_c) \
@ -608,7 +619,9 @@ EXTRA_DIST += \
$(bsp_skyf_src) \
$(bsp_turbf_src) \
$(debug_src) \
$(entid_src) \
$(light_attach_h) \
$(light_entid_src) \
$(light_flat_src) \
$(light_splatv_src) \
$(light_splatf_src) \

View file

@ -65,6 +65,7 @@
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/mouse_pick.h"
#include "QF/Vulkan/projection.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/staging.h"
@ -99,6 +100,7 @@ vulkan_R_Init (void)
Vulkan_CreateSwapchain (vulkan_ctx);
QFV_Capture_Init (vulkan_ctx);
QFV_MousePick_Init (vulkan_ctx);
Vulkan_Output_Init (vulkan_ctx);
Vulkan_Matrix_Init (vulkan_ctx);
@ -372,12 +374,12 @@ vulkan_capture_screen (capfunc_t callback, void *data)
static void
vulkan_debug_ui (struct imui_ctx_s *imui_ctx)
{
QFV_Render_UI (vulkan_ctx, imui_ctx);
#define IMUI_context imui_ctx
UI_ExtendPanel ("Renderer##menu") {
QFV_Render_Menu (vulkan_ctx, imui_ctx);
}
#undef IMUI_context
QFV_Render_UI (vulkan_ctx, imui_ctx);
}
static void
@ -560,6 +562,7 @@ vulkan_vid_render_shutdown (void)
Vulkan_Scene_Shutdown (vulkan_ctx);
Vulkan_Matrix_Shutdown (vulkan_ctx);
QFV_MousePick_Shutdown (vulkan_ctx);
QFV_Capture_Shutdown (vulkan_ctx);
Vulkan_Output_Shutdown (vulkan_ctx);

View file

@ -0,0 +1,263 @@
/*
mouse_pick.c
Vulkan frame mouse picking support
Copyright (C) 2023 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/cexpr.h"
#include "QF/va.h"
#include "QF/Vulkan/command.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/mouse_pick.h"
#include "QF/Vulkan/render.h"
#include "QF/Vulkan/resource.h"
#include "QF/plugin/vid_render.h"
#include "vid_vulkan.h"
static void
mousepick_initiate (const exprval_t **params, exprval_t *result,
exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto mpctx = ctx->mousepick_context;
auto frame = &mpctx->frames.a[ctx->curFrame];
if (!frame->callback) {
return;
}
auto device = ctx->device;
auto dfunc = device->funcs;
auto cmd = QFV_GetCmdBuffer (ctx, false);
dfunc->vkBeginCommandBuffer (cmd, &(VkCommandBufferBeginInfo) {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
});
dfunc->vkCmdCopyImageToBuffer (cmd, frame->entid_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
frame->entid_buffer, 1,
&(VkBufferImageCopy) {
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
.imageOffset = frame->offset,
.imageExtent = frame->extent,
});
dfunc->vkEndCommandBuffer (cmd);
QFV_AppendCmdBuffer (ctx, cmd);
frame->initiated = true;
}
static void
mousepick_finalize (const exprval_t **params, exprval_t *result,
exprctx_t *ectx)
{
auto taskctx = (qfv_taskctx_t *) ectx;
auto ctx = taskctx->ctx;
auto mpctx = ctx->mousepick_context;
auto frame = &mpctx->frames.a[ctx->curFrame];
if (!frame->callback || !frame->initiated) {
return;
}
auto device = ctx->device;
auto dfunc = device->funcs;
dfunc->vkInvalidateMappedMemoryRanges (device->dev, 1,
&(VkMappedMemoryRange) {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = mpctx->resources->memory,
.offset = 0,
.size = VK_WHOLE_SIZE,
});
uint32_t entids[mousepick_size * mousepick_size];
memset (entids, 0xff, sizeof (entids));
if (frame->extent.width > mousepick_size
|| frame->extent.height > mousepick_size) {
Sys_Error ("mousepick_finalize: invalid extent: %d %d\n",
frame->extent.width, frame->extent.height);
}
for (uint32_t j = 0; j < frame->extent.height; j++) {
uint32_t y = j + 2 - (frame->y - frame->offset.y);
uint32_t srcInd = j * frame->extent.width;
uint32_t dstInd = y * mousepick_size;
for (uint32_t i = 0; i < frame->extent.width; i++) {
uint32_t x = i + 2 - (frame->x - frame->offset.x);
entids[dstInd + x] = frame->entid_data[srcInd + i];
}
}
#if 0
for (uint32_t j = 0; j < mousepick_size; j++) {
for (uint32_t i = 0; i < mousepick_size; i++) {
printf ("%03x ", entids[j * mousepick_size + i] & 0xfff);
}
puts ("");
}
puts ("");
#endif
frame->callback (entids, frame->callback_data);
frame->callback = 0;
frame->callback_data = 0;
frame->initiated = false;
}
static exprfunc_t mousepick_initiate_func[] = {
{ .func = mousepick_initiate },
{}
};
static exprfunc_t mousepick_finalize_func[] = {
{ .func = mousepick_finalize },
{}
};
static exprsym_t mousepick_task_syms[] = {
{ "mousepick_initiate", &cexpr_function, mousepick_initiate_func },
{ "mousepick_finalize", &cexpr_function, mousepick_finalize_func },
{}
};
void
QFV_MousePick_Init (vulkan_ctx_t *ctx)
{
QFV_Render_AddTasks (ctx, mousepick_task_syms);
qfvPushDebug (ctx, "mouse pick init");
auto device = ctx->device;
auto dfunc = device->funcs;
ctx->mousepick_context = calloc (1, sizeof (qfv_mousepickctx_t));
auto mpctx = ctx->mousepick_context;
auto rctx = ctx->render_context;
size_t frames = rctx->frames.size;
DARRAY_INIT (&mpctx->frames, frames);
DARRAY_RESIZE (&mpctx->frames, frames);
mpctx->frames.grow = 0;
mpctx->resources = calloc (1, sizeof (qfv_resource_t)
+ sizeof (qfv_resobj_t[frames]));
auto buffers = (qfv_resobj_t *) &mpctx->resources[1];
mpctx->resources[0] = (qfv_resource_t) {
.name = "mousepick",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
.num_objects = frames,
.objects = buffers,
};
for (size_t i = 0; i < frames; i++) {
buffers[i] = (qfv_resobj_t) {
.name = va (ctx->va_ctx, "entids:%zd", i),
.type = qfv_res_buffer,
.buffer = {
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.size = 16 * 16 * sizeof (uint32_t),
},
};
}
QFV_CreateResource (device, mpctx->resources);
byte *entid_data;
dfunc->vkMapMemory (device->dev, mpctx->resources->memory, 0,
mpctx->resources->size, 0, (void **) &entid_data);
for (size_t i = 0; i < frames; i++) {
auto frame = &mpctx->frames.a[i];
*frame = (qfv_mousepick_frame_t) {
.entid_buffer = buffers[i].buffer.buffer,
.entid_data = (uint32_t *) (entid_data + buffers[i].buffer.offset),
};
}
qfvPopDebug (ctx);
}
void
QFV_MousePick_Shutdown (vulkan_ctx_t *ctx)
{
auto device = ctx->device;
auto dfunc = device->funcs;
auto mpctx = ctx->mousepick_context;
dfunc->vkUnmapMemory (device->dev, mpctx->resources->memory);
QFV_DestroyResource (device, mpctx->resources);
free (mpctx->resources);
free (mpctx->frames.a);
free (mpctx);
}
void
QFV_MousePick_Read (vulkan_ctx_t *ctx, uint32_t x, uint32_t y,
mousepickfunc_t callback, void *data)
{
auto mpctx = ctx->mousepick_context;
auto frame = &mpctx->frames.a[ctx->curFrame];
if (!mpctx->entid_res) {
auto job = ctx->render_context->job;
auto step = QFV_FindStep ("main", job);
auto rp = &step->render->renderpasses[0];
mpctx->entid_res = QFV_FindResource ("entid", rp);
}
frame->entid_image = mpctx->entid_res->image.image;
VkExtent3D extent = mpctx->entid_res->image.extent;
if (x >= extent.width || y >= extent.height) {
return;
}
frame->callback = callback;
frame->callback_data = data;
frame->x = x;
frame->y = y;
frame->offset = (VkOffset3D) {
.x = x >= (mousepick_size / 2) ? x - (mousepick_size / 2) : 0,
.y = y >= (mousepick_size / 2) ? y - (mousepick_size / 2) : 0,
};
frame->extent = (VkExtent3D) {
.width = extent.width > x + (mousepick_size / 2)
? (mousepick_size / 2) + 1 : extent.width - x,
.height = extent.height > y + (mousepick_size / 2)
? (mousepick_size / 2) + 1 : extent.height - y,
.depth = 1,
};
frame->extent.width += x - frame->offset.x;
frame->extent.height += y - frame->offset.y;
}

View file

@ -629,6 +629,21 @@ QFV_Render_AddAttachments (vulkan_ctx_t *ctx, uint32_t num_attachments,
}
}
qfv_resobj_t *
QFV_FindResource (const char *name, qfv_renderpass_t *rp)
{
if (!rp->resources) {
return 0;
}
for (uint32_t i = 0; i < rp->resources->num_objects; i++) {
auto obj = &rp->resources->objects[i];
if (!strcmp (obj->name, name)) {
return obj;
}
}
return 0;
}
qfv_step_t *
QFV_FindStep (const char *name, qfv_job_t *job)
{

View file

@ -28,14 +28,25 @@
# include "config.h"
#endif
#include "QF/fbsearch.h"
#include "QF/va.h"
#include "QF/ui/imui.h"
#include "QF/Vulkan/mouse_pick.h"
#include "QF/Vulkan/render.h"
#include "vid_vulkan.h"
#define IMUI_context imui_ctx
#define picked_entity_count (mousepick_size * mousepick_size)
typedef struct qfv_renderdebug_s {
imui_window_t job_timings_window;
imui_window_t job_control_window;
imui_window_t entity_window;
uint32_t picked_enties[picked_entity_count];
} qfv_renderdebug_t;
static void
hs (imui_ctx_t *imui_ctx, int size)
{
@ -61,16 +72,8 @@ static void
job_timings_window (vulkan_ctx_t *ctx, imui_ctx_t *imui_ctx)
{
auto rctx = ctx->render_context;
if (!rctx->job_timings_window) {
rctx->job_timings_window = malloc (sizeof (imui_window_t));
*rctx->job_timings_window = (imui_window_t) {
.name = nva ("Job Timings##%p.window", rctx),
.xpos = 100,
.ypos = 50,
};
}
UI_Window (rctx->job_timings_window) {
if (rctx->job_timings_window->is_collapsed) {
UI_Window (&rctx->debug->job_timings_window) {
if (rctx->debug->job_timings_window.is_collapsed) {
continue;
}
auto job = rctx->job;
@ -126,16 +129,8 @@ static void
job_control_window (vulkan_ctx_t *ctx, imui_ctx_t *imui_ctx)
{
auto rctx = ctx->render_context;
if (!rctx->job_control_window) {
rctx->job_control_window = malloc (sizeof (imui_window_t));
*rctx->job_control_window = (imui_window_t) {
.name = nva ("Job Control##%p.window", rctx),
.xpos = 100,
.ypos = 50,
};
}
UI_Window (rctx->job_control_window) {
if (rctx->job_control_window->is_collapsed) {
UI_Window (&rctx->debug->job_control_window) {
if (rctx->debug->job_control_window.is_collapsed) {
continue;
}
auto job = rctx->job;
@ -180,11 +175,114 @@ job_control_window (vulkan_ctx_t *ctx, imui_ctx_t *imui_ctx)
}
}
static void
mousepick_callback (const uint32_t *entid, void *data)
{
auto debug = ((qfv_renderctx_t *) data)->debug;
memcpy (debug->picked_enties, entid, sizeof (debug->picked_enties));
}
static int
entid_counts_cmp (const void *_a, const void *_b)
{
const uint32_t *a = _a;
const uint32_t *b = _b;
return *a - *b;
}
static void
entity_window (vulkan_ctx_t *ctx, imui_ctx_t *imui_ctx)
{
auto rctx = ctx->render_context;
auto debug = rctx->debug;
UI_Window (&debug->entity_window) {
if (debug->entity_window.is_collapsed) {
continue;
}
typedef struct {
uint32_t entid;
uint32_t count;
} entid_counts_t;
entid_counts_t entid_counts[picked_entity_count];
uint32_t num_entids = 0;
for (uint32_t i = 0; i < picked_entity_count; i++) {
if (debug->picked_enties[i] == nullent) {
continue;
}
if (!num_entids) {
entid_counts[num_entids++] = (entid_counts_t) {
.entid = debug->picked_enties[i],
.count = 1,
};
continue;
}
entid_counts_t *ec = 0;
ec = fbsearch (&debug->picked_enties[i],
entid_counts, num_entids, sizeof (entid_counts_t),
entid_counts_cmp);
uint32_t ind = 0;
if (ec) {
if (ec->entid == debug->picked_enties[i]) {
ec->count++;
continue;
}
ind = ec - entid_counts + 1;
}
memmove (entid_counts + ind + 1, entid_counts + ind,
sizeof (entid_counts_t[num_entids - ind]));
entid_counts[ind] = (entid_counts_t) {
.entid = debug->picked_enties[i],
.count = 1,
};
num_entids++;
}
auto io = IMUI_GetIO (imui_ctx);
if (io.hot == nullent && io.active == nullent) {
QFV_MousePick_Read (ctx, io.mouse.x, io.mouse.y,
mousepick_callback, rctx);
for (uint32_t i = num_entids; i-- > 0; ) {
UI_Horizontal {
UI_Label ("Entity");
hs (imui_ctx, 1);
UI_FlexibleSpace ();
UI_Labelf ("%08x %2d##%p.entity", entid_counts[i].entid,
entid_counts[i].count, rctx);
}
}
}
}
}
void
QFV_Render_UI (vulkan_ctx_t *ctx, imui_ctx_t *imui_ctx)
{
auto rctx = ctx->render_context;
if (!rctx->debug) {
rctx->debug = malloc (sizeof (qfv_renderdebug_t));
*rctx->debug = (qfv_renderdebug_t) {
.job_timings_window = {
.name = nva ("Job Timings##%p.window", rctx),
.xpos = 100,
.ypos = 50,
},
.job_control_window = {
.name = nva ("Job Control##%p.window", rctx),
.xpos = 100,
.ypos = 50,
},
.entity_window = {
.name = nva ("Entities##%p.window", rctx),
.xpos = 100,
.ypos = 50,
},
};
memset (rctx->debug->picked_enties, 0xff,
sizeof (rctx->debug->picked_enties));
}
job_timings_window (ctx, imui_ctx);
job_control_window (ctx, imui_ctx);
entity_window (ctx, imui_ctx);
}
void
@ -192,10 +290,13 @@ QFV_Render_Menu (vulkan_ctx_t *ctx, imui_ctx_t *imui_ctx)
{
auto rctx = ctx->render_context;
if (UI_MenuItem (va (ctx->va_ctx, "Job Timings##%p", rctx))) {
rctx->job_timings_window->is_open = true;
rctx->debug->job_timings_window.is_open = true;
}
if (UI_MenuItem (va (ctx->va_ctx, "Job Control##%p", rctx))) {
rctx->job_control_window->is_open = true;
rctx->debug->job_control_window.is_open = true;
}
if (UI_MenuItem (va (ctx->va_ctx, "Entities##%p", rctx))) {
rctx->debug->entity_window.is_open = true;
}
}
@ -203,12 +304,10 @@ void
QFV_Render_UI_Shutdown (vulkan_ctx_t *ctx)
{
auto rctx = ctx->render_context;
if (rctx->job_timings_window) {
free ((char *) rctx->job_timings_window->name);
free (rctx->job_timings_window);
}
if (rctx->job_control_window) {
free ((char *) rctx->job_control_window->name);
free (rctx->job_control_window);
if (rctx->debug) {
free ((char *) rctx->debug->job_timings_window.name);
free ((char *) rctx->debug->job_control_window.name);
free ((char *) rctx->debug->entity_window.name);
free (rctx->debug);
}
}

View file

@ -610,6 +610,15 @@ properties = {
descriptorSets = (matrix_set, planes_set);
};
};
general = {
shader = {
fragment_entid = {
stage = fragment;
name = main;
module = $builtin/entid.frag;
};
};
};
lighting = {
shader = {
vertex_splat = {
@ -617,6 +626,11 @@ properties = {
name = main;
module = $builtin/light_splat.vert;
};
vertex_entid = {
stage = vertex;
name = main;
module = $builtin/light_entid.vert;
};
vertex_flat = {
stage = vertex;
name = main;
@ -983,6 +997,12 @@ descriptorSetLayouts = {
descriptorCount = 1;
stageFlags = fragment;
},
{
binding = 4;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = vertex;
},
);
};
lighting_shadow = {
@ -1103,6 +1123,11 @@ images = {
usage = color_attachment|input_attachment|sampled;
format = $render_output.format;
};
entid = {
@inherit = $image_base;
usage = color_attachment|transfer_src;
format = r32_uint;
};
cube_depth = {
@inherit = $cube_image_base;
@ -1135,6 +1160,11 @@ images = {
usage = color_attachment|input_attachment|sampled;
format = $render_output.format;
};
cube_entid = {
@inherit = $cube_image_base;
usage = color_attachment|transfer_src;
format = r32_uint;
};
};
imageviews = {
depth = {
@ -1175,6 +1205,10 @@ imageviews = {
image = output;
format = $render_output.format;
};
entid = {
@inherit = $view_base;
image = entid;
};
cube_depth = {
@inherit = $cube_view_base;
image = cube_depth;
@ -1214,6 +1248,10 @@ imageviews = {
image = cube_output;
format = $render_output.format;
};
cube_entid = {
@inherit = $cube_view_base;
image = cube_entid;
};
};
output = {
view = $output;
@ -1264,6 +1302,17 @@ renderpasses = {
loadOp = clear;
view = light;
};
entid = {
@inherit = $attachment_base;
format = $images.entid.format;
loadOp = clear;
storeOp = store;
finalLayout = transfer_src_optimal;
clearValue = {
color = { int32 = ( -1, -1, -1, -1); };
};
view = entid;
};
output = {
@inherit = $attachment_base;
format = $render_output.format;
@ -1748,13 +1797,51 @@ renderpasses = {
};
};
};
entid = {
color = "[ 1, 0, 0, 1]";
dependencies = {
depth = $depth_dependency;
};
attachments = {
color = {
entid = {
layout = color_attachment_optimal;
blend = $blend_disable;
};
};
preserve = (depth, normal, position, output);
};
pipelines = {
entid_lights = {
@inherit = $compose_base;
disabled = false;
color = $color.lights;
tasks = (
{ func = lighting_bind_descriptors;
params = (debug, none); },
{ func = lighting_draw_splats; },
);
stages = (
$lighting.shader.vertex_entid,
$general.shader.fragment_entid,
);
vertexInput = $lighting.vertexInput_splat;
inputAssembly = $lighting.inputAssembly;
layout = $lighting.splat_layout;
rasterization = $debug_poly_lines;
depthStencil = $depth_disable;
};
};
};
};
output = output;
};
deferred_cube = {
@inherit = $renderpasses.deferred;
@next = (VkRenderPassMultiviewCreateInfo, {
viewMasks = (0x3fu, 0x3fu, 0x3fu, 0x3fu, 0x3fu);
viewMasks = (0x3fu, 0x3fu, 0x3fu, 0x3fu, 0x3fu, 0x3fu);
});
framebuffer = {
width = "min($render_output.extent.width,$render_output.extent.height)";
@ -1796,6 +1883,17 @@ renderpasses = {
format = $images.cube_light.format;
view = cube_light;
};
entid = {
@inherit = $attachment_base;
format = $images.cube_entid.format;
loadOp = clear;
storeOp = store;
finalLayout = transfer_src_optimal;
clearValue = {
color = { int32 = ( -1, -1, -1, -1); };
};
view = cube_entid;
};
output = {
@inherit = $attachment_base;
format = $render_output.format;
@ -2020,6 +2118,7 @@ steps = {
tasks = (
{ func = wait_on_fence; },
{ func = capture_finalize; },
{ func = mousepick_finalize; },
{ func = update_matrices; },
{ func = draw_scr_funcs; },
);
@ -2140,4 +2239,12 @@ steps = {
);
};
};
mouse_pick = {
dependencies = (main);
process = {
tasks = (
{ func = mousepick_initiate; },
);
};
};
};

View file

@ -91,6 +91,10 @@ static
static
#include "libs/video/renderer/vulkan/shader/debug.frag.spvc"
static
#include "libs/video/renderer/vulkan/shader/entid.frag.spvc"
static
#include "libs/video/renderer/vulkan/shader/light_entid.vert.spvc"
static
#include "libs/video/renderer/vulkan/shader/light_flat.vert.spvc"
static
#include "libs/video/renderer/vulkan/shader/light_splat.vert.spvc"
@ -174,6 +178,8 @@ static shaderdata_t builtin_shaders[] = {
{ "bsp_sky.frag", bsp_sky_frag, sizeof (bsp_sky_frag) },
{ "bsp_turb.frag", bsp_turb_frag, sizeof (bsp_turb_frag) },
{ "debug.frag", debug_frag, sizeof (debug_frag) },
{ "entid.frag", entid_frag, sizeof (entid_frag) },
{ "light_entid.vert", light_entid_vert, sizeof (light_entid_vert) },
{ "light_flat.vert", light_flat_vert, sizeof (light_flat_vert) },
{ "light_splat.vert", light_splat_vert, sizeof (light_splat_vert) },
{ "light_splat.frag", light_splat_frag, sizeof (light_splat_frag) },

View file

@ -13,11 +13,11 @@ layout (set = 1, binding = 0) buffer Entities {
};
layout (location = 0) in vec4 vertex;
layout (location = 2) in uint entid;
layout (location = 2) in uint entind;
void
main (void)
{
vec3 vert = vertex * entities[entid].transform;
vec3 vert = vertex * entities[entind].transform;
gl_Position = Projection3d * (View[gl_ViewIndex] * vec4 (vert, 1));
}

View file

@ -13,7 +13,7 @@ layout (set = 1, binding = 0) buffer Entities {
layout (location = 0) in vec4 vertex;
layout (location = 1) in vec4 tl_uv;
layout (location = 2) in uint entid;
layout (location = 2) in uint entind;
layout (location = 0) out vec4 tl_st;
layout (location = 1) out vec3 direction;
@ -23,9 +23,9 @@ void
main (void)
{
// geometry shader will take care of Projection and View
vec3 vert = vertex * entities[entid].transform;
vec3 vert = vertex * entities[entind].transform;
gl_Position = vec4 (vert, 1);
direction = (Sky * vertex).xyz;
tl_st = tl_uv;
color = entities[entid].color;
color = entities[entind].color;
}

View file

@ -0,0 +1,10 @@
#version 450
layout (location = 0) in flat uint entid;
layout (location = 0) out uint frag_entid;
void
main (void)
{
frag_entid = entid;
}

View file

@ -0,0 +1,46 @@
#version 450
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_multiview : enable
layout (set = 0, binding = 0) uniform
#include "matrices.h"
;
#include "lighting.h"
layout (location = 0) in uint light_index;
layout (location = 1) in vec3 splat_vert;
layout (location = 0) out uint light_index_out;
vec4 // assumes a and b are unit vectors
from_to_rotation (vec3 a, vec3 b)
{
float d = dot (a + b, a + b);
float qc = sqrt (d);
vec3 qv = d > 1e-6 ? cross (a, b) / qc : vec3 (1, 0, 0);
return vec4 (qv, qc * 0.5);
}
vec3
quat_mul (vec4 q, vec3 v)
{
vec3 uv = cross (q.xyz, v);
vec3 uuv = cross (q.xyz, uv);
return v + ((uv * q.w) + uuv) * 2;
}
void
main (void)
{
LightData l = lights[light_index];
float sz = l.attenuation.w > 0 ? 1 / l.attenuation.w : sqrt(abs(l.color.w));
float c = l.direction.w;
float sxy = sz * (c < 0 ? sqrt (c*c * (1 - c*c)) / (0.5 - c) : 1);
vec3 scale = vec3 (sxy, sxy, sz);
vec4 q = from_to_rotation (vec3 (0, 0, -1), l.direction.xyz);
vec4 pos = vec4 (quat_mul (q, splat_vert * scale), 0);
gl_Position = Projection3d * (View[gl_ViewIndex] * (pos + l.position));
light_index_out = light_entids[gl_InstanceIndex];
}

View file

@ -27,3 +27,6 @@ layout (set = 1, binding = 2) buffer Renderer {
layout (set = 1, binding = 3) buffer Style {
vec4 style[];
};
layout (set = 1, binding = 4) buffer LightEntIds {
uint light_entids[];
};

View file

@ -372,7 +372,8 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
QFV_PacketCopyBuffer (packet, lframe->style_buffer, 0, bb);
QFV_PacketSubmit (packet);
uint32_t ids[4][MaxLights];
uint32_t light_ids[4][MaxLights];
uint32_t entids[4][MaxLights];
uint32_t light_count = 0;
auto queue = lframe->light_queue;
@ -398,7 +399,9 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
light_count++;
uint32_t id = lctx->light_control.a[get_lightid (ent)].light_id;
int mode = lctx->light_control.a[get_lightid (ent)].mode;
ids[mode][queue[mode].count++] = id;
light_ids[mode][queue[mode].count] = id;
entids[mode][queue[mode].count] = ent.id;
queue[mode].count++;
}
if (ndlight) {
@ -408,7 +411,9 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
for (int i = 0; i < ndlight; i++) {
uint32_t id = lctx->dynamic_base + i;
set_lightid (dynamic_light_entities[i], lctx->scene->reg, id);
ids[ST_CUBE][queue[ST_CUBE].count++] = id;
light_ids[ST_CUBE][queue[ST_CUBE].count] = id;
entids[ST_CUBE][queue[ST_CUBE].count] = dynamic_light_entities[i];
queue[ST_CUBE].count++;
VectorCopy (dynamic_lights[i]->color, lights[i].color);
// dynamic lights seem a tad faint, so 16x map lights
@ -464,12 +469,23 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
uint32_t *lids = QFV_PacketExtend (packet,
sizeof (uint32_t[light_count]));
for (int i = 0; i < 4; i++) {
memcpy (lids + queue[i].start, ids[i],
memcpy (lids + queue[i].start, light_ids[i],
sizeof (uint32_t[queue[i].count]));
}
QFV_PacketCopyBuffer (packet, lframe->id_buffer, 0,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
packet = QFV_PacketAcquire (ctx->staging);
uint32_t *eids = QFV_PacketExtend (packet,
sizeof (uint32_t[light_count]));
for (int i = 0; i < 4; i++) {
memcpy (eids + queue[i].start, entids[i],
sizeof (uint32_t[queue[i].count]));
}
QFV_PacketCopyBuffer (packet, lframe->entid_buffer, 0,
&bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]);
QFV_PacketSubmit (packet);
}
}
@ -894,6 +910,8 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
+ sizeof (qfv_resobj_t)
// default shadow map and views
+ 3 * sizeof (qfv_resobj_t)
// light entids
+ sizeof (qfv_resobj_t[frames])
// light ids
+ sizeof (qfv_resobj_t[frames])
// light data
@ -908,7 +926,7 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
.name = "lights",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = 2 + 3 + 5 * frames,
.num_objects = 2 + 3 + 6 * frames,
.objects = (qfv_resobj_t *) &lctx->light_resources[1],
};
auto splat_verts = lctx->light_resources->objects;
@ -916,7 +934,8 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
auto default_map = &splat_inds[1];
auto default_view_cube = &default_map[1];
auto default_view_2d = &default_view_cube[1];
auto light_ids = &default_view_2d[1];
auto light_entids = &default_view_2d[1];
auto light_ids = &light_entids[frames];
auto light_data = &light_ids[frames];
auto light_render = &light_data[frames];
auto light_styles = &light_render[frames];
@ -983,11 +1002,21 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
},
};
for (size_t i = 0; i < frames; i++) {
light_entids[i] = (qfv_resobj_t) {
.name = va (ctx->va_ctx, "entids:%zd", i),
.type = qfv_res_buffer,
.buffer = {
.size = MaxLights * sizeof (uint32_t),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
};
light_ids[i] = (qfv_resobj_t) {
.name = va (ctx->va_ctx, "ids:%zd", i),
.type = qfv_res_buffer,
.buffer = {
.size = 2 * MaxLights * sizeof (uint32_t),
.size = MaxLights * sizeof (uint32_t),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
@ -1063,6 +1092,7 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
.render_buffer = light_render[i].buffer.buffer,
.style_buffer = light_styles[i].buffer.buffer,
.id_buffer = light_ids[i].buffer.buffer,
.entid_buffer = light_entids[i].buffer.buffer,
};
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET,
@ -1089,6 +1119,8 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
.offset = 0, .range = VK_WHOLE_SIZE, },
{ .buffer = lframe->style_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
{ .buffer = lframe->entid_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
};
VkWriteDescriptorSet bufferWrite[] = {
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
@ -1122,8 +1154,14 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[4], },
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->lights_set,
.dstBinding = 4,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[5], },
};
dfunc->vkUpdateDescriptorSets (device->dev, 5, bufferWrite, 0, 0);
dfunc->vkUpdateDescriptorSets (device->dev, 6, bufferWrite, 0, 0);
}
make_default_map (64, lctx->default_map, ctx);