[scene] Put lights into the bsp tree via efrags

This eliminates the O(N^2) (N = map leaf count) operation of finding
visible lights and will later allow for finer culling of the lights as
they can be tested against the leaf volume (which they currently are
not as this was just getting things going). However, this has severely
hurt ad_tears' performance (I suspect due to the extreme number of
leafs), but the speed seems to be very steady. Hopefully, reconstructing
the vis clusters will help (I imagine it will help in many places, not
just lights).
This commit is contained in:
Bill Currie 2023-07-22 17:53:07 +09:00
parent c0f8d102ad
commit 72f6048a20
9 changed files with 110 additions and 120 deletions

View file

@ -369,6 +369,8 @@ typedef enum {
mod_alias,
mod_iqm,
mod_light,
mod_num_types
} modtype_t;

View file

@ -150,6 +150,8 @@ Entity_Transform (entity_t ent)
}
struct mod_brush_s;
efrag_t **R_LinkEfrag (struct mleaf_s *leaf, entity_t ent, uint32_t queue,
efrag_t **lastlink);
void R_AddEfrags (struct mod_brush_s *, entity_t ent);
void R_ShutdownEfrags (void);
void R_ClearEfragChain (efrag_t *ef);

View file

@ -53,15 +53,7 @@ typedef struct light_s {
vec4f_t attenuation;
} light_t;
typedef struct lightset_s DARRAY_TYPE (light_t) lightset_t;
typedef struct lightleafset_s DARRAY_TYPE (int) lightintset_t;
typedef struct lightvisset_s DARRAY_TYPE (byte) lightvisset_t;
typedef struct lightingdata_s {
lightset_t lights;
lightintset_t lightstyles;
lightintset_t lightleafs;
lightvisset_t lightvis;
struct set_s *sun_pvs;
// A fat PVS of leafs visible from visible leafs so hidden lights can
// illuminate the leafs visible to the player
@ -75,6 +67,5 @@ void Light_DestroyLightingData (lightingdata_t *ldata);
void Light_ClearLights (lightingdata_t *ldata);
void Light_AddLight (lightingdata_t *ldata, const light_t *light, int style);
void Light_EnableSun (lightingdata_t *ldata);
void Light_FindVisibleLights (lightingdata_t *ldata);
#endif//__QF_scene_light_h

View file

@ -49,6 +49,10 @@ enum scene_components {
scene_old_origin, //XXX FIXME XXX should not be here
scene_colormap,
scene_light,
scene_efrags,
scene_lightstyle,
//FIXME these should probably be private to the sw renderer (and in a
//group, which needs to be implemented), but need to sort out a good
//scheme for semi-dynamic components

View file

@ -10,6 +10,8 @@
#include "QF/plist.h"
#include "QF/progs.h" //for ED_ConvertToPlist
#include "QF/set.h"
#include "QF/ecs.h"
#include "QF/scene/entity.h"
#include "QF/scene/light.h"
#include "QF/scene/scene.h"
#include "QF/simd/vec4f.h"
@ -17,8 +19,12 @@
#include "client/world.h"
static void
dump_light (light_t *light, int leaf)
dump_light (light_t *light, efrag_t *efrags)
{
int leafcount = 0;
for (auto e = efrags; e; e = e->leafnext) {
leafcount++;
}
Sys_MaskPrintf (SYS_lighting,
"[%g, %g, %g] %g, "
"[%g, %g, %g, %g], [%g %g %g] %g, [%g, %g, %g, %g] %d\n",
@ -26,7 +32,7 @@ dump_light (light_t *light, int leaf)
VEC4_EXP (light->position),
VEC4_EXP (light->direction),
VEC4_EXP (light->attenuation),
leaf);
leafcount);
}
static float
@ -323,8 +329,12 @@ CL_LoadLights (plitem_t *entities, scene_t *scene)
}
PL_Release (targets);
for (size_t i = 0; i < ldata->lights.size; i++) {
dump_light (&ldata->lights.a[i], ldata->lightleafs.a[i]);
auto lights = &scene->reg->comp_pools[scene_light];
auto lefrags = &scene->reg->comp_pools[scene_efrags];
for (uint32_t i = 0; i < lights->count; i++) {
auto light = &((light_t *)lights->data)[i];
auto efrags = ((efrag_t **)lefrags->data)[i];
dump_light (light, efrags);
}
Sys_MaskPrintf (SYS_lighting, "loaded %zd lights\n", ldata->lights.size);
Sys_MaskPrintf (SYS_lighting, "loaded %d lights\n", lights->count);
}

View file

@ -130,11 +130,28 @@ R_ClearEfragChain (efrag_t *ef)
}
}
efrag_t **
R_LinkEfrag (mleaf_t *leaf, entity_t ent, uint32_t queue, efrag_t **lastlink)
{
efrag_t *ef = new_efrag (); // ensures ef->entnext is 0
// add the link to the chain of links on the entity
ef->entity = ent;
ef->queue_num = queue;
*lastlink = ef;
// add the link too the chain of links on the leaf
ef->leaf = leaf;
ef->leafnext = leaf->efrags;
leaf->efrags = ef;
return &ef->entnext;
}
static void
R_SplitEntityOnNode (mod_brush_t *brush, entity_t ent, uint32_t queue,
visibility_t *visibility, vec3_t emins, vec3_t emaxs)
{
efrag_t *ef;
plane_t *splitplane;
mleaf_t *leaf;
int sides;
@ -159,18 +176,7 @@ R_SplitEntityOnNode (mod_brush_t *brush, entity_t ent, uint32_t queue,
leaf = brush->leafs + ~node_id;
ef = new_efrag (); // ensures ef->entnext is 0
// add the link to the chain of links on the entity
ef->entity = ent;
ef->queue_num = queue;
*lastlink = ef;
lastlink = &ef->entnext;
// add the link too the chain of links on the leaf
ef->leaf = leaf;
ef->leafnext = leaf->efrags;
leaf->efrags = ef;
lastlink = R_LinkEfrag (leaf, ent, queue, lastlink);
node_id = *--node_ptr;
} else {

View file

@ -6,6 +6,7 @@
#include "QF/model.h"
#include "QF/set.h"
#include "QF/scene/entity.h"
#include "QF/scene/light.h"
#include "QF/scene/scene.h"
#include "QF/simd/vec4f.h"
@ -27,11 +28,6 @@ Light_CreateLightingData (scene_t *scene)
{
lightingdata_t *ldata = calloc (1, sizeof (lightingdata_t));
DARRAY_INIT (&ldata->lights, 16);
DARRAY_INIT (&ldata->lightstyles, 16);
DARRAY_INIT (&ldata->lightleafs, 16);
DARRAY_INIT (&ldata->lightvis, 16);
ldata->scene = scene;
return ldata;
@ -40,21 +36,12 @@ Light_CreateLightingData (scene_t *scene)
void
Light_DestroyLightingData (lightingdata_t *ldata)
{
DARRAY_CLEAR (&ldata->lights);
DARRAY_CLEAR (&ldata->lightstyles);
DARRAY_CLEAR (&ldata->lightleafs);
DARRAY_CLEAR (&ldata->lightvis);
free (ldata);
}
void
Light_ClearLights (lightingdata_t *ldata)
{
ldata->lights.size = 0;
ldata->lightstyles.size = 0;
ldata->lightleafs.size = 0;
ldata->lightvis.size = 0;
if (ldata->sun_pvs) {
set_delete (ldata->sun_pvs);
}
@ -72,20 +59,34 @@ Light_AddLight (lightingdata_t *ldata, const light_t *light, int style)
scene_t *scene = ldata->scene;
model_t *model = scene->worldmodel;
DARRAY_APPEND (&ldata->lights, *light);
DARRAY_APPEND (&ldata->lightstyles, style);
entity_t ent = {
.reg = scene->reg,
.id = ECS_NewEntity (scene->reg),
};
int visleaf = -1; // directional light
Ent_SetComponent (ent.id, scene_light, ent.reg, light);
Ent_SetComponent (ent.id, scene_lightstyle, ent.reg, &style);
set_t _pvs = SET_STATIC_INIT (model->brush.visleafs, alloca);
set_t *pvs = &_pvs;
if (light->position[3]) {
// positional light
mleaf_t *leaf = Mod_PointInLeaf (light->position, &model->brush);
visleaf = leaf - model->brush.leafs - 1;
} else if (!DotProduct (light->direction, light->direction)) {
Mod_LeafPVS_set (leaf, &model->brush, 0, pvs);
} else if (DotProduct (light->direction, light->direction)) {
// directional light (sun)
pvs = ldata->sun_pvs;
} else {
// ambient light
visleaf = -2;
Mod_LeafPVS_set (model->brush.leafs, &model->brush, 0, pvs);
}
DARRAY_APPEND (&ldata->lightleafs, visleaf);
DARRAY_APPEND (&ldata->lightvis, 0);
efrag_t *efrags = 0;
efrag_t **lastlink = &efrags;
for (auto li = set_first (pvs); li; li = set_next (li)) {
mleaf_t *leaf = model->brush.leafs + li->element + 1;
lastlink = R_LinkEfrag (leaf, ent, mod_light, lastlink);
}
Ent_SetComponent (ent.id, scene_efrags, ent.reg, &efrags);
}
void
@ -111,52 +112,3 @@ Light_EnableSun (lightingdata_t *ldata)
// can receive shadows from the sun
expand_pvs (ldata->sun_pvs, brush);
}
void
Light_FindVisibleLights (lightingdata_t *ldata)
{
scene_t *scene = ldata->scene;
mleaf_t *leaf = scene->viewleaf;
auto brush = &scene->worldmodel->brush;
if (!leaf) {
return;
}
if (!ldata->pvs) {
ldata->pvs = set_new_size (brush->visleafs);
}
if (leaf != ldata->leaf) {
//double start = Sys_DoubleTime ();
int flags = 0;
if (leaf == brush->leafs) {
set_everything (ldata->pvs);
flags = SURF_DRAWSKY;
} else {
Mod_LeafPVS_set (leaf, brush, 0, ldata->pvs);
if (set_is_intersecting (ldata->pvs, ldata->sun_pvs)) {
flags |= SURF_DRAWSKY;
}
expand_pvs (ldata->pvs, brush);
}
ldata->leaf = leaf;
//double end = Sys_DoubleTime ();
//Sys_Printf ("find_visible_lights: %.5gus\n", (end - start) * 1e6);
int visible = 0;
memset (ldata->lightvis.a, 0, ldata->lightvis.size * sizeof (byte));
for (size_t i = 0; i < ldata->lightleafs.size; i++) {
int l = ldata->lightleafs.a[i];
if ((l == -2) || (l == -1 && (flags & SURF_DRAWSKY))
|| set_is_member (ldata->pvs, l)) {
ldata->lightvis.a[i] = 1;
visible++;
}
}
Sys_MaskPrintf (SYS_lighting,
"find_visible_lights: %d / %zd visible\n", visible,
ldata->lightvis.size);
}
}

View file

@ -40,7 +40,9 @@
#include "QF/model.h"
#include "QF/plugin/vid_render.h"
#include "QF/scene/entity.h"
#include "QF/scene/light.h"
#include "QF/scene/scene.h"
#include "QF/scene/transform.h"
@ -83,6 +85,13 @@ destroy_renderer (void *_renderer)
}
}
static void
destroy_efrags (void *_efrags)
{
efrag_t **efrags = _efrags;
R_ClearEfragChain (*efrags);
}
static void
sw_identity_matrix (void *_mat)
{
@ -144,6 +153,20 @@ static const component_t scene_components[scene_comp_count] = {
.name = "colormap",
},
[scene_light] = {
.size = sizeof (light_t),
.name = "light",
},
[scene_efrags] = {
.size = sizeof (efrag_t *),
.destroy = destroy_efrags,
.name = "efrags",
},
[scene_lightstyle] = {
.size = sizeof (int),
.name = "lightstyle",
},
[scene_sw_matrix] = {
.size = sizeof (mat4f_t),
.create = sw_identity_matrix,

View file

@ -183,10 +183,6 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
return;
}
lightingdata_t *ldata = lctx->ldata;
Light_FindVisibleLights (ldata);
dlight_t *lights[MaxLights];
auto packet = QFV_PacketAcquire (ctx->staging);
qfv_light_buffer_t *light_data = QFV_PacketExtend (packet,
@ -220,23 +216,26 @@ lighting_update_lights (const exprval_t **params, exprval_t *result,
// full sphere, normal light (not ambient)
light_data->lights[i].direction = (vec4f_t) { 0, 0, 1, 1 };
}
for (size_t i = 0; (i < ldata->lightvis.size
&& light_data->lightCount < MaxLights); i++) {
if (ldata->lightvis.a[i]) {
uint32_t id = light_data->lightCount++;
auto light = &light_data->lights[id];
*light = ldata->lights.a[i];
light->color[3] *= style_intensities[ldata->lightstyles.a[i]];
if (light->position[3] && !VectorIsZero (light->direction)
&& light->attenuation[3]) {
if (light->direction[3] < 0) {
cone_ids[lframe->cone_count++] = id;
} else {
ico_ids[lframe->ico_count++] = id;
}
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];
light_t *l = Ent_GetComponent (ent.id, scene_light, ent.reg);
int ls = *(int *) Ent_GetComponent (ent.id, scene_lightstyle,
ent.reg);
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) {
cone_ids[lframe->cone_count++] = id;
} else {
flat_ids[lframe->flat_count++] = id;
ico_ids[lframe->ico_count++] = id;
}
} else {
flat_ids[lframe->flat_count++] = id;
}
}
if (developer & SYS_lighting) {
@ -700,7 +699,7 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx)
free (lctx->frames.a);
free (lctx);
}
/*
static vec4f_t ref_direction = { 0, 0, 1, 0 };
static void
@ -1026,7 +1025,7 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
totalLayers, lctx->light_images.size,
lctx->shadow_resources->size);
}
*/
void
Vulkan_LoadLights (scene_t *scene, vulkan_ctx_t *ctx)
{
@ -1034,11 +1033,12 @@ Vulkan_LoadLights (scene_t *scene, vulkan_ctx_t *ctx)
lctx->scene = scene;
lctx->ldata = scene ? scene->lights : 0;
/*
clear_shadows (ctx);
if (lctx->ldata && lctx->ldata->lights.size) {
build_shadow_maps (lctx, ctx);
create_light_matrices (lctx);
}
*/
}