GPU: Implement "Crop Mode" (none, overscan, all borders)

This commit is contained in:
Connor McLaughlin 2020-02-28 17:01:01 +10:00
parent 5df7fbd68c
commit fcc0ae9571
28 changed files with 491 additions and 337 deletions

View File

@ -3,6 +3,7 @@
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "dma.h" #include "dma.h"
#include "host_display.h"
#include "host_interface.h" #include "host_interface.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "stb_image_write.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_dma = dma;
m_interrupt_controller = interrupt_controller; m_interrupt_controller = interrupt_controller;
m_timers = timers; 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_tick_event =
m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true); m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true);
return true; return true;
@ -34,7 +35,8 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr
void GPU::UpdateSettings() 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() 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.horizontal_display_range);
sw.Do(&m_crtc_state.regs.vertical_display_range); sw.Do(&m_crtc_state.regs.vertical_display_range);
sw.Do(&m_crtc_state.dot_clock_divider); sw.Do(&m_crtc_state.dot_clock_divider);
sw.Do(&m_crtc_state.display_width); sw.Do(&m_crtc_state.visible_display_width);
sw.Do(&m_crtc_state.display_height); 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_total);
sw.Do(&m_crtc_state.horizontal_display_start); sw.Do(&m_crtc_state.horizontal_display_start);
sw.Do(&m_crtc_state.horizontal_display_end); sw.Do(&m_crtc_state.horizontal_display_end);
@ -161,6 +167,7 @@ bool GPU::DoState(StateWrapper& sw)
// Restore mask setting. // Restore mask setting.
m_GPUSTAT.bits = old_GPUSTAT; m_GPUSTAT.bits = old_GPUSTAT;
UpdateCRTCConfig();
UpdateDisplay(); UpdateDisplay();
UpdateSliceTicks(); UpdateSliceTicks();
} }
@ -285,8 +292,9 @@ void GPU::Synchronize()
void GPU::UpdateCRTCConfig() void GPU::UpdateCRTCConfig()
{ {
static constexpr std::array<TickCount, 8> dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}}; static constexpr std::array<u16, 8> dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}};
CRTCState& cs = m_crtc_state; CRTCState& cs = m_crtc_state;
const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode;
if (m_GPUSTAT.pal_mode) if (m_GPUSTAT.pal_mode)
{ {
@ -303,6 +311,7 @@ void GPU::UpdateCRTCConfig()
const float vertical_frequency = const float vertical_frequency =
static_cast<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame)); static_cast<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame));
m_system->SetThrottleFrequency(vertical_frequency); 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); const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2);
cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index]; cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index];
@ -311,16 +320,34 @@ void GPU::UpdateCRTCConfig()
cs.vertical_display_start = static_cast<TickCount>(std::min<u32>(cs.regs.Y1, cs.vertical_total)); cs.vertical_display_start = static_cast<TickCount>(std::min<u32>(cs.regs.Y1, cs.vertical_total));
cs.vertical_display_end = static_cast<TickCount>(std::min<u32>(cs.regs.Y2, cs.vertical_total)); cs.vertical_display_end = static_cast<TickCount>(std::min<u32>(cs.regs.Y2, cs.vertical_total));
// check for a change in resolution // determine the active display size
const u32 old_horizontal_resolution = cs.display_width; cs.active_display_width = std::clamp<u16>((cs.regs.X2 - cs.regs.X1) / cs.dot_clock_divider, 1, VRAM_WIDTH);
const u32 old_vertical_resolution = cs.display_height; cs.active_display_height =
const u32 visible_lines = cs.regs.Y2 - cs.regs.Y1; std::clamp<u16>((cs.regs.Y2 - cs.regs.Y1), 1, VRAM_HEIGHT >> BoolToUInt8(m_GPUSTAT.In480iMode()));
cs.display_width = std::max<u32>((cs.regs.X2 - cs.regs.X1) / cs.dot_clock_divider, 1);
cs.display_height = visible_lines << BoolToUInt8(m_GPUSTAT.In480iMode());
if (cs.display_width != old_horizontal_resolution || cs.display_height != old_vertical_resolution) // Construct screen borders from configured active area and the standard visible range.
Log_InfoPrintf("Visible resolution is now %ux%u", cs.display_width, cs.display_height); // 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<u16>(m_crtc_state.regs.X1, horizontal_start_display_tick) - horizontal_start_display_tick) /
cs.dot_clock_divider;
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<u16>(cs.vertical_total) :
static_cast<u16>(m_GPUSTAT.pal_mode ? 308 : 256));
const u16 bottom_padding = vertical_end_display_line - std::min<u16>(m_crtc_state.regs.Y2, vertical_end_display_line);
cs.active_display_top =
std::max<u16>(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;
// Aspect ratio is always 4:3.
cs.display_aspect_ratio = 4.0f / 3.0f;
if (crop_mode == DisplayCropMode::Borders)
{
// Compute the aspect ratio necessary to display borders in the inactive region of the picture. // Compute the aspect ratio necessary to display borders in the inactive region of the picture.
// Convert total dots/lines to time. // Convert total dots/lines to time.
const float dot_clock = const float dot_clock =
@ -331,9 +358,9 @@ void GPU::UpdateCRTCConfig()
const float vertical_period = horizontal_period * static_cast<float>(cs.vertical_total); const float vertical_period = horizontal_period * static_cast<float>(cs.vertical_total);
// Convert active dots/lines to time. // Convert active dots/lines to time.
const float visible_dots_per_scanline = static_cast<float>(cs.display_width); const float visible_dots_per_scanline = static_cast<float>(cs.active_display_width);
const float horizontal_active_time = horizontal_period * visible_dots_per_scanline; const float horizontal_active_time = horizontal_period * visible_dots_per_scanline;
const float vertical_active_time = horizontal_active_time * static_cast<float>(visible_lines); const float vertical_active_time = horizontal_active_time * static_cast<float>(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 // 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 // transformation for the active area in our framebuffer. For the purposes of these calculations, we're assuming
@ -367,7 +394,18 @@ void GPU::UpdateCRTCConfig()
// Ensure the numbers are sane, and not due to a misconfigured active display range. // 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.display_aspect_ratio = (std::isnormal(display_ratio) && display_ratio != 0.0f) ? display_ratio : (4.0f / 3.0f);
m_tick_event->SetInterval(cs.horizontal_total); 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;
}
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) static TickCount GPUTicksToSystemTicks(u32 gpu_ticks)
@ -581,6 +619,7 @@ void GPU::WriteGP1(u32 value)
const bool disable = ConvertToBoolUnchecked(value & 0x01); const bool disable = ConvertToBoolUnchecked(value & 0x01);
Log_DebugPrintf("Display %s", disable ? "disabled" : "enabled"); Log_DebugPrintf("Display %s", disable ? "disabled" : "enabled");
m_GPUSTAT.display_disable = disable; m_GPUSTAT.display_disable = disable;
UpdateCRTCConfig();
} }
break; break;
@ -940,7 +979,7 @@ void GPU::DrawDebugStateWindow()
m_GPUSTAT.interlaced_field ? "odd" : "even"); m_GPUSTAT.interlaced_field ? "odd" : "even");
ImGui::Text("Display Disable: %s", m_GPUSTAT.display_disable ? "Yes" : "No"); 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("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("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("Start Offset: (%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue());
ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total, ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total,

View File

@ -496,28 +496,32 @@ protected:
union union
{ {
u32 display_address_start; u32 display_address_start;
BitField<u32, u32, 0, 10> X; BitField<u32, u16, 0, 10> X;
BitField<u32, u32, 10, 9> Y; BitField<u32, u16, 10, 9> Y;
}; };
union union
{ {
u32 horizontal_display_range; u32 horizontal_display_range;
BitField<u32, u32, 0, 12> X1; BitField<u32, u16, 0, 12> X1;
BitField<u32, u32, 12, 12> X2; BitField<u32, u16, 12, 12> X2;
}; };
union union
{ {
u32 vertical_display_range; u32 vertical_display_range;
BitField<u32, u32, 0, 10> Y1; BitField<u32, u16, 0, 10> Y1;
BitField<u32, u32, 10, 10> Y2; BitField<u32, u16, 10, 10> Y2;
}; };
} regs; } regs;
TickCount dot_clock_divider; u16 dot_clock_divider;
u32 display_width; u16 visible_display_width;
u32 display_height; 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_total;
TickCount horizontal_display_start; TickCount horizontal_display_start;
@ -533,6 +537,15 @@ protected:
float display_aspect_ratio; float display_aspect_ratio;
bool in_hblank; bool in_hblank;
bool in_vblank; 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<s32> GetActiveDisplayRectangle() const
{
return Common::Rectangle<s32>::FromExtents(
static_cast<s32>(ZeroExtend32(active_display_left)), static_cast<s32>(ZeroExtend32(active_display_top)),
static_cast<s32>(ZeroExtend32(active_display_width)), static_cast<s32>(ZeroExtend32(active_display_height)));
}
} m_crtc_state = {}; } m_crtc_state = {};
State m_state = State::Idle; State m_state = State::Idle;

