Skip to content

Commit e368dbb

Browse files
committed
GPU: Implement non-interleaved interlaced rendering
Fixes screen shaking in True Pinball.
1 parent 0daea7c commit e368dbb

File tree

11 files changed

+76
-43
lines changed

11 files changed

+76
-43
lines changed

src/core/gpu.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ void GPU::UpdateCRTCDisplayParameters()
497497
}
498498
}
499499

500-
const u8 height_shift = BoolToUInt8(m_GPUSTAT.In480iMode());
500+
const u8 height_shift = BoolToUInt8(m_GPUSTAT.vertical_interlace);
501501

502502
// Determine screen size.
503503
cs.display_width = (((cs.horizontal_active_end - cs.horizontal_active_start) / cs.dot_clock_divider) + 2u) & ~3u;
@@ -711,16 +711,16 @@ void GPU::Execute(TickCount ticks)
711711
}
712712

713713
// alternating even line bit in 240-line mode
714-
if (m_GPUSTAT.In480iMode())
714+
if (m_GPUSTAT.vertical_interlace)
715715
{
716716
m_crtc_state.displaying_odd_lines =
717717
ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(m_crtc_state.displaying_odd_field)) & u32(1));
718-
m_GPUSTAT.displaying_odd_line = m_crtc_state.displaying_odd_lines && !m_crtc_state.in_vblank;
718+
m_GPUSTAT.drawing_odd_lines = !m_crtc_state.displaying_odd_lines && !m_crtc_state.in_vblank;
719719
}
720720
else
721721
{
722722
m_crtc_state.displaying_odd_lines = false;
723-
m_GPUSTAT.displaying_odd_line =
723+
m_GPUSTAT.drawing_odd_lines =
724724
ConvertToBoolUnchecked((m_crtc_state.regs.Y + m_crtc_state.current_scanline) & u32(1));
725725
}
726726

@@ -743,7 +743,8 @@ bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y
743743
return false;
744744
}
745745

746-
*out_line = (static_cast<u32>(display_y) >> BoolToUInt8(m_GPUSTAT.In480iMode())) + m_crtc_state.vertical_active_start;
746+
*out_line =
747+
(static_cast<u32>(display_y) >> BoolToUInt8(m_GPUSTAT.vertical_interlace)) + m_crtc_state.vertical_active_start;
747748
*out_tick = (static_cast<u32>(display_x) * m_crtc_state.dot_clock_divider) + m_crtc_state.horizontal_active_start;
748749
return true;
749750
}

src/core/gpu.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,10 @@ class GPU
345345
}
346346

347347
/// Returns true if scanout should be interlaced.
348-
ALWAYS_INLINE bool IsInterlacedDisplayEnabled() const { return (!m_force_progressive_scan) & m_GPUSTAT.In480iMode(); }
348+
ALWAYS_INLINE bool IsInterlacedDisplayEnabled() const
349+
{
350+
return (!m_force_progressive_scan) & m_GPUSTAT.vertical_interlace;
351+
}
349352

350353
/// Returns true if interlaced rendering is enabled and force progressive scan is disabled.
351354
ALWAYS_INLINE bool IsInterlacedRenderingEnabled() const
@@ -443,18 +446,13 @@ class GPU
443446
BitField<u32, bool, 27, 1> ready_to_send_vram;
444447
BitField<u32, bool, 28, 1> ready_to_recieve_dma;
445448
BitField<u32, DMADirection, 29, 2> dma_direction;
446-
BitField<u32, bool, 31, 1> displaying_odd_line;
449+
BitField<u32, bool, 31, 1> drawing_odd_lines;
447450

448451
bool IsMaskingEnabled() const
449452
{
450453
static constexpr u32 MASK = ((1 << 11) | (1 << 12));
451454
return ((bits & MASK) != 0);
452455
}
453-
bool In480iMode() const
454-
{
455-
static constexpr u32 MASK = (1 << 19) | (1 << 22);
456-
return ((bits & MASK) == MASK);
457-
}
458456
bool SkipDrawingToActiveField() const
459457
{
460458
static constexpr u32 MASK = (1 << 19) | (1 << 22) | (1 << 10);

src/core/gpu_hw.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ class GPU_HW : public GPU
2727
OnlyTransparent
2828
};
2929

30+
enum class InterlacedRenderMode : u8
31+
{
32+
None,
33+
InterleavedFields,
34+
SeparateFields
35+
};
36+
3037
GPU_HW();
3138
virtual ~GPU_HW();
3239

