diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 3418e97629..cd6a75c34e 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -136,6 +136,8 @@ const ConfigInfo GFX_STEREO_DEPTH_PERCENTAGE{ // Graphics.Hacks const ConfigInfo GFX_HACK_EFB_ACCESS_ENABLE{{System::GFX, "Hacks", "EFBAccessEnable"}, true}; +const ConfigInfo GFX_HACK_EFB_ACCESS_TILE_SIZE{{System::GFX, "Hacks", "EFBAccessTileSize"}, + 64}; const ConfigInfo GFX_HACK_BBOX_ENABLE{{System::GFX, "Hacks", "BBoxEnable"}, false}; const ConfigInfo GFX_HACK_FORCE_PROGRESSIVE{{System::GFX, "Hacks", "ForceProgressive"}, true}; const ConfigInfo GFX_HACK_SKIP_EFB_COPY_TO_RAM{{System::GFX, "Hacks", "EFBToTextureEnable"}, diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 8e29c13a77..bcc251c3ce 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -101,6 +101,7 @@ extern const ConfigInfo GFX_STEREO_DEPTH_PERCENTAGE; // Graphics.Hacks extern const ConfigInfo GFX_HACK_EFB_ACCESS_ENABLE; +extern const ConfigInfo GFX_HACK_EFB_ACCESS_TILE_SIZE; extern const ConfigInfo GFX_HACK_BBOX_ENABLE; extern const ConfigInfo GFX_HACK_FORCE_PROGRESSIVE; extern const ConfigInfo GFX_HACK_SKIP_EFB_COPY_TO_RAM; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index b1021f7827..aa0735ec62 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -113,6 +113,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) // Graphics.Hacks Config::GFX_HACK_EFB_ACCESS_ENABLE.location, + Config::GFX_HACK_EFB_ACCESS_TILE_SIZE.location, Config::GFX_HACK_BBOX_ENABLE.location, Config::GFX_HACK_FORCE_PROGRESSIVE.location, Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM.location, diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 0af19012ea..ec055a7278 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -43,6 +43,7 @@ bool FramebufferManager::Initialize() return false; } + m_efb_cache_tile_size = static_cast(std::max(g_ActiveConfig.iEFBAccessTileSize, 0)); if (!CreateReadbackFramebuffer()) { PanicAlert("Failed to create EFB readback framebuffer"); @@ -288,6 +289,7 @@ bool FramebufferManager::ReinterpretPixelData(EFBReinterpretType convtype) std::swap(m_efb_color_texture, m_efb_convert_color_texture); std::swap(m_efb_framebuffer, m_efb_convert_framebuffer); g_renderer->EndUtilityDrawing(); + InvalidatePeekCache(); return true; } @@ -324,92 +326,101 @@ void FramebufferManager::DestroyConversionPipelines() pipeline.reset(); } -bool FramebufferManager::PopulateColorReadbackTexture() +bool FramebufferManager::IsUsingTiledEFBCache() const { - g_vertex_manager->OnCPUEFBAccess(); - - // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. - AbstractTexture* src_texture = - ResolveEFBColorTexture(MathUtil::Rectangle(0, 0, GetEFBWidth(), GetEFBHeight())); - if (g_renderer->GetEFBScale() != 1) - { - // Downsample from internal resolution to 1x. - // TODO: This won't produce correct results at IRs above 2x. - g_renderer->BeginUtilityDrawing(); - g_renderer->SetAndDiscardFramebuffer(m_color_copy_framebuffer.get()); - g_renderer->SetViewportAndScissor(m_color_copy_framebuffer->GetRect()); - g_renderer->SetPipeline(m_color_copy_pipeline.get()); - g_renderer->SetTexture(0, src_texture); - g_renderer->SetSamplerState(0, RenderState::GetLinearSamplerState()); - g_renderer->Draw(0, 3); - - // Copy from EFB or copy texture to staging texture. - m_color_readback_texture->CopyFromTexture(m_color_copy_texture.get(), - m_color_readback_texture->GetRect(), 0, 0, - m_color_readback_texture->GetRect()); - - g_renderer->EndUtilityDrawing(); - } - else - { - m_color_readback_texture->CopyFromTexture(src_texture, m_color_readback_texture->GetRect(), 0, - 0, m_color_readback_texture->GetRect()); - } - - // Wait until the copy is complete. - m_color_readback_texture->Flush(); - m_color_readback_texture_valid = true; - return true; + return m_efb_cache_tile_size > 0; } -bool FramebufferManager::PopulateDepthReadbackTexture() +bool FramebufferManager::IsEFBCacheTilePresent(bool depth, u32 x, u32 y, u32* tile_index) const { - g_vertex_manager->OnCPUEFBAccess(); - - // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. - AbstractTexture* src_texture = - ResolveEFBDepthTexture(MathUtil::Rectangle(0, 0, GetEFBWidth(), GetEFBHeight())); - if (g_renderer->GetEFBScale() != 1) + const EFBCacheData& data = depth ? m_efb_depth_cache : m_efb_color_cache; + if (m_efb_cache_tile_size == 0) { - // Downsample from internal resolution to 1x. - // TODO: This won't produce correct results at IRs above 2x. - g_renderer->BeginUtilityDrawing(); - g_renderer->SetAndDiscardFramebuffer(m_depth_copy_framebuffer.get()); - g_renderer->SetViewportAndScissor(m_depth_copy_framebuffer->GetRect()); - g_renderer->SetPipeline(m_depth_copy_pipeline.get()); - g_renderer->SetTexture(0, src_texture); - g_renderer->SetSamplerState(0, RenderState::GetLinearSamplerState()); - g_renderer->Draw(0, 3); - - // No need to call FinishedRendering() here because CopyFromTexture() transitions. - m_depth_readback_texture->CopyFromTexture(m_depth_copy_texture.get(), - m_depth_readback_texture->GetRect(), 0, 0, - m_depth_readback_texture->GetRect()); - - g_renderer->EndUtilityDrawing(); + *tile_index = 0; + return data.valid; } else { - m_depth_readback_texture->CopyFromTexture(src_texture, m_depth_readback_texture->GetRect(), 0, - 0, m_depth_readback_texture->GetRect()); + *tile_index = + ((y / m_efb_cache_tile_size) * m_efb_cache_tiles_wide) + (x / m_efb_cache_tile_size); + return data.valid && data.tiles[*tile_index]; } +} - // Wait until the copy is complete. - m_depth_readback_texture->Flush(); - m_depth_readback_texture_valid = true; - return true; +MathUtil::Rectangle FramebufferManager::GetEFBCacheTileRect(u32 tile_index) const +{ + if (m_efb_cache_tile_size == 0) + return MathUtil::Rectangle(0, 0, EFB_WIDTH, EFB_HEIGHT); + + const u32 tile_y = tile_index / m_efb_cache_tiles_wide; + const u32 tile_x = tile_index % m_efb_cache_tiles_wide; + const u32 start_y = tile_y * m_efb_cache_tile_size; + const u32 start_x = tile_x * m_efb_cache_tile_size; + return MathUtil::Rectangle( + start_x, start_y, std::min(start_x + m_efb_cache_tile_size, static_cast(EFB_WIDTH)), + std::min(start_y + m_efb_cache_tile_size, static_cast(EFB_HEIGHT))); +} + +u32 FramebufferManager::PeekEFBColor(u32 x, u32 y) +{ + // The y coordinate here assumes upper-left origin, but the readback texture is lower-left in GL. + if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) + y = EFB_HEIGHT - 1 - y; + + u32 tile_index; + if (!IsEFBCacheTilePresent(false, x, y, &tile_index)) + PopulateEFBCache(false, tile_index); + + u32 value; + m_efb_color_cache.readback_texture->ReadTexel(x, y, &value); + return value; +} + +float FramebufferManager::PeekEFBDepth(u32 x, u32 y) +{ + // The y coordinate here assumes upper-left origin, but the readback texture is lower-left in GL. + if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) + y = EFB_HEIGHT - 1 - y; + + u32 tile_index; + if (!IsEFBCacheTilePresent(true, x, y, &tile_index)) + PopulateEFBCache(true, tile_index); + + float value; + m_efb_depth_cache.readback_texture->ReadTexel(x, y, &value); + return value; +} + +void FramebufferManager::SetEFBCacheTileSize(u32 size) +{ + if (m_efb_cache_tile_size == size) + return; + + InvalidatePeekCache(); + m_efb_cache_tile_size = size; + DestroyReadbackFramebuffer(); + if (!CreateReadbackFramebuffer()) + PanicAlert("Failed to create EFB readback framebuffers"); } void FramebufferManager::InvalidatePeekCache() { - m_color_readback_texture_valid = false; - m_depth_readback_texture_valid = false; + if (m_efb_color_cache.valid) + { + m_efb_color_cache.valid = false; + std::fill(m_efb_color_cache.tiles.begin(), m_efb_color_cache.tiles.end(), false); + } + if (m_efb_depth_cache.valid) + { + m_efb_depth_cache.valid = false; + std::fill(m_efb_depth_cache.tiles.begin(), m_efb_depth_cache.tiles.end(), false); + } } bool FramebufferManager::CompileReadbackPipelines() { AbstractPipelineConfig config = {}; - config.vertex_shader = g_shader_cache->GetScreenQuadVertexShader(); + config.vertex_shader = g_shader_cache->GetTextureCopyVertexShader(); config.geometry_shader = IsEFBStereo() ? g_shader_cache->GetTexcoordGeometryShader() : nullptr; config.pixel_shader = g_shader_cache->GetTextureCopyPixelShader(); config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); @@ -417,15 +428,15 @@ bool FramebufferManager::CompileReadbackPipelines() config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetColorFramebufferState(GetEFBColorFormat()); config.usage = AbstractPipelineUsage::Utility; - m_color_copy_pipeline = g_renderer->CreatePipeline(config); - if (!m_color_copy_pipeline) + m_efb_color_cache.copy_pipeline = g_renderer->CreatePipeline(config); + if (!m_efb_color_cache.copy_pipeline) return false; // same for depth, except different format config.framebuffer_state.color_texture_format = AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()); - m_depth_copy_pipeline = g_renderer->CreatePipeline(config); - if (!m_depth_copy_pipeline) + m_efb_depth_cache.copy_pipeline = g_renderer->CreatePipeline(config); + if (!m_efb_depth_cache.copy_pipeline) return false; if (IsEFBMultisampled()) @@ -447,49 +458,130 @@ bool FramebufferManager::CompileReadbackPipelines() void FramebufferManager::DestroyReadbackPipelines() { m_efb_depth_resolve_pipeline.reset(); - m_depth_copy_pipeline.reset(); - m_color_copy_pipeline.reset(); + m_efb_depth_cache.copy_pipeline.reset(); + m_efb_color_cache.copy_pipeline.reset(); } bool FramebufferManager::CreateReadbackFramebuffer() { - const TextureConfig color_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), - AbstractTextureFlag_RenderTarget); - const TextureConfig depth_config( - EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, - AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()), - AbstractTextureFlag_RenderTarget); - if (g_renderer->GetEFBScale() != 1) + // Since we can't partially copy from a depth buffer directly to the staging texture in D3D, we + // use an intermediate buffer to avoid copying the whole texture. + if ((IsUsingTiledEFBCache() && !g_ActiveConfig.backend_info.bSupportsPartialDepthCopies) || + g_renderer->GetEFBScale() != 1) { - m_color_copy_texture = g_renderer->CreateTexture(color_config); - m_depth_copy_texture = g_renderer->CreateTexture(depth_config); - if (!m_color_copy_texture || !m_depth_copy_texture) + const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH, + IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1, + 1, 1, GetEFBColorFormat(), AbstractTextureFlag_RenderTarget); + const TextureConfig depth_config( + color_config.width, color_config.height, 1, 1, 1, + AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()), + AbstractTextureFlag_RenderTarget); + + m_efb_color_cache.texture = g_renderer->CreateTexture(color_config); + m_efb_depth_cache.texture = g_renderer->CreateTexture(depth_config); + if (!m_efb_color_cache.texture || !m_efb_depth_cache.texture) return false; - m_color_copy_framebuffer = g_renderer->CreateFramebuffer(m_color_copy_texture.get(), nullptr); - m_depth_copy_framebuffer = g_renderer->CreateFramebuffer(m_depth_copy_texture.get(), nullptr); - if (!m_color_copy_framebuffer || !m_depth_copy_framebuffer) + m_efb_color_cache.framebuffer = + g_renderer->CreateFramebuffer(m_efb_color_cache.texture.get(), nullptr); + m_efb_depth_cache.framebuffer = + g_renderer->CreateFramebuffer(m_efb_depth_cache.texture.get(), nullptr); + if (!m_efb_color_cache.framebuffer || !m_efb_depth_cache.framebuffer) return false; } - m_color_readback_texture = - g_renderer->CreateStagingTexture(StagingTextureType::Mutable, color_config); - m_depth_readback_texture = - g_renderer->CreateStagingTexture(StagingTextureType::Mutable, depth_config); - if (!m_color_readback_texture || !m_depth_readback_texture) + // Staging texture use the full EFB dimensions, as this is the buffer for the whole cache. + m_efb_color_cache.readback_texture = g_renderer->CreateStagingTexture( + StagingTextureType::Mutable, + TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), 0)); + m_efb_depth_cache.readback_texture = g_renderer->CreateStagingTexture( + StagingTextureType::Mutable, + TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, + AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()), 0)); + if (!m_efb_color_cache.readback_texture || !m_efb_depth_cache.readback_texture) return false; + if (IsUsingTiledEFBCache()) + { + const u32 tiles_wide = ((EFB_WIDTH + (m_efb_cache_tile_size - 1)) / m_efb_cache_tile_size); + const u32 tiles_high = ((EFB_HEIGHT + (m_efb_cache_tile_size - 1)) / m_efb_cache_tile_size); + const u32 total_tiles = tiles_wide * tiles_high; + m_efb_color_cache.tiles.resize(total_tiles); + std::fill(m_efb_color_cache.tiles.begin(), m_efb_color_cache.tiles.end(), false); + m_efb_depth_cache.tiles.resize(total_tiles); + std::fill(m_efb_depth_cache.tiles.begin(), m_efb_depth_cache.tiles.end(), false); + m_efb_cache_tiles_wide = tiles_wide; + } + return true; } void FramebufferManager::DestroyReadbackFramebuffer() { - m_depth_copy_framebuffer.reset(); - m_depth_copy_texture.reset(); - m_depth_readback_texture_valid = false; - m_color_copy_framebuffer.reset(); - m_color_copy_texture.reset(); - m_color_readback_texture_valid = false; + auto DestroyCache = [](EFBCacheData& data) { + data.readback_texture.reset(); + data.framebuffer.reset(); + data.texture.reset(); + data.valid = false; + }; + DestroyCache(m_efb_color_cache); + DestroyCache(m_efb_depth_cache); +} + +void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index) +{ + g_vertex_manager->OnCPUEFBAccess(); + + // Force the path through the intermediate texture, as we can't do an image copy from a depth + // buffer directly to a staging texture (must be the whole resource). + const bool force_intermediate_copy = + depth && !g_ActiveConfig.backend_info.bSupportsPartialDepthCopies && IsUsingTiledEFBCache(); + + // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. + EFBCacheData& data = depth ? m_efb_depth_cache : m_efb_color_cache; + const MathUtil::Rectangle rect = GetEFBCacheTileRect(tile_index); + const MathUtil::Rectangle native_rect = g_renderer->ConvertEFBRectangle(rect); + AbstractTexture* src_texture = + depth ? ResolveEFBDepthTexture(native_rect) : ResolveEFBColorTexture(native_rect); + if (g_renderer->GetEFBScale() != 1 || force_intermediate_copy) + { + // Downsample from internal resolution to 1x. + // TODO: This won't produce correct results at IRs above 2x. More samples are required. + // This is the same issue as with EFB copies. + g_renderer->BeginUtilityDrawing(); + + const float rcp_src_width = 1.0f / m_efb_framebuffer->GetWidth(); + const float rcp_src_height = 1.0f / m_efb_framebuffer->GetHeight(); + const std::array uniforms = { + {native_rect.left * rcp_src_width, native_rect.top * rcp_src_height, + native_rect.GetWidth() * rcp_src_width, native_rect.GetHeight() * rcp_src_height}}; + g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); + g_renderer->SetAndDiscardFramebuffer(data.framebuffer.get()); + g_renderer->SetViewportAndScissor(data.framebuffer->GetRect()); + g_renderer->SetPipeline(data.copy_pipeline.get()); + g_renderer->SetTexture(0, src_texture); + g_renderer->SetSamplerState(0, depth ? RenderState::GetPointSamplerState() : + RenderState::GetLinearSamplerState()); + g_renderer->Draw(0, 3); + + // Copy from EFB or copy texture to staging texture. + // No need to call FinishedRendering() here because CopyFromTexture() transitions. + data.readback_texture->CopyFromTexture( + data.texture.get(), MathUtil::Rectangle(0, 0, rect.GetWidth(), rect.GetHeight()), 0, 0, + rect); + + g_renderer->EndUtilityDrawing(); + } + else + { + data.readback_texture->CopyFromTexture(src_texture, rect, 0, 0, rect); + } + + // Wait until the copy is complete. + data.readback_texture->Flush(); + data.valid = true; + if (IsUsingTiledEFBCache()) + data.tiles[tile_index] = true; } void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool clear_color, @@ -578,34 +670,6 @@ void FramebufferManager::DestroyClearPipelines() } } -u32 FramebufferManager::PeekEFBColor(u32 x, u32 y) -{ - if (!m_color_readback_texture_valid && !PopulateColorReadbackTexture()) - return 0; - - // The y coordinate here assumes upper-left origin, but the readback texture is lower-left in GL. - if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) - y = EFB_HEIGHT - 1 - y; - - u32 value; - m_color_readback_texture->ReadTexel(x, y, &value); - return value; -} - -float FramebufferManager::PeekEFBDepth(u32 x, u32 y) -{ - if (!m_depth_readback_texture_valid && !PopulateDepthReadbackTexture()) - return 0.0f; - - // The y coordinate here assumes upper-left origin, but the readback texture is lower-left in GL. - if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) - y = EFB_HEIGHT - 1 - y; - - float value; - m_depth_readback_texture->ReadTexel(x, y, &value); - return value; -} - void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color) { // Flush if we exceeded the number of vertices per batch. @@ -614,15 +678,14 @@ void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color) CreatePokeVertices(&m_color_poke_vertices, x, y, 0.0f, color); - // Update the peek cache if it's valid, since we know the color of the pixel now. - if (m_color_readback_texture_valid) - { - // See comment above for reasoning for lower-left coordinates. - if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) - y = EFB_HEIGHT - 1 - y; + // See comment above for reasoning for lower-left coordinates. + if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) + y = EFB_HEIGHT - 1 - y; - m_color_readback_texture->WriteTexel(x, y, &color); - } + // Update the peek cache if it's valid, since we know the color of the pixel now. + u32 tile_index; + if (IsEFBCacheTilePresent(false, x, y, &tile_index)) + m_efb_color_cache.readback_texture->WriteTexel(x, y, &color); } void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth) @@ -633,15 +696,14 @@ void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth) CreatePokeVertices(&m_depth_poke_vertices, x, y, depth, 0); - // Update the peek cache if it's valid, since we know the color of the pixel now. - if (m_depth_readback_texture_valid) - { - // See comment above for reasoning for lower-left coordinates. - if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) - y = EFB_HEIGHT - 1 - y; + // See comment above for reasoning for lower-left coordinates. + if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) + y = EFB_HEIGHT - 1 - y; - m_depth_readback_texture->WriteTexel(x, y, &depth); - } + // Update the peek cache if it's valid, since we know the color of the pixel now. + u32 tile_index; + if (IsEFBCacheTilePresent(true, x, y, &tile_index)) + m_efb_depth_cache.readback_texture->WriteTexel(x, y, &depth); } void FramebufferManager::CreatePokeVertices(std::vector* destination_list, u32 x, diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index d0de7b62b1..b6fb53f949 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -6,15 +6,16 @@ #include #include +#include #include "Common/CommonTypes.h" #include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractStagingTexture.h" +#include "VideoCommon/AbstractPipeline.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/TextureConfig.h" -class AbstractFramebuffer; -class AbstractPipeline; -class AbstractStagingTexture; class NativeVertexFormat; enum class EFBReinterpretType @@ -85,6 +86,7 @@ public: // Reads a framebuffer value back from the GPU. This may block if the cache is not current. u32 PeekEFBColor(u32 x, u32 y); float PeekEFBDepth(u32 x, u32 y); + void SetEFBCacheTileSize(u32 size); void InvalidatePeekCache(); // Writes a value to the framebuffer. This will never block, and writes will be batched. @@ -100,6 +102,18 @@ protected: }; static_assert(std::is_standard_layout::value, "EFBPokeVertex is standard-layout"); + // EFB cache - for CPU EFB access + // Tiles are ordered left-to-right, then top-to-bottom + struct EFBCacheData + { + std::unique_ptr texture; + std::unique_ptr framebuffer; + std::unique_ptr readback_texture; + std::unique_ptr copy_pipeline; + std::vector tiles; + bool valid; + }; + bool CreateEFBFramebuffer(); void DestroyEFBFramebuffer(); @@ -118,8 +132,10 @@ protected: bool CompilePokePipelines(); void DestroyPokePipelines(); - bool PopulateColorReadbackTexture(); - bool PopulateDepthReadbackTexture(); + bool IsUsingTiledEFBCache() const; + bool IsEFBCacheTilePresent(bool depth, u32 x, u32 y, u32* tile_index) const; + MathUtil::Rectangle GetEFBCacheTileRect(u32 tile_index) const; + void PopulateEFBCache(bool depth, u32 tile_index); void CreatePokeVertices(std::vector* destination_list, u32 x, u32 y, float z, u32 color); @@ -141,19 +157,11 @@ protected: // Format conversion shaders std::array, 6> m_format_conversion_pipelines; - // EFB readback texture - std::unique_ptr m_color_copy_texture; - std::unique_ptr m_depth_copy_texture; - std::unique_ptr m_color_copy_framebuffer; - std::unique_ptr m_depth_copy_framebuffer; - std::unique_ptr m_color_copy_pipeline; - std::unique_ptr m_depth_copy_pipeline; - - // CPU-side EFB readback texture - std::unique_ptr m_color_readback_texture; - std::unique_ptr m_depth_readback_texture; - bool m_color_readback_texture_valid = false; - bool m_depth_readback_texture_valid = false; + // EFB cache - for CPU EFB access + u32 m_efb_cache_tile_size = 0; + u32 m_efb_cache_tiles_wide = 0; + EFBCacheData m_efb_color_cache = {}; + EFBCacheData m_efb_depth_cache = {}; // EFB clear pipelines // Indexed by [color_write_enabled][alpha_write_enabled][depth_write_enabled] diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 611eca29e6..74760f57e9 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -386,6 +386,7 @@ void Renderer::CheckForConfigChanges() const StereoMode old_stereo = g_ActiveConfig.stereo_mode; const u32 old_multisamples = g_ActiveConfig.iMultisamples; const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; + const int old_efb_access_tile_size = g_ActiveConfig.iEFBAccessTileSize; const bool old_force_filtering = g_ActiveConfig.bForceFiltering; const bool old_vsync = g_ActiveConfig.bVSyncActive; const bool old_bbox = g_ActiveConfig.bBBoxEnable; @@ -395,6 +396,10 @@ void Renderer::CheckForConfigChanges() // Update texture cache settings with any changed options. g_texture_cache->OnConfigChanged(g_ActiveConfig); + // EFB tile cache doesn't need to notify the backend. + if (old_efb_access_tile_size != g_ActiveConfig.iEFBAccessTileSize) + g_framebuffer_manager->SetEFBCacheTileSize(std::max(g_ActiveConfig.iEFBAccessTileSize, 0)); + // Check for post-processing shader changes. Done up here as it doesn't affect anything outside // the post-processor. Note that options are applied every frame, so no need to check those. if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 3b1b22dc89..2385d427a2 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -154,6 +154,7 @@ void VideoConfig::Refresh() bCopyEFBScaled = Config::Get(Config::GFX_HACK_COPY_EFB_SCALED); bEFBEmulateFormatChanges = Config::Get(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES); bVertexRounding = Config::Get(Config::GFX_HACK_VERTEX_ROUDING); + iEFBAccessTileSize = Config::Get(Config::GFX_HACK_EFB_ACCESS_TILE_SIZE); bPerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index a22b0d3381..c6d00162ce 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -128,6 +128,7 @@ struct VideoConfig final bool bEnablePixelLighting; bool bFastDepthCalc; bool bVertexRounding; + int iEFBAccessTileSize; int iLog; // CONF_ bits int iSaveTargetId; // TODO: Should be dropped