diff --git a/include/QF/Vulkan/qf_lighting.h b/include/QF/Vulkan/qf_lighting.h index 9c6f2902a..cdf3b612e 100644 --- a/include/QF/Vulkan/qf_lighting.h +++ b/include/QF/Vulkan/qf_lighting.h @@ -48,26 +48,43 @@ typedef struct qfv_light_s { } qfv_light_t; typedef struct qfv_lightset_s DARRAY_TYPE (qfv_light_t) qfv_lightset_t; -typedef struct qfv_lightleafset_s DARRAY_TYPE (int) qfv_lightleafset_t; +typedef struct qfv_lightleafset_s DARRAY_TYPE (int) qfv_lightintset_t; typedef struct qfv_lightvisset_s DARRAY_TYPE (byte) qfv_lightvisset_t; typedef struct qfv_lightmatset_s DARRAY_TYPE (mat4f_t) qfv_lightmatset_t; -#define NUM_LIGHTS 256 -#define NUM_STYLES 64 +#define MaxLights 256 + +#define StyleMask 0x07f +#define ModelMask 0x380 +#define ShadowMask 0xc00 + +#define LM_LINEAR (0 << 7) // light - dist (or radius + dist if -ve) +#define LM_INVERSE (1 << 7) // distFactor1 * light / dist +#define LM_INVERSE2 (2 << 7) // distFactor2 * light / (dist * dist) +#define LM_INFINITE (3 << 7) // light +#define LM_AMBIENT (4 << 7) // light +#define LM_INVERSE3 (5 << 7) // distFactor2 * light / (dist + distFactor2)**2 + +#define ST_NONE (0 << 10) // no shadows +#define ST_PLANE (1 << 10) // single plane shadow map (small spotlight) +#define ST_CASCADE (2 << 10) // cascaded shadow maps +#define ST_CUBE (3 << 10) // cubemap (omni, large spotlight) + +#define NumStyles 64 typedef struct qfv_light_buffer_s { - float intensity[NUM_STYLES + 4]; + float intensity[NumStyles + 4]; float distFactor1; float distFactor2; int lightCount; - qfv_light_t lights[NUM_LIGHTS] __attribute__((aligned(16))); - mat4f_t shadowMat[NUM_LIGHTS]; - vec4f_t shadowCascade[NUM_LIGHTS]; + qfv_light_t lights[MaxLights] __attribute__((aligned(16))); + mat4f_t shadowMat[MaxLights]; + vec4f_t shadowCascade[MaxLights]; } qfv_light_buffer_t; #define LIGHTING_BUFFER_INFOS 1 #define LIGHTING_ATTACH_INFOS 5 -#define LIGHTING_SHADOW_INFOS NUM_LIGHTS +#define LIGHTING_SHADOW_INFOS MaxLights #define LIGHTING_DESCRIPTORS (LIGHTING_BUFFER_INFOS + LIGHTING_ATTACH_INFOS + 1) typedef struct lightingframe_s { @@ -100,10 +117,13 @@ typedef struct lightingctx_s { VkPipelineLayout layout; VkSampler sampler; VkDeviceMemory light_memory; + VkDeviceMemory shadow_memory; qfv_lightset_t lights; - qfv_lightleafset_t lightleafs; + qfv_lightintset_t lightleafs; qfv_lightmatset_t lightmats; - //qfv_imageviewset_t lightviews; + qfv_imageset_t lightimages; + qfv_lightintset_t lightlayers; + qfv_imageviewset_t lightviews; } lightingctx_t; struct vulkan_ctx_s; diff --git a/libs/video/renderer/vulkan/shader/lighting.frag b/libs/video/renderer/vulkan/shader/lighting.frag index a6db899c8..4f53c412c 100644 --- a/libs/video/renderer/vulkan/shader/lighting.frag +++ b/libs/video/renderer/vulkan/shader/lighting.frag @@ -14,7 +14,7 @@ struct LightData { vec3 direction; float cone; }; - +//XXX can't include :( be sure to keep up to date with qf_lighting.h #define MaxLights 256 #define StyleMask 0x07f @@ -29,8 +29,8 @@ struct LightData { #define LM_INVERSE3 (5 << 7) // distFactor2 * light / (dist + distFactor2)**2 #define ST_NONE (0 << 10) // no shadows -#define ST_CASCADE (1 << 10) // cascaded shadow maps -#define ST_PLANE (2 << 10) // single plane shadow map (small spotlight) +#define ST_PLANE (1 << 10) // single plane shadow map (small spotlight) +#define ST_CASCADE (2 << 10) // cascaded shadow maps #define ST_CUBE (3 << 10) // cubemap (omni, large spotlight) layout (set = 2, binding = 0) uniform sampler2DArrayShadow shadowCascade[MaxLights]; diff --git a/libs/video/renderer/vulkan/vulkan_lighting.c b/libs/video/renderer/vulkan/vulkan_lighting.c index 5fddc2653..e666e4c77 100644 --- a/libs/video/renderer/vulkan/vulkan_lighting.c +++ b/libs/video/renderer/vulkan/vulkan_lighting.c @@ -56,6 +56,8 @@ #include "QF/Vulkan/descriptor.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/projection.h" #include "QF/Vulkan/staging.h" #include "compat.h" @@ -63,6 +65,8 @@ #include "r_internal.h" #include "vid_vulkan.h" +static vec4f_t ref_direction = { 0, 0, 1, 0 }; + static void find_visible_lights (vulkan_ctx_t *ctx) { @@ -126,12 +130,12 @@ update_lights (vulkan_ctx_t *ctx) find_visible_lights (ctx); - dlight_t *lights[NUM_LIGHTS]; + dlight_t *lights[MaxLights]; qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); qfv_light_buffer_t *light_data = QFV_PacketExtend (packet, sizeof (*light_data)); - for (int i = 0; i < NUM_STYLES; i++) { + for (int i = 0; i < NumStyles; i++) { light_data->intensity[i] = d_lightstylevalue[i] / 65536.0; } // dynamic lights seem a tad faint, so 16x map lights @@ -144,8 +148,8 @@ update_lights (vulkan_ctx_t *ctx) light_data->distFactor2 = 1 / 16384.0; light_data->lightCount = 0; - R_FindNearLights (r_origin, NUM_LIGHTS - 1, lights); - for (int i = 0; i < NUM_LIGHTS - 1; i++) { + R_FindNearLights (r_origin, MaxLights - 1, lights); + for (int i = 0; i < MaxLights - 1; i++) { if (!lights[i]) { break; } @@ -158,7 +162,7 @@ update_lights (vulkan_ctx_t *ctx) light_data->lights[i].cone = 1; } for (size_t i = 0; (i < lframe->lightvis.size - && light_data->lightCount < NUM_LIGHTS); i++) { + && light_data->lightCount < MaxLights); i++) { if (lframe->lightvis.a[i]) { light_data->lights[light_data->lightCount++] = lctx->lights.a[i]; } @@ -285,6 +289,9 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx) DARRAY_INIT (&lctx->lights, 16); DARRAY_INIT (&lctx->lightleafs, 16); DARRAY_INIT (&lctx->lightmats, 16); + DARRAY_INIT (&lctx->lightlayers, 16); + DARRAY_INIT (&lctx->lightimages, 16); + DARRAY_INIT (&lctx->lightviews, 16); size_t frames = ctx->frames.size; DARRAY_INIT (&lctx->frames, frames); @@ -388,13 +395,33 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx) lframe->shadowWrite = base_image_write; lframe->shadowWrite.dstSet = shadow_set->a[i]; lframe->shadowWrite.dstBinding = 0; - lframe->shadowWrite.descriptorCount = NUM_LIGHTS; + lframe->shadowWrite.descriptorCount = MaxLights; lframe->shadowWrite.pImageInfo = lframe->shadowInfo; } free (attach_set); free (lights_set); } +static void +clear_shadows (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + lightingctx_t *lctx = ctx->lighting_context; + + if (lctx->shadow_memory) { + dfunc->vkFreeMemory (device->dev, lctx->shadow_memory, 0); + } + for (size_t i = 0; i < lctx->lightviews.size; i++) { + dfunc->vkDestroyImageView (device->dev, lctx->lightviews.a[i], 0); + } + for (size_t i = 0; i < lctx->lightimages.size; i++) { + dfunc->vkDestroyImage (device->dev, lctx->lightimages.a[i], 0); + } + lctx->lightimages.size = 0; + lctx->lightviews.size = 0; +} + void Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx) { @@ -402,6 +429,8 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx) qfv_devfuncs_t *dfunc = device->funcs; lightingctx_t *lctx = ctx->lighting_context; + clear_shadows (ctx); + for (size_t i = 0; i < lctx->frames.size; i++) { lightingframe_t *lframe = &lctx->frames.a[i]; dfunc->vkDestroyBuffer (device->dev, lframe->light_buffer, 0); @@ -412,12 +441,15 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx) DARRAY_CLEAR (&lctx->lights); DARRAY_CLEAR (&lctx->lightleafs); DARRAY_CLEAR (&lctx->lightmats); + DARRAY_CLEAR (&lctx->lightimages); + DARRAY_CLEAR (&lctx->lightlayers); + DARRAY_CLEAR (&lctx->lightviews); free (lctx->frames.a); free (lctx); } static void -dump_light (qfv_light_t *light, int leaf) +dump_light (qfv_light_t *light, int leaf, mat4f_t mat) { Sys_MaskPrintf (SYS_vulkan, "[%g, %g, %g] %d %d %d, " @@ -429,6 +461,10 @@ dump_light (qfv_light_t *light, int leaf) VectorExpand (light->position), light->light, VectorExpand (light->direction), light->cone, leaf); + Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 0)); + Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 1)); + Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 2)); + Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 3)); } static float @@ -516,13 +552,12 @@ parse_sun (lightingctx_t *lctx, plitem_t *entity) return; } VectorSet (1, 1, 1, light.color); - light.data = 3 << 7; //FIXME magic number (LM_INFINITE) + light.data = LM_INFINITE | ST_CASCADE; light.light = sunlight; sun_vector (sunangle, light.direction); light.cone = 1; DARRAY_APPEND (&lctx->lights, light); DARRAY_APPEND (&lctx->lightleafs, -1); - dump_light (&light, -1); } static void @@ -577,9 +612,11 @@ parse_light (qfv_light_t *light, const plitem_t *entity, } if ((str = PL_String (PL_ObjectForKey (entity, "delay")))) { - model = atoi (str) & 0x7; - if (model == 2) model = 5; //FIXME for marcher (need a map) - light->data |= model << 7; + model = (atoi (str) & 0x7) << 7; + if (model == LM_INVERSE2) { + model = LM_INVERSE3; //FIXME for marcher (need a map) + } + light->data |= model; } if ((str = PL_String (PL_ObjectForKey (entity, "color"))) @@ -588,18 +625,263 @@ parse_light (qfv_light_t *light, const plitem_t *entity, VectorScale (light->color, 1/255.0, light->color); } - //FIXME magic numbers - if (model == 3) { // infinite - light->data |= 1 << 10; // cascade - } else if (model != 4) {// ambient + if (model == LM_INFINITE) { + light->data |= ST_CASCADE; + } else if (model != LM_AMBIENT) { if (light->cone > -0.5) { - light->data |= 3 << 10; // cube + light->data |= ST_CUBE; } else { - light->data |= 2 << 10; // plane + light->data |= ST_PLANE; } } } +static void +create_light_matrices (lightingctx_t *lctx) +{ + DARRAY_RESIZE (&lctx->lightmats, lctx->lights.size); + for (size_t i = 0; i < lctx->lights.size; i++) { + qfv_light_t *light = &lctx->lights.a[i]; + mat4f_t view; + mat4f_t proj; + + switch (light->data & ShadowMask) { + case ST_NONE: + case ST_CUBE: + mat4fidentity (view); + break; + case ST_CASCADE: + case ST_PLANE: + //FIXME will fail for -ref_direction + mat4fquat (view, qrotf (loadvec3f (light->direction), + ref_direction)); + break; + } + VectorNegate (light->position, view[3]); + + switch (light->data & ShadowMask) { + case ST_NONE: + mat4fidentity (proj); + break; + case ST_CUBE: + QFV_PerspectiveTan (proj, 1, 1); + break; + case ST_CASCADE: + // dependent on view fustrum and cascade level + mat4fidentity (proj); + break; + case ST_PLANE: + QFV_PerspectiveCos (proj, light->cone, 1); + break; + } + mmulf (lctx->lightmats.a[i], proj, view); + } +} + +static int +light_compare (const void *_l2, const void *_l1) +{ + const qfv_light_t *l1 = _l1; + const qfv_light_t *l2 = _l2; + + if (l1->light == l2->light) { + return (l1->data & ShadowMask) - (l2->data & ShadowMask); + } + return l1->light - l2->light; +} + +static VkImage +create_map (int size, int layers, int cube, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (layers < 6) { + cube = 0; + } + + VkImageCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 0, + cube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0, VK_IMAGE_TYPE_2D, + VK_FORMAT_X8_D24_UNORM_PACK32, + { size, size, 1 }, 1, layers, + VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT, VK_SHARING_MODE_EXCLUSIVE, + 0, 0, + VK_IMAGE_LAYOUT_UNDEFINED, + }; + VkImage image; + dfunc->vkCreateImage (device->dev, &createInfo, 0, &image); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, image, + va (ctx->va_ctx, "image:shadowmap:%d:%d", + size, layers)); + return image; +} + +static VkImageView +create_view (VkImage image, int baseLayer, int data, int id, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + int layers = 0; + VkImageViewType type = 0; + const char *viewtype = 0; + + switch (data & ShadowMask) { + case ST_NONE: + return 0; + case ST_PLANE: + layers = 1; + type = VK_IMAGE_VIEW_TYPE_2D; + viewtype = "plane"; + break; + case ST_CASCADE: + layers = 4; + type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + viewtype = "cascade"; + break; + case ST_CUBE: + layers = 6; + type = VK_IMAGE_VIEW_TYPE_CUBE; + viewtype = "cube"; + break; + } + + VkImageViewCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 0, + 0, + image, type, VK_FORMAT_X8_D24_UNORM_PACK32, + { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + }, + { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, baseLayer, layers } + }; + + VkImageView view; + dfunc->vkCreateImageView (device->dev, &createInfo, 0, &view); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, view, + va (ctx->va_ctx, "iview:shadowmap:%s:%d", + viewtype, id)); + return view; +} + +static void +build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + qfv_physdev_t *physDev = device->physDev; + int maxLayers = physDev->properties.limits.maxImageArrayLayers; + qfv_light_t *lights = lctx->lights.a; + int numLights = lctx->lights.size; + int size = -1; + int numLayers = 0; + int totalLayers = 0; + int *imageMap = alloca (numLights * sizeof (int)); + size_t memsize = 0; + + DARRAY_RESIZE (&lctx->lightlayers, numLights); + qsort (lights, numLights, sizeof (qfv_light_t), light_compare); + for (int i = 0; i < numLights; i++) { + int shadow = lights[i].data & ShadowMask; + int layers = 1; + if (shadow == ST_CASCADE || shadow == ST_NONE) { + // cascade shadows will be handled separately, and "none" has no + // shadow map at all + imageMap[i] = -1; + continue; + } + if (shadow == ST_CUBE) { + layers = 6; + } + if (size != (int) lights[i].light || numLayers + layers > maxLayers) { + if (numLayers) { + VkImage shadow_map = create_map (size, numLayers, 1, ctx); + DARRAY_APPEND (&lctx->lightimages, shadow_map); + numLayers = 0; + } + size = lights[i].light; + } + imageMap[i] = lctx->lightimages.size; + lctx->lightlayers.a[i] = numLayers; + numLayers += layers; + totalLayers += layers; + } + if (numLayers) { + VkImage shadow_map = create_map (size, numLayers, 1, ctx); + DARRAY_APPEND (&lctx->lightimages, shadow_map); + } + + numLayers = 0; + size = 1024; + for (int i = 0; i < numLights; i++) { + int shadow = lights[i].data & ShadowMask; + int layers = 4; + + if (shadow != ST_CASCADE) { + continue; + } + if (numLayers + layers > maxLayers) { + VkImage shadow_map = create_map (size, numLayers, 0, ctx); + DARRAY_APPEND (&lctx->lightimages, shadow_map); + numLayers = 0; + } + imageMap[i] = lctx->lightimages.size; + lctx->lightlayers.a[i] = numLayers; + numLayers += layers; + totalLayers += layers; + } + if (numLayers) { + VkImage shadow_map = create_map (size, numLayers, 0, ctx); + DARRAY_APPEND (&lctx->lightimages, shadow_map); + } + + for (size_t i = 0; i < lctx->lightimages.size; i++) { + memsize += QFV_GetImageSize (device, lctx->lightimages.a[i]); + } + lctx->shadow_memory = QFV_AllocImageMemory (device, lctx->lightimages.a[0], + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + memsize, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + lctx->shadow_memory, "memory:shadowmap"); + + size_t offset = 0; + for (size_t i = 0; i < lctx->lightimages.size; i++) { + dfunc->vkBindImageMemory (device->dev, lctx->lightimages.a[i], + lctx->shadow_memory, offset); + offset += QFV_GetImageSize (device, lctx->lightimages.a[i]); + } + + DARRAY_RESIZE (&lctx->lightviews, numLights); + for (int i = 0; i < numLights; i++) { + if (imageMap[i] == -1) { + lctx->lightviews.a[i] = 0; + continue; + } + lctx->lightviews.a[i] = create_view (lctx->lightimages.a[imageMap[i]], + lctx->lightlayers.a[i], + lctx->lights.a[i].data, i, ctx); + } + Sys_MaskPrintf (SYS_vulkan, "shadow maps: %d layers in %zd images: %zd\n", + totalLayers, lctx->lightimages.size, memsize); +} + +static void +locate_lights (model_t *model, lightingctx_t *lctx) +{ + qfv_light_t *lights = lctx->lights.a; + DARRAY_RESIZE (&lctx->lightleafs, lctx->lights.size); + for (size_t i = 0; i < lctx->lights.size; i++) { + mleaf_t *leaf = Mod_PointInLeaf (&lights[i].position[0], model); + lctx->lightleafs.a[i] = leaf - model->brush.leafs - 1; + } +} + void Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx) { @@ -610,6 +892,8 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx) lctx->lightleafs.size = 0; lctx->lightmats.size = 0; + clear_shadows (ctx); + script_t *script = Script_New (); Script_Start (script, "ent data", entity_data); @@ -654,12 +938,6 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx) parse_light (&light, entity, targets); DARRAY_APPEND (&lctx->lights, light); - mleaf_t *leaf = Mod_PointInLeaf (&light.position[0], - model); - DARRAY_APPEND (&lctx->lightleafs, - leaf - model->brush.leafs - 1); - dump_light (&light, - lctx->lightleafs.a[lctx->lightleafs.size - 1]); } } for (size_t i = 0; i < ctx->frames.size; i++) { @@ -675,4 +953,11 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx) PL_Free (entities); } Sys_MaskPrintf (SYS_vulkan, "loaded %zd lights\n", lctx->lights.size); + build_shadow_maps (lctx, ctx); + create_light_matrices (lctx); + locate_lights (model, lctx); + for (size_t i = 0; i < lctx->lights.size; i++) { + dump_light (&lctx->lights.a[i], lctx->lightleafs.a[i], + lctx->lightmats.a[i]); + } }