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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -201,7 +201,7 @@ void QtHostInterface::onDisplayWindowResized(int width, int height)
{
// this can be null if it was destroyed and the main thread is late catching up
if (m_display_window)
m_display_window->onWindowResized(width, height);
m_display_window->WindowResized(width, height);
}
bool QtHostInterface::AcquireHostDisplay()