GPU: Add display line offset game setting

Use when games need additional cropping.
This commit is contained in:
Connor McLaughlin 2020-12-03 22:31:21 +10:00
parent c9ca57f013
commit 362905e3df
11 changed files with 163 additions and 25 deletions

View file

@ -462,3 +462,8 @@ GPUMaxRunAhead = 1
# SLUS-00022 (Slam 'n Jam '96 featuring Magic & Kareem (USA))
[SLUS-00022]
DisableUpscaling = true
# SCES-01224 (Blasto (Europe) (En,Fr,De,Es,It))
[SCES-01224]
DisplayLineStartOffset = 1

View file

@ -545,15 +545,15 @@ void GPU::UpdateCRTCDisplayParameters()
case DisplayCropMode::None:
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 487 + g_settings.display_active_start_offset));
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3282 + g_settings.display_active_end_offset));
cs.vertical_active_start = 20;
cs.vertical_active_end = 308;
cs.vertical_active_start = static_cast<u16>(std::max<int>(0, 20 + g_settings.display_line_start_offset));
cs.vertical_active_end = static_cast<u16>(std::max<int>(0, 308 + g_settings.display_line_end_offset));
break;
case DisplayCropMode::Overscan:
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 628 + g_settings.display_active_start_offset));
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3188 + g_settings.display_active_end_offset));
cs.vertical_active_start = 30;
cs.vertical_active_end = 298;
cs.vertical_active_start = static_cast<u16>(std::max<int>(0, 30 + g_settings.display_line_start_offset));
cs.vertical_active_end = static_cast<u16>(std::max<int>(0, 298 + g_settings.display_line_end_offset));
break;
case DisplayCropMode::Borders:
@ -572,15 +572,15 @@ void GPU::UpdateCRTCDisplayParameters()
case DisplayCropMode::None:
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 488 + g_settings.display_active_start_offset));
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3288 + g_settings.display_active_end_offset));
cs.vertical_active_start = 16;
cs.vertical_active_end = 256;
cs.vertical_active_start = static_cast<u16>(std::max<int>(0, 16 + g_settings.display_line_start_offset));
cs.vertical_active_end = static_cast<u16>(std::max<int>(0, 256 + g_settings.display_line_end_offset));
break;
case DisplayCropMode::Overscan:
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 608 + g_settings.display_active_start_offset));
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3168 + g_settings.display_active_end_offset));
cs.vertical_active_start = 24;
cs.vertical_active_end = 248;
cs.vertical_active_start = static_cast<u16>(std::max<int>(0, 24 + g_settings.display_line_start_offset));
cs.vertical_active_end = static_cast<u16>(std::max<int>(0, 248 + g_settings.display_line_end_offset));
break;
case DisplayCropMode::Borders:

View file

@ -451,6 +451,8 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetStringValue("Display", "CropMode", Settings::GetDisplayCropModeName(Settings::DEFAULT_DISPLAY_CROP_MODE));
si.SetIntValue("Display", "ActiveStartOffset", 0);
si.SetIntValue("Display", "ActiveEndOffset", 0);
si.SetIntValue("Display", "LineStartOffset", 0);
si.SetIntValue("Display", "LineEndOffset", 0);
si.SetStringValue("Display", "AspectRatio",
Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO));
si.SetBoolValue("Display", "Force4_3For24Bit", false);
@ -652,7 +654,9 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
g_settings.display_active_start_offset != old_settings.display_active_start_offset ||
g_settings.display_active_end_offset != old_settings.display_active_end_offset)
g_settings.display_active_end_offset != old_settings.display_active_end_offset ||
g_settings.display_line_start_offset != old_settings.display_line_start_offset ||
g_settings.display_line_end_offset != old_settings.display_line_end_offset)
{
g_gpu->UpdateSettings();
}

View file

@ -171,6 +171,8 @@ void Settings::Load(SettingsInterface& si)
display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false);
display_active_start_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveStartOffset", 0));
display_active_end_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveEndOffset", 0));
display_line_start_offset = static_cast<s8>(si.GetIntValue("Display", "LineStartOffset", 0));
display_line_end_offset = static_cast<s8>(si.GetIntValue("Display", "LineEndOffset", 0));
display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
display_integer_scaling = si.GetBoolValue("Display", "IntegerScaling", false);
display_post_processing = si.GetBoolValue("Display", "PostProcessing", false);
@ -296,6 +298,8 @@ void Settings::Save(SettingsInterface& si) const
si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
si.SetIntValue("Display", "ActiveStartOffset", display_active_start_offset);
si.SetIntValue("Display", "ActiveEndOffset", display_active_end_offset);
si.SetIntValue("Display", "LineStartOffset", display_line_start_offset);
si.SetIntValue("Display", "LineEndOffset", display_line_end_offset);
si.SetBoolValue("Display", "Force4_3For24Bit", display_force_4_3_for_24bit);
si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);

