summaryrefslogtreecommitdiff
path: root/Data/Sys/Shaders/integer_scaling.glsl
blob: ff620ad3595a81115d257ebb7e5753cf2375ce5f (plain)
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Set aspect ratio to 'stretch'

// Integer Scaling shader by One_More_Try / TryTwo
// Uses Sharp Bilinear from
// https://github.com/libretro/slang-shaders/blob/master/interpolation/shaders/sharp-bilinear.slang
// by Themaister, Public Domain license

/*
[configuration]
[OptionBool]
GUIName = Please set aspect ratio to stretch
OptionName = ASPECT_MSG
DefaultValue = false

[OptionBool]
GUIName = Use non-integer width
OptionName = WIDTH_UNLOCK
DefaultValue = false

[OptionBool]
GUIName = Stretch width to window
OptionName = WIDTH_SKIP
DependentOption = WIDTH_UNLOCK
DefaultValue = false

[OptionBool]
GUIName = Scale width to fit 4:3
OptionName = WIDTH_43
DependentOption = WIDTH_UNLOCK
DefaultValue = false

[OptionBool]
GUIName = Scale width to fit 16:9
OptionName = WIDTH_169
DependentOption = WIDTH_UNLOCK
DefaultValue = false

[OptionBool]
GUIName = Apply sharp bilinear for custom widths
OptionName = SHARP_BILINEAR
DefaultValue = false

[OptionRangeFloat]
GUIName = Sharp bilinear factor (0 = auto)
OptionName = SHARP_PRESCALE
MinValue = 0.0
MaxValue = 16.0
StepAmount = 1.0
DefaultValue = 0.0

[OptionBool]
GUIName = Manual scale - Set IR first
OptionName = MANUALSCALE
DefaultValue = false

[OptionRangeFloat]
GUIName = Integer scale - No higher than IR
OptionName = INTEGER_SCALE
DependentOption = MANUALSCALE
MaxValue = 5.0
MinValue = 1.0
DefaultValue = 1.0
StepAmount = 1.0

[OptionRangeFloat]
GUIName = Scale width
OptionName = WIDTH_SCALE
DependentOption = MANUALSCALE
MaxValue = 5.0
MinValue = -2.0
DefaultValue = 0.0
StepAmount = 1.0

[OptionBool]
GUIName = Auto downscaling
OptionName = DOWNSCALE
DefaultValue = false

[/configuration]
*/

void main()
{
	float4 c0 = float4(0.0, 0.0, 0.0, 0.0);
	float2 scale = float2(1.0, 1.0);
	float2 xfb_res = GetResolution();
	float2 win_res = GetWindowResolution();
	float2 coords = GetCoordinates();

	// ratio is used to rescale the coords to the xfb size, which allows for integer scaling.
	// ratio can then be multiplied by an integer to upscale/downscale, but upscale isn't supported by
	// point-sampling.
	float2 ratio = win_res / xfb_res;

	if (OptionEnabled(WIDTH_UNLOCK))
	{
		if (OptionEnabled(WIDTH_SKIP))
			ratio.x = 1.0;
		else if (OptionEnabled(WIDTH_43))
			ratio.x = win_res.x / (xfb_res.y * 4 / 3);
		else if (OptionEnabled(WIDTH_169))
			ratio.x = win_res.x / (xfb_res.y * 16 / 9);
	}

	if (OptionEnabled(MANUALSCALE))
	{
		// There's no IR variable, so this guesses the IR, but may be off for some games.
		float calc_ir = ceil(xfb_res.y / 500);
		scale.y = calc_ir / GetOption(INTEGER_SCALE);
		float manual_width = GetOption(WIDTH_SCALE);

		if (manual_width < 0.0)
			scale.x = scale.y * (abs(manual_width) + 1);
		else
			scale.x = scale.y / (manual_width + 1);

		ratio = ratio * scale;
	}
	else if (OptionEnabled(DOWNSCALE) && (ratio.x < 1 || ratio.y < 1))
	{
		scale.x = ceil(max(1.0 / ratio.y, 1.0 / ratio.x));
		scale.y = scale.x;
		ratio = ratio * scale;
	}

	// y and x are used to determine black bars vs drawable space.
	float y = win_res.y - xfb_res.y / scale.y;
	float y_top = (y / 2.0) * GetInvWindowResolution().y;
	float y_bottom = (win_res.y - y / 2.0) * GetInvWindowResolution().y;
	float yloc = (coords.y - y_top) * ratio.y;

	float x = win_res.x - xfb_res.x / scale.x;

	if (OptionEnabled(WIDTH_UNLOCK))
	{
		if (OptionEnabled(WIDTH_SKIP))
			x = 0.0;
		else if (OptionEnabled(WIDTH_43))
			x = win_res.x - xfb_res.y / scale.y * 4 / 3;
		else if (OptionEnabled(WIDTH_169))
			x = win_res.x - xfb_res.y / scale.y * 16 / 9;
	}

	float x_left = (x / 2.0) * GetInvWindowResolution().x;
	float x_right = (win_res.x - x / 2.0) * GetInvWindowResolution().x;
	float xloc = (coords.x - x_left) * ratio.x;

	if (OptionEnabled(SHARP_BILINEAR) &&
		(OptionEnabled(WIDTH_SKIP) || OptionEnabled(WIDTH_43) || OptionEnabled(WIDTH_169) ||
			(OptionEnabled(MANUALSCALE) && GetOption(WIDTH_SCALE) != 0.0)))
	{
		float texel = xloc * xfb_res.x;
		float texel_floored = floor(texel);
		float s = frac(texel);
		float scale_sharp = GetOption(SHARP_PRESCALE);

		if (scale_sharp == 0)
		{
			if (OptionEnabled(WIDTH_43))
				scale_sharp = (4 / 3 * xfb_res.y / xfb_res.x);
			else if (OptionEnabled(WIDTH_169))
				scale_sharp = (16 / 9 * xfb_res.y / xfb_res.x);
			else
				scale_sharp = ceil(win_res.x / xfb_res.x);
		}

		float region_range = 0.5 - 0.5 / scale_sharp;
		float center_dist = s - 0.5;
		float f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale_sharp + 0.5;

		float mod_texel = texel_floored + f;
		xloc = mod_texel / xfb_res.x;
	}

	if (coords.x >= x_left && x_right >= coords.x && coords.y >= y_top && y_bottom >= coords.y)
	{
		float2 sample_loc = float2(xloc, yloc);
		c0 = SampleLocation(sample_loc);
	}

	SetOutput(c0);
}