Panda3DS/include/renderer_gl/surface_cache.hpp
Sky 17b08a25fa [GPU] Converted Depth/Color Surfaces to a ring buffer
Additionally made the surface cache search hit for any address that lies in the surface.

This should allow multiple races to be done in Mario Kart and fixes the intro video.
2023-07-06 11:18:14 -07:00

87 lines
3 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 evict_on_overflow is false, or kick out the oldest item if it is true.
// - 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 evict_on_overflow=false>
class SurfaceCache {
// Vanilla std::optional can't hold actual references
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
static_assert(std::is_same<SurfaceType, ColourBuffer>() || std::is_same<SurfaceType, DepthBuffer>() ||
std::is_same<SurfaceType, Texture>(), "Invalid surface type");
size_t size;
size_t eviction_index;
std::array<SurfaceType, capacity> buffer;
public:
void reset() {
size = 0;
eviction_index=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(evict_on_overflow){
auto & e = buffer[eviction_index % size];
eviction_index++;
e.free();
e.valid = false;
e = surface;
e.allocate();
return e;
}else Helpers::panic("Surface cache full! Add emptying!");
}
size++;
// 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];
}
};