View file

@ -113,6 +113,12 @@ struct Settings
DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::R4_3;
s16 display_active_start_offset = 0;
s16 display_active_end_offset = 0;
s8 display_line_start_offset = 0;
s8 display_line_end_offset = 0;
s8 display_crop_left = 0;
s8 display_crop_right = 0;
s8 display_crop_top = 0;
s8 display_crop_bottom = 0;
bool display_force_4_3_for_24bit = false;
bool gpu_24bit_chroma_smoothing = false;
bool display_linear_filtering = true;

View file

@ -489,7 +489,7 @@ void LibretroHostInterface::OnSystemDestroyed()
m_using_hardware_renderer = false;
}
static std::array<retro_core_option_definition, 46> s_option_definitions = {{
static std::array<retro_core_option_definition, 49> s_option_definitions = {{
{"duckstation_Console.Region",
"Console Region",
"Determines which region/hardware to emulate. Auto-Detect will use the region of the disc inserted.",
@ -834,6 +834,45 @@ static std::array<retro_core_option_definition, 46> s_option_definitions = {{
"LUT"
#endif
},
{"duckstation_Display.LineStartOffset",
"Display Line Start Offset",
"Pads or crops off lines from the top of the displayed image.",
{{"-30", "-30"}, {"-29", "-29"}, {"-28", "-28"}, {"-27", "-27"}, {"-26", "-26"}, {"-25", "-25"}, {"-24", "-24"},
{"-23", "-23"}, {"-22", "-22"}, {"-21", "-21"}, {"-20", "-20"}, {"-19", "-19"}, {"-18", "-18"}, {"-17", "-17"},
{"-16", "-16"}, {"-15", "-15"}, {"-14", "-14"}, {"-13", "-13"}, {"-12", "-12"}, {"-11", "-11"}, {"-10", "-10"},
{"-9", "-9"}, {"-8", "-8"}, {"-7", "-7"}, {"-6", "-6"}, {"-5", "-5"}, {"-4", "-4"}, {"-3", "-3"},
{"-2", "-2"}, {"-1", "-1"}, {"0", "0"}, {"1", "1"}, {"2", "2"}, {"3", "3"}, {"4", "4"},
{"5", "5"}, {"6", "6"}, {"7", "7"}, {"8", "8"}, {"9", "9"}, {"10", "10"}, {"11", "11"},
{"12", "12"}, {"13", "13"}, {"14", "14"}, {"15", "15"}, {"16", "16"}, {"17", "17"}, {"18", "18"},
{"19", "19"}, {"20", "20"}, {"21", "21"}, {"22", "22"}, {"23", "23"}, {"24", "24"}, {"25", "25"},
{"26", "26"}, {"27", "27"}, {"28", "28"}, {"29", "29"}, {"30", "30"}},
"0"},
{"duckstation_Display.LineEndOffset",
"Display Line End Offset",
"Pads or crops off lines from the bottom of the displayed image.",
{{"-30", "-30"}, {"-29", "-29"}, {"-28", "-28"}, {"-27", "-27"}, {"-26", "-26"}, {"-25", "-25"}, {"-24", "-24"},
{"-23", "-23"}, {"-22", "-22"}, {"-21", "-21"}, {"-20", "-20"}, {"-19", "-19"}, {"-18", "-18"}, {"-17", "-17"},
{"-16", "-16"}, {"-15", "-15"}, {"-14", "-14"}, {"-13", "-13"}, {"-12", "-12"}, {"-11", "-11"}, {"-10", "-10"},
{"-9", "-9"}, {"-8", "-8"}, {"-7", "-7"}, {"-6", "-6"}, {"-5", "-5"}, {"-4", "-4"}, {"-3", "-3"},
{"-2", "-2"}, {"-1", "-1"}, {"0", "0"}, {"1", "1"}, {"2", "2"}, {"3", "3"}, {"4", "4"},
{"5", "5"}, {"6", "6"}, {"7", "7"}, {"8", "8"}, {"9", "9"}, {"10", "10"}, {"11", "11"},
{"12", "12"}, {"13", "13"}, {"14", "14"}, {"15", "15"}, {"16", "16"}, {"17", "17"}, {"18", "18"},
{"19", "19"}, {"20", "20"}, {"21", "21"}, {"22", "22"}, {"23", "23"}, {"24", "24"}, {"25", "25"},
{"26", "26"}, {"27", "27"}, {"28", "28"}, {"29", "29"}, {"30", "30"}},
"0"},
{"duckstation_GPU.PGXPTolerance",
"PGXP Geometry Tolerance",
"Ignores precise positions if the difference exceeds this threshold.",
{
{"-1.0", "None"}, {"0.5", "0.5 pixels"}, {"1.0", "1.0 pixels"}, {"1.5", "1.5 pixels"},
{"2.0", "2.0 pixels"}, {"2.5", "2.5 pixels"}, {"3.0", "3.0 pixels"}, {"3.5", "3.5 pixels"},
{"4.0", "4.0 pixels"}, {"4.5", "4.5 pixels"}, {"5.0", "5.0 pixels"}, {"5.5", "5.5 pixels"},
{"6.0", "6.0 pixels"}, {"6.5", "6.5 pixels"}, {"7.0", "7.0 pixels"}, {"7.5", "7.5 pixels"},
{"8.0", "8.0 pixels"}, {"8.5", "8.5 pixels"}, {"9.0", "9.0 pixels"}, {"9.5", "9.0 pixels"},
{"10.0", "10.0 pixels"},
},
"-1.0"},
{},
}};

View file

@ -289,6 +289,16 @@ void GamePropertiesDialog::populateGameSettings()
QSignalBlocker sb(m_ui.displayActiveEndOffset);
m_ui.displayActiveEndOffset->setValue(static_cast<int>(gs.display_active_end_offset.value()));
}
if (gs.display_line_start_offset.has_value())
{
QSignalBlocker sb(m_ui.displayLineStartOffset);
m_ui.displayLineStartOffset->setValue(static_cast<int>(gs.display_line_start_offset.value()));
}
if (gs.display_line_end_offset.has_value())
{
QSignalBlocker sb(m_ui.displayLineEndOffset);
m_ui.displayLineEndOffset->setValue(static_cast<int>(gs.display_line_end_offset.value()));
}
if (gs.dma_max_slice_ticks.has_value())
{
@ -645,6 +655,20 @@ void GamePropertiesDialog::connectUi()
m_game_settings.display_active_end_offset = static_cast<s16>(value);
saveGameSettings();
});
connect(m_ui.displayLineStartOffset, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
if (value == 0)
m_game_settings.display_line_start_offset.reset();
else
m_game_settings.display_line_start_offset = static_cast<s16>(value);
saveGameSettings();
});
connect(m_ui.displayLineEndOffset, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
if (value == 0)
m_game_settings.display_line_end_offset.reset();
else
m_game_settings.display_line_end_offset = static_cast<s16>(value);
saveGameSettings();
});
connect(m_ui.dmaMaxSliceTicks, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
if (value == 0)
m_game_settings.dma_max_slice_ticks.reset();

View file

@ -687,13 +687,50 @@
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Display Line Offset:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSpinBox" name="displayLineStartOffset">
<property name="minimum">
<number>-128</number>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="displayLineEndOffset">
<property name="minimum">
<number>-128</number>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>DMA Max Slice Ticks:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QSpinBox" name="dmaMaxSliceTicks">
<property name="maximum">
<number>10000</number>
@ -703,14 +740,14 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>DMA Halt Ticks:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSpinBox" name="dmaHaltTicks">
<property name="maximum">
<number>1000</number>
@ -720,28 +757,28 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>GPU FIFO Size:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QSpinBox" name="gpuFIFOSize">
<property name="maximum">
<number>128</number>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>GPU Max Run Ahead:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QSpinBox" name="gpuMaxRunAhead">
<property name="maximum">
<number>1000</number>
@ -751,14 +788,14 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>PGXP Geometry Tolerance:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="gpuPGXPTolerance">
<property name="minimum">
<number>-1</number>

View file

@ -108,7 +108,7 @@ private:
enum : u32
{
GAME_LIST_CACHE_SIGNATURE = 0x45434C47,
GAME_LIST_CACHE_VERSION = 15
GAME_LIST_CACHE_VERSION = 16
};
using DatabaseMap = std::unordered_map<std::string, GameListDatabaseEntry>;

View file

@ -111,6 +111,8 @@ bool Entry::LoadFromStream(ByteStream* stream)
!ReadOptionalFromStream(stream, &cpu_overclock_enable) || !ReadOptionalFromStream(stream, &cdrom_read_speedup) ||
!ReadOptionalFromStream(stream, &display_active_start_offset) ||
!ReadOptionalFromStream(stream, &display_active_end_offset) ||
!ReadOptionalFromStream(stream, &display_line_start_offset) ||
!ReadOptionalFromStream(stream, &display_line_end_offset) ||
!ReadOptionalFromStream(stream, &dma_max_slice_ticks) || !ReadOptionalFromStream(stream, &dma_halt_ticks) ||
!ReadOptionalFromStream(stream, &gpu_fifo_size) || !ReadOptionalFromStream(stream, &gpu_max_run_ahead) ||
!ReadOptionalFromStream(stream, &gpu_pgxp_tolerance) || !ReadOptionalFromStream(stream, &display_crop_mode) ||
@ -158,10 +160,11 @@ bool Entry::SaveToStream(ByteStream* stream) const
WriteOptionalToStream(stream, cpu_overclock_enable) && WriteOptionalToStream(stream, cdrom_read_speedup) &&
WriteOptionalToStream(stream, display_active_start_offset) &&
WriteOptionalToStream(stream, display_active_end_offset) &&
WriteOptionalToStream(stream, dma_max_slice_ticks) && WriteOptionalToStream(stream, dma_halt_ticks) &&
WriteOptionalToStream(stream, gpu_fifo_size) && WriteOptionalToStream(stream, gpu_max_run_ahead) &&
WriteOptionalToStream(stream, gpu_pgxp_tolerance) && WriteOptionalToStream(stream, display_crop_mode) &&
WriteOptionalToStream(stream, display_aspect_ratio) &&
WriteOptionalToStream(stream, display_line_start_offset) &&
WriteOptionalToStream(stream, display_line_end_offset) && WriteOptionalToStream(stream, dma_max_slice_ticks) &&
WriteOptionalToStream(stream, dma_halt_ticks) && WriteOptionalToStream(stream, gpu_fifo_size) &&
WriteOptionalToStream(stream, gpu_max_run_ahead) && WriteOptionalToStream(stream, gpu_pgxp_tolerance) &&
WriteOptionalToStream(stream, display_crop_mode) && WriteOptionalToStream(stream, display_aspect_ratio) &&
WriteOptionalToStream(stream, display_linear_upscaling) &&
WriteOptionalToStream(stream, display_integer_upscaling) &&
WriteOptionalToStream(stream, display_force_4_3_for_24bit) &&
@ -204,7 +207,13 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA
entry->display_active_start_offset = static_cast<s16>(lvalue);
lvalue = ini.GetLongValue(section, "DisplayActiveEndOffset", 0);
if (lvalue != 0)
entry->display_active_end_offset = static_cast<s16>(lvalue);
entry->display_active_end_offset = static_cast<s8>(lvalue);
lvalue = ini.GetLongValue(section, "DisplayLineStartOffset", 0);
if (lvalue != 0)
entry->display_line_start_offset = static_cast<s8>(lvalue);
lvalue = ini.GetLongValue(section, "DisplayLineEndOffset", 0);
if (lvalue != 0)
entry->display_line_end_offset = static_cast<s16>(lvalue);
lvalue = ini.GetLongValue(section, "DMAMaxSliceTicks", 0);
if (lvalue > 0)
entry->dma_max_slice_ticks = static_cast<u32>(lvalue);
@ -311,6 +320,10 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA
ini.SetLongValue(section, "DisplayActiveStartOffset", entry.display_active_start_offset.value());
if (entry.display_active_end_offset.has_value())
ini.SetLongValue(section, "DisplayActiveEndOffset", entry.display_active_end_offset.value());
if (entry.display_line_start_offset.has_value())
ini.SetLongValue(section, "DisplayLineStartOffset", entry.display_line_start_offset.value());
if (entry.display_line_end_offset.has_value())
ini.SetLongValue(section, "DisplayLineEndOffset", entry.display_line_end_offset.value());
if (entry.dma_max_slice_ticks.has_value())
ini.SetLongValue(section, "DMAMaxSliceTicks", static_cast<long>(entry.dma_max_slice_ticks.value()));
if (entry.dma_halt_ticks.has_value())
@ -488,6 +501,10 @@ void Entry::ApplySettings(bool display_osd_messages) const
g_settings.display_active_start_offset = display_active_start_offset.value();
if (display_active_end_offset.has_value())
g_settings.display_active_end_offset = display_active_end_offset.value();
if (display_line_start_offset.has_value())
g_settings.display_line_start_offset = display_line_start_offset.value();
if (display_line_end_offset.has_value())
g_settings.display_line_end_offset = display_line_end_offset.value();
if (dma_max_slice_ticks.has_value())
g_settings.dma_max_slice_ticks = dma_max_slice_ticks.value();
if (dma_halt_ticks.has_value())

View file

@ -39,6 +39,8 @@ struct Entry
std::bitset<static_cast<int>(Trait::Count)> traits{};
std::optional<s16> display_active_start_offset;
std::optional<s16> display_active_end_offset;
std::optional<s8> display_line_start_offset;
std::optional<s8> display_line_end_offset;
std::optional<u32> dma_max_slice_ticks;
std::optional<u32> dma_halt_ticks;
std::optional<u32> gpu_fifo_size;