mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
TextureCache: Simplify XFB reconstruction
This also better handles in-memory interlaced XFB data placed by the CPU by considering the stride from the VI.
This commit is contained in:
@@ -686,6 +686,9 @@ static void BeginField(FieldType field, u64 ticks)
|
||||
xfbAddr = GetXFBAddressTop();
|
||||
}
|
||||
|
||||
// Multiply the stride by 2 to get the byte offset for each subsequent line.
|
||||
fbStride *= 2;
|
||||
|
||||
if (potentially_interlaced_xfb && interlaced_video_mode && g_ActiveConfig.bForceProgressive)
|
||||
{
|
||||
// Strictly speaking, in interlaced mode, we're only supposed to read
|
||||
@@ -704,10 +707,10 @@ static void BeginField(FieldType field, u64 ticks)
|
||||
// offset the xfb by (-stride_of_one_line) to get the start
|
||||
// address of the full xfb.
|
||||
if (field == FieldType::Odd && m_VBlankTimingOdd.PRB == m_VBlankTimingEven.PRB + 1 && xfbAddr)
|
||||
xfbAddr -= fbStride * 2;
|
||||
xfbAddr -= fbStride;
|
||||
|
||||
if (field == FieldType::Even && m_VBlankTimingOdd.PRB == m_VBlankTimingEven.PRB - 1 && xfbAddr)
|
||||
xfbAddr -= fbStride * 2;
|
||||
xfbAddr -= fbStride;
|
||||
}
|
||||
|
||||
LogField(field, xfbAddr);
|
||||
@@ -787,10 +790,8 @@ void Update(u64 ticks)
|
||||
}
|
||||
|
||||
// Create a fake VI mode for a fifolog
|
||||
void FakeVIUpdate(u32 xfb_address, u32 fb_width, u32 fb_height)
|
||||
void FakeVIUpdate(u32 xfb_address, u32 fb_width, u32 fb_stride, u32 fb_height)
|
||||
{
|
||||
u32 fb_stride = fb_width;
|
||||
|
||||
bool interlaced = fb_height > 480 / 2;
|
||||
if (interlaced)
|
||||
{
|
||||
@@ -807,7 +808,7 @@ void FakeVIUpdate(u32 xfb_address, u32 fb_width, u32 fb_height)
|
||||
m_VBlankTimingEven.PRB = 503 - fb_height * 2;
|
||||
m_VBlankTimingEven.PSB = 4;
|
||||
m_PictureConfiguration.WPL = fb_width / 16;
|
||||
m_PictureConfiguration.STD = fb_stride / 16;
|
||||
m_PictureConfiguration.STD = (fb_stride / 2) / 16;
|
||||
|
||||
UpdateParameters();
|
||||
|
||||
|
||||
@@ -374,6 +374,6 @@ u32 GetTicksPerField();
|
||||
float GetAspectRatio();
|
||||
|
||||
// Create a fake VI mode for a fifolog
|
||||
void FakeVIUpdate(u32 xfb_address, u32 fb_width, u32 fb_height);
|
||||
void FakeVIUpdate(u32 xfb_address, u32 fb_width, u32 fb_stride, u32 fb_height);
|
||||
|
||||
} // namespace VideoInterface
|
||||
|
||||
@@ -113,7 +113,6 @@ void AsyncRequests::SetEnable(bool enable)
|
||||
|
||||
void AsyncRequests::HandleEvent(const AsyncRequests::Event& e)
|
||||
{
|
||||
EFBRectangle rc;
|
||||
switch (e.type)
|
||||
{
|
||||
case Event::EFB_POKE_COLOR:
|
||||
@@ -145,7 +144,7 @@ void AsyncRequests::HandleEvent(const AsyncRequests::Event& e)
|
||||
|
||||
case Event::SWAP_EVENT:
|
||||
g_renderer->Swap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride,
|
||||
e.swap_event.fbHeight, rc, e.time);
|
||||
e.swap_event.fbHeight, e.time);
|
||||
break;
|
||||
|
||||
case Event::BBOX_READ:
|
||||
|
||||
@@ -312,14 +312,13 @@ static void BPWritten(const BPCmd& bp)
|
||||
if (g_ActiveConfig.bImmediateXFB)
|
||||
{
|
||||
// below div two to convert from bytes to pixels - it expects width, not stride
|
||||
g_renderer->Swap(destAddr, destStride / 2, destStride / 2, height, srcRect,
|
||||
CoreTiming::GetTicks());
|
||||
g_renderer->Swap(destAddr, destStride / 2, destStride, height, CoreTiming::GetTicks());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FifoPlayer::GetInstance().IsRunningWithFakeVideoInterfaceUpdates())
|
||||
{
|
||||
VideoInterface::FakeVIUpdate(destAddr, srcRect.GetWidth(), height);
|
||||
VideoInterface::FakeVIUpdate(destAddr, srcRect.GetWidth(), destStride, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1154,8 +1154,7 @@ void Renderer::EndUIFrame()
|
||||
BeginImGuiFrame();
|
||||
}
|
||||
|
||||
void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
|
||||
u64 ticks)
|
||||
void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
|
||||
{
|
||||
const AspectMode suggested = g_ActiveConfig.suggested_aspect_mode;
|
||||
if (suggested == AspectMode::Analog || suggested == AspectMode::AnalogWide)
|
||||
@@ -1188,33 +1187,15 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
||||
// behind the renderer.
|
||||
FlushFrameDump();
|
||||
|
||||
if (xfbAddr && fbWidth && fbStride && fbHeight)
|
||||
if (xfb_addr && fb_width && fb_stride && fb_height)
|
||||
{
|
||||
constexpr int force_safe_texture_cache_hash = 0;
|
||||
// Get the current XFB from texture cache
|
||||
auto* xfb_entry = g_texture_cache->GetXFBTexture(
|
||||
xfbAddr, fbStride, fbHeight, TextureFormat::XFB, force_safe_texture_cache_hash);
|
||||
|
||||
MathUtil::Rectangle<int> xfb_rect;
|
||||
const auto* xfb_entry =
|
||||
g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect);
|
||||
if (xfb_entry && xfb_entry->id != m_last_xfb_id)
|
||||
{
|
||||
const TextureConfig& texture_config = xfb_entry->texture->GetConfig();
|
||||
m_last_xfb_texture = xfb_entry->texture.get();
|
||||
m_last_xfb_id = xfb_entry->id;
|
||||
m_last_xfb_ticks = ticks;
|
||||
|
||||
auto xfb_rect = texture_config.GetRect();
|
||||
|
||||
// It's possible that the returned XFB texture is native resolution
|
||||
// even when we're rendering at higher than native resolution
|
||||
// if the XFB was was loaded entirely from console memory.
|
||||
// If so, adjust the rectangle by native resolution instead of scaled resolution.
|
||||
const u32 native_stride_width_difference = fbStride - fbWidth;
|
||||
if (texture_config.width == xfb_entry->native_width)
|
||||
xfb_rect.right -= native_stride_width_difference;
|
||||
else
|
||||
xfb_rect.right -= EFBToScaledX(native_stride_width_difference);
|
||||
|
||||
m_last_xfb_region = xfb_rect;
|
||||
|
||||
// Since we use the common pipelines here and draw vertices if a batch is currently being
|
||||
// built by the vertex loader, we end up trampling over its pointer, as we share the buffer
|
||||
@@ -1247,7 +1228,7 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
||||
|
||||
// Update the window size based on the frame that was just rendered.
|
||||
// Due to depending on guest state, we need to call this every frame.
|
||||
SetWindowSize(texture_config.width, texture_config.height);
|
||||
SetWindowSize(xfb_rect.GetWidth(), xfb_rect.GetHeight());
|
||||
}
|
||||
|
||||
m_fps_counter.Update();
|
||||
@@ -1259,7 +1240,7 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
||||
DolphinAnalytics::Instance()->ReportPerformanceInfo(std::move(perf_sample));
|
||||
|
||||
if (IsFrameDumping())
|
||||
DumpCurrentFrame();
|
||||
DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks);
|
||||
|
||||
// Begin new frame
|
||||
m_frame_count++;
|
||||
@@ -1295,8 +1276,8 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
||||
}
|
||||
|
||||
// Update our last xfb values
|
||||
m_last_xfb_width = (fbStride < 1 || fbStride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fbStride;
|
||||
m_last_xfb_height = (fbHeight < 1 || fbHeight > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fbHeight;
|
||||
m_last_xfb_width = (fb_width < 1 || fb_width > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fb_width;
|
||||
m_last_xfb_height = (fb_height < 1 || fb_height > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fb_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1333,8 +1314,11 @@ bool Renderer::IsFrameDumping()
|
||||
return false;
|
||||
}
|
||||
|
||||
void Renderer::DumpCurrentFrame()
|
||||
void Renderer::DumpCurrentFrame(const AbstractTexture* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect, u64 ticks)
|
||||
{
|
||||
int source_width = src_rect.GetWidth();
|
||||
int source_height = src_rect.GetHeight();
|
||||
int target_width, target_height;
|
||||
if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless())
|
||||
{
|
||||
@@ -1344,22 +1328,20 @@ void Renderer::DumpCurrentFrame()
|
||||
}
|
||||
else
|
||||
{
|
||||
std::tie(target_width, target_height) = CalculateOutputDimensions(
|
||||
m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().height);
|
||||
std::tie(target_width, target_height) = CalculateOutputDimensions(source_width, source_height);
|
||||
}
|
||||
|
||||
// We only need to render a copy if we need to stretch/scale the XFB copy.
|
||||
const AbstractTexture* source_tex = m_last_xfb_texture;
|
||||
MathUtil::Rectangle<int> source_rect = m_last_xfb_region;
|
||||
if (source_rect.GetWidth() != target_width || source_rect.GetHeight() != target_height)
|
||||
MathUtil::Rectangle<int> copy_rect = src_rect;
|
||||
if (source_width != target_width || source_height != target_height)
|
||||
{
|
||||
if (!CheckFrameDumpRenderTexture(target_width, target_height))
|
||||
return;
|
||||
|
||||
source_tex = m_frame_dump_render_texture.get();
|
||||
source_rect = MathUtil::Rectangle<int>(0, 0, target_width, target_height);
|
||||
ScaleTexture(m_frame_dump_render_framebuffer.get(), source_rect, m_last_xfb_texture,
|
||||
m_last_xfb_region);
|
||||
ScaleTexture(m_frame_dump_render_framebuffer.get(), m_frame_dump_render_framebuffer->GetRect(),
|
||||
src_texture, src_rect);
|
||||
src_texture = m_frame_dump_render_texture.get();
|
||||
copy_rect = src_texture->GetRect();
|
||||
}
|
||||
|
||||
// Index 0 was just sent to AVI dump. Swap with the second texture.
|
||||
@@ -1369,12 +1351,9 @@ void Renderer::DumpCurrentFrame()
|
||||
if (!CheckFrameDumpReadbackTexture(target_width, target_height))
|
||||
return;
|
||||
|
||||
const auto converted_region =
|
||||
ConvertFramebufferRectangle(source_rect, source_tex->GetWidth(), source_tex->GetHeight());
|
||||
m_frame_dump_readback_textures[0]->CopyFromTexture(
|
||||
source_tex, converted_region, 0, 0,
|
||||
MathUtil::Rectangle<int>(0, 0, target_width, target_height));
|
||||
m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks);
|
||||
m_frame_dump_readback_textures[0]->CopyFromTexture(src_texture, copy_rect, 0, 0,
|
||||
m_frame_dump_readback_textures[0]->GetRect());
|
||||
m_last_frame_state = AVIDump::FetchState(ticks);
|
||||
m_last_frame_exported = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,8 +213,7 @@ public:
|
||||
virtual void WaitForGPUIdle() {}
|
||||
|
||||
// Finish up the current frame, print some stats
|
||||
void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
|
||||
u64 ticks);
|
||||
void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks);
|
||||
|
||||
// Draws the specified XFB buffer to the screen, performing any post-processing.
|
||||
// Assumes that the backbuffer has already been bound and cleared.
|
||||
@@ -350,10 +349,7 @@ private:
|
||||
bool m_last_frame_exported = false;
|
||||
|
||||
// Tracking of XFB textures so we don't render duplicate frames.
|
||||
AbstractTexture* m_last_xfb_texture = nullptr;
|
||||
u64 m_last_xfb_id = std::numeric_limits<u64>::max();
|
||||
u64 m_last_xfb_ticks = 0;
|
||||
EFBRectangle m_last_xfb_region;
|
||||
|
||||
// Note: Only used for auto-ir
|
||||
u32 m_last_xfb_width = MAX_XFB_WIDTH;
|
||||
@@ -377,7 +373,8 @@ private:
|
||||
bool CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height);
|
||||
|
||||
// Fills the frame dump staging texture with the current XFB texture.
|
||||
void DumpCurrentFrame();
|
||||
void DumpCurrentFrame(const AbstractTexture* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect, u64 ticks);
|
||||
|
||||
// Asynchronously encodes the specified pointer of frame data to the frame dump.
|
||||
void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -77,42 +77,6 @@ struct EFBCopyFilterCoefficients
|
||||
float lower;
|
||||
};
|
||||
|
||||
struct TextureLookupInformation
|
||||
{
|
||||
u32 address;
|
||||
|
||||
u32 block_width;
|
||||
u32 block_height;
|
||||
u32 bytes_per_block;
|
||||
|
||||
u32 expanded_width;
|
||||
u32 expanded_height;
|
||||
u32 native_width;
|
||||
u32 native_height;
|
||||
u32 total_bytes;
|
||||
u32 native_levels = 1;
|
||||
u32 computed_levels;
|
||||
|
||||
u64 base_hash;
|
||||
u64 full_hash;
|
||||
|
||||
TextureAndTLUTFormat full_format;
|
||||
u32 tlut_address = 0;
|
||||
|
||||
bool is_palette_texture = false;
|
||||
u32 palette_size = 0;
|
||||
|
||||
bool use_mipmaps = false;
|
||||
|
||||
bool from_tmem = false;
|
||||
u32 tmem_address_even = 0;
|
||||
u32 tmem_address_odd = 0;
|
||||
|
||||
int texture_cache_safety_color_sample_size = 0; // Default to safe hashing
|
||||
|
||||
u8* src_data;
|
||||
};
|
||||
|
||||
class TextureCacheBase
|
||||
{
|
||||
private:
|
||||
@@ -138,6 +102,7 @@ public:
|
||||
// content, aren't just downscaled
|
||||
bool should_force_safe_hashing = false; // for XFB
|
||||
bool is_xfb_copy = false;
|
||||
bool is_xfb_container = false;
|
||||
u64 id;
|
||||
|
||||
bool reference_changed = false; // used by xfb to determine when a reference xfb changed
|
||||
@@ -243,20 +208,9 @@ public:
|
||||
TLUTFormat tlutfmt = TLUTFormat::IA8, bool use_mipmaps = false,
|
||||
u32 tex_levels = 1, bool from_tmem = false, u32 tmem_address_even = 0,
|
||||
u32 tmem_address_odd = 0);
|
||||
TCacheEntry* GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
|
||||
MathUtil::Rectangle<int>* display_rect);
|
||||
|
||||
TCacheEntry* GetXFBTexture(u32 address, u32 width, u32 height, TextureFormat texformat,
|
||||
int textureCacheSafetyColorSampleSize);
|
||||
std::optional<TextureLookupInformation>
|
||||
ComputeTextureInformation(u32 address, u32 width, u32 height, TextureFormat texformat,
|
||||
int textureCacheSafetyColorSampleSize, bool from_tmem,
|
||||
u32 tmem_address_even, u32 tmem_address_odd, u32 tlutaddr,
|
||||
TLUTFormat tlutfmt, u32 levels);
|
||||
TCacheEntry* GetXFBFromCache(const TextureLookupInformation& tex_info);
|
||||
TCacheEntry* GetTextureFromOverlappingTextures(const TextureLookupInformation& tex_info);
|
||||
TCacheEntry* GetTextureFromMemory(const TextureLookupInformation& tex_info);
|
||||
TCacheEntry* CreateNormalTexture(const TextureLookupInformation& tex_info, u32 layers);
|
||||
void LoadTextureLevelZeroFromMemory(TCacheEntry* entry_to_update,
|
||||
const TextureLookupInformation& tex_info, bool decode_on_gpu);
|
||||
virtual void BindTextures();
|
||||
void CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, u32 width, u32 height,
|
||||
u32 dstStride, bool is_depth_copy, const EFBRectangle& srcRect,
|
||||
@@ -322,10 +276,13 @@ private:
|
||||
|
||||
void SetBackupConfig(const VideoConfig& config);
|
||||
|
||||
TCacheEntry* GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride, u64 hash);
|
||||
|
||||
TCacheEntry* ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, TLUTFormat tlutfmt);
|
||||
|
||||
TCacheEntry* DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette,
|
||||
TLUTFormat tlutfmt);
|
||||
void StitchXFBCopy(TCacheEntry* entry_to_update);
|
||||
|
||||
void DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level, bool is_arbitrary);
|
||||
void CheckTempSize(size_t required_size);
|
||||
|
||||
Reference in New Issue
Block a user