FramebufferManager: Correctly handle read back D24S8 textures

Needed for the Adreno/Vulkan workaround, and if we ever switch to a D24
texture for the depth buffer w/ unrestricted depth range.
This commit is contained in:
Stenzek 2019-07-31 15:33:27 +10:00
parent 2698e311aa
commit 06daf58032
4 changed files with 54 additions and 39 deletions

View File

@ -88,20 +88,19 @@ bool AbstractTexture::IsStencilFormat(AbstractTextureFormat format)
return format == AbstractTextureFormat::D24_S8 || format == AbstractTextureFormat::D32F_S8; return format == AbstractTextureFormat::D24_S8 || format == AbstractTextureFormat::D32F_S8;
} }
AbstractTextureFormat AbstractTexture::GetColorFormatForDepthFormat(AbstractTextureFormat format) bool AbstractTexture::IsCompatibleDepthAndColorFormats(AbstractTextureFormat depth_format,
AbstractTextureFormat color_format)
{ {
switch (format) switch (depth_format)
{ {
case AbstractTextureFormat::D16: case AbstractTextureFormat::D16:
return AbstractTextureFormat::R16; return color_format == AbstractTextureFormat::R16;
case AbstractTextureFormat::D24_S8: // TODO: Incorrect
case AbstractTextureFormat::D32F: case AbstractTextureFormat::D32F:
case AbstractTextureFormat::D32F_S8: return color_format == AbstractTextureFormat::R32F;
return AbstractTextureFormat::R32F;
default: default:
return format; return false;
} }
} }

View File

@ -44,7 +44,8 @@ public:
static bool IsCompressedFormat(AbstractTextureFormat format); static bool IsCompressedFormat(AbstractTextureFormat format);
static bool IsDepthFormat(AbstractTextureFormat format); static bool IsDepthFormat(AbstractTextureFormat format);
static bool IsStencilFormat(AbstractTextureFormat format); static bool IsStencilFormat(AbstractTextureFormat format);
static AbstractTextureFormat GetColorFormatForDepthFormat(AbstractTextureFormat format); static bool IsCompatibleDepthAndColorFormats(AbstractTextureFormat depth_format,
AbstractTextureFormat color_format);
static u32 CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length); static u32 CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length);
static u32 GetTexelSizeForFormat(AbstractTextureFormat format); static u32 GetTexelSizeForFormat(AbstractTextureFormat format);
static u32 GetBlockSizeForFormat(AbstractTextureFormat format); static u32 GetBlockSizeForFormat(AbstractTextureFormat format);

View File