View File

@ -14,7 +14,7 @@ GPU_HW_D3D11::~GPU_HW_D3D11()
{ {
if (m_host_display) if (m_host_display)
{ {
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); m_host_display->ClearDisplayTexture();
ResetGraphicsAPIState(); ResetGraphicsAPIState();
} }
} }
@ -521,9 +521,11 @@ void GPU_HW_D3D11::UpdateDisplay()
if (m_system->GetSettings().debugging.show_vram) if (m_system->GetSettings().debugging.show_vram)
{ {
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), 0, 0, m_vram_texture.GetWidth(), m_host_display->SetDisplayTexture(
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
1.0f); Common::Rectangle<s32>(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()));
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT,
Common::Rectangle<s32>(0, 0, VRAM_WIDTH, VRAM_HEIGHT), 1.0f);
} }
else else
{ {
@ -531,21 +533,23 @@ void GPU_HW_D3D11::UpdateDisplay()
const u32 vram_offset_y = m_crtc_state.regs.Y; 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_x = vram_offset_x * m_resolution_scale;
const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale;
const u32 display_width = std::min<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); const u32 display_width = std::min<u32>(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x);
const u32 display_height = std::min<u32>(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); const u32 display_height = std::min<u32>(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_width = display_width * m_resolution_scale;
const u32 scaled_display_height = display_height * m_resolution_scale; const u32 scaled_display_height = display_height * m_resolution_scale;
const bool interlaced = IsDisplayInterlaced(); const bool interlaced = IsDisplayInterlaced();
if (m_GPUSTAT.display_disable) 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) 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, m_host_display->SetDisplayTexture(
scaled_display_width, scaled_display_height, m_vram_texture.GetWidth(), m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
m_vram_texture.GetHeight(), m_crtc_state.display_aspect_ratio); Common::Rectangle<s32>::FromExtents(scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
scaled_display_height));
} }
else else
{ {
@ -570,9 +574,9 @@ void GPU_HW_D3D11::UpdateDisplay()
SetViewportAndScissor(0, field_offset, display_width, display_height); SetViewportAndScissor(0, field_offset, display_width, display_height);
DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms));
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, display_width, display_height, m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), m_display_texture.GetWidth(),
m_display_texture.GetWidth(), m_display_texture.GetHeight(), m_display_texture.GetHeight(),
m_crtc_state.display_aspect_ratio); Common::Rectangle<s32>(0, 0, display_width, display_height));
} }
else else
{ {
@ -583,13 +587,16 @@ void GPU_HW_D3D11::UpdateDisplay()
SetViewportAndScissor(0, field_offset, scaled_display_width, scaled_display_height); SetViewportAndScissor(0, field_offset, scaled_display_width, scaled_display_height);
DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms)); DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms));
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), 0, 0, scaled_display_width, m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), m_display_texture.GetWidth(),
scaled_display_height, m_display_texture.GetWidth(), m_display_texture.GetHeight(),
m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); Common::Rectangle<s32>(0, 0, scaled_display_width, scaled_display_height));
} }
RestoreGraphicsAPIState(); 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);
} }
} }

View File

