Skip to content

Commit

Permalink
GPU: Add scaling shader support
Browse files Browse the repository at this point in the history
Currently only Bilinear (Sharp).
  • Loading branch information
stenzek committed Aug 31, 2023
1 parent 9439cf6 commit 09e7a58
Show file tree
Hide file tree
Showing 23 changed files with 370 additions and 219 deletions.
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -46,6 +46,8 @@ add_library(core
gpu_hw.h
gpu_hw_shadergen.cpp
gpu_hw_shadergen.h
gpu_shadergen.cpp
gpu_shadergen.h
gpu_sw.cpp
gpu_sw.h
gpu_sw_backend.cpp
Expand Down
4 changes: 3 additions & 1 deletion src/core/core.vcxproj
Expand Up @@ -39,6 +39,7 @@
<ClCompile Include="gpu_backend.cpp" />
<ClCompile Include="gpu_commands.cpp" />
<ClCompile Include="gpu_hw_shadergen.cpp" />
<ClCompile Include="gpu_shadergen.cpp" />
<ClCompile Include="gpu_sw.cpp" />
<ClCompile Include="gpu_sw_backend.cpp" />
<ClCompile Include="gte.cpp" />
Expand Down Expand Up @@ -103,6 +104,7 @@
<ClInclude Include="game_list.h" />
<ClInclude Include="gpu_backend.h" />
<ClInclude Include="gpu_hw_shadergen.h" />
<ClInclude Include="gpu_shadergen.h" />
<ClInclude Include="gpu_sw.h" />
<ClInclude Include="gpu_sw_backend.h" />
<ClInclude Include="gpu_types.h" />
Expand Down Expand Up @@ -190,4 +192,4 @@
</ClCompile>
</ItemDefinitionGroup>
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
</Project>
</Project>
4 changes: 3 additions & 1 deletion src/core/core.vcxproj.filters
Expand Up @@ -58,6 +58,7 @@
<ClCompile Include="fullscreen_ui.cpp" />
<ClCompile Include="achievements.cpp" />
<ClCompile Include="hotkeys.cpp" />
<ClCompile Include="gpu_shadergen.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
Expand Down Expand Up @@ -120,5 +121,6 @@
<ClInclude Include="imgui_overlays.h" />
<ClInclude Include="fullscreen_ui.h" />
<ClInclude Include="shader_cache_version.h" />
<ClInclude Include="gpu_shadergen.h" />
</ItemGroup>
</Project>
</Project>
18 changes: 6 additions & 12 deletions src/core/fullscreen_ui.cpp
Expand Up @@ -3883,7 +3883,7 @@ void FullscreenUI::DrawDisplaySettingsPage()
DrawEnumSetting(
bsi, FSUI_CSTR("Position"), FSUI_CSTR("Determines the position on the screen when black borders must be added."),
"Display", "Alignment", Settings::DEFAULT_DISPLAY_ALIGNMENT, &Settings::ParseDisplayAlignment,
&Settings::GetDisplayAlignmentDisplayName, &Settings::GetDisplayAlignmentDisplayName, DisplayAlignment::Count);
&Settings::GetDisplayAlignmentName, &Settings::GetDisplayAlignmentDisplayName, DisplayAlignment::Count);

