Procházet zdrojové kódy

Merge pull request #2121 from FernandoS27/texception2

Improve the Accuracy of the Rasterizer Cache through a Texception Pass
bunnei před 7 roky
rodič
revize
1f5d6a8fed

+ 9 - 9
src/video_core/rasterizer_cache.h

@@ -129,6 +129,15 @@ protected:
         return ++modified_ticks;
         return ++modified_ticks;
     }
     }
 
 
+    /// Flushes the specified object, updating appropriate cache state as needed
+    void FlushObject(const T& object) {
+        if (!object->IsDirty()) {
+            return;
+        }
+        object->Flush();
+        object->MarkAsModified(false, *this);
+    }
+
 private:
 private:
     /// Returns a list of cached objects from the specified memory region, ordered by access time
     /// Returns a list of cached objects from the specified memory region, ordered by access time
     std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
     std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
@@ -154,15 +163,6 @@ private:
         return objects;
         return objects;
     }
     }
 
 
-    /// Flushes the specified object, updating appropriate cache state as needed
-    void FlushObject(const T& object) {
-        if (!object->IsDirty()) {
-            return;
-        }
-        object->Flush();
-        object->MarkAsModified(false, *this);
-    }
-
     using ObjectSet = std::set<T>;
     using ObjectSet = std::set<T>;
     using ObjectCache = std::unordered_map<VAddr, T>;
     using ObjectCache = std::unordered_map<VAddr, T>;
     using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;
     using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;

+ 4 - 0
src/video_core/renderer_opengl/gl_rasterizer.cpp

@@ -738,9 +738,13 @@ void RasterizerOpenGL::DrawArrays() {
     shader_program_manager->ApplyTo(state);
     shader_program_manager->ApplyTo(state);
     state.Apply();
     state.Apply();
 
 
+    res_cache.SignalPreDrawCall();
+
     // Execute draw call
     // Execute draw call
     params.DispatchDraw();
     params.DispatchDraw();
 
 
+    res_cache.SignalPostDrawCall();
+
     // Disable scissor test
     // Disable scissor test
     state.viewports[0].scissor.enabled = false;
     state.viewports[0].scissor.enabled = false;
 
 

+ 118 - 6
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp

@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
 #include <algorithm>
 #include <algorithm>
+#include <optional>
 #include <glad/glad.h>
 #include <glad/glad.h>
 
 
 #include "common/alignment.h"
 #include "common/alignment.h"
@@ -549,6 +550,8 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
     // alternatives. This signals a bug on those functions.
     // alternatives. This signals a bug on those functions.
     const auto width = static_cast<GLsizei>(params.MipWidth(0));
     const auto width = static_cast<GLsizei>(params.MipWidth(0));
     const auto height = static_cast<GLsizei>(params.MipHeight(0));
     const auto height = static_cast<GLsizei>(params.MipHeight(0));
+    memory_size = params.MemorySize();
+    reinterpreted = false;
 
 
     const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
     const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
     gl_internal_format = format_tuple.internal_format;
     gl_internal_format = format_tuple.internal_format;
@@ -970,22 +973,23 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
     ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
     ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
 
 
     if (index >= regs.rt_control.count) {
     if (index >= regs.rt_control.count) {
-        return last_color_buffers[index] = {};
+        return current_color_buffers[index] = {};
     }
     }
 
 
     if (regs.rt[index].Address() == 0 || regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
     if (regs.rt[index].Address() == 0 || regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
-        return last_color_buffers[index] = {};
+        return current_color_buffers[index] = {};
     }
     }
 
 
     const SurfaceParams color_params{SurfaceParams::CreateForFramebuffer(index)};
     const SurfaceParams color_params{SurfaceParams::CreateForFramebuffer(index)};
 
 
-    return last_color_buffers[index] = GetSurface(color_params, preserve_contents);
+    return current_color_buffers[index] = GetSurface(color_params, preserve_contents);
 }
 }
 
 
 void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
 void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
     surface->LoadGLBuffer();
     surface->LoadGLBuffer();
     surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
     surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
     surface->MarkAsModified(false, *this);
     surface->MarkAsModified(false, *this);
