mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 02:33:44 -04:00
added fsr/sharpbilinear shaders, and readied opengl for integration
This commit is contained in:
parent
300b68e18d
commit
1035238473
16 changed files with 4303 additions and 49 deletions
|
|
@ -44,9 +44,9 @@ ConfigureEnhancements::~ConfigureEnhancements() = default;
|
|||
|
||||
void ConfigureEnhancements::SetConfiguration() {
|
||||
|
||||
const s32 volume =
|
||||
static_cast<s32>(Settings::values.fsr_sharpness.GetValue() * 100);
|
||||
ui->fsr_sharpness_slider->setValue(volume);
|
||||
const s32 sharpness =
|
||||
static_cast<s32>(Settings::values.fsr_sharpness.GetValue());
|
||||
ui->fsr_sharpness_slider->setValue(sharpness);
|
||||
SetFSRSharpnessIndicatorText(ui->fsr_sharpness_slider->sliderPosition());
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
|
|
|
|||
|
|
@ -554,7 +554,7 @@ struct Values {
|
|||
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::NoFilter, Keys::texture_filter};
|
||||
SwitchableSetting<AntiAliasingFilter> antialiasing_filter{AntiAliasingFilter::None, Keys::antialiasing_filter};
|
||||
SwitchableSetting<OutputScaling> output_scaling{OutputScaling::Adaptive, Keys::output_scaling};
|
||||
SwitchableSetting<float, true> fsr_sharpness{0.8f, 0.f, 1.f, Keys::fsr_sharpness};
|
||||
SwitchableSetting<int, true> fsr_sharpness{80, 0, 100, Keys::fsr_sharpness};
|
||||
SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled,
|
||||
Keys::texture_sampling};
|
||||
SwitchableSetting<u16, true> delay_game_render_thread_us{0, 0, 16000,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,17 @@ set(SHADER_FILES
|
|||
scaling/opengl_area_sampling.vert
|
||||
scaling/vulkan_area_sampling.frag
|
||||
scaling/vulkan_area_sampling.vert
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass0.vert
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass0_part1.frag
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass0_part2.frag
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass1.vert
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass1_part1.frag
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass1_part2.frag
|
||||
scaling/FSR/OpenGL/opengl_fsr_pass1_part3.frag
|
||||
scaling/FSR/ffx_a.h
|
||||
scaling/FSR/ffx_fsr1.h
|
||||
scaling/SharpBilinear/OpenGL/opengl_sharpbilinear.vert
|
||||
scaling/SharpBilinear/OpenGL/opengl_sharpbilinear.frag
|
||||
full_screen_triangle.vert
|
||||
opengl_present.frag
|
||||
opengl_present.vert
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
|
||||
// SM 4.0 compatible: no textureGather, direct texelFetch of 12 unique texels.
|
||||
layout(location = 0) in vec2 vert_position;
|
||||
layout(location = 1) in vec2 vert_tex_coord;
|
||||
layout(location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vert_position, 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
|
||||
// SM 4.0 compatible: no textureGather, direct texelFetch of 12 unique texels.
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
uniform vec4 i_resolution;
|
||||
uniform vec4 o_resolution;
|
||||
|
||||
#define A_GPU 1
|
||||
#define A_GLSL 1
|
||||
// #include "ffx_a.h"
|
||||
|
||||
// // We intentionally do NOT define FSR_EASU_F here.
|
||||
// // We only need FsrEasuCon (which compiles under A_GPU alone),
|
||||
// // and we inline the EASU filter logic below to avoid the
|
||||
// // textureGather-based callback system entirely.
|
||||
// // This yields 12 texelFetch calls instead of the original
|
||||
// // 12 textureGather calls (4 gathers x 3 channels), and is
|
||||
// // faster than emulating gathers with 48 individual fetches.
|
||||
// #include "ffx_fsr1.h"
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
void main() {
|
||||
// --- Setup constants (same as original) ---
|
||||
AU4 con0, con1, con2, con3;
|
||||
FsrEasuCon(con0, con1, con2, con3,
|
||||
i_resolution.x, i_resolution.y,
|
||||
i_resolution.x, i_resolution.y,
|
||||
o_resolution.x, o_resolution.y);
|
||||
|
||||
AU2 gxy = AU2(frag_tex_coord.xy * o_resolution.xy);
|
||||
|
||||
// --- Get position of 'f' (the center texel of the kernel) ---
|
||||
AF2 pp = AF2(gxy) * AF2_AU2(con0.xy) + AF2_AU2(con0.zw);
|
||||
AF2 fp = floor(pp);
|
||||
pp -= fp;
|
||||
|
||||
// --- Fetch all 12 unique texels directly as RGB ---
|
||||
// The 12-tap kernel layout relative to 'fp':
|
||||
// b c (0,-1) (1,-1)
|
||||
// e f g h (-1,0) (0,0) (1,0) (2,0)
|
||||
// i j k l (-1,1) (0,1) (1,1) (2,1)
|
||||
// n o (0, 2) (1, 2)
|
||||
ivec2 sp = ivec2(fp);
|
||||
AF3 b = texelFetch(color_texture, sp + ivec2( 0,-1), 0).rgb;
|
||||
AF3 c = texelFetch(color_texture, sp + ivec2( 1,-1), 0).rgb;
|
||||
AF3 e = texelFetch(color_texture, sp + ivec2(-1, 0), 0).rgb;
|
||||
AF3 f = texelFetch(color_texture, sp + ivec2( 0, 0), 0).rgb;
|
||||
AF3 g = texelFetch(color_texture, sp + ivec2( 1, 0), 0).rgb;
|
||||
AF3 h = texelFetch(color_texture, sp + ivec2( 2, 0), 0).rgb;
|
||||
AF3 i = texelFetch(color_texture, sp + ivec2(-1, 1), 0).rgb;
|
||||
AF3 j = texelFetch(color_texture, sp + ivec2( 0, 1), 0).rgb;
|
||||
AF3 k = texelFetch(color_texture, sp + ivec2( 1, 1), 0).rgb;
|
||||
AF3 l = texelFetch(color_texture, sp + ivec2( 2, 1), 0).rgb;
|
||||
AF3 n = texelFetch(color_texture, sp + ivec2( 0, 2), 0).rgb;
|
||||
AF3 o = texelFetch(color_texture, sp + ivec2( 1, 2), 0).rgb;
|
||||
|
||||
// --- Approximate luma (luma times 2, in 2 FMA/MAD) ---
|
||||
AF1 bL = b.b * AF1_(0.5) + (b.r * AF1_(0.5) + b.g);
|
||||
AF1 cL = c.b * AF1_(0.5) + (c.r * AF1_(0.5) + c.g);
|
||||
AF1 eL = e.b * AF1_(0.5) + (e.r * AF1_(0.5) + e.g);
|
||||
AF1 fL = f.b * AF1_(0.5) + (f.r * AF1_(0.5) + f.g);
|
||||
AF1 gL = g.b * AF1_(0.5) + (g.r * AF1_(0.5) + g.g);
|
||||
AF1 hL = h.b * AF1_(0.5) + (h.r * AF1_(0.5) + h.g);
|
||||
AF1 iL = i.b * AF1_(0.5) + (i.r * AF1_(0.5) + i.g);
|
||||
AF1 jL = j.b * AF1_(0.5) + (j.r * AF1_(0.5) + j.g);
|
||||
AF1 kL = k.b * AF1_(0.5) + (k.r * AF1_(0.5) + k.g);
|
||||
AF1 lL = l.b * AF1_(0.5) + (l.r * AF1_(0.5) + l.g);
|
||||
AF1 nL = n.b * AF1_(0.5) + (n.r * AF1_(0.5) + n.g);
|
||||
AF1 oL = o.b * AF1_(0.5) + (o.r * AF1_(0.5) + o.g);
|
||||
|
||||
// --- Accumulate direction and length ---
|
||||
// Inlined FsrEasuSetF for each of the 4 bilinear quadrants.
|
||||
// Each quadrant computes gradient direction and edge length
|
||||
// from its 5-tap cross pattern centered on the quadrant's
|
||||
// nearest texel.
|
||||
//
|
||||
// Quadrant layout (bilinear weights):
|
||||
// s=(1-x)(1-y) t=x(1-y)
|
||||
// u=(1-x)y v=xy
|
||||
//
|
||||
// Cross pattern for each quadrant:
|
||||
// s: center=f, left=e, right=g, up=b, down=j
|
||||
// t: center=g, left=f, right=h, up=c, down=k
|
||||
// u: center=j, left=i, right=k, up=f, down=n
|
||||
// v: center=k, left=j, right=l, up=g, down=o
|
||||
|
||||
AF2 dir = AF2_(0.0);
|
||||
AF1 len = AF1_(0.0);
|
||||
|
||||
// Quadrant s
|
||||
{
|
||||
AF1 w = (AF1_(1.0) - pp.x) * (AF1_(1.0) - pp.y);
|
||||
AF1 dc = gL - fL; AF1 cb = fL - eL;
|
||||
AF1 lenX = max(abs(dc), abs(cb));
|
||||
lenX = APrxLoRcpF1(lenX);
|
||||
AF1 dirX = gL - eL;
|
||||
dir.x += dirX * w;
|
||||
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
|
||||
AF1 ec = jL - fL; AF1 ca = fL - bL;
|
||||
AF1 lenY = max(abs(ec), abs(ca));
|
||||
lenY = APrxLoRcpF1(lenY);
|
||||
AF1 dirY = jL - bL;
|
||||
dir.y += dirY * w;
|
||||
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
|
||||
}
|
||||
// Quadrant t
|
||||
{
|
||||
AF1 w = pp.x * (AF1_(1.0) - pp.y);
|
||||
AF1 dc = hL - gL; AF1 cb = gL - fL;
|
||||
AF1 lenX = max(abs(dc), abs(cb));
|
||||
lenX = APrxLoRcpF1(lenX);
|
||||
AF1 dirX = hL - fL;
|
||||
dir.x += dirX * w;
|
||||
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
|
||||
AF1 ec = kL - gL; AF1 ca = gL - cL;
|
||||
AF1 lenY = max(abs(ec), abs(ca));
|
||||
lenY = APrxLoRcpF1(lenY);
|
||||
AF1 dirY = kL - cL;
|
||||
dir.y += dirY * w;
|
||||
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
|
||||
}
|
||||
// Quadrant u
|
||||
{
|
||||
AF1 w = (AF1_(1.0) - pp.x) * pp.y;
|
||||
AF1 dc = kL - jL; AF1 cb = jL - iL;
|
||||
AF1 lenX = max(abs(dc), abs(cb));
|
||||
lenX = APrxLoRcpF1(lenX);
|
||||
AF1 dirX = kL - iL;
|
||||
dir.x += dirX * w;
|
||||
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
|
||||
AF1 ec = nL - jL; AF1 ca = jL - fL;
|
||||
AF1 lenY = max(abs(ec), abs(ca));
|
||||
lenY = APrxLoRcpF1(lenY);
|
||||
AF1 dirY = nL - fL;
|
||||
dir.y += dirY * w;
|
||||
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
|
||||
}
|
||||
// Quadrant v
|
||||
{
|
||||
AF1 w = pp.x * pp.y;
|
||||
AF1 dc = lL - kL; AF1 cb = kL - jL;
|
||||
AF1 lenX = max(abs(dc), abs(cb));
|
||||
lenX = APrxLoRcpF1(lenX);
|
||||
AF1 dirX = lL - jL;
|
||||
dir.x += dirX * w;
|
||||
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
|
||||
AF1 ec = oL - kL; AF1 ca = kL - gL;
|
||||
AF1 lenY = max(abs(ec), abs(ca));
|
||||
lenY = APrxLoRcpF1(lenY);
|
||||
AF1 dirY = oL - gL;
|
||||
dir.y += dirY * w;
|
||||
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
|
||||
}
|
||||
|
||||
// --- Normalize direction ---
|
||||
AF2 dir2 = dir * dir;
|
||||
AF1 dirR = dir2.x + dir2.y;
|
||||
AP1 zro = dirR < AF1_(1.0 / 32768.0);
|
||||
dirR = APrxLoRsqF1(dirR);
|
||||
dirR = zro ? AF1_(1.0) : dirR;
|
||||
dir.x = zro ? AF1_(1.0) : dir.x;
|
||||
dir *= AF2_(dirR);
|
||||
|
||||
// --- Shape length ---
|
||||
len = len * AF1_(0.5);
|
||||
len *= len;
|
||||
AF1 stretch = (dir.x * dir.x + dir.y * dir.y) * APrxLoRcpF1(max(abs(dir.x), abs(dir.y)));
|
||||
AF2 len2 = AF2(AF1_(1.0) + (stretch - AF1_(1.0)) * len, AF1_(1.0) + AF1_(-0.5) * len);
|
||||
AF1 lob = AF1_(0.5) + AF1_((1.0 / 4.0 - 0.04) - 0.5) * len;
|
||||
AF1 clp = APrxLoRcpF1(lob);
|
||||
|
||||
// --- Min/max of 4 nearest (f, g, j, k) for de-ringing ---
|
||||
AF3 min4 = min(min(f, g), min(j, k));
|
||||
AF3 max4 = max(max(f, g), max(j, k));
|
||||
|
||||
// --- Accumulate 12 taps (inlined FsrEasuTapF) ---
|
||||
AF3 aC = AF3_(0.0);
|
||||
AF1 aW = AF1_(0.0);
|
||||
|
||||
// Macro for the Lanczos-like kernel evaluation per tap.
|
||||
// Rotates offset by direction, applies anisotropic scaling,
|
||||
// evaluates the approximated windowed Lanczos kernel, accumulates.
|
||||
#define FSR_EASU_TAP(OFF_X, OFF_Y, COLOR) { \
|
||||
AF2 v; \
|
||||
v.x = ((OFF_X) - pp.x) * dir.x + ((OFF_Y) - pp.y) * dir.y; \
|
||||
v.y = ((OFF_X) - pp.x) * (-dir.y) + ((OFF_Y) - pp.y) * dir.x; \
|
||||
v *= len2; \
|
||||
AF1 d2 = min(v.x * v.x + v.y * v.y, clp); \
|
||||
AF1 wB = AF1_(2.0 / 5.0) * d2 + AF1_(-1.0); \
|
||||
AF1 wA = lob * d2 + AF1_(-1.0); \
|
||||
wB *= wB; wA *= wA; \
|
||||
wB = AF1_(25.0 / 16.0) * wB + AF1_(-(25.0 / 16.0 - 1.0)); \
|
||||
AF1 w = wB * wA; \
|
||||
aC += (COLOR) * w; aW += w; }
|
||||
|
||||
FSR_EASU_TAP( 0.0, -1.0, b) // b
|
||||
FSR_EASU_TAP( 1.0, -1.0, c) // c
|
||||
FSR_EASU_TAP(-1.0, 1.0, i) // i
|
||||
FSR_EASU_TAP( 0.0, 1.0, j) // j
|
||||
FSR_EASU_TAP( 0.0, 0.0, f) // f
|
||||
FSR_EASU_TAP(-1.0, 0.0, e) // e
|
||||
FSR_EASU_TAP( 1.0, 1.0, k) // k
|
||||
FSR_EASU_TAP( 2.0, 1.0, l) // l
|
||||
FSR_EASU_TAP( 2.0, 0.0, h) // h
|
||||
FSR_EASU_TAP( 1.0, 0.0, g) // g
|
||||
FSR_EASU_TAP( 1.0, 2.0, o) // o
|
||||
FSR_EASU_TAP( 0.0, 2.0, n) // n
|
||||
|
||||
#undef FSR_EASU_TAP
|
||||
|
||||
// --- Normalize and de-ring ---
|
||||
AF3 pix = min(max4, max(min4, aC * AF3_(ARcpF1(aW))));
|
||||
color = vec4(pix, 1.0);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
|
||||
//? #version 450
|
||||
layout(location = 0) in vec2 vert_position;
|
||||
layout(location = 1) in vec2 vert_tex_coord;
|
||||
layout(location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vert_position, 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
uniform float FSR_SHARPENING;
|
||||
uniform vec4 o_resolution;
|
||||
|
||||
#define A_GPU 1
|
||||
#define A_GLSL 1
|
||||
// #include "ffx_a.h"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#define FSR_RCAS_F 1
|
||||
AU4 con0;
|
||||
|
||||
AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(color_texture, p, 0)); }
|
||||
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
|
||||
|
||||
// #include "ffx_fsr1.h"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
void main() {
|
||||
FsrRcasCon(con0, params.FSR_SHARPENING);
|
||||
|
||||
AU2 gxy = AU2(frag_tex_coord.xy * o_resolution.xy); // Integer pixel position in output.
|
||||
AF3 Gamma2Color = AF3(0, 0, 0);
|
||||
FsrRcasF(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, gxy, con0);
|
||||
|
||||
color = vec4(Gamma2Color, 1.0);
|
||||
}
|
||||
2656
src/video_core/host_shaders/scaling/FSR/ffx_a.h
Normal file
2656
src/video_core/host_shaders/scaling/FSR/ffx_a.h
Normal file
File diff suppressed because it is too large
Load diff
1199
src/video_core/host_shaders/scaling/FSR/ffx_fsr1.h
Normal file
1199
src/video_core/host_shaders/scaling/FSR/ffx_fsr1.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Author: KojoZero (modified from rsn8887's shader)
|
||||
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.
|
||||
|
||||
This is a modified version rsn8887's shader which has been modified to scale
|
||||
until above the output resolution, rather than right below the output resolution.
|
||||
|
||||
The prescale factor and texel coordinates are precalculated
|
||||
in the vertex shader for speed.
|
||||
*/
|
||||
|
||||
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 1) in vec2 precalc_texel;
|
||||
layout(location = 2) in vec2 precalc_scale;
|
||||
layout(location = 0) out vec4 color;
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
uniform vec4 i_resolution;
|
||||
uniform vec4 o_resolution;
|
||||
uniform int convert_colors;
|
||||
|
||||
vec3 LinearTosRGB(vec3 c) {
|
||||
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
vec4 pixel = vec4(texture(color_texture, mod_texel / i_resolution.xy).rgb, 1.0);
|
||||
if (convert_colors == 2){
|
||||
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
|
||||
}
|
||||
color = pixel;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
layout(location = 0) in vec2 vert_position;
|
||||
layout(location = 1) in vec2 vert_tex_coord;
|
||||
layout(location = 0) out vec2 frag_tex_coord;
|
||||
layout(location = 1) out vec2 precalc_texel;
|
||||
layout(location = 2) out vec2 precalc_scale;
|
||||
uniform mat3x2 modelview_matrix;
|
||||
uniform vec4 i_resolution;
|
||||
uniform vec4 o_resolution;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
precalc_scale = ceil(o_resolution.xy / i_resolution.xy);
|
||||
precalc_texel = vert_tex_coord.xy * i_resolution.xy;
|
||||
}
|
||||
|
|
@ -44,6 +44,17 @@
|
|||
#include "video_core/host_shaders/antialiasing/SearchTex.h"
|
||||
#include "video_core/host_shaders/scaling/opengl_area_sampling_frag.h"
|
||||
#include "video_core/host_shaders/scaling/opengl_area_sampling_vert.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass0_vert.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass0_part1_frag.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass0_part2_frag.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_vert.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_part1_frag.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_part2_frag.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_part3_frag.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/ffx_a_h.h"
|
||||
#include "video_core/host_shaders/scaling/FSR/ffx_fsr1_h.h"
|
||||
#include "video_core/host_shaders/scaling/SharpBilinear/OpenGL/opengl_sharpbilinear_vert.h"
|
||||
#include "video_core/host_shaders/scaling/SharpBilinear/OpenGL/opengl_sharpbilinear_frag.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
|
|
@ -381,22 +392,37 @@ void RendererOpenGL::AllocateSMAATextures(){
|
|||
}
|
||||
|
||||
void RendererOpenGL::AllocatePPTextures(){
|
||||
for (int i = 0; i < 5; i++){
|
||||
intermediateTextureTop[i].Release();
|
||||
intermediateTextureTop[i].Create();
|
||||
intermediateTextureTop[i].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currTopTextureWidth, currTopTextureHeight);
|
||||
intermediateTextureBottom[i].Release();
|
||||
intermediateTextureBottom[i].Create();
|
||||
intermediateTextureBottom[i].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currBottomTextureWidth, currBottomTextureHeight);
|
||||
}
|
||||
antialiasFBOTextureTop.Release();
|
||||
antialiasFBOTextureTop.Create();
|
||||
antialiasFBOTextureTop.Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currTopTextureWidth, currTopTextureHeight);
|
||||
antialiasFBOTextureBottom.Release();
|
||||
antialiasFBOTextureBottom.Create();
|
||||
antialiasFBOTextureBottom.Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currBottomTextureWidth, currBottomTextureHeight);
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Reallocated Shaders");
|
||||
|
||||
for (int j = 0; j < intermediateTextures[0].size(); j++){
|
||||
intermediateTextures[0][j].Release();
|
||||
intermediateTextures[0][j].Create();
|
||||
intermediateTextures[0][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currTopTextureWidth, currTopTextureHeight);
|
||||
intermediateTextures[1][j].Release();
|
||||
intermediateTextures[1][j].Create();
|
||||
intermediateTextures[1][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currBottomTextureWidth, currBottomTextureHeight);
|
||||
}
|
||||
antialiasFBOTexture[0].Release();
|
||||
antialiasFBOTexture[0].Create();
|
||||
antialiasFBOTexture[0].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currTopTextureWidth, currTopTextureHeight);
|
||||
antialiasFBOTexture[1].Release();
|
||||
antialiasFBOTexture[1].Create();
|
||||
antialiasFBOTexture[1].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currBottomTextureWidth, currBottomTextureHeight);
|
||||
LOG_INFO(Render_OpenGL, "Reallocated Textures");
|
||||
}
|
||||
|
||||
void RendererOpenGL::AllocateOutputSizeTextures(){
|
||||
for (int i = 0; i < intermediateOutputSizeTextures.size(); i++){
|
||||
if (currScreenRects[i].GetHeight() != 0 && currScreenRects[i].GetWidth() != 0){
|
||||
for (int j = 0; j < intermediateOutputSizeTextures[0].size(); j++){
|
||||
intermediateOutputSizeTextures[i][j].Release();
|
||||
intermediateOutputSizeTextures[i][j].Create();
|
||||
intermediateOutputSizeTextures[i][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currScreenRects[i].GetWidth(), currScreenRects[i].GetHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -548,7 +574,23 @@ void RendererOpenGL::ReloadShader(Settings::StereoRenderOption render_3d) {
|
|||
SMAA_PASS_2_shader.Create(SMAA_PASS_2_shader_vert_data, SMAA_PASS_2_shader_frag_data);
|
||||
|
||||
|
||||
//
|
||||
std::string FSR_PASS_0_shader_frag_data = fragment_shader_precision_OES;
|
||||
FSR_PASS_0_shader_frag_data += HostShaders::OPENGL_FSR_PASS0_PART1_FRAG;
|
||||
FSR_PASS_0_shader_frag_data += HostShaders::FFX_A_H;
|
||||
FSR_PASS_0_shader_frag_data += HostShaders::FFX_FSR1_H;
|
||||
FSR_PASS_0_shader_frag_data += HostShaders::OPENGL_FSR_PASS0_PART2_FRAG;
|
||||
FSR_PASS_0_shader.Create(HostShaders::OPENGL_FSR_PASS0_VERT, FSR_PASS_0_shader_frag_data);
|
||||
|
||||
std::string FSR_PASS_1_shader_frag_data = fragment_shader_precision_OES;
|
||||
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART1_FRAG;
|
||||
FSR_PASS_1_shader_frag_data += HostShaders::FFX_A_H;
|
||||
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART2_FRAG;
|
||||
FSR_PASS_1_shader_frag_data += HostShaders::FFX_FSR1_H;
|
||||
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART3_FRAG;
|
||||
FSR_PASS_1_shader.Create(HostShaders::OPENGL_FSR_PASS1_VERT, FSR_PASS_1_shader_frag_data);
|
||||
|
||||
SharpBilinear_shader.Create(HostShaders::OPENGL_SHARPBILINEAR_VERT, HostShaders::OPENGL_SHARPBILINEAR_FRAG);
|
||||
|
||||
state.Apply();
|
||||
if (render_3d == Settings::StereoRenderOption::Anaglyph ||
|
||||
render_3d == Settings::StereoRenderOption::Interlaced ||
|
||||
|
|
@ -654,12 +696,11 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
const u32 scale_factor = GetResolutionScaleFactor();
|
||||
float textureWidth = static_cast<float>(screen_info.texture.height * scale_factor);
|
||||
float textureHeight = static_cast<float>(screen_info.texture.width * scale_factor);
|
||||
int currScreen;
|
||||
if (textureWidth == currTopTextureWidth && textureHeight == currTopTextureHeight){
|
||||
currAntialiasFBOTexture = &antialiasFBOTextureTop;
|
||||
currIntermediateTexture = &intermediateTextureTop;
|
||||
currScreen = 0;
|
||||
} else {
|
||||
currAntialiasFBOTexture = &antialiasFBOTextureBottom;
|
||||
currIntermediateTexture = &intermediateTextureBottom;
|
||||
currScreen = 1;
|
||||
}
|
||||
|
||||
// Texture Width and Height when correctly rotated to landscape
|
||||
|
|
@ -667,6 +708,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
int scalingMode; //0 is Nearest Neighbor, 1 is Gamma Corrected Bilinear, 2 is Adaptive (Bilinear/Area), 3 is FSR, 4 is Sharp Bilinear
|
||||
scalingMode = static_cast<int>(Settings::values.output_scaling.GetValue());
|
||||
int antialiasingMode = static_cast<int>(Settings::values.antialiasing_filter.GetValue()); //0 is none, 1 is FXAA, 2 is SMAA
|
||||
float fsr_sharpening = 2 - (2 * (Settings::values.fsr_sharpness.GetValue()/ 100.0f));
|
||||
if (orientation == Layout::DisplayOrientation::Landscape || orientation == Layout::DisplayOrientation::LandscapeFlipped) {
|
||||
if (textureWidth > screenWidth){
|
||||
isDownsampling = true;
|
||||
|
|
@ -748,7 +790,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currIntermediateTexture)[0].handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][0].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SimplePresent_shader.handle;
|
||||
state.Apply();
|
||||
|
|
@ -767,12 +809,12 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currAntialiasFBOTexture).handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, antialiasFBOTexture[currScreen].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = FXAA_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currIntermediateTexture)[0].handle;
|
||||
state.texture_units[0].texture_2d = intermediateTextures[currScreen][0].handle;
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
glUniform1i(uniform_convert_colors, 1);
|
||||
|
|
@ -791,7 +833,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currIntermediateTexture)[0].handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][0].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SimplePresent_shader.handle;
|
||||
state.Apply();
|
||||
|
|
@ -810,12 +852,12 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currIntermediateTexture)[3].handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][3].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SimplePresent_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currIntermediateTexture)[0].handle;
|
||||
state.texture_units[0].texture_2d = intermediateTextures[currScreen][0].handle;
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
glUniform1i(uniform_convert_colors, 1);
|
||||
|
|
@ -829,12 +871,12 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currIntermediateTexture)[1].handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][1].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SMAA_PASS_0_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currIntermediateTexture)[0].handle;
|
||||
state.texture_units[0].texture_2d = intermediateTextures[currScreen][0].handle;
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
|
||||
|
|
@ -848,12 +890,12 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currIntermediateTexture)[2].handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][2].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SMAA_PASS_1_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currIntermediateTexture)[1].handle;
|
||||
state.texture_units[0].texture_2d = intermediateTextures[currScreen][1].handle;
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
state.texture_units[1].texture_2d = areatex.handle;
|
||||
state.texture_units[1].sampler = samplers[1].handle;
|
||||
|
|
@ -875,14 +917,14 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currAntialiasFBOTexture).handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, antialiasFBOTexture[currScreen].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SMAA_PASS_2_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currIntermediateTexture)[2].handle;
|
||||
state.texture_units[0].texture_2d = intermediateTextures[currScreen][2].handle;
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
state.texture_units[1].texture_2d = (*currIntermediateTexture)[3].handle;
|
||||
state.texture_units[1].texture_2d = intermediateTextures[currScreen][3].handle;
|
||||
state.texture_units[1].sampler = samplers[1].handle;
|
||||
GLuint uniform_smaa_input = glGetUniformLocation(state.draw.shader_program, "SMAA_Input");
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
|
|
@ -901,7 +943,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.viewport.width = textureWidth;
|
||||
state.viewport.height = textureHeight;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (*currAntialiasFBOTexture).handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, antialiasFBOTexture[currScreen].handle, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
state.draw.shader_program = SimplePresent_shader.handle;
|
||||
state.Apply();
|
||||
|
|
@ -928,7 +970,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.draw.shader_program = AREA_SAMPLING_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currAntialiasFBOTexture).handle;
|
||||
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
|
||||
state.texture_units[0].sampler = samplers[0].handle;
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
glUniform1i(uniform_convert_colors, 2);
|
||||
|
|
@ -951,7 +993,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.draw.shader_program = Present_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currAntialiasFBOTexture).handle;
|
||||
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
glUniform1i(uniform_convert_colors, 2);
|
||||
|
|
@ -973,7 +1015,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
|
|||
state.draw.shader_program = Present_shader.handle;
|
||||
state.Apply();
|
||||
AttachUniforms();
|
||||
state.texture_units[0].texture_2d = (*currAntialiasFBOTexture).handle;
|
||||
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
|
||||
if (scalingMode == 1){
|
||||
state.texture_units[0].sampler = samplers[1].handle;
|
||||
} else {
|
||||
|
|
@ -1095,13 +1137,24 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
|||
currBottomTextureHeight = static_cast<float>(screen_infos[2].texture.width * GetResolutionScaleFactor());
|
||||
if (currTopTextureWidth != prevTopTextureWidth || currTopTextureHeight != prevTopTextureHeight || currBottomTextureWidth != prevBottomTextureWidth || currBottomTextureHeight != prevBottomTextureHeight){
|
||||
AllocatePPTextures();
|
||||
LOG_INFO(Render_OpenGL, "PrevTopTexture Res: {}x{}, CurrTopTexture Res: {}x{}, PrevBottomTexture Res: {}x{}, CurrBottomTexture Res: {}x{}", prevTopTextureWidth, prevTopTextureHeight, currTopTextureWidth, currTopTextureHeight, prevBottomTextureWidth, prevBottomTextureHeight, currBottomTextureWidth, currBottomTextureHeight);
|
||||
// LOG_INFO(Render_OpenGL, "PrevTopTexture Res: {}x{}, CurrTopTexture Res: {}x{}, PrevBottomTexture Res: {}x{}, CurrBottomTexture Res: {}x{}", prevTopTextureWidth, prevTopTextureHeight, currTopTextureWidth, currTopTextureHeight, prevBottomTextureWidth, prevBottomTextureHeight, currBottomTextureWidth, currBottomTextureHeight);
|
||||
}
|
||||
prevTopTextureWidth = currTopTextureWidth;
|
||||
prevTopTextureHeight = currTopTextureHeight;
|
||||
prevBottomTextureWidth = currBottomTextureWidth;
|
||||
prevBottomTextureHeight = currBottomTextureHeight;
|
||||
|
||||
//Track Layout Changes
|
||||
currScreenRects[0] = layout.top_screen;
|
||||
currScreenRects[1] = layout.bottom_screen;
|
||||
currScreenRects[2] = layout.additional_screen;
|
||||
if (currScreenRects[0] != prevScreenRects[0] || currScreenRects[1] != prevScreenRects[1] || currScreenRects[2] != prevScreenRects[2]){
|
||||
AllocateOutputSizeTextures();
|
||||
}
|
||||
prevScreenRects[0] = currScreenRects[0];
|
||||
prevScreenRects[1] = currScreenRects[1];
|
||||
prevScreenRects[2] = currScreenRects[2];
|
||||
|
||||
//Set the Viewport
|
||||
state.viewport.x = 0;
|
||||
state.viewport.y = 0;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ private:
|
|||
void ReloadShader(Settings::StereoRenderOption render_3d);
|
||||
void AllocateSMAATextures();
|
||||
void AllocatePPTextures();
|
||||
void AllocateOutputSizeTextures();
|
||||
void PrepareRendertarget();
|
||||
void RenderScreenshot();
|
||||
void RenderToMailbox(const Layout::FramebufferLayout& layout,
|
||||
|
|
@ -102,19 +103,22 @@ private:
|
|||
OGLProgram SMAA_PASS_1_shader;
|
||||
OGLProgram SMAA_PASS_2_shader;
|
||||
OGLProgram AREA_SAMPLING_shader;
|
||||
OGLProgram FSR_PASS_0_shader;
|
||||
OGLProgram FSR_PASS_1_shader;
|
||||
OGLProgram SharpBilinear_shader;
|
||||
OGLFramebuffer screenshot_framebuffer;
|
||||
std::array<OGLSampler, 2> samplers;
|
||||
|
||||
// OpenGL objects for post processing
|
||||
OGLFramebuffer textureFBO;
|
||||
std::array<OGLTexture, 5> intermediateTextureTop;
|
||||
std::array<OGLTexture, 5> intermediateTextureBottom;
|
||||
OGLTexture antialiasFBOTextureTop;
|
||||
OGLTexture antialiasFBOTextureBottom;
|
||||
//Textures for Top and Bottom Screen Respectively
|
||||
std::array<std::array<OGLTexture, 5>, 2> intermediateTextures;
|
||||
std::array<OGLTexture, 2> antialiasFBOTexture;
|
||||
|
||||
OGLTexture* currAntialiasFBOTexture;
|
||||
std::array<OGLTexture, 5>* currIntermediateTexture;
|
||||
|
||||
//Intermediate Textures at output size. These are for Top Screen, Bottom Screen and Additional Screen Respectively
|
||||
std::array<std::array<OGLTexture, 3>, 3> intermediateOutputSizeTextures;
|
||||
std::array<Common::Rectangle<u32>, 3> prevScreenRects;
|
||||
std::array<Common::Rectangle<u32>, 3> currScreenRects;
|
||||
OGLTexture areatex;
|
||||
OGLTexture searchtex;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue