summaryrefslogtreecommitdiffstats
path: root/src/video_core/host_shaders/opengl_present_scaleforce.frag
blob: 71ff9e1e36c298f05d8f6432108869b7505d2613 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// MIT License
//
// Copyright (c) 2020 BreadFish64
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// Adapted from https://github.com/BreadFish64/ScaleFish/tree/master/scaleforce

//! #version 460

#extension GL_ARB_separate_shader_objects : enable

#ifdef YUZU_USE_FP16

#extension GL_AMD_gpu_shader_half_float : enable
#extension GL_NV_gpu_shader5 : enable

#define lfloat float16_t
#define lvec2 f16vec2
#define lvec3 f16vec3
#define lvec4 f16vec4

#else

#define lfloat float
#define lvec2 vec2
#define lvec3 vec3
#define lvec4 vec4

#endif

#ifdef VULKAN

#define BINDING_COLOR_TEXTURE 1

#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv

#define BINDING_COLOR_TEXTURE 0

#endif

layout (location = 0) in vec2 tex_coord;

layout (location = 0) out vec4 frag_color;

layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture;

const bool ignore_alpha = true;

lfloat ColorDist1(lvec4 a, lvec4 b) {
    // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
    const lvec3 K = lvec3(0.2627, 0.6780, 0.0593);
    const lfloat scaleB = lfloat(0.5) / (lfloat(1.0) - K.b);
    const lfloat scaleR = lfloat(0.5) / (lfloat(1.0) - K.r);
    lvec4 diff = a - b;
    lfloat Y = dot(diff.rgb, K);
    lfloat Cb = scaleB * (diff.b - Y);
    lfloat Cr = scaleR * (diff.r - Y);
    lvec3 YCbCr = lvec3(Y, Cb, Cr);
    lfloat d = length(YCbCr);
    if (ignore_alpha) {
        return d;
    }
    return sqrt(a.a * b.a * d * d + diff.a * diff.a);
}

lvec4 ColorDist(lvec4 ref, lvec4 A, lvec4 B, lvec4 C, lvec4 D) {
    return lvec4(
            ColorDist1(ref, A),
            ColorDist1(ref, B),
            ColorDist1(ref, C),
            ColorDist1(ref, D)
        );
}

vec4 Scaleforce(sampler2D tex, vec2 tex_coord) {
    lvec4 bl = lvec4(textureOffset(tex, tex_coord, ivec2(-1, -1)));
    lvec4 bc = lvec4(textureOffset(tex, tex_coord, ivec2(0, -1)));
    lvec4 br = lvec4(textureOffset(tex, tex_coord, ivec2(1, -1)));
    lvec4 cl = lvec4(textureOffset(tex, tex_coord, ivec2(-1, 0)));
    lvec4 cc = lvec4(texture(tex, tex_coord));
    lvec4 cr = lvec4(textureOffset(tex, tex_coord, ivec2(1, 0)));
    lvec4 tl = lvec4(textureOffset(tex, tex_coord, ivec2(-1, 1)));
    lvec4 tc = lvec4(textureOffset(tex, tex_coord, ivec2(0, 1)));
    lvec4 tr = lvec4(textureOffset(tex, tex_coord, ivec2(1, 1)));

    lvec4 offset_tl = ColorDist(cc, tl, tc, tr, cr);
    lvec4 offset_br = ColorDist(cc, br, bc, bl, cl);

    // Calculate how different cc is from the texels around it
    const lfloat plus_weight = lfloat(1.5);
    const lfloat cross_weight = lfloat(1.5);
    lfloat total_dist = dot(offset_tl + offset_br, lvec4(cross_weight, plus_weight, cross_weight, plus_weight));

    if (total_dist == lfloat(0.0)) {
        return cc;
    } else {
        // Add together all the distances with direction taken into account
        lvec4 tmp = offset_tl - offset_br;
        lvec2 total_offset = tmp.wy * plus_weight + (tmp.zz + lvec2(-tmp.x, tmp.x)) * cross_weight;

        // When the image has thin points, they tend to split apart.
        // This is because the texels all around are different and total_offset reaches into clear areas.
        // This works pretty well to keep the offset in bounds for these cases.
        lfloat clamp_val = length(total_offset) / total_dist;
        vec2 final_offset = vec2(clamp(total_offset, -clamp_val, clamp_val)) / textureSize(tex, 0);

        return texture(tex, tex_coord - final_offset);
    }
}

void main() {
    frag_color = Scaleforce(input_texture, tex_coord);
}