+    surface->MarkForReload(false);
 }
 }
 
 
 Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
 Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -997,18 +1001,23 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
     Surface surface{TryGet(params.addr)};
     Surface surface{TryGet(params.addr)};
     if (surface) {
     if (surface) {
         if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
         if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
-            // Use the cached surface as-is
+            // Use the cached surface as-is unless it's not synced with memory
+            if (surface->MustReload())
+                LoadSurface(surface);
             return surface;
             return surface;
         } else if (preserve_contents) {
         } else if (preserve_contents) {
             // If surface parameters changed and we care about keeping the previous data, recreate
             // If surface parameters changed and we care about keeping the previous data, recreate
             // the surface from the old one
             // the surface from the old one
             Surface new_surface{RecreateSurface(surface, params)};
             Surface new_surface{RecreateSurface(surface, params)};
-            Unregister(surface);
+            UnregisterSurface(surface);
             Register(new_surface);
             Register(new_surface);
+            if (new_surface->IsUploaded()) {
+                RegisterReinterpretSurface(new_surface);
+            }
             return new_surface;
             return new_surface;
         } else {
         } else {
             // Delete the old surface before creating a new one to prevent collisions.
             // Delete the old surface before creating a new one to prevent collisions.
-            Unregister(surface);
+            UnregisterSurface(surface);
         }
         }
     }
     }
 
 
@@ -1290,4 +1299,107 @@ Surface RasterizerCacheOpenGL::TryGetReservedSurface(const SurfaceParams& params
     return {};
     return {};
 }
 }
 
 
+static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfaceParams params,
+                                            u32 height) {
+    for (u32 i = 0; i < params.max_mip_level; i++) {
+        if (memory == params.GetMipmapSingleSize(i) && params.MipHeight(i) == height) {
+            return {i};
+        }
+    }
+    return {};
+}
+
+static std::optional<u32> TryFindBestLayer(VAddr addr, const SurfaceParams params, u32 mipmap) {
+    const std::size_t size = params.LayerMemorySize();
+    VAddr start = params.addr + params.GetMipmapLevelOffset(mipmap);
+    for (u32 i = 0; i < params.depth; i++) {
+        if (start == addr) {
+            return {i};
+        }
+        start += size;
+    }
+    return {};
+}
+
+static 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();
+    const std::size_t src_memory_size = src_params.size_in_bytes;
+    const std::optional<u32> level =
+        TryFindBestMipMap(src_memory_size, dst_params, src_params.height);
+    if (level.has_value()) {
+        if (src_params.width == dst_params.MipWidthGobAligned(*level) &&
+            src_params.height == dst_params.MipHeight(*level) &&
+            src_params.block_height >= dst_params.MipBlockHeight(*level)) {
+            const std::optional<u32> slot =
+                TryFindBestLayer(render_surface->GetAddr(), dst_params, *level);
+            if (slot.has_value()) {
+                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;
+}
+
+static bool IsReinterpretInvalid(const Surface render_surface, const Surface blitted_surface) {
+    const VAddr bound1 = blitted_surface->GetAddr() + blitted_surface->GetMemorySize();
+    const 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();
+    return (dst_params.component_type != src_params.component_type);
+}
+
+static bool IsReinterpretInvalidSecond(const Surface render_surface,
+                                       const Surface blitted_surface) {
+    const auto& dst_params = blitted_surface->GetSurfaceParams();
+    const auto& src_params = render_surface->GetSurfaceParams();
+    return (dst_params.height > src_params.height && dst_params.width > src_params.width);
+}
+
+bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface,
+                                                      Surface intersect) {
+    if (IsReinterpretInvalid(triggering_surface, intersect)) {
+        UnregisterSurface(intersect);
+        return false;
+    }
+    if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) {
+        if (IsReinterpretInvalidSecond(triggering_surface, intersect)) {
+            UnregisterSurface(intersect);
+            return false;
+        }
+        FlushObject(intersect);
+        FlushObject(triggering_surface);
+        intersect->MarkForReload(true);
+    }
+    return true;
+}
+
+void RasterizerCacheOpenGL::SignalPreDrawCall() {
+    if (texception && GLAD_GL_ARB_texture_barrier) {
+        glTextureBarrier();
+    }
+    texception = false;
+}
+
+void RasterizerCacheOpenGL::SignalPostDrawCall() {
+    for (u32 i = 0; i < Maxwell::NumRenderTargets; i++) {
+        if (current_color_buffers[i] != nullptr) {
+            Surface intersect = CollideOnReinterpretedSurface(current_color_buffers[i]->GetAddr());
+            if (intersect != nullptr) {
+                PartialReinterpretSurface(current_color_buffers[i], intersect);
+                texception = true;
+            }
+        }
+    }
+}
+
 } // namespace OpenGL
 } // namespace OpenGL

