summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp75
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h33
2 files changed, 91 insertions, 17 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index b1769c99b..83d8d3d94 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -780,17 +780,30 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
} else if (preserve_contents) {
// If surface parameters changed and we care about keeping the previous data, recreate
// the surface from the old one
- return RecreateSurface(surface, params);
+ UnregisterSurface(surface);
+ Surface new_surface{RecreateSurface(surface, params)};
+ RegisterSurface(new_surface);
+ return new_surface;
} else {
// Delete the old surface before creating a new one to prevent collisions.
UnregisterSurface(surface);
}
}
+ // Try to get a previously reserved surface
+ surface = TryGetReservedSurface(params);
+
// No surface found - create a new one
- surface = std::make_shared<CachedSurface>(params);
- RegisterSurface(surface);
- LoadSurface(surface);
+ if (!surface) {
+ surface = std::make_shared<CachedSurface>(params);
+ ReserveSurface(surface);
+ RegisterSurface(surface);
+ }
+
+ // Only load surface from memory if we care about the contents
+ if (preserve_contents) {
+ LoadSurface(surface);
+ }
return surface;
}
@@ -799,13 +812,18 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
const SurfaceParams& new_params) {
// Verify surface is compatible for blitting
const auto& params{surface->GetSurfaceParams()};
- ASSERT(params.type == new_params.type);
- ASSERT_MSG(params.GetCompressionFactor(params.pixel_format) == 1,
- "Compressed texture reinterpretation is not supported");
// Create a new surface with the new parameters, and blit the previous surface to it
Surface new_surface{std::make_shared<CachedSurface>(new_params)};
+ // If format is unchanged, we can do a faster blit without reinterpreting pixel data
+ if (params.pixel_format == new_params.pixel_format) {
+ BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
+ new_surface->GetSurfaceParams().GetRect(), params.type,
+ read_framebuffer.handle, draw_framebuffer.handle);
+ return new_surface;
+ }
+
auto source_format = GetFormatTuple(params.pixel_format, params.component_type);
auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type);
@@ -818,9 +836,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
- glGetTextureImage(surface->Texture().handle, 0, source_format.format, source_format.type,
- params.SizeInBytes(), nullptr);
-
+ if (source_format.compressed) {
+ glGetCompressedTextureImage(surface->Texture().handle, 0,
+ static_cast<GLsizei>(params.SizeInBytes()), nullptr);
+ } else {
+ glGetTextureImage(surface->Texture().handle, 0, source_format.format, source_format.type,
+ static_cast<GLsizei>(params.SizeInBytes()), nullptr);
+ }
// If the new texture is bigger than the previous one, we need to fill in the rest with data
// from the CPU.
if (params.SizeInBytes() < new_params.SizeInBytes()) {
@@ -846,17 +868,21 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
const auto& dest_rect{new_params.GetRect()};
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle);
- glTextureSubImage2D(
- new_surface->Texture().handle, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()),
- static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, dest_format.type, nullptr);
+ if (dest_format.compressed) {
+ glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ static_cast<GLsizei>(dest_rect.GetWidth()),
+ static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
+ static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
+ } else {
+ glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
+ static_cast<GLsizei>(dest_rect.GetWidth()),
+ static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
+ dest_format.type, nullptr);
+ }
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo.Release();
- // Update cache accordingly
- UnregisterSurface(surface);
- RegisterSurface(new_surface);
-
return new_surface;
}
@@ -931,6 +957,21 @@ void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
surface_cache.erase(search);
}
+void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
+ const auto& surface_reserve_key{SurfaceReserveKey::Create(surface->GetSurfaceParams())};
+ surface_reserve[surface_reserve_key] = surface;
+}
+
+Surface RasterizerCacheOpenGL::TryGetReservedSurface(const SurfaceParams& params) {
+ const auto& surface_reserve_key{SurfaceReserveKey::Create(params)};
+ auto search{surface_reserve.find(surface_reserve_key)};
+ if (search != surface_reserve.end()) {
+ RegisterSurface(search->second);
+ return search->second;
+ }
+ return {};
+}
+
template <typename Map, typename Interval>
constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval));
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index f273152a2..c8c615df2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -11,6 +11,7 @@
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
+#include "common/hash.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -682,6 +683,27 @@ struct SurfaceParams {
u32 cache_height;
};
+}; // namespace OpenGL
+
+/// Hashable variation of SurfaceParams, used for a key in the surface cache
+struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
+ static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
+ SurfaceReserveKey res;
+ res.state = params;
+ return res;
+ }
+};
+namespace std {
+template <>
+struct hash<SurfaceReserveKey> {
+ size_t operator()(const SurfaceReserveKey& k) const {
+ return k.Hash();
+ }
+};
+} // namespace std
+
+namespace OpenGL {
+
class CachedSurface final {
public:
CachedSurface(const SurfaceParams& params);
@@ -752,12 +774,23 @@ private:
/// Remove surface from the cache
void UnregisterSurface(const Surface& surface);
+ /// Reserves a unique surface that can be reused later
+ void ReserveSurface(const Surface& surface);
+
+ /// Tries to get a reserved surface for the specified parameters
+ Surface TryGetReservedSurface(const SurfaceParams& params);
+
/// Increase/decrease the number of surface in pages touching the specified region
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta);
std::unordered_map<Tegra::GPUVAddr, Surface> surface_cache;
PageMap cached_pages;
+ /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
+ /// previously been used. This is to prevent surfaces from being constantly created and
+ /// destroyed when used with different surface parameters.
+ std::unordered_map<SurfaceReserveKey, Surface> surface_reserve;
+
OGLFramebuffer read_framebuffer;
OGLFramebuffer draw_framebuffer;
};