diff --git a/tools/qfvis/include/vis.h b/tools/qfvis/include/vis.h index e1ccefe16..cc321619d 100644 --- a/tools/qfvis/include/vis.h +++ b/tools/qfvis/include/vis.h @@ -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); diff --git a/tools/qfvis/source/fatpvs.c b/tools/qfvis/source/fatpvs.c index 2ea925fee..d832adfe9 100644 --- a/tools/qfvis/source/fatpvs.c +++ b/tools/qfvis/source/fatpvs.c @@ -32,6 +32,7 @@ #include #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 (); +} diff --git a/tools/qfvis/source/qfvis.c b/tools/qfvis/source/qfvis.c index e49569649..bf600f98f 100644 --- a/tools/qfvis/source/qfvis.c +++ b/tools/qfvis/source/qfvis.c @@ -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);