mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 06:05:40 +12:00
107 lines
3.4 KiB
C++
107 lines
3.4 KiB
C++
#pragma once
|
|
#include <functional>
|
|
#include <optional>
|
|
#include "surfaces.hpp"
|
|
#include "textures.hpp"
|
|
|
|
// Surface cache class that can fit "capacity" instances of the "SurfaceType" class of surfaces
|
|
// SurfaceType *must* have all of the following.
|
|
// - An "allocate" function that allocates GL resources for the surfaces. On overflow it will panic
|
|
// if evictOnOverflow is false, or kick out the oldest item if it is true (like a ring buffer)
|
|
// - A "free" function that frees up all resources the surface is taking up
|
|
// - A "matches" function that, when provided with a SurfaceType object reference
|
|
// Will tell us if the 2 surfaces match (Only as far as location in VRAM, format, dimensions, etc)
|
|
// Are concerned. We could overload the == operator, but that implies full equality
|
|
// Including equality of the allocated OpenGL resources, which we don't want
|
|
// - A "valid" member that tells us whether the function is still valid or not
|
|
// - A "location" member which tells us which location in 3DS memory this surface occupies
|
|
template <typename SurfaceType, size_t capacity, bool evictOnOverflow = false>
|
|
class SurfaceCache {
|
|
// Vanilla std::optional can't hold actual references
|
|
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
|
|
|
|
size_t size;
|
|
size_t evictionIndex;
|
|
std::array<SurfaceType, capacity> buffer;
|
|
|
|
public:
|
|
void reset() {
|
|
size = 0;
|
|
evictionIndex = 0;
|
|
for (auto& e : buffer) { // Free the VRAM of all surfaces
|
|
e.free();
|
|
}
|
|
}
|
|
|
|
OptionalRef find(SurfaceType& other) {
|
|
for (auto& e : buffer) {
|
|
if (e.matches(other) && e.valid)
|
|
return e;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
OptionalRef findFromAddress(u32 address) {
|
|
for (auto& e : buffer) {
|
|
if (e.location <= address && e.location + e.sizeInBytes() > address && e.valid)
|
|
return e;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Adds a surface object to the cache and returns it
|
|
SurfaceType& add(const SurfaceType& surface) {
|
|
if (size >= capacity) {
|
|
if constexpr (evictOnOverflow) { // Do a ring buffer if evictOnOverflow is true
|
|
if constexpr (std::is_same<SurfaceType, ColourBuffer>() || std::is_same<SurfaceType, DepthBuffer>()) {
|
|
Helpers::panicDev("Colour/Depth buffer cache overflowed, currently stubbed to do a ring-buffer. This might snap in half");
|
|
}
|
|
|
|
auto& e = buffer[evictionIndex];
|
|
evictionIndex = (evictionIndex + 1) % capacity;
|
|
|
|
e.valid = false;
|
|
e.free();
|
|
e = surface;
|
|
e.allocate();
|
|
return e;
|
|
} else {
|
|
Helpers::panic("Surface cache full! Add emptying!");
|
|
}
|
|
}
|
|
|
|
size++;
|
|
|
|
// Find an existing surface we completely invalidate and overwrite it with the new surface
|
|
for (auto& e : buffer) {
|
|
if (e.valid && e.range.lower() >= surface.range.lower() && e.range.upper() <= surface.range.upper()) {
|
|
e.free();
|
|
e = surface;
|
|
e.allocate();
|
|
return e;
|
|
}
|
|
}
|
|
|
|
// Find an invalid entry in the cache and overwrite it with the new surface
|
|
for (auto& e : buffer) {
|
|
if (!e.valid) {
|
|
e = surface;
|
|
e.allocate();
|
|
return e;
|
|
}
|
|
}
|
|
|
|
// This should be unreachable but helps to panic anyways
|
|
Helpers::panic("Couldn't add surface to cache\n");
|
|
}
|
|
|
|
SurfaceType& operator[](size_t i) {
|
|
return buffer[i];
|
|
}
|
|
|
|
const SurfaceType& operator[](size_t i) const {
|
|
return buffer[i];
|
|
}
|
|
};
|