mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-14 13:21:43 +00:00
[vulkan] Use occlusion queries for culling lights
The info isn't used yet, but this shows that vulkan's occlusion queries are at least somewhat useful. However, the technique isn't perfect: infinite radius lights (1/r and 1/r^2) are difficult to cull, and all lights can poke through thin enough walls, and then lights containing the camera get culled incorrectly (will need a separate test). Still, it looks like it will help once everything is tied together.
This commit is contained in:
parent
1c13879fb9
commit
f282bfc045
5 changed files with 307 additions and 4 deletions
include/QF/Vulkan
libs/video/renderer/vulkan
|
@ -196,6 +196,14 @@ DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDrawIndexed)
|
|||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDrawIndexedIndirect)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDrawIndirect)
|
||||
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateQueryPool)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyQueryPool)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdResetQueryPool)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBeginQuery)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdEndQuery)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyQueryPoolResults)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkGetQueryPoolResults)
|
||||
|
||||
#undef DEVICE_LEVEL_VULKAN_FUNCTION
|
||||
|
||||
#ifndef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION
|
||||
|
|
|
@ -85,6 +85,9 @@ typedef struct lightingframe_s {
|
|||
VkDescriptorSet lights_set;
|
||||
VkDescriptorSet attach_set;
|
||||
|
||||
VkQueryPool query;
|
||||
VkFence fence;
|
||||
|
||||
VkBuffer shadowmat_buffer;
|
||||
VkBuffer shadowmat_id_buffer;
|
||||
VkBuffer light_buffer;
|
||||
|
|
|
@ -1100,6 +1100,11 @@ descriptorSetLayouts = {
|
|||
};
|
||||
};
|
||||
images = {
|
||||
occlusion_depth = {
|
||||
@inherit = $image_base;
|
||||
format = x8_d24_unorm_pack32;
|
||||
usage = depth_stencil_attachment|input_attachment|transient_attachment;
|
||||
};
|
||||
depth = {
|
||||
@inherit = $image_base;
|
||||
format = x8_d24_unorm_pack32;
|
||||
|
@ -1136,6 +1141,11 @@ images = {
|
|||
format = r32_uint;
|
||||
};
|
||||
|
||||
occlusion_cube_depth = {
|
||||
@inherit = $cube_image_base;
|
||||
format = x8_d24_unorm_pack32;
|
||||
usage = depth_stencil_attachment|input_attachment|transient_attachment;
|
||||
};
|
||||
cube_depth = {
|
||||
@inherit = $cube_image_base;
|
||||
format = x8_d24_unorm_pack32;
|
||||
|
@ -1174,6 +1184,14 @@ images = {
|
|||
};
|
||||
};
|
||||
imageviews = {
|
||||
occlusion_depth = {
|
||||
@inherit = $view_base;
|
||||
image = occlusion_depth;
|
||||
format = $images.occlusion_depth.format;
|
||||
subresourceRange = {
|
||||
aspectMask = depth;
|
||||
};
|
||||
};
|
||||
depth = {
|
||||
@inherit = $view_base;
|
||||
image = depth;
|
||||
|
@ -1216,6 +1234,14 @@ imageviews = {
|
|||
@inherit = $view_base;
|
||||
image = entid;
|
||||
};
|
||||
occlusion_cube_depth = {
|
||||
@inherit = $cube_view_base;
|
||||
image = occlusion_cube_depth;
|
||||
format = $images.occlusion_cube_depth.format;
|
||||
subresourceRange = {
|
||||
aspectMask = depth;
|
||||
};
|
||||
};
|
||||
cube_depth = {
|
||||
@inherit = $cube_view_base;
|
||||
image = cube_depth;
|
||||
|
@ -1266,6 +1292,127 @@ output = {
|
|||
finalLayout = shader_read_only_optimal;
|
||||
};
|
||||
renderpasses = {
|
||||
occlusion = {
|
||||
color = "[0.8, 0.6, 0, 1]";
|
||||
framebuffer = {
|
||||
width = $render_output.extent.width;
|
||||
height = $render_output.extent.height;
|
||||
layers = 1;
|
||||
attachments = {
|
||||
depth = {
|
||||
@inherit = $attachment_base;
|
||||
format = $images.depth.format;
|
||||
loadOp = clear;
|
||||
finalLayout = depth_stencil_attachment_optimal;
|
||||
clearValue = { depthStencil = { depth = 0; stencil = 0; }; };
|
||||
view = occlusion_depth;
|
||||
};
|
||||
};
|
||||
};
|
||||
subpasses = {
|
||||
depth = {
|
||||
color = "[ 0.5, 0.5, 0.5, 1]";
|
||||
attachments = {
|
||||
depth = {
|
||||
depth = depth_stencil_attachment_optimal;
|
||||
};
|
||||
};
|
||||
|
||||
base_pipeline = {
|
||||
@inherit = $pipeline_base;
|
||||
depthStencil = $depth_test_and_write;
|
||||
rasterization = $cw_cull_back;
|
||||
};
|
||||
pipelines = {
|
||||
bsp:depth = {
|
||||
color = $color.bsp;
|
||||
tasks = (
|
||||
{ func = bsp_draw_queue;
|
||||
params = (main, solid, 0); },
|
||||
{ func = bsp_draw_queue;
|
||||
params = (main, sky, 0); },
|
||||
);
|
||||
|
||||
stages = (
|
||||
$brush.shader.depth_vertex,
|
||||
);
|
||||
vertexInput = {
|
||||
bindings = (
|
||||
"$brush.vertexInput.bindings[0]",
|
||||
"$brush.vertexInput.bindings[1]",
|
||||
);
|
||||
attributes = (
|
||||
"$brush.vertexInput.attributes[0]",
|
||||
"$brush.vertexInput.attributes[2]",
|
||||
);
|
||||
};
|
||||
inputAssembly = $brush.inputAssembly;
|
||||
layout = $brush.layout;
|
||||
};
|
||||
};
|
||||
};
|
||||
lightcull = {
|
||||
color = "[ 0.7, 0.5, 0, 1]";
|
||||
dependencies = {
|
||||
depth = $depth_dependency;
|
||||
};
|
||||
attachments = {
|
||||
depth = {
|
||||
depth = depth_stencil_attachment_optimal;
|
||||
};
|
||||
};
|
||||
|
||||
base_pipeline = {
|
||||
@inherit = $pipeline_base;
|
||||
depthStencil = $depth_test_and_write;
|
||||
rasterization = $cw_cull_back;
|
||||
};
|
||||
pipelines = {
|
||||
cull_lights = {
|
||||
@inherit = $compose_base;
|
||||
|
||||
color = $color.lights;
|
||||
tasks = (
|
||||
{ func = lighting_bind_descriptors;
|
||||
params = (debug, none); },
|
||||
{ func = lighting_draw_hulls; },
|
||||
);
|
||||
|
||||
stages = (
|
||||
$lighting.shader.vertex_splat,
|
||||
$lighting.shader.debug_fragment,
|
||||
);
|
||||
vertexInput = $lighting.vertexInput_splat;
|
||||
inputAssembly = $lighting.inputAssembly;
|
||||
layout = $lighting.splat_layout;
|
||||
rasterization = $cw_cull_back;
|
||||
depthStencil = $depth_test_only;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
occlusion_cube = {
|
||||
@inherit = $renderpasses.occlusion;
|
||||
@next = (VkRenderPassMultiviewCreateInfo, {
|
||||
viewMasks = (0x3fu, 0x3fu);
|
||||
});
|
||||
framebuffer = {
|
||||
width = "min($render_output.extent.width,$render_output.extent.height)";
|
||||
height = "min($render_output.extent.width,$render_output.extent.height)";
|
||||
layers = 1;
|
||||
attachments = {
|
||||
depth = {
|
||||
@inherit = $attachment_base;
|
||||
format = $images.cube_depth.format;
|
||||
loadOp = clear;
|
||||
finalLayout = depth_stencil_attachment_optimal;
|
||||
clearValue = { depthStencil = { depth = 0; stencil = 0; }; };
|
||||
view = occlusion_cube_depth;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
deferred = {
|
||||
color = "[0, 1, 0, 1]";
|
||||
framebuffer = {
|
||||
|
@ -2377,12 +2524,30 @@ steps = {
|
|||
params = (main); },
|
||||
{ func = scene_draw_viewmodel; },
|
||||
{ func = lighting_update_lights; },
|
||||
{ func = update_framebuffer;
|
||||
params = ("\"light_cull\""); },
|
||||
);
|
||||
};
|
||||
};
|
||||
light_cull = {
|
||||
color = "[0.8, 0.6, 0, 1]";
|
||||
dependencies = (world);
|
||||
process = {
|
||||
tasks = (
|
||||
{ func = lighting_cull_lights;
|
||||
params = ("\"light_cull\""); },
|
||||
);
|
||||
};
|
||||
render = {
|
||||
renderpasses = {
|
||||
occlusion = $renderpasses.occlusion;
|
||||
occlusion_cube = $renderpasses.occlusion_cube;
|
||||
};
|
||||
};
|
||||
};
|
||||
shadow = {
|
||||
color = "[0.3, 0.3, 0.3]";
|
||||
dependencies = (world);
|
||||
dependencies = (light_cull);
|
||||
process = {
|
||||
tasks = (
|
||||
{ func = lighting_setup_shadow; },
|
||||
|
|
|
@ -33,7 +33,10 @@ void
|
|||
main (void)
|
||||
{
|
||||
LightData l = lights[light_index];
|
||||
float sz = l.attenuation.w > 0 ? 1 / l.attenuation.w : sqrt(abs(l.color.w));
|
||||
float sz = l.attenuation.w > 0 ? 1 / l.attenuation.w
|
||||
: l.attenuation.x > 0 ? sqrt(abs(l.color.w/l.attenuation.x))
|
||||
: l.attenuation.y > 0 ? abs(l.color.w/l.attenuation.y)
|
||||
: sqrt(abs(l.color.w));
|
||||
float c = l.direction.w;
|
||||
float sxy = sz * (c < 0 ? (sqrt (1 - c*c) / -c) : 1);
|
||||
vec3 scale = vec3 (sxy, sxy, sz);
|
||||
|
|
|
@ -984,6 +984,106 @@ lighting_draw_splats (const exprval_t **params, exprval_t *result,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lighting_cull_lights (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 lctx = ctx->lighting_context;
|
||||
|
||||
auto lframe = &lctx->frames.a[ctx->curFrame];
|
||||
auto queue = lframe->light_queue;
|
||||
uint32_t count = queue[ST_CUBE].count + queue[ST_PLANE].count;
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto light_cull = QFV_GetStep (params[0], ctx->render_context->job);
|
||||
auto render = light_cull->render;
|
||||
|
||||
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,
|
||||
});
|
||||
{
|
||||
qftVkScopedZoneC (taskctx->frame->qftVkCtx, cmd, "reset", 0xc0a000);
|
||||
dfunc->vkCmdResetQueryPool (cmd, lframe->query, 0, MaxLights);
|
||||
}
|
||||
auto renderpass = &render->renderpasses[0];
|
||||
QFV_RunRenderPassCmd (cmd, ctx, renderpass, 0);
|
||||
dfunc->vkEndCommandBuffer (cmd);
|
||||
|
||||
qfMessageL ("submit");
|
||||
auto dev_queue = &device->queue;
|
||||
VkSubmitInfo submitInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd,
|
||||
};
|
||||
dfunc->vkResetFences (device->dev, 1, &lframe->fence);
|
||||
dfunc->vkQueueSubmit (dev_queue->queue, 1, &submitInfo, lframe->fence);
|
||||
dfunc->vkWaitForFences (device->dev, 1, &lframe->fence, VK_TRUE, 200000000);
|
||||
|
||||
uint32_t frag_counts[count];
|
||||
VkDeviceSize size = sizeof (frag_counts);
|
||||
dfunc->vkGetQueryPoolResults (device->dev, lframe->query, 0, count,
|
||||
size, frag_counts, sizeof (uint32_t),
|
||||
VK_QUERY_RESULT_WAIT_BIT);
|
||||
uint32_t c = 0;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
c += frag_counts[i] != 0;
|
||||
}
|
||||
if (1) printf ("%d/%d visible\n", c, count);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_hull (uint32_t indexCount, uint32_t firstIndex, int32_t vertOffset,
|
||||
uint32_t hull, uint32_t id, VkCommandBuffer cmd, VkQueryPool query,
|
||||
qfv_devfuncs_t *dfunc, qftVkCtx_t *vk)
|
||||
{
|
||||
qfZoneNamed (zone, true);
|
||||
qftVkScopedZoneC (vk, cmd, "draw_hull", 0xc0a000);
|
||||
dfunc->vkCmdBeginQuery (cmd, query, id, 0);
|
||||
dfunc->vkCmdDrawIndexed (cmd, indexCount, 1, firstIndex, vertOffset, hull);
|
||||
dfunc->vkCmdEndQuery (cmd, query, id);
|
||||
}
|
||||
|
||||
static void
|
||||
lighting_draw_hulls (const exprval_t **params, exprval_t *result,
|
||||
exprctx_t *ectx)
|
||||
{
|
||||
qfZoneNamed (zone, true);
|
||||
auto taskctx = (qfv_taskctx_t *) ectx;
|
||||
auto ctx = taskctx->ctx;
|
||||
auto device = ctx->device;
|
||||
auto dfunc = device->funcs;
|
||||
auto lctx = ctx->lighting_context;
|
||||
auto cmd = taskctx->cmd;
|
||||
|
||||
auto lframe = &lctx->frames.a[ctx->curFrame];
|
||||
uint32_t id = 0;
|
||||
if (lframe->light_queue[ST_CUBE].count) {
|
||||
auto q = lframe->light_queue[ST_CUBE];
|
||||
for (uint32_t i = 0; i < q.count; i++) {
|
||||
uint32_t hull = q.start + i;
|
||||
draw_hull (num_ico_inds, 0, 0, hull, id++,
|
||||
cmd, lframe->query, dfunc, taskctx->frame->qftVkCtx);
|
||||
}
|
||||
}
|
||||
if (lframe->light_queue[ST_PLANE].count) {
|
||||
auto q = lframe->light_queue[ST_PLANE];
|
||||
for (uint32_t i = 0; i < q.count; i++) {
|
||||
uint32_t hull = q.start + i;
|
||||
draw_hull (num_cone_inds, num_ico_inds, 12, hull, id++,
|
||||
cmd, lframe->query, dfunc, taskctx->frame->qftVkCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lighting_draw_lights (const exprval_t **params, exprval_t *result,
|
||||
exprctx_t *ectx)
|
||||
|
@ -1090,6 +1190,15 @@ static exprfunc_t lighting_draw_splats_func[] = {
|
|||
{ .func = lighting_draw_splats },
|
||||
{}
|
||||
};
|
||||
static exprfunc_t lighting_cull_lights_func[] = {
|
||||
{ .func = lighting_cull_lights, .num_params = 1,
|
||||
.param_types = stepref_param },
|
||||
{}
|
||||
};
|
||||
static exprfunc_t lighting_draw_hulls_func[] = {
|
||||
{ .func = lighting_draw_hulls },
|
||||
{}
|
||||
};
|
||||
static exprfunc_t lighting_draw_lights_func[] = {
|
||||
{ .func = lighting_draw_lights, .num_params = 2,
|
||||
.param_types = shadow_type_param },
|
||||
|
@ -1111,6 +1220,8 @@ static exprsym_t lighting_task_syms[] = {
|
|||
{ "lighting_bind_descriptors", &cexpr_function,
|
||||
lighting_bind_descriptors_func },
|
||||
{ "lighting_draw_splats", &cexpr_function, lighting_draw_splats_func },
|
||||
{ "lighting_cull_lights", &cexpr_function, lighting_cull_lights_func },
|
||||
{ "lighting_draw_hulls", &cexpr_function, lighting_draw_hulls_func },
|
||||
{ "lighting_draw_lights", &cexpr_function, lighting_draw_lights_func },
|
||||
{ "lighting_setup_shadow", &cexpr_function, lighting_setup_shadow_func },
|
||||
{ "lighting_draw_shadow_maps", &cexpr_function,
|
||||
|
@ -1554,6 +1665,13 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
|
|||
.pBufferInfo = &bufferInfo[6], },
|
||||
};
|
||||
dfunc->vkUpdateDescriptorSets (device->dev, 7, bufferWrite, 0, 0);
|
||||
|
||||
dfunc->vkCreateQueryPool (device->dev, &(VkQueryPoolCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
.queryType = VK_QUERY_TYPE_OCCLUSION,
|
||||
.queryCount = MaxLights,
|
||||
}, 0, &lframe->query);
|
||||
lframe->fence = QFV_CreateFence (device, 1);
|
||||
}
|
||||
size_t target_count = MaxLights * 6;
|
||||
size_t target_size = frames * sizeof (uint16_t[target_count]);
|
||||
|
@ -1618,14 +1736,20 @@ clear_shadows (vulkan_ctx_t *ctx)
|
|||
void
|
||||
Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx)
|
||||
{
|
||||
qfv_device_t *device = ctx->device;
|
||||
lightingctx_t *lctx = ctx->lighting_context;
|
||||
auto device = ctx->device;
|
||||
auto dfunc = device->funcs;
|
||||
auto lctx = ctx->lighting_context;
|
||||
|
||||
clear_shadows (ctx);
|
||||
|
||||
QFV_DestroyResource (device, lctx->light_resources);
|
||||
free (lctx->light_resources);
|
||||
|
||||
for (size_t i = 0; i < lctx->frames.size; i++) {
|
||||
auto lframe = &lctx->frames.a[i];
|
||||
dfunc->vkDestroyQueryPool (device->dev, lframe->query, 0);
|
||||
dfunc->vkDestroyFence (device->dev, lframe->fence, 0);
|
||||
}
|
||||
free (lctx->frames.a[0].stage_targets);
|
||||
DARRAY_CLEAR (&lctx->light_mats);
|
||||
DARRAY_CLEAR (&lctx->light_control);
|
||||
|
|
Loading…
Reference in a new issue