sm64coopdx/data/dynos_misc.cpp
Isaac0-dev 1e4ede799b
Some checks are pending
Build coop / build-linux (push) Waiting to run
Build coop / build-steamos (push) Waiting to run
Build coop / build-windows-opengl (push) Waiting to run
Build coop / build-windows-directx (push) Waiting to run
Build coop / build-macos-arm (push) Waiting to run
Build coop / build-macos-intel (push) Waiting to run
massive performance improvements for add_scroll_target (#1219)
Loading the scroll targets was probably the slowest parts to loading a romhack.
The reason for this is that many romhacks can have thousands of calls to `add_scroll_target`.
So, for 4,107 calls to `add_scroll_target`, the time went from ~4.5759 seconds to ~0.0173 seconds in total. 
Changes made:
- Previously, simply finding the material data to scroll was rather slow due to using a linear sub string search across all vertices in all levels. To speed this up, I added a cache.
The cache bypasses checking every level by storing the exact string (rather than the substring) in a hashmap, so lookups become a simple case of a string lookup as a key in the map. It falls back to the full lookup if the cache doesn't hit.
- Changed the vertex buffer management in `scroll_targets.c` to behave closer to a modern dynamic array, where buffer size is doubled each time a new vertex buffer is added, to reduce the number of allocations performed.
2026-05-08 20:57:06 +10:00

69 lines
2.1 KiB
C++

#include <unordered_map>
#include <string_view>
#include <string>
#include "dynos.cpp.h"
extern "C" {
#include "game/scroll_targets.h"
}
//
// Scroll Targets
//
static void DynOS_Add_Scroll_Target_Match(u32 index, const char* name, u32 offset, u32 size, DataNode<Vtx>* node) {
if (offset >= node->mSize) { return; }
u32 finalSize = (size > 0 && size <= (node->mSize - offset)) ? size : (node->mSize - offset);
add_vtx_scroll_target(
index,
&node->mData[offset],
finalSize,
offset > 0
);
}
void DynOS_Add_Scroll_Target(u32 index, const char* name, u32 offset, u32 size) {
static std::unordered_multimap<std::string_view, DataNode<Vtx>*> sVertexNodesExactMap;
static std::vector<GfxData*> sLvlGfxDataCache; // cache existing level pointers to know when to rebuild
auto& lvlArray = DynOS_Lvl_GetArray();
// Check if cache needs rebuilding
bool rebuild = (lvlArray.size() != sLvlGfxDataCache.size());
if (!rebuild) {
for (size_t i = 0; i < lvlArray.size(); ++i) {
if (lvlArray[i].second != sLvlGfxDataCache[i]) {
rebuild = true;
break;
}
}
}
if (rebuild) {
sVertexNodesExactMap.clear();
sLvlGfxDataCache.clear();
for (const auto& lvlPair : lvlArray) {
sLvlGfxDataCache.push_back(lvlPair.second);
for (const auto& node : lvlPair.second->mVertices) {
sVertexNodesExactMap.emplace(std::string_view(node->mName.begin(), node->mName.Length()), node);
}
}
}
// Check exact match
auto range = sVertexNodesExactMap.equal_range(name);
if (range.first != range.second) {
for (auto it = range.first; it != range.second; ++it) {
DynOS_Add_Scroll_Target_Match(index, name, offset, size, it->second);
}
return;
}
// Fallback to substring search
for (const auto& lvlPair : lvlArray) {
for (const auto& node : lvlPair.second->mVertices) {
if (node->mName.Find(name) >= 0) {
DynOS_Add_Scroll_Target_Match(index, name, offset, size, node);
}
}
}
}