From 04a2bdcda9c189eab7afbfdfa8be36be4f4be480 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Mar 2023 12:59:27 -0700 Subject: [PATCH] DEVELOP: warn about duplicate textures in mainwads Checks on a background thread since the complexity should be O(n^2). The specific texture data it checks shouldn't change after R_LoadTextures so it doesn't need a mutex. --- src/CMakeLists.txt | 1 + src/console.c | 5 ++ src/r_textures.c | 14 ++++ src/r_textures.h | 3 + src/r_textures_dups.cpp | 157 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 src/r_textures_dups.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0313a411..f781ab76a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,6 +77,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 r_things.c r_bbox.c r_textures.c + r_textures_dups.cpp r_patch.cpp r_patchrotation.c r_picformats.c diff --git a/src/console.c b/src/console.c index aac5c2c5f..d5727b23c 100644 --- a/src/console.c +++ b/src/console.c @@ -1873,6 +1873,11 @@ void CON_Drawer(void) CON_RecalcSize(); if (con_curlines <= 0) CON_ClearHUD(); + +#ifdef DEVELOP + // Must be done here so lines don't get reflowed to 320x200 + R_PrintTextureDuplicates(); +#endif } if (con_curlines > 0) diff --git a/src/r_textures.c b/src/r_textures.c index 35d79e872..2f36fca08 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -1136,10 +1136,20 @@ void R_LoadTextures(void) { INT32 i, w; INT32 newtextures = 0; +#ifdef DEVELOP + INT32 maintextures = 0; +#endif for (w = 0; w < numwadfiles; w++) { newtextures += R_CountTextures((UINT16)w); + +#ifdef DEVELOP + if (w == mainwads) + { + maintextures = newtextures; + } +#endif } // If no textures found by this point, bomb out @@ -1154,6 +1164,10 @@ void R_LoadTextures(void) } R_FinishLoadingTextures(newtextures); + +#ifdef DEVELOP + R_CheckTextureDuplicates(0, maintextures); +#endif } void R_LoadTexturesPwad(UINT16 wadnum) diff --git a/src/r_textures.h b/src/r_textures.h index f413e9d0f..066b10fbd 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -109,6 +109,9 @@ INT32 R_TextureNumForName(const char *name); INT32 R_CheckTextureNumForName(const char *name); lumpnum_t R_GetFlatNumForName(const char *name); +void R_CheckTextureDuplicates(INT32 start, INT32 end); +void R_PrintTextureDuplicates(void); + extern INT32 numtextures; #ifdef __cplusplus diff --git a/src/r_textures_dups.cpp b/src/r_textures_dups.cpp new file mode 100644 index 000000000..6b2fd54a7 --- /dev/null +++ b/src/r_textures_dups.cpp @@ -0,0 +1,157 @@ +// RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cxxutil.hpp" +#include "doomstat.h" +#include "r_textures.h" +#include "w_wad.h" + +namespace +{ + +std::unordered_map> g_dups; +std::thread g_dups_thread; + +std::string key8char(const char cstr[8]) +{ + std::string_view view(cstr, 8); + std::string key; + + view = view.substr(0, view.find('\0')); // terminate by '\0' + key.reserve(view.size()); + std::transform( + view.cbegin(), + view.cend(), + std::back_inserter(key), + [](unsigned char c) { return std::toupper(c); } + ); + + return key; +} + +std::string texture_location(const texture_t& tex) +{ + if (tex.type == TEXTURETYPE_SINGLEPATCH) + { + SRB2_ASSERT(tex.patchcount == 1); + + const texpatch_t& texpat = tex.patches[0]; + const wadfile_t& wad = *wadfiles[texpat.wad]; + + return fmt::format( + "'{}/{}'", + std::filesystem::path(wad.filename).filename().string(), + wad.lumpinfo[texpat.lump].fullname + ); + } + else + { + return "TEXTURES"; + } +} + +void print_dup(const texture_t* tex) +{ + // Do not use CONS_Alert because it is not thread-safe + CONS_Printf( + "\x82" + "WARNING:" + "\x80 duplicate texture '%.8s' (%s)\n", + tex->name, + texture_location(*tex).c_str() + ); +} + +}; // namespace + +void R_CheckTextureDuplicates(INT32 start, INT32 end) +{ + SRB2_ASSERT(start >= 0); + SRB2_ASSERT(end <= numtextures); + + auto find_dup = [end](const texture_t* t1, int32_t idx) + { + while (++idx < end) + { + const texture_t* t2 = textures[idx]; + + if (t1->hash == t2->hash && !strncmp(t1->name, t2->name, 8)) + { + break; + } + } + + return idx; + }; + + auto collate_dups = [end, find_dup](int32_t start) + { + const texture_t* t1 = textures[start]; + const std::string key = key8char(t1->name); + + if (g_dups.find(key) != g_dups.end()) + { + return; + } + + int32_t idx = find_dup(t1, start); + + if (idx < end) + { + std::vector& v = g_dups[key]; + + v.push_back(textures[start]); + + do + { + v.push_back(textures[idx]); + } while ((idx = find_dup(t1, idx)) < end); + } + }; + + auto worker = [start, end, collate_dups] + { + for (int32_t i = start; i < end; ++i) + { + collate_dups(i); + } + }; + + SRB2_ASSERT(g_dups_thread.joinable() == false); + g_dups_thread = std::thread(worker); +} + +void R_PrintTextureDuplicates(void) +{ + if (g_dups.empty()) + { + return; + } + + g_dups_thread.join(); + + for (auto [key, v] : g_dups) + { + std::for_each(v.cbegin(), v.cend(), print_dup); + } + + g_dups = {}; +}