@ -20,7 +20,7 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
if (m_host_display) if (m_host_display)
{ {
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); m_host_display->ClearDisplayTexture();
ResetGraphicsAPIState(); ResetGraphicsAPIState();
} }
} }
@ -70,9 +70,6 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* d
return false; return false;
} }
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(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(); RestoreGraphicsAPIState();
return true; return true;
} }
@ -479,10 +476,12 @@ void GPU_HW_OpenGL::UpdateDisplay()
if (m_system->GetSettings().debugging.show_vram) if (m_system->GetSettings().debugging.show_vram)
{ {
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), 0, m_host_display->SetDisplayTexture(
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
-static_cast<s32>(m_vram_texture.GetHeight()), m_vram_texture.GetWidth(), -static_cast<s32>(m_vram_texture.GetHeight()),
m_vram_texture.GetHeight(), 1.0f); Common::Rectangle<s32>(0, m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), 0));
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT,
Common::Rectangle<s32>(0, 0, VRAM_WIDTH, VRAM_HEIGHT), 1.0f);
} }
else else
{ {
@ -490,23 +489,23 @@ void GPU_HW_OpenGL::UpdateDisplay()
const u32 vram_offset_y = m_crtc_state.regs.Y; 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_x = vram_offset_x * m_resolution_scale;
const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale;
const u32 display_width = std::min<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); const u32 display_width = std::min<u32>(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x);
const u32 display_height = std::min<u32>(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); const u32 display_height = std::min<u32>(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_width = display_width * m_resolution_scale;
const u32 scaled_display_height = display_height * m_resolution_scale; const u32 scaled_display_height = display_height * m_resolution_scale;
const bool interlaced = IsDisplayInterlaced(); const bool interlaced = IsDisplayInterlaced();
if (m_GPUSTAT.display_disable) 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) else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced)
{ {
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_host_display->SetDisplayTexture(
scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
scaled_display_width, -static_cast<s32>(scaled_display_height), m_vram_texture.GetHeight(),
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), Common::Rectangle<s32>(scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y,
m_crtc_state.display_aspect_ratio); scaled_display_width, -static_cast<s32>(scaled_display_height)));
} }
else else
{ {
@ -545,10 +544,10 @@ void GPU_HW_OpenGL::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_host_display->SetDisplayTexture(
0, display_height, display_width, -static_cast<s32>(display_height), reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_display_texture.GetWidth(),
m_display_texture.GetWidth(), m_display_texture.GetHeight(), m_display_texture.GetHeight(),
m_crtc_state.display_aspect_ratio); Common::Rectangle<s32>(0, display_height, display_width, -static_cast<s32>(display_height)));
} }
else else
{ {
@ -564,9 +563,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
0, scaled_display_height, scaled_display_width, m_display_texture.GetWidth(), m_display_texture.GetHeight(),
-static_cast<s32>(scaled_display_height), m_display_texture.GetWidth(), Common::Rectangle<s32>(0, scaled_display_height, scaled_display_width,
m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); -static_cast<s32>(scaled_display_height)));
} }
// restore state // restore state
@ -574,6 +573,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
glViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); glViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
glEnable(GL_SCISSOR_TEST); 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);
} }
} }

View File

@ -13,7 +13,7 @@ GPU_HW_OpenGL_ES::~GPU_HW_OpenGL_ES()
// TODO: Destroy objects... // TODO: Destroy objects...
if (m_host_display) if (m_host_display)
{ {
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, 1.0f); m_host_display->ClearDisplayTexture();
ResetGraphicsAPIState(); ResetGraphicsAPIState();
} }
} }
@ -44,9 +44,6 @@ bool GPU_HW_OpenGL_ES::Initialize(HostDisplay* host_display, System* system, DMA
return false; return false;
} }
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(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(); RestoreGraphicsAPIState();
return true; return true;
} }
@ -346,10 +343,12 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
if (m_system->GetSettings().debugging.show_vram) if (m_system->GetSettings().debugging.show_vram)
{ {
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), 0, m_host_display->SetDisplayTexture(
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
-static_cast<s32>(m_vram_texture.GetHeight()), m_vram_texture.GetWidth(), -static_cast<s32>(m_vram_texture.GetHeight()),
m_vram_texture.GetHeight(), 1.0f); Common::Rectangle<s32>(0, m_vram_texture.GetHeight(), m_vram_texture.GetWidth(), 0));
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, Common::Rectangle<s32>(0, 0, VRAM_WIDTH, VRAM_HEIGHT),
1.0f);
} }
else else
{ {
@ -357,23 +356,23 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
const u32 vram_offset_y = m_crtc_state.regs.Y; 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_x = vram_offset_x * m_resolution_scale;
const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale; const u32 scaled_vram_offset_y = vram_offset_y * m_resolution_scale;
const u32 display_width = std::min<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); const u32 display_width = std::min<u32>(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x);
const u32 display_height = std::min<u32>(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); const u32 display_height = std::min<u32>(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_width = display_width * m_resolution_scale;
const u32 scaled_display_height = display_height * m_resolution_scale; const u32 scaled_display_height = display_height * m_resolution_scale;
const bool interlaced = IsDisplayInterlaced(); const bool interlaced = IsDisplayInterlaced();
if (m_GPUSTAT.display_disable) 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) else if (!m_GPUSTAT.display_area_color_depth_24 && !interlaced)
{ {
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_host_display->SetDisplayTexture(
scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y, reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
scaled_display_width, -static_cast<s32>(scaled_display_height), m_vram_texture.GetHeight(),
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), Common::Rectangle<s32>(scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y,
m_crtc_state.display_aspect_ratio); scaled_display_width, -static_cast<s32>(scaled_display_height)));
} }
else else
{ {
@ -412,10 +411,10 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_host_display->SetDisplayTexture(
0, display_height, display_width, -static_cast<s32>(display_height), reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_display_texture.GetWidth(),
m_display_texture.GetWidth(), m_display_texture.GetHeight(), m_display_texture.GetHeight(),
m_crtc_state.display_aspect_ratio); Common::Rectangle<s32>(0, display_height, display_width, -static_cast<s32>(display_height)));
} }
else else
{ {
@ -431,9 +430,9 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
0, scaled_display_height, scaled_display_width, m_display_texture.GetWidth(), m_display_texture.GetHeight(),
-static_cast<s32>(scaled_display_height), m_display_texture.GetWidth(), Common::Rectangle<s32>(0, scaled_display_height, scaled_display_width,
m_display_texture.GetHeight(), m_crtc_state.display_aspect_ratio); -static_cast<s32>(scaled_display_height)));
} }
// restore state // restore state
@ -441,6 +440,9 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
glViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); glViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
glEnable(GL_SCISSOR_TEST); 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);
} }
} }

View File

@ -12,7 +12,8 @@ GPU_SW::GPU_SW()
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 bool GPU_SW::IsHardwareRenderer() const
@ -113,21 +114,17 @@ void GPU_SW::UpdateDisplay()
// fill display texture // fill display texture
m_display_texture_buffer.resize(VRAM_WIDTH * VRAM_HEIGHT); 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) if (!m_system->GetSettings().debugging.show_vram)
{ {
// TODO: Handle interlacing // TODO: Handle interlacing
const u32 vram_offset_x = m_crtc_state.regs.X; const u32 vram_offset_x = m_crtc_state.regs.X;
const u32 vram_offset_y = m_crtc_state.regs.Y; const u32 vram_offset_y = m_crtc_state.regs.Y;
display_width = std::min<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x); const u32 display_width = std::min<u32>(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x);
display_height = std::min<u32>(m_crtc_state.display_height, VRAM_HEIGHT - vram_offset_y); const u32 display_height = std::min<u32>(m_crtc_state.active_display_height, VRAM_HEIGHT - vram_offset_y);
display_aspect_ratio = m_crtc_state.display_aspect_ratio;
if (m_GPUSTAT.display_disable) if (m_GPUSTAT.display_disable)
{ {
m_host_display->SetDisplayTexture(nullptr, 0, 0, 0, 0, 0, 0, display_aspect_ratio); m_host_display->ClearDisplayTexture();
return; return;
} }
else if (m_GPUSTAT.display_area_color_depth_24) 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, 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_display_texture_buffer.data(), display_width, display_width, display_height);
} }
}
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);
}
m_host_display->UpdateTexture(m_display_texture.get(), 0, 0, 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_display_texture_buffer.data(), display_width * sizeof(u32));
m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), 0, 0, display_width, display_height, VRAM_WIDTH, m_host_display->SetDisplayTexture(m_display_texture->GetHandle(), VRAM_WIDTH, VRAM_HEIGHT,
VRAM_HEIGHT, display_aspect_ratio); Common::Rectangle<s32>(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
{
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<s32>(0, 0, VRAM_WIDTH, VRAM_HEIGHT));
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT,
Common::Rectangle<s32>(0, 0, VRAM_WIDTH, VRAM_HEIGHT), 1.0f);
}
} }
void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr) void GPU_SW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr)

View File

@ -4,24 +4,45 @@ HostDisplayTexture::~HostDisplayTexture() = default;
HostDisplay::~HostDisplay() = default; HostDisplay::~HostDisplay() = default;
std::tuple<int, int, int, int> 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<s32, s32, s32, s32> 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<float>(window_width) / static_cast<float>(window_height);
float scale;
int left, top, width, height; int left, top, width, height;
if (window_ratio >= display_ratio) if (window_ratio >= m_display_aspect_ratio)
{ {
width = static_cast<int>(float(window_height) * display_ratio); width = static_cast<int>(static_cast<float>(window_height) * m_display_aspect_ratio);
height = static_cast<int>(window_height); height = static_cast<int>(window_height);
scale = static_cast<float>(window_height) / static_cast<float>(m_display_height);
left = (window_width - width) / 2; left = (window_width - width) / 2;
top = 0; top = 0;
} }
else else
{ {
width = static_cast<int>(window_width); width = static_cast<int>(window_width);
height = static_cast<int>(float(window_width) / display_ratio); height = static_cast<int>(float(window_width) / m_display_aspect_ratio);
scale = static_cast<float>(window_width) / static_cast<float>(m_display_width);
left = 0; left = 0;
top = (window_height - height) / 2; top = (window_height - height) / 2;
} }
// add in padding
left += static_cast<s32>(static_cast<float>(m_display_area.left) * scale);
top += static_cast<s32>(static_cast<float>(m_display_area.top) * scale);
width -= static_cast<s32>(static_cast<float>(m_display_area.left + (m_display_width - m_display_area.right)) * scale);
height -=
static_cast<s32>(static_cast<float>(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); return std::tie(left, top, width, height);
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "common/rectangle.h"
#include "types.h" #include "types.h"
#include <memory> #include <memory>
#include <tuple> #include <tuple>
@ -36,6 +37,9 @@ public:
/// Switches the render window, recreating the surface. /// Switches the render window, recreating the surface.
virtual void ChangeRenderWindow(void* new_window) = 0; 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. /// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below.
virtual std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, virtual std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
bool dynamic = false) = 0; bool dynamic = false) = 0;
@ -46,42 +50,59 @@ public:
virtual void SetVSync(bool enabled) = 0; virtual void SetVSync(bool enabled) = 0;
virtual std::tuple<u32, u32> GetWindowSize() const = 0;
virtual void WindowResized() = 0;
const s32 GetDisplayTopMargin() const { return m_display_top_margin; } 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, void ClearDisplayTexture()
u32 texture_height, float aspect_ratio) {
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<s32>& texture_rect)
{ {
m_display_texture_handle = texture_handle; 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_width = texture_width;
m_display_texture_height = texture_height; m_display_texture_height = texture_height;
m_display_aspect_ratio = aspect_ratio; m_display_texture_rect = texture_rect;
m_display_texture_changed = true; m_display_changed = true;
}
void SetDisplayParameters(s32 display_width, s32 display_height, const Common::Rectangle<s32>& 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 SetDisplayLinearFiltering(bool enabled) { m_display_linear_filtering = enabled; }
void SetDisplayTopMargin(s32 height) { m_display_top_margin = height; } void SetDisplayTopMargin(s32 height) { m_display_top_margin = height; }
// Helper function for computing the draw rectangle in a larger window. // Helper function for computing the draw rectangle in a larger window.
static std::tuple<int, int, int, int> CalculateDrawRect(int window_width, int window_height, float display_ratio); std::tuple<s32, s32, s32, s32> CalculateDrawRect() const;
protected: protected:
void* m_display_texture_handle = nullptr; s32 m_window_width = 0;
s32 m_display_offset_x = 0; s32 m_window_height = 0;
s32 m_display_offset_y = 0;
s32 m_display_width = 0; s32 m_display_width = 0;
s32 m_display_height = 0; s32 m_display_height = 0;
u32 m_display_texture_width = 0; Common::Rectangle<s32> m_display_area{};
u32 m_display_texture_height = 0;
s32 m_display_top_margin = 0;
float m_display_aspect_ratio = 1.0f; 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<s32> m_display_texture_rect{};
s32 m_display_top_margin = 0;
bool m_display_linear_filtering = false; bool m_display_linear_filtering = false;
bool m_display_changed = false;
}; };

View File

@ -813,9 +813,10 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetIntValue("GPU", "ResolutionScale", 1); si.SetIntValue("GPU", "ResolutionScale", 1);
si.SetBoolValue("GPU", "TrueColor", true); si.SetBoolValue("GPU", "TrueColor", true);
si.SetBoolValue("GPU", "TextureFiltering", false); si.SetBoolValue("GPU", "TextureFiltering", false);
si.SetBoolValue("GPU", "ForceProgressiveScan", true);
si.SetBoolValue("GPU", "UseDebugDevice", false); si.SetBoolValue("GPU", "UseDebugDevice", false);
si.SetBoolValue("Display", "CropMode", "Overscan");
si.SetBoolValue("Display", "ForceProgressiveScan", true);
si.SetBoolValue("Display", "LinearFiltering", true); si.SetBoolValue("Display", "LinearFiltering", true);
si.SetBoolValue("Display", "Fullscreen", false); si.SetBoolValue("Display", "Fullscreen", false);
si.SetBoolValue("Display", "VSync", true); si.SetBoolValue("Display", "VSync", true);
@ -855,11 +856,12 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
const u32 old_gpu_resolution_scale = m_settings.gpu_resolution_scale; 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_true_color = m_settings.gpu_true_color;
const bool old_gpu_texture_filtering = m_settings.gpu_texture_filtering; 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_gpu_debug_device = m_settings.gpu_use_debug_device;
const bool old_vsync_enabled = m_settings.video_sync_enabled; const bool old_vsync_enabled = m_settings.video_sync_enabled;
const bool old_audio_sync_enabled = m_settings.audio_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 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_display_linear_filtering = m_settings.display_linear_filtering;
const bool old_cdrom_read_thread = m_settings.cdrom_read_thread; const bool old_cdrom_read_thread = m_settings.cdrom_read_thread;
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types; std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types;
@ -906,7 +908,8 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
if (m_settings.gpu_resolution_scale != old_gpu_resolution_scale || if (m_settings.gpu_resolution_scale != old_gpu_resolution_scale ||
m_settings.gpu_true_color != old_gpu_true_color || m_settings.gpu_true_color != old_gpu_true_color ||
m_settings.gpu_texture_filtering != old_gpu_texture_filtering || 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(); m_system->UpdateGPUSettings();
} }

View File

@ -2,4 +2,4 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 1; static constexpr u32 SAVE_STATE_VERSION = 2;

View File

@ -24,9 +24,12 @@ void Settings::Load(SettingsInterface& si)
gpu_resolution_scale = static_cast<u32>(si.GetIntValue("GPU", "ResolutionScale", 1)); gpu_resolution_scale = static_cast<u32>(si.GetIntValue("GPU", "ResolutionScale", 1));
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", false); gpu_true_color = si.GetBoolValue("GPU", "TrueColor", false);
gpu_texture_filtering = si.GetBoolValue("GPU", "TextureFiltering", 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); 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_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
display_fullscreen = si.GetBoolValue("Display", "Fullscreen", false); display_fullscreen = si.GetBoolValue("Display", "Fullscreen", false);
video_sync_enabled = si.GetBoolValue("Display", "VSync", true); video_sync_enabled = si.GetBoolValue("Display", "VSync", true);
@ -76,9 +79,9 @@ void Settings::Save(SettingsInterface& si) const
si.SetIntValue("GPU", "ResolutionScale", static_cast<long>(gpu_resolution_scale)); si.SetIntValue("GPU", "ResolutionScale", static_cast<long>(gpu_resolution_scale));
si.SetBoolValue("GPU", "TrueColor", gpu_true_color); si.SetBoolValue("GPU", "TrueColor", gpu_true_color);
si.SetBoolValue("GPU", "TextureFiltering", gpu_texture_filtering); 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("GPU", "UseDebugDevice", gpu_use_debug_device);
si.SetBoolValue("Display", "ForceProgressiveScan", display_force_progressive_scan);
si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering); si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);
si.SetBoolValue("Display", "Fullscreen", display_fullscreen); si.SetBoolValue("Display", "Fullscreen", display_fullscreen);
si.SetBoolValue("Display", "VSync", video_sync_enabled); 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<int>(renderer)]; return s_gpu_renderer_display_names[static_cast<int>(renderer)];
} }
static std::array<const char*, 3> s_display_crop_mode_names = {{"None", "Overscan", "Borders"}};
static std::array<const char*, 3> s_display_crop_mode_display_names = {{"None", "Only Overscan Area", "All Borders"}};
std::optional<DisplayCropMode> 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<DisplayCropMode>(index);
index++;
}
return std::nullopt;
}
const char* Settings::GetDisplayCropModeName(DisplayCropMode crop_mode)
{
return s_display_crop_mode_names[static_cast<int>(crop_mode)];
}
const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode)
{
return s_display_crop_mode_display_names[static_cast<int>(crop_mode)];
}
static std::array<const char*, 3> s_audio_backend_names = {{"Null", "Cubeb", "SDL"}}; static std::array<const char*, 3> s_audio_backend_names = {{"Null", "Cubeb", "SDL"}};
static std::array<const char*, 3> s_audio_backend_display_names = {{"Null (No Output)", "Cubeb", "SDL"}}; static std::array<const char*, 3> s_audio_backend_display_names = {{"Null (No Output)", "Cubeb", "SDL"}};

View File

@ -48,8 +48,9 @@ struct Settings
u32 gpu_resolution_scale = 1; u32 gpu_resolution_scale = 1;
bool gpu_true_color = false; bool gpu_true_color = false;
bool gpu_texture_filtering = false; bool gpu_texture_filtering = false;
bool gpu_force_progressive_scan = false;
bool gpu_use_debug_device = 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_linear_filtering = true;
bool display_fullscreen = false; bool display_fullscreen = false;
bool video_sync_enabled = true; bool video_sync_enabled = true;
@ -97,6 +98,10 @@ struct Settings
static const char* GetRendererName(GPURenderer renderer); static const char* GetRendererName(GPURenderer renderer);
static const char* GetRendererDisplayName(GPURenderer renderer); static const char* GetRendererDisplayName(GPURenderer renderer);
static std::optional<DisplayCropMode> ParseDisplayCropMode(const char* str);
static const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
static std::optional<AudioBackend> ParseAudioBackend(const char* str); static std::optional<AudioBackend> ParseAudioBackend(const char* str);
static const char* GetAudioBackendName(AudioBackend backend); static const char* GetAudioBackendName(AudioBackend backend);
static const char* GetAudioBackendDisplayName(AudioBackend backend); static const char* GetAudioBackendDisplayName(AudioBackend backend);

View File

@ -49,6 +49,14 @@ enum class GPURenderer : u8
Count Count
}; };
enum class DisplayCropMode : u8
{
None,
Overscan,
Borders,
Count
};
enum class AudioBackend : u8 enum class AudioBackend : u8
{ {
Null, Null,

View File

@ -98,6 +98,25 @@ void D3D11DisplayWindow::ChangeRenderWindow(void* new_window)
Panic("Not supported"); 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<HostDisplayTexture> D3D11DisplayWindow::CreateTexture(u32 width, u32 height, const void* data, std::unique_ptr<HostDisplayTexture> D3D11DisplayWindow::CreateTexture(u32 width, u32 height, const void* data,
u32 data_stride, bool dynamic) u32 data_stride, bool dynamic)
{ {
@ -145,32 +164,6 @@ void D3D11DisplayWindow::SetVSync(bool enabled)
m_vsync = enabled; m_vsync = enabled;
} }
std::tuple<u32, u32> D3D11DisplayWindow::GetWindowSize() const
{
const QSize s = size();
return std::make_tuple(static_cast<u32>(s.width()), static_cast<u32>(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 bool D3D11DisplayWindow::hasDeviceContext() const
{ {
return static_cast<bool>(m_device); return static_cast<bool>(m_device);
@ -415,10 +408,7 @@ void D3D11DisplayWindow::renderDisplay()
if (!m_display_texture_handle) if (!m_display_texture_handle)
return; return;
// - 20 for main menu padding auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
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;
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);
@ -427,11 +417,11 @@ void D3D11DisplayWindow::renderDisplay()
m_context->PSSetSamplers( m_context->PSSetSamplers(
0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf()); 0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf());
const float uniforms[4] = {static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width), const float uniforms[4] = {
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height), static_cast<float>(m_display_texture_rect.left) / static_cast<float>(m_display_texture_width),
(static_cast<float>(m_display_width) - 0.5f) / static_cast<float>(m_display_texture_width), static_cast<float>(m_display_texture_rect.top) / static_cast<float>(m_display_texture_height),
(static_cast<float>(m_display_height) - 0.5f) / (static_cast<float>(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
static_cast<float>(m_display_texture_height)}; (static_cast<float>(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast<float>(m_display_texture_height)};
const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms)); const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms));
std::memcpy(map.pointer, uniforms, sizeof(uniforms)); std::memcpy(map.pointer, uniforms, sizeof(uniforms));
m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms)); m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms));

View File

@ -32,6 +32,7 @@ public:
void* GetRenderWindow() const override; void* GetRenderWindow() const override;
void ChangeRenderWindow(void* new_window) override; void ChangeRenderWindow(void* new_window) override;
void WindowResized(s32 new_window_width, s32 new_window_height) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
bool dynamic) override; bool dynamic) override;
@ -40,13 +41,8 @@ public:
void SetVSync(bool enabled) override; void SetVSync(bool enabled) override;
std::tuple<u32, u32> GetWindowSize() const override;
void WindowResized() override;
void Render() override; void Render() override;
void onWindowResized(int width, int height) override;
private: private:
static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16;

View File

@ -11,15 +11,18 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.renderer, "GPU/Renderer", SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.renderer, "GPU/Renderer",
&Settings::ParseRendererName, &Settings::GetRendererName); &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, SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.displayLinearFiltering,
"Display/LinearFiltering"); "Display/LinearFiltering");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.startFullscreen, "Display/Fullscreen");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.vsync, "Display/VSync"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.vsync, "Display/VSync");
SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.resolutionScale, "GPU/ResolutionScale"); 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.trueColor, "GPU/TrueColor");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.linearTextureFiltering, "GPU/TextureFiltering"); 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; GPUSettingsWidget::~GPUSettingsWidget() = default;
@ -29,6 +32,12 @@ void GPUSettingsWidget::setupAdditionalUi()
for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); i++) for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); i++)
m_ui.renderer->addItem(QString::fromLocal8Bit(Settings::GetRendererDisplayName(static_cast<GPURenderer>(i)))); m_ui.renderer->addItem(QString::fromLocal8Bit(Settings::GetRendererDisplayName(static_cast<GPURenderer>(i))));
for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
{
m_ui.cropMode->addItem(
QString::fromLocal8Bit(Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
}
m_ui.resolutionScale->addItem(tr("Automatic based on window size")); m_ui.resolutionScale->addItem(tr("Automatic based on window size"));
for (u32 i = 1; i <= GPU::MAX_RESOLUTION_SCALE; i++) 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)); m_ui.resolutionScale->addItem(tr("%1x (%2x%3)").arg(i).arg(GPU::VRAM_WIDTH * i).arg(GPU::VRAM_HEIGHT * i));

View File

@ -42,31 +42,57 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="renderer"/> <widget class="QComboBox" name="renderer"/>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="useDebugDevice"> <widget class="QCheckBox" name="useDebugDevice">
<property name="text"> <property name="text">
<string>Use Debug Device</string> <string>Use Debug Device</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2"> </layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Screen Display</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Crop:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cropMode"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="forceProgressiveScan">
<property name="text">
<string>Force Progressive Scan</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="displayLinearFiltering"> <widget class="QCheckBox" name="displayLinearFiltering">
<property name="text"> <property name="text">
<string>Linear Upscaling</string> <string>Linear Upscaling</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="vsync"> <widget class="QCheckBox" name="startFullscreen">
<property name="text"> <property name="text">
<string>VSync</string> <string>Start Fullscreen</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="fullscreen"> <widget class="QCheckBox" name="vsync">
<property name="text"> <property name="text">
<string>Fullscreen</string> <string>VSync</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -103,13 +129,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="forceProgressiveScan">
<property name="text">
<string>Force Progressive Scan</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -134,6 +134,12 @@ void OpenGLDisplayWindow::ChangeRenderWindow(void* new_window)
Panic("Not implemented"); 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<HostDisplayTexture> OpenGLDisplayWindow::CreateTexture(u32 width, u32 height, const void* data, std::unique_ptr<HostDisplayTexture> OpenGLDisplayWindow::CreateTexture(u32 width, u32 height, const void* data,
u32 data_stride, bool dynamic) u32 data_stride, bool dynamic)
{ {
@ -165,13 +171,6 @@ void OpenGLDisplayWindow::SetVSync(bool enabled)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
} }
std::tuple<u32, u32> OpenGLDisplayWindow::GetWindowSize() const
{
return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(m_window_height));
}
void OpenGLDisplayWindow::WindowResized() {}
const char* OpenGLDisplayWindow::GetGLSLVersionString() const const char* OpenGLDisplayWindow::GetGLSLVersionString() const
{ {
if (m_gl_context->isOpenGLES()) if (m_gl_context->isOpenGLES())
@ -466,9 +465,7 @@ void OpenGLDisplayWindow::renderDisplay()
if (!m_display_texture_handle) if (!m_display_texture_handle)
return; return;
// - 20 for main menu padding const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
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);
glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height); glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height);
glDisable(GL_BLEND); glDisable(GL_BLEND);
@ -478,10 +475,10 @@ void OpenGLDisplayWindow::renderDisplay()
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
m_display_program.Bind(); m_display_program.Bind();
m_display_program.Uniform4f( m_display_program.Uniform4f(
0, static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width), 0, static_cast<float>(m_display_texture_rect.left) / static_cast<float>(m_display_texture_width),
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height), static_cast<float>(m_display_texture_rect.top) / static_cast<float>(m_display_texture_height),
(static_cast<float>(m_display_width) - 0.5f) / static_cast<float>(m_display_texture_width), (static_cast<float>(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
(static_cast<float>(m_display_height) - 0.5f) / static_cast<float>(m_display_texture_height)); (static_cast<float>(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast<float>(m_display_texture_height));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(m_display_texture_handle))); glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(m_display_texture_handle)));
glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler);
glBindVertexArray(m_display_vao); glBindVertexArray(m_display_vao);

View File

@ -38,6 +38,7 @@ public:
void* GetRenderWindow() const override; void* GetRenderWindow() const override;
void ChangeRenderWindow(void* new_window) override; void ChangeRenderWindow(void* new_window) override;
void WindowResized(s32 new_window_width, s32 new_window_height) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
bool dynamic) override; bool dynamic) override;
@ -46,9 +47,6 @@ public:
void SetVSync(bool enabled) override; void SetVSync(bool enabled) override;
std::tuple<u32, u32> GetWindowSize() const override;
void WindowResized() override;
void Render() override; void Render() override;
private: private:

View File

@ -28,9 +28,6 @@ bool QtDisplayWindow::createDeviceContext(QThread* worker_thread, bool debug_dev
bool QtDisplayWindow::initializeDeviceContext(bool debug_device) bool QtDisplayWindow::initializeDeviceContext(bool debug_device)
{ {
m_window_width = static_cast<int>(static_cast<qreal>(width()) * devicePixelRatio());
m_window_height = static_cast<int>(static_cast<qreal>(height()) * devicePixelRatio());
if (!createImGuiContext() || !createDeviceResources()) if (!createImGuiContext() || !createDeviceResources())
return false; return false;
@ -49,8 +46,8 @@ bool QtDisplayWindow::createImGuiContext()
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
io.IniFilename = nullptr; io.IniFilename = nullptr;
io.DisplaySize.x = static_cast<float>(m_window_width); io.DisplaySize.x = static_cast<float>(getScaledWindowWidth());
io.DisplaySize.y = static_cast<float>(m_window_height); io.DisplaySize.y = static_cast<float>(getScaledWindowHeight());
const float framebuffer_scale = static_cast<float>(devicePixelRatio()); const float framebuffer_scale = static_cast<float>(devicePixelRatio());
io.DisplayFramebufferScale.x = framebuffer_scale; io.DisplayFramebufferScale.x = framebuffer_scale;
@ -73,18 +70,15 @@ bool QtDisplayWindow::createDeviceResources()
void QtDisplayWindow::destroyDeviceResources() {} 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 // imgui may not have been initialized yet
if (!ImGui::GetCurrentContext()) if (!ImGui::GetCurrentContext())
return; return;
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
io.DisplaySize.x = static_cast<float>(width); io.DisplaySize.x = static_cast<float>(new_window_width);
io.DisplaySize.y = static_cast<float>(height); io.DisplaySize.y = static_cast<float>(new_window_height);
} }
void QtDisplayWindow::keyPressEvent(QKeyEvent* event) void QtDisplayWindow::keyPressEvent(QKeyEvent* event)

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include "common/types.h"
class QKeyEvent; class QKeyEvent;
class QResizeEvent; class QResizeEvent;
@ -26,12 +27,15 @@ public:
virtual void Render() = 0; virtual void Render() = 0;
// this comes back on the emu thread // 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: Q_SIGNALS:
void windowResizedEvent(int width, int height); void windowResizedEvent(int width, int height);
protected: protected:
int getScaledWindowWidth() const { return static_cast<int>(static_cast<qreal>(width()) * devicePixelRatio()); }
int getScaledWindowHeight() const { return static_cast<int>(static_cast<qreal>(height()) * devicePixelRatio()); }
virtual bool createImGuiContext(); virtual bool createImGuiContext();
virtual void destroyImGuiContext(); virtual void destroyImGuiContext();
virtual bool createDeviceResources(); virtual bool createDeviceResources();
@ -42,7 +46,4 @@ protected:
virtual void resizeEvent(QResizeEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override;
QtHostInterface* m_host_interface; QtHostInterface* m_host_interface;
int m_window_width = 0;
int m_window_height = 0;
}; };

View File

@ -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 // this can be null if it was destroyed and the main thread is late catching up
if (m_display_window) if (m_display_window)
m_display_window->onWindowResized(width, height); m_display_window->WindowResized(width, height);
} }
bool QtHostInterface::AcquireHostDisplay() bool QtHostInterface::AcquireHostDisplay()

View File

@ -102,6 +102,30 @@ void D3D11HostDisplay::ChangeRenderWindow(void* new_window)
Panic("Not supported"); 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<int>(desc.BufferDesc.Width);
m_window_height = static_cast<int>(desc.BufferDesc.Height);
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_height);
}
}
std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data, std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data,
u32 data_stride, bool dynamic) u32 data_stride, bool dynamic)
{ {
@ -149,33 +173,6 @@ void D3D11HostDisplay::SetVSync(bool enabled)
m_vsync = enabled; m_vsync = enabled;
} }
std::tuple<u32, u32> D3D11HostDisplay::GetWindowSize() const
{
return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(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<int>(desc.BufferDesc.Width);
m_window_height = static_cast<int>(desc.BufferDesc.Height);
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_height);
}
}
bool D3D11HostDisplay::CreateD3DDevice(bool debug_device) bool D3D11HostDisplay::CreateD3DDevice(bool debug_device)
{ {
SDL_SysWMinfo syswm = {}; SDL_SysWMinfo syswm = {};
@ -388,10 +385,7 @@ void D3D11HostDisplay::RenderDisplay()
if (!m_display_texture_handle) if (!m_display_texture_handle)
return; return;
// - 20 for main menu padding const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
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;
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);
@ -400,11 +394,11 @@ void D3D11HostDisplay::RenderDisplay()
m_context->PSSetSamplers( m_context->PSSetSamplers(
0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf()); 0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf());
const float uniforms[4] = {static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width), const float uniforms[4] = {
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height), static_cast<float>(m_display_texture_rect.left) / static_cast<float>(m_display_texture_width),
(static_cast<float>(m_display_width) - 0.5f) / static_cast<float>(m_display_texture_width), static_cast<float>(m_display_texture_rect.top) / static_cast<float>(m_display_texture_height),
(static_cast<float>(m_display_height) - 0.5f) / (static_cast<float>(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
static_cast<float>(m_display_texture_height)}; (static_cast<float>(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast<float>(m_display_texture_height)};
const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms)); const auto map = m_display_uniform_buffer.Map(m_context.Get(), sizeof(uniforms), sizeof(uniforms));
std::memcpy(map.pointer, uniforms, sizeof(uniforms)); std::memcpy(map.pointer, uniforms, sizeof(uniforms));
m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms)); m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms));

