[vulkan] Hook up all the shadow resources

This gets everything but the actual shadow map bindings working: the
validation layers don't like my type punning (which may well be the
right thing) and specialization constants don't help (yet, anyway) but I
want to get things into git.
This commit is contained in:
Bill Currie 2023-08-01 23:34:08 +09:00
parent 7487a00b36
commit 00040c8900
9 changed files with 511 additions and 160 deletions

View file

@ -49,10 +49,17 @@ typedef struct qfv_lightmatset_s DARRAY_TYPE (mat4f_t) qfv_lightmatset_t;
#define ST_CASCADE 2 // cascaded shadow maps
#define ST_CUBE 3 // cubemap (omni, large spotlight)
typedef struct qfv_light_buffer_s {
light_t lights[MaxLights] __attribute__((aligned(16)));
int lightCount;
} qfv_light_buffer_t;
typedef struct qfv_lightid_buffer_s {
uint32_t lightCount;
uint32_t lightIds[MaxLights];
} qfv_lightid_buffer_t;
typedef struct qfv_light_render_s {
// mat_id (13) map_id (5) layer (11) type (2)
uint32_t id_data;
// light style (6)
uint32_t style;
} qfv_light_render_t;
#define LIGHTING_BUFFER_INFOS 1
#define LIGHTING_ATTACH_INFOS 4
@ -69,6 +76,8 @@ typedef struct lightingframe_s {
VkBuffer shadowmat_buffer;
VkBuffer light_buffer;
VkBuffer render_buffer;
VkBuffer style_buffer;
VkBuffer id_buffer;
uint32_t ico_count;
uint32_t cone_count;
@ -81,18 +90,20 @@ typedef struct lightingframe_s {
typedef struct lightingframeset_s
DARRAY_TYPE (lightingframe_t) lightingframeset_t;
typedef struct light_renderer_s {
typedef struct light_control_s {
uint8_t renderpass_index;
uint8_t image_index;
uint8_t map_index;
uint16_t size;
uint16_t layer;
uint8_t numLayers;
uint8_t mode;
uint16_t matrix_base;
} light_renderer_t;
uint16_t light_id;
uint16_t matrix_base;// for rendering maps
uint16_t matrix_id; // for rendering shadows
} light_control_t;
typedef struct light_renderer_set_s
DARRAY_TYPE (light_renderer_t) light_renderer_set_t;
typedef struct light_control_set_s
DARRAY_TYPE (light_control_t) light_control_set_t;
typedef struct lightingctx_s {
@ -103,14 +114,21 @@ typedef struct lightingctx_s {
qfv_lightmatset_t light_mats;
qfv_imageset_t light_images;
qfv_imageviewset_t light_views;
light_renderer_set_t light_renderers;
light_control_set_t light_control;
qfv_attachmentinfo_t shadow_info;
VkSampler shadow_sampler;
VkDescriptorSet shadow_set;
VkBuffer splat_verts;
VkBuffer splat_inds;
uint32_t dynamic_base;
uint32_t dynamic_matrix_base;
uint32_t dynamic_count;
struct lightingdata_s *ldata;
struct scene_s *scene;
} lightingctx_t;

View file

@ -155,6 +155,7 @@ QFV_CreateDevice (vulkan_ctx_t *ctx, const char **extensions)
.multiviewGeometryShader = 1,
};
VkPhysicalDeviceFeatures features = {
.imageCubeArray = 1,
.independentBlend = 1,
.geometryShader = 1,
.multiViewport = 1,

View file

@ -618,10 +618,51 @@ properties = {
name = main;
module = $builtin/light_splat.frag;
};
fragment = {
shadow_specialization = {
mapEntries = (
// ShadowType
{ size = 4; offset = 0; constantID = 0; },
);
};
fragment_none = {
stage = fragment;
name = main;
module = $builtin/lighting.frag;
specializationInfo = {
@inherit = $lighting.shader.shadow_specialization;
// ShadowType
data = "array(0)";
};
};
fragment_plane = {
stage = fragment;
name = main;
module = $builtin/lighting.frag;
specializationInfo = {
@inherit = $lighting.shader.shadow_specialization;
// ShadowType
data = "array(1)";
};
};
fragment_cascade = {
stage = fragment;
name = main;
module = $builtin/lighting.frag;
specializationInfo = {
@inherit = $lighting.shader.shadow_specialization;
// ShadowType
data = "array(2)";
};
};
fragment_cube = {
stage = fragment;
name = main;
module = $builtin/lighting.frag;
specializationInfo = {
@inherit = $lighting.shader.shadow_specialization;
// ShadowType
data = "array(3)";
};
};
debug_fragment = {
stage = fragment;
@ -651,8 +692,11 @@ properties = {
topology = triangle_fan;
primitiveRestartEnable = true;
};
splat_layout = {
descriptorSets = (matrix_set, lighting_lights);
};
layout = {
descriptorSets = (matrix_set, lighting_lights,
descriptorSets = (shadowmat_set, lighting_lights,
lighting_attach, lighting_shadow);
};
};
@ -929,10 +973,28 @@ descriptorSetLayouts = {
bindings = (
{
binding = 0;
descriptorType = uniform_buffer;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = vertex|fragment;
},
{
binding = 1;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = vertex|fragment;
},
{
binding = 2;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = fragment;
},
{
binding = 3;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = fragment;
},
);
};
lighting_shadow = {
@ -1520,19 +1582,71 @@ renderpasses = {
preserve = (depth, output);
};
pipelines = {
lights = {
lights_none = {
@inherit = $compose_base;
color = $color.lights;
tasks = (
{ func = lighting_update_descriptors; },
{ func = lighting_bind_descriptors; },
{ func = lighting_draw_lights; },
{ func = lighting_bind_descriptors;
params = (none); },
{ func = lighting_draw_lights;
params = (none); },
);
stages = (
$fstriangle.shader.vertex,
$lighting.shader.fragment,
$lighting.shader.fragment_none,
);
layout = $lighting.layout;
};
lights_plane = {
@inherit = $compose_base;
color = $color.lights;
tasks = (
{ func = lighting_bind_descriptors;
params = (plane); },
{ func = lighting_draw_lights;
params = (plane); },
);
stages = (
$fstriangle.shader.vertex,
$lighting.shader.fragment_plane,
);
layout = $lighting.layout;
};
lights_cascade = {
@inherit = $compose_base;
color = $color.lights;
tasks = (
{ func = lighting_bind_descriptors;
params = (cascade); },
{ func = lighting_draw_lights;
params = (cascade); },
);
stages = (
$fstriangle.shader.vertex,
$lighting.shader.fragment_cascade,
);
layout = $lighting.layout;
};
lights_cube = {
@inherit = $compose_base;
color = $color.lights;
tasks = (
{ func = lighting_bind_descriptors;
params = (cube); },
{ func = lighting_draw_lights;
params = (cube); },
);
stages = (
$fstriangle.shader.vertex,
$lighting.shader.fragment_cube,
);
layout = $lighting.layout;
};
@ -1623,7 +1737,7 @@ renderpasses = {
);
vertexInput = $lighting.vertexInput_splat;
inputAssembly = $lighting.inputAssembly;
layout = $lighting.layout;
layout = $lighting.splat_layout;
rasterization = $debug_poly_lines;
depthStencil = $depth_disable;
};
@ -1993,6 +2107,8 @@ steps = {
{ func = clear_translucent;
params = ("\"main\""); },
{ func = particle_wait_physics; },
{ func = lighting_update_descriptors;
params = ("\"main\""); },
);
};
};

View file

@ -8,9 +8,9 @@ layout (input_attachment_index = 1, set = 2, binding = 1) uniform subpassInput e
layout (input_attachment_index = 2, set = 2, binding = 2) uniform subpassInput normal;
layout (input_attachment_index = 3, set = 2, binding = 3) uniform subpassInput position;
layout (set = 3, binding = 0) uniform sampler2DArrayShadow shadowCascade[MaxLights];
layout (set = 3, binding = 0) uniform sampler2DShadow shadowPlane[MaxLights];
layout (set = 3, binding = 0) uniform samplerCubeShadow shadowCube[MaxLights];
layout (set = 3, binding = 0) uniform sampler2DArrayShadow shadowCascade[32];
layout (set = 3, binding = 0) uniform sampler2DShadow shadowPlane[32];
layout (set = 3, binding = 0) uniform samplerCubeShadow shadowCube[32];
layout (location = 0) flat in uint light_index;
layout (location = 0) out vec4 frag_color;

View file

@ -8,12 +8,14 @@ layout (input_attachment_index = 1, set = 2, binding = 1) uniform subpassInput e
layout (input_attachment_index = 2, set = 2, binding = 2) uniform subpassInput normal;
layout (input_attachment_index = 3, set = 2, binding = 3) uniform subpassInput position;
layout (set = 3, binding = 0) uniform sampler2DArrayShadow shadowCascade[MaxLights];
layout (set = 3, binding = 0) uniform sampler2DShadow shadowPlane[MaxLights];
layout (set = 3, binding = 0) uniform samplerCubeShadow shadowCube[MaxLights];
layout (set = 3, binding = 0) uniform sampler2DArrayShadow shadowCascade[32];
layout (set = 3, binding = 0) uniform sampler2DArrayShadow shadowPlane[32];
layout (set = 3, binding = 0) uniform samplerCubeArrayShadow shadowCube[32];
layout (location = 0) out vec4 frag_color;
layout (constant_id = 0) const int ShadowType = ST_NONE;
float
spot_cone (LightData light, vec3 incoming)
{
@ -31,19 +33,19 @@ diffuse (vec3 incoming, vec3 normal)
}
float
shadow_cascade (sampler2DArrayShadow map)
shadow_cascade (sampler2DArrayShadow map, uint layer, uint mat_id, vec3 pos)
{
return 1;
}
float
shadow_plane (sampler2DShadow map)
shadow_plane (sampler2DArrayShadow map, uint layer, uint mat_id, vec3 pos)
{
return 1;
}
float
shadow_cube (samplerCubeShadow map)
shadow_cube (samplerCubeArrayShadow map, uint layer, uint mat_id, vec3 pos)
{
return 1;
}
@ -59,7 +61,8 @@ main (void)
//vec3 minLight = vec3 (0);
for (int i = 0; i < lightCount; i++) {
LightData l = lights[i];
uint id = lightIds[i];
LightData l = lights[id];
vec3 dir = l.position.xyz - l.position.w * p;
float r2 = dot (dir, dir);
vec4 a = l.attenuation;
@ -71,19 +74,26 @@ main (void)
vec3 incoming = dir / r.y;
float I = (1 - a.w * r.y) / dot (a, r);
/*int shadow = lights[i].data & ShadowMask;
if (shadow == ST_CASCADE) {
I *= shadow_cascade (shadowCascade[i]);
} else if (shadow == ST_PLANE) {
I *= shadow_plane (shadowPlane[i]);
} else if (shadow == ST_CUBE) {
I *= shadow_cube (shadowCube[i]);
}*/
uint id_data = renderer[id].id_data;
uint mat_id = bitfieldExtract (id_data, 0, 13);
uint map_id = bitfieldExtract (id_data, 13, 5);
uint layer = bitfieldExtract (id_data, 18, 11);
if (ShadowType == ST_CASCADE) {
I *= shadow_cascade (shadowCascade[map_id], layer, mat_id, p);
} else if (ShadowType == ST_PLANE) {
I *= shadow_plane (shadowPlane[map_id], layer, mat_id, p);
} else if (ShadowType == ST_CUBE) {
I *= shadow_cube (shadowCube[map_id], layer, mat_id, p);
}
float namb = dot(l.direction.xyz, l.direction.xyz);
I *= spot_cone (l, incoming) * diffuse (incoming, n);
I = mix (1, I, namb);
light += I * l.color.w * l.color.xyz;
vec4 col = l.color;
if (bitfieldExtract(id_data, 31, 1) == 0) {
col *= style[renderer[id].style];
}
light += I * col.w * col.xyz;
}
//light = max (light, minLight);

View file

@ -5,9 +5,26 @@ struct LightData {
vec4 attenuation;
};
layout (constant_id = 0) const int MaxLights = 768;
#define ST_NONE 0 // no shadows
#define ST_PLANE 1 // single plane shadow map (small spotlight)
#define ST_CASCADE 2 // cascaded shadow maps
#define ST_CUBE 3 // cubemap (omni, large spotlight)
layout (set = 1, binding = 0) uniform Lights {
LightData lights[MaxLights];
int lightCount;
struct LightRender {
uint id_data;
uint style;
};
layout (set = 1, binding = 0) buffer LightIds {
uint lightCount;
uint lightIds[];
};
layout (set = 1, binding = 1) buffer Lights {
LightData lights[];
};
layout (set = 1, binding = 2) buffer Renderer {
LightRender renderer[];
};
layout (set = 1, binding = 3) buffer Style {
vec4 style[];
};

View file

@ -1,25 +0,0 @@
#version 450
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
layout (constant_id = 0) const int MaxLights = 128;
layout (set = 0, binding = 0) uniform UBO {
mat4 vp[MaxLights];
} ubo;
layout (location = 0) in int InstanceIndex[];
void
main (void)
{
int index = InstanceIndex[0];
for (int i = 0; i < gl_in.length(); i++) {
gl_Layer = index;
gl_Position = ubo.vp[index] * gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}

View file

@ -112,10 +112,10 @@ samplers = {
anisotropyEnable = false;
maxAnisotropy = 0;
compareEnable = true;
compareOp = greater_or_equal;
compareOp = less;
minLod = 0;
maxLod = 1000;
borderColor = float_transparent_black;
borderColor = float_opaque_white;
unnormalizedCoordinates = false;
};
linear = {

View file

@ -101,10 +101,10 @@ get_light (entity_t ent)
return Ent_GetComponent (ent.id, scene_light, ent.reg);
}
static int
static uint32_t
get_lightstyle (entity_t ent)
{
return *(int *) Ent_GetComponent (ent.id, scene_lightstyle, ent.reg);
return *(uint32_t *) Ent_GetComponent (ent.id, scene_lightstyle, ent.reg);
}
static uint32_t
@ -180,7 +180,7 @@ lighting_setup_aux (const exprval_t **params, exprval_t *result,
}
static VkImageView
create_view (vulkan_ctx_t *ctx, light_renderer_t *renderer)
create_view (vulkan_ctx_t *ctx, light_control_t *renderer)
{
auto device = ctx->device;
auto dfunc = device->funcs;
@ -188,7 +188,7 @@ create_view (vulkan_ctx_t *ctx, light_renderer_t *renderer)
VkImageViewCreateInfo cInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = lctx->light_images.a[renderer->image_index],
.image = lctx->light_images.a[renderer->map_index],
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
.format = VK_FORMAT_X8_D24_UNORM_PACK32,
.subresourceRange = {
@ -204,7 +204,7 @@ create_view (vulkan_ctx_t *ctx, light_renderer_t *renderer)
}
static VkFramebuffer
create_framebuffer (vulkan_ctx_t *ctx, light_renderer_t *renderer,
create_framebuffer (vulkan_ctx_t *ctx, light_control_t *renderer,
VkImageView view, VkRenderPass renderpass)
{
auto device = ctx->device;
@ -264,7 +264,7 @@ lighting_draw_shadow_maps (const exprval_t **params, exprval_t *result,
entity_t ent = queue->ent_queues[mod_light].a[i];
auto ls = get_lightstyle (ent);
uint32_t id = get_lightid (ent);
auto r = &lctx->light_renderers.a[id];
auto r = &lctx->light_control.a[id];
if (!r->numLayers || !d_lightstylevalue[ls]) {
continue;
}
@ -297,39 +297,64 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
return;
}
dlight_t *lights[MaxLights];
auto packet = QFV_PacketAcquire (ctx->staging);
qfv_light_buffer_t *light_data = QFV_PacketExtend (packet,
sizeof (*light_data));
auto bb = &bufferBarriers[qfv_BB_TransferWrite_to_UniformRead];
float style_intensities[NumStyles];
auto packet = QFV_PacketAcquire (ctx->staging);
vec4f_t *styles = QFV_PacketExtend (packet, sizeof (vec4f_t[NumStyles]));
for (int i = 0; i < NumStyles; i++) {
style_intensities[i] = d_lightstylevalue[i] / 65536.0;
styles[i] = (vec4f_t) { 1, 1, 1, d_lightstylevalue[i] / 65536.0};
}
QFV_PacketCopyBuffer (packet, lframe->style_buffer, 0, bb);
QFV_PacketSubmit (packet);
uint32_t ico_ids[MaxLights];
uint32_t cone_ids[MaxLights];
uint32_t flat_ids[MaxLights];
light_data->lightCount = 0;
R_FindNearLights (r_refdef.frame.position, MaxLights - 1, lights);
for (int i = 0; i < MaxLights - 1; i++) {
if (!lights[i]) {
break;
}
ico_ids[lframe->ico_count++] = light_data->lightCount++;
uint32_t light_count = 0;
VectorCopy (lights[i]->color, light_data->lights[i].color);
// dynamic lights seem a tad faint, so 16x map lights
light_data->lights[i].color[3] = lights[i]->radius / 16;
VectorCopy (lights[i]->origin, light_data->lights[i].position);
// dlights are local point sources
light_data->lights[i].position[3] = 1;
light_data->lights[i].attenuation =
(vec4f_t) { 0, 0, 1, 1/lights[i]->radius };
// full sphere, normal light (not ambient)
light_data->lights[i].direction = (vec4f_t) { 0, 0, 1, 1 };
dlight_t *dynamic_lights[MaxLights];
int maxdlight = MaxLights - lctx->dynamic_base;
int ndlight = R_FindNearLights (r_refdef.frame.position, maxdlight,
dynamic_lights);
if (ndlight) {
packet = QFV_PacketAcquire (ctx->staging);
light_t *lights = QFV_PacketExtend (packet, sizeof (light_t[ndlight]));
for (int i = 0; i < ndlight; i++) {
ico_ids[lframe->ico_count++] = lctx->dynamic_base + light_count++;
VectorCopy (dynamic_lights[i]->color, lights[i].color);
// dynamic lights seem a tad faint, so 16x map lights
lights[i].color[3] = dynamic_lights[i]->radius / 16;
VectorCopy (dynamic_lights[i]->origin, lights[i].position);
// dlights are local point sources
lights[i].position[3] = 1;
lights[i].attenuation =
(vec4f_t) { 0, 0, 1, 1/dynamic_lights[i]->radius };
// full sphere, normal light (not ambient)
lights[i].direction = (vec4f_t) { 0, 0, 1, 1 };
}
VkDeviceSize dlight_offset = sizeof (light_t[lctx->dynamic_base]);
QFV_PacketCopyBuffer (packet, lframe->light_buffer, dlight_offset, bb);
QFV_PacketSubmit (packet);
packet = QFV_PacketAcquire (ctx->staging);
uint32_t r_size = sizeof (qfv_light_render_t[ndlight]);
qfv_light_render_t *render = QFV_PacketExtend (packet, r_size);
for (int i = 0; i < ndlight; i++) {
render[i] = (qfv_light_render_t) {
//.id_data = make_id(r->matrix_id, r->map_index, r->layer,
//r->mode),
.id_data = 0x80000000, // no style
.style = 11,
};
}
dlight_offset = sizeof (qfv_light_render_t[lctx->dynamic_base]);
QFV_PacketCopyBuffer (packet, lframe->render_buffer, dlight_offset, bb);
QFV_PacketSubmit (packet);
}
auto queue = r_ent_queue; //FIXME fetch from scene
for (size_t i = 0; i < queue->ent_queues[mod_light].size; i++) {
entity_t ent = queue->ent_queues[mod_light].a[i];
@ -339,13 +364,11 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
continue;
}
uint32_t id = light_data->lightCount++;
auto light = &light_data->lights[id];
*light = *l;
light->color[3] *= style_intensities[ls];
if (light->position[3] && !VectorIsZero (light->direction)
&& light->attenuation[3]) {
if (light->direction[3] < 0) {
light_count++;
uint32_t id = lctx->light_control.a[get_lightid (ent)].light_id;
if (l->position[3] && !VectorIsZero (l->direction)
&& l->attenuation[3]) {
if (l->direction[3] < 0) {
cone_ids[lframe->cone_count++] = id;
} else {
ico_ids[lframe->ico_count++] = id;
@ -356,18 +379,20 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
}
if (developer & SYS_lighting) {
Vulkan_Draw_String (vid.width - 32, 8,
va (ctx->va_ctx, "%3d", light_data->lightCount),
va (ctx->va_ctx, "%3d", light_count),
ctx);
}
QFV_PacketCopyBuffer (packet, lframe->light_buffer, 0,
&bufferBarriers[qfv_BB_TransferWrite_to_UniformRead]);
QFV_PacketSubmit (packet);
uint32_t id_count = lframe->ico_count + lframe->cone_count
+ lframe->flat_count;
if (id_count != light_count) {
Sys_Error ("taniwha can't count: %d != %d", id_count, light_count);
}
if (id_count) {
packet = QFV_PacketAcquire (ctx->staging);
uint32_t *ids = QFV_PacketExtend (packet, id_count * sizeof (uint32_t));
uint32_t *count = QFV_PacketExtend (packet, sizeof (uint32_t));
*count = id_count;
uint32_t *ids = QFV_PacketExtend (packet, sizeof (uint32_t[id_count]));
memcpy (ids, ico_ids, lframe->ico_count * sizeof (uint32_t));
ids += lframe->ico_count;
memcpy (ids, cone_ids, lframe->cone_count * sizeof (uint32_t));
@ -391,7 +416,10 @@ lighting_update_descriptors (const exprval_t **params, exprval_t *result,
auto lframe = &lctx->frames.a[ctx->curFrame];
auto fb = &taskctx->renderpass->framebuffer;
auto job = ctx->render_context->job;
auto step = QFV_GetStep (params[0], job);
auto render = step->render;
auto fb = &render->active->framebuffer;
VkDescriptorImageInfo attachInfo[] = {
{ .imageView = fb->views[QFV_attachColor],
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
@ -428,7 +456,6 @@ lighting_update_descriptors (const exprval_t **params, exprval_t *result,
.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
.pImageInfo = &attachInfo[3], },
};
//lframe->bufferInfo[0].buffer = lframe->light_buffer;
dfunc->vkUpdateDescriptorSets (device->dev,
LIGHTING_ATTACH_INFOS, attachWrite,
0, 0);
@ -449,18 +476,20 @@ lighting_bind_descriptors (const exprval_t **params, exprval_t *result,
auto lframe = &lctx->frames.a[ctx->curFrame];
VkDescriptorSet sets[] = {
Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
//Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
lframe->shadowmat_set,
lframe->lights_set,
lframe->attach_set,
lctx->shadow_set,
};
dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
layout, 0, 3, sets, 0, 0);
layout, 0, 4, sets, 0, 0);
if (1) {
VkBuffer buffers[] = {
lframe->id_buffer,
lctx->splat_verts,
};
VkDeviceSize offsets[] = { 0, 0 };
VkDeviceSize offsets[] = { sizeof (uint32_t), 0 };
dfunc->vkCmdBindVertexBuffers (cmd, 0, 2, buffers, offsets);
dfunc->vkCmdBindIndexBuffer (cmd, lctx->splat_inds, 0,
VK_INDEX_TYPE_UINT32);
@ -527,6 +556,31 @@ lighting_draw_lights (const exprval_t **params, exprval_t *result,
dfunc->vkCmdDraw (cmd, 3, 1, 0, 0);
}
static exprenum_t shadow_type_enum;
static exprtype_t shadow_type_type = {
.name = "shadow_type",
.size = sizeof (int),
.get_string = cexpr_enum_get_string,
.data = &shadow_type_enum,
};
static int shadow_type_values[] = { ST_NONE, ST_PLANE, ST_CASCADE, ST_CUBE };
static exprsym_t shadow_type_symbols[] = {
{"none", &shadow_type_type, shadow_type_values + 0},
{"plane", &shadow_type_type, shadow_type_values + 1},
{"cascade", &shadow_type_type, shadow_type_values + 2},
{"cube", &shadow_type_type, shadow_type_values + 3},
{}
};
static exprtab_t shadow_type_symtab = { .symbols = shadow_type_symbols };
static exprenum_t shadow_type_enum = {
&shadow_type_type,
&shadow_type_symtab,
};
static exprtype_t *shadow_type_param[] = {
&shadow_type_type,
};
static exprtype_t *stepref_param[] = {
&cexpr_string,
};
@ -536,11 +590,13 @@ static exprfunc_t lighting_update_lights_func[] = {
{}
};
static exprfunc_t lighting_update_descriptors_func[] = {
{ .func = lighting_update_descriptors },
{ .func = lighting_update_descriptors, .num_params = 1,
.param_types = stepref_param },
{}
};
static exprfunc_t lighting_bind_descriptors_func[] = {
{ .func = lighting_bind_descriptors },
{ .func = lighting_bind_descriptors, .num_params = 1,
.param_types = shadow_type_param },
{}
};
static exprfunc_t lighting_draw_splats_func[] = {
@ -552,7 +608,8 @@ static exprfunc_t lighting_draw_flats_func[] = {
{}
};
static exprfunc_t lighting_draw_lights_func[] = {
{ .func = lighting_draw_lights },
{ .func = lighting_draw_lights, .num_params = 1,
.param_types = shadow_type_param },
{}
};
static exprfunc_t lighting_setup_aux_func[] = {
@ -676,7 +733,8 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
DARRAY_INIT (&lctx->light_mats, 16);
DARRAY_INIT (&lctx->light_images, 16);
DARRAY_INIT (&lctx->light_renderers, 16);
DARRAY_INIT (&lctx->light_views, 16);
DARRAY_INIT (&lctx->light_control, 16);
auto rctx = ctx->render_context;
size_t frames = rctx->frames.size;
@ -693,7 +751,9 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
+ sizeof (qfv_resobj_t[frames])
// light data
+ sizeof (qfv_resobj_t[frames])
// light matrix ids
// light render
+ sizeof (qfv_resobj_t[frames])
// light styles
+ sizeof (qfv_resobj_t[frames])
// light matrices
+ sizeof (qfv_resobj_t[frames]));
@ -701,13 +761,14 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
auto splat_inds = &splat_verts[1];
auto light_ids = &splat_inds[1];
auto light_data = &light_ids[frames];
auto light_matids = &light_data[frames];
auto light_mats = &light_matids[frames];
auto light_render = &light_data[frames];
auto light_styles = &light_render[frames];
auto light_mats = &light_styles[frames];
lctx->light_resources[0] = (qfv_resource_t) {
.name = "lights",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = 2 + 4 * frames,
.num_objects = 2 + 5 * frames,
.objects = splat_verts,
};
splat_verts[0] = (qfv_resobj_t) {
@ -729,20 +790,39 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
},
};
for (size_t i = 0; i < frames; i++) {
light_ids[i] = (qfv_resobj_t) {
.name = "ids",
.type = qfv_res_buffer,
.buffer = {
.size = 2 * MaxLights * sizeof (uint32_t),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
};
light_data[i] = (qfv_resobj_t) {
.name = "lights",
.type = qfv_res_buffer,
.buffer = {
.size = sizeof (qfv_light_buffer_t),
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
.size = sizeof (light_t[MaxLights]),
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
};
light_matids[i] = (qfv_resobj_t) {
.name = "matrixids",
light_render[i] = (qfv_resobj_t) {
.name = "render",
.type = qfv_res_buffer,
.buffer = {
.size = sizeof (uint32_t[MaxLights]),
.size = sizeof (qfv_light_render_t[MaxLights]),
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
};
light_styles[i] = (qfv_resobj_t) {
.name = "styles",
.type = qfv_res_buffer,
.buffer = {
.size = sizeof (vec4f_t[NumStyles]),
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
@ -757,15 +837,6 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
};
light_ids[i] = (qfv_resobj_t) {
.name = "ids",
.type = qfv_res_buffer,
.buffer = {
.size = 2 * MaxLights * sizeof (uint32_t),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
},
};
}
QFV_CreateResource (device, lctx->light_resources);
@ -773,6 +844,12 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
lctx->splat_verts = splat_verts[0].buffer.buffer;
lctx->splat_inds = splat_inds[0].buffer.buffer;
auto shadow_mgr = QFV_Render_DSManager (ctx, "lighting_shadow");
lctx->shadow_set = QFV_DSManager_AllocSet (shadow_mgr);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET,
lctx->shadow_set, "lighting:shadow_set");
lctx->shadow_sampler = QFV_Render_Sampler (ctx, "shadow_sampler");
auto attach_mgr = QFV_Render_DSManager (ctx, "lighting_attach");
auto lights_mgr = QFV_Render_DSManager (ctx, "lighting_lights");
auto shadowmat_mgr = QFV_Render_DSManager (ctx, "shadowmat_set");
@ -784,6 +861,8 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
.attach_set = QFV_DSManager_AllocSet (attach_mgr),
.shadowmat_buffer = light_mats[i].buffer.buffer,
.light_buffer = light_data[i].buffer.buffer,
.render_buffer = light_render[i].buffer.buffer,
.style_buffer = light_styles[i].buffer.buffer,
.id_buffer = light_ids[i].buffer.buffer,
};
@ -801,26 +880,51 @@ Vulkan_Lighting_Setup (vulkan_ctx_t *ctx)
lframe->framebuffers = (qfv_framebufferset_t) DARRAY_STATIC_INIT (16);
VkDescriptorBufferInfo bufferInfo[] = {
{ .buffer = lframe->shadowmat_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
{ .buffer = lframe->id_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
{ .buffer = lframe->light_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
{ .buffer = lframe->shadowmat_buffer,
{ .buffer = lframe->render_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
{ .buffer = lframe->style_buffer,
.offset = 0, .range = VK_WHOLE_SIZE, },
};
VkWriteDescriptorSet bufferWrite[] = {
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->lights_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &bufferInfo[0], },
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->shadowmat_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[0], },
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->lights_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[1], },
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->lights_set,
.dstBinding = 1,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[2], },
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->lights_set,
.dstBinding = 2,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[3], },
{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lframe->lights_set,
.dstBinding = 3,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferInfo[4], },
};
dfunc->vkUpdateDescriptorSets (device->dev, 2, bufferWrite, 0, 0);
dfunc->vkUpdateDescriptorSets (device->dev, 5, bufferWrite, 0, 0);
}
auto packet = QFV_PacketAcquire (ctx->staging);
@ -850,7 +954,8 @@ clear_shadows (vulkan_ctx_t *ctx)
lctx->shadow_resources = 0;
}
lctx->light_images.size = 0;
lctx->light_renderers.size = 0;
lctx->light_views.size = 0;
lctx->light_control.size = 0;
}
void
@ -873,7 +978,8 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx)
DARRAY_CLEAR (&lctx->light_mats);
DARRAY_CLEAR (&lctx->light_images);
DARRAY_CLEAR (&lctx->light_renderers);
DARRAY_CLEAR (&lctx->light_views);
DARRAY_CLEAR (&lctx->light_control);
free (lctx->frames.a);
free (lctx);
}
@ -890,16 +996,17 @@ create_light_matrices (lightingctx_t *lctx)
for (uint32_t i = 0; i < light_pool->count; i++) {
entity_t ent = { .reg = reg, .id = light_pool->dense[i] };
uint32_t id = get_lightid (ent);
auto r = &lctx->light_renderers.a[id];
auto r = &lctx->light_control.a[id];
r->matrix_base = mat_count;
mat_count += r->numLayers;
}
DARRAY_RESIZE (&lctx->light_mats, mat_count);
lctx->dynamic_matrix_base = mat_count;
for (uint32_t i = 0; i < light_pool->count; i++) {
light_t *light = &light_data[i];
entity_t ent = { .reg = reg, .id = light_pool->dense[i] };
uint32_t id = get_lightid (ent);
auto r = &lctx->light_renderers.a[id];
auto r = &lctx->light_control.a[id];
auto lm = &lctx->light_mats.a[r->matrix_base];
mat4f_t view;
mat4f_t proj;
@ -936,6 +1043,7 @@ create_light_matrices (lightingctx_t *lctx)
mmulf (side_view, qfv_z_up, side_view);
mmulf (lm[j], proj, side_view);
}
r->matrix_id = r->matrix_base + BOX_FRONT;
break;
case ST_CASCADE:
// dependent on view fustrum and cascade level
@ -944,11 +1052,13 @@ create_light_matrices (lightingctx_t *lctx)
for (int j = 0; j < 4; j++) {
mmulf (lm[j], proj, view);
}
r->matrix_id = r->matrix_base;
break;
case ST_PLANE:
QFV_PerspectiveCos (proj, -light->direction[3]);
mmulf (view, qfv_z_up, view);
mmulf (lm[0], proj, view);
r->matrix_id = r->matrix_base;
break;
}
}
@ -969,6 +1079,56 @@ upload_light_matrices (lightingctx_t *lctx, vulkan_ctx_t *ctx)
QFV_PacketSubmit (packet);
}
static uint32_t
make_id (uint32_t matrix_index, uint32_t map_index, uint32_t layer,
uint32_t type)
{
return ((matrix_index & 0x1fff) << 0)
| ((map_index & 0x1f) << 13)
| ((layer & 0x7ff) << 18)
| ((type & 3) << 29);
}
static void
upload_light_data (lightingctx_t *lctx, vulkan_ctx_t *ctx)
{
auto reg = lctx->scene->reg;
auto light_pool = &reg->comp_pools[scene_light];
auto lights = (light_t *) light_pool->data;
uint32_t count = light_pool->count;
auto packet = QFV_PacketAcquire (ctx->staging);
auto light_data = QFV_PacketExtend (packet, sizeof (light_t[count]));
memcpy (light_data, lights, sizeof (light_t[count]));
auto bb = &bufferBarriers[qfv_BB_TransferWrite_to_UniformRead];
for (size_t i = 0; i < lctx->frames.size; i++) {
auto lframe = &lctx->frames.a[i];
QFV_PacketCopyBuffer (packet, lframe->light_buffer, 0, bb);
}
QFV_PacketSubmit (packet);
packet = QFV_PacketAcquire (ctx->staging);
uint32_t r_size = sizeof (qfv_light_render_t[count]);
qfv_light_render_t *render = QFV_PacketExtend (packet, r_size);
for (uint32_t i = 0; i < count; i++) {
entity_t ent = { .reg = reg, .id = light_pool->dense[i] };
uint32_t id = get_lightid (ent);
auto r = &lctx->light_control.a[id];
render[i] = (qfv_light_render_t) {
.id_data = make_id(r->matrix_id, r->map_index, r->layer, r->mode),
.style = get_lightstyle (ent),
};
}
for (size_t i = 0; i < lctx->frames.size; i++) {
auto lframe = &lctx->frames.a[i];
QFV_PacketCopyBuffer (packet, lframe->render_buffer, 0, bb);
}
QFV_PacketSubmit (packet);
lctx->dynamic_base = count;
lctx->dynamic_count = 0;
}
static int
light_compare (const void *_li2, const void *_li1, void *_lights)
{
@ -1001,6 +1161,9 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
qfv_device_t *device = ctx->device;
qfv_physdev_t *physDev = device->physDev;
int maxLayers = physDev->properties->limits.maxImageArrayLayers;
if (maxLayers > 2048) {
maxLayers = 2048;
}
auto reg = lctx->scene->reg;
auto light_pool = &reg->comp_pools[scene_light];
auto lights = (light_t *) light_pool->data;
@ -1018,12 +1181,12 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
}
heapsort_r (lightMap, numLights, sizeof (int), light_compare, lights);
DARRAY_RESIZE (&lctx->light_renderers, numLights);
DARRAY_RESIZE (&lctx->light_control, numLights);
for (int i = 0; i < numLights; i++) {
int layers = 1;
auto li = lightMap[i];
auto lr = &lctx->light_renderers.a[li];
*lr = (light_renderer_t) {};
auto lr = &lctx->light_control.a[li];
*lr = (light_control_t) { .light_id = li, };
set_lightid (light_pool->dense[li], reg, li);
if (!lights[li].position[3]) {
@ -1078,7 +1241,7 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
for (int i = 0; i < numLights; i++) {
int layers = 4;
auto li = lightMap[i];
auto lr = &lctx->light_renderers.a[li];
auto lr = &lctx->light_control.a[li];
if (lr->mode != ST_CASCADE) {
continue;
@ -1108,19 +1271,22 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
if (numMaps) {
qfv_resource_t *shad = calloc (1, sizeof (qfv_resource_t)
+ sizeof (qfv_resobj_t[numMaps])
+ sizeof (qfv_resobj_t[numMaps]));
lctx->shadow_resources = shad;
*shad = (qfv_resource_t) {
.name = "shadow",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
.num_objects = numMaps,
.num_objects = 2 * numMaps,
.objects = (qfv_resobj_t *) &shad[1],
};
auto images = shad->objects;
auto views = &images[numMaps];
for (int i = 0; i < numMaps; i++) {
int cube = maps[i].layers < 6 ? 0 : maps[i].cube;
shad->objects[i] = (qfv_resobj_t) {
.name = va (ctx->va_ctx, "map:%d", maps[i].size),
images[i] = (qfv_resobj_t) {
.name = va (ctx->va_ctx, "map:image:%d:%d", i, maps[i].size),
.type = qfv_res_image,
.image = {
.flags = cube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0,
@ -1134,16 +1300,32 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
| VK_IMAGE_USAGE_SAMPLED_BIT,
},
};
views[i] = (qfv_resobj_t) {
.name = va (ctx->va_ctx, "map:view:%d:%d", i, maps[i].size),
.type = qfv_res_image_view,
.image_view = {
.image = i,
.type = cube ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY
: VK_IMAGE_VIEW_TYPE_2D_ARRAY,
.format = VK_FORMAT_X8_D24_UNORM_PACK32,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.levelCount = VK_REMAINING_MIP_LEVELS,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
},
};
}
QFV_CreateResource (device, shad);
for (int i = 0; i < numMaps; i++) {
DARRAY_APPEND (&lctx->light_images, shad->objects[i].image.image);
DARRAY_APPEND (&lctx->light_images, images[i].image.image);
DARRAY_APPEND (&lctx->light_views, views[i].image_view.view);
}
}
for (int i = 0; i < numLights; i++) {
int li = lightMap[i];
auto lr = &lctx->light_renderers.a[li];
auto lr = &lctx->light_control.a[li];
if (imageMap[li] == -1) {
continue;
@ -1163,7 +1345,7 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
Sys_Error ("build_shadow_maps: invalid light layer count: %u",
lr->numLayers);
}
lr->image_index = imageMap[li];
lr->map_index = imageMap[li];
}
Sys_MaskPrintf (SYS_lighting,
"shadow maps: %d layers in %zd images: %"PRId64"\n",
@ -1171,6 +1353,36 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
lctx->shadow_resources->size);
}
static void
update_shadow_descriptors (lightingctx_t *lctx, vulkan_ctx_t *ctx)
{
auto device = ctx->device;
auto dfunc = device->funcs;
VkDescriptorImageInfo imageInfo[32];
VkWriteDescriptorSet imageWrite[1];
for (size_t i = 0; i < 32; i++) {
VkImageView view = ctx->default_black->view;
if (i < lctx->light_views.size) {
view = lctx->light_views.a[i];
}
imageInfo[i] = (VkDescriptorImageInfo) {
.sampler = lctx->shadow_sampler,
.imageView = view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
}
imageWrite[0] = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = lctx->shadow_set,
.dstBinding = 0,
.descriptorCount = 32,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = imageInfo,
};
dfunc->vkUpdateDescriptorSets (device->dev, 1, imageWrite, 0, 0);
}
void
Vulkan_LoadLights (scene_t *scene, vulkan_ctx_t *ctx)
{
@ -1186,8 +1398,10 @@ Vulkan_LoadLights (scene_t *scene, vulkan_ctx_t *ctx)
auto light_pool = &reg->comp_pools[scene_light];
if (light_pool->count) {
build_shadow_maps (lctx, ctx);
update_shadow_descriptors (lctx, ctx);
create_light_matrices (lctx);
upload_light_matrices (lctx, ctx);
upload_light_data (lctx, ctx);
}
lctx->ldata = scene->lights;
}