From fcc0ae9571d526c7359d6a30dd698f16793d476f Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 28 Feb 2020 17:01:01 +1000 Subject: [PATCH] GPU: Implement "Crop Mode" (none, overscan, all borders) --- src/core/gpu.cpp | 151 ++++++++++++-------- src/core/gpu.h | 31 ++-- src/core/gpu_hw_d3d11.cpp | 39 ++--- src/core/gpu_hw_opengl.cpp | 48 ++++--- src/core/gpu_hw_opengl_es.cpp | 48 ++++--- src/core/gpu_sw.cpp | 37 ++--- src/core/host_display.cpp | 31 +++- src/core/host_display.h | 59 +++++--- src/core/host_interface.cpp | 9 +- src/core/save_state_version.h | 2 +- src/core/settings.cpp | 34 ++++- src/core/settings.h | 7 +- src/core/types.h | 8 ++ src/duckstation-qt/d3d11displaywindow.cpp | 60 ++++---- src/duckstation-qt/d3d11displaywindow.h | 6 +- src/duckstation-qt/gpusettingswidget.cpp | 15 +- src/duckstation-qt/gpusettingswidget.ui | 49 +++++-- src/duckstation-qt/opengldisplaywindow.cpp | 25 ++-- src/duckstation-qt/opengldisplaywindow.h | 4 +- src/duckstation-qt/qtdisplaywindow.cpp | 16 +-- src/duckstation-qt/qtdisplaywindow.h | 9 +- src/duckstation-qt/qthostinterface.cpp | 2 +- src/duckstation-sdl/d3d11_host_display.cpp | 66 ++++----- src/duckstation-sdl/d3d11_host_display.h | 6 +- src/duckstation-sdl/main.cpp | 2 +- src/duckstation-sdl/opengl_host_display.cpp | 34 ++--- src/duckstation-sdl/opengl_host_display.h | 6 +- src/duckstation-sdl/sdl_host_interface.cpp | 24 +++- 28 files changed, 491 insertions(+), 337 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 7a1469dda..1c3d04d3b 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -3,6 +3,7 @@ #include "common/log.h" #include "common/state_wrapper.h" #include "dma.h" +#include "host_display.h" #include "host_interface.h" #include "interrupt_controller.h" #include "stb_image_write.h" @@ -26,7 +27,7 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr m_dma = dma; m_interrupt_controller = interrupt_controller; m_timers = timers; - m_force_progressive_scan = m_system->GetSettings().gpu_force_progressive_scan; + m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan; m_tick_event = m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true); return true; @@ -34,7 +35,8 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr void GPU::UpdateSettings() { - m_force_progressive_scan = m_system->GetSettings().gpu_force_progressive_scan; + m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan; + UpdateCRTCConfig(); } void GPU::Reset() @@ -108,8 +110,12 @@ bool GPU::DoState(StateWrapper& sw) sw.Do(&m_crtc_state.regs.horizontal_display_range); sw.Do(&m_crtc_state.regs.vertical_display_range); sw.Do(&m_crtc_state.dot_clock_divider); - sw.Do(&m_crtc_state.display_width); - sw.Do(&m_crtc_state.display_height); + sw.Do(&m_crtc_state.visible_display_width); + sw.Do(&m_crtc_state.visible_display_height); + sw.Do(&m_crtc_state.active_display_left); + sw.Do(&m_crtc_state.active_display_top); + sw.Do(&m_crtc_state.active_display_width); + sw.Do(&m_crtc_state.active_display_height); sw.Do(&m_crtc_state.horizontal_total); sw.Do(&m_crtc_state.horizontal_display_start); sw.Do(&m_crtc_state.horizontal_display_end); @@ -161,6 +167,7 @@ bool GPU::DoState(StateWrapper& sw) // Restore mask setting. m_GPUSTAT.bits = old_GPUSTAT; + UpdateCRTCConfig(); UpdateDisplay(); UpdateSliceTicks(); } @@ -285,8 +292,9 @@ void GPU::Synchronize() void GPU::UpdateCRTCConfig() { - static constexpr std::array dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}}; + static constexpr std::array dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}}; CRTCState& cs = m_crtc_state; + const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode; if (m_GPUSTAT.pal_mode) { @@ -303,6 +311,7 @@ void GPU::UpdateCRTCConfig() const float vertical_frequency = static_cast(static_cast((u64(MASTER_CLOCK) * 11) / 7) / static_cast(ticks_per_frame)); m_system->SetThrottleFrequency(vertical_frequency); + m_tick_event->SetInterval(cs.horizontal_total); 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]; @@ -311,63 +320,92 @@ void GPU::UpdateCRTCConfig() cs.vertical_display_start = static_cast(std::min(cs.regs.Y1, cs.vertical_total)); cs.vertical_display_end = static_cast(std::min(cs.regs.Y2, cs.vertical_total)); - // check for a change in resolution - const u32 old_horizontal_resolution = cs.display_width; - const u32 old_vertical_resolution = cs.display_height; - const u32 visible_lines = cs.regs.Y2 - cs.regs.Y1; - cs.display_width = std::max((cs.regs.X2 - cs.regs.X1) / cs.dot_clock_divider, 1); - cs.display_height = visible_lines << BoolToUInt8(m_GPUSTAT.In480iMode()); + // determine the active display size + cs.active_display_width = std::clamp((cs.regs.X2 - cs.regs.X1) / cs.dot_clock_divider, 1, VRAM_WIDTH); + cs.active_display_height = + std::clamp((cs.regs.Y2 - cs.regs.Y1), 1, VRAM_HEIGHT >> BoolToUInt8(m_GPUSTAT.In480iMode())); - if (cs.display_width != old_horizontal_resolution || cs.display_height != old_vertical_resolution) - Log_InfoPrintf("Visible resolution is now %ux%u", cs.display_width, cs.display_height); + // Construct screen borders from configured active area and the standard visible range. + // TODO: Ensure it doesn't overflow + const u16 horizontal_start_display_tick = (crop_mode == DisplayCropMode::None ? 488 : 608); + const u16 horizontal_end_display_tick = (crop_mode == DisplayCropMode::None ? 2800 : 2560); + cs.visible_display_width = horizontal_end_display_tick / cs.dot_clock_divider; + cs.active_display_left = + (std::max(m_crtc_state.regs.X1, horizontal_start_display_tick) - horizontal_start_display_tick) / + cs.dot_clock_divider; - // Compute the aspect ratio necessary to display borders in the inactive region of the picture. - // Convert total dots/lines to time. - const float dot_clock = - (static_cast(MASTER_CLOCK) * (11.0f / 7.0f / static_cast(cs.dot_clock_divider))); - const float dot_clock_period = 1.0f / dot_clock; - const float dots_per_scanline = static_cast(cs.horizontal_total) / static_cast(cs.dot_clock_divider); - const float horizontal_period = dots_per_scanline * dot_clock_period; - const float vertical_period = horizontal_period * static_cast(cs.vertical_total); + const u16 vertical_start_display_line = (crop_mode == DisplayCropMode::None ? 8 : (m_GPUSTAT.pal_mode ? 20 : 16)); + const u16 vertical_end_display_line = + (crop_mode == DisplayCropMode::None ? static_cast(cs.vertical_total) : + static_cast(m_GPUSTAT.pal_mode ? 308 : 256)); + const u16 bottom_padding = vertical_end_display_line - std::min(m_crtc_state.regs.Y2, vertical_end_display_line); + cs.active_display_top = + std::max(m_crtc_state.regs.Y1, vertical_start_display_line) - vertical_start_display_line; + cs.visible_display_height = cs.active_display_top + cs.active_display_height + bottom_padding; - // Convert active dots/lines to time. - const float visible_dots_per_scanline = static_cast(cs.display_width); - const float horizontal_active_time = horizontal_period * visible_dots_per_scanline; - const float vertical_active_time = horizontal_active_time * static_cast(visible_lines); + // Aspect ratio is always 4:3. + cs.display_aspect_ratio = 4.0f / 3.0f; - // Use the reference active time/lines for the signal to work out the border area, and thus aspect ratio - // transformation for the active area in our framebuffer. For the purposes of these calculations, we're assuming - // progressive scan. - float display_ratio; - if (m_GPUSTAT.pal_mode) + if (crop_mode == DisplayCropMode::Borders) { - // Wikipedia says PAL is active 51.95us of 64.00us, and 576/625 lines. - const float signal_horizontal_active_time = 51.95f; - const float signal_horizontal_total_time = 64.0f; - const float signal_vertical_active_lines = 576.0f; - const float signal_vertical_total_lines = 625.0f; - const float h_ratio = - (horizontal_active_time / horizontal_period) * (signal_horizontal_total_time / signal_horizontal_active_time); - const float v_ratio = - (vertical_active_time / vertical_period) * (signal_vertical_total_lines / signal_vertical_active_lines); - display_ratio = h_ratio / v_ratio; - } - else - { - const float signal_horizontal_active_time = 52.66f; - const float signal_horizontal_total_time = 63.56f; - const float signal_vertical_active_lines = 486.0f; - const float signal_vertical_total_lines = 525.0f; - const float h_ratio = - (horizontal_active_time / horizontal_period) * (signal_horizontal_total_time / signal_horizontal_active_time); - const float v_ratio = - (vertical_active_time / vertical_period) * (signal_vertical_total_lines / signal_vertical_active_lines); - display_ratio = h_ratio / v_ratio; + // Compute the aspect ratio necessary to display borders in the inactive region of the picture. + // Convert total dots/lines to time. + const float dot_clock = + (static_cast(MASTER_CLOCK) * (11.0f / 7.0f / static_cast(cs.dot_clock_divider))); + const float dot_clock_period = 1.0f / dot_clock; + const float dots_per_scanline = static_cast(cs.horizontal_total) / static_cast(cs.dot_clock_divider); + const float horizontal_period = dots_per_scanline * dot_clock_period; + const float vertical_period = horizontal_period * static_cast(cs.vertical_total); + + // Convert active dots/lines to time. + const float visible_dots_per_scanline = static_cast(cs.active_display_width); + const float horizontal_active_time = horizontal_period * visible_dots_per_scanline; + const float vertical_active_time = horizontal_active_time * static_cast(cs.regs.Y2 - cs.regs.Y1); + + // Use the reference active time/lines for the signal to work out the border area, and thus aspect ratio + // transformation for the active area in our framebuffer. For the purposes of these calculations, we're assuming + // progressive scan. + float display_ratio; + if (m_GPUSTAT.pal_mode) + { + // Wikipedia says PAL is active 51.95us of 64.00us, and 576/625 lines. + const float signal_horizontal_active_time = 51.95f; + const float signal_horizontal_total_time = 64.0f; + const float signal_vertical_active_lines = 576.0f; + const float signal_vertical_total_lines = 625.0f; + const float h_ratio = + (horizontal_active_time / horizontal_period) * (signal_horizontal_total_time / signal_horizontal_active_time); + const float v_ratio = + (vertical_active_time / vertical_period) * (signal_vertical_total_lines / signal_vertical_active_lines); + display_ratio = h_ratio / v_ratio; + } + else + { + const float signal_horizontal_active_time = 52.66f; + const float signal_horizontal_total_time = 63.56f; + const float signal_vertical_active_lines = 486.0f; + const float signal_vertical_total_lines = 525.0f; + const float h_ratio = + (horizontal_active_time / horizontal_period) * (signal_horizontal_total_time / signal_horizontal_active_time); + const float v_ratio = + (vertical_active_time / vertical_period) * (signal_vertical_total_lines / signal_vertical_active_lines); + display_ratio = h_ratio / v_ratio; + } + + // Ensure the numbers are sane, and not due to a misconfigured active display range. + cs.display_aspect_ratio = (std::isnormal(display_ratio) && display_ratio != 0.0f) ? display_ratio : (4.0f / 3.0f); + cs.visible_display_width = cs.active_display_width; + cs.visible_display_height = cs.active_display_height; + cs.active_display_left = 0; + cs.active_display_top = 0; } - // Ensure the numbers are sane, and not due to a misconfigured active display range. - cs.display_aspect_ratio = (std::isnormal(display_ratio) && display_ratio != 0.0f) ? display_ratio : (4.0f / 3.0f); - m_tick_event->SetInterval(cs.horizontal_total); + Log_InfoPrintf("Screen resolution: %ux%u", cs.visible_display_width, cs.visible_display_height); + Log_InfoPrintf("Active display: %ux%u @ %u,%u", cs.active_display_width, cs.active_display_height, + cs.active_display_left, cs.active_display_top); + Log_InfoPrintf("Padding: Left=%u, Top=%u, Right=%u, Bottom=%u", cs.active_display_left, cs.active_display_top, + cs.visible_display_width - cs.active_display_width - cs.active_display_left, + cs.visible_display_height - cs.active_display_height - cs.active_display_top); } static TickCount GPUTicksToSystemTicks(u32 gpu_ticks) @@ -581,6 +619,7 @@ void GPU::WriteGP1(u32 value) const bool disable = ConvertToBoolUnchecked(value & 0x01); Log_DebugPrintf("Display %s", disable ? "disabled" : "enabled"); m_GPUSTAT.display_disable = disable; + UpdateCRTCConfig(); } break; @@ -940,7 +979,7 @@ void GPU::DrawDebugStateWindow() m_GPUSTAT.interlaced_field ? "odd" : "even"); ImGui::Text("Display Disable: %s", m_GPUSTAT.display_disable ? "Yes" : "No"); ImGui::Text("Drawing Even Line: %s", m_GPUSTAT.drawing_even_line ? "Yes" : "No"); - ImGui::Text("Display Resolution: %ux%u", cs.display_width, cs.display_height); + ImGui::Text("Display Resolution: %ux%u", cs.active_display_width, cs.active_display_height); ImGui::Text("Color Depth: %u-bit", m_GPUSTAT.display_area_color_depth_24 ? 24 : 15); ImGui::Text("Start Offset: (%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue()); ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total, diff --git a/src/core/gpu.h b/src/core/gpu.h index e13c1290e..d0d23e887 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -496,28 +496,32 @@ protected: union { u32 display_address_start; - BitField X; - BitField Y; + BitField X; + BitField Y; }; union { u32 horizontal_display_range; - BitField X1; - BitField X2; + BitField X1; + BitField X2; }; union { u32 vertical_display_range; - BitField Y1; - BitField Y2; + BitField Y1; + BitField Y2; }; } regs; - TickCount dot_clock_divider; + u16 dot_clock_divider; - u32 display_width; - u32 display_height; + u16 visible_display_width; + u16 visible_display_height; + u16 active_display_left; + u16 active_display_top; + u16 active_display_width; + u16 active_display_height; TickCount horizontal_total; TickCount horizontal_display_start; @@ -533,6 +537,15 @@ protected: float display_aspect_ratio; bool in_hblank; bool in_vblank; + + /// Returns a rectangle representing the active display region within the visible area of the screen, i.e. where the + /// VRAM texture should be "scanned out" to. Areas outside this region (the border) should be displayed as black. + Common::Rectangle GetActiveDisplayRectangle() const + { + return Common::Rectangle::FromExtents( + static_cast(ZeroExtend32(active_display_left)), static_cast(ZeroExtend32(active_display_top)), + static_cast(ZeroExtend32(active_display_width)), static_cast(ZeroExtend32(active_display_height))); + } } m_crtc_state = {}; State m_state = State::Idle; diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index 91d82c6f5..3ba88bfbf 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -14,7 +14,7 @@ GPU_HW_D3D11::~GPU_HW_D3D11() { if (m_host_display) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); + m_host_display->ClearDisplayTexture(); ResetGraphicsAPIState(); } } @@ -521,9 +521,11 @@ void GPU_HW_D3D11::UpdateDisplay() if (m_system->GetSettings().debugging.show_vram) { - m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), 0, 0, m_vram_texture.GetWidth(), - m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), - 1.0f); + m_host_display->SetDisplayTexture( + m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), + Common::Rectangle(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight())); + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, + Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), 1.0f); } else { @@ -531,21 +533,23 @@ void GPU_HW_D3D11::UpdateDisplay() const u32 vram_offset_y = m_crtc_state.regs.Y; const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; - const u32 display_width = std::min(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); + const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); + const u32 display_height = std::min(m_crtc_state.active_display_height << BoolToUInt8(m_GPUSTAT.In480iMode()), + VRAM_HEIGHT - vram_offset_y); const u32 scaled_display_width = display_width * m_resolution_scale; const u32 scaled_display_height = display_height * m_resolution_scale; const bool interlaced = IsDisplayInterlaced(); if (m_GPUSTAT.display_disable) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio); + m_host_display->ClearDisplayTexture(); } else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced) { - m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), scaled_vram_offset_x, scaled_vram_offset_y, - scaled_display_width, scaled_display_height, m_vram_texture.GetWidth(), - m_vram_texture.GetHeight(), m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture( + m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), + Common::Rectangle::FromExtents(scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width, + scaled_display_height)); } else { @@ -570,9 +574,9 @@ void GPU_HW_D3D11::UpdateDisplay() SetViewportAndScissor(0, field_offset, display_width, display_height); DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); - m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, display_width, display_height, - m_display_texture.GetWidth(), m_display_texture.GetHeight(), - m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), m_display_texture.GetWidth(), + m_display_texture.GetHeight(), + Common::Rectangle(0, 0, display_width, display_height)); } else { @@ -583,13 +587,16 @@ void GPU_HW_D3D11::UpdateDisplay() SetViewportAndScissor(0, field_offset, scaled_display_width, scaled_display_height); DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); - m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, scaled_display_width, - scaled_display_height, m_display_texture.GetWidth(), - m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), m_display_texture.GetWidth(), + m_display_texture.GetHeight(), + Common::Rectangle(0, 0, scaled_display_width, scaled_display_height)); } RestoreGraphicsAPIState(); } + + m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, + m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); } } diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index 2728cd969..72cf9a5df 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -20,7 +20,7 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL() if (m_host_display) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); + m_host_display->ClearDisplayTexture(); ResetGraphicsAPIState(); } } @@ -70,9 +70,6 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* d return false; } - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), 0, 0, - m_display_texture.GetWidth(), m_display_texture.GetHeight(), - m_display_texture.GetWidth(), m_display_texture.GetHeight(), 1.0f); RestoreGraphicsAPIState(); return true; } @@ -479,10 +476,12 @@ void GPU_HW_OpenGL::UpdateDisplay() if (m_system->GetSettings().debugging.show_vram) { - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), 0, - m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), - -static_cast(m_vram_texture.GetHeight()), m_vram_texture.GetWidth(), - m_vram_texture.GetHeight(), 1.0f); + m_host_display->SetDisplayTexture( + reinterpret_cast(static_cast(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(), + -static_cast(m_vram_texture.GetHeight()), + Common::Rectangle(0, m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), 0)); + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, + Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), 1.0f); } else { @@ -490,23 +489,23 @@ void GPU_HW_OpenGL::UpdateDisplay() const u32 vram_offset_y = m_crtc_state.regs.Y; const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; - const u32 display_width = std::min(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); + const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); + const u32 display_height = std::min(m_crtc_state.active_display_height, VRAM_HEIGHT - vram_offset_y); const u32 scaled_display_width = display_width * m_resolution_scale; const u32 scaled_display_height = display_height * m_resolution_scale; const bool interlaced = IsDisplayInterlaced(); if (m_GPUSTAT.display_disable) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio); + m_host_display->ClearDisplayTexture(); } else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced) { - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), - scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, - scaled_display_width, -static_cast(scaled_display_height), - m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), - m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture( + reinterpret_cast(static_cast(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(), + m_vram_texture.GetHeight(), + Common::Rectangle(scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, + scaled_display_width, -static_cast(scaled_display_height))); } else { @@ -545,10 +544,10 @@ void GPU_HW_OpenGL::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_display_texture.GetGLId())), - 0, display_height, display_width, -static_cast(display_height), - m_display_texture.GetWidth(), m_display_texture.GetHeight(), - m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture( + reinterpret_cast(static_cast(m_display_texture.GetGLId())), m_display_texture.GetWidth(), + m_display_texture.GetHeight(), + Common::Rectangle(0, display_height, display_width, -static_cast(display_height))); } else { @@ -564,9 +563,9 @@ void GPU_HW_OpenGL::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_display_texture.GetGLId())), - 0, scaled_display_height, scaled_display_width, - -static_cast(scaled_display_height), m_display_texture.GetWidth(), - m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); + m_display_texture.GetWidth(), m_display_texture.GetHeight(), + Common::Rectangle(0, scaled_display_height, scaled_display_width, + -static_cast(scaled_display_height))); } // restore state @@ -574,6 +573,9 @@ void GPU_HW_OpenGL::UpdateDisplay() glViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); glEnable(GL_SCISSOR_TEST); } + + m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, + m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); } } diff --git a/src/core/gpu_hw_opengl_es.cpp b/src/core/gpu_hw_opengl_es.cpp index d9160036e..72146bc93 100644 --- a/src/core/gpu_hw_opengl_es.cpp +++ b/src/core/gpu_hw_opengl_es.cpp @@ -13,7 +13,7 @@ GPU_HW_OpenGL_ES::~GPU_HW_OpenGL_ES() // TODO: Destroy objects... if (m_host_display) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); + m_host_display->ClearDisplayTexture(); ResetGraphicsAPIState(); } } @@ -44,9 +44,6 @@ bool GPU_HW_OpenGL_ES::Initialize(HostDisplay* host_display, System* system, DMA return false; } - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), 0, 0, - m_display_texture.GetWidth(), m_display_texture.GetHeight(), - m_display_texture.GetWidth(), m_display_texture.GetHeight(), 1.0f); RestoreGraphicsAPIState(); return true; } @@ -346,10 +343,12 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() if (m_system->GetSettings().debugging.show_vram) { - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), 0, - m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), - -static_cast(m_vram_texture.GetHeight()), m_vram_texture.GetWidth(), - m_vram_texture.GetHeight(), 1.0f); + m_host_display->SetDisplayTexture( + reinterpret_cast(static_cast(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(), + -static_cast(m_vram_texture.GetHeight()), + Common::Rectangle(0, m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), 0)); + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), + 1.0f); } else { @@ -357,23 +356,23 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() const u32 vram_offset_y = m_crtc_state.regs.Y; const u32 scaled_vram_offset_x = vram_offset_x * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; - const u32 display_width = std::min(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); - const u32 display_height = std::min(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); + const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); + const u32 display_height = std::min(m_crtc_state.active_display_height, VRAM_HEIGHT - vram_offset_y); const u32 scaled_display_width = display_width * m_resolution_scale; const u32 scaled_display_height = display_height * m_resolution_scale; const bool interlaced = IsDisplayInterlaced(); if (m_GPUSTAT.display_disable) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, m_crtc_state.display_aspect_ratio); + m_host_display->ClearDisplayTexture(); } else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced) { - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_vram_texture.GetGLId())), - scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, - scaled_display_width, -static_cast(scaled_display_height), - m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), - m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture( + reinterpret_cast(static_cast(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(), + m_vram_texture.GetHeight(), + Common::Rectangle(scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, + scaled_display_width, -static_cast(scaled_display_height))); } else { @@ -412,10 +411,10 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); - m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_display_texture.GetGLId())), - 0, display_height, display_width, -static_cast(display_height), - m_display_texture.GetWidth(), m_display_texture.GetHeight(), - m_crtc_state.display_aspect_ratio); + m_host_display->SetDisplayTexture( + reinterpret_cast(static_cast(m_display_texture.GetGLId())), m_display_texture.GetWidth(), + m_display_texture.GetHeight(), + Common::Rectangle(0, display_height, display_width, -static_cast(display_height))); } else { @@ -431,9 +430,9 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() glDrawArrays(GL_TRIANGLES, 0, 3); m_host_display->SetDisplayTexture(reinterpret_cast(static_cast(m_display_texture.GetGLId())), - 0, scaled_display_height, scaled_display_width, - -static_cast(scaled_display_height), m_display_texture.GetWidth(), - m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); + m_display_texture.GetWidth(), m_display_texture.GetHeight(), + Common::Rectangle(0, scaled_display_height, scaled_display_width, + -static_cast(scaled_display_height))); } // restore state @@ -441,6 +440,9 @@ void GPU_HW_OpenGL_ES::UpdateDisplay() glViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); glEnable(GL_SCISSOR_TEST); } + + m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, + m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); } } diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index e670e63f4..90c08419c 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -12,7 +12,8 @@ GPU_SW::GPU_SW() GPU_SW::~GPU_SW() { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); + if (m_host_display) + m_host_display->ClearDisplayTexture(); } bool GPU_SW::IsHardwareRenderer() const @@ -113,21 +114,17 @@ void GPU_SW::UpdateDisplay() // fill display texture m_display_texture_buffer.resize(VRAM_WIDTH * VRAM_HEIGHT); - u32 display_width; - u32 display_height; - float display_aspect_ratio; if (!m_system->GetSettings().debugging.show_vram) { // TODO: Handle interlacing const u32 vram_offset_x = m_crtc_state.regs.X; const u32 vram_offset_y = m_crtc_state.regs.Y; - display_width = std::min(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); - display_height = std::min(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); - display_aspect_ratio = m_crtc_state.display_aspect_ratio; + const u32 display_width = std::min(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x); + const u32 display_height = std::min(m_crtc_state.active_display_height, VRAM_HEIGHT - vram_offset_y); if (m_GPUSTAT.display_disable) { - m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, display_aspect_ratio); + m_host_display->ClearDisplayTexture(); return; } else if (m_GPUSTAT.display_area_color_depth_24) @@ -140,20 +137,24 @@ void GPU_SW::UpdateDisplay() CopyOut15Bit(m_vram.data() + vram_offset_y * VRAM_WIDTH + vram_offset_x, VRAM_WIDTH, m_display_texture_buffer.data(), display_width, display_width, display_height); } + + m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, display_width, display_height, + m_display_texture_buffer.data(), display_width * sizeof(u32)); + m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT, + Common::Rectangle(0, 0, display_width, display_height)); + m_host_display->SetDisplayParameters(m_crtc_state.visible_display_width, m_crtc_state.visible_display_height, + m_crtc_state.GetActiveDisplayRectangle(), m_crtc_state.display_aspect_ratio); } else { - display_width = VRAM_WIDTH; - display_height = VRAM_HEIGHT; - display_aspect_ratio = 1.0f; - CopyOut15Bit(m_vram.data(), VRAM_WIDTH, m_display_texture_buffer.data(), display_width, display_width, - display_height); + CopyOut15Bit(m_vram.data(), VRAM_WIDTH, m_display_texture_buffer.data(), VRAM_WIDTH, VRAM_HEIGHT, VRAM_HEIGHT); + m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT, + m_display_texture_buffer.data(), VRAM_WIDTH * sizeof(u32)); + m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT, + Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT)); + m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, + Common::Rectangle(0, 0, VRAM_WIDTH, VRAM_HEIGHT), 1.0f); } - - m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, display_width, display_height, - m_display_texture_buffer.data(), display_width * sizeof(u32)); - m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), 0, 0, display_width, display_height, VRAM_WIDTH, - VRAM_HEIGHT, display_aspect_ratio); } void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr) diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index bbfc2edd0..6345dabdb 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -4,24 +4,45 @@ HostDisplayTexture::~HostDisplayTexture() = default; HostDisplay::~HostDisplay() = default; -std::tuple HostDisplay::CalculateDrawRect(int window_width, int window_height, float display_ratio) +void HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) { - const float window_ratio = float(window_width) / float(window_height); + m_window_width = new_window_width; + m_window_height = new_window_height; +} + +std::tuple HostDisplay::CalculateDrawRect() const +{ + const s32 window_width = m_window_width; + const s32 window_height = m_window_height - m_display_top_margin; + const float window_ratio = static_cast(window_width) / static_cast(window_height); + + float scale; int left, top, width, height; - if (window_ratio >= display_ratio) + if (window_ratio >= m_display_aspect_ratio) { - width = static_cast(float(window_height) * display_ratio); + width = static_cast(static_cast(window_height) * m_display_aspect_ratio); height = static_cast(window_height); + scale = static_cast(window_height) / static_cast(m_display_height); left = (window_width - width) / 2; top = 0; } else { width = static_cast(window_width); - height = static_cast(float(window_width) / display_ratio); + height = static_cast(float(window_width) / m_display_aspect_ratio); + scale = static_cast(window_width) / static_cast(m_display_width); left = 0; top = (window_height - height) / 2; } + // add in padding + left += static_cast(static_cast(m_display_area.left) * scale); + top += static_cast(static_cast(m_display_area.top) * scale); + width -= static_cast(static_cast(m_display_area.left + (m_display_width - m_display_area.right)) * scale); + height -= + static_cast(static_cast(m_display_area.top + (m_display_height - m_display_area.bottom)) * scale); + + // add in margin + top += m_display_top_margin; return std::tie(left, top, width, height); } diff --git a/src/core/host_display.h b/src/core/host_display.h index 50bb1f366..9a3b27b97 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -1,4 +1,5 @@ #pragma once +#include "common/rectangle.h" #include "types.h" #include #include @@ -36,6 +37,9 @@ public: /// Switches the render window, recreating the surface. virtual void ChangeRenderWindow(void* new_window) = 0; + /// Call when the window size changes externally to recreate any resources. + virtual void WindowResized(s32 new_window_width, s32 new_window_height); + /// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below. virtual std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) = 0; @@ -46,42 +50,59 @@ public: virtual void SetVSync(bool enabled) = 0; - virtual std::tuple GetWindowSize() const = 0; - virtual void WindowResized() = 0; - const s32 GetDisplayTopMargin() const { return m_display_top_margin; } - void SetDisplayTexture(void* texture_handle, s32 offset_x, s32 offset_y, s32 width, s32 height, u32 texture_width, - u32 texture_height, float aspect_ratio) + void ClearDisplayTexture() + { + m_display_texture_handle = nullptr; + m_display_texture_width = 0; + m_display_texture_height = 0; + m_display_texture_rect = {}; + m_display_changed = true; + } + + void SetDisplayTexture(void* texture_handle, s32 texture_width, s32 texture_height, + const Common::Rectangle& texture_rect) { m_display_texture_handle = texture_handle; - m_display_offset_x = offset_x; - m_display_offset_y = offset_y; - m_display_width = width; - m_display_height = height; m_display_texture_width = texture_width; m_display_texture_height = texture_height; - m_display_aspect_ratio = aspect_ratio; - m_display_texture_changed = true; + m_display_texture_rect = texture_rect; + m_display_changed = true; + } + + void SetDisplayParameters(s32 display_width, s32 display_height, const Common::Rectangle& display_area, + float pixel_aspect_ratio) + { + m_display_width = display_width; + m_display_height = display_height; + m_display_area = display_area; + m_display_aspect_ratio = pixel_aspect_ratio; + m_display_changed = true; } void SetDisplayLinearFiltering(bool enabled) { m_display_linear_filtering = enabled; } void SetDisplayTopMargin(s32 height) { m_display_top_margin = height; } // Helper function for computing the draw rectangle in a larger window. - static std::tuple CalculateDrawRect(int window_width, int window_height, float display_ratio); + std::tuple CalculateDrawRect() const; protected: - void* m_display_texture_handle = nullptr; - s32 m_display_offset_x = 0; - s32 m_display_offset_y = 0; + s32 m_window_width = 0; + s32 m_window_height = 0; + s32 m_display_width = 0; s32 m_display_height = 0; - u32 m_display_texture_width = 0; - u32 m_display_texture_height = 0; - s32 m_display_top_margin = 0; + Common::Rectangle m_display_area{}; float m_display_aspect_ratio = 1.0f; - bool m_display_texture_changed = false; + void* m_display_texture_handle = nullptr; + s32 m_display_texture_width = 0; + s32 m_display_texture_height = 0; + Common::Rectangle m_display_texture_rect{}; + + s32 m_display_top_margin = 0; + bool m_display_linear_filtering = false; + bool m_display_changed = false; }; diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index e4e6f4e07..d35037021 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -813,9 +813,10 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si) si.SetIntValue("GPU", "ResolutionScale", 1); si.SetBoolValue("GPU", "TrueColor", true); si.SetBoolValue("GPU", "TextureFiltering", false); - si.SetBoolValue("GPU", "ForceProgressiveScan", true); si.SetBoolValue("GPU", "UseDebugDevice", false); + si.SetBoolValue("Display", "CropMode", "Overscan"); + si.SetBoolValue("Display", "ForceProgressiveScan", true); si.SetBoolValue("Display", "LinearFiltering", true); si.SetBoolValue("Display", "Fullscreen", false); si.SetBoolValue("Display", "VSync", true); @@ -855,11 +856,12 @@ void HostInterface::UpdateSettings(const std::function& apply_callback) const u32 old_gpu_resolution_scale = m_settings.gpu_resolution_scale; const bool old_gpu_true_color = m_settings.gpu_true_color; const bool old_gpu_texture_filtering = m_settings.gpu_texture_filtering; - const bool old_gpu_force_progressive_scan = m_settings.gpu_force_progressive_scan; + 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; const bool old_audio_sync_enabled = m_settings.audio_sync_enabled; const bool old_speed_limiter_enabled = m_settings.speed_limiter_enabled; + const DisplayCropMode old_display_crop_mode = m_settings.display_crop_mode; const bool old_display_linear_filtering = m_settings.display_linear_filtering; const bool old_cdrom_read_thread = m_settings.cdrom_read_thread; std::array old_controller_types = m_settings.controller_types; @@ -906,7 +908,8 @@ void HostInterface::UpdateSettings(const std::function& apply_callback) if (m_settings.gpu_resolution_scale != old_gpu_resolution_scale || m_settings.gpu_true_color != old_gpu_true_color || m_settings.gpu_texture_filtering != old_gpu_texture_filtering || - m_settings.gpu_force_progressive_scan != old_gpu_force_progressive_scan) + m_settings.display_force_progressive_scan != old_display_force_progressive_scan || + m_settings.display_crop_mode != old_display_crop_mode) { m_system->UpdateGPUSettings(); } diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index 12bc78ac0..b48c14f9d 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,4 +2,4 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 1; +static constexpr u32 SAVE_STATE_VERSION = 2; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 133628b21..dcdb10d47 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -24,9 +24,12 @@ void Settings::Load(SettingsInterface& si) gpu_resolution_scale = static_cast(si.GetIntValue("GPU", "ResolutionScale", 1)); gpu_true_color = si.GetBoolValue("GPU", "TrueColor", false); gpu_texture_filtering = si.GetBoolValue("GPU", "TextureFiltering", false); - gpu_force_progressive_scan = si.GetBoolValue("GPU", "ForceProgressiveScan", true); gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false); + display_crop_mode = ParseDisplayCropMode( + si.GetStringValue("Display", "CropMode", GetDisplayCropModeName(DisplayCropMode::None)).c_str()) + .value_or(DisplayCropMode::None); + display_force_progressive_scan = si.GetBoolValue("Display", "ForceProgressiveScan", true); display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true); display_fullscreen = si.GetBoolValue("Display", "Fullscreen", false); video_sync_enabled = si.GetBoolValue("Display", "VSync", true); @@ -76,9 +79,9 @@ void Settings::Save(SettingsInterface& si) const si.SetIntValue("GPU", "ResolutionScale", static_cast(gpu_resolution_scale)); si.SetBoolValue("GPU", "TrueColor", gpu_true_color); si.SetBoolValue("GPU", "TextureFiltering", gpu_texture_filtering); - si.SetBoolValue("GPU", "ForceProgressiveScan", gpu_force_progressive_scan); si.SetBoolValue("GPU", "UseDebugDevice", gpu_use_debug_device); + si.SetBoolValue("Display", "ForceProgressiveScan", display_force_progressive_scan); si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering); si.SetBoolValue("Display", "Fullscreen", display_fullscreen); si.SetBoolValue("Display", "VSync", video_sync_enabled); @@ -213,6 +216,33 @@ const char* Settings::GetRendererDisplayName(GPURenderer renderer) return s_gpu_renderer_display_names[static_cast(renderer)]; } +static std::array s_display_crop_mode_names = {{"None", "Overscan", "Borders"}}; +static std::array s_display_crop_mode_display_names = {{"None", "Only Overscan Area", "All Borders"}}; + +std::optional Settings::ParseDisplayCropMode(const char* str) +{ + int index = 0; + for (const char* name : s_display_crop_mode_names) + { + if (StringUtil::Strcasecmp(name, str) == 0) + return static_cast(index); + + index++; + } + + return std::nullopt; +} + +const char* Settings::GetDisplayCropModeName(DisplayCropMode crop_mode) +{ + return s_display_crop_mode_names[static_cast(crop_mode)]; +} + +const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode) +{ + return s_display_crop_mode_display_names[static_cast(crop_mode)]; +} + static std::array s_audio_backend_names = {{"Null", "Cubeb", "SDL"}}; static std::array s_audio_backend_display_names = {{"Null (No Output)", "Cubeb", "SDL"}}; diff --git a/src/core/settings.h b/src/core/settings.h index fb1037196..344817cca 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -48,8 +48,9 @@ struct Settings u32 gpu_resolution_scale = 1; bool gpu_true_color = false; bool gpu_texture_filtering = false; - bool gpu_force_progressive_scan = false; bool gpu_use_debug_device = false; + DisplayCropMode display_crop_mode = DisplayCropMode::None; + bool display_force_progressive_scan = false; bool display_linear_filtering = true; bool display_fullscreen = false; bool video_sync_enabled = true; @@ -97,6 +98,10 @@ struct Settings static const char* GetRendererName(GPURenderer renderer); static const char* GetRendererDisplayName(GPURenderer renderer); + static std::optional ParseDisplayCropMode(const char* str); + static const char* GetDisplayCropModeName(DisplayCropMode crop_mode); + static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode); + static std::optional ParseAudioBackend(const char* str); static const char* GetAudioBackendName(AudioBackend backend); static const char* GetAudioBackendDisplayName(AudioBackend backend); diff --git a/src/core/types.h b/src/core/types.h index 7800da74a..1645ac883 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -49,6 +49,14 @@ enum class GPURenderer : u8 Count }; +enum class DisplayCropMode : u8 +{ + None, + Overscan, + Borders, + Count +}; + enum class AudioBackend : u8 { Null, diff --git a/src/duckstation-qt/d3d11displaywindow.cpp b/src/duckstation-qt/d3d11displaywindow.cpp index 361d00647..d9261ba12 100644 --- a/src/duckstation-qt/d3d11displaywindow.cpp +++ b/src/duckstation-qt/d3d11displaywindow.cpp @@ -98,6 +98,25 @@ void D3D11DisplayWindow::ChangeRenderWindow(void* new_window) Panic("Not supported"); } +void D3D11DisplayWindow::WindowResized(s32 new_window_width, s32 new_window_height) +{ + QtDisplayWindow::WindowResized(new_window_width, new_window_height); + HostDisplay::WindowResized(new_window_width, new_window_height); + + if (!m_swap_chain) + return; + + m_swap_chain_rtv.Reset(); + + HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, + m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); + if (FAILED(hr)) + Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); + + if (!createSwapChainRTV()) + Panic("Failed to recreate swap chain RTV after resize"); +} + std::unique_ptr D3D11DisplayWindow::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) { @@ -145,32 +164,6 @@ void D3D11DisplayWindow::SetVSync(bool enabled) m_vsync = enabled; } -std::tuple D3D11DisplayWindow::GetWindowSize() const -{ - const QSize s = size(); - return std::make_tuple(static_cast(s.width()), static_cast(s.height())); -} - -void D3D11DisplayWindow::WindowResized() {} - -void D3D11DisplayWindow::onWindowResized(int width, int height) -{ - QtDisplayWindow::onWindowResized(width, height); - - if (!m_swap_chain) - return; - - m_swap_chain_rtv.Reset(); - - HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, - m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); - if (FAILED(hr)) - Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); - - if (!createSwapChainRTV()) - Panic("Failed to recreate swap chain RTV after resize"); -} - bool D3D11DisplayWindow::hasDeviceContext() const { return static_cast(m_device); @@ -415,10 +408,7 @@ void D3D11DisplayWindow::renderDisplay() if (!m_display_texture_handle) return; - // - 20 for main menu padding - auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio); - vp_top += m_display_top_margin; + auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); @@ -427,11 +417,11 @@ void D3D11DisplayWindow::renderDisplay() m_context->PSSetSamplers( 0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf()); - const float uniforms[4] = {static_cast(m_display_offset_x) / static_cast(m_display_texture_width), - static_cast(m_display_offset_y) / static_cast(m_display_texture_height), - (static_cast(m_display_width) - 0.5f) / static_cast(m_display_texture_width), - (static_cast(m_display_height) - 0.5f) / - static_cast(m_display_texture_height)}; + const float uniforms[4] = { + static_cast(m_display_texture_rect.left) / static_cast(m_display_texture_width), + static_cast(m_display_texture_rect.top) / static_cast(m_display_texture_height), + (static_cast(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast(m_display_texture_width), + (static_cast(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast(m_display_texture_height)}; const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms)); std::memcpy(map.pointer, uniforms, sizeof(uniforms)); m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms)); diff --git a/src/duckstation-qt/d3d11displaywindow.h b/src/duckstation-qt/d3d11displaywindow.h index 9eab0fc25..e960934ea 100644 --- a/src/duckstation-qt/d3d11displaywindow.h +++ b/src/duckstation-qt/d3d11displaywindow.h @@ -32,6 +32,7 @@ public: void* GetRenderWindow() const override; void ChangeRenderWindow(void* new_window) override; + void WindowResized(s32 new_window_width, s32 new_window_height) override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) override; @@ -40,13 +41,8 @@ public: void SetVSync(bool enabled) override; - std::tuple GetWindowSize() const override; - void WindowResized() override; - void Render() override; - void onWindowResized(int width, int height) override; - private: static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; diff --git a/src/duckstation-qt/gpusettingswidget.cpp b/src/duckstation-qt/gpusettingswidget.cpp index a734b00c3..f1c2d14c6 100644 --- a/src/duckstation-qt/gpusettingswidget.cpp +++ b/src/duckstation-qt/gpusettingswidget.cpp @@ -11,15 +11,18 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.renderer, "GPU/Renderer", &Settings::ParseRendererName, &Settings::GetRendererName); - SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.fullscreen, "Display/Fullscreen"); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.useDebugDevice, "GPU/UseDebugDevice"); + SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cropMode, "Display/CropMode", + &Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceProgressiveScan, + "Display/ForceProgressiveScan"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.displayLinearFiltering, "Display/LinearFiltering"); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.startFullscreen, "Display/Fullscreen"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.vsync, "Display/VSync"); 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.linearTextureFiltering, "GPU/TextureFiltering"); - SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceProgressiveScan, "GPU/ForceProgressiveScan"); - SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.useDebugDevice, "GPU/UseDebugDevice"); } GPUSettingsWidget::~GPUSettingsWidget() = default; @@ -29,6 +32,12 @@ void GPUSettingsWidget::setupAdditionalUi() for (u32 i = 0; i < static_cast(GPURenderer::Count); i++) m_ui.renderer->addItem(QString::fromLocal8Bit(Settings::GetRendererDisplayName(static_cast(i)))); + for (u32 i = 0; i < static_cast(DisplayCropMode::Count); i++) + { + m_ui.cropMode->addItem( + QString::fromLocal8Bit(Settings::GetDisplayCropModeDisplayName(static_cast(i)))); + } + m_ui.resolutionScale->addItem(tr("Automatic based on window size")); for (u32 i = 1; i <= GPU::MAX_RESOLUTION_SCALE; i++) m_ui.resolutionScale->addItem(tr("%1x (%2x%3)").arg(i).arg(GPU::VRAM_WIDTH * i).arg(GPU::VRAM_HEIGHT * i)); diff --git a/src/duckstation-qt/gpusettingswidget.ui b/src/duckstation-qt/gpusettingswidget.ui index 81ee0cf7d..d60426e07 100644 --- a/src/duckstation-qt/gpusettingswidget.ui +++ b/src/duckstation-qt/gpusettingswidget.ui @@ -42,31 +42,57 @@ - + Use Debug Device - + + + + + + + Screen Display + + + + + + Crop: + + + + + + + + + + Force Progressive Scan + + + + Linear Upscaling - - + + - VSync + Start Fullscreen - - + + - Fullscreen + VSync @@ -103,13 +129,6 @@ - - - - Force Progressive Scan - - - diff --git a/src/duckstation-qt/opengldisplaywindow.cpp b/src/duckstation-qt/opengldisplaywindow.cpp index 37ac63253..c8c4fe5b3 100644 --- a/src/duckstation-qt/opengldisplaywindow.cpp +++ b/src/duckstation-qt/opengldisplaywindow.cpp @@ -134,6 +134,12 @@ void OpenGLDisplayWindow::ChangeRenderWindow(void* new_window) Panic("Not implemented"); } +void OpenGLDisplayWindow::WindowResized(s32 new_window_width, s32 new_window_height) +{ + QtDisplayWindow::WindowResized(new_window_width, new_window_height); + HostDisplay::WindowResized(new_window_width, new_window_height); +} + std::unique_ptr OpenGLDisplayWindow::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) { @@ -165,13 +171,6 @@ void OpenGLDisplayWindow::SetVSync(bool enabled) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); } -std::tuple OpenGLDisplayWindow::GetWindowSize() const -{ - return std::make_tuple(static_cast(m_window_width), static_cast(m_window_height)); -} - -void OpenGLDisplayWindow::WindowResized() {} - const char* OpenGLDisplayWindow::GetGLSLVersionString() const { if (m_gl_context->isOpenGLES()) @@ -466,9 +465,7 @@ void OpenGLDisplayWindow::renderDisplay() if (!m_display_texture_handle) return; - // - 20 for main menu padding - const auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio); + const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(); glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height); glDisable(GL_BLEND); @@ -478,10 +475,10 @@ void OpenGLDisplayWindow::renderDisplay() glDepthMask(GL_FALSE); m_display_program.Bind(); m_display_program.Uniform4f( - 0, static_cast(m_display_offset_x) / static_cast(m_display_texture_width), - static_cast(m_display_offset_y) / static_cast(m_display_texture_height), - (static_cast(m_display_width) - 0.5f) / static_cast(m_display_texture_width), - (static_cast(m_display_height) - 0.5f) / static_cast(m_display_texture_height)); + 0, static_cast(m_display_texture_rect.left) / static_cast(m_display_texture_width), + static_cast(m_display_texture_rect.top) / static_cast(m_display_texture_height), + (static_cast(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast(m_display_texture_width), + (static_cast(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast(m_display_texture_height)); glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(m_display_texture_handle))); glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); glBindVertexArray(m_display_vao); diff --git a/src/duckstation-qt/opengldisplaywindow.h b/src/duckstation-qt/opengldisplaywindow.h index 4c3ca04fb..225ca0f33 100644 --- a/src/duckstation-qt/opengldisplaywindow.h +++ b/src/duckstation-qt/opengldisplaywindow.h @@ -38,6 +38,7 @@ public: void* GetRenderWindow() const override; void ChangeRenderWindow(void* new_window) override; + void WindowResized(s32 new_window_width, s32 new_window_height) override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) override; @@ -46,9 +47,6 @@ public: void SetVSync(bool enabled) override; - std::tuple GetWindowSize() const override; - void WindowResized() override; - void Render() override; private: diff --git a/src/duckstation-qt/qtdisplaywindow.cpp b/src/duckstation-qt/qtdisplaywindow.cpp index 3c3fbaf08..42028a6fd 100644 --- a/src/duckstation-qt/qtdisplaywindow.cpp +++ b/src/duckstation-qt/qtdisplaywindow.cpp @@ -28,9 +28,6 @@ bool QtDisplayWindow::createDeviceContext(QThread* worker_thread, bool debug_dev bool QtDisplayWindow::initializeDeviceContext(bool debug_device) { - m_window_width = static_cast(static_cast(width()) * devicePixelRatio()); - m_window_height = static_cast(static_cast(height()) * devicePixelRatio()); - if (!createImGuiContext() || !createDeviceResources()) return false; @@ -49,8 +46,8 @@ bool QtDisplayWindow::createImGuiContext() auto& io = ImGui::GetIO(); io.IniFilename = nullptr; - io.DisplaySize.x = static_cast(m_window_width); - io.DisplaySize.y = static_cast(m_window_height); + io.DisplaySize.x = static_cast(getScaledWindowWidth()); + io.DisplaySize.y = static_cast(getScaledWindowHeight()); const float framebuffer_scale = static_cast(devicePixelRatio()); io.DisplayFramebufferScale.x = framebuffer_scale; @@ -73,18 +70,15 @@ bool QtDisplayWindow::createDeviceResources() void QtDisplayWindow::destroyDeviceResources() {} -void QtDisplayWindow::onWindowResized(int width, int height) +void QtDisplayWindow::WindowResized(s32 new_window_width, s32 new_window_height) { - m_window_width = width; - m_window_height = height; - // imgui may not have been initialized yet if (!ImGui::GetCurrentContext()) return; auto& io = ImGui::GetIO(); - io.DisplaySize.x = static_cast(width); - io.DisplaySize.y = static_cast(height); + io.DisplaySize.x = static_cast(new_window_width); + io.DisplaySize.y = static_cast(new_window_height); } void QtDisplayWindow::keyPressEvent(QKeyEvent* event) diff --git a/src/duckstation-qt/qtdisplaywindow.h b/src/duckstation-qt/qtdisplaywindow.h index 01ad8b243..9162b3031 100644 --- a/src/duckstation-qt/qtdisplaywindow.h +++ b/src/duckstation-qt/qtdisplaywindow.h @@ -1,5 +1,6 @@ #pragma once #include +#include "common/types.h" class QKeyEvent; class QResizeEvent; @@ -26,12 +27,15 @@ public: virtual void Render() = 0; // this comes back on the emu thread - virtual void onWindowResized(int width, int height); + virtual void WindowResized(s32 new_window_width, s32 new_window_height); Q_SIGNALS: void windowResizedEvent(int width, int height); protected: + int getScaledWindowWidth() const { return static_cast(static_cast(width()) * devicePixelRatio()); } + int getScaledWindowHeight() const { return static_cast(static_cast(height()) * devicePixelRatio()); } + virtual bool createImGuiContext(); virtual void destroyImGuiContext(); virtual bool createDeviceResources(); @@ -42,7 +46,4 @@ protected: virtual void resizeEvent(QResizeEvent* event) override; QtHostInterface* m_host_interface; - - int m_window_width = 0; - int m_window_height = 0; }; diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 51b0fc65f..0e67a5425 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -201,7 +201,7 @@ void QtHostInterface::onDisplayWindowResized(int width, int height) { // this can be null if it was destroyed and the main thread is late catching up if (m_display_window) - m_display_window->onWindowResized(width, height); + m_display_window->WindowResized(width, height); } bool QtHostInterface::AcquireHostDisplay() diff --git a/src/duckstation-sdl/d3d11_host_display.cpp b/src/duckstation-sdl/d3d11_host_display.cpp index d972a6450..a6548a579 100644 --- a/src/duckstation-sdl/d3d11_host_display.cpp +++ b/src/duckstation-sdl/d3d11_host_display.cpp @@ -102,6 +102,30 @@ void D3D11HostDisplay::ChangeRenderWindow(void* new_window) Panic("Not supported"); } +void D3D11HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) +{ + HostDisplay::WindowResized(new_window_width, new_window_height); + + m_swap_chain_rtv.Reset(); + + HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, + m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); + if (FAILED(hr)) + Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); + + if (!CreateSwapChainRTV()) + Panic("Failed to recreate swap chain RTV after resize"); + + DXGI_SWAP_CHAIN_DESC desc; + if (SUCCEEDED(m_swap_chain->GetDesc(&desc))) + { + m_window_width = static_cast(desc.BufferDesc.Width); + m_window_height = static_cast(desc.BufferDesc.Height); + ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); + } +} + std::unique_ptr D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) { @@ -149,33 +173,6 @@ void D3D11HostDisplay::SetVSync(bool enabled) m_vsync = enabled; } -std::tuple D3D11HostDisplay::GetWindowSize() const -{ - return std::make_tuple(static_cast(m_window_width), static_cast(m_window_height)); -} - -void D3D11HostDisplay::WindowResized() -{ - m_swap_chain_rtv.Reset(); - - HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, - m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); - if (FAILED(hr)) - Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); - - if (!CreateSwapChainRTV()) - Panic("Failed to recreate swap chain RTV after resize"); - - DXGI_SWAP_CHAIN_DESC desc; - if (SUCCEEDED(m_swap_chain->GetDesc(&desc))) - { - m_window_width = static_cast(desc.BufferDesc.Width); - m_window_height = static_cast(desc.BufferDesc.Height); - ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); - } -} - bool D3D11HostDisplay::CreateD3DDevice(bool debug_device) { SDL_SysWMinfo syswm = {}; @@ -388,10 +385,7 @@ void D3D11HostDisplay::RenderDisplay() if (!m_display_texture_handle) return; - // - 20 for main menu padding - auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio); - vp_top += m_display_top_margin; + const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); @@ -400,11 +394,11 @@ void D3D11HostDisplay::RenderDisplay() m_context->PSSetSamplers( 0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf()); - const float uniforms[4] = {static_cast(m_display_offset_x) / static_cast(m_display_texture_width), - static_cast(m_display_offset_y) / static_cast(m_display_texture_height), - (static_cast(m_display_width) - 0.5f) / static_cast(m_display_texture_width), - (static_cast(m_display_height) - 0.5f) / - static_cast(m_display_texture_height)}; + const float uniforms[4] = { + static_cast(m_display_texture_rect.left) / static_cast(m_display_texture_width), + static_cast(m_display_texture_rect.top) / static_cast(m_display_texture_height), + (static_cast(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast(m_display_texture_width), + (static_cast(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast(m_display_texture_height)}; const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms)); std::memcpy(map.pointer, uniforms, sizeof(uniforms)); m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms)); diff --git a/src/duckstation-sdl/d3d11_host_display.h b/src/duckstation-sdl/d3d11_host_display.h index 285558fe5..70e47f6a5 100644 --- a/src/duckstation-sdl/d3d11_host_display.h +++ b/src/duckstation-sdl/d3d11_host_display.h @@ -25,6 +25,7 @@ public: void* GetRenderWindow() const override; void ChangeRenderWindow(void* new_window) override; + void WindowResized(s32 new_window_width, s32 new_window_height) override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) override; @@ -33,9 +34,6 @@ public: void SetVSync(bool enabled) override; - std::tuple GetWindowSize() const override; - void WindowResized() override; - private: static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; @@ -49,8 +47,6 @@ private: SDL_Window* m_window = nullptr; SDL_GLContext m_gl_context = nullptr; - int m_window_width = 0; - int m_window_height = 0; ComPtr m_device; ComPtr m_context; diff --git a/src/duckstation-sdl/main.cpp b/src/duckstation-sdl/main.cpp index 85675d775..e03e2f084 100644 --- a/src/duckstation-sdl/main.cpp +++ b/src/duckstation-sdl/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char* argv[]) Log::SetFilterLevel(level); #else Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); - // Log::SetConsoleOutputParams(true, "Pad DigitalController MemoryCard", LOGLEVEL_DEBUG); + Log::SetConsoleOutputParams(true, "Pad DigitalController MemoryCard SPU", LOGLEVEL_DEBUG); // Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); // Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU // MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE); diff --git a/src/duckstation-sdl/opengl_host_display.cpp b/src/duckstation-sdl/opengl_host_display.cpp index 0daf42fba..60490471f 100644 --- a/src/duckstation-sdl/opengl_host_display.cpp +++ b/src/duckstation-sdl/opengl_host_display.cpp @@ -100,6 +100,14 @@ void OpenGLHostDisplay::ChangeRenderWindow(void* new_window) Panic("Not implemented"); } +void OpenGLHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) +{ + HostDisplay::WindowResized(new_window_width, new_window_height); + SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height); + ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); +} + std::unique_ptr OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) { @@ -131,18 +139,6 @@ void OpenGLHostDisplay::SetVSync(bool enabled) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); } -std::tuple OpenGLHostDisplay::GetWindowSize() const -{ - return std::make_tuple(static_cast(m_window_width), static_cast(m_window_height)); -} - -void OpenGLHostDisplay::WindowResized() -{ - SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height); - ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); -} - const char* OpenGLHostDisplay::GetGLSLVersionString() const { if (m_is_gles) @@ -380,11 +376,9 @@ void OpenGLHostDisplay::RenderDisplay() if (!m_display_texture_handle) return; - // - 20 for main menu padding - const auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio); + const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(); - glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height); + glViewport(vp_left, m_window_height - vp_top - vp_height, vp_width, vp_height); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); @@ -392,10 +386,10 @@ void OpenGLHostDisplay::RenderDisplay() glDepthMask(GL_FALSE); m_display_program.Bind(); m_display_program.Uniform4f( - 0, static_cast(m_display_offset_x) / static_cast(m_display_texture_width), - static_cast(m_display_offset_y) / static_cast(m_display_texture_height), - (static_cast(m_display_width) - 0.5f) / static_cast(m_display_texture_width), - (static_cast(m_display_height) - 0.5f) / static_cast(m_display_texture_height)); + 0, static_cast(m_display_texture_rect.left) / static_cast(m_display_texture_width), + static_cast(m_display_texture_rect.top) / static_cast(m_display_texture_height), + (static_cast(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast(m_display_texture_width), + (static_cast(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast(m_display_texture_height)); glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(m_display_texture_handle))); glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); glBindVertexArray(m_display_vao); diff --git a/src/duckstation-sdl/opengl_host_display.h b/src/duckstation-sdl/opengl_host_display.h index 45ff35fab..e54093f35 100644 --- a/src/duckstation-sdl/opengl_host_display.h +++ b/src/duckstation-sdl/opengl_host_display.h @@ -20,6 +20,7 @@ public: void* GetRenderWindow() const override; void ChangeRenderWindow(void* new_window) override; + void WindowResized(s32 new_window_width, s32 new_window_height) override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) override; @@ -28,9 +29,6 @@ public: void SetVSync(bool enabled) override; - std::tuple GetWindowSize() const override; - void WindowResized() override; - private: const char* GetGLSLVersionString() const; std::string GetGLSLVersionHeader() const; @@ -44,8 +42,6 @@ private: SDL_Window* m_window = nullptr; SDL_GLContext m_gl_context = nullptr; - int m_window_width = 0; - int m_window_height = 0; GL::Program m_display_program; GLuint m_display_vao = 0; diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index fa2fc9fb4..3c09a423a 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -287,7 +287,9 @@ void SDLHostInterface::SetFullscreen(bool enabled) // We set the margin only in windowed mode, the menu bar is drawn on top in fullscreen. m_display->SetDisplayTopMargin(enabled ? 0 : static_cast(20.0f * ImGui::GetIO().DisplayFramebufferScale.x)); - m_display->WindowResized(); + int window_width, window_height; + SDL_GetWindowSize(m_window, &window_width, &window_height); + m_display->WindowResized(window_width, window_height); m_fullscreen = enabled; } @@ -371,7 +373,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) { if (event->window.event == SDL_WINDOWEVENT_RESIZED) { - m_display->WindowResized(); + m_display->WindowResized(event->window.data1, event->window.data2); UpdateFramebufferScale(); } else if (event->window.event == SDL_WINDOWEVENT_MOVED) @@ -1221,6 +1223,22 @@ void SDLHostInterface::DrawSettingsWindow() if (DrawSettingsSectionHeader("Display Output")) { + ImGui::Text("Crop:"); + ImGui::SameLine(indent); + + int display_crop_mode = static_cast(m_settings_copy.display_crop_mode); + if (ImGui::Combo( + "##display_crop_mode", &display_crop_mode, + [](void*, int index, const char** out_text) { + *out_text = Settings::GetDisplayCropModeDisplayName(static_cast(index)); + return true; + }, + nullptr, static_cast(DisplayCropMode::Count))) + { + m_settings_copy.display_crop_mode = static_cast(display_crop_mode); + settings_changed = true; + } + if (ImGui::Checkbox("Start Fullscreen", &m_settings_copy.display_fullscreen)) settings_changed = true; @@ -1265,7 +1283,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 Progressive Scan", &m_settings_copy.gpu_force_progressive_scan); + settings_changed |= ImGui::Checkbox("Force Progressive Scan", &m_settings_copy.display_force_progressive_scan); } ImGui::EndTabItem();