+ 82 - 1
src/video_core/renderer_opengl/gl_rasterizer_cache.h

@@ -34,6 +34,7 @@ using SurfaceTarget = VideoCore::Surface::SurfaceTarget;
 using SurfaceType = VideoCore::Surface::SurfaceType;
 using SurfaceType = VideoCore::Surface::SurfaceType;
 using PixelFormat = VideoCore::Surface::PixelFormat;
 using PixelFormat = VideoCore::Surface::PixelFormat;
 using ComponentType = VideoCore::Surface::ComponentType;
 using ComponentType = VideoCore::Surface::ComponentType;
+using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 
 
 struct SurfaceParams {
 struct SurfaceParams {
     enum class SurfaceClass {
     enum class SurfaceClass {
@@ -140,10 +141,18 @@ struct SurfaceParams {
         return offset;
         return offset;
     }
     }
 
 
+    std::size_t GetMipmapSingleSize(u32 mip_level) const {
+        return InnerMipmapMemorySize(mip_level, false, is_layered);
+    }
+
     u32 MipWidth(u32 mip_level) const {
     u32 MipWidth(u32 mip_level) const {
         return std::max(1U, width >> mip_level);
         return std::max(1U, width >> mip_level);
     }
     }
 
 
+    u32 MipWidthGobAligned(u32 mip_level) const {
+        return Common::AlignUp(std::max(1U, width >> mip_level), 64U * 8U / GetFormatBpp());
+    }
+
     u32 MipHeight(u32 mip_level) const {
     u32 MipHeight(u32 mip_level) const {
         return std::max(1U, height >> mip_level);
         return std::max(1U, height >> mip_level);
     }
     }
@@ -346,6 +355,10 @@ public:
         return cached_size_in_bytes;
         return cached_size_in_bytes;
     }
     }
 
 
+    std::size_t GetMemorySize() const {
+        return memory_size;
+    }
+
     void Flush() override {
     void Flush() override {
         FlushGLBuffer();
         FlushGLBuffer();
     }
     }
@@ -395,6 +408,26 @@ public:
                        Tegra::Texture::SwizzleSource swizzle_z,
                        Tegra::Texture::SwizzleSource swizzle_z,
                        Tegra::Texture::SwizzleSource swizzle_w);
                        Tegra::Texture::SwizzleSource swizzle_w);
 
 
+    void MarkReinterpreted() {
+        reinterpreted = true;
+    }
+
+    bool IsReinterpreted() const {
+        return reinterpreted;
+    }
+
+    void MarkForReload(bool reload) {
+        must_reload = reload;
+    }
+
+    bool MustReload() const {
+        return must_reload;
+    }
+
+    bool IsUploaded() const {
+        return params.identity == SurfaceParams::SurfaceClass::Uploaded;
+    }
+
 private:
 private:
     void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
     void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
 
 
