VideoCommon: ignore zero-stride EFB/XFB copies

just clear if requested
This commit is contained in:
Tillmann Karras
2025-12-25 11:09:39 +00:00
parent 932769c4e2
commit 51e488e84a

View File

@@ -298,75 +298,78 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
const u32 copy_width = srcRect.GetWidth();
const u32 copy_height = srcRect.GetHeight();
// Check if we are to copy from the EFB or draw to the XFB
if (PE_copy.copy_to_xfb == 0)
if (destStride != 0)
{
// bpmem.zcontrol.pixel_format to PixelFormat::Z24 is when the game wants to copy from ZBuffer
// (Zbuffer uses 24-bit Format)
bool is_depth_copy = bpmem.zcontrol.pixel_format == PixelFormat::Z24;
g_texture_cache->CopyRenderTargetToTexture(
destAddr, PE_copy.tp_realFormat(), copy_width, copy_height, destStride, is_depth_copy,
srcRect, PE_copy.intensity_fmt && PE_copy.auto_conv, PE_copy.half_scale, 1.0f,
s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
}
else
{
// We should be able to get away with deactivating the current bbox tracking
// here. Not sure if there's a better spot to put this.
// the number of lines copied is determined by the y scale * source efb height
g_bounding_box->Disable(pixel_shader_manager);
float yScale;
if (PE_copy.scale_invert)
yScale = 256.0f / static_cast<float>(bpmem.dispcopyyscale);
else
yScale = static_cast<float>(bpmem.dispcopyyscale) / 256.0f;
float num_xfb_lines = 1.0f + bpmem.copyTexSrcWH.y * yScale;
u32 height = static_cast<u32>(num_xfb_lines);
DEBUG_LOG_FMT(VIDEO,
"RenderToXFB: destAddr: {:08x} | srcRect [{} {} {} {}] | fbWidth: {} | "
"fbStride: {} | fbHeight: {} | yScale: {}",
destAddr, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
bpmem.copyTexSrcWH.x + 1, destStride, height, yScale);
bool is_depth_copy = bpmem.zcontrol.pixel_format == PixelFormat::Z24;
g_texture_cache->CopyRenderTargetToTexture(
destAddr, EFBCopyFormat::XFB, copy_width, height, destStride, is_depth_copy, srcRect,
false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
auto& system = Core::System::GetInstance();
// This is as closest as we have to an "end of the frame"
// It works 99% of the time.
// But sometimes games want to render an XFB larger than the EFB's 640x528 pixel resolution
// (especially when using the 3xMSAA mode, which cuts EFB resolution to 640x264). So they
// render multiple sub-frames and arrange the XFB copies in next to each-other in main memory
// so they form a single completed XFB.
// See https://dolphin-emu.org/blog/2017/11/19/hybridxfb/ for examples and more detail.
system.GetVideoEvents().after_frame_event.Trigger(system);
// Note: Theoretically, in the future we could track the VI configuration and try to detect
// when an XFB is the last XFB copy of a frame. Not only would we get a clean "end of
// the frame", but we would also be able to use ImmediateXFB even for these games.
// Might also clean up some issues with games doing XFB copies they don't intend to
// display.
if (g_ActiveConfig.bImmediateXFB)
// Check if we are to copy from the EFB or draw to the XFB
if (PE_copy.copy_to_xfb == 0)
{
// below div two to convert from bytes to pixels - it expects width, not stride
g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height);
// bpmem.zcontrol.pixel_format to PixelFormat::Z24 is when the game wants to copy from
// ZBuffer (Zbuffer uses 24-bit Format)
bool is_depth_copy = bpmem.zcontrol.pixel_format == PixelFormat::Z24;
g_texture_cache->CopyRenderTargetToTexture(
destAddr, PE_copy.tp_realFormat(), copy_width, copy_height, destStride, is_depth_copy,
srcRect, PE_copy.intensity_fmt && PE_copy.auto_conv, PE_copy.half_scale, 1.0f,
s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
}
else
{
if (system.GetFifoPlayer().IsRunningWithFakeVideoInterfaceUpdates())
// We should be able to get away with deactivating the current bbox tracking
// here. Not sure if there's a better spot to put this.
// the number of lines copied is determined by the y scale * source efb height
g_bounding_box->Disable(pixel_shader_manager);
float yScale;
if (PE_copy.scale_invert)
yScale = 256.0f / static_cast<float>(bpmem.dispcopyyscale);
else
yScale = static_cast<float>(bpmem.dispcopyyscale) / 256.0f;
float num_xfb_lines = 1.0f + bpmem.copyTexSrcWH.y * yScale;
u32 height = static_cast<u32>(num_xfb_lines);
DEBUG_LOG_FMT(VIDEO,
"RenderToXFB: destAddr: {:08x} | srcRect [{} {} {} {}] | fbWidth: {} | "
"fbStride: {} | fbHeight: {} | yScale: {}",
destAddr, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
bpmem.copyTexSrcWH.x + 1, destStride, height, yScale);
bool is_depth_copy = bpmem.zcontrol.pixel_format == PixelFormat::Z24;
g_texture_cache->CopyRenderTargetToTexture(
destAddr, EFBCopyFormat::XFB, copy_width, height, destStride, is_depth_copy, srcRect,
false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
auto& system = Core::System::GetInstance();
// This is as closest as we have to an "end of the frame"
// It works 99% of the time.
// But sometimes games want to render an XFB larger than the EFB's 640x528 pixel resolution
// (especially when using the 3xMSAA mode, which cuts EFB resolution to 640x264). So they
// render multiple sub-frames and arrange the XFB copies in next to each-other in main
// memory so they form a single completed XFB. See
// https://dolphin-emu.org/blog/2017/11/19/hybridxfb/ for examples and more detail.
system.GetVideoEvents().after_frame_event.Trigger(system);
// Note: Theoretically, in the future we could track the VI configuration and try to detect
// when an XFB is the last XFB copy of a frame. Not only would we get a clean "end of
// the frame", but we would also be able to use ImmediateXFB even for these games.
// Might also clean up some issues with games doing XFB copies they don't intend to
// display.
if (g_ActiveConfig.bImmediateXFB)
{
auto& vi = system.GetVideoInterface();
vi.FakeVIUpdate(destAddr, srcRect.GetWidth(), destStride, height);
// below div two to convert from bytes to pixels - it expects width, not stride
g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height);
}
else
{
if (system.GetFifoPlayer().IsRunningWithFakeVideoInterfaceUpdates())
{
auto& vi = system.GetVideoInterface();
vi.FakeVIUpdate(destAddr, srcRect.GetWidth(), destStride, height);
}
}
}
}