mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-19 07:20:50 +00:00
[qfvis] Reconstruct the leaf clusters in a bsp
This is only the first half (vertical) in that the vis bits are still for the leafs rather than the clusters, but ad_tears goes from 500s to 7s for calculating the fat pvs (3852 clusters).
This commit is contained in:
parent
42dc30ec29
commit
8da019e31c
3 changed files with 167 additions and 76 deletions
|
@ -204,7 +204,7 @@ extern struct bsp_s *bsp;
|
|||
|
||||
extern portal_t *portals;
|
||||
extern cluster_t *clusters;
|
||||
extern int *leafcluster;
|
||||
extern uint32_t *leafcluster;
|
||||
extern byte *uncompressed;
|
||||
|
||||
winding_t *NewWinding (threaddata_t *thread, int points);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/heapsort.h"
|
||||
#include "QF/pvsfile.h"
|
||||
#include "QF/quakefs.h"
|
||||
#include "QF/set.h"
|
||||
|
@ -41,14 +42,26 @@
|
|||
#include "tools/qfvis/include/options.h"
|
||||
#include "tools/qfvis/include/vis.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t visoffs;
|
||||
uint32_t leafnum;
|
||||
} leafvis_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t first_leaf;
|
||||
uint32_t num_leafs;
|
||||
} leafmap_t;
|
||||
|
||||
static set_pool_t *set_pool;
|
||||
static set_t *base_pvs;
|
||||
static set_t *fat_pvs;
|
||||
static set_t *base_pvs;
|
||||
static set_t *fat_pvs;
|
||||
static leafvis_t *leafvis;
|
||||
static leafmap_t *leafmap;
|
||||
static sizebuf_t *cmp_pvs;
|
||||
|
||||
static unsigned num_leafs;
|
||||
static unsigned visbytes;
|
||||
static unsigned work_leaf;
|
||||
static uint32_t num_leafs;
|
||||
static uint32_t num_clusters;
|
||||
static uint32_t work_cluster;
|
||||
|
||||
typedef struct {
|
||||
long pvs_visible;
|
||||
|
@ -69,18 +82,18 @@ update_stats (fatstats_t *stats)
|
|||
}
|
||||
|
||||
static int
|
||||
leaf_progress (void)
|
||||
cluster_progress (void)
|
||||
{
|
||||
return work_leaf * 100 / num_leafs;
|
||||
return work_cluster * 100 / num_clusters;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
next_leaf (void)
|
||||
next_cluster (void)
|
||||
{
|
||||
unsigned leaf = ~0;
|
||||
WRLOCK (global_lock);
|
||||
if (work_leaf < num_leafs) {
|
||||
leaf = work_leaf++;
|
||||
if (work_cluster < num_clusters) {
|
||||
leaf = work_cluster++;
|
||||
}
|
||||
UNLOCK (global_lock);
|
||||
return leaf;
|
||||
|
@ -100,22 +113,24 @@ decompress_vis (const byte *in, unsigned numleafs, set_t *pvs)
|
|||
*out++ = 0xff;
|
||||
row--;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
do {
|
||||
if (*in) {
|
||||
*out++ = *in++;
|
||||
continue;
|
||||
}
|
||||
c = in[1];
|
||||
in += 2;
|
||||
while (c) {
|
||||
*out++ = 0;
|
||||
c--;
|
||||
}
|
||||
} while (out - start < row);
|
||||
}
|
||||
|
||||
do {
|
||||
if (*in) {
|
||||
*out++ = *in++;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = in[1];
|
||||
in += 2;
|
||||
while (c) {
|
||||
*out++ = 0;
|
||||
c--;
|
||||
}
|
||||
} while (out - start < row);
|
||||
while ((out - start) & (sizeof (set_bits_t) - 1)) {
|
||||
*out++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
|
@ -125,23 +140,23 @@ decompress_thread (void *d)
|
|||
int thread = (intptr_t) d;
|
||||
|
||||
while (1) {
|
||||
unsigned leaf_num = next_leaf ();
|
||||
unsigned cluster_num = next_cluster ();
|
||||
|
||||
if (working)
|
||||
working[thread] = leaf_num;
|
||||
if (leaf_num == ~0u) {
|
||||
working[thread] = cluster_num;
|
||||
if (cluster_num == ~0u) {
|
||||
break;
|
||||
}
|
||||
byte *visdata = 0;
|
||||
dleaf_t *leaf = &bsp->leafs[leaf_num];
|
||||
dleaf_t *leaf = &bsp->leafs[leafmap[cluster_num].first_leaf + 1];
|
||||
if (leaf->visofs >= 0) {
|
||||
visdata = bsp->visdata + leaf->visofs;
|
||||
}
|
||||
decompress_vis (visdata, num_leafs, &base_pvs[leaf_num]);
|
||||
if (leaf_num == 0) {
|
||||
decompress_vis (visdata, num_leafs, &base_pvs[cluster_num]);
|
||||
if (cluster_num == 0) {
|
||||
continue;
|
||||
}
|
||||
stats.pvs_visible += set_count (&base_pvs[leaf_num]);
|
||||
stats.pvs_visible += set_count (&base_pvs[cluster_num]);
|
||||
}
|
||||
update_stats (&stats);
|
||||
return 0;
|
||||
|
@ -154,29 +169,24 @@ fatten_thread (void *d)
|
|||
int thread = (intptr_t) d;
|
||||
|
||||
while (1) {
|
||||
unsigned leaf_num = next_leaf ();
|
||||
unsigned cluster_num = next_cluster ();
|
||||
|
||||
if (working)
|
||||
working[thread] = leaf_num;
|
||||
if (leaf_num == ~0u) {
|
||||
working[thread] = cluster_num;
|
||||
if (cluster_num == ~0u) {
|
||||
break;
|
||||
}
|
||||
|
||||
set_assign (&fat_pvs[leaf_num], &base_pvs[leaf_num]);
|
||||
set_assign (&fat_pvs[cluster_num], &base_pvs[cluster_num]);
|
||||
for (set_iter_t *iter = set_first_r (&set_pool[thread],
|
||||
&base_pvs[leaf_num]);
|
||||
&base_pvs[cluster_num]);
|
||||
iter;
|
||||
iter = set_next_r (&set_pool[thread], iter)) {
|
||||
|
||||
// or this pvs row into the fat pvs
|
||||
// +1 because pvs is 1 based
|
||||
set_union (&fat_pvs[leaf_num], &base_pvs[iter->element + 1]);
|
||||
set_union (&fat_pvs[cluster_num], &base_pvs[leafcluster[iter->element]]);
|
||||
}
|
||||
if (leaf_num == 0) {
|
||||
continue;
|
||||
}
|
||||
stats.fat_visible += set_count (&fat_pvs[leaf_num]);
|
||||
set_difference (&fat_pvs[leaf_num], &base_pvs[leaf_num]);
|
||||
stats.fat_visible += set_count (&fat_pvs[cluster_num]);
|
||||
set_difference (&fat_pvs[cluster_num], &base_pvs[cluster_num]);
|
||||
}
|
||||
update_stats (&stats);
|
||||
return 0;
|
||||
|
@ -218,15 +228,15 @@ compress_thread (void *d)
|
|||
qboolean rle = options.utf8;
|
||||
|
||||
while (1) {
|
||||
unsigned leaf_num = next_leaf ();
|
||||
unsigned cluster_num = next_cluster ();
|
||||
|
||||
if (working)
|
||||
working[thread] = leaf_num;
|
||||
if (leaf_num == ~0u) {
|
||||
working[thread] = cluster_num;
|
||||
if (cluster_num == ~0u) {
|
||||
break;
|
||||
}
|
||||
sizebuf_t *compressed = &cmp_pvs[leaf_num];
|
||||
const byte *fat_bytes = (const byte *) fat_pvs[leaf_num].map;
|
||||
sizebuf_t *compressed = &cmp_pvs[cluster_num];
|
||||
const byte *fat_bytes = (const byte *) fat_pvs[cluster_num].map;
|
||||
int bytes = CompressRow (compressed, fat_bytes, num_leafs, rle);
|
||||
stats.fat_bytes += bytes;
|
||||
}
|
||||
|
@ -234,34 +244,81 @@ compress_thread (void *d)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
CalcFatPVS (void)
|
||||
static int
|
||||
leaf_compare (const void *_la, const void *_lb)
|
||||
{
|
||||
const leafvis_t *la = _la;
|
||||
const leafvis_t *lb = _lb;
|
||||
if (la->visoffs == lb->visoffs) {
|
||||
return la->leafnum - lb->leafnum;
|
||||
}
|
||||
return la->visoffs - lb->visoffs;
|
||||
}
|
||||
|
||||
static void
|
||||
reconstruct_clusters (void)
|
||||
{
|
||||
leafvis = malloc (num_leafs * sizeof (leafvis_t));
|
||||
for (unsigned i = 0; i < num_leafs; i++) {
|
||||
leafvis[i].visoffs = bsp->leafs[i + 1].visofs;
|
||||
leafvis[i].leafnum = i;
|
||||
}
|
||||
heapsort (leafvis, num_leafs, sizeof (leafvis_t), leaf_compare);
|
||||
|
||||
num_clusters = 1;
|
||||
for (unsigned i = 1; i < num_leafs; i++) {
|
||||
num_clusters += leafvis[i].visoffs != leafvis[i - 1].visoffs;
|
||||
}
|
||||
|
||||
printf ("num_clusters: %u\n", num_clusters);
|
||||
|
||||
leafcluster = malloc (num_leafs * sizeof (uint32_t));
|
||||
leafmap = calloc (num_clusters, sizeof (leafmap_t));
|
||||
leafmap_t *lm = leafmap;
|
||||
uint32_t offs = leafvis[0].visoffs;
|
||||
for (unsigned i = 0; i < num_leafs; i++) {
|
||||
if (leafvis[i].visoffs != offs) {
|
||||
lm++;
|
||||
lm->first_leaf = i;
|
||||
offs = leafvis[i].visoffs;
|
||||
}
|
||||
leafcluster[leafvis[i].leafnum] = lm - leafmap;
|
||||
lm->num_leafs++;
|
||||
}
|
||||
//for (unsigned i = 0; i < num_clusters; i++) {
|
||||
// printf ("%d %d %d\n", i, leafmap[i].first_leaf, leafmap[i].num_leafs);
|
||||
//}
|
||||
printf ("%d\n", num_leafs);
|
||||
}
|
||||
|
||||
static void
|
||||
allocate_data (void)
|
||||
{
|
||||
set_pool = calloc (options.threads, sizeof (set_pool_t));
|
||||
num_leafs = bsp->models[0].visleafs;
|
||||
visbytes = (num_leafs + 7) / 8;
|
||||
base_pvs = malloc (num_leafs * sizeof (set_t));
|
||||
fat_pvs = malloc (num_leafs * sizeof (set_t));
|
||||
cmp_pvs = malloc (num_leafs * sizeof (sizebuf_t));
|
||||
for (unsigned i = 0; i < num_leafs; i++) {
|
||||
base_pvs[i] = (set_t) SET_STATIC_INIT (num_leafs, malloc);
|
||||
fat_pvs[i] = (set_t) SET_STATIC_INIT (num_leafs, malloc);
|
||||
|
||||
base_pvs = malloc (num_clusters * sizeof (set_t));
|
||||
fat_pvs = malloc (num_clusters * sizeof (set_t));
|
||||
cmp_pvs = malloc (num_clusters * sizeof (sizebuf_t));
|
||||
|
||||
uint32_t visbytes = (num_leafs + 7) / 8;
|
||||
// Worst case, RLE can add 50% to the bytes required (alternating zero
|
||||
// and non-zero bytes: 0 x 0 y -> 0 1 x 0 1 y ...). Also, if the map is
|
||||
// very tiny (8 leafs or fewer), there will be only one byte for vis, but
|
||||
// if that byte is 0, an extra byte for the count is required.
|
||||
visbytes = (visbytes * 3) / 2 + 1;
|
||||
for (unsigned i = 0; i < num_clusters; i++) {
|
||||
base_pvs[i] = (set_t) SET_STATIC_INIT (num_leafs - 1, malloc);
|
||||
fat_pvs[i] = (set_t) SET_STATIC_INIT (num_leafs - 1, malloc);
|
||||
cmp_pvs[i] = (sizebuf_t) {
|
||||
.data = malloc ((visbytes * 3) / 2),
|
||||
.maxsize = (visbytes * 3) / 2,
|
||||
.data = malloc (visbytes),
|
||||
.maxsize = visbytes,
|
||||
};
|
||||
}
|
||||
work_leaf = 0;
|
||||
RunThreads (decompress_thread, leaf_progress);
|
||||
work_leaf = 0;
|
||||
RunThreads (fatten_thread, leaf_progress);
|
||||
work_leaf = 0;
|
||||
RunThreads (compress_thread, leaf_progress);
|
||||
printf ("Average leafs visible / fat visible / total: %d / %d / %d\n",
|
||||
(int) (fatstats.pvs_visible / num_leafs),
|
||||
(int) (fatstats.fat_visible / num_leafs), num_leafs);
|
||||
printf ("Compressed fat vis size: %ld\n", fatstats.fat_bytes);
|
||||
}
|
||||
|
||||
static void
|
||||
write_pvs_file (void)
|
||||
{
|
||||
uint32_t offset = sizeof (pvsfile_t) + num_leafs * sizeof (uint32_t);
|
||||
pvsfile_t *pvsfile = malloc (offset + fatstats.fat_bytes);
|
||||
|
||||
|
@ -273,9 +330,12 @@ CalcFatPVS (void)
|
|||
pvsfile->flags |= PVS_UTF8_RLE;
|
||||
}
|
||||
pvsfile->visleafs = num_leafs;
|
||||
for (unsigned i = 0; i < num_leafs; i++) {
|
||||
for (uint32_t i = 0; i < num_clusters; i++) {
|
||||
unsigned size = cmp_pvs[i].cursize;
|
||||
pvsfile->visoffsets[i] = offset;
|
||||
for (uint32_t j = 0; j < leafmap[i].num_leafs; j++) {
|
||||
uint32_t l = leafmap[i].first_leaf + j;
|
||||
pvsfile->visoffsets[leafvis[l].leafnum] = offset;
|
||||
}
|
||||
memcpy ((byte *) pvsfile + offset, cmp_pvs[i].data, size);
|
||||
offset += size;
|
||||
}
|
||||
|
@ -291,3 +351,28 @@ CalcFatPVS (void)
|
|||
Qwrite (f, pvsfile, offset);
|
||||
Qclose (f);
|
||||
}
|
||||
|
||||
void
|
||||
CalcFatPVS (void)
|
||||
{
|
||||
num_leafs = bsp->models[0].visleafs;
|
||||
|
||||
reconstruct_clusters ();
|
||||
allocate_data ();
|
||||
|
||||
work_cluster = 0;
|
||||
RunThreads (decompress_thread, cluster_progress);
|
||||
|
||||
work_cluster = 0;
|
||||
RunThreads (fatten_thread, cluster_progress);
|
||||
|
||||
work_cluster = 0;
|
||||
RunThreads (compress_thread, cluster_progress);
|
||||
|
||||
printf ("Average leafs visible / fat visible / total: %d / %d / %d\n",
|
||||
(int) (fatstats.pvs_visible / num_clusters),
|
||||
(int) (fatstats.fat_visible / num_clusters), num_leafs);
|
||||
printf ("Compressed fat vis size: %ld\n", fatstats.fat_bytes);
|
||||
|
||||
write_pvs_file ();
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ portal_t *portals;
|
|||
cluster_t *clusters;
|
||||
dstring_t *visdata;
|
||||
byte *uncompressed; // [bitbytes * portalleafs]
|
||||
int *leafcluster; // leaf to cluster mappings as read from .prt file
|
||||
uint32_t *leafcluster; // leaf to cluster mappings as read from .prt file
|
||||
|
||||
int *working; // per thread current portal #
|
||||
|
||||
|
@ -1469,7 +1469,7 @@ LoadPortals (char *name)
|
|||
free (clusternums);
|
||||
free (windings);
|
||||
|
||||
leafcluster = calloc (numrealleafs, sizeof (int));
|
||||
leafcluster = calloc (numrealleafs, sizeof (uint32_t));
|
||||
if (read_leafs) {
|
||||
for (unsigned i = 0; i < numrealleafs; i++) {
|
||||
line = Qgetline (f);
|
||||
|
@ -1490,6 +1490,7 @@ generate_pvs (void)
|
|||
QFile *f;
|
||||
|
||||
dstring_t *portalfile = dstring_new ();
|
||||
dstring_t *backupfile = dstring_new ();
|
||||
|
||||
visdata = dstring_new ();
|
||||
|
||||
|
@ -1513,12 +1514,17 @@ generate_pvs (void)
|
|||
|
||||
CalcAmbientSounds ();
|
||||
|
||||
dstring_copystr (backupfile, options.bspfile->str);
|
||||
QFS_SetExtension (backupfile, ".bsp~");
|
||||
|
||||
Qrename (options.bspfile->str, backupfile->str);
|
||||
f = Qopen (options.bspfile->str, "wb");
|
||||
if (!f)
|
||||
Sys_Error ("couldn't open %s for writing.", options.bspfile->str);
|
||||
WriteBSPFile (bsp, f);
|
||||
Qclose (f);
|
||||
|
||||
dstring_delete (backupfile);
|
||||
dstring_delete (portalfile);
|
||||
dstring_delete (visdata);
|
||||
dstring_delete (options.bspfile);
|
||||
|
|
Loading…
Reference in a new issue