@@ -408,6 +441,9 @@ private:
     GLenum gl_internal_format{};
     GLenum gl_internal_format{};
     std::size_t cached_size_in_bytes{};
     std::size_t cached_size_in_bytes{};
     std::array<GLenum, 4> swizzle{GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
     std::array<GLenum, 4> swizzle{GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
+    std::size_t memory_size;
+    bool reinterpreted = false;
+    bool must_reload = false;
 };
 };
 
 
 class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
 class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -433,6 +469,9 @@ public:
                           const Common::Rectangle<u32>& src_rect,
                           const Common::Rectangle<u32>& src_rect,
                           const Common::Rectangle<u32>& dst_rect);
                           const Common::Rectangle<u32>& dst_rect);
 
 
+    void SignalPreDrawCall();
+    void SignalPostDrawCall();
+
 private:
 private:
     void LoadSurface(const Surface& surface);
     void LoadSurface(const Surface& surface);
     Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true);
     Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true);
@@ -449,6 +488,10 @@ private:
     /// Tries to get a reserved surface for the specified parameters
     /// Tries to get a reserved surface for the specified parameters
     Surface TryGetReservedSurface(const SurfaceParams& params);
     Surface TryGetReservedSurface(const SurfaceParams& params);
 
 
+    // Partialy reinterpret a surface based on a triggering_surface that collides with it.
+    // returns true if the reinterpret was successful, false in case it was not.
+    bool PartialReinterpretSurface(Surface triggering_surface, Surface intersect);
+
     /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
     /// 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 AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
     void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface);
     void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface);
@@ -465,12 +508,50 @@ private:
     OGLFramebuffer read_framebuffer;
     OGLFramebuffer read_framebuffer;
     OGLFramebuffer draw_framebuffer;
     OGLFramebuffer draw_framebuffer;
 
 
+    bool texception = false;
+
     /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
     /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
     /// using the new format.
     /// using the new format.
     OGLBuffer copy_pbo;
     OGLBuffer copy_pbo;
 
 
-    std::array<Surface, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> last_color_buffers;
+    std::array<Surface, Maxwell::NumRenderTargets> last_color_buffers;
+    std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers;
     Surface last_depth_buffer;
     Surface last_depth_buffer;
+
+    using SurfaceIntervalCache = boost::icl::interval_map<VAddr, Surface>;
+    using SurfaceInterval = typename SurfaceIntervalCache::interval_type;
+
+    static auto GetReinterpretInterval(const Surface& object) {
+        return SurfaceInterval::right_open(object->GetAddr() + 1,
+                                           object->GetAddr() + object->GetMemorySize() - 1);
+    }
+
+    // Reinterpreted surfaces are very fragil as the game may keep rendering into them.
+    SurfaceIntervalCache reinterpreted_surfaces;
+
+    void RegisterReinterpretSurface(Surface reinterpret_surface) {
+        auto interval = GetReinterpretInterval(reinterpret_surface);
+        reinterpreted_surfaces.insert({interval, reinterpret_surface});
+        reinterpret_surface->MarkReinterpreted();
+    }
+
+    Surface CollideOnReinterpretedSurface(VAddr addr) const {
+        const SurfaceInterval interval{addr};
+        for (auto& pair :
+             boost::make_iterator_range(reinterpreted_surfaces.equal_range(interval))) {
+            return pair.second;
+        }
+        return nullptr;
+    }
+
+    /// Unregisters an object from the cache
+    void UnregisterSurface(const Surface& object) {
+        if (object->IsReinterpreted()) {
+            auto interval = GetReinterpretInterval(object);
+            reinterpreted_surfaces.erase(interval);
+        }
+        Unregister(object);
+    }
 };
 };
 
 
 } // namespace OpenGL
 } // namespace OpenGL