@@ -189,6 +196,20 @@ class GPU_HW : public GPU
189196
return m_batch.check_mask_before_draw || m_render_api != HostDisplay::RenderAPI::D3D11;
190197
}
191198

199+
/// Returns the interlaced mode to use when scanning out/displaying.
200+
ALWAYS_INLINE InterlacedRenderMode GetInterlacedRenderMode() const
201+
{
202+
if (IsInterlacedDisplayEnabled())
203+
{
204+
return m_GPUSTAT.vertical_resolution ? InterlacedRenderMode::InterleavedFields :
205+
InterlacedRenderMode::SeparateFields;
206+
}
207+
else
208+
{
209+
return InterlacedRenderMode::None;
210+
}
211+
}
212+
192213
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override;
193214
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override;
194215
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override;

src/core/gpu_hw_d3d11.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -448,10 +448,10 @@ bool GPU_HW_D3D11::CompileShaders()
448448

449449
for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++)
450450
{
451-
for (u8 interlacing = 0; interlacing < 2; interlacing++)
451+
for (u8 interlacing = 0; interlacing < 3; interlacing++)
452452
{
453453
const std::string ps = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit),
454-
ConvertToBoolUnchecked(interlacing));
454+
static_cast<InterlacedRenderMode>(interlacing));
455455
m_display_pixel_shaders[depth_24bit][interlacing] = m_shader_cache.GetPixelShader(m_device.Get(), ps);
456456
if (!m_display_pixel_shaders[depth_24bit][interlacing])
457457
return false;
@@ -590,13 +590,13 @@ void GPU_HW_D3D11::UpdateDisplay()
590590
const u32 display_height = m_crtc_state.display_vram_height;
591591
const u32 scaled_display_width = display_width * m_resolution_scale;
592592
const u32 scaled_display_height = display_height * m_resolution_scale;
593-
const bool interlaced = IsInterlacedDisplayEnabled();
593+
const InterlacedRenderMode interlaced = GetInterlacedRenderMode();
594594

595595
if (IsDisplayDisabled())
596596
{
597597
m_host_display->ClearDisplayTexture();
598598
}
599-
else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced &&
599+
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
600600
(scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
601601
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
602602
{
@@ -616,9 +616,9 @@ void GPU_HW_D3D11::UpdateDisplay()
616616
const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y, reinterpret_crop_left,
617617
reinterpret_field_offset};
618618
ID3D11PixelShader* display_pixel_shader =
619-
m_display_pixel_shaders[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)][BoolToUInt8(interlaced)].Get();
619+
m_display_pixel_shaders[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)][static_cast<u8>(interlaced)].Get();
620620

621-
SetViewportAndScissor(0, reinterpret_field_offset, scaled_display_width, scaled_display_height);
621+
SetViewportAndScissor(0, 0, scaled_display_width, scaled_display_height);
622622
DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms));
623623

624624
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), m_display_texture.GetWidth(),

src/core/gpu_hw_d3d11.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,5 @@ class GPU_HW_D3D11 : public GPU_HW
121121
ComPtr<ID3D11PixelShader> m_vram_write_pixel_shader;
122122
ComPtr<ID3D11PixelShader> m_vram_copy_pixel_shader;
123123
ComPtr<ID3D11PixelShader> m_vram_update_depth_pixel_shader;
124-
std::array<std::array<ComPtr<ID3D11PixelShader>, 2>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
124+
std::array<std::array<ComPtr<ID3D11PixelShader>, 3>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
125125
};

src/core/gpu_hw_opengl.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,11 @@ bool GPU_HW_OpenGL::CompilePrograms()
403403

