gl_rasterizer_cache: Recycle host textures

Allocating new textures has fairly high driver overhead.
We can avoid some of this by reusing the textures from destroyed surfaces since the game will probably create more textures with the same dimensions and format.
This commit is contained in:
BreadFish64 2021-02-02 20:46:25 -06:00
parent 935e88a580
commit 85e9ba897d
2 changed files with 61 additions and 14 deletions

View file

@ -494,6 +494,10 @@ static bool FillSurface(const Surface& surface, const u8* fill_data,
return true; return true;
} }
CachedSurface::~CachedSurface() {
owner.host_texture_recycler.emplace(*this, std::move(texture));
}
bool CachedSurface::CanFill(const SurfaceParams& dest_surface, bool CachedSurface::CanFill(const SurfaceParams& dest_surface,
SurfaceInterval fill_interval) const { SurfaceInterval fill_interval) const {
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) && if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
@ -1893,12 +1897,17 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
Surface surface = std::make_shared<CachedSurface>(*this); Surface surface = std::make_shared<CachedSurface>(*this);
static_cast<SurfaceParams&>(*surface) = params; static_cast<SurfaceParams&>(*surface) = params;
surface->texture.Create();
surface->gl_buffer.resize(0);
surface->invalid_regions.insert(surface->GetInterval()); surface->invalid_regions.insert(surface->GetInterval());
auto recycled_texture = host_texture_recycler.find(params);
if (recycled_texture == host_texture_recycler.end()) {
surface->texture.Create();
AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format), AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format),
surface->GetScaledWidth(), surface->GetScaledHeight()); surface->GetScaledWidth(), surface->GetScaledHeight());
} else {
surface->texture = std::move(recycled_texture->second);
host_texture_recycler.erase(recycled_texture);
}
return surface; return surface;
} }

View file

@ -36,6 +36,36 @@ class RasterizerCacheOpenGL;
class TextureFilterer; class TextureFilterer;
class FormatReinterpreterOpenGL; class FormatReinterpreterOpenGL;
struct FormatTuple {
GLint internal_format;
GLenum format;
GLenum type;
};
constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
const FormatTuple& GetFormatTuple(SurfaceParams::PixelFormat pixel_format);
struct HostTextureTag {
GLint internal_format;
GLenum format;
u32 width;
u32 height;
HostTextureTag(const SurfaceParams& params) noexcept {
auto format_tuple = GetFormatTuple(params.pixel_format);
internal_format = format_tuple.internal_format;
format = format_tuple.format;
// The type in the format tuple is irrelevant for the tag since the type is only for
// interpreting data on upload/download
width = params.GetScaledWidth();
height = params.GetScaledHeight();
}
bool operator==(const HostTextureTag& rhs) const noexcept {
return std::tie(internal_format, format, width, height) ==
std::tie(rhs.internal_format, rhs.format, rhs.width, rhs.height);
};
};
struct TextureCubeConfig { struct TextureCubeConfig {
PAddr px; PAddr px;
PAddr nx; PAddr nx;
@ -59,6 +89,18 @@ struct TextureCubeConfig {
} // namespace OpenGL } // namespace OpenGL
namespace std { namespace std {
template <>
struct hash<OpenGL::HostTextureTag> {
std::size_t operator()(const OpenGL::HostTextureTag& tag) const noexcept {
std::size_t hash = 0;
boost::hash_combine(hash, tag.format);
boost::hash_combine(hash, tag.internal_format);
boost::hash_combine(hash, tag.width);
boost::hash_combine(hash, tag.height);
return hash;
}
};
template <> template <>
struct hash<OpenGL::TextureCubeConfig> { struct hash<OpenGL::TextureCubeConfig> {
std::size_t operator()(const OpenGL::TextureCubeConfig& config) const noexcept { std::size_t operator()(const OpenGL::TextureCubeConfig& config) const noexcept {
@ -139,6 +181,7 @@ private:
struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface> { struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface> {
CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {} CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {}
~CachedSurface();
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
@ -326,17 +369,12 @@ private:
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache; std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
public: public:
// Textures from destroyed surfaces are stored here to be recyled to reduce allocation overhead
// in the driver
std::unordered_multimap<HostTextureTag, OGLTexture> host_texture_recycler;
std::unique_ptr<TextureFilterer> texture_filterer; std::unique_ptr<TextureFilterer> texture_filterer;
std::unique_ptr<FormatReinterpreterOpenGL> format_reinterpreter; std::unique_ptr<FormatReinterpreterOpenGL> format_reinterpreter;
}; };
struct FormatTuple {
GLint internal_format;
GLenum format;
GLenum type;
};
constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
const FormatTuple& GetFormatTuple(SurfaceParams::PixelFormat pixel_format);
} // namespace OpenGL } // namespace OpenGL