Merge pull request #5710 from BreadFish64/bonk_textures
gl_rasterizer_cache: Remove all fully invalid surfaces from the cache
This commit is contained in:
commit
e6c479f497
|
@ -311,13 +311,21 @@ static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||||
static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width,
|
OGLTexture RasterizerCacheOpenGL::AllocateSurfaceTexture(const FormatTuple& format_tuple, u32 width,
|
||||||
u32 height) {
|
u32 height) {
|
||||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
auto recycled_tex = host_texture_recycler.find({format_tuple, width, height});
|
||||||
|
if (recycled_tex != host_texture_recycler.end()) {
|
||||||
|
OGLTexture texture = std::move(recycled_tex->second);
|
||||||
|
host_texture_recycler.erase(recycled_tex);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
OGLTexture texture;
|
||||||
|
texture.Create();
|
||||||
|
|
||||||
|
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||||
// Keep track of previous texture bindings
|
// Keep track of previous texture bindings
|
||||||
GLuint old_tex = cur_state.texture_units[0].texture_2d;
|
GLuint old_tex = cur_state.texture_units[0].texture_2d;
|
||||||
cur_state.texture_units[0].texture_2d = texture;
|
cur_state.texture_units[0].texture_2d = texture.handle;
|
||||||
cur_state.Apply();
|
cur_state.Apply();
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
@ -332,6 +340,8 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup
|
||||||
// Restore previous texture bindings
|
// Restore previous texture bindings
|
||||||
cur_state.texture_units[0].texture_2d = old_tex;
|
cur_state.texture_units[0].texture_2d = old_tex;
|
||||||
cur_state.Apply();
|
cur_state.Apply();
|
||||||
|
|
||||||
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AllocateTextureCube(GLuint texture, const FormatTuple& format_tuple, u32 width) {
|
static void AllocateTextureCube(GLuint texture, const FormatTuple& format_tuple, u32 width) {
|
||||||
|
@ -494,6 +504,17 @@ static bool FillSurface(const Surface& surface, const u8* fill_data,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CachedSurface::~CachedSurface() {
|
||||||
|
if (texture.handle) {
|
||||||
|
auto tag = is_custom ? HostTextureTag{GetFormatTuple(PixelFormat::RGBA8),
|
||||||
|
custom_tex_info.width, custom_tex_info.height}
|
||||||
|
: HostTextureTag{GetFormatTuple(pixel_format), GetScaledWidth(),
|
||||||
|
GetScaledHeight()};
|
||||||
|
|
||||||
|
owner.host_texture_recycler.emplace(tag, 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) &&
|
||||||
|
@ -812,12 +833,11 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
|
||||||
x0 = 0;
|
x0 = 0;
|
||||||
y0 = 0;
|
y0 = 0;
|
||||||
|
|
||||||
unscaled_tex.Create();
|
|
||||||
if (is_custom) {
|
if (is_custom) {
|
||||||
AllocateSurfaceTexture(unscaled_tex.handle, GetFormatTuple(PixelFormat::RGBA8),
|
unscaled_tex = owner.AllocateSurfaceTexture(
|
||||||
custom_tex_info.width, custom_tex_info.height);
|
GetFormatTuple(PixelFormat::RGBA8), custom_tex_info.width, custom_tex_info.height);
|
||||||
} else {
|
} else {
|
||||||
AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
|
unscaled_tex = owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), rect.GetHeight());
|
||||||
}
|
}
|
||||||
target_tex = unscaled_tex.handle;
|
target_tex = unscaled_tex.handle;
|
||||||
}
|
}
|
||||||
|
@ -832,7 +852,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
|
||||||
ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
|
ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
|
||||||
if (is_custom) {
|
if (is_custom) {
|
||||||
if (res_scale == 1) {
|
if (res_scale == 1) {
|
||||||
AllocateSurfaceTexture(texture.handle, GetFormatTuple(PixelFormat::RGBA8),
|
texture = owner.AllocateSurfaceTexture(GetFormatTuple(PixelFormat::RGBA8),
|
||||||
custom_tex_info.width, custom_tex_info.height);
|
custom_tex_info.width, custom_tex_info.height);
|
||||||
cur_state.texture_units[0].texture_2d = texture.handle;
|
cur_state.texture_units[0].texture_2d = texture.handle;
|
||||||
cur_state.Apply();
|
cur_state.Apply();
|
||||||
|
@ -910,11 +930,9 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint
|
||||||
scaled_rect.right *= res_scale;
|
scaled_rect.right *= res_scale;
|
||||||
scaled_rect.bottom *= res_scale;
|
scaled_rect.bottom *= res_scale;
|
||||||
|
|
||||||
OGLTexture unscaled_tex;
|
|
||||||
unscaled_tex.Create();
|
|
||||||
|
|
||||||
Common::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
|
Common::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
|
||||||
AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
|
OGLTexture unscaled_tex =
|
||||||
|
owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), rect.GetHeight());
|
||||||
BlitTextures(texture.handle, scaled_rect, unscaled_tex.handle, unscaled_tex_rect, type,
|
BlitTextures(texture.handle, scaled_rect, unscaled_tex.handle, unscaled_tex_rect, type,
|
||||||
read_fb_handle, draw_fb_handle);
|
read_fb_handle, draw_fb_handle);
|
||||||
|
|
||||||
|
@ -1734,15 +1752,12 @@ bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface,
|
||||||
if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 &&
|
if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 &&
|
||||||
surface->res_scale == resolution_scale_factor) {
|
surface->res_scale == resolution_scale_factor) {
|
||||||
// The destination surface is either a framebuffer, or a filtered texture.
|
// The destination surface is either a framebuffer, or a filtered texture.
|
||||||
OGLTexture tmp_tex;
|
|
||||||
tmp_tex.Create();
|
|
||||||
// Create an intermediate surface to convert to before blitting to the
|
// Create an intermediate surface to convert to before blitting to the
|
||||||
// destination.
|
// destination.
|
||||||
Common::Rectangle<u32> tmp_rect{0, dest_rect.GetHeight() / resolution_scale_factor,
|
Common::Rectangle<u32> tmp_rect{0, dest_rect.GetHeight() / resolution_scale_factor,
|
||||||
dest_rect.GetWidth() / resolution_scale_factor, 0};
|
dest_rect.GetWidth() / resolution_scale_factor, 0};
|
||||||
AllocateSurfaceTexture(tmp_tex.handle,
|
OGLTexture tmp_tex = AllocateSurfaceTexture(
|
||||||
GetFormatTuple(reinterpreter->first.dst_format),
|
GetFormatTuple(reinterpreter->first.dst_format), tmp_rect.right, tmp_rect.top);
|
||||||
tmp_rect.right, tmp_rect.top);
|
|
||||||
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
|
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
|
||||||
read_framebuffer.handle, tmp_tex.handle,
|
read_framebuffer.handle, tmp_tex.handle,
|
||||||
tmp_rect, draw_framebuffer.handle);
|
tmp_rect, draw_framebuffer.handle);
|
||||||
|
@ -1857,9 +1872,9 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface
|
||||||
cached_surface->invalid_regions.insert(interval);
|
cached_surface->invalid_regions.insert(interval);
|
||||||
cached_surface->InvalidateAllWatcher();
|
cached_surface->InvalidateAllWatcher();
|
||||||
|
|
||||||
// Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
|
// If the surface has no salvageable data it should be removed from the cache to avoid
|
||||||
if (cached_surface->type == SurfaceType::Fill &&
|
// clogging the data structure
|
||||||
cached_surface->IsSurfaceFullyInvalid()) {
|
if (cached_surface->IsSurfaceFullyInvalid()) {
|
||||||
remove_surfaces.emplace(cached_surface);
|
remove_surfaces.emplace(cached_surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1892,12 +1907,11 @@ 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());
|
||||||
AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format),
|
|
||||||
surface->GetScaledWidth(), surface->GetScaledHeight());
|
surface->texture =
|
||||||
|
AllocateSurfaceTexture(GetFormatTuple(surface->pixel_format), surface->GetScaledWidth(),
|
||||||
|
surface->GetScaledHeight());
|
||||||
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,27 @@ 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 {
|
||||||
|
FormatTuple format_tuple;
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
bool operator==(const HostTextureTag& rhs) const noexcept {
|
||||||
|
return std::tie(format_tuple.format, format_tuple.internal_format, width, height) ==
|
||||||
|
std::tie(rhs.format_tuple.format, rhs.format_tuple.internal_format, rhs.width,
|
||||||
|
rhs.height);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
struct TextureCubeConfig {
|
struct TextureCubeConfig {
|
||||||
PAddr px;
|
PAddr px;
|
||||||
PAddr nx;
|
PAddr nx;
|
||||||
|
@ -59,6 +80,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_tuple.format);
|
||||||
|
boost::hash_combine(hash, tag.format_tuple.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 +172,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 +360,14 @@ private:
|
||||||
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
|
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
OGLTexture AllocateSurfaceTexture(const FormatTuple& format_tuple, u32 width, u32 height);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
Reference in New Issue