404404
for (u8 depth_24bit = 0; depth_24bit < 2; depth_24bit++)
405405
{
406-
for (u8 interlaced = 0; interlaced < 2; interlaced++)
406+
for (u8 interlaced = 0; interlaced < 3; interlaced++)
407407
{
408408
const std::string vs = shadergen.GenerateScreenQuadVertexShader();
409409
const std::string fs = shadergen.GenerateDisplayFragmentShader(ConvertToBoolUnchecked(depth_24bit),
410-
ConvertToBoolUnchecked(interlaced));
410+
static_cast<InterlacedRenderMode>(interlaced));
411411

412412
std::optional<GL::Program> prog =
413413
m_shader_cache.GetProgram(vs, {}, fs, [this, use_binding_layout](GL::Program& prog) {
@@ -587,13 +587,13 @@ void GPU_HW_OpenGL::UpdateDisplay()
587587
const u32 display_height = m_crtc_state.display_vram_height;
588588
const u32 scaled_display_width = display_width * m_resolution_scale;
589589
const u32 scaled_display_height = display_height * m_resolution_scale;
590-
const bool interlaced = IsInterlacedDisplayEnabled();
590+
const InterlacedRenderMode interlaced = GetInterlacedRenderMode();
591591

592592
if (IsDisplayDisabled())
593593
{
594594
m_host_display->ClearDisplayTexture();
595595
}
596-
else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced &&
596+
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == GPU_HW::InterlacedRenderMode::None &&
597597
(scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
598598
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
599599
{
@@ -608,7 +608,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
608608
glDisable(GL_SCISSOR_TEST);
609609
glDisable(GL_DEPTH_TEST);
610610

611-
m_display_programs[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)][BoolToUInt8(interlaced)].Bind();
611+
m_display_programs[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)][static_cast<u8>(interlaced)].Bind();
612612
m_display_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
613613
m_vram_texture.Bind();
614614

src/core/gpu_hw_opengl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class GPU_HW_OpenGL : public GPU_HW
8585
m_render_programs; // [render_mode][texture_mode][dithering][interlacing]
8686
std::array<std::array<std::array<GL::Program, 2>, 2>, 4>
8787
m_line_render_programs; // [render_mode][dithering][interlacing]
88-
std::array<std::array<GL::Program, 2>, 2> m_display_programs; // [depth_24][interlaced]
88+
std::array<std::array<GL::Program, 3>, 2> m_display_programs; // [depth_24][interlaced]
8989
GL::Program m_vram_interlaced_fill_program;
9090
GL::Program m_vram_read_program;
9191
GL::Program m_vram_write_program;

src/core/gpu_hw_shadergen.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,12 +1008,13 @@ std::string GPU_HW_ShaderGen::GenerateCopyFragmentShader()
10081008
return ss.str();
10091009
}
10101010

1011-
std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced)
1011+
std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode)
10121012
{
10131013
std::stringstream ss;
10141014
WriteHeader(ss);
10151015
DefineMacro(ss, "DEPTH_24BIT", depth_24bit);
1016-
DefineMacro(ss, "INTERLACED", interlaced);
1016+
DefineMacro(ss, "INTERLACED", interlace_mode != GPU_HW::InterlacedRenderMode::None);
1017+
DefineMacro(ss, "INTERLEAVED", interlace_mode == GPU_HW::InterlacedRenderMode::InterleavedFields);
10171018

10181019
WriteCommonFunctions(ss);
10191020
DeclareUniformBuffer(ss, {"uint2 u_vram_offset", "uint u_crop_left", "uint u_field_offset"});
@@ -1027,6 +1028,10 @@ std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit, bo
10271028
#if INTERLACED
10281029
if (((icoords.y / RESOLUTION_SCALE) & 1u) != u_field_offset)
10291030
discard;
1031+
1032+
#if !INTERLEAVED
1033+
icoords.y /= 2u;
1034+
#endif
10301035
#endif
10311036
10321037
#if DEPTH_24BIT

src/core/gpu_hw_shadergen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class GPU_HW_ShaderGen
2121
std::string GenerateFillFragmentShader();
2222
std::string GenerateInterlacedFillFragmentShader();
2323
std::string GenerateCopyFragmentShader();
24-
std::string GenerateDisplayFragmentShader(bool depth_24bit, bool interlaced);
24+
std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode);
2525
std::string GenerateVRAMReadFragmentShader();
2626
std::string GenerateVRAMWriteFragmentShader();
2727
std::string GenerateVRAMCopyFragmentShader();

src/core/gpu_sw.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ void GPU_SW::Reset()
4343
m_vram.fill(0);
4444
}
4545

46-
void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced)
46+
void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced,
47+
bool interleaved)
4748
{
4849
const u8 interlaced_shift = BoolToUInt8(interlaced);
50+
const u8 interleaved_shift = BoolToUInt8(interleaved);
4951

5052
// Fast path when not wrapping around.
5153
if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT)
@@ -54,7 +56,7 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u3
5456
height >>= interlaced_shift;
5557

5658
const u16* src_ptr = &m_vram[src_y * VRAM_WIDTH + src_x];
57-
const u32 src_stride = VRAM_WIDTH << interlaced_shift;
59+
const u32 src_stride = VRAM_WIDTH << interleaved_shift;
5860
for (u32 row = 0; row < height; row++)
5961
{
6062
const u16* src_row_ptr = src_ptr;
@@ -80,23 +82,25 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u3
8082
for (u32 col = src_x; col < end_x; col++)
8183
*(dst_row_ptr++) = RGBA5551ToRGBA8888(src_row_ptr[col % VRAM_WIDTH]);
8284

83-
src_y += (1 << interlaced_shift);
85+
src_y += (1 << interleaved_shift);
8486
dst_ptr += dst_stride;
8587
}
8688
}
8789
}
8890

