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:
parent
a06240141e
commit
3325d2c42c
133
src/core/gpu.cpp
133
src/core/gpu.cpp
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue