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/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<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;
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<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(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<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));
// 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<u32>((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<u16>((cs.regs.X2 - cs.regs.X1) / cs.dot_clock_divider, 1, VRAM_WIDTH);
cs.active_display_height =
std::clamp<u16>((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<u16>(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<float>(MASTER_CLOCK) * (11.0f / 7.0f / static_cast<float>(cs.dot_clock_divider)));
const float dot_clock_period = 1.0f / dot_clock;
const float dots_per_scanline = static_cast<float>(cs.horizontal_total) / static_cast<float>(cs.dot_clock_divider);
const float horizontal_period = dots_per_scanline * dot_clock_period;
const float vertical_period = horizontal_period * static_cast<float>(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<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;
// Convert active dots/lines to time.
const float visible_dots_per_scanline = static_cast<float>(cs.display_width);
const float horizontal_active_time = horizontal_period * visible_dots_per_scanline;
const float vertical_active_time = horizontal_active_time * static_cast<float>(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<float>(MASTER_CLOCK) * (11.0f / 7.0f / static_cast<float>(cs.dot_clock_divider)));
const float dot_clock_period = 1.0f / dot_clock;
const float dots_per_scanline = static_cast<float>(cs.horizontal_total) / static_cast<float>(cs.dot_clock_divider);
const float horizontal_period = dots_per_scanline * dot_clock_period;
const float vertical_period = horizontal_period * static_cast<float>(cs.vertical_total);
// Convert active dots/lines to time.
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 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
// 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,

View File

@ -496,28 +496,32 @@ protected:
union
{
u32 display_address_start;
BitField<u32, u32, 0, 10> X;
BitField<u32, u32, 10, 9> Y;
BitField<u32, u16, 0, 10> X;
BitField<u32, u16, 10, 9> Y;
};
union
{
u32 horizontal_display_range;
BitField<u32, u32, 0, 12> X1;
BitField<u32, u32, 12, 12> X2;
BitField<u32, u16, 0, 12> X1;
BitField<u32, u16, 12, 12> X2;
};
union
{
u32 vertical_display_range;
BitField<u32, u32, 0, 10> Y1;
BitField<u32, u32, 10, 10> Y2;
BitField<u32, u16, 0, 10> Y1;
BitField<u32, u16, 10, 10> 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<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 = {};
State m_state = State::Idle;

View File

@ -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<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
{
@ -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<u32>(m_crtc_state.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_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.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<s32>::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<s32>(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<s32>(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);
}
}

View File

@ -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<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();
return true;
}
@ -479,10 +476,12 @@ void GPU_HW_OpenGL::UpdateDisplay()
if (m_system->GetSettings().debugging.show_vram)
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), 0,
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(),
-static_cast<s32>(m_vram_texture.GetHeight()), m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), 1.0f);
m_host_display->SetDisplayTexture(
reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
-static_cast<s32>(m_vram_texture.GetHeight()),
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
{
@ -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<u32>(m_crtc_state.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_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.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<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y,
scaled_display_width, -static_cast<s32>(scaled_display_height),
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
m_crtc_state.display_aspect_ratio);
m_host_display->SetDisplayTexture(
reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(),
Common::Rectangle<s32>(scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y,
scaled_display_width, -static_cast<s32>(scaled_display_height)));
}
else
{
@ -545,10 +544,10 @@ void GPU_HW_OpenGL::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
0, display_height, display_width, -static_cast<s32>(display_height),
m_display_texture.GetWidth(), m_display_texture.GetHeight(),
m_crtc_state.display_aspect_ratio);
m_host_display->SetDisplayTexture(
reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_display_texture.GetWidth(),
m_display_texture.GetHeight(),
Common::Rectangle<s32>(0, display_height, display_width, -static_cast<s32>(display_height)));
}
else
{
@ -564,9 +563,9 @@ void GPU_HW_OpenGL::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
0, scaled_display_height, scaled_display_width,
-static_cast<s32>(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<s32>(0, scaled_display_height, scaled_display_width,
-static_cast<s32>(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);
}
}

View File

@ -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<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();
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<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), 0,
m_vram_texture.GetHeight(), m_vram_texture.GetWidth(),
-static_cast<s32>(m_vram_texture.GetHeight()), m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), 1.0f);
m_host_display->SetDisplayTexture(
reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
-static_cast<s32>(m_vram_texture.GetHeight()),
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
{
@ -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<u32>(m_crtc_state.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_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.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<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y,
scaled_display_width, -static_cast<s32>(scaled_display_height),
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
m_crtc_state.display_aspect_ratio);
m_host_display->SetDisplayTexture(
reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())), m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(),
Common::Rectangle<s32>(scaled_vram_offset_x, m_vram_texture.GetHeight() - scaled_vram_offset_y,
scaled_display_width, -static_cast<s32>(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<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
0, display_height, display_width, -static_cast<s32>(display_height),
m_display_texture.GetWidth(), m_display_texture.GetHeight(),
m_crtc_state.display_aspect_ratio);
m_host_display->SetDisplayTexture(
reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())), m_display_texture.GetWidth(),
m_display_texture.GetHeight(),
Common::Rectangle<s32>(0, display_height, display_width, -static_cast<s32>(display_height)));
}
else
{
@ -431,9 +430,9 @@ void GPU_HW_OpenGL_ES::UpdateDisplay()
glDrawArrays(GL_TRIANGLES, 0, 3);
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
0, scaled_display_height, scaled_display_width,
-static_cast<s32>(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<s32>(0, scaled_display_height, scaled_display_width,
-static_cast<s32>(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);
}
}

View File

@ -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<u32>(m_crtc_state.display_width, VRAM_WIDTH - vram_offset_x);
display_height = std::min<u32>(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<u32>(m_crtc_state.active_display_width, VRAM_WIDTH - vram_offset_x);
const u32 display_height = std::min<u32>(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<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
{
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<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);
}
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)

View File

@ -4,24 +4,45 @@ HostDisplayTexture::~HostDisplayTexture() = 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;
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);
scale = static_cast<float>(window_height) / static_cast<float>(m_display_height);
left = (window_width - width) / 2;
top = 0;
}
else
{
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;
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);
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "common/rectangle.h"
#include "types.h"
#include <memory>
#include <tuple>
@ -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<HostDisplayTexture> 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<u32, u32> 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<s32>& 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<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 SetDisplayTopMargin(s32 height) { m_display_top_margin = height; }
// 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:
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<s32> 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<s32> m_display_texture_rect{};
s32 m_display_top_margin = 0;
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.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<void()>& 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<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 ||
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();
}

View File

@ -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;

View File

@ -24,9 +24,12 @@ void Settings::Load(SettingsInterface& si)
gpu_resolution_scale = static_cast<u32>(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<long>(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<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_display_names = {{"Null (No Output)", "Cubeb", "SDL"}};

View File

@ -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<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 const char* GetAudioBackendName(AudioBackend backend);
static const char* GetAudioBackendDisplayName(AudioBackend backend);

View File

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