FramebufferManager: Implement EFB tile cache

The new tile cache is dynamic in size and can be turned on/off.
This commit is contained in:
Stenzek
2019-03-02 15:11:47 +10:00
parent 65216c9e87
commit 6bc4bfd26a
8 changed files with 240 additions and 159 deletions

View File

@ -136,6 +136,8 @@ const ConfigInfo<int> GFX_STEREO_DEPTH_PERCENTAGE{
// Graphics.Hacks // Graphics.Hacks
const ConfigInfo<bool> GFX_HACK_EFB_ACCESS_ENABLE{{System::GFX, "Hacks", "EFBAccessEnable"}, true}; const ConfigInfo<bool> GFX_HACK_EFB_ACCESS_ENABLE{{System::GFX, "Hacks", "EFBAccessEnable"}, true};
const ConfigInfo<int> GFX_HACK_EFB_ACCESS_TILE_SIZE{{System::GFX, "Hacks", "EFBAccessTileSize"},
64};
const ConfigInfo<bool> GFX_HACK_BBOX_ENABLE{{System::GFX, "Hacks", "BBoxEnable"}, false}; const ConfigInfo<bool> GFX_HACK_BBOX_ENABLE{{System::GFX, "Hacks", "BBoxEnable"}, false};
const ConfigInfo<bool> GFX_HACK_FORCE_PROGRESSIVE{{System::GFX, "Hacks", "ForceProgressive"}, true}; const ConfigInfo<bool> GFX_HACK_FORCE_PROGRESSIVE{{System::GFX, "Hacks", "ForceProgressive"}, true};
const ConfigInfo<bool> GFX_HACK_SKIP_EFB_COPY_TO_RAM{{System::GFX, "Hacks", "EFBToTextureEnable"}, const ConfigInfo<bool> GFX_HACK_SKIP_EFB_COPY_TO_RAM{{System::GFX, "Hacks", "EFBToTextureEnable"},

View File

@ -101,6 +101,7 @@ extern const ConfigInfo<int> GFX_STEREO_DEPTH_PERCENTAGE;
// Graphics.Hacks // Graphics.Hacks
extern const ConfigInfo<bool> GFX_HACK_EFB_ACCESS_ENABLE; extern const ConfigInfo<bool> GFX_HACK_EFB_ACCESS_ENABLE;
extern const ConfigInfo<int> GFX_HACK_EFB_ACCESS_TILE_SIZE;
extern const ConfigInfo<bool> GFX_HACK_BBOX_ENABLE; extern const ConfigInfo<bool> GFX_HACK_BBOX_ENABLE;
extern const ConfigInfo<bool> GFX_HACK_FORCE_PROGRESSIVE; extern const ConfigInfo<bool> GFX_HACK_FORCE_PROGRESSIVE;
extern const ConfigInfo<bool> GFX_HACK_SKIP_EFB_COPY_TO_RAM; extern const ConfigInfo<bool> GFX_HACK_SKIP_EFB_COPY_TO_RAM;

View File

@ -113,6 +113,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location)
// Graphics.Hacks // Graphics.Hacks
Config::GFX_HACK_EFB_ACCESS_ENABLE.location, Config::GFX_HACK_EFB_ACCESS_ENABLE.location,
Config::GFX_HACK_EFB_ACCESS_TILE_SIZE.location,
Config::GFX_HACK_BBOX_ENABLE.location, Config::GFX_HACK_BBOX_ENABLE.location,
Config::GFX_HACK_FORCE_PROGRESSIVE.location, Config::GFX_HACK_FORCE_PROGRESSIVE.location,
Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM.location, Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM.location,

View File

@ -43,6 +43,7 @@ bool FramebufferManager::Initialize()
return false; return false;
} }
m_efb_cache_tile_size = static_cast<u32>(std::max(g_ActiveConfig.iEFBAccessTileSize, 0));
if (!CreateReadbackFramebuffer()) if (!CreateReadbackFramebuffer())
{ {
PanicAlert("Failed to create EFB readback framebuffer"); 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_color_texture, m_efb_convert_color_texture);
std::swap(m_efb_framebuffer, m_efb_convert_framebuffer); std::swap(m_efb_framebuffer, m_efb_convert_framebuffer);
g_renderer->EndUtilityDrawing(); g_renderer->EndUtilityDrawing();
InvalidatePeekCache();
return true; return true;
} }
@ -324,92 +326,101 @@ void FramebufferManager::DestroyConversionPipelines()
pipeline.reset(); pipeline.reset();
} }
bool FramebufferManager::PopulateColorReadbackTexture() bool FramebufferManager::IsUsingTiledEFBCache() const
{ {
g_vertex_manager->OnCPUEFBAccess(); return m_efb_cache_tile_size > 0;
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
AbstractTexture* src_texture =
ResolveEFBColorTexture(MathUtil::Rectangle<int>(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;
} }
bool FramebufferManager::PopulateDepthReadbackTexture() bool FramebufferManager::IsEFBCacheTilePresent(bool depth, u32 x, u32 y, u32* tile_index) const
{ {
g_vertex_manager->OnCPUEFBAccess(); const EFBCacheData& data = depth ? m_efb_depth_cache : m_efb_color_cache;
if (m_efb_cache_tile_size == 0)
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
AbstractTexture* src_texture =
ResolveEFBDepthTexture(MathUtil::Rectangle<int>(0, 0, GetEFBWidth(), GetEFBHeight()));
if (g_renderer->GetEFBScale() != 1)
{ {
// Downsample from internal resolution to 1x. *tile_index = 0;
// TODO: This won't produce correct results at IRs above 2x. return data.valid;
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();
} }
else else
{ {
m_depth_readback_texture->CopyFromTexture(src_texture, m_depth_readback_texture->GetRect(), 0, *tile_index =
0, m_depth_readback_texture->GetRect()); ((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. MathUtil::Rectangle<int> FramebufferManager::GetEFBCacheTileRect(u32 tile_index) const
m_depth_readback_texture->Flush(); {
m_depth_readback_texture_valid = true; if (m_efb_cache_tile_size == 0)
return true; return MathUtil::Rectangle<int>(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<int>(
start_x, start_y, std::min(start_x + m_efb_cache_tile_size, static_cast<u32>(EFB_WIDTH)),
std::min(start_y + m_efb_cache_tile_size, static_cast<u32>(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() void FramebufferManager::InvalidatePeekCache()
{ {
m_color_readback_texture_valid = false; if (m_efb_color_cache.valid)
m_depth_readback_texture_valid = false; {
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() bool FramebufferManager::CompileReadbackPipelines()
{ {
AbstractPipelineConfig config = {}; 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.geometry_shader = IsEFBStereo() ? g_shader_cache->GetTexcoordGeometryShader() : nullptr;
config.pixel_shader = g_shader_cache->GetTextureCopyPixelShader(); config.pixel_shader = g_shader_cache->GetTextureCopyPixelShader();
config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
@ -417,15 +428,15 @@ bool FramebufferManager::CompileReadbackPipelines()
config.blending_state = RenderState::GetNoBlendingBlendState(); config.blending_state = RenderState::GetNoBlendingBlendState();
config.framebuffer_state = RenderState::GetColorFramebufferState(GetEFBColorFormat()); config.framebuffer_state = RenderState::GetColorFramebufferState(GetEFBColorFormat());
config.usage = AbstractPipelineUsage::Utility; config.usage = AbstractPipelineUsage::Utility;
m_color_copy_pipeline = g_renderer->CreatePipeline(config); m_efb_color_cache.copy_pipeline = g_renderer->CreatePipeline(config);
if (!m_color_copy_pipeline) if (!m_efb_color_cache.copy_pipeline)
return false; return false;
// same for depth, except different format // same for depth, except different format
config.framebuffer_state.color_texture_format = config.framebuffer_state.color_texture_format =
AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()); AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat());
m_depth_copy_pipeline = g_renderer->CreatePipeline(config); m_efb_depth_cache.copy_pipeline = g_renderer->CreatePipeline(config);
if (!m_depth_copy_pipeline) if (!m_efb_depth_cache.copy_pipeline)
return false; return false;
if (IsEFBMultisampled()) if (IsEFBMultisampled())
@ -447,49 +458,130 @@ bool FramebufferManager::CompileReadbackPipelines()
void FramebufferManager::DestroyReadbackPipelines() void FramebufferManager::DestroyReadbackPipelines()
{ {
m_efb_depth_resolve_pipeline.reset(); m_efb_depth_resolve_pipeline.reset();
m_depth_copy_pipeline.reset(); m_efb_depth_cache.copy_pipeline.reset();
m_color_copy_pipeline.reset(); m_efb_color_cache.copy_pipeline.reset();
} }
bool FramebufferManager::CreateReadbackFramebuffer() bool FramebufferManager::CreateReadbackFramebuffer()
{ {
const TextureConfig color_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), // Since we can't partially copy from a depth buffer directly to the staging texture in D3D, we
AbstractTextureFlag_RenderTarget); // use an intermediate buffer to avoid copying the whole texture.
const TextureConfig depth_config( if ((IsUsingTiledEFBCache() && !g_ActiveConfig.backend_info.bSupportsPartialDepthCopies) ||
EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, g_renderer->GetEFBScale() != 1)
AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()),
AbstractTextureFlag_RenderTarget);
if (g_renderer->GetEFBScale() != 1)
{ {
m_color_copy_texture = g_renderer->CreateTexture(color_config); const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH,
m_depth_copy_texture = g_renderer->CreateTexture(depth_config); IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1,
if (!m_color_copy_texture || !m_depth_copy_texture) 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; return false;
m_color_copy_framebuffer = g_renderer->CreateFramebuffer(m_color_copy_texture.get(), nullptr); m_efb_color_cache.framebuffer =
m_depth_copy_framebuffer = g_renderer->CreateFramebuffer(m_depth_copy_texture.get(), nullptr); g_renderer->CreateFramebuffer(m_efb_color_cache.texture.get(), nullptr);
if (!m_color_copy_framebuffer || !m_depth_copy_framebuffer) 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; return false;
} }
m_color_readback_texture = // Staging texture use the full EFB dimensions, as this is the buffer for the whole cache.
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, color_config); m_efb_color_cache.readback_texture = g_renderer->CreateStagingTexture(
m_depth_readback_texture = StagingTextureType::Mutable,
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, depth_config); TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), 0));
if (!m_color_readback_texture || !m_depth_readback_texture) 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; 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; return true;
} }
void FramebufferManager::DestroyReadbackFramebuffer() void FramebufferManager::DestroyReadbackFramebuffer()
{ {
m_depth_copy_framebuffer.reset(); auto DestroyCache = [](EFBCacheData& data) {
m_depth_copy_texture.reset(); data.readback_texture.reset();
m_depth_readback_texture_valid = false; data.framebuffer.reset();
m_color_copy_framebuffer.reset(); data.texture.reset();
m_color_copy_texture.reset(); data.valid = false;
m_color_readback_texture_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<int> rect = GetEFBCacheTileRect(tile_index);
const MathUtil::Rectangle<int> 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<float, 4> 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<int>(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<int>& rc, bool clear_color, void FramebufferManager::ClearEFB(const MathUtil::Rectangle<int>& 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) void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color)
{ {
// Flush if we exceeded the number of vertices per batch. // 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); 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. // See comment above for reasoning for lower-left coordinates.
if (m_color_readback_texture_valid) 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) 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); 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. // See comment above for reasoning for lower-left coordinates.
if (m_depth_readback_texture_valid) 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<EFBPokeVertex>* destination_list, u32 x, void FramebufferManager::CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x,

View File

@ -6,15 +6,16 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include <optional>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "VideoCommon/AbstractTexture.h" #include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/AbstractFramebuffer.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractPipeline.h"
#include "VideoCommon/RenderState.h" #include "VideoCommon/RenderState.h"
#include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureConfig.h"
class AbstractFramebuffer;
class AbstractPipeline;
class AbstractStagingTexture;
class NativeVertexFormat; class NativeVertexFormat;
enum class EFBReinterpretType 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. // Reads a framebuffer value back from the GPU. This may block if the cache is not current.
u32 PeekEFBColor(u32 x, u32 y); u32 PeekEFBColor(u32 x, u32 y);
float PeekEFBDepth(u32 x, u32 y); float PeekEFBDepth(u32 x, u32 y);
void SetEFBCacheTileSize(u32 size);
void InvalidatePeekCache(); void InvalidatePeekCache();
// Writes a value to the framebuffer. This will never block, and writes will be batched. // 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<EFBPokeVertex>::value, "EFBPokeVertex is standard-layout"); static_assert(std::is_standard_layout<EFBPokeVertex>::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<AbstractTexture> texture;
std::unique_ptr<AbstractFramebuffer> framebuffer;
std::unique_ptr<AbstractStagingTexture> readback_texture;
std::unique_ptr<AbstractPipeline> copy_pipeline;
std::vector<bool> tiles;
bool valid;
};
bool CreateEFBFramebuffer(); bool CreateEFBFramebuffer();
void DestroyEFBFramebuffer(); void DestroyEFBFramebuffer();
@ -118,8 +132,10 @@ protected:
bool CompilePokePipelines(); bool CompilePokePipelines();
void DestroyPokePipelines(); void DestroyPokePipelines();
bool PopulateColorReadbackTexture(); bool IsUsingTiledEFBCache() const;
bool PopulateDepthReadbackTexture(); bool IsEFBCacheTilePresent(bool depth, u32 x, u32 y, u32* tile_index) const;
MathUtil::Rectangle<int> GetEFBCacheTileRect(u32 tile_index) const;
void PopulateEFBCache(bool depth, u32 tile_index);
void CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x, u32 y, float z, void CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x, u32 y, float z,
u32 color); u32 color);
@ -141,19 +157,11 @@ protected:
// Format conversion shaders // Format conversion shaders
std::array<std::unique_ptr<AbstractPipeline>, 6> m_format_conversion_pipelines; std::array<std::unique_ptr<AbstractPipeline>, 6> m_format_conversion_pipelines;
// EFB readback texture // EFB cache - for CPU EFB access
std::unique_ptr<AbstractTexture> m_color_copy_texture; u32 m_efb_cache_tile_size = 0;
std::unique_ptr<AbstractTexture> m_depth_copy_texture; u32 m_efb_cache_tiles_wide = 0;
std::unique_ptr<AbstractFramebuffer> m_color_copy_framebuffer; EFBCacheData m_efb_color_cache = {};
std::unique_ptr<AbstractFramebuffer> m_depth_copy_framebuffer; EFBCacheData m_efb_depth_cache = {};
std::unique_ptr<AbstractPipeline> m_color_copy_pipeline;
std::unique_ptr<AbstractPipeline> m_depth_copy_pipeline;
// CPU-side EFB readback texture
std::unique_ptr<AbstractStagingTexture> m_color_readback_texture;
std::unique_ptr<AbstractStagingTexture> m_depth_readback_texture;
bool m_color_readback_texture_valid = false;
bool m_depth_readback_texture_valid = false;
// EFB clear pipelines // EFB clear pipelines
// Indexed by [color_write_enabled][alpha_write_enabled][depth_write_enabled] // Indexed by [color_write_enabled][alpha_write_enabled][depth_write_enabled]

View File

@ -386,6 +386,7 @@ void Renderer::CheckForConfigChanges()
const StereoMode old_stereo = g_ActiveConfig.stereo_mode; const StereoMode old_stereo = g_ActiveConfig.stereo_mode;
const u32 old_multisamples = g_ActiveConfig.iMultisamples; const u32 old_multisamples = g_ActiveConfig.iMultisamples;
const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; 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_force_filtering = g_ActiveConfig.bForceFiltering;
const bool old_vsync = g_ActiveConfig.bVSyncActive; const bool old_vsync = g_ActiveConfig.bVSyncActive;
const bool old_bbox = g_ActiveConfig.bBBoxEnable; const bool old_bbox = g_ActiveConfig.bBBoxEnable;
@ -395,6 +396,10 @@ void Renderer::CheckForConfigChanges()
// Update texture cache settings with any changed options. // Update texture cache settings with any changed options.
g_texture_cache->OnConfigChanged(g_ActiveConfig); 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 // 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. // 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) if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader)

View File

@ -154,6 +154,7 @@ void VideoConfig::Refresh()
bCopyEFBScaled = Config::Get(Config::GFX_HACK_COPY_EFB_SCALED); bCopyEFBScaled = Config::Get(Config::GFX_HACK_COPY_EFB_SCALED);
bEFBEmulateFormatChanges = Config::Get(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES); bEFBEmulateFormatChanges = Config::Get(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES);
bVertexRounding = Config::Get(Config::GFX_HACK_VERTEX_ROUDING); 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); bPerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE);

View File

@ -128,6 +128,7 @@ struct VideoConfig final
bool bEnablePixelLighting; bool bEnablePixelLighting;
bool bFastDepthCalc; bool bFastDepthCalc;
bool bVertexRounding; bool bVertexRounding;
int iEFBAccessTileSize;
int iLog; // CONF_ bits int iLog; // CONF_ bits
int iSaveTargetId; // TODO: Should be dropped int iSaveTargetId; // TODO: Should be dropped