diff options
Diffstat (limited to 'src/video_core')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 89 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 11 | 
2 files changed, 100 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 diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0b4d720e2..2be056f0a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -141,10 +141,18 @@ struct SurfaceParams {          return offset;      } +    std::size_t GetMipmapSingleSize(u32 mip_level) const { +        return InnerMipmapMemorySize(mip_level, false, is_layered); +    } +      u32 MipWidth(u32 mip_level) const {          return std::max(1U, width >> mip_level);      } +    u32 MipWidthGobAligned(u32 mip_level) const { +        return std::max(64U*8U / GetFormatBpp(), width >> mip_level); +    } +      u32 MipHeight(u32 mip_level) const {          return std::max(1U, height >> mip_level);      } @@ -480,6 +488,9 @@ private:      /// When a render target is changed, this method is called with the previous render target      void NotifyFrameBufferChange(Surface triggering_surface); +    // Partialy reinterpret a surface based on a triggering_surface that collides with it. +    bool PartialReinterpretSurface(Surface triggering_surface, Surface intersect); +      /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data      void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);      void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface); | 
