GPU: Implement "Crop Mode" (none, overscan, all borders)
This commit is contained in:
parent
5df7fbd68c
commit
fcc0ae9571
151
src/core/gpu.cpp
151
src/core/gpu.cpp
|
@ -3,6 +3,7 @@
|
|||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "dma.h"
|
||||
#include "host_display.h"
|
||||
#include "host_interface.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "stb_image_write.h"
|
||||
|
@ -26,7 +27,7 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr
|
|||
m_dma = dma;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_timers = timers;
|
||||
m_force_progressive_scan = m_system->GetSettings().gpu_force_progressive_scan;
|
||||
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
|
||||
m_tick_event =
|
||||
m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true);
|
||||
return true;
|
||||
|
@ -34,7 +35,8 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr
|
|||
|
||||
void GPU::UpdateSettings()
|
||||
{
|
||||
m_force_progressive_scan = m_system->GetSettings().gpu_force_progressive_scan;
|
||||
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
|
||||
UpdateCRTCConfig();
|
||||
}
|
||||
|
||||
void GPU::Reset()
|
||||
|
@ -108,8 +110,12 @@ bool GPU::DoState(StateWrapper& sw)
|
|||
sw.Do(&m_crtc_state.regs.horizontal_display_range);
|
||||
sw.Do(&m_crtc_state.regs.vertical_display_range);
|
||||
sw.Do(&m_crtc_state.dot_clock_divider);
|
||||
sw.Do(&m_crtc_state.display_width);
|
||||
sw.Do(&m_crtc_state.display_height);
|
||||
sw.Do(&m_crtc_state.visible_display_width);
|
||||
sw.Do(&m_crtc_state.visible_display_height);
|
||||
sw.Do(&m_crtc_state.active_display_left);
|
||||
sw.Do(&m_crtc_state.active_display_top);
|
||||
sw.Do(&m_crtc_state.active_display_width);
|
||||
sw.Do(&m_crtc_state.active_display_height);
|
||||
sw.Do(&m_crtc_state.horizontal_total);
|
||||
sw.Do(&m_crtc_state.horizontal_display_start);
|
||||
sw.Do(&m_crtc_state.horizontal_display_end);
|
||||
|
@ -161,6 +167,7 @@ bool GPU::DoState(StateWrapper& sw)
|
|||
// Restore mask setting.
|
||||
m_GPUSTAT.bits = old_GPUSTAT;
|
||||
|
||||
UpdateCRTCConfig();
|
||||
UpdateDisplay();
|
||||
UpdateSliceTicks();
|
||||
}
|
||||
|
@ -285,8 +292,9 @@ void GPU::Synchronize()
|
|||
|
||||
void GPU::UpdateCRTCConfig()
|
||||
{
|
||||
static constexpr std::array<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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"}};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -49,6 +49,14 @@ enum class GPURenderer : u8
|
|||
Count
|
||||
};
|
||||
|
||||
enum class DisplayCropMode : u8
|
||||
{
|
||||
None,
|
||||
Overscan,
|
||||
Borders,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class AudioBackend : u8
|
||||
{
|
||||
Null,
|
||||
|
|
|
@ -98,6 +98,25 @@ void D3D11DisplayWindow::ChangeRenderWindow(void* new_window)
|
|||
Panic("Not supported");
|
||||
}
|
||||
|
||||
void D3D11DisplayWindow::WindowResized(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
QtDisplayWindow::WindowResized(new_window_width, new_window_height);
|
||||
HostDisplay::WindowResized(new_window_width, new_window_height);
|
||||
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||
m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||
if (FAILED(hr))
|
||||
Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr);
|
||||
|
||||
if (!createSwapChainRTV())
|
||||
Panic("Failed to recreate swap chain RTV after resize");
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> D3D11DisplayWindow::CreateTexture(u32 width, u32 height, const void* data,
|
||||
u32 data_stride, bool dynamic)
|
||||
{
|
||||
|
@ -145,32 +164,6 @@ void D3D11DisplayWindow::SetVSync(bool enabled)
|
|||
m_vsync = enabled;
|
||||
}
|
||||
|
||||
std::tuple<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
|
||||
{
|
||||
return static_cast<bool>(m_device);
|
||||
|
@ -415,10 +408,7 @@ void D3D11DisplayWindow::renderDisplay()
|
|||
if (!m_display_texture_handle)
|
||||
return;
|
||||
|
||||
// - 20 for main menu padding
|
||||
auto [vp_left, vp_top, vp_width, vp_height] =
|
||||
CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio);
|
||||
vp_top += m_display_top_margin;
|
||||
auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
|
||||
|
||||
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);
|
||||
|
@ -427,11 +417,11 @@ void D3D11DisplayWindow::renderDisplay()
|
|||
m_context->PSSetSamplers(
|
||||
0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf());
|
||||
|
||||
const float uniforms[4] = {static_cast<float>(m_display_offset_x) / 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_width) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(static_cast<float>(m_display_height) - 0.5f) /
|
||||
static_cast<float>(m_display_texture_height)};
|
||||
const float uniforms[4] = {
|
||||
static_cast<float>(m_display_texture_rect.left) / 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_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(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));
|
||||
std::memcpy(map.pointer, uniforms, sizeof(uniforms));
|
||||
m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms));
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
void* GetRenderWindow() const override;
|
||||
|
||||
void ChangeRenderWindow(void* new_window) override;
|
||||
void WindowResized(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||
bool dynamic) override;
|
||||
|
@ -40,13 +41,8 @@ public:
|
|||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
std::tuple<u32, u32> GetWindowSize() const override;
|
||||
void WindowResized() override;
|
||||
|
||||
void Render() override;
|
||||
|
||||
void onWindowResized(int width, int height) override;
|
||||
|
||||
private:
|
||||
static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16;
|
||||
|
||||
|
|
|
@ -11,15 +11,18 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
|
|||
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.renderer, "GPU/Renderer",
|
||||
&Settings::ParseRendererName, &Settings::GetRendererName);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.fullscreen, "Display/Fullscreen");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.useDebugDevice, "GPU/UseDebugDevice");
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cropMode, "Display/CropMode",
|
||||
&Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceProgressiveScan,
|
||||
"Display/ForceProgressiveScan");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.displayLinearFiltering,
|
||||
"Display/LinearFiltering");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.startFullscreen, "Display/Fullscreen");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.vsync, "Display/VSync");
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.resolutionScale, "GPU/ResolutionScale");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.trueColor, "GPU/TrueColor");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.linearTextureFiltering, "GPU/TextureFiltering");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceProgressiveScan, "GPU/ForceProgressiveScan");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.useDebugDevice, "GPU/UseDebugDevice");
|
||||
}
|
||||
|
||||
GPUSettingsWidget::~GPUSettingsWidget() = default;
|
||||
|
@ -29,6 +32,12 @@ void GPUSettingsWidget::setupAdditionalUi()
|
|||
for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); 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"));
|
||||
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));
|
||||
|
|
|
@ -42,31 +42,57 @@
|
|||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="renderer"/>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="useDebugDevice">
|
||||
<property name="text">
|
||||
<string>Use Debug Device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<property name="text">
|
||||
<string>Linear Upscaling</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="vsync">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="startFullscreen">
|
||||
<property name="text">
|
||||
<string>VSync</string>
|
||||
<string>Start Fullscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="fullscreen">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="vsync">
|
||||
<property name="text">
|
||||
<string>Fullscreen</string>
|
||||
<string>VSync</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -103,13 +129,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -134,6 +134,12 @@ void OpenGLDisplayWindow::ChangeRenderWindow(void* new_window)
|
|||
Panic("Not implemented");
|
||||
}
|
||||
|
||||
void OpenGLDisplayWindow::WindowResized(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
QtDisplayWindow::WindowResized(new_window_width, new_window_height);
|
||||
HostDisplay::WindowResized(new_window_width, new_window_height);
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> OpenGLDisplayWindow::CreateTexture(u32 width, u32 height, const void* data,
|
||||
u32 data_stride, bool dynamic)
|
||||
{
|
||||
|
@ -165,13 +171,6 @@ void OpenGLDisplayWindow::SetVSync(bool enabled)
|
|||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
}
|
||||
|
||||
std::tuple<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
|
||||
{
|
||||
if (m_gl_context->isOpenGLES())
|
||||
|
@ -466,9 +465,7 @@ void OpenGLDisplayWindow::renderDisplay()
|
|||
if (!m_display_texture_handle)
|
||||
return;
|
||||
|
||||
// - 20 for main menu padding
|
||||
const auto [vp_left, vp_top, vp_width, vp_height] =
|
||||
CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio);
|
||||
const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
|
||||
|
||||
glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height);
|
||||
glDisable(GL_BLEND);
|
||||
|
@ -478,10 +475,10 @@ void OpenGLDisplayWindow::renderDisplay()
|
|||
glDepthMask(GL_FALSE);
|
||||
m_display_program.Bind();
|
||||
m_display_program.Uniform4f(
|
||||
0, static_cast<float>(m_display_offset_x) / 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_width) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(static_cast<float>(m_display_height) - 0.5f) / static_cast<float>(m_display_texture_height));
|
||||
0, static_cast<float>(m_display_texture_rect.left) / 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_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(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)));
|
||||
glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler);
|
||||
glBindVertexArray(m_display_vao);
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
void* GetRenderWindow() const override;
|
||||
|
||||
void ChangeRenderWindow(void* new_window) override;
|
||||
void WindowResized(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||
bool dynamic) override;
|
||||
|
@ -46,9 +47,6 @@ public:
|
|||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
std::tuple<u32, u32> GetWindowSize() const override;
|
||||
void WindowResized() override;
|
||||
|
||||
void Render() override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -28,9 +28,6 @@ bool QtDisplayWindow::createDeviceContext(QThread* worker_thread, bool debug_dev
|
|||
|
||||
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())
|
||||
return false;
|
||||
|
||||
|
@ -49,8 +46,8 @@ bool QtDisplayWindow::createImGuiContext()
|
|||
|
||||
auto& io = ImGui::GetIO();
|
||||
io.IniFilename = nullptr;
|
||||
io.DisplaySize.x = static_cast<float>(m_window_width);
|
||||
io.DisplaySize.y = static_cast<float>(m_window_height);
|
||||
io.DisplaySize.x = static_cast<float>(getScaledWindowWidth());
|
||||
io.DisplaySize.y = static_cast<float>(getScaledWindowHeight());
|
||||
|
||||
const float framebuffer_scale = static_cast<float>(devicePixelRatio());
|
||||
io.DisplayFramebufferScale.x = framebuffer_scale;
|
||||
|
@ -73,18 +70,15 @@ bool QtDisplayWindow::createDeviceResources()
|
|||
|
||||
void QtDisplayWindow::destroyDeviceResources() {}
|
||||
|
||||
void QtDisplayWindow::onWindowResized(int width, int height)
|
||||
void QtDisplayWindow::WindowResized(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
m_window_width = width;
|
||||
m_window_height = height;
|
||||
|
||||
// imgui may not have been initialized yet
|
||||
if (!ImGui::GetCurrentContext())
|
||||
return;
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
io.DisplaySize.x = static_cast<float>(width);
|
||||
io.DisplaySize.y = static_cast<float>(height);
|
||||
io.DisplaySize.x = static_cast<float>(new_window_width);
|
||||
io.DisplaySize.y = static_cast<float>(new_window_height);
|
||||
}
|
||||
|
||||
void QtDisplayWindow::keyPressEvent(QKeyEvent* event)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <QtGui/QWindow>
|
||||
#include "common/types.h"
|
||||
|
||||
class QKeyEvent;
|
||||
class QResizeEvent;
|
||||
|
@ -26,12 +27,15 @@ public:
|
|||
virtual void Render() = 0;
|
||||
|
||||
// this comes back on the emu thread
|
||||
virtual void onWindowResized(int width, int height);
|
||||
virtual void WindowResized(s32 new_window_width, s32 new_window_height);
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowResizedEvent(int width, int height);
|
||||
|
||||
protected:
|
||||
int getScaledWindowWidth() const { return static_cast<int>(static_cast<qreal>(width()) * devicePixelRatio()); }
|
||||
int getScaledWindowHeight() const { return static_cast<int>(static_cast<qreal>(height()) * devicePixelRatio()); }
|
||||
|
||||
virtual bool createImGuiContext();
|
||||
virtual void destroyImGuiContext();
|
||||
virtual bool createDeviceResources();
|
||||
|
@ -42,7 +46,4 @@ protected:
|
|||
virtual void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
QtHostInterface* m_host_interface;
|
||||
|
||||
int m_window_width = 0;
|
||||
int m_window_height = 0;
|
||||
};
|
||||
|
|
|
@ -201,7 +201,7 @@ void QtHostInterface::onDisplayWindowResized(int width, int height)
|
|||
{
|
||||
// this can be null if it was destroyed and the main thread is late catching up
|
||||
if (m_display_window)
|
||||
m_display_window->onWindowResized(width, height);
|
||||
m_display_window->WindowResized(width, height);
|
||||
}
|
||||
|
||||
bool QtHostInterface::AcquireHostDisplay()
|
||||
|
|
|
@ -102,6 +102,30 @@ void D3D11HostDisplay::ChangeRenderWindow(void* new_window)
|
|||
Panic("Not supported");
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
HostDisplay::WindowResized(new_window_width, new_window_height);
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||
m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
|
||||
if (FAILED(hr))
|
||||
Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr);
|
||||
|
||||
if (!CreateSwapChainRTV())
|
||||
Panic("Failed to recreate swap chain RTV after resize");
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)))
|
||||
{
|
||||
m_window_width = static_cast<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,
|
||||
u32 data_stride, bool dynamic)
|
||||
{
|
||||
|
@ -149,33 +173,6 @@ void D3D11HostDisplay::SetVSync(bool 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)
|
||||
{
|
||||
SDL_SysWMinfo syswm = {};
|
||||
|
@ -388,10 +385,7 @@ void D3D11HostDisplay::RenderDisplay()
|
|||
if (!m_display_texture_handle)
|
||||
return;
|
||||
|
||||
// - 20 for main menu padding
|
||||
auto [vp_left, vp_top, vp_width, vp_height] =
|
||||
CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio);
|
||||
vp_top += m_display_top_margin;
|
||||
const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
|
||||
|
||||
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);
|
||||
|
@ -400,11 +394,11 @@ void D3D11HostDisplay::RenderDisplay()
|
|||
m_context->PSSetSamplers(
|
||||
0, 1, m_display_linear_filtering ? m_linear_sampler.GetAddressOf() : m_point_sampler.GetAddressOf());
|
||||
|
||||
const float uniforms[4] = {static_cast<float>(m_display_offset_x) / 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_width) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(static_cast<float>(m_display_height) - 0.5f) /
|
||||
static_cast<float>(m_display_texture_height)};
|
||||
const float uniforms[4] = {
|
||||
static_cast<float>(m_display_texture_rect.left) / 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_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(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));
|
||||
std::memcpy(map.pointer, uniforms, sizeof(uniforms));
|
||||
m_display_uniform_buffer.Unmap(m_context.Get(), sizeof(uniforms));
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
void* GetRenderWindow() const override;
|
||||
|
||||
void ChangeRenderWindow(void* new_window) override;
|
||||
void WindowResized(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||
bool dynamic) override;
|
||||
|
@ -33,9 +34,6 @@ public:
|
|||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
std::tuple<u32, u32> GetWindowSize() const override;
|
||||
void WindowResized() override;
|
||||
|
||||
private:
|
||||
static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16;
|
||||
|
||||
|
@ -49,8 +47,6 @@ private:
|
|||
|
||||
SDL_Window* m_window = nullptr;
|
||||
SDL_GLContext m_gl_context = nullptr;
|
||||
int m_window_width = 0;
|
||||
int m_window_height = 0;
|
||||
|
||||
ComPtr<ID3D11Device> m_device;
|
||||
ComPtr<ID3D11DeviceContext> m_context;
|
||||
|
|
|
@ -77,7 +77,7 @@ int main(int argc, char* argv[])
|
|||
Log::SetFilterLevel(level);
|
||||
#else
|
||||
Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
|
||||
// Log::SetConsoleOutputParams(true, "Pad DigitalController MemoryCard", LOGLEVEL_DEBUG);
|
||||
Log::SetConsoleOutputParams(true, "Pad DigitalController MemoryCard SPU", LOGLEVEL_DEBUG);
|
||||
// Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG);
|
||||
// Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU
|
||||
// MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE);
|
||||
|
|
|
@ -100,6 +100,14 @@ void OpenGLHostDisplay::ChangeRenderWindow(void* new_window)
|
|||
Panic("Not implemented");
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
HostDisplay::WindowResized(new_window_width, new_window_height);
|
||||
SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height);
|
||||
ImGui::GetIO().DisplaySize.x = static_cast<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,
|
||||
u32 data_stride, bool dynamic)
|
||||
{
|
||||
|
@ -131,18 +139,6 @@ void OpenGLHostDisplay::SetVSync(bool enabled)
|
|||
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
|
||||
{
|
||||
if (m_is_gles)
|
||||
|
@ -380,11 +376,9 @@ void OpenGLHostDisplay::RenderDisplay()
|
|||
if (!m_display_texture_handle)
|
||||
return;
|
||||
|
||||
// - 20 for main menu padding
|
||||
const auto [vp_left, vp_top, vp_width, vp_height] =
|
||||
CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio);
|
||||
const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
|
||||
|
||||
glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height);
|
||||
glViewport(vp_left, m_window_height - vp_top - vp_height, vp_width, vp_height);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
@ -392,10 +386,10 @@ void OpenGLHostDisplay::RenderDisplay()
|
|||
glDepthMask(GL_FALSE);
|
||||
m_display_program.Bind();
|
||||
m_display_program.Uniform4f(
|
||||
0, static_cast<float>(m_display_offset_x) / 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_width) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(static_cast<float>(m_display_height) - 0.5f) / static_cast<float>(m_display_texture_height));
|
||||
0, static_cast<float>(m_display_texture_rect.left) / 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_texture_rect.GetWidth()) - 0.5f) / static_cast<float>(m_display_texture_width),
|
||||
(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)));
|
||||
glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler);
|
||||
glBindVertexArray(m_display_vao);
|
||||
|
|
|
@ -20,6 +20,7 @@ public:
|
|||
void* GetRenderWindow() const override;
|
||||
|
||||
void ChangeRenderWindow(void* new_window) override;
|
||||
void WindowResized(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||
bool dynamic) override;
|
||||
|
@ -28,9 +29,6 @@ public:
|
|||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
std::tuple<u32, u32> GetWindowSize() const override;
|
||||
void WindowResized() override;
|
||||
|
||||
private:
|
||||
const char* GetGLSLVersionString() const;
|
||||
std::string GetGLSLVersionHeader() const;
|
||||
|
@ -44,8 +42,6 @@ private:
|
|||
|
||||
SDL_Window* m_window = nullptr;
|
||||
SDL_GLContext m_gl_context = nullptr;
|
||||
int m_window_width = 0;
|
||||
int m_window_height = 0;
|
||||
|
||||
GL::Program m_display_program;
|
||||
GLuint m_display_vao = 0;
|
||||
|
|
|
@ -287,7 +287,9 @@ void SDLHostInterface::SetFullscreen(bool enabled)
|
|||
// We set the margin only in windowed mode, the menu bar is drawn on top in fullscreen.
|
||||
m_display->SetDisplayTopMargin(enabled ? 0 : static_cast<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;
|
||||
}
|
||||
|
||||
|
@ -371,7 +373,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
{
|
||||
if (event->window.event == SDL_WINDOWEVENT_RESIZED)
|
||||
{
|
||||
m_display->WindowResized();
|
||||
m_display->WindowResized(event->window.data1, event->window.data2);
|
||||
UpdateFramebufferScale();
|
||||
}
|
||||
else if (event->window.event == SDL_WINDOWEVENT_MOVED)
|
||||
|
@ -1221,6 +1223,22 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
|
||||
if (DrawSettingsSectionHeader("Display Output"))
|
||||
{
|
||||
ImGui::Text("Crop:");
|
||||
ImGui::SameLine(indent);
|
||||
|
||||
int display_crop_mode = static_cast<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))
|
||||
settings_changed = true;
|
||||
|
||||
|
@ -1265,7 +1283,7 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
|
||||
settings_changed |= ImGui::Checkbox("True 24-bit Color (disables dithering)", &m_settings_copy.gpu_true_color);
|
||||
settings_changed |= ImGui::Checkbox("Texture Filtering", &m_settings_copy.gpu_texture_filtering);
|
||||
settings_changed |= ImGui::Checkbox("Force Progressive Scan", &m_settings_copy.gpu_force_progressive_scan);
|
||||
settings_changed |= ImGui::Checkbox("Force Progressive Scan", &m_settings_copy.display_force_progressive_scan);
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
|
|
Loading…
Reference in New Issue