Use multimap for indexing surfaces (#771)
Some checks are pending
Android Build / x64 (release) (push) Waiting to run
Android Build / arm64 (release) (push) Waiting to run
HTTP Server Build / build (push) Waiting to run
Hydra Core Build / Windows (push) Waiting to run
Hydra Core Build / MacOS (push) Waiting to run
Hydra Core Build / Linux (push) Waiting to run
Hydra Core Build / Android-x64 (push) Waiting to run
Hydra Core Build / ARM-Libretro (push) Waiting to run
Linux AppImage Build / build (push) Waiting to run
Linux Build / build (push) Waiting to run
MacOS Build / MacOS-arm64 (push) Waiting to run
MacOS Build / MacOS-x86_64 (push) Waiting to run
MacOS Build / MacOS-Universal (push) Blocked by required conditions
Qt Build / Windows (push) Waiting to run
Qt Build / MacOS-arm64 (push) Waiting to run
Qt Build / MacOS-x86_64 (push) Waiting to run
Qt Build / MacOS-Universal (push) Blocked by required conditions
Qt Build / Linux (push) Waiting to run
Windows Build / build (push) Waiting to run
iOS Simulator Build / build (push) Waiting to run

* Use an std::map for faster texture cache lookup

* Surface cache: Use map instead of multimap, optimize `find` to perform tree scan

* Add comments
This commit is contained in:
wheremyfoodat 2025-07-04 14:23:08 +03:00 committed by GitHub
parent 80840b6c5e
commit d06f600b3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 38 deletions

View file

@ -1,6 +1,9 @@
#pragma once #pragma once
#include <array>
#include <functional> #include <functional>
#include <map>
#include <optional> #include <optional>
#include "surfaces.hpp" #include "surfaces.hpp"
#include "textures.hpp" #include "textures.hpp"
@ -17,41 +20,69 @@
// - A "location" member which tells us which location in 3DS memory this surface occupies // - A "location" member which tells us which location in 3DS memory this surface occupies
template <typename SurfaceType, size_t capacity, bool evictOnOverflow = false> template <typename SurfaceType, size_t capacity, bool evictOnOverflow = false>
class SurfaceCache { class SurfaceCache {
// Vanilla std::optional can't hold actual references // Vanilla std::optional can't hold actual references
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>; using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
size_t size; size_t size = 0;
size_t evictionIndex; size_t evictionIndex = 0;
std::array<SurfaceType, capacity> buffer; std::array<SurfaceType, capacity> buffer;
public: // Map from address to a surface in the above buffer.
void reset() { // Several cached surfaces may have the same starting address, so we use a multimap.
size = 0; std::multimap<u32, SurfaceType*> surfaceMap;
evictionIndex = 0;
for (auto& e : buffer) { // Free the VRAM of all surfaces
e.free();
}
}
OptionalRef find(SurfaceType& other) { // Adds a surface to our map
for (auto& e : buffer) { void indexSurface(SurfaceType& surface) { surfaceMap.emplace(surface.location, &surface); }
if (e.matches(other) && e.valid)
return e;
}
return std::nullopt; // Removes a surface from our map
} void unindexSurface(SurfaceType& surface) {
auto range = surfaceMap.equal_range(surface.location);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == &surface) {
surfaceMap.erase(it);
break;
}
}
}
OptionalRef findFromAddress(u32 address) { public:
for (auto& e : buffer) { void reset() {
if (e.location <= address && e.location + e.sizeInBytes() > address && e.valid) size = 0;
return e; evictionIndex = 0;
} surfaceMap.clear();
return std::nullopt; // Free the memory of all surfaces
} for (auto& e : buffer) {
e.free();
e.valid = false;
}
}
// Adds a surface object to the cache and returns it // Use our map to only scan the surfaces with the same starting location
OptionalRef find(SurfaceType& other) {
auto range = surfaceMap.equal_range(other.location);
for (auto it = range.first; it != range.second; ++it) {
SurfaceType* candidate = it->second;
if (candidate->valid && candidate->matches(other)) {
return *candidate;
}
}
return std::nullopt;
}
OptionalRef findFromAddress(u32 address) {
for (auto it = surfaceMap.begin(); it != surfaceMap.end(); ++it) {
SurfaceType* surface = it->second;
if (surface->valid && surface->location <= address && surface->location + surface->sizeInBytes() > address) {
return *surface;
}
}
return std::nullopt;
}
// Adds a surface object to the cache and returns it
SurfaceType& add(const SurfaceType& surface) { SurfaceType& add(const SurfaceType& surface) {
if (size >= capacity) { if (size >= capacity) {
if constexpr (evictOnOverflow) { // Do a ring buffer if evictOnOverflow is true if constexpr (evictOnOverflow) { // Do a ring buffer if evictOnOverflow is true
@ -60,12 +91,14 @@ public:
} }
auto& e = buffer[evictionIndex]; auto& e = buffer[evictionIndex];
unindexSurface(e);
evictionIndex = (evictionIndex + 1) % capacity; evictionIndex = (evictionIndex + 1) % capacity;
e.valid = false; e.valid = false;
e.free(); e.free();
e = surface; e = surface;
e.allocate(); e.allocate();
indexSurface(e);
return e; return e;
} else { } else {
Helpers::panic("Surface cache full! Add emptying!"); Helpers::panic("Surface cache full! Add emptying!");
@ -74,12 +107,14 @@ public:
size++; size++;
// Find an existing surface we completely invalidate and overwrite it with the new surface // See if any existing surface fully overlaps
for (auto& e : buffer) { for (auto& e : buffer) {
if (e.valid && e.range.lower() >= surface.range.lower() && e.range.upper() <= surface.range.upper()) { if (e.valid && e.range.lower() >= surface.range.lower() && e.range.upper() <= surface.range.upper()) {
unindexSurface(e);
e.free(); e.free();
e = surface; e = surface;
e.allocate(); e.allocate();
indexSurface(e);
return e; return e;
} }
} }
@ -89,6 +124,7 @@ public:
if (!e.valid) { if (!e.valid) {
e = surface; e = surface;
e.allocate(); e.allocate();
indexSurface(e);
return e; return e;
} }
} }
@ -97,11 +133,6 @@ public:
Helpers::panic("Couldn't add surface to cache\n"); Helpers::panic("Couldn't add surface to cache\n");
} }
SurfaceType& operator[](size_t i) { SurfaceType& operator[](size_t i) { return buffer[i]; }
return buffer[i]; const SurfaceType& operator[](size_t i) const { return buffer[i]; }
}
const SurfaceType& operator[](size_t i) const {
return buffer[i];
}
}; };