summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_opengl/gl_rasterizer_cache.cpp')
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index fe81ff227..367dbd041 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -1304,9 +1304,98 @@ Surface RasterizerCacheOpenGL::TryGetReservedSurface(const SurfaceParams& params
return {};
}
+bool FindBestMipMap(std::size_t memory, const SurfaceParams params, u32 height, u32& mipmap) {
+ for (u32 i = 0; i < params.max_mip_level; i++)
+ if (memory == params.GetMipmapSingleSize(i) && params.MipHeight(i) == height) {
+ mipmap = i;
+ return true;
+ }
+ return false;
+}
+
+bool FindBestLayer(VAddr addr, const SurfaceParams params, u32 mipmap, u32& layer) {
+ std::size_t size = params.LayerMemorySize();
+ VAddr start = params.addr + params.GetMipmapLevelOffset(mipmap);
+ for (u32 i = 0; i < params.depth; i++) {
+ if (start == addr) {
+ layer = i;
+ return true;
+ }
+ start += size;
+ }
+ return false;
+}
+
+bool LayerFitReinterpretSurface(RasterizerCacheOpenGL& cache, const Surface render_surface,
+ const Surface blitted_surface) {
+ const auto dst_params = blitted_surface->GetSurfaceParams();
+ const auto src_params = render_surface->GetSurfaceParams();
+ u32 level = 0;
+ std::size_t src_memory_size = src_params.size_in_bytes;
+ if (FindBestMipMap(src_memory_size, dst_params, src_params.height, level)) {
+ if (src_params.width == dst_params.MipWidthGobAligned(level) &&
+ src_params.height == dst_params.MipHeight(level) &&
+ src_params.block_height >= dst_params.MipBlockHeight(level)) {
+ u32 slot = 0;
+ if (FindBestLayer(render_surface->GetAddr(), dst_params, level, slot)) {
+ glCopyImageSubData(
+ render_surface->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, 0,
+ 0, blitted_surface->Texture().handle, SurfaceTargetToGL(dst_params.target),
+ level, 0, 0, slot, dst_params.MipWidth(level), dst_params.MipHeight(level), 1);
+ blitted_surface->MarkAsModified(true, cache);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) {
+ VAddr bound1 = blitted_surface->GetAddr() + blitted_surface->GetMemorySize();
+ VAddr bound2 = render_surface->GetAddr() + render_surface->GetMemorySize();
+ if (bound2 > bound1)
+ return true;
+ const auto dst_params = blitted_surface->GetSurfaceParams();
+ const auto src_params = render_surface->GetSurfaceParams();
+ if (dst_params.component_type != src_params.component_type)
+ return true;
+ return false;
+}
+
+bool IsReinterpretInvalidSecond(const Surface render_surface, const Surface blitted_surface) {
+ const auto dst_params = blitted_surface->GetSurfaceParams();
+ const auto src_params = render_surface->GetSurfaceParams();
+ if (dst_params.height > src_params.height && dst_params.width > src_params.width)
+ return false;
+ return true;
+}
+
+bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface,
+ Surface intersect) {
+ if (IsReinterpretInvalid(triggering_surface, intersect)) {
+ Unregister(intersect);
+ return false;
+ }
+ if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) {
+ if (IsReinterpretInvalidSecond(triggering_surface, intersect)) {
+ Unregister(intersect);
+ return false;
+ }
+ FlushObject(intersect);
+ FlushObject(triggering_surface);
+ intersect->MarkForReload(true);
+ }
+ return true;
+}
+
+
void RasterizerCacheOpenGL::NotifyFrameBufferChange(Surface triggering_surface) {
if (triggering_surface == nullptr)
return;
+ Surface intersect = CollideOnReinterpretedSurface(triggering_surface->GetAddr());
+ if (intersect != nullptr) {
+ PartialReinterpretSurface(triggering_surface, intersect);
+ }
}
} // namespace OpenGL