DrawEnumSetting(bsi, FSUI_CSTR("Downsampling"),
FSUI_CSTR("Downsamples the rendered image prior to displaying it. Can improve "
Expand All @@ -3892,17 +3892,11 @@ void FullscreenUI::DrawDisplaySettingsPage()
&Settings::GetDownsampleModeName, &Settings::GetDownsampleModeDisplayName, GPUDownsampleMode::Count,
(renderer != GPURenderer::Software));

DrawToggleSetting(bsi, FSUI_CSTR("Linear Upscaling"),
FSUI_CSTR("Uses a bilinear filter when upscaling to display, smoothing out the image."), "Display",
"LinearFiltering", true);

DrawToggleSetting(bsi, FSUI_CSTR("Integer Upscaling"),
FSUI_CSTR("Adds padding to ensure pixels are a whole number in size."), "Display", "IntegerScaling",
false);

DrawToggleSetting(bsi, FSUI_CSTR("Stretch To Fit"),
FSUI_CSTR("Fills the window with the active display area, regardless of the aspect ratio."),
"Display", "Stretch", false);
DrawEnumSetting(
bsi, FSUI_CSTR("Scaling"),
FSUI_CSTR("Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution."),
"Display", "Scaling", Settings::DEFAULT_DISPLAY_SCALING, &Settings::ParseDisplayScaling,
&Settings::GetDisplayScalingName, &Settings::GetDisplayScalingDisplayName, DisplayScalingMode::Count);

DrawToggleSetting(bsi, FSUI_CSTR("Internal Resolution Screenshots"),
FSUI_CSTR("Saves screenshots at internal render resolution and without postprocessing."), "Display",
Expand Down
146 changes: 104 additions & 42 deletions src/core/gpu.cpp
@@ -1,8 +1,9 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)

#include "gpu.h"
#include "dma.h"
#include "gpu_shadergen.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
Expand Down Expand Up @@ -59,7 +60,7 @@ bool GPU::Initialize()
m_console_is_pal = System::IsPALRegion();
UpdateCRTCConfig();

if (!CompilePipelines())
if (!CompileDisplayPipeline())
{
Host::ReportErrorAsync("Error", "Failed to compile base GPU pipelines.");
return false;
Expand All @@ -70,7 +71,7 @@ bool GPU::Initialize()
return true;
}

void GPU::UpdateSettings()
void GPU::UpdateSettings(const Settings& old_settings)
{
m_force_progressive_scan = g_settings.gpu_disable_interlacing;
m_fifo_size = g_settings.gpu_fifo_size;
Expand All @@ -86,6 +87,12 @@ void GPU::UpdateSettings()
// Crop mode calls this, so recalculate the display area
UpdateCRTCDisplayParameters();

if (g_settings.display_scaling != old_settings.display_scaling)
{
if (!CompileDisplayPipeline())
Panic("Failed to compile display pipeline on settings change.");
}

g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu);
}

Expand Down Expand Up @@ -1540,9 +1547,9 @@ void GPU::SetTextureWindow(u32 value)
m_draw_mode.texture_window_changed = true;
}

bool GPU::CompilePipelines()
bool GPU::CompileDisplayPipeline()
{
ShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend);
GPUShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend);

GPUPipeline::GraphicsConfig plconfig;
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
Expand All @@ -1556,20 +1563,35 @@ bool GPU::CompilePipelines()
plconfig.samples = 1;
plconfig.per_sample_shading = false;

std::unique_ptr<GPUShader> display_vs =
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader());
std::unique_ptr<GPUShader> display_fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
if (!display_vs || !display_fs)
std::string vs = shadergen.GenerateDisplayVertexShader();
std::string fs;
switch (g_settings.display_scaling)
{
case DisplayScalingMode::BilinearSharp:
fs = shadergen.GenerateDisplaySharpBilinearFragmentShader();
break;

case DisplayScalingMode::Nearest:
case DisplayScalingMode::BilinearSmooth:
case DisplayScalingMode::NearestInteger:
default:
fs = shadergen.GenerateDisplayFragmentShader();
break;
}

std::unique_ptr<GPUShader> vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, vs);
std::unique_ptr<GPUShader> fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, fs);
if (!vso || !fso)
return false;
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
GL_OBJECT_NAME(vso, "Display Vertex Shader");
GL_OBJECT_NAME(fso, "Display Fragment Shader [%s]", Settings::GetDisplayScalingName(g_settings.display_scaling));

plconfig.vertex_shader = display_vs.get();
plconfig.fragment_shader = display_fs.get();
plconfig.vertex_shader = vso.get();
plconfig.fragment_shader = fso.get();
if (!(m_display_pipeline = g_gpu_device->CreatePipeline(plconfig)))
return false;
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline [%s]",
Settings::GetDisplayScalingName(g_settings.display_scaling));

return true;
}
Expand Down Expand Up @@ -1620,17 +1642,56 @@ bool GPU::PresentDisplay()

const Common::Rectangle<s32> draw_rect =
CalculateDrawRect(g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight());
return RenderDisplay(nullptr, draw_rect, g_settings.display_linear_filtering, true);
return RenderDisplay(nullptr, draw_rect, true);
}

bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& draw_rect, bool linear_filter,
bool postfx)
bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& draw_rect, bool postfx)
{
GL_SCOPE("RenderDisplay: %dx%d at %d,%d", draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight());

if (m_display_texture)
m_display_texture->MakeReadyForSampling();

bool texture_filter_linear = false;
bool bilinear_adjust = false;

struct Uniforms
{
float src_rect[4];
float src_size[4];
float params[4];
} uniforms;
std::memset(uniforms.params, 0, sizeof(uniforms.params));

switch (g_settings.display_scaling)
{
case DisplayScalingMode::Nearest:
case DisplayScalingMode::NearestInteger:
break;

case DisplayScalingMode::BilinearSmooth:
texture_filter_linear = true;
bilinear_adjust = true;
break;

case DisplayScalingMode::BilinearSharp:
{
texture_filter_linear = true;
uniforms.params[0] = std::max(
std::floor(static_cast<float>(draw_rect.GetWidth()) / static_cast<float>(m_display_texture_view_width)), 1.0f);
uniforms.params[1] = std::max(
std::floor(static_cast<float>(draw_rect.GetHeight()) / static_cast<float>(m_display_texture_view_height)),
1.0f);
uniforms.params[2] = 0.5f - 0.5f / uniforms.params[0];
uniforms.params[3] = 0.5f - 0.5f / uniforms.params[1];
}
break;

default:
UnreachableCode();
break;
}

const GPUTexture::Format hdformat =
(target && target->GetRT()) ? target->GetRT()->GetFormat() : g_gpu_device->GetWindowFormat();
const u32 target_width = target ? target->GetWidth() : g_gpu_device->GetWindowWidth();
Expand All @@ -1654,29 +1715,30 @@ bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& dr
return true;

g_gpu_device->SetPipeline(m_display_pipeline.get());
g_gpu_device->SetTextureSampler(0, m_display_texture,
linear_filter ? g_gpu_device->GetLinearSampler() : g_gpu_device->GetNearestSampler());

const float position_adjust = linear_filter ? 0.5f : 0.0f;
const float size_adjust = linear_filter ? 1.0f : 0.0f;
const float uniforms[4] = {(static_cast<float>(m_display_texture_view_x) + position_adjust) /
static_cast<float>(m_display_texture->GetWidth()),
(static_cast<float>(m_display_texture_view_y) + position_adjust) /
static_cast<float>(m_display_texture->GetHeight()),
(static_cast<float>(m_display_texture_view_width) - size_adjust) /
static_cast<float>(m_display_texture->GetWidth()),
(static_cast<float>(m_display_texture_view_height) - size_adjust) /
static_cast<float>(m_display_texture->GetHeight())};
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
g_gpu_device->SetTextureSampler(
0, m_display_texture, texture_filter_linear ? g_gpu_device->GetLinearSampler() : g_gpu_device->GetNearestSampler());

const float position_adjust = bilinear_adjust ? 0.5f : 0.0f;
const float size_adjust = bilinear_adjust ? 1.0f : 0.0f;
const float rcp_width = 1.0f / static_cast<float>(m_display_texture->GetWidth());
const float rcp_height = 1.0f / static_cast<float>(m_display_texture->GetHeight());
uniforms.src_rect[0] = (static_cast<float>(m_display_texture_view_x) + position_adjust) * rcp_width;
uniforms.src_rect[1] = (static_cast<float>(m_display_texture_view_y) + position_adjust) * rcp_height;
uniforms.src_rect[2] = (static_cast<float>(m_display_texture_view_width) - size_adjust) * rcp_width;
uniforms.src_rect[3] = (static_cast<float>(m_display_texture_view_height) - size_adjust) * rcp_height;
uniforms.src_size[0] = static_cast<float>(m_display_texture->GetWidth());
uniforms.src_size[1] = static_cast<float>(m_display_texture->GetHeight());
uniforms.src_size[2] = rcp_width;
uniforms.src_size[3] = rcp_height;
g_gpu_device->PushUniformBuffer(&uniforms, sizeof(uniforms));

g_gpu_device->SetViewportAndScissor(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight());
g_gpu_device->Draw(3, 0);

if (really_postfx)
{
return PostProcessing::Apply(target, draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
draw_rect.GetHeight(), m_display_texture_view_width,
m_display_texture_view_height);
return PostProcessing::Apply(target, draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
m_display_texture_view_width, m_display_texture_view_height);
}
else
{
Expand All @@ -1689,10 +1751,9 @@ Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_hei
bool apply_aspect_ratio /* = true */) const
{
const float window_ratio = static_cast<float>(window_width) / static_cast<float>(window_height);
const float display_aspect_ratio = g_settings.display_stretch ? window_ratio : m_display_aspect_ratio;
const float x_scale =
apply_aspect_ratio ?
(display_aspect_ratio / (static_cast<float>(m_display_width) / static_cast<float>(m_display_height))) :
(m_display_aspect_ratio / (static_cast<float>(m_display_width) / static_cast<float>(m_display_height))) :
1.0f;
const float display_width = g_settings.display_stretch_vertically ? static_cast<float>(m_display_width) :
static_cast<float>(m_display_width) * x_scale;
Expand All @@ -1717,12 +1778,12 @@ Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_hei
{
// align in middle vertically
scale = static_cast<float>(window_width) / display_width;
if (g_settings.display_integer_scaling)
if (g_settings.display_scaling == DisplayScalingMode::NearestInteger)
scale = std::max(std::floor(scale), 1.0f);

if (out_left_padding)
{
if (g_settings.display_integer_scaling)
if (g_settings.display_scaling == DisplayScalingMode::NearestInteger)
*out_left_padding = std::max<float>((static_cast<float>(window_width) - display_width * scale) / 2.0f, 0.0f);
else
*out_left_padding = 0.0f;
Expand Down Expand Up @@ -1751,7 +1812,7 @@ Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_hei
{
// align in middle horizontally
scale = static_cast<float>(window_height) / display_height;
if (g_settings.display_integer_scaling)
if (g_settings.display_scaling == DisplayScalingMode::NearestInteger)
scale = std::max(std::floor(scale), 1.0f);

if (out_left_padding)
Expand All @@ -1776,7 +1837,7 @@ Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_hei

if (out_top_padding)
{
if (g_settings.display_integer_scaling)
if (g_settings.display_scaling == DisplayScalingMode::NearestInteger)
*out_top_padding = std::max<float>((static_cast<float>(window_height) - (display_height * scale)) / 2.0f, 0.0f);
else
*out_top_padding = 0.0f;
Expand Down Expand Up @@ -1974,7 +2035,8 @@ bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangl

g_gpu_device->ClearRenderTarget(render_texture.get(), 0);

RenderDisplay(render_fb.get(), draw_rect, g_settings.display_linear_filtering, postfx);
// TODO: this should use copy shader instead.
RenderDisplay(render_fb.get(), draw_rect, postfx);

g_gpu_device->SetFramebuffer(nullptr);

Expand Down
7 changes: 4 additions & 3 deletions src/core/gpu.h
Expand Up @@ -27,6 +27,7 @@ class GPUFramebuffer;
class GPUTexture;
class GPUPipeline;

struct Settings;
class TimingEvent;

namespace Threading {
Expand Down Expand Up @@ -151,7 +152,7 @@ class GPU
void SynchronizeCRTC();

/// Recompile shaders/recreate framebuffers when needed.
virtual void UpdateSettings();
virtual void UpdateSettings(const Settings& old_settings);

/// Updates the resolution scale when it's set to automatic.
virtual void UpdateResolutionScale();
Expand Down Expand Up @@ -582,7 +583,7 @@ class GPU
float* out_top_padding, float* out_scale, float* out_x_scale,
bool apply_aspect_ratio = true) const;

bool RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& draw_rect, bool linear_filter, bool postfx);
bool RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& draw_rect, bool postfx);

s32 m_display_width = 0;
s32 m_display_height = 0;
Expand Down Expand Up @@ -612,7 +613,7 @@ class GPU
Stats m_last_stats = {};

private:
bool CompilePipelines();
bool CompileDisplayPipeline();

using GP0CommandHandler = bool (GPU::*)();
using GP0CommandHandlerTable = std::array<GP0CommandHandler, 256>;
Expand Down

0 comments on commit 09e7a58

Please sign in to comment.