From b25ed6c151e65d630e2c2a1ce449a8bdbd7109a8 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 17 May 2020 01:02:20 +1000 Subject: [PATCH] GPU: Fix interlaced rendering in vblank breaking --- src/core/gpu.cpp | 27 ++++++++++++++++----------- src/core/gpu.h | 7 +++++-- src/core/gpu_hw.cpp | 2 +- src/core/gpu_hw_d3d11.cpp | 4 ++-- src/core/gpu_hw_opengl.cpp | 4 ++-- src/core/gpu_sw.cpp | 4 ++-- src/core/save_state_version.h | 2 +- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 8dcd32e47..cac3f8b56 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -158,6 +158,8 @@ bool GPU::DoState(StateWrapper& sw) sw.Do(&m_crtc_state.current_scanline); sw.Do(&m_crtc_state.in_hblank); sw.Do(&m_crtc_state.in_vblank); + sw.Do(&m_crtc_state.displaying_odd_field); + sw.Do(&m_crtc_state.displaying_odd_lines); sw.Do(&m_blitter_state); sw.Do(&m_command_ticks); @@ -683,6 +685,12 @@ void GPU::Execute(TickCount ticks) FlushRender(); UpdateDisplay(); m_system->IncrementFrameNumber(); + + // switch fields early. this is needed so we draw to the correct one. + if (m_GPUSTAT.vertical_interlace) + m_crtc_state.displaying_odd_field ^= true; + else + m_crtc_state.displaying_odd_field = false; } m_timers->SetGate(HBLANK_TIMER_INDEX, new_vblank); @@ -694,23 +702,19 @@ void GPU::Execute(TickCount ticks) { // start the new frame m_crtc_state.current_scanline = 0; - - // switch fields for interlaced modes - if (m_GPUSTAT.vertical_interlace) - m_GPUSTAT.interlaced_field ^= true; - else - m_GPUSTAT.interlaced_field = false; } } // alternating even line bit in 240-line mode if (m_GPUSTAT.In480iMode()) { - m_GPUSTAT.displaying_odd_line = ConvertToBoolUnchecked( - (m_crtc_state.regs.Y + BoolToUInt32(m_GPUSTAT.interlaced_field && !m_crtc_state.in_vblank)) & u32(1)); + m_crtc_state.displaying_odd_lines = + ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(m_crtc_state.displaying_odd_field)) & u32(1)); + m_GPUSTAT.displaying_odd_line = m_crtc_state.displaying_odd_lines && !m_crtc_state.in_vblank; } else { + m_crtc_state.displaying_odd_lines = false; m_GPUSTAT.displaying_odd_line = ConvertToBoolUnchecked((m_crtc_state.regs.Y + m_crtc_state.current_scanline) & u32(1)); } @@ -1009,7 +1013,8 @@ void GPU::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) // Hardware tests show that fills seem to break on the first two lines when the offset matches the displayed field. if (IsRasterScanlinePending()) Synchronize(); - const u32 active_field = GetInterlacedField(); + + const u32 active_field = GetInterlacedDisplayLineOffset(); for (u32 yoffs = 0; yoffs < height; yoffs++) { const u32 row = (y + yoffs) % VRAM_HEIGHT; @@ -1300,9 +1305,9 @@ void GPU::DrawDebugStateWindow() const auto& cs = m_crtc_state; ImGui::Text("Dot Clock Divider: %u", cs.dot_clock_divider); ImGui::Text("Vertical Interlace: %s (%s field)", m_GPUSTAT.vertical_interlace ? "Yes" : "No", - m_GPUSTAT.interlaced_field ? "odd" : "even"); + m_crtc_state.displaying_odd_field ? "odd" : "even"); ImGui::Text("Display Disable: %s", m_GPUSTAT.display_disable ? "Yes" : "No"); - ImGui::Text("Displaying Odd Line/Field: %s", m_GPUSTAT.displaying_odd_line ? "Yes" : "No"); + ImGui::Text("Displaying Odd Lines: %s", m_crtc_state.displaying_odd_lines ? "Yes" : "No"); ImGui::Text("Color Depth: %u-bit", m_GPUSTAT.display_area_color_depth_24 ? 24 : 15); ImGui::Text("Start Offset: (%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue()); ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total, diff --git a/src/core/gpu.h b/src/core/gpu.h index bbf4f93c9..e7b249e3a 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -350,8 +350,8 @@ protected: return (!m_force_progressive_scan) & m_GPUSTAT.SkipDrawingToActiveField(); } - /// Returns 0 if the currently-rendered field is even, otherwise 1. - ALWAYS_INLINE u32 GetInterlacedField() const { return BoolToUInt32(m_GPUSTAT.displaying_odd_line); } + /// Returns 0 if the currently-displayed field is on an even line, otherwise 1. + ALWAYS_INLINE u32 GetInterlacedDisplayLineOffset() const { return BoolToUInt32(m_crtc_state.displaying_odd_lines); } /// Sets/decodes GP0(E1h) (set draw mode). void SetDrawMode(u16 bits); @@ -636,6 +636,9 @@ protected: float display_aspect_ratio; bool in_hblank; bool in_vblank; + + bool displaying_odd_field; + bool displaying_odd_lines; } m_crtc_state = {}; BlitterState m_blitter_state = BlitterState::Idle; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index ef3a90ccc..7ca2fb3ae 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -712,7 +712,7 @@ void GPU_HW::DispatchRenderCommand() m_batch.interlacing = IsInterlacedRenderingEnabled(); if (m_batch.interlacing) { - const u32 displayed_field = GetInterlacedField(); + const u32 displayed_field = GetInterlacedDisplayLineOffset(); m_batch_ubo_dirty |= (m_batch_ubo_data.u_interlaced_displayed_field != displayed_field); m_batch_ubo_data.u_interlaced_displayed_field = displayed_field; } diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index 751941005..b4f516fcb 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -610,7 +610,7 @@ void GPU_HW_D3D11::UpdateDisplay() m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0); m_context->PSSetShaderResources(0, 1, m_vram_texture.GetD3DSRVArray()); - const u32 reinterpret_field_offset = GetInterlacedField(); + const u32 reinterpret_field_offset = GetInterlacedDisplayLineOffset(); const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale; const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * m_resolution_scale; const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y, reinterpret_crop_left, @@ -694,7 +694,7 @@ void GPU_HW_D3D11::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) Uniforms uniforms; std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) = RGBA8ToFloat(color); - uniforms.u_interlaced_displayed_field = GetInterlacedField(); + uniforms.u_interlaced_displayed_field = GetInterlacedDisplayLineOffset(); m_context->OMSetDepthStencilState(m_depth_test_always_state.Get(), 0); diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index f21971370..4ade9d2dd 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -619,7 +619,7 @@ void GPU_HW_OpenGL::UpdateDisplay() const u32 flipped_vram_offset_y = VRAM_HEIGHT - vram_offset_y - display_height; const u32 scaled_flipped_vram_offset_y = m_vram_texture.GetHeight() - scaled_vram_offset_y - scaled_display_height; - const u32 reinterpret_field_offset = GetInterlacedField(); + const u32 reinterpret_field_offset = GetInterlacedDisplayLineOffset(); const u32 reinterpret_start_x = m_crtc_state.regs.X * m_resolution_scale; const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * m_resolution_scale; const u32 uniforms[4] = {reinterpret_start_x, scaled_flipped_vram_offset_y, reinterpret_crop_left, @@ -727,7 +727,7 @@ void GPU_HW_OpenGL::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) Uniforms uniforms; std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) = RGBA8ToFloat(color); - uniforms.u_interlaced_displayed_field = GetInterlacedField(); + uniforms.u_interlaced_displayed_field = GetInterlacedDisplayLineOffset(); m_vram_interlaced_fill_program.Bind(); UploadUniformBuffer(&uniforms, sizeof(uniforms)); diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 762aeb1e3..e8f3359d7 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -157,7 +157,7 @@ void GPU_SW::UpdateDisplay() const u32 texture_offset_x = m_crtc_state.display_vram_left - m_crtc_state.regs.X; if (IsInterlacedDisplayEnabled()) { - const u32 field = GetInterlacedField(); + const u32 field = GetInterlacedDisplayLineOffset(); if (m_GPUSTAT.display_area_color_depth_24) { CopyOut24Bit(m_crtc_state.regs.X, vram_offset_y + field, m_display_texture_buffer.data() + field * VRAM_WIDTH, @@ -701,7 +701,7 @@ void GPU_SW::ShadePixel(u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 tex if ((bg_color.bits & mask_and) != 0) return; - if (IsInterlacedRenderingEnabled() && GetInterlacedField() == (static_cast(y) & 1u)) + if (IsInterlacedRenderingEnabled() && GetInterlacedDisplayLineOffset() == (static_cast(y) & 1u)) return; SetPixel(static_cast(x), static_cast(y), color.bits | m_GPUSTAT.GetMaskOR()); diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index effc9a36d..fb6abae24 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,7 +2,7 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 32; +static constexpr u32 SAVE_STATE_VERSION = 33; #pragma pack(push, 4) struct SAVE_STATE_HEADER