View File

@ -25,6 +25,7 @@ public:
void* GetRenderWindow() const override; void* GetRenderWindow() const override;
void ChangeRenderWindow(void* new_window) override; void ChangeRenderWindow(void* new_window) override;
void WindowResized(s32 new_window_width, s32 new_window_height) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
bool dynamic) override; bool dynamic) override;
@ -33,9 +34,6 @@ public:
void SetVSync(bool enabled) override; void SetVSync(bool enabled) override;
std::tuple<u32, u32> GetWindowSize() const override;
void WindowResized() override;
private: private:
static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16;
@ -49,8 +47,6 @@ private:
SDL_Window* m_window = nullptr; SDL_Window* m_window = nullptr;
SDL_GLContext m_gl_context = nullptr; SDL_GLContext m_gl_context = nullptr;
int m_window_width = 0;
int m_window_height = 0;
ComPtr<ID3D11Device> m_device; ComPtr<ID3D11Device> m_device;
ComPtr<ID3D11DeviceContext> m_context; ComPtr<ID3D11DeviceContext> m_context;

View File

@ -77,7 +77,7 @@ int main(int argc, char* argv[])
Log::SetFilterLevel(level); Log::SetFilterLevel(level);
#else #else
Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); 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 SPU Pad DigitalController", LOGLEVEL_DEBUG);
// Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU // Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU
// MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE); // MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE);

View File

@ -100,6 +100,14 @@ void OpenGLHostDisplay::ChangeRenderWindow(void* new_window)
Panic("Not implemented"); 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<float>(m_window_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_height);
}
std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data,
u32 data_stride, bool dynamic) u32 data_stride, bool dynamic)
{ {
@ -131,18 +139,6 @@ void OpenGLHostDisplay::SetVSync(bool enabled)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
} }
std::tuple<u32, u32> OpenGLHostDisplay::GetWindowSize() const
{
return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(m_window_height));
}
void OpenGLHostDisplay::WindowResized()
{
SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height);
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_height);
}
const char* OpenGLHostDisplay::GetGLSLVersionString() const const char* OpenGLHostDisplay::GetGLSLVersionString() const
{ {
if (m_is_gles) if (m_is_gles)
@ -380,11 +376,9 @@ void OpenGLHostDisplay::RenderDisplay()
if (!m_display_texture_handle) if (!m_display_texture_handle)
return; return;
// - 20 for main menu padding const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
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);
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_BLEND);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@ -392,10 +386,10 @@ void OpenGLHostDisplay::RenderDisplay()
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
m_display_program.Bind(); m_display_program.Bind();
m_display_program.Uniform4f( m_display_program.Uniform4f(
0, static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width), 0, static_cast<float>(m_display_texture_rect.left) / static_cast<float>(m_display_texture_width),
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height), static_cast<float>(m_display_texture_rect.top) / static_cast<float>(m_display_texture_height),
(static_cast<float>(m_display_width) - 0.5f) / static_cast<float>(m_display_texture_width), (static_cast<float>(m_display_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
(static_cast<float>(m_display_height) - 0.5f) / static_cast<float>(m_display_texture_height)); (static_cast<float>(m_display_texture_rect.GetHeight()) - 0.5f) / static_cast<float>(m_display_texture_height));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(m_display_texture_handle))); glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(m_display_texture_handle)));
glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler);
glBindVertexArray(m_display_vao); glBindVertexArray(m_display_vao);

View File

@ -20,6 +20,7 @@ public:
void* GetRenderWindow() const override; void* GetRenderWindow() const override;
void ChangeRenderWindow(void* new_window) override; void ChangeRenderWindow(void* new_window) override;
void WindowResized(s32 new_window_width, s32 new_window_height) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
bool dynamic) override; bool dynamic) override;
@ -28,9 +29,6 @@ public:
void SetVSync(bool enabled) override; void SetVSync(bool enabled) override;
std::tuple<u32, u32> GetWindowSize() const override;
void WindowResized() override;
private: private:
const char* GetGLSLVersionString() const; const char* GetGLSLVersionString() const;
std::string GetGLSLVersionHeader() const; std::string GetGLSLVersionHeader() const;
@ -44,8 +42,6 @@ private:
SDL_Window* m_window = nullptr; SDL_Window* m_window = nullptr;
SDL_GLContext m_gl_context = nullptr; SDL_GLContext m_gl_context = nullptr;
int m_window_width = 0;
int m_window_height = 0;
GL::Program m_display_program; GL::Program m_display_program;
GLuint m_display_vao = 0; GLuint m_display_vao = 0;

View File

@ -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. // We set the margin only in windowed mode, the menu bar is drawn on top in fullscreen.
m_display->SetDisplayTopMargin(enabled ? 0 : static_cast<int>(20.0f * ImGui::GetIO().DisplayFramebufferScale.x)); m_display->SetDisplayTopMargin(enabled ? 0 : static_cast<int>(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; m_fullscreen = enabled;
} }
@ -371,7 +373,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
{ {
if (event->window.event == SDL_WINDOWEVENT_RESIZED) if (event->window.event == SDL_WINDOWEVENT_RESIZED)
{ {
m_display->WindowResized(); m_display->WindowResized(event->window.data1, event->window.data2);
UpdateFramebufferScale(); UpdateFramebufferScale();
} }
else if (event->window.event == SDL_WINDOWEVENT_MOVED) else if (event->window.event == SDL_WINDOWEVENT_MOVED)
@ -1221,6 +1223,22 @@ void SDLHostInterface::DrawSettingsWindow()
if (DrawSettingsSectionHeader("Display Output")) if (DrawSettingsSectionHeader("Display Output"))
{ {
ImGui::Text("Crop:");
ImGui::SameLine(indent);
int display_crop_mode = static_cast<int>(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<DisplayCropMode>(index));
return true;
},
nullptr, static_cast<int>(DisplayCropMode::Count)))
{
m_settings_copy.display_crop_mode = static_cast<DisplayCropMode>(display_crop_mode);
settings_changed = true;
}
if (ImGui::Checkbox("Start Fullscreen", &m_settings_copy.display_fullscreen)) if (ImGui::Checkbox("Start Fullscreen", &m_settings_copy.display_fullscreen))
settings_changed = true; 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("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("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(); ImGui::EndTabItem();