mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-29 09:05:41 +00:00
GPU: Fix interlaced rendering in vblank breaking
This commit is contained in:
parent
93031fc27f
commit
b25ed6c151
|
@ -158,6 +158,8 @@ bool GPU::DoState(StateWrapper& sw)
|
||||||
sw.Do(&m_crtc_state.current_scanline);
|
sw.Do(&m_crtc_state.current_scanline);
|
||||||
sw.Do(&m_crtc_state.in_hblank);
|
sw.Do(&m_crtc_state.in_hblank);
|
||||||
sw.Do(&m_crtc_state.in_vblank);
|
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_blitter_state);
|
||||||
sw.Do(&m_command_ticks);
|
sw.Do(&m_command_ticks);
|
||||||
|
@ -683,6 +685,12 @@ void GPU::Execute(TickCount ticks)
|
||||||
FlushRender();
|
FlushRender();
|
||||||
UpdateDisplay();
|
UpdateDisplay();
|
||||||
m_system->IncrementFrameNumber();
|
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);
|
m_timers->SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||||
|
@ -694,23 +702,19 @@ void GPU::Execute(TickCount ticks)
|
||||||
{
|
{
|
||||||
// start the new frame
|
// start the new frame
|
||||||
m_crtc_state.current_scanline = 0;
|
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
|
// alternating even line bit in 240-line mode
|
||||||
if (m_GPUSTAT.In480iMode())
|
if (m_GPUSTAT.In480iMode())
|
||||||
{
|
{
|
||||||
m_GPUSTAT.displaying_odd_line = ConvertToBoolUnchecked(
|
m_crtc_state.displaying_odd_lines =
|
||||||
(m_crtc_state.regs.Y + BoolToUInt32(m_GPUSTAT.interlaced_field && !m_crtc_state.in_vblank)) & u32(1));
|
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
|
else
|
||||||
{
|
{
|
||||||
|
m_crtc_state.displaying_odd_lines = false;
|
||||||
m_GPUSTAT.displaying_odd_line =
|
m_GPUSTAT.displaying_odd_line =
|
||||||
ConvertToBoolUnchecked((m_crtc_state.regs.Y + m_crtc_state.current_scanline) & u32(1));
|
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.
|
// Hardware tests show that fills seem to break on the first two lines when the offset matches the displayed field.
|
||||||
if (IsRasterScanlinePending())
|
if (IsRasterScanlinePending())
|
||||||
Synchronize();
|
Synchronize();
|
||||||
const u32 active_field = GetInterlacedField();
|
|
||||||
|
const u32 active_field = GetInterlacedDisplayLineOffset();
|
||||||
for (u32 yoffs = 0; yoffs < height; yoffs++)
|
for (u32 yoffs = 0; yoffs < height; yoffs++)
|
||||||
{
|
{
|
||||||
const u32 row = (y + yoffs) % VRAM_HEIGHT;
|
const u32 row = (y + yoffs) % VRAM_HEIGHT;
|
||||||
|
@ -1300,9 +1305,9 @@ void GPU::DrawDebugStateWindow()
|
||||||
const auto& cs = m_crtc_state;
|
const auto& cs = m_crtc_state;
|
||||||
ImGui::Text("Dot Clock Divider: %u", cs.dot_clock_divider);
|
ImGui::Text("Dot Clock Divider: %u", cs.dot_clock_divider);
|
||||||
ImGui::Text("Vertical Interlace: %s (%s field)", m_GPUSTAT.vertical_interlace ? "Yes" : "No",
|
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("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("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("Start Offset: (%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue());
|
||||||
ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total,
|
ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total,
|
||||||
|
|
|
@ -350,8 +350,8 @@ protected:
|
||||||
return (!m_force_progressive_scan) & m_GPUSTAT.SkipDrawingToActiveField();
|
return (!m_force_progressive_scan) & m_GPUSTAT.SkipDrawingToActiveField();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns 0 if the currently-rendered field is even, otherwise 1.
|
/// Returns 0 if the currently-displayed field is on an even line, otherwise 1.
|
||||||
ALWAYS_INLINE u32 GetInterlacedField() const { return BoolToUInt32(m_GPUSTAT.displaying_odd_line); }
|
ALWAYS_INLINE u32 GetInterlacedDisplayLineOffset() const { return BoolToUInt32(m_crtc_state.displaying_odd_lines); }
|
||||||
|
|
||||||
/// Sets/decodes GP0(E1h) (set draw mode).
|
/// Sets/decodes GP0(E1h) (set draw mode).
|
||||||
void SetDrawMode(u16 bits);
|
void SetDrawMode(u16 bits);
|
||||||
|
@ -636,6 +636,9 @@ protected:
|
||||||
float display_aspect_ratio;
|
float display_aspect_ratio;
|
||||||
bool in_hblank;
|
bool in_hblank;
|
||||||
bool in_vblank;
|
bool in_vblank;
|
||||||
|
|
||||||
|
bool displaying_odd_field;
|
||||||
|
bool displaying_odd_lines;
|
||||||
} m_crtc_state = {};
|
} m_crtc_state = {};
|
||||||
|
|
||||||
BlitterState m_blitter_state = BlitterState::Idle;
|
BlitterState m_blitter_state = BlitterState::Idle;
|
||||||
|
|
|
@ -712,7 +712,7 @@ void GPU_HW::DispatchRenderCommand()
|
||||||
m_batch.interlacing = IsInterlacedRenderingEnabled();
|
m_batch.interlacing = IsInterlacedRenderingEnabled();
|
||||||
if (m_batch.interlacing)
|
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_dirty |= (m_batch_ubo_data.u_interlaced_displayed_field != displayed_field);
|
||||||
m_batch_ubo_data.u_interlaced_displayed_field = displayed_field;
|
m_batch_ubo_data.u_interlaced_displayed_field = displayed_field;
|
||||||
}
|
}
|
||||||
|
|
|
@ -610,7 +610,7 @@ void GPU_HW_D3D11::UpdateDisplay()
|
||||||
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
||||||
m_context->PSSetShaderResources(0, 1, m_vram_texture.GetD3DSRVArray());
|
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_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 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,
|
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;
|
Uniforms uniforms;
|
||||||
std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) =
|
std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) =
|
||||||
RGBA8ToFloat(color);
|
RGBA8ToFloat(color);
|
||||||
uniforms.u_interlaced_displayed_field = GetInterlacedField();
|
uniforms.u_interlaced_displayed_field = GetInterlacedDisplayLineOffset();
|
||||||
|
|
||||||
m_context->OMSetDepthStencilState(m_depth_test_always_state.Get(), 0);
|
m_context->OMSetDepthStencilState(m_depth_test_always_state.Get(), 0);
|
||||||
|
|
||||||
|
|
|
@ -619,7 +619,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
||||||
const u32 flipped_vram_offset_y = VRAM_HEIGHT - vram_offset_y - display_height;
|
const u32 flipped_vram_offset_y = VRAM_HEIGHT - vram_offset_y - display_height;
|
||||||
const u32 scaled_flipped_vram_offset_y =
|
const u32 scaled_flipped_vram_offset_y =
|
||||||
m_vram_texture.GetHeight() - scaled_vram_offset_y - scaled_display_height;
|
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_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 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,
|
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;
|
Uniforms uniforms;
|
||||||
std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) =
|
std::tie(uniforms.u_fill_color[0], uniforms.u_fill_color[1], uniforms.u_fill_color[2], uniforms.u_fill_color[3]) =
|
||||||
RGBA8ToFloat(color);
|
RGBA8ToFloat(color);
|
||||||
uniforms.u_interlaced_displayed_field = GetInterlacedField();
|
uniforms.u_interlaced_displayed_field = GetInterlacedDisplayLineOffset();
|
||||||
|
|
||||||
m_vram_interlaced_fill_program.Bind();
|
m_vram_interlaced_fill_program.Bind();
|
||||||
UploadUniformBuffer(&uniforms, sizeof(uniforms));
|
UploadUniformBuffer(&uniforms, sizeof(uniforms));
|
||||||
|
|
|
@ -157,7 +157,7 @@ void GPU_SW::UpdateDisplay()
|
||||||
const u32 texture_offset_x = m_crtc_state.display_vram_left - m_crtc_state.regs.X;
|
const u32 texture_offset_x = m_crtc_state.display_vram_left - m_crtc_state.regs.X;
|
||||||
if (IsInterlacedDisplayEnabled())
|
if (IsInterlacedDisplayEnabled())
|
||||||
{
|
{
|
||||||
const u32 field = GetInterlacedField();
|
const u32 field = GetInterlacedDisplayLineOffset();
|
||||||
if (m_GPUSTAT.display_area_color_depth_24)
|
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,
|
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)
|
if ((bg_color.bits & mask_and) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (IsInterlacedRenderingEnabled() && GetInterlacedField() == (static_cast<u32>(y) & 1u))
|
if (IsInterlacedRenderingEnabled() && GetInterlacedDisplayLineOffset() == (static_cast<u32>(y) & 1u))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetPixel(static_cast<u32>(x), static_cast<u32>(y), color.bits | m_GPUSTAT.GetMaskOR());
|
SetPixel(static_cast<u32>(x), static_cast<u32>(y), color.bits | m_GPUSTAT.GetMaskOR());
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
|
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)
|
#pragma pack(push, 4)
|
||||||
struct SAVE_STATE_HEADER
|
struct SAVE_STATE_HEADER
|
||||||
|
|
Loading…
Reference in a new issue