Skip to content

Commit

Permalink
PlatformCore/Qt: Add sharp bilinear shader (#340)
Browse files Browse the repository at this point in the history
  • Loading branch information
GranMinigun authored Dec 13, 2023
1 parent 570c333 commit b2a2cfd
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/platform/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ set(HEADERS
src/device/shader/common.glsl.hpp
src/device/shader/lcd_ghosting.glsl.hpp
src/device/shader/output.glsl.hpp
src/device/shader/sharp_bilinear.glsl.hpp
src/device/shader/xbrz.glsl.hpp
)

set(HEADERS_PUBLIC
Expand Down
1 change: 1 addition & 0 deletions src/platform/core/include/platform/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct PlatformConfig : Config {
enum class Filter {
Nearest,
Linear,
Sharp,
xBRZ
} filter = Filter::Linear;

Expand Down
2 changes: 2 additions & 0 deletions src/platform/core/src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ void PlatformConfig::Load(std::string const& path) {
const std::map<std::string, Video::Filter> filters{
{ "nearest", Video::Filter::Nearest },
{ "linear", Video::Filter::Linear },
{ "sharp", Video::Filter::Sharp },
{ "xbrz", Video::Filter::xBRZ }
};

Expand Down Expand Up @@ -179,6 +180,7 @@ void PlatformConfig::Save(std::string const& path) {
switch(this->video.filter) {
case Video::Filter::Nearest: filter = "nearest"; break;
case Video::Filter::Linear: filter = "linear"; break;
case Video::Filter::Sharp: filter = "sharp"; break;
case Video::Filter::xBRZ: filter = "xbrz"; break;
}

Expand Down
12 changes: 11 additions & 1 deletion src/platform/core/src/device/ogl_video_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "device/shader/color_agb.glsl.hpp"
#include "device/shader/lcd_ghosting.glsl.hpp"
#include "device/shader/output.glsl.hpp"
#include "device/shader/sharp_bilinear.glsl.hpp"
#include "device/shader/xbrz.glsl.hpp"

using Video = nba::PlatformConfig::Video;
Expand Down Expand Up @@ -70,7 +71,8 @@ void OGLVideoDevice::Initialize() {
}

void OGLVideoDevice::ReloadConfig() {
if(config->video.filter == Video::Filter::Linear) {
if(config->video.filter == Video::Filter::Linear ||
config->video.filter == Video::Filter::Sharp) {
texture_filter = GL_LINEAR;
} else {
texture_filter = GL_NEAREST;
Expand Down Expand Up @@ -184,6 +186,14 @@ void OGLVideoDevice::CreateShaderPrograms() {
}
break;
}
// Sharp bilinear.
case Video::Filter::Sharp: {
auto [success, program] = CompileProgram(sharp_bilinear_vert, sharp_bilinear_frag);
if (success) {
shader_passes.push_back({program});
}
break;
}
// Plain linear/nearest-interpolated output.
case Video::Filter::Nearest:
case Video::Filter::Linear: {
Expand Down
67 changes: 67 additions & 0 deletions src/platform/core/src/device/shader/sharp_bilinear.glsl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Author: rsn8887 (based on TheMaister)
License: Public domain
This is an integer prescale filter that should be combined
with a bilinear hardware filtering (GL_BILINEAR filter or some such) to achieve
a smooth scaling result with minimum blur. This is good for pixelgraphics
that are scaled by non-integer factors.
The prescale factor and texel coordinates are precalculated
in the vertex shader for speed.
*/

#pragma once

constexpr auto sharp_bilinear_vert = R"(
#version 330 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec2 uv;
out vec2 v_uv;
out vec2 precalc_texel;
out vec2 precalc_scale;
uniform vec2 u_output_size;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
v_uv = vec2(uv.x, 1.0 - uv.y);
const vec2 input_size = vec2(240, 160);
precalc_scale = max(floor(u_output_size / input_size), vec2(1.0, 1.0));
precalc_texel = v_uv * input_size;
}
)";

constexpr auto sharp_bilinear_frag = R"(
#version 330 core
in vec2 v_uv;
in vec2 precalc_texel;
in vec2 precalc_scale;
layout(location = 0) out vec4 frag_color;
uniform sampler2D u_input_map;
void main() {
vec2 texel = precalc_texel;
vec2 scale = precalc_scale;
vec2 texel_floored = floor(texel);
vec2 s = fract(texel);
vec2 region_range = 0.5 - 0.5 / scale;
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
// Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.
vec2 center_dist = s - 0.5;
vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
vec2 mod_texel = texel_floored + f;
const vec2 input_size = vec2(240, 160);
frag_color = vec4(texture(u_input_map, mod_texel / input_size).rgb, 1.0);
}
)";
1 change: 1 addition & 0 deletions src/platform/qt/src/widget/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ void MainWindow::CreateVideoMenu(QMenu* parent) {
CreateSelectionOption(menu->addMenu(tr("Filter")), {
{ "Nearest", nba::PlatformConfig::Video::Filter::Nearest },
{ "Linear", nba::PlatformConfig::Video::Filter::Linear },
{ "Sharp", nba::PlatformConfig::Video::Filter::Sharp },
{ "xBRZ", nba::PlatformConfig::Video::Filter::xBRZ }
}, &config->video.filter, false, reload_config);

Expand Down

0 comments on commit b2a2cfd

Please sign in to comment.