From b2842ca7782b3ee808fa70b665b7815a7904d59f Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 9 Mar 2024 11:55:10 +0100 Subject: [PATCH 1/7] faster qsort for rendering --- src/Makefile | 1 + src/hardware/hw_batching.c | 7 +- src/hardware/hw_main.c | 6 +- src/qs22j.c | 222 +++++++++++++++++++++++++++++++++++++ src/qs22j.h | 2 + 5 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 src/qs22j.c create mode 100644 src/qs22j.h diff --git a/src/Makefile b/src/Makefile index bed91c98c..7c742671c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -459,6 +459,7 @@ DBGNAME?=$(EXENAME).debug # SRB2kart kart.o on line 433 below OBJS:=$(i_main_o) \ $(OBJDIR)/comptime.o \ + $(OBJDIR)/qs22j.o \ $(OBJDIR)/string.o \ $(OBJDIR)/d_main.o \ $(OBJDIR)/d_protocol.o \ diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c index 2a59530b4..e0afb6054 100644 --- a/src/hardware/hw_batching.c +++ b/src/hardware/hw_batching.c @@ -15,6 +15,9 @@ #include "hw_main.h" #include "../i_system.h" +#include "../qs22j.h" + + // The texture for the next polygon given to HWR_ProcessPolygon. // Set with HWR_SetCurrentTexture. GLMipmap_t *current_texture = NULL; @@ -264,9 +267,9 @@ void HWR_RenderBatches(void) // sort polygons PS_START_TIMING(ps_hw_batchsorttime); if (cv_grshaders.value && gr_shadersavailable) - qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons); + qs22j(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons); else - qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders); + qs22j(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders); PS_STOP_TIMING(ps_hw_batchsorttime); // sort order // 1. shader diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 7d5e9afa2..dd0d96a41 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -15,6 +15,8 @@ #include "../doomstat.h" +#include "../qs22j.h" + #ifdef HWRENDER #include "hw_main.h" #include "hw_glob.h" @@ -4425,7 +4427,7 @@ static void HWR_SortVisSprites(void) { gr_vsprorder[i] = HWR_GetVisSprite(i); } - qsort(gr_vsprorder, gr_visspritecount, sizeof(gr_vissprite_t*), CompareVisSprites); + qs22j(gr_tvsprorder, gr_tvisspritecount, sizeof(gr_vissprite_t*), CompareVisSprites); } // A drawnode is something that points to a 3D floor, 3D side, or masked @@ -4633,7 +4635,7 @@ void HWR_RenderDrawNodes(void) if (run_end > run_start) // if there are multiple consecutive planes, not just one { // consecutive run of planes found, now sort it - qsort(sortindex + run_start, run_end - run_start + 1, sizeof(INT32), CompareDrawNodePlanes); + qs22j(sortindex + run_start, run_end - run_start + 1, sizeof(INT32), CompareDrawNodePlanes); } run_start = run_end + 1; // continue looking for runs coming right after this one } diff --git a/src/qs22j.c b/src/qs22j.c new file mode 100644 index 000000000..7a1c6f080 --- /dev/null +++ b/src/qs22j.c @@ -0,0 +1,222 @@ +// License: 0BSD +// +// Copyright 2022 Raymond Gardner +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +#include +#include +#include "qs22j.h" + +#define INSORTTHRESH 5 // if n < this use insertion sort + // MUST be >= 2 +#define MIDTHRESH 20 // < this use middle as pivot +#define MEDOF3THRESH 50 // < this use median-of-3 as pivot + // larger subfiles use med-of-3-medians + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef int32_t WORD; +typedef int64_t DWORD; +typedef void *pref_typ; + +// if no uintptr_t, use Bentley-McIlroy trick (undefined behavior) +//#define ptr_to_int(p) (p-(char*)0) +#define ptr_to_int(p) ((uintptr_t)(void *)p) + +#define ASWAP(a, b, t) ((void)(t = a, a = b, b = t)) + +#define SWAP(a, b) if (swap_type) swapf(a, b, size);\ + else do {pref_typ t; ASWAP(*(pref_typ*)(a), *(pref_typ*)(b), t);} while (0) + +#define COMP(a, b) ((*compar)((void *)(a), (void *)(b))) + +static void swapbytes(void *a0, void *b0, size_t n) +{ + char *a = a0, *b = b0, t; + do {ASWAP(*a, *b, t); a++; b++;} while (--n); +} + +static void swapdword(void *a0, void *b0, size_t n) +{ + DWORD *a = a0, *b = b0, t; + ASWAP(*a, *b, t); + (void)n; +} + +static void swapdwords(void *a0, void *b0, size_t n) +{ + DWORD *a = a0, *b = b0, t; + do {ASWAP(*a, *b, t); a++; b++;} while (n -= sizeof(DWORD)); +} + +static void swapword(void *a0, void *b0, size_t n) +{ + WORD *a = a0, *b = b0, t; + ASWAP(*a, *b, t); + (void)n; +} + +static void swapwords(void *a0, void *b0, size_t n) +{ + WORD *a = a0, *b = b0, t; + do {ASWAP(*a, *b, t); a++; b++;} while (n -= sizeof(WORD)); +} + +typedef void (*swapf_typ)(void *, void *, size_t); + +static char *med3(char *a, char *b, char *c, int (*compar)(const void *, const void *)) +{ + return COMP(a, b) < 0 ? + (COMP(b, c) < 0 ? b : COMP(a, c) < 0 ? c : a) : + (COMP(b, c) > 0 ? b : COMP(a, c) > 0 ? c : a); +} + +void qs22j(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)) +{ + char *stack[2*8*sizeof(size_t)], **sp = stack; // stack and stack pointer + char *left = base; // set up char * base pointer + char *limit = left + nmemb * size; // pointer past end of array + char *i, *ii, *j, *jj; // scan pointers + int ki = 0, kj = 0; + int swap_type = 1; + swapf_typ swapf, vecswapf; + + vecswapf = swapf = swapbytes; + if ((ptr_to_int(left) | size) % sizeof(WORD)) { + ; // unaligned or not multple of WORD size; swap bytes + } else if (size == sizeof(DWORD)) { + swapf = swapdword; + vecswapf = swapdwords; + if (size == sizeof(pref_typ)) + swap_type = 0; + } else if (size == sizeof(WORD)) { + swapf = swapword; + vecswapf = swapwords; + if (size == sizeof(pref_typ)) + swap_type = 0; + } else if ((size % sizeof(DWORD)) == 0) { + swapf = vecswapf = swapdwords; + } else if ((size % sizeof(WORD)) == 0) { + swapf = vecswapf = swapwords; + } + for (;;) { + nmemb = (limit - left) / size; + for (i = left + size; i < limit && COMP(i - size, i) <= 0; i += size) + ; + if (i == limit) // if already in order + goto pop; + if (nmemb >= INSORTTHRESH) { // otherwise use insertion sort + char *right = limit - size; + // best so far? fewer compares, a few more swaps + char *p = left + (nmemb / 2) * size; + if (nmemb >= MIDTHRESH) { + char *pleft = left + size; + char *pright = right - size; + if (nmemb >= MEDOF3THRESH) { + size_t k = (nmemb / 8) * size; + pleft = med3(pleft, left + k, left + k * 2, compar); + p = med3(p - k, p, p + k, compar); + pright = med3(right - k * 2, right - k, pright, compar); + } + p = med3(pleft, p, pright, compar); + } + + i = ii = left; // i scans left to right + j = jj = right; // j scans right to left + for (;;) { + + while (i <= j) { + if (i != p && ((ki = COMP(i, p)) >= 0)) { + if (ki) + break; + if (ii == p) + p = i; + else if (i != ii) + SWAP(i, ii); + ii += size; + } + i += size; + } + + while (i < j) { + if (j != p && ((kj = COMP(j, p)) <= 0)) { + if (kj) + break; + if (jj == p) + p = j; + else if (j != jj) + SWAP(j, jj); + jj -= size; + } + j -= size; + } + + if (i >= j) + break; + SWAP(i, j); + i += size; + j -= size; + } + + if (p < i) + i -= size; + if (p != i) + SWAP(p, i); + + ptrdiff_t lessthan = i - ii; + size_t k = min(lessthan, ii - left); + if (k) + vecswapf(left, i - k, k); + ptrdiff_t morethan = jj - i; + k = min(morethan, right - jj); + if (k) + vecswapf(i + size, limit - k, k); + + if (lessthan > morethan) { + if (lessthan > 1) { + sp[0] = left; + sp[1] = left + lessthan; + sp += 2; // increment stack pointer + } + if (morethan <= 1) + goto pop; + left = limit - morethan; + } else { + if (morethan > 1) { + sp[0] = limit - morethan; + sp[1] = limit; + sp += 2; // increment stack pointer + } + if (lessthan <= 1) + goto pop; + limit = left + lessthan; + } + + } else { // else subfile is small, use insertion sort + for (i = left + size; i < limit; i += size) { + for (j = i; j != left && COMP(j - size, j) > 0; j -= size) { + SWAP(j - size, j); + } + } +pop: + if (sp != stack) { // if any entries on stack + sp -= 2; // pop the left and limit + left = sp[0]; + limit = sp[1]; + } else // else stack empty, done + break; + } + } +} diff --git a/src/qs22j.h b/src/qs22j.h new file mode 100644 index 000000000..1a4727e9f --- /dev/null +++ b/src/qs22j.h @@ -0,0 +1,2 @@ +void qs22j(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); -- 2.54.0 From 09b59541713bad704799fb75597359e67ab19f22 Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 9 Mar 2024 07:02:16 +0100 Subject: [PATCH 2/7] use it for other stuff --- src/r_bsp.c | 4 ++-- src/r_things.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/r_bsp.c b/src/r_bsp.c index e0b8039af..594ddb1a0 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -725,7 +725,7 @@ void R_SortPolyObjects(subsector_t *sub) // 03/10/06: only bother if there are actually polys to sort if (numpolys >= 2) { - qsort(po_ptrs, numpolys, sizeof(polyobj_t *), + qs22j(po_ptrs, numpolys, sizeof(polyobj_t *), R_PolyobjCompare); } } @@ -828,7 +828,7 @@ static void R_AddPolyObjects(subsector_t *sub) // render polyobjects for (i = 0; i < numpolys; ++i) { - qsort(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare); + qs22j(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare); for (j = 0; j < po_ptrs[i]->segCount; ++j) R_AddLine(po_ptrs[i]->segs[j]); } diff --git a/src/r_things.c b/src/r_things.c index 98d2dc53a..cfa3199a5 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3284,7 +3284,7 @@ static int skinSortFunc(const void *a, const void *b) //tbh i have no clue what void sortSkinGrid(void) { CONS_Printf("Sorting skin list (%d)...\n", cv_skinselectgridsort.value); - qsort(skinsorted, numskins, sizeof(UINT8), skinSortFunc); + qs22j(skinsorted, numskins, sizeof(UINT8), skinSortFunc); } // -- 2.54.0 From a5184fac136806d80e0b99c985e54cef2117268c Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 9 Mar 2024 07:12:18 +0100 Subject: [PATCH 3/7] fix dumbass compiler warnings + reformat it a bit --- src/qs22j.c | 102 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/src/qs22j.c b/src/qs22j.c index 7a1c6f080..53277521e 100644 --- a/src/qs22j.c +++ b/src/qs22j.c @@ -94,70 +94,104 @@ void qs22j(void *base, size_t nmemb, size_t size, swapf_typ swapf, vecswapf; vecswapf = swapf = swapbytes; - if ((ptr_to_int(left) | size) % sizeof(WORD)) { + if ((ptr_to_int(left) | size) % sizeof(WORD)) + { ; // unaligned or not multple of WORD size; swap bytes - } else if (size == sizeof(DWORD)) { + } + else if (size == sizeof(DWORD)) + { swapf = swapdword; vecswapf = swapdwords; if (size == sizeof(pref_typ)) swap_type = 0; - } else if (size == sizeof(WORD)) { + } + else if (size == sizeof(WORD)) + { swapf = swapword; vecswapf = swapwords; if (size == sizeof(pref_typ)) swap_type = 0; - } else if ((size % sizeof(DWORD)) == 0) { + } + else if ((size % sizeof(DWORD)) == 0) + { swapf = vecswapf = swapdwords; - } else if ((size % sizeof(WORD)) == 0) { + } + else if ((size % sizeof(WORD)) == 0) + { swapf = vecswapf = swapwords; } - for (;;) { + for (;;) + { nmemb = (limit - left) / size; for (i = left + size; i < limit && COMP(i - size, i) <= 0; i += size) ; + if (i == limit) // if already in order goto pop; - if (nmemb >= INSORTTHRESH) { // otherwise use insertion sort + + if (nmemb >= INSORTTHRESH) // otherwise use insertion sort + { char *right = limit - size; // best so far? fewer compares, a few more swaps char *p = left + (nmemb / 2) * size; - if (nmemb >= MIDTHRESH) { + if (nmemb >= MIDTHRESH) + { char *pleft = left + size; char *pright = right - size; - if (nmemb >= MEDOF3THRESH) { + + if (nmemb >= MEDOF3THRESH) + { size_t k = (nmemb / 8) * size; pleft = med3(pleft, left + k, left + k * 2, compar); p = med3(p - k, p, p + k, compar); pright = med3(right - k * 2, right - k, pright, compar); } + p = med3(pleft, p, pright, compar); } i = ii = left; // i scans left to right j = jj = right; // j scans right to left - for (;;) { + for (;;) + { - while (i <= j) { - if (i != p && ((ki = COMP(i, p)) >= 0)) { + while (i <= j) + { + if (i != p && ((ki = COMP(i, p)) >= 0)) + { if (ki) break; + if (ii == p) + { p = i; + } else if (i != ii) + { SWAP(i, ii); + } + ii += size; } i += size; } - while (i < j) { - if (j != p && ((kj = COMP(j, p)) <= 0)) { + while (i < j) + { + if (j != p && ((kj = COMP(j, p)) <= 0)) + { if (kj) break; + if (jj == p) + { p = j; + } else if (j != jj) + { SWAP(j, jj); + } + jj -= size; } j -= size; @@ -165,6 +199,7 @@ void qs22j(void *base, size_t nmemb, size_t size, if (i >= j) break; + SWAP(i, j); i += size; j -= size; @@ -172,50 +207,71 @@ void qs22j(void *base, size_t nmemb, size_t size, if (p < i) i -= size; + if (p != i) + { SWAP(p, i); + } ptrdiff_t lessthan = i - ii; size_t k = min(lessthan, ii - left); + if (k) vecswapf(left, i - k, k); + ptrdiff_t morethan = jj - i; k = min(morethan, right - jj); + if (k) vecswapf(i + size, limit - k, k); - if (lessthan > morethan) { - if (lessthan > 1) { + if (lessthan > morethan) + { + if (lessthan > 1) + { sp[0] = left; sp[1] = left + lessthan; sp += 2; // increment stack pointer } + if (morethan <= 1) goto pop; + left = limit - morethan; - } else { - if (morethan > 1) { + } + else + { + if (morethan > 1) + { sp[0] = limit - morethan; sp[1] = limit; sp += 2; // increment stack pointer } + if (lessthan <= 1) goto pop; + limit = left + lessthan; } - } else { // else subfile is small, use insertion sort - for (i = left + size; i < limit; i += size) { - for (j = i; j != left && COMP(j - size, j) > 0; j -= size) { + } + else // else subfile is small, use insertion sort + { + for (i = left + size; i < limit; i += size) + { + for (j = i; j != left && COMP(j - size, j) > 0; j -= size) + { SWAP(j - size, j); } } pop: - if (sp != stack) { // if any entries on stack + if (sp != stack) // if any entries on stack + { sp -= 2; // pop the left and limit left = sp[0]; limit = sp[1]; - } else // else stack empty, done + } + else // else stack empty, done break; } } -- 2.54.0 From c6641c58ab86ec5c481d8c0a4e977a665970646c Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 9 Mar 2024 07:12:56 +0100 Subject: [PATCH 4/7] implicit declaration of deez nuts --- src/r_bsp.c | 2 ++ src/r_things.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/r_bsp.c b/src/r_bsp.c index 594ddb1a0..97cbca37c 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -21,6 +21,8 @@ #include "p_slopes.h" #include "z_zone.h" // Check R_Prep3DFloors +#include "qs22j.h" + seg_t *curline; side_t *sidedef; line_t *linedef; diff --git a/src/r_things.c b/src/r_things.c index cfa3199a5..f954e0cbf 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -36,6 +36,8 @@ #include "hardware/hw_md2.h" #endif +#include "qs22j.h" + #ifdef PC_DOS #include // for snprintf int snprintf(char *str, size_t n, const char *fmt, ...); -- 2.54.0 From 6489100e13e6103ed5e18133def37fe98128463c Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 9 Mar 2024 11:56:11 +0100 Subject: [PATCH 5/7] oops --- src/hardware/hw_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index dd0d96a41..815a7db66 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -1032,7 +1032,7 @@ void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend if (HWR_UseShader()) { - shader = SHADER_WALL; // wall shader + shader = SHADER_WALL; blendmode |= PF_ColorMapped; } @@ -1043,7 +1043,7 @@ void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend blendmode &= ~PF_Masked; } - HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, shader, false); // wall shader + HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, shader, false); #ifdef WALLSPLATS if (gr_curline->linedef->splats && cv_splats.value) @@ -4427,7 +4427,7 @@ static void HWR_SortVisSprites(void) { gr_vsprorder[i] = HWR_GetVisSprite(i); } - qs22j(gr_tvsprorder, gr_tvisspritecount, sizeof(gr_vissprite_t*), CompareVisSprites); + qs22j(gr_vsprorder, gr_visspritecount, sizeof(gr_vissprite_t*), CompareVisSprites); } // A drawnode is something that points to a 3D floor, 3D side, or masked -- 2.54.0 From 1957712f3828bcff89cafdf242e404748481cd3b Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 10 Mar 2024 20:32:30 +0100 Subject: [PATCH 6/7] faster serverlist sorting replace qsort here too --- src/m_menu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 5cf720be7..baf9aef97 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -50,6 +50,8 @@ #include "p_setup.h" #include "f_finale.h" +#include "qs22j.h" + #ifdef HWRENDER #include "hardware/hw_main.h" #endif @@ -9719,22 +9721,22 @@ void M_SortServerList(void) switch(cv_serversort.value) { case 0: // Ping. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); break; case 1: // Modified state. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); break; case 2: // Most players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); break; case 3: // Least players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); break; case 4: // Max players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); break; case 5: // Gametype. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); break; } #endif -- 2.54.0 From 1152ccbf0915389d0176981aeac175de2afa7539 Mon Sep 17 00:00:00 2001 From: Indev Date: Sun, 17 Mar 2024 14:09:22 +0300 Subject: [PATCH 7/7] Add include guard for qs22j.h --- src/qs22j.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qs22j.h b/src/qs22j.h index 1a4727e9f..95d1c1292 100644 --- a/src/qs22j.h +++ b/src/qs22j.h @@ -1,2 +1,7 @@ +#ifndef __QS22J__ +#define __QS22J__ + void qs22j(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); + +#endif -- 2.54.0