GPU: Add Force NTSC timings option

This option forces NTSC timings for PAL games, causing them to either
run faster (more likely) or smoother (less likely).
This commit is contained in:
Connor McLaughlin 2020-04-10 13:34:12 +10:00
parent a06240141e
commit 3325d2c42c
8 changed files with 110 additions and 59 deletions

View File

@ -28,6 +28,7 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr
m_interrupt_controller = interrupt_controller;
m_timers = timers;
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
m_force_ntsc_timings = m_system->GetSettings().gpu_force_ntsc_timings;
m_tick_event =
m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true);
return true;
@ -37,6 +38,12 @@ void GPU::UpdateSettings()
{
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
if (m_force_ntsc_timings != m_system->GetSettings().gpu_force_ntsc_timings)
{
m_force_ntsc_timings = m_system->GetSettings().gpu_force_ntsc_timings;
UpdateCRTCConfig();
}
// Crop mode calls this, so recalculate the display area
UpdateCRTCDisplayParameters();
}
@ -342,11 +349,6 @@ void GPU::UpdateCRTCConfig()
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
}
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
const float vertical_frequency =
static_cast<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame));
m_system->SetThrottleFrequency(vertical_frequency);
const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2);
cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index];
cs.horizontal_display_start = std::min<u16>(cs.regs.X1, cs.horizontal_total);
@ -354,6 +356,30 @@ void GPU::UpdateCRTCConfig()
cs.vertical_display_start = std::min<u16>(cs.regs.Y1, cs.vertical_total);
cs.vertical_display_end = std::min<u16>(cs.regs.Y2, cs.vertical_total);
if (m_GPUSTAT.pal_mode && m_force_ntsc_timings)
{
// scale to NTSC parameters
cs.horizontal_display_start =
static_cast<u16>((static_cast<u32>(cs.horizontal_display_start) * NTSC_TICKS_PER_LINE) / PAL_TICKS_PER_LINE);
cs.horizontal_display_end = static_cast<u16>(
((static_cast<u32>(cs.horizontal_display_end) * NTSC_TICKS_PER_LINE) + (PAL_TICKS_PER_LINE - 1)) /
PAL_TICKS_PER_LINE);
cs.vertical_display_start =
static_cast<u16>((static_cast<u32>(cs.vertical_display_start) * NTSC_TOTAL_LINES) / PAL_TOTAL_LINES);
cs.vertical_display_end = static_cast<u16>(
((static_cast<u32>(cs.vertical_display_end) * NTSC_TOTAL_LINES) + (PAL_TOTAL_LINES - 1)) / PAL_TOTAL_LINES);
cs.vertical_total = NTSC_TOTAL_LINES;
cs.current_scanline %= NTSC_TOTAL_LINES;
cs.horizontal_total = NTSC_TICKS_PER_LINE;
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
}
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
const float vertical_frequency =
static_cast<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame));
m_system->SetThrottleFrequency(vertical_frequency);
UpdateCRTCDisplayParameters();
UpdateSliceTicks();
}
@ -363,33 +389,40 @@ void GPU::UpdateCRTCDisplayParameters()
CRTCState& cs = m_crtc_state;
const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode;
u16 horizontal_display_start_tick, horizontal_display_end_tick;
u16 vertical_display_start_line, vertical_display_end_line;
const u16 horizontal_total = m_GPUSTAT.pal_mode ? PAL_TICKS_PER_LINE : NTSC_TICKS_PER_LINE;
const u16 vertical_total = m_GPUSTAT.pal_mode ? PAL_TOTAL_LINES : NTSC_TOTAL_LINES;
const u16 horizontal_display_start = std::min<u16>(cs.regs.X1, horizontal_total);
const u16 horizontal_display_end = std::min<u16>(cs.regs.X2, horizontal_total);
const u16 vertical_display_start = std::min<u16>(cs.regs.Y1, vertical_total);
const u16 vertical_display_end = std::min<u16>(cs.regs.Y2, vertical_total);
u16 horizontal_visible_start_tick, horizontal_visible_end_tick;
u16 vertical_visible_start_line, vertical_visible_end_line;
if (m_GPUSTAT.pal_mode)
{
// TODO: Verify PAL numbers.
switch (crop_mode)
{
case DisplayCropMode::None:
horizontal_display_start_tick = 487;
horizontal_display_end_tick = 3282;
vertical_display_start_line = 20;
vertical_display_end_line = 308;
horizontal_visible_start_tick = 487;
horizontal_visible_end_tick = 3282;
vertical_visible_start_line = 20;
vertical_visible_end_line = 308;
break;
case DisplayCropMode::Overscan:
horizontal_display_start_tick = 628;
horizontal_display_end_tick = 3188;
vertical_display_start_line = 30;
vertical_display_end_line = 298;
horizontal_visible_start_tick = 628;
horizontal_visible_end_tick = 3188;
vertical_visible_start_line = 30;
vertical_visible_end_line = 298;
break;
case DisplayCropMode::Borders:
default:
horizontal_display_start_tick = m_crtc_state.horizontal_display_start;
horizontal_display_end_tick = m_crtc_state.horizontal_display_end;
vertical_display_start_line = m_crtc_state.vertical_display_start;
vertical_display_end_line = m_crtc_state.vertical_display_end;
horizontal_visible_start_tick = horizontal_display_start;
horizontal_visible_end_tick = horizontal_display_end;
vertical_visible_start_line = vertical_display_start;
vertical_visible_end_line = vertical_display_end;
break;
}
}
@ -398,25 +431,25 @@ void GPU::UpdateCRTCDisplayParameters()
switch (crop_mode)
{
case DisplayCropMode::None:
horizontal_display_start_tick = 488;
horizontal_display_end_tick = 3288;
vertical_display_start_line = 16;
vertical_display_end_line = 256;
horizontal_visible_start_tick = 488;
horizontal_visible_end_tick = 3288;
vertical_visible_start_line = 16;
vertical_visible_end_line = 256;
break;
case DisplayCropMode::Overscan:
horizontal_display_start_tick = 608;
horizontal_display_end_tick = 3168;
vertical_display_start_line = 24;
vertical_display_end_line = 248;
horizontal_visible_start_tick = 608;
horizontal_visible_end_tick = 3168;
vertical_visible_start_line = 24;
vertical_visible_end_line = 248;
break;
case DisplayCropMode::Borders:
default:
horizontal_display_start_tick = m_crtc_state.horizontal_display_start;
horizontal_display_end_tick = m_crtc_state.horizontal_display_end;
vertical_display_start_line = m_crtc_state.vertical_display_start;
vertical_display_end_line = m_crtc_state.vertical_display_end;
horizontal_visible_start_tick = horizontal_display_start;
horizontal_visible_end_tick = horizontal_display_end;
vertical_visible_start_line = vertical_display_start;
vertical_visible_end_line = vertical_display_end;
break;
}
}
@ -425,69 +458,67 @@ void GPU::UpdateCRTCDisplayParameters()
// Determine screen size.
cs.display_width = std::max<u16>(
((horizontal_display_end_tick - horizontal_display_start_tick) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider,
((horizontal_visible_end_tick - horizontal_visible_start_tick) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider,
1u);
cs.display_height = std::max<u16>((vertical_display_end_line - vertical_display_start_line) << height_shift, 1u);
cs.display_height = std::max<u16>((vertical_visible_end_line - vertical_visible_start_line) << height_shift, 1u);
// Determine if we need to adjust the VRAM rectangle (because the display is starting outside the visible area) or add
// padding.
if (cs.horizontal_display_start >= horizontal_display_start_tick)
if (horizontal_display_start >= horizontal_visible_start_tick)
{
cs.display_origin_left = (cs.horizontal_display_start - horizontal_display_start_tick) / cs.dot_clock_divider;
cs.display_origin_left = (horizontal_display_start - horizontal_visible_start_tick) / cs.dot_clock_divider;
cs.display_vram_left = m_crtc_state.regs.X;
}
else
{
cs.display_origin_left = 0;
cs.display_vram_left = std::min<u16>(
m_crtc_state.regs.X + ((horizontal_display_start_tick - cs.horizontal_display_start) / cs.dot_clock_divider),
m_crtc_state.regs.X + ((horizontal_visible_start_tick - horizontal_display_start) / cs.dot_clock_divider),
VRAM_WIDTH - 1);
}
if (cs.horizontal_display_end <= horizontal_display_end_tick)
if (horizontal_display_end <= horizontal_visible_end_tick)
{
cs.display_vram_width = std::min<u16>(
std::max<u16>(
(((cs.horizontal_display_end - std::max(cs.horizontal_display_start, horizontal_display_start_tick)) +
(cs.dot_clock_divider - 1)) /
cs.dot_clock_divider),
1u),
std::max<u16>((((horizontal_display_end - std::max(horizontal_display_start, horizontal_visible_start_tick)) +
(cs.dot_clock_divider - 1)) /
cs.dot_clock_divider),
1u),
VRAM_WIDTH - cs.display_vram_left);
}
else
{
cs.display_vram_width = std::min<u16>(
std::max<u16>(
(((horizontal_display_end_tick - std::max(cs.horizontal_display_start, horizontal_display_start_tick)) +
(((horizontal_visible_end_tick - std::max(horizontal_display_start, horizontal_visible_start_tick)) +
(cs.dot_clock_divider - 1)) /
cs.dot_clock_divider),
1u),
VRAM_WIDTH - cs.display_vram_left);
}
if (cs.vertical_display_start >= vertical_display_start_line)
if (vertical_display_start >= vertical_visible_start_line)
{
cs.display_origin_top = (cs.vertical_display_start - vertical_display_start_line) << height_shift;
cs.display_origin_top = (vertical_display_start - vertical_visible_start_line) << height_shift;
cs.display_vram_top = m_crtc_state.regs.Y;
}
else
{
cs.display_origin_top = 0;
cs.display_vram_top =
std::min<u16>(m_crtc_state.regs.Y + ((vertical_display_start_line - cs.vertical_display_start) << height_shift),
VRAM_HEIGHT - 1);
cs.display_vram_top = std::min<u16>(
m_crtc_state.regs.Y + ((vertical_visible_start_line - vertical_display_start) << height_shift), VRAM_HEIGHT - 1);
}
if (cs.vertical_display_end <= vertical_display_end_line)
if (vertical_display_end <= vertical_visible_end_line)
{
cs.display_vram_height = std::min<u16>(
(cs.vertical_display_end - std::max(cs.vertical_display_start, vertical_display_start_line)) << height_shift,
(vertical_display_end - std::max(vertical_display_start, vertical_visible_start_line)) << height_shift,
VRAM_HEIGHT - cs.display_vram_top);
}
else
{
cs.display_vram_height = std::min<u16>(
(vertical_display_end_line - std::max(cs.vertical_display_start, vertical_display_start_line)) << height_shift,
(vertical_visible_end_line - std::max(vertical_display_start, vertical_visible_start_line)) << height_shift,
VRAM_HEIGHT - cs.display_vram_top);
}

View File

@ -95,6 +95,10 @@ public:
DOT_TIMER_INDEX = 0,
HBLANK_TIMER_INDEX = 1,
MAX_RESOLUTION_SCALE = 16,
};
enum : u16
{
NTSC_TICKS_PER_LINE = 3413,
NTSC_TOTAL_LINES = 263,
PAL_TICKS_PER_LINE = 3406,
@ -531,6 +535,7 @@ protected:
bool m_set_texture_disable_mask = false;
bool m_drawing_area_changed = false;
bool m_force_progressive_scan = false;
bool m_force_ntsc_timings = false;
struct CRTCState
{

View File

@ -877,6 +877,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetBoolValue("GPU", "ScaledDithering", false);
si.SetBoolValue("GPU", "TextureFiltering", false);
si.SetBoolValue("GPU", "UseDebugDevice", false);
si.SetBoolValue("GPU", "ForceNTSCTimings", false);
si.SetStringValue("Display", "CropMode", "Overscan");
si.SetBoolValue("Display", "ForceProgressiveScan", true);
@ -926,6 +927,7 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
const bool old_gpu_true_color = m_settings.gpu_true_color;
const bool old_gpu_scaled_dithering = m_settings.gpu_scaled_dithering;
const bool old_gpu_texture_filtering = m_settings.gpu_texture_filtering;
const bool old_gpu_force_ntsc_timings = m_settings.gpu_force_ntsc_timings;
const bool old_display_force_progressive_scan = m_settings.display_force_progressive_scan;
const bool old_gpu_debug_device = m_settings.gpu_use_debug_device;
const bool old_vsync_enabled = m_settings.video_sync_enabled;
@ -979,6 +981,7 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
m_settings.gpu_true_color != old_gpu_true_color ||
m_settings.gpu_scaled_dithering != old_gpu_scaled_dithering ||
m_settings.gpu_texture_filtering != old_gpu_texture_filtering ||
m_settings.gpu_force_ntsc_timings != old_gpu_force_ntsc_timings ||
m_settings.display_force_progressive_scan != old_display_force_progressive_scan ||
m_settings.display_crop_mode != old_display_crop_mode)
{

View File

@ -27,6 +27,7 @@ void Settings::Load(SettingsInterface& si)
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", false);
gpu_texture_filtering = si.GetBoolValue("GPU", "TextureFiltering", false);
gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false);
gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false);
display_crop_mode = ParseDisplayCropMode(
si.GetStringValue("Display", "CropMode", GetDisplayCropModeName(DisplayCropMode::None)).c_str())
@ -89,6 +90,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
si.SetBoolValue("GPU", "TextureFiltering", gpu_texture_filtering);
si.SetBoolValue("GPU", "UseDebugDevice", gpu_use_debug_device);
si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings);
si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
si.SetBoolValue("Display", "ForceProgressiveScan", display_force_progressive_scan);

View File

@ -51,6 +51,7 @@ struct Settings
bool gpu_scaled_dithering = false;
bool gpu_texture_filtering = false;
bool gpu_use_debug_device = false;
bool gpu_force_ntsc_timings = false;
DisplayCropMode display_crop_mode = DisplayCropMode::None;
bool display_force_progressive_scan = false;
bool display_linear_filtering = true;

View File

@ -22,6 +22,7 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.resolutionScale, "GPU/ResolutionScale");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.trueColor, "GPU/TrueColor");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.scaledDithering, "GPU/ScaledDithering");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceNTSCTimings, "GPU/ForceNTSCTimings");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.linearTextureFiltering, "GPU/TextureFiltering");
connect(m_ui.resolutionScale, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,

View File

@ -69,20 +69,13 @@
<widget class="QComboBox" name="cropMode"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="forceProgressiveScan">
<property name="text">
<string>Force Progressive Scan</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="displayLinearFiltering">
<property name="text">
<string>Linear Upscaling</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="vsync">
<property name="text">
<string>VSync</string>
@ -123,6 +116,20 @@
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="forceProgressiveScan">
<property name="text">
<string>Force Progressive Scan</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="forceNTSCTimings">
<property name="text">
<string>Force NTSC Timings (60hz-on-PAL)</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="linearTextureFiltering">
<property name="text">
<string>Bilinear Texture Filtering</string>

View File

@ -1302,6 +1302,7 @@ void SDLHostInterface::DrawSettingsWindow()
settings_changed |= ImGui::Checkbox("True 24-bit Color (disables dithering)", &m_settings_copy.gpu_true_color);
settings_changed |= ImGui::Checkbox("Texture Filtering", &m_settings_copy.gpu_texture_filtering);
settings_changed |= ImGui::Checkbox("Force NTSC Timings", &m_settings_copy.gpu_force_ntsc_timings);
settings_changed |= ImGui::Checkbox("Force Progressive Scan", &m_settings_copy.display_force_progressive_scan);
}