89-
void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced)
91+
void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u32 width, u32 height, bool interlaced,
92+
bool interleaved)
9093
{
9194
const u8 interlaced_shift = BoolToUInt8(interlaced);
95+
const u8 interleaved_shift = BoolToUInt8(interleaved);
9296

9397
if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT)
9498
{
9599
dst_stride <<= interlaced_shift;
96100
height >>= interlaced_shift;
97101

98102
const u8* src_ptr = reinterpret_cast<const u8*>(&m_vram[src_y * VRAM_WIDTH + src_x]);
99-
const u32 src_stride = (VRAM_WIDTH << interlaced_shift) * sizeof(u16);
103+
const u32 src_stride = (VRAM_WIDTH << interleaved_shift) * sizeof(u16);
100104
for (u32 row = 0; row < height; row++)
101105
{
102106
const u8* src_row_ptr = src_ptr;
@@ -133,7 +137,7 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32* dst_ptr, u32 dst_stride, u3
133137
*(dst_row_ptr++) = (((ZeroExtend32(s1) << 16) | ZeroExtend32(s0)) >> shift) | 0xFF000000u;
134138
}
135139

136-
src_y += (1 << interlaced_shift);
140+
src_y += (1 << interleaved_shift);
137141
dst_ptr += dst_stride;
138142
}
139143
}
@@ -162,26 +166,28 @@ void GPU_SW::UpdateDisplay()
162166
const u32 field = GetInterlacedDisplayLineOffset();
163167
if (m_GPUSTAT.display_area_color_depth_24)
164168
{
165-
CopyOut24Bit(m_crtc_state.regs.X, vram_offset_y + field, m_display_texture_buffer.data() + field * VRAM_WIDTH,
166-
VRAM_WIDTH, display_width + texture_offset_x, display_height, true);
169+
CopyOut24Bit(m_crtc_state.regs.X, vram_offset_y + (m_GPUSTAT.vertical_resolution ? field : 0u),
170+
m_display_texture_buffer.data() + field * VRAM_WIDTH, VRAM_WIDTH, display_width + texture_offset_x,
171+
display_height, true, m_GPUSTAT.vertical_resolution);
167172
}
168173
else
169174
{
170-
CopyOut15Bit(m_crtc_state.regs.X, vram_offset_y + field, m_display_texture_buffer.data() + field * VRAM_WIDTH,
171-
VRAM_WIDTH, display_width + texture_offset_x, display_height, true);
175+
CopyOut15Bit(m_crtc_state.regs.X, vram_offset_y + (m_GPUSTAT.vertical_resolution ? field : 0u),
176+
m_display_texture_buffer.data() + field * VRAM_WIDTH, VRAM_WIDTH, display_width + texture_offset_x,
177+
display_height, true, m_GPUSTAT.vertical_resolution);
172178
}
173179
}
174180
else
175181
{
176182
if (m_GPUSTAT.display_area_color_depth_24)
177183
{
178184
CopyOut24Bit(m_crtc_state.regs.X, vram_offset_y, m_display_texture_buffer.data(), VRAM_WIDTH,
179-
display_width + texture_offset_x, display_height, false);
185+
display_width + texture_offset_x, display_height, false, false);
180186
}
181187
else
182188
{
183189
CopyOut15Bit(m_crtc_state.regs.X, vram_offset_y, m_display_texture_buffer.data(), VRAM_WIDTH,
184-
display_width + texture_offset_x, display_height, false);
190+
display_width + texture_offset_x, display_height, false, false);
185191
}
186192
}
187193

@@ -196,7 +202,7 @@ void GPU_SW::UpdateDisplay()
196202
}
197203
else
198204
{
199-
CopyOut15Bit(0, 0, m_display_texture_buffer.data(), VRAM_WIDTH, VRAM_WIDTH, VRAM_HEIGHT, false);
205+
CopyOut15Bit(0, 0, m_display_texture_buffer.data(), VRAM_WIDTH, VRAM_WIDTH, VRAM_HEIGHT, false, false);
200206
m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
201207
m_display_texture_buffer.data(), VRAM_WIDTH * sizeof(u32));
202208
m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH,

0 commit comments

Comments
 (0)