mirror of
https://github.com/stenzek/duckstation.git
synced 2026-04-05 21:50:48 +00:00
GPU: Add "Fine Crop" settings
Allows the image to be cropped while preserving output aspect ratio.
This commit is contained in:
@@ -1064,25 +1064,33 @@ bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, std::string_view t
|
||||
|
||||
const float midpoint = LayoutScale(150.0f);
|
||||
const float end = (ImGui::GetCurrentWindow()->WorkRect.GetWidth() - midpoint) + ImGui::GetStyle().WindowPadding.x;
|
||||
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
|
||||
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Left: ")));
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(midpoint);
|
||||
ImGui::SetNextItemWidth(end);
|
||||
const bool left_modified =
|
||||
ImGui::SliderInt("##left", &dlg_left_value, min_value, max_value, format, ImGuiSliderFlags_NoInput);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
|
||||
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Top: ")));
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(midpoint);
|
||||
ImGui::SetNextItemWidth(end);
|
||||
const bool top_modified =
|
||||
ImGui::SliderInt("##top", &dlg_top_value, min_value, max_value, format, ImGuiSliderFlags_NoInput);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
|
||||
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Right: ")));
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(midpoint);
|
||||
ImGui::SetNextItemWidth(end);
|
||||
const bool right_modified =
|
||||
ImGui::SliderInt("##right", &dlg_right_value, min_value, max_value, format, ImGuiSliderFlags_NoInput);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
|
||||
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Bottom: ")));
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(midpoint);
|
||||
ImGui::SetNextItemWidth(end);
|
||||
const bool bottom_modified =
|
||||
@@ -3874,6 +3882,21 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
||||
Settings::DEFAULT_DISPLAY_ROTATION, &Settings::ParseDisplayRotation,
|
||||
&Settings::GetDisplayRotationName, &Settings::GetDisplayRotationDisplayName, DisplayRotation::Count);
|
||||
|
||||
DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_CROP_SIMPLE, "Fine Crop Mode"),
|
||||
FSUI_VSTR("Enables manual fine cropping of the display area, while preserving the aspect ratio of "
|
||||
"the image. Useful for removing black borders in certain games."),
|
||||
"Display", "FineCropMode", Settings::DEFAULT_DISPLAY_FINE_CROP_MODE,
|
||||
&Settings::ParseDisplayFineCropMode, &Settings::GetDisplayFineCropModeName,
|
||||
&Settings::GetDisplayFineCropModeDisplayName, DisplayFineCropMode::MaxCount);
|
||||
if (Settings::ParseDisplayFineCropMode(GetEffectiveTinyStringSetting(bsi, "Display", "FineCropMode", "").c_str())
|
||||
.value_or(Settings::DEFAULT_DISPLAY_FINE_CROP_MODE) != DisplayFineCropMode::None)
|
||||
{
|
||||
DrawIntRectSetting(bsi, FSUI_ICONVSTR(ICON_FA_CROP_SIMPLE, "Fine Crop Amount"),
|
||||
FSUI_VSTR("Determines how much to crop the display area."), "Display", "FineCropLeft", 0,
|
||||
"FineCropTop", 0, "FineCropRight", 0, "FineCropBottom", 0, std::numeric_limits<s16>::min(),
|
||||
std::numeric_limits<s16>::max(), "%dpx");
|
||||
}
|
||||
|
||||
if (is_hardware)
|
||||
{
|
||||
DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_GRIP_LINES_VERTICAL, "Line Detection"),
|
||||
|
||||
@@ -243,6 +243,7 @@ TRANSLATE_NOOP("FullscreenUI", "Determines how much button pressure is ignored b
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how much latency there is between the audio being picked up by the host API, and played through speakers.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how much of the area typically not visible on a consumer TV set to crop/hide.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how much pressure is simulated when macro is active.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how much to crop the display area.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how the emulated CPU executes instructions.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines quality of audio when not running at 100% speed.");
|
||||
@@ -325,6 +326,7 @@ TRANSLATE_NOOP("FullscreenUI", "Enables depth testing for semi-transparent polyg
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables dumping of textures to image files, which can be replaced. Not compatible with all games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables loading of cheats for this game from DuckStation's database.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables loading of replacement textures. Not compatible with all games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables manual fine cropping of the display area, while preserving the aspect ratio of the image. Useful for removing black borders in certain games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables smooth scrolling of menus in the Big Picture UI.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables the cheats that are selected below.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables the older, less accurate MDEC decoding routines. May be required for old replacement backgrounds to match/load.");
|
||||
@@ -359,6 +361,8 @@ TRANSLATE_NOOP("FullscreenUI", "Fast forwards through memory card access, both l
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fast forwards through the early loading process when fast booting, saving time. Results may vary between games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "File Size");
|
||||
TRANSLATE_NOOP("FullscreenUI", "File Title");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fine Crop Amount");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fine Crop Mode");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Force 4:3 For FMVs");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Forces a full rescan of all games previously identified.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Forces texture upload tracking to be enabled regardless of whether it is needed.");
|
||||
@@ -676,6 +680,7 @@ TRANSLATE_NOOP("FullscreenUI", "Settings reset to default.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shader {} added as stage {}.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shared Card Name");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Achievement Trophy Icons");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show All");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show CPU Usage");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Controller Input");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Enhancement Settings");
|
||||
@@ -687,6 +692,7 @@ TRANSLATE_NOOP("FullscreenUI", "Show Grid View Titles");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Latency Statistics");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Localized Titles");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Messages");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Nearby");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Resolution");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Speed");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Status Indicators");
|
||||
|
||||
127
src/core/gpu.cpp
127
src/core/gpu.cpp
@@ -735,18 +735,44 @@ float GPU::ComputeAspectRatioCorrection() const
|
||||
return (relative_width / relative_height);
|
||||
}
|
||||
|
||||
GSVector2 GPU::ApplyPixelAspectRatioToSize(float par, GSVector2 size)
|
||||
GSVector2 GPU::CalculateDisplayWindowSize(DisplayFineCropMode mode, std::span<const s16, 4> amount,
|
||||
float pixel_aspect_ratio, const GSVector2 video_size,
|
||||
const GSVector2 source_size, const GSVector2 window_size)
|
||||
{
|
||||
if (par < 1.0f)
|
||||
GSVector2 size = video_size;
|
||||
if (pixel_aspect_ratio < 1.0f)
|
||||
{
|
||||
// stretch height, preserve width
|
||||
size.y = std::ceil(size.y / par);
|
||||
size.y = size.y / pixel_aspect_ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
// stretch width, preserve height
|
||||
size.x = std::ceil(size.x * par);
|
||||
size.x = size.x * pixel_aspect_ratio;
|
||||
}
|
||||
|
||||
if (mode != DisplayFineCropMode::None)
|
||||
{
|
||||
GSVector4 crop_amount = GSVector4(GSVector4i::loadl<false>(amount.data()).s16to32());
|
||||
switch (mode)
|
||||
{
|
||||
case DisplayFineCropMode::VideoResolution:
|
||||
break;
|
||||
|
||||
case DisplayFineCropMode::InternalResolution:
|
||||
crop_amount *= GSVector4::xyxy(size / source_size);
|
||||
break;
|
||||
|
||||
case DisplayFineCropMode::WindowResolution:
|
||||
crop_amount *= GSVector4::xyxy(size / window_size);
|
||||
break;
|
||||
|
||||
DefaultCaseIsUnreachable();
|
||||
}
|
||||
|
||||
size = (size - crop_amount.xy() - crop_amount.zw()).max(GSVector2::cxpr(1.0f));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -1342,45 +1368,44 @@ u8 GPU::UpdateOrGetGPUBusyPct()
|
||||
return m_last_gpu_busy_pct;
|
||||
}
|
||||
|
||||
void GPU::ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
|
||||
float* display_y) const
|
||||
GSVector2 GPU::ConvertScreenCoordinatesToDisplayCoordinates(GSVector2 window_pos) const
|
||||
{
|
||||
const WindowInfo& wi = GPUThread::GetRenderWindowInfo();
|
||||
if (wi.IsSurfaceless())
|
||||
{
|
||||
*display_x = *display_y = -1.0f;
|
||||
return;
|
||||
}
|
||||
return GSVector2::cxpr(-1.0f);
|
||||
|
||||
GSVector4 crop_amount = GSVector4::zero();
|
||||
GSVector4i source_rc, display_rc, draw_rc;
|
||||
CalculateDrawRect(GSVector2i(wi.surface_width, wi.surface_height), GetCRTCVideoSize(), GetCRTCVideoActiveRect(),
|
||||
const GSVector2i crtc_video_size = GetCRTCVideoSize();
|
||||
CalculateDrawRect(GSVector2i(wi.surface_width, wi.surface_height), crtc_video_size, GetCRTCVideoActiveRect(),
|
||||
GetCRTCVRAMSourceRect(), g_settings.display_rotation, g_settings.display_alignment,
|
||||
ComputePixelAspectRatio(),
|
||||
(g_settings.display_scaling == DisplayScalingMode::NearestInteger ||
|
||||
g_settings.display_scaling == DisplayScalingMode::BilinearInteger),
|
||||
&source_rc, &display_rc, &draw_rc);
|
||||
g_settings.display_fine_crop_mode, g_settings.display_fine_crop_amount, &source_rc, &display_rc,
|
||||
&draw_rc, &crop_amount);
|
||||
|
||||
// convert coordinates to active display region, then to full display region
|
||||
const float scaled_display_x =
|
||||
(window_x - static_cast<float>(display_rc.left)) / static_cast<float>(display_rc.width());
|
||||
const float scaled_display_y =
|
||||
(window_y - static_cast<float>(display_rc.top)) / static_cast<float>(display_rc.height());
|
||||
const GSVector2 local_pos = window_pos - GSVector2(display_rc.xy());
|
||||
GSVector2 scaled_display_pos = local_pos / GSVector2(display_rc.rsize());
|
||||
|
||||
// scale back to internal resolution
|
||||
*display_x = scaled_display_x * static_cast<float>(m_crtc_state.display_width);
|
||||
*display_y = scaled_display_y * static_cast<float>(m_crtc_state.display_height);
|
||||
const GSVector2 cropped_video_size = GSVector2(crtc_video_size) - crop_amount.xy() - crop_amount.zw();
|
||||
const GSVector2 display_pos = scaled_display_pos * cropped_video_size + crop_amount.xy();
|
||||
|
||||
// TODO: apply rotation matrix
|
||||
|
||||
DEV_LOG("win {:.0f},{:.0f} -> local {:.0f},{:.0f}, disp {:.2f},{:.2f} (size {},{} frac {},{})", window_x, window_y,
|
||||
window_x - display_rc.left, window_y - display_rc.top, *display_x, *display_y, m_crtc_state.display_width,
|
||||
m_crtc_state.display_height, *display_x / static_cast<float>(m_crtc_state.display_width),
|
||||
*display_y / static_cast<float>(m_crtc_state.display_height));
|
||||
DEV_LOG("win {} -> local {}, disp {} (size {} frac {})", window_pos, local_pos, display_pos, cropped_video_size,
|
||||
display_pos / cropped_video_size);
|
||||
|
||||
return display_pos;
|
||||
}
|
||||
|
||||
bool GPU::ConvertDisplayCoordinatesToBeamTicksAndLines(float display_x, float display_y, float x_scale, u32* out_tick,
|
||||
bool GPU::ConvertDisplayCoordinatesToBeamTicksAndLines(const GSVector2& display_pos, float x_scale, u32* out_tick,
|
||||
u32* out_line) const
|
||||
{
|
||||
float display_x = display_pos.x;
|
||||
float display_y = display_pos.y;
|
||||
if (x_scale != 1.0f)
|
||||
{
|
||||
const float dw = static_cast<float>(m_crtc_state.display_width);
|
||||
@@ -1803,12 +1828,14 @@ static bool IntegerScalePreferWidth(float display_width, float display_height, f
|
||||
void GPU::CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& video_size,
|
||||
const GSVector4i& video_active_rect, const GSVector4i& source_rect,
|
||||
DisplayRotation rotation, DisplayAlignment alignment, float pixel_aspect_ratio,
|
||||
bool integer_scale, GSVector4i* out_source_rect, GSVector4i* out_display_rect,
|
||||
GSVector4i* out_draw_rect)
|
||||
bool integer_scale, DisplayFineCropMode fine_crop,
|
||||
const std::span<const s16, 4>& fine_crop_amount, GSVector4i* out_source_rect,
|
||||
GSVector4i* out_display_rect, GSVector4i* out_draw_rect, GSVector4* out_crop_amount)
|
||||
{
|
||||
GSVector2 fwindow_size = GSVector2(window_size);
|
||||
GSVector2 fvideo_size = GSVector2(video_size);
|
||||
GSVector4 fvideo_active_rect = GSVector4(video_active_rect);
|
||||
GSVector4 fsource_rect = GSVector4(source_rect);
|
||||
|
||||
// for integer scale, use whichever gets us a greater effective display size
|
||||
// this is needed for games like crash where the framebuffer is wide to not lose detail
|
||||
@@ -1825,6 +1852,51 @@ void GPU::CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& vid
|
||||
fvideo_active_rect = fvideo_active_rect.blend32<10>(fvideo_active_rect / pixel_aspect_ratio);
|
||||
}
|
||||
|
||||
if (fine_crop != DisplayFineCropMode::None)
|
||||
{
|
||||
GSVector4 crop_amount = GSVector4(GSVector4i::loadl<false>(fine_crop_amount.data()).s16to32());
|
||||
switch (fine_crop)
|
||||
{
|
||||
case DisplayFineCropMode::VideoResolution:
|
||||
break;
|
||||
|
||||
case DisplayFineCropMode::InternalResolution:
|
||||
crop_amount *= GSVector4::xyxy(fvideo_size / fsource_rect.rsize());
|
||||
break;
|
||||
|
||||
case DisplayFineCropMode::WindowResolution:
|
||||
crop_amount *= GSVector4::xyxy(fvideo_size / fwindow_size);
|
||||
break;
|
||||
|
||||
DefaultCaseIsUnreachable();
|
||||
}
|
||||
|
||||
if (out_crop_amount)
|
||||
*out_crop_amount = crop_amount;
|
||||
|
||||
// apply crop to padding first
|
||||
const GSVector2 crop_padding_left_top = fvideo_active_rect.xy().min(crop_amount.xy());
|
||||
const GSVector2 crop_padding_right_bottom = (fvideo_size - fvideo_active_rect.zw()).min(crop_amount.zw());
|
||||
fvideo_active_rect = fvideo_active_rect - GSVector4::xyxy(crop_padding_left_top);
|
||||
fvideo_size = fvideo_size - crop_padding_left_top - crop_padding_right_bottom;
|
||||
crop_amount = crop_amount - GSVector4::xyxy(crop_padding_left_top, crop_padding_right_bottom);
|
||||
|
||||
// apply remaining crop to active area
|
||||
const GSVector2 crop_size = crop_amount.xy() + crop_amount.zw();
|
||||
fvideo_active_rect -= GSVector4::loadh(crop_size);
|
||||
fvideo_size -= crop_size;
|
||||
|
||||
// need to take it off the source too
|
||||
const GSVector2 video_to_source = fsource_rect.rsize() / fvideo_active_rect.rsize();
|
||||
const GSVector4 source_crop_amount = crop_amount * GSVector4::xyxy(video_to_source);
|
||||
fsource_rect = (fsource_rect + source_crop_amount).blend32<12>(fsource_rect - source_crop_amount);
|
||||
|
||||
// ensure we haven't cropped everything away
|
||||
fvideo_active_rect = fvideo_active_rect.sat(GSVector4::zero(), fvideo_active_rect);
|
||||
fvideo_size -= crop_size.sat(GSVector2::zero(), GSVector2::cxpr(1.0f, 1.0f));
|
||||
fsource_rect = fsource_rect.sat(GSVector4::zero(), fsource_rect);
|
||||
}
|
||||
|
||||
// swap width/height when rotated, the flipping of padding is taken care of in the shader with the rotation matrix
|
||||
if (rotation == DisplayRotation::Rotate90 || rotation == DisplayRotation::Rotate270)
|
||||
{
|
||||
@@ -1901,7 +1973,7 @@ void GPU::CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& vid
|
||||
const GSVector4 padding4 = GSVector4::xyxy(padding);
|
||||
fvideo_size *= scale;
|
||||
fvideo_active_rect *= scale;
|
||||
*out_source_rect = source_rect;
|
||||
*out_source_rect = GSVector4i(fsource_rect);
|
||||
*out_draw_rect = GSVector4i(fvideo_active_rect + padding4);
|
||||
*out_display_rect = GSVector4i(GSVector4::loadh(fvideo_size) + padding4);
|
||||
}
|
||||
@@ -2044,7 +2116,8 @@ u8 GPU::CalculateAutomaticResolutionScale() const
|
||||
CalculateDrawRect(GSVector2i(main_window_info.surface_width, main_window_info.surface_height), GetCRTCVideoSize(),
|
||||
GetCRTCVideoActiveRect(), GetCRTCVRAMSourceRect(), g_settings.display_rotation,
|
||||
g_settings.display_alignment, g_settings.gpu_show_vram ? 1.0f : ComputePixelAspectRatio(),
|
||||
g_settings.IsUsingIntegerDisplayScaling(false), &source_rect, &display_rect, &draw_rect);
|
||||
g_settings.IsUsingIntegerDisplayScaling(false), g_settings.display_fine_crop_mode,
|
||||
g_settings.display_fine_crop_amount, &source_rect, &display_rect, &draw_rect);
|
||||
|
||||
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
|
||||
// anamorphic aspect ratio.
|
||||
|
||||
@@ -197,12 +197,13 @@ public:
|
||||
float ComputeAspectRatioCorrection() const;
|
||||
|
||||
/// Applies the pixel aspect ratio to a given size, preserving the larger dimension.
|
||||
static GSVector2 ApplyPixelAspectRatioToSize(float par, GSVector2 size);
|
||||
static GSVector2 CalculateDisplayWindowSize(DisplayFineCropMode mode, std::span<const s16, 4> amount,
|
||||
float pixel_aspect_ratio, const GSVector2 video_size,
|
||||
const GSVector2 source_size, const GSVector2 window_size);
|
||||
|
||||
// Converts window coordinates into horizontal ticks and scanlines. Returns false if out of range. Used for lightguns.
|
||||
void ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
|
||||
float* display_y) const;
|
||||
bool ConvertDisplayCoordinatesToBeamTicksAndLines(float display_x, float display_y, float x_scale, u32* out_tick,
|
||||
// Converts window coordinates into horizontal ticks and scanlines. Returns -1 if out of range. Used for lightguns.
|
||||
GSVector2 ConvertScreenCoordinatesToDisplayCoordinates(GSVector2 window_pos) const;
|
||||
bool ConvertDisplayCoordinatesToBeamTicksAndLines(const GSVector2& display_pos, float x_scale, u32* out_tick,
|
||||
u32* out_line) const;
|
||||
|
||||
// Returns the current beam position.
|
||||
@@ -244,8 +245,10 @@ public:
|
||||
static void CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& video_size,
|
||||
const GSVector4i& video_active_rect, const GSVector4i& source_rect,
|
||||
DisplayRotation rotation, DisplayAlignment alignment, float pixel_aspect_ratio,
|
||||
bool integer_scale, GSVector4i* out_source_rect, GSVector4i* out_display_rect,
|
||||
GSVector4i* out_draw_rect);
|
||||
bool integer_scale, DisplayFineCropMode fine_crop,
|
||||
const std::span<const s16, 4>& fine_crop_amount, GSVector4i* out_source_rect,
|
||||
GSVector4i* out_display_rect, GSVector4i* out_draw_rect,
|
||||
GSVector4* out_crop_amount = nullptr);
|
||||
|
||||
private:
|
||||
TickCount CRTCTicksToSystemTicks(TickCount crtc_ticks, TickCount fractional_ticks) const;
|
||||
|
||||
@@ -703,7 +703,7 @@ bool GPUBackend::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bo
|
||||
// Crop it if border overlay isn't enabled.
|
||||
GSVector4i source_rect, draw_rect, display_rect;
|
||||
backend->GetPresenter().CalculateDrawRect(GSVector2i(static_cast<s32>(width), static_cast<s32>(height)),
|
||||
apply_aspect_ratio, false, false, &source_rect, &display_rect,
|
||||
apply_aspect_ratio, false, true, false, &source_rect, &display_rect,
|
||||
&draw_rect);
|
||||
image_width = static_cast<u32>(display_rect.width());
|
||||
image_height = static_cast<u32>(display_rect.height());
|
||||
|
||||
@@ -457,8 +457,8 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
|
||||
GSVector4i(GSVector4(m_border_overlay_display_rect) * GSVector4::xyxy(scale)).add32(overlay_rect.xyxy());
|
||||
|
||||
// Draw to the overlay area instead of the whole screen. Always align in center, we align the overlay instead.
|
||||
CalculateDrawRect(overlay_display_rect.rsize(), apply_aspect_ratio, integer_scale, false, &source_rect,
|
||||
&display_rect_without_overlay, &draw_rect_without_overlay);
|
||||
CalculateDrawRect(overlay_display_rect.rsize(), apply_aspect_ratio, integer_scale, !is_vram_view, false,
|
||||
&source_rect, &display_rect_without_overlay, &draw_rect_without_overlay);
|
||||
|
||||
// Apply overlay area offset.
|
||||
display_rect = display_rect_without_overlay.add32(overlay_display_rect.xyxy());
|
||||
@@ -466,8 +466,8 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateDrawRect(target_size, apply_aspect_ratio, integer_scale, true, &source_rect, &display_rect_without_overlay,
|
||||
&draw_rect_without_overlay);
|
||||
CalculateDrawRect(target_size, apply_aspect_ratio, integer_scale, !is_vram_view, true, &source_rect,
|
||||
&display_rect_without_overlay, &draw_rect_without_overlay);
|
||||
display_rect = display_rect_without_overlay;
|
||||
draw_rect = draw_rect_without_overlay;
|
||||
}
|
||||
@@ -738,7 +738,7 @@ void GPUPresenter::DrawDisplay(const GSVector2i target_size, const GSVector2i fi
|
||||
const GSVector2 region_range =
|
||||
(GSVector2(display_rect.rsize()) / GSVector2(display_source_rect)).floor().max(GSVector2::cxpr(1.0f));
|
||||
GSVector2::store<true>(&uniforms.params[0], region_range);
|
||||
GSVector2::store<true>(&uniforms.params[1], GSVector2::cxpr(0.5f) - (GSVector2::cxpr(0.5f) / region_range));
|
||||
GSVector2::store<true>(&uniforms.params[2], GSVector2::cxpr(0.5f) - (GSVector2::cxpr(0.5f) / region_range));
|
||||
texture_filter_linear = true;
|
||||
}
|
||||
break;
|
||||
@@ -1175,13 +1175,15 @@ bool GPUPresenter::ApplyChromaSmoothing()
|
||||
}
|
||||
|
||||
void GPUPresenter::CalculateDrawRect(const GSVector2i& window_size, bool apply_aspect_ratio, bool integer_scale,
|
||||
bool apply_alignment, GSVector4i* source_rect, GSVector4i* display_rect,
|
||||
GSVector4i* draw_rect) const
|
||||
bool apply_crop, bool apply_alignment, GSVector4i* source_rect,
|
||||
GSVector4i* display_rect, GSVector4i* draw_rect) const
|
||||
{
|
||||
GPU::CalculateDrawRect(
|
||||
window_size, m_video_size, m_video_active_rect, m_display_texture_rect, g_gpu_settings.display_rotation,
|
||||
apply_alignment ? g_gpu_settings.display_alignment : DisplayAlignment::Center,
|
||||
apply_aspect_ratio ? m_display_pixel_aspect_ratio : 1.0f, integer_scale, source_rect, display_rect, draw_rect);
|
||||
GPU::CalculateDrawRect(window_size, m_video_size, m_video_active_rect, m_display_texture_rect,
|
||||
g_gpu_settings.display_rotation,
|
||||
apply_alignment ? g_gpu_settings.display_alignment : DisplayAlignment::Center,
|
||||
apply_aspect_ratio ? m_display_pixel_aspect_ratio : 1.0f, integer_scale,
|
||||
apply_crop ? g_gpu_settings.display_fine_crop_mode : DisplayFineCropMode::None,
|
||||
g_gpu_settings.display_fine_crop_amount, source_rect, display_rect, draw_rect);
|
||||
}
|
||||
|
||||
bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bool allow_skip_present, u64 present_time)
|
||||
@@ -1362,6 +1364,8 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx,
|
||||
|
||||
GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) const
|
||||
{
|
||||
const GSVector2i window_size =
|
||||
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetSizeVec() : GSVector2i::cxpr(1, 1);
|
||||
if (m_display_texture)
|
||||
{
|
||||
if (g_gpu_settings.gpu_show_vram)
|
||||
@@ -1370,10 +1374,15 @@ GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) con
|
||||
}
|
||||
else if (mode != DisplayScreenshotMode::ScreenResolution)
|
||||
{
|
||||
GSVector2 f_size =
|
||||
GSVector2(m_video_size) * (GSVector2(m_display_texture_rect.rsize()) / GSVector2(m_video_active_rect.rsize()));
|
||||
if (mode != DisplayScreenshotMode::UncorrectedInternalResolution)
|
||||
f_size = GPU::ApplyPixelAspectRatioToSize(m_display_pixel_aspect_ratio, f_size);
|
||||
const GSVector2 fvideo_size = GSVector2(m_video_size);
|
||||
const GSVector2 fsource_size = GSVector2(m_display_texture_rect.rsize());
|
||||
const GSVector2 factive_size = GSVector2(m_video_active_rect.rsize());
|
||||
const GSVector2 fscale = (fsource_size / factive_size);
|
||||
GSVector2 f_size = GPU::CalculateDisplayWindowSize(
|
||||
g_gpu_settings.display_fine_crop_mode, g_gpu_settings.display_fine_crop_amount,
|
||||
(mode != DisplayScreenshotMode::UncorrectedInternalResolution) ? m_display_pixel_aspect_ratio : 1.0f,
|
||||
fvideo_size, fsource_size, GSVector2(window_size));
|
||||
f_size *= fscale;
|
||||
|
||||
// DX11 won't go past 16K texture size.
|
||||
const float max_texture_size = static_cast<float>(g_gpu_device->GetMaxTextureSize());
|
||||
@@ -1392,7 +1401,7 @@ GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) con
|
||||
}
|
||||
}
|
||||
|
||||
return g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetSizeVec() : GSVector2i(1, 1);
|
||||
return window_size;
|
||||
}
|
||||
|
||||
void GPUPresenter::LoadPostProcessingSettings(bool force_load)
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
bool ApplyChromaSmoothing();
|
||||
|
||||
/// Helper function for computing the draw rectangle in a larger window.
|
||||
void CalculateDrawRect(const GSVector2i& window_size, bool apply_aspect_ratio, bool integer_scale,
|
||||
void CalculateDrawRect(const GSVector2i& window_size, bool apply_aspect_ratio, bool integer_scale, bool apply_crop,
|
||||
bool apply_alignment, GSVector4i* source_rect, GSVector4i* display_rect,
|
||||
GSVector4i* draw_rect) const;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/gsvector_formatter.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -204,16 +205,14 @@ bool GunCon::Transfer(const u8 data_in, u8* data_out)
|
||||
|
||||
void GunCon::UpdatePosition()
|
||||
{
|
||||
float display_x, display_y;
|
||||
const auto& [window_x, window_y] = (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() :
|
||||
InputManager::GetPointerAbsolutePosition(m_cursor_index);
|
||||
g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y);
|
||||
const GSVector2 display_pos = g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(GSVector2(window_x, window_y));
|
||||
|
||||
// are we within the active display area?
|
||||
u32 tick, line;
|
||||
if (display_x < 0 || display_y < 0 ||
|
||||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_x, display_y, m_x_scale, &tick, &line) ||
|
||||
m_shoot_offscreen)
|
||||
if ((display_pos < GSVector2::zero()).anytrue() ||
|
||||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_pos, m_x_scale, &tick, &line) || m_shoot_offscreen)
|
||||
{
|
||||
DEBUG_LOG("Lightgun out of range for window coordinates {:.0f},{:.0f}", window_x, window_y);
|
||||
m_position_x = 0x01;
|
||||
@@ -225,8 +224,7 @@ void GunCon::UpdatePosition()
|
||||
const double divider = static_cast<double>(g_gpu.GetCRTCFrequency()) / 8000000.0;
|
||||
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
|
||||
m_position_y = static_cast<u16>(line);
|
||||
DEBUG_LOG("Lightgun window coordinates {:.0f},{:.0f} -> tick {} line {} 8mhz ticks {}", display_x, display_y, tick,
|
||||
line, m_position_x);
|
||||
DEBUG_LOG("Lightgun window coordinates {} -> tick {} line {} 8mhz ticks {}", display_pos, tick, line, m_position_x);
|
||||
}
|
||||
|
||||
std::pair<float, float> GunCon::GetAbsolutePositionFromRelativeAxes() const
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/gsvector_formatter.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -220,16 +221,14 @@ void Justifier::UpdatePosition()
|
||||
return;
|
||||
}
|
||||
|
||||
float display_x, display_y;
|
||||
const auto [window_x, window_y] = (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() :
|
||||
InputManager::GetPointerAbsolutePosition(m_cursor_index);
|
||||
g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y);
|
||||
const GSVector2 display_pos = g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(GSVector2(window_x, window_y));
|
||||
|
||||
// are we within the active display area?
|
||||
u32 tick, line;
|
||||
if (display_x < 0 || display_y < 0 ||
|
||||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_x, display_y, m_x_scale, &tick, &line) ||
|
||||
m_shoot_offscreen)
|
||||
if ((display_pos < GSVector2::zero()).anytrue() ||
|
||||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_pos, m_x_scale, &tick, &line) || m_shoot_offscreen)
|
||||
{
|
||||
DEV_LOG("Lightgun out of range for window coordinates {:.0f},{:.0f}", window_x, window_y);
|
||||
m_position_valid = false;
|
||||
@@ -248,8 +247,8 @@ void Justifier::UpdatePosition()
|
||||
static_cast<s32>(g_gpu.GetCRTCActiveStartLine()),
|
||||
static_cast<s32>(g_gpu.GetCRTCActiveEndLine())));
|
||||
|
||||
DEV_LOG("Lightgun window coordinates {},{} -> dpy {},{} -> tick {} line {} [{}-{}]", window_x, window_y, display_x,
|
||||
display_y, tick, line, m_irq_first_line, m_irq_last_line);
|
||||
DEV_LOG("Lightgun window coordinates {},{} -> dpy {} -> tick {} line {} [{}-{}]", window_x, window_y, display_pos,
|
||||
tick, line, m_irq_first_line, m_irq_last_line);
|
||||
|
||||
UpdateIRQEvent();
|
||||
}
|
||||
|
||||
@@ -319,6 +319,16 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
|
||||
.value_or(DEFAULT_DISPLAY_CROP_MODE);
|
||||
display_aspect_ratio =
|
||||
ParseDisplayAspectRatio(si.GetStringValue("Display", "AspectRatio")).value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
|
||||
display_fine_crop_mode = ParseDisplayFineCropMode(si.GetStringValue("Display", "FineCropMode").c_str())
|
||||
.value_or(DEFAULT_DISPLAY_FINE_CROP_MODE);
|
||||
display_fine_crop_amount[0] = static_cast<s16>(std::clamp<s32>(
|
||||
si.GetIntValue("Display", "FineCropLeft", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
|
||||
display_fine_crop_amount[1] = static_cast<s16>(std::clamp<s32>(
|
||||
si.GetIntValue("Display", "FineCropTop", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
|
||||
display_fine_crop_amount[2] = static_cast<s16>(std::clamp<s32>(
|
||||
si.GetIntValue("Display", "FineCropRight", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
|
||||
display_fine_crop_amount[3] = static_cast<s16>(std::clamp<s32>(
|
||||
si.GetIntValue("Display", "FineCropBottom", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
|
||||
display_alignment =
|
||||
ParseDisplayAlignment(
|
||||
si.GetStringValue("Display", "Alignment", GetDisplayAlignmentName(DEFAULT_DISPLAY_ALIGNMENT)).c_str())
|
||||
@@ -695,6 +705,11 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
||||
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).c_str());
|
||||
si.SetStringValue("Display", "FineCropMode", GetDisplayFineCropModeName(display_fine_crop_mode));
|
||||
si.SetIntValue("Display", "FineCropLeft", display_fine_crop_amount[0]);
|
||||
si.SetIntValue("Display", "FineCropTop", display_fine_crop_amount[1]);
|
||||
si.SetIntValue("Display", "FineCropRight", display_fine_crop_amount[2]);
|
||||
si.SetIntValue("Display", "FineCropBottom", display_fine_crop_amount[3]);
|
||||
si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment));
|
||||
si.SetStringValue("Display", "Rotation", GetDisplayRotationName(display_rotation));
|
||||
si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
|
||||
@@ -1910,6 +1925,46 @@ const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode)
|
||||
"DisplayCropMode");
|
||||
}
|
||||
|
||||
static constexpr const std::array s_display_fine_crop_mode_names = {
|
||||
"None",
|
||||
"VideoResolution",
|
||||
"InternalResolution",
|
||||
"WindowResolution",
|
||||
};
|
||||
static constexpr const std::array s_display_fine_crop_mode_display_names = {
|
||||
TRANSLATE_DISAMBIG_NOOP("Settings", "None", "DisplayFineCropMode"),
|
||||
TRANSLATE_DISAMBIG_NOOP("Settings", "Video Resolution", "DisplayFineCropMode"),
|
||||
TRANSLATE_DISAMBIG_NOOP("Settings", "Internal Resolution", "DisplayFineCropMode"),
|
||||
TRANSLATE_DISAMBIG_NOOP("Settings", "Window Resolution", "DisplayFineCropMode"),
|
||||
};
|
||||
static_assert(s_display_fine_crop_mode_names.size() == static_cast<size_t>(DisplayFineCropMode::MaxCount));
|
||||
static_assert(s_display_fine_crop_mode_display_names.size() == static_cast<size_t>(DisplayFineCropMode::MaxCount));
|
||||
|
||||
std::optional<DisplayFineCropMode> Settings::ParseDisplayFineCropMode(const char* str)
|
||||
{
|
||||
int index = 0;
|
||||
for (const char* name : s_display_fine_crop_mode_names)
|
||||
{
|
||||
if (StringUtil::Strcasecmp(name, str) == 0)
|
||||
return static_cast<DisplayFineCropMode>(index);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* Settings::GetDisplayFineCropModeName(DisplayFineCropMode mode)
|
||||
{
|
||||
return s_display_fine_crop_mode_names[static_cast<size_t>(mode)];
|
||||
}
|
||||
|
||||
const char* Settings::GetDisplayFineCropModeDisplayName(DisplayFineCropMode mode)
|
||||
{
|
||||
return Host::TranslateToCString("Settings", s_display_fine_crop_mode_display_names[static_cast<size_t>(mode)],
|
||||
"DisplayFineCropMode");
|
||||
}
|
||||
|
||||
static constexpr const std::string_view s_auto_aspect_ratio_name =
|
||||
TRANSLATE_DISAMBIG_NOOP("Settings", "Auto (Game Native)", "DisplayAspectRatio");
|
||||
static constexpr const std::string_view s_stretch_aspect_ratio_name =
|
||||
|
||||
@@ -43,6 +43,7 @@ struct GPUSettings
|
||||
DisplayDeinterlacingMode display_deinterlacing_mode = DEFAULT_DISPLAY_DEINTERLACING_MODE;
|
||||
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
|
||||
DisplayCropMode display_crop_mode = DEFAULT_DISPLAY_CROP_MODE;
|
||||
DisplayFineCropMode display_fine_crop_mode = DEFAULT_DISPLAY_FINE_CROP_MODE;
|
||||
DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT;
|
||||
DisplayRotation display_rotation = DEFAULT_DISPLAY_ROTATION;
|
||||
DisplayScalingMode display_scaling = DEFAULT_DISPLAY_SCALING;
|
||||
@@ -119,6 +120,8 @@ struct GPUSettings
|
||||
float gpu_pgxp_tolerance = -1.0f;
|
||||
float gpu_pgxp_depth_clear_threshold = 0.0f;
|
||||
|
||||
std::array<s16, 4> display_fine_crop_amount = {};
|
||||
|
||||
float display_osd_scale = DEFAULT_OSD_SCALE;
|
||||
float display_osd_margin = 0.0f;
|
||||
|
||||
@@ -218,6 +221,7 @@ struct GPUSettings
|
||||
static constexpr DisplayDeinterlacingMode DEFAULT_DISPLAY_DEINTERLACING_MODE = DisplayDeinterlacingMode::Progressive;
|
||||
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
|
||||
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto();
|
||||
static constexpr DisplayFineCropMode DEFAULT_DISPLAY_FINE_CROP_MODE = DisplayFineCropMode::None;
|
||||
static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center;
|
||||
static constexpr DisplayRotation DEFAULT_DISPLAY_ROTATION = DisplayRotation::Normal;
|
||||
static constexpr DisplayScalingMode DEFAULT_DISPLAY_SCALING = DisplayScalingMode::BilinearSmooth;
|
||||
@@ -506,6 +510,10 @@ struct Settings : public GPUSettings
|
||||
static const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
|
||||
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
|
||||
|
||||
static std::optional<DisplayFineCropMode> ParseDisplayFineCropMode(const char* str);
|
||||
static const char* GetDisplayFineCropModeName(DisplayFineCropMode mode);
|
||||
static const char* GetDisplayFineCropModeDisplayName(DisplayFineCropMode mode);
|
||||
|
||||
static std::optional<DisplayAspectRatio> ParseDisplayAspectRatio(std::string_view str);
|
||||
static TinyString GetDisplayAspectRatioName(DisplayAspectRatio ar);
|
||||
static TinyString GetDisplayAspectRatioDisplayName(DisplayAspectRatio ar);
|
||||
|
||||
@@ -4603,6 +4603,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
|
||||
g_settings.display_scaling != old_settings.display_scaling ||
|
||||
g_settings.display_scaling_24bit != old_settings.display_scaling_24bit ||
|
||||
g_settings.display_fine_crop_mode != old_settings.display_fine_crop_mode ||
|
||||
g_settings.display_fine_crop_amount != old_settings.display_fine_crop_amount ||
|
||||
g_settings.display_alignment != old_settings.display_alignment ||
|
||||
g_settings.display_rotation != old_settings.display_rotation ||
|
||||
g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode ||
|
||||
@@ -6070,12 +6072,15 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/)
|
||||
GSVector2 requested_size;
|
||||
if (g_settings.gpu_show_vram)
|
||||
{
|
||||
requested_size = GSVector2::cxpr(static_cast<s32>(VRAM_WIDTH), static_cast<s32>(VRAM_HEIGHT)) * scale;
|
||||
requested_size = GSVector2::cxpr(static_cast<s32>(VRAM_WIDTH), static_cast<s32>(VRAM_HEIGHT));
|
||||
}
|
||||
else
|
||||
{
|
||||
requested_size =
|
||||
g_gpu.ApplyPixelAspectRatioToSize(g_gpu.ComputePixelAspectRatio(), GSVector2(g_gpu.GetCRTCVideoSize()) * scale);
|
||||
const WindowInfo& wi = GPUThread::GetRenderWindowInfo();
|
||||
requested_size = GPU::CalculateDisplayWindowSize(
|
||||
g_settings.display_fine_crop_mode, g_settings.display_fine_crop_amount, g_gpu.ComputePixelAspectRatio(),
|
||||
GSVector2(g_gpu.GetCRTCVideoSize()), GSVector2(g_gpu.GetCRTCVRAMSourceRect().rsize()) * scale,
|
||||
GSVector2(GSVector2i(wi.surface_width, wi.surface_height)));
|
||||
}
|
||||
|
||||
if (g_settings.display_rotation == DisplayRotation::Rotate90 ||
|
||||
@@ -6084,6 +6089,8 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/)
|
||||
requested_size = requested_size.yx();
|
||||
}
|
||||
|
||||
requested_size *= scale;
|
||||
|
||||
const GSVector2i requested_sizei = GSVector2i(requested_size.ceil());
|
||||
Host::RequestResizeHostDisplay(requested_sizei.x, requested_sizei.y);
|
||||
}
|
||||
|
||||
@@ -163,6 +163,15 @@ enum class DisplayCropMode : u8
|
||||
MaxCount
|
||||
};
|
||||
|
||||
enum class DisplayFineCropMode : u8
|
||||
{
|
||||
None,
|
||||
VideoResolution,
|
||||
InternalResolution,
|
||||
WindowResolution,
|
||||
MaxCount
|
||||
};
|
||||
|
||||
enum class DisplayAlignment : u8
|
||||
{
|
||||
LeftOrTop,
|
||||
|
||||
@@ -139,6 +139,14 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||
&Settings::ParseDisplayRotation, &Settings::GetDisplayRotationName,
|
||||
&Settings::GetDisplayRotationDisplayName,
|
||||
Settings::DEFAULT_DISPLAY_ROTATION, DisplayRotation::Count);
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||
sif, m_ui.displayFineCropMode, "Display", "FineCropMode", &Settings::ParseDisplayFineCropMode,
|
||||
&Settings::GetDisplayFineCropModeName, &Settings::GetDisplayFineCropModeDisplayName,
|
||||
Settings::DEFAULT_DISPLAY_FINE_CROP_MODE, DisplayFineCropMode::MaxCount);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropLeft, "Display", "FineCropLeft", 0);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropTop, "Display", "FineCropTop", 0);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropRight, "Display", "FineCropRight", 0);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropBottom, "Display", "FineCropBottom", 0);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableMailboxPresentation, "Display",
|
||||
"DisableMailboxPresentation", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticallyResizeWindow, "Display", "AutoResizeWindow",
|
||||
@@ -159,6 +167,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates",
|
||||
false);
|
||||
|
||||
connect(m_ui.displayFineCropMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&GraphicsSettingsWidget::onFineCropModeChanged);
|
||||
connect(m_ui.displayFineCropReset, &QPushButton::clicked, this, &GraphicsSettingsWidget::onFineCropResetClicked);
|
||||
connect(m_ui.gpuThread, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onGPUThreadChanged);
|
||||
|
||||
SettingWidgetBinder::SetAvailability(m_ui.scaledInterlacing,
|
||||
@@ -377,6 +388,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||
// Init all dependent options.
|
||||
updateRendererDependentOptions();
|
||||
onDownsampleModeChanged();
|
||||
onFineCropModeChanged();
|
||||
updateResolutionDependentOptions();
|
||||
onOSDShowMessagesChanged();
|
||||
onMediaCaptureBackendChanged();
|
||||
@@ -478,6 +490,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||
m_ui.displayAlignment, tr("Position"),
|
||||
QString::fromUtf8(Settings::GetDisplayAlignmentDisplayName(Settings::DEFAULT_DISPLAY_ALIGNMENT)),
|
||||
tr("Determines the position on the screen when black borders must be added."));
|
||||
dialog->registerWidgetHelp(m_ui.displayFineCropMode, tr("Fine Crop Mode"), tr("None"),
|
||||
tr("Enables manual fine cropping of the display area, while preserving the aspect ratio "
|
||||
"of the image. Useful for removing black borders in certain games."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.disableMailboxPresentation, tr("Disable Mailbox Presentation"), tr("Unchecked"),
|
||||
tr("Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. "
|
||||
@@ -1162,6 +1177,54 @@ void GraphicsSettingsWidget::onDownsampleModeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSettingsWidget::onFineCropModeChanged()
|
||||
{
|
||||
const DisplayFineCropMode mode =
|
||||
Settings::ParseDisplayFineCropMode(m_dialog->getEffectiveStringValue("Display", "FineCropMode", "").c_str())
|
||||
.value_or(Settings::DEFAULT_DISPLAY_FINE_CROP_MODE);
|
||||
const bool enabled = (mode != DisplayFineCropMode::None);
|
||||
m_ui.displayFineCropLabel->setEnabled(enabled);
|
||||
m_ui.displayFineCropLeftLabel->setEnabled(enabled);
|
||||
m_ui.displayFineCropLeft->setEnabled(enabled);
|
||||
m_ui.displayFineCropTopLabel->setEnabled(enabled);
|
||||
m_ui.displayFineCropTop->setEnabled(enabled);
|
||||
m_ui.displayFineCropRightLabel->setEnabled(enabled);
|
||||
m_ui.displayFineCropRight->setEnabled(enabled);
|
||||
m_ui.displayFineCropBottomLabel->setEnabled(enabled);
|
||||
m_ui.displayFineCropBottom->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void GraphicsSettingsWidget::onFineCropResetClicked()
|
||||
{
|
||||
if (m_dialog->isPerGameSettings())
|
||||
{
|
||||
// Super nasty.. need to set the default without the signal firing and writing the global value to the ini...
|
||||
const auto reset = [this]<typename T>(T* widget, const char* key) {
|
||||
const QSignalBlocker sb(widget);
|
||||
if constexpr (std::is_same_v<T, QComboBox>)
|
||||
SettingWidgetBinder::SettingAccessor<T>::setNullableStringValue(widget, std::nullopt);
|
||||
else
|
||||
SettingWidgetBinder::SettingAccessor<QSpinBox>::setNullableIntValue(widget, std::nullopt);
|
||||
|
||||
m_dialog->removeSettingValue("Display", key);
|
||||
};
|
||||
|
||||
reset(m_ui.displayFineCropMode, "FineCropMode");
|
||||
reset(m_ui.displayFineCropLeft, "FineCropLeft");
|
||||
reset(m_ui.displayFineCropTop, "FineCropTop");
|
||||
reset(m_ui.displayFineCropRight, "FineCropRight");
|
||||
reset(m_ui.displayFineCropBottom, "FineCropBottom");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.displayFineCropMode->setCurrentIndex(0);
|
||||
m_ui.displayFineCropLeft->setValue(0);
|
||||
m_ui.displayFineCropTop->setValue(0);
|
||||
m_ui.displayFineCropRight->setValue(0);
|
||||
m_ui.displayFineCropBottom->setValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSettingsWidget::onOSDShowMessagesChanged()
|
||||
{
|
||||
const bool enabled = m_dialog->getEffectiveBoolValue("Display", "ShowOSDMessages", true);
|
||||
|
||||
@@ -48,6 +48,8 @@ private:
|
||||
|
||||
void updateResolutionDependentOptions();
|
||||
void onDownsampleModeChanged();
|
||||
void onFineCropModeChanged();
|
||||
void onFineCropResetClicked();
|
||||
|
||||
void onOSDShowMessagesChanged();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>655</width>
|
||||
<width>734</width>
|
||||
<height>511</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -351,7 +351,128 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="displayFineCropModeLabel">
|
||||
<property name="text">
|
||||
<string>Fine Crop Mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0">
|
||||
<item>
|
||||
<widget class="QComboBox" name="displayFineCropMode"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="displayFineCropReset">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reset Fine Cropping</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="delete-back-2-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="displayFineCropLabel">
|
||||
<property name="text">
|
||||
<string>Fine Crop Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0,1,0,1,0,1">
|
||||
<item>
|
||||
<widget class="QLabel" name="displayFineCropLeftLabel">
|
||||
<property name="text">
|
||||
<string>Left:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="displayFineCropLeft">
|
||||
<property name="suffix">
|
||||
<string>px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-32768</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32767</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="displayFineCropTopLabel">
|
||||
<property name="text">
|
||||
<string>Top:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="displayFineCropTop">
|
||||
<property name="suffix">
|
||||
<string>px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-32768</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32767</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="displayFineCropRightLabel">
|
||||
<property name="text">
|
||||
<string>Right:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="displayFineCropRight">
|
||||
<property name="suffix">
|
||||
<string>px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-32768</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32767</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="displayFineCropBottomLabel">
|
||||
<property name="text">
|
||||
<string>Bottom:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="displayFineCropBottom">
|
||||
<property name="suffix">
|
||||
<string>px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-32768</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32767</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="advancedDisplayOptionsLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="automaticallyResizeWindow">
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<file>icons/black/svg/debugger-go-to-address.svg</file>
|
||||
<file>icons/black/svg/debugger-go-to-cursor.svg</file>
|
||||
<file>icons/black/svg/debugger-go-to-pc.svg</file>
|
||||
<file>icons/black/svg/delete-back-2-line.svg</file>
|
||||
<file>icons/black/svg/disc-eject-line.svg</file>
|
||||
<file>icons/black/svg/disc-line.svg</file>
|
||||
<file>icons/black/svg/door-open-line.svg</file>
|
||||
@@ -200,6 +201,7 @@
|
||||
<file>icons/white/svg/debugger-go-to-address.svg</file>
|
||||
<file>icons/white/svg/debugger-go-to-cursor.svg</file>
|
||||
<file>icons/white/svg/debugger-go-to-pc.svg</file>
|
||||
<file>icons/white/svg/delete-back-2-line.svg</file>
|
||||
<file>icons/white/svg/disc-eject-line.svg</file>
|
||||
<file>icons/white/svg/disc-line.svg</file>
|
||||
<file>icons/white/svg/door-open-line.svg</file>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.53451 3H20.9993C21.5516 3 21.9993 3.44772 21.9993 4V20C21.9993 20.5523 21.5516 21 20.9993 21H6.53451C6.20015 21 5.88792 20.8329 5.70246 20.5547L0.369122 12.5547C0.145189 12.2188 0.145189 11.7812 0.369122 11.4453L5.70246 3.4453C5.88792 3.1671 6.20015 3 6.53451 3ZM7.06969 5L2.40302 12L7.06969 19H19.9993V5H7.06969ZM12.9993 10.5858L15.8277 7.75736L17.242 9.17157L14.4135 12L17.242 14.8284L15.8277 16.2426L12.9993 13.4142L10.1709 16.2426L8.75668 14.8284L11.5851 12L8.75668 9.17157L10.1709 7.75736L12.9993 10.5858Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 597 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M6.53451 3H20.9993C21.5516 3 21.9993 3.44772 21.9993 4V20C21.9993 20.5523 21.5516 21 20.9993 21H6.53451C6.20015 21 5.88792 20.8329 5.70246 20.5547L0.369122 12.5547C0.145189 12.2188 0.145189 11.7812 0.369122 11.4453L5.70246 3.4453C5.88792 3.1671 6.20015 3 6.53451 3ZM7.06969 5L2.40302 12L7.06969 19H19.9993V5H7.06969ZM12.9993 10.5858L15.8277 7.75736L17.242 9.17157L14.4135 12L17.242 14.8284L15.8277 16.2426L12.9993 13.4142L10.1709 16.2426L8.75668 14.8284L11.5851 12L8.75668 9.17157L10.1709 7.75736L12.9993 10.5858Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 612 B |
Reference in New Issue
Block a user