@ -129,6 +129,11 @@ AbstractTextureFormat FramebufferManager::GetEFBDepthFormat()
return AbstractTextureFormat::D32F; return AbstractTextureFormat::D32F;
} }
AbstractTextureFormat FramebufferManager::GetEFBDepthCopyFormat()
{
return AbstractTextureFormat::R32F;
}
static u32 CalculateEFBLayers() static u32 CalculateEFBLayers()
{ {
return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? 2 : 1; return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? 2 : 1;
@ -183,11 +188,10 @@ bool FramebufferManager::CreateEFBFramebuffer()
m_efb_resolve_color_texture = g_renderer->CreateTexture( m_efb_resolve_color_texture = g_renderer->CreateTexture(
TextureConfig(efb_color_texture_config.width, efb_color_texture_config.height, 1, TextureConfig(efb_color_texture_config.width, efb_color_texture_config.height, 1,
efb_color_texture_config.layers, 1, efb_color_texture_config.format, 0)); efb_color_texture_config.layers, 1, efb_color_texture_config.format, 0));
m_efb_depth_resolve_texture = g_renderer->CreateTexture(TextureConfig( m_efb_depth_resolve_texture = g_renderer->CreateTexture(
efb_depth_texture_config.width, efb_depth_texture_config.height, 1, TextureConfig(efb_depth_texture_config.width, efb_depth_texture_config.height, 1,
efb_depth_texture_config.layers, 1, efb_depth_texture_config.layers, 1, GetEFBDepthCopyFormat(),
AbstractTexture::GetColorFormatForDepthFormat(efb_depth_texture_config.format), AbstractTextureFlag_RenderTarget));
AbstractTextureFlag_RenderTarget));
if (!m_efb_resolve_color_texture || !m_efb_depth_resolve_texture) if (!m_efb_resolve_color_texture || !m_efb_depth_resolve_texture)
return false; return false;
@ -447,8 +451,7 @@ bool FramebufferManager::CompileReadbackPipelines()
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 = GetEFBDepthCopyFormat();
AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat());
m_efb_depth_cache.copy_pipeline = g_renderer->CreatePipeline(config); m_efb_depth_cache.copy_pipeline = g_renderer->CreatePipeline(config);
if (!m_efb_depth_cache.copy_pipeline) if (!m_efb_depth_cache.copy_pipeline)
return false; return false;
@ -492,29 +495,39 @@ void FramebufferManager::DestroyReadbackPipelines()
bool FramebufferManager::CreateReadbackFramebuffer() bool FramebufferManager::CreateReadbackFramebuffer()
{ {
// Since we can't partially copy from a depth buffer directly to the staging texture in D3D, we if (g_renderer->GetEFBScale() != 1)
// use an intermediate buffer to avoid copying the whole texture.
if ((IsUsingTiledEFBCache() && !g_ActiveConfig.backend_info.bSupportsPartialDepthCopies) ||
g_renderer->GetEFBScale() != 1)
{ {
const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH, const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH,
IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1, IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1,
1, 1, GetEFBColorFormat(), AbstractTextureFlag_RenderTarget); 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_color_cache.texture = g_renderer->CreateTexture(color_config);
m_efb_depth_cache.texture = g_renderer->CreateTexture(depth_config); if (!m_efb_color_cache.texture)
if (!m_efb_color_cache.texture || !m_efb_depth_cache.texture)
return false; return false;
m_efb_color_cache.framebuffer = m_efb_color_cache.framebuffer =
g_renderer->CreateFramebuffer(m_efb_color_cache.texture.get(), nullptr); g_renderer->CreateFramebuffer(m_efb_color_cache.texture.get(), nullptr);
if (!m_efb_color_cache.framebuffer)
return false;
}
// 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) ||
!AbstractTexture::IsCompatibleDepthAndColorFormats(m_efb_depth_texture->GetFormat(),
GetEFBDepthCopyFormat()) ||
g_renderer->GetEFBScale() != 1)
{
const TextureConfig depth_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH,
IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1,
1, 1, GetEFBDepthCopyFormat(),
AbstractTextureFlag_RenderTarget);
m_efb_depth_cache.texture = g_renderer->CreateTexture(depth_config);
if (!m_efb_depth_cache.texture)
return false;
m_efb_depth_cache.framebuffer = m_efb_depth_cache.framebuffer =
g_renderer->CreateFramebuffer(m_efb_depth_cache.texture.get(), nullptr); g_renderer->CreateFramebuffer(m_efb_depth_cache.texture.get(), nullptr);
if (!m_efb_color_cache.framebuffer || !m_efb_depth_cache.framebuffer) if (!m_efb_depth_cache.framebuffer)
return false; return false;
} }
@ -524,8 +537,7 @@ bool FramebufferManager::CreateReadbackFramebuffer()
TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), 0)); TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), 0));
m_efb_depth_cache.readback_texture = g_renderer->CreateStagingTexture( m_efb_depth_cache.readback_texture = g_renderer->CreateStagingTexture(
StagingTextureType::Mutable, StagingTextureType::Mutable,
TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBDepthCopyFormat(), 0));
AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()), 0));
if (!m_efb_color_cache.readback_texture || !m_efb_depth_cache.readback_texture) if (!m_efb_color_cache.readback_texture || !m_efb_depth_cache.readback_texture)
return false; return false;
@ -563,7 +575,10 @@ void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index)
// Force the path through the intermediate texture, as we can't do an image copy from a depth // 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). // buffer directly to a staging texture (must be the whole resource).
const bool force_intermediate_copy = const bool force_intermediate_copy =
depth && !g_ActiveConfig.backend_info.bSupportsPartialDepthCopies && IsUsingTiledEFBCache(); depth &&
((!g_ActiveConfig.backend_info.bSupportsPartialDepthCopies && IsUsingTiledEFBCache()) ||
!AbstractTexture::IsCompatibleDepthAndColorFormats(m_efb_depth_texture->GetFormat(),
GetEFBDepthCopyFormat()));
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. // 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; EFBCacheData& data = depth ? m_efb_depth_cache : m_efb_color_cache;
@ -888,21 +903,20 @@ void FramebufferManager::DoSaveState(PointerWrap& p)
1, GetEFBColorFormat(), 0); 1, GetEFBColorFormat(), 0);
g_texture_cache->SerializeTexture(color_texture, color_texture_config, p); g_texture_cache->SerializeTexture(color_texture, color_texture_config, p);
if (GetEFBDepthFormat() == AbstractTextureFormat::D32F) if (AbstractTexture::IsCompatibleDepthAndColorFormats(m_efb_depth_texture->GetFormat(),
GetEFBDepthCopyFormat()))
{ {
const TextureConfig depth_texture_config( const TextureConfig depth_texture_config(depth_texture->GetWidth(), depth_texture->GetHeight(),
depth_texture->GetWidth(), depth_texture->GetHeight(), depth_texture->GetLevels(), depth_texture->GetLevels(), depth_texture->GetLayers(),
depth_texture->GetLayers(), 1, 1, GetEFBDepthCopyFormat(), 0);
AbstractTexture::GetColorFormatForDepthFormat(GetEFBDepthFormat()), 0);
g_texture_cache->SerializeTexture(depth_texture, depth_texture_config, p); g_texture_cache->SerializeTexture(depth_texture, depth_texture_config, p);
} }
else else
{ {
// If the EFB is backed by a D24S8 texture, we first have to convert it to R32F. // If the EFB is backed by a D24S8 texture, we first have to convert it to R32F.
const TextureConfig temp_texture_config(depth_texture->GetWidth(), depth_texture->GetHeight(), const TextureConfig temp_texture_config(
depth_texture->GetLevels(), depth_texture->GetLayers(), depth_texture->GetWidth(), depth_texture->GetHeight(), depth_texture->GetLevels(),
1, AbstractTextureFormat::R32F, depth_texture->GetLayers(), 1, GetEFBDepthCopyFormat(), AbstractTextureFlag_RenderTarget);
AbstractTextureFlag_RenderTarget);
std::unique_ptr<AbstractTexture> temp_texture = g_renderer->CreateTexture(temp_texture_config); std::unique_ptr<AbstractTexture> temp_texture = g_renderer->CreateTexture(temp_texture_config);
std::unique_ptr<AbstractFramebuffer> temp_fb = std::unique_ptr<AbstractFramebuffer> temp_fb =
g_renderer->CreateFramebuffer(temp_texture.get(), nullptr); g_renderer->CreateFramebuffer(temp_texture.get(), nullptr);

View File

@ -44,6 +44,7 @@ public:
// Does not require the framebuffer to be created. Slower than direct queries. // Does not require the framebuffer to be created. Slower than direct queries.
static AbstractTextureFormat GetEFBColorFormat(); static AbstractTextureFormat GetEFBColorFormat();
static AbstractTextureFormat GetEFBDepthFormat(); static AbstractTextureFormat GetEFBDepthFormat();
static AbstractTextureFormat GetEFBDepthCopyFormat();
static TextureConfig GetEFBColorTextureConfig(); static TextureConfig GetEFBColorTextureConfig();
static TextureConfig GetEFBDepthTextureConfig(); static TextureConfig GetEFBDepthTextureConfig();