2021-07-06 19:37:47 +00:00
|
|
|
/*
|
|
|
|
PowerVR2 buffer shader
|
|
|
|
Authors: leilei
|
|
|
|
|
|
|
|
This file is part of Flycast.
|
|
|
|
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2023-01-24 13:26:14 +00:00
|
|
|
#include "postprocess.h"
|
2023-01-21 15:59:38 +00:00
|
|
|
|
2023-03-27 14:31:57 +00:00
|
|
|
#ifdef LIBRETRO
|
2023-01-21 15:59:38 +00:00
|
|
|
#include <array>
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
PostProcessor postProcessor;
|
|
|
|
|
2021-07-19 10:49:47 +00:00
|
|
|
static const char* VertexShaderSource = R"(
|
|
|
|
in vec3 in_pos;
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
void main()
|
|
|
|
{
|
|
|
|
gl_Position = vec4(in_pos, 1.0);
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
2021-07-19 10:49:47 +00:00
|
|
|
static const char* FragmentShaderSource = R"(
|
2021-07-06 19:37:47 +00:00
|
|
|
#define LUMBOOST 0
|
|
|
|
|
|
|
|
#if TARGET_GL == GLES2 || TARGET_GL == GLES3
|
|
|
|
precision highp float;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uniform int FrameCount;
|
|
|
|
uniform sampler2D Texture;
|
2023-03-27 14:31:57 +00:00
|
|
|
uniform vec2 videoShift;
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
// compatibility #defines
|
|
|
|
#define Source Texture
|
|
|
|
#define TextureSize textureSize(Texture, 0)
|
2023-03-27 14:31:57 +00:00
|
|
|
#define vTexCoord ((gl_FragCoord.xy + videoShift) / vec2(textureSize(Texture, 0)))
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
float dithertable[16] = float[](
|
|
|
|
16.,4.,13.,1.,
|
|
|
|
8.,12.,5.,9.,
|
|
|
|
14.,2.,15.,3.,
|
|
|
|
6.,10.,7.,11.
|
|
|
|
);
|
|
|
|
|
|
|
|
//#pragma parameter INTERLACED "PVR - Interlace smoothing" 1.00 0.00 1.00 1.0
|
|
|
|
//#pragma parameter VGASIGNAL "PVR - VGA signal loss" 0.00 0.00 1.00 1.0
|
|
|
|
//#pragma parameter LUMBOOST "PVR - Luminance gain" 0.35 0.00 1.00 0.01
|
|
|
|
|
|
|
|
#define LUM_R (76.0/255.0)
|
|
|
|
#define LUM_G (150.0/255.0)
|
|
|
|
#define LUM_B (28.0/255.0)
|
|
|
|
|
|
|
|
void main()
|
|
|
|
{
|
|
|
|
vec2 texcoord = vTexCoord;
|
2023-09-23 12:57:19 +00:00
|
|
|
texcoord.y = 1. - texcoord.y;
|
2021-07-06 19:37:47 +00:00
|
|
|
vec2 texcoord2 = vTexCoord;
|
2023-09-23 12:57:19 +00:00
|
|
|
texcoord2.y = 1. - texcoord2.y;
|
2021-07-06 19:37:47 +00:00
|
|
|
texcoord2.x *= float(TextureSize.x);
|
|
|
|
texcoord2.y *= float(TextureSize.y);
|
2021-07-19 10:49:47 +00:00
|
|
|
vec4 color = texture(Source, texcoord);
|
2021-07-06 19:37:47 +00:00
|
|
|
float fc = mod(float(FrameCount), 2.0);
|
|
|
|
|
|
|
|
#if INTERLACED == 1
|
|
|
|
// Blend vertically for composite mode
|
|
|
|
int taps = int(3);
|
|
|
|
float tap = (2.666f/float(taps)) / float(min(TextureSize.y, 720));
|
|
|
|
vec2 texcoord4 = vTexCoord;
|
2023-09-23 12:57:19 +00:00
|
|
|
texcoord4.y = 1. - texcoord4.y;
|
2021-07-06 19:37:47 +00:00
|
|
|
texcoord4.y -= tap * 2.f;
|
|
|
|
int bl;
|
2023-04-13 16:07:17 +00:00
|
|
|
vec4 ble = vec4(0.0);
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
for (bl=0;bl<taps;bl++)
|
|
|
|
{
|
|
|
|
texcoord4.y += tap;
|
2021-07-19 10:49:47 +00:00
|
|
|
ble.rgb += (texture(Source, texcoord4).rgb / float(taps+1));
|
2021-07-06 19:37:47 +00:00
|
|
|
}
|
|
|
|
color.rgb = (color.rgb / float(taps+1)) + ( ble.rgb );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if LUMBOOST == 1
|
|
|
|
// Some games use a luminance boost (JSR etc)
|
|
|
|
color.rgb += (((color.r * LUM_R) + (color.g * LUM_G) + (color.b * LUM_B)) * LUMBOOST);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if DITHERING == 1
|
|
|
|
// Dither
|
|
|
|
int ditdex = int(mod(texcoord2.x, 4.0)) * 4 + int(mod(texcoord2.y, 4.0));
|
|
|
|
int yeh = 0;
|
|
|
|
float ohyes;
|
|
|
|
vec4 how;
|
|
|
|
|
|
|
|
for (yeh=ditdex; yeh<(ditdex+16); yeh++) ohyes = ((((dithertable[yeh-15]) - 1.f) * 0.1));
|
|
|
|
color.rb -= (ohyes / 128.);
|
|
|
|
color.g -= (ohyes / 128.);
|
|
|
|
{
|
|
|
|
vec4 reduct; // 16 bits per pixel (5-6-5)
|
|
|
|
reduct.r = 32.;
|
|
|
|
reduct.g = 64.;
|
|
|
|
reduct.b = 32.;
|
|
|
|
how = color;
|
|
|
|
how = pow(how, vec4(1.0, 1.0, 1.0, 1.0)); how *= reduct; how = floor(how); how = how / reduct; how = pow(how, vec4(1.0, 1.0, 1.0, 1.0));
|
|
|
|
}
|
|
|
|
|
|
|
|
color.rb = how.rb;
|
|
|
|
color.g = how.g;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if VGASIGNAL == 1
|
|
|
|
// There's a bit of a precision drop involved in the RGB565ening for VGA
|
|
|
|
// I'm not sure why that is. it's exhibited on PVR1 and PVR3 hardware too
|
|
|
|
if (mod(color.r*32, 2.0)>0) color.r -= 0.023;
|
|
|
|
if (mod(color.g*64, 2.0)>0) color.g -= 0.01;
|
|
|
|
if (mod(color.b*32, 2.0)>0) color.b -= 0.023;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// RGB565 clamp
|
|
|
|
|
|
|
|
color.rb = floor(color.rb * 32. + 0.5)/32.;
|
|
|
|
color.g = floor(color.g * 64. + 0.5)/64.;
|
|
|
|
|
|
|
|
#if VGASIGNAL == 1
|
|
|
|
// VGA Signal Loss, which probably is very wrong but i tried my best
|
|
|
|
int taps = 32;
|
|
|
|
float tap = 12.0/taps;
|
|
|
|
vec2 texcoord4 = vTexCoord;
|
2023-09-23 12:57:19 +00:00
|
|
|
texcoord4.y = 1. - texcoord4.y;
|
2021-07-06 19:37:47 +00:00
|
|
|
texcoord4.x = texcoord4.x + (2.0/640.0);
|
|
|
|
texcoord4.y = texcoord4.y;
|
2021-07-19 10:49:47 +00:00
|
|
|
vec4 blur1 = texture(Source, texcoord4);
|
2021-07-06 19:37:47 +00:00
|
|
|
int bl;
|
2023-04-13 16:07:17 +00:00
|
|
|
vec4 ble = vec4(0.0);
|
2021-07-06 19:37:47 +00:00
|
|
|
for (bl=0;bl<taps;bl++)
|
|
|
|
{
|
|
|
|
float e = 1;
|
|
|
|
if (bl>=3)
|
2023-04-13 16:07:17 +00:00
|
|
|
e=0.35;
|
2021-07-06 19:37:47 +00:00
|
|
|
texcoord4.x -= (tap / 640);
|
2021-07-19 10:49:47 +00:00
|
|
|
ble.rgb += (texture(Source, texcoord4).rgb * e) / (taps/(bl+1));
|
2021-07-06 19:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
color.rgb += ble.rgb * 0.015;
|
|
|
|
|
|
|
|
//color.rb += (4.0/255.0);
|
|
|
|
color.g += (9.0/255.0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
FragColor = vec4(color);
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
class PostProcessShader
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void select(bool dither, bool interlaced, bool vga)
|
|
|
|
{
|
|
|
|
u32 key = ((int)dither << 2) | ((int)interlaced << 1) | (int)vga;
|
|
|
|
if (shaders[key].program == 0)
|
|
|
|
shaders[key].compile(dither, interlaced, vga);
|
|
|
|
shaders[key].select();
|
|
|
|
}
|
|
|
|
static void term()
|
|
|
|
{
|
|
|
|
for (auto& shader : shaders)
|
|
|
|
{
|
|
|
|
if (shader.program != 0)
|
|
|
|
{
|
|
|
|
glDeleteProgram(shader.program);
|
|
|
|
shader.program = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void compile(bool dither, bool interlaced, bool vga)
|
|
|
|
{
|
2021-07-19 10:49:47 +00:00
|
|
|
OpenGlSource vertexShader;
|
|
|
|
vertexShader.addSource(VertexCompatShader)
|
|
|
|
.addSource(VertexShaderSource);
|
|
|
|
|
|
|
|
OpenGlSource fragmentShader;
|
|
|
|
fragmentShader.addConstant("DITHERING", dither)
|
|
|
|
.addConstant("INTERLACED", interlaced)
|
|
|
|
.addConstant("VGASIGNAL", vga)
|
|
|
|
.addSource(PixelCompatShader)
|
|
|
|
.addSource(FragmentShaderSource);
|
|
|
|
|
|
|
|
program = gl_CompileAndLink(vertexShader.generate().c_str(), fragmentShader.generate().c_str());
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
//setup texture 0 as the input for the shader
|
|
|
|
GLint gu = glGetUniformLocation(program, "Texture");
|
|
|
|
if (gu != -1)
|
|
|
|
glUniform1i(gu, 0);
|
|
|
|
|
|
|
|
frameCountUniform = glGetUniformLocation(program, "FrameCount");
|
2023-03-27 14:31:57 +00:00
|
|
|
videoShiftUniform = glGetUniformLocation(program, "videoShift");
|
2021-07-06 19:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void select()
|
|
|
|
{
|
|
|
|
glcache.UseProgram(program);
|
|
|
|
glUniform1f(frameCountUniform, FrameCount);
|
2023-10-10 09:37:00 +00:00
|
|
|
float shift[] = { -gl.ofbo.shiftX, gl.ofbo.shiftY };
|
2023-03-27 14:31:57 +00:00
|
|
|
glUniform2fv(videoShiftUniform, 1, shift);
|
2021-07-06 19:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLuint program = 0;
|
2023-03-27 14:31:57 +00:00
|
|
|
GLint frameCountUniform = -1;
|
|
|
|
GLint videoShiftUniform = -1;
|
2021-07-06 19:37:47 +00:00
|
|
|
static std::array<PostProcessShader, 8> shaders;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::array<PostProcessShader, 8> PostProcessShader::shaders;
|
|
|
|
|
2023-03-27 14:31:57 +00:00
|
|
|
void PostProcessor::VertexArray::defineVtxAttribs()
|
|
|
|
{
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);
|
|
|
|
glDisableVertexAttribArray(1);
|
|
|
|
glDisableVertexAttribArray(2);
|
|
|
|
glDisableVertexAttribArray(3);
|
|
|
|
}
|
|
|
|
|
2022-10-23 14:32:42 +00:00
|
|
|
void PostProcessor::init(int width, int height)
|
2021-07-06 19:37:47 +00:00
|
|
|
{
|
2023-03-27 14:31:57 +00:00
|
|
|
framebuffer = std::make_unique<GlFramebuffer>(width, height, true, true);
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
float vertices[] = {
|
|
|
|
-1, 1, 1,
|
|
|
|
-1, -1, 1,
|
|
|
|
1, 1, 1,
|
|
|
|
1, -1, 1,
|
|
|
|
};
|
2023-03-27 14:31:57 +00:00
|
|
|
vertexBuffer = std::make_unique<GlBuffer>(GL_ARRAY_BUFFER, GL_STATIC_DRAW);
|
|
|
|
vertexBuffer->update(vertices, sizeof(vertices));
|
2021-07-06 19:37:47 +00:00
|
|
|
glCheck();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PostProcessor::term()
|
|
|
|
{
|
2022-10-23 14:32:42 +00:00
|
|
|
framebuffer.reset();
|
2023-03-27 14:31:57 +00:00
|
|
|
vertexBuffer.reset();
|
|
|
|
vertexArray.term();
|
2021-07-06 19:37:47 +00:00
|
|
|
PostProcessShader::term();
|
|
|
|
glCheck();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GLuint PostProcessor::getFramebuffer(int width, int height)
|
|
|
|
{
|
2022-10-23 14:32:42 +00:00
|
|
|
if (framebuffer != nullptr
|
|
|
|
&& (width != framebuffer->getWidth() || height != framebuffer->getHeight()))
|
2021-07-06 19:37:47 +00:00
|
|
|
term();
|
|
|
|
|
2022-10-23 14:32:42 +00:00
|
|
|
if (framebuffer == nullptr)
|
|
|
|
init(width, height);
|
|
|
|
|
|
|
|
return framebuffer->getFramebuffer();
|
2021-07-06 19:37:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PostProcessor::render(GLuint output_fbo)
|
|
|
|
{
|
|
|
|
glcache.Disable(GL_SCISSOR_TEST);
|
|
|
|
glcache.Disable(GL_DEPTH_TEST);
|
|
|
|
glcache.Disable(GL_STENCIL_TEST);
|
|
|
|
glcache.Disable(GL_CULL_FACE);
|
|
|
|
glcache.Disable(GL_BLEND);
|
|
|
|
|
2023-03-27 14:31:57 +00:00
|
|
|
if (!config::PowerVR2Filter)
|
|
|
|
{
|
2023-09-23 12:57:19 +00:00
|
|
|
// Just handle shifting and Y flipping
|
2023-03-27 14:31:57 +00:00
|
|
|
if (gl.gl_major < 3)
|
|
|
|
{
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, output_fbo);
|
|
|
|
glViewport(0, 0, framebuffer->getWidth(), framebuffer->getHeight());
|
|
|
|
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
static float vertices[20] = {
|
2023-10-10 09:37:00 +00:00
|
|
|
-1.f, -1.f, 1.f, 0.f, 1.f,
|
|
|
|
-1.f, 1.f, 1.f, 0.f, 0.f,
|
|
|
|
1.f, -1.f, 1.f, 1.f, 1.f,
|
|
|
|
1.f, 1.f, 1.f, 1.f, 0.f,
|
2023-03-27 14:31:57 +00:00
|
|
|
};
|
|
|
|
vertices[0] = vertices[5] = -1.f + gl.ofbo.shiftX * 2.f / framebuffer->getWidth();
|
|
|
|
vertices[10] = vertices[15] = vertices[0] + 2;
|
2023-10-10 09:37:00 +00:00
|
|
|
vertices[1] = vertices[11] = -1.f - gl.ofbo.shiftY * 2.f / framebuffer->getHeight();
|
|
|
|
vertices[6] = vertices[16] = vertices[1] + 2;
|
2023-03-27 14:31:57 +00:00
|
|
|
glcache.Disable(GL_BLEND);
|
2023-10-10 09:37:00 +00:00
|
|
|
drawQuad(framebuffer->getTexture(), false, false, vertices);
|
2023-03-27 14:31:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifndef GLES2
|
|
|
|
framebuffer->bind(GL_READ_FRAMEBUFFER);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, output_fbo);
|
|
|
|
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
2023-09-24 11:09:00 +00:00
|
|
|
glBlitFramebuffer(-gl.ofbo.shiftX, -gl.ofbo.shiftY, framebuffer->getWidth() - gl.ofbo.shiftX, framebuffer->getHeight() - gl.ofbo.shiftY,
|
2023-09-23 12:57:19 +00:00
|
|
|
0, framebuffer->getHeight(), framebuffer->getWidth(), 0,
|
2023-03-27 14:31:57 +00:00
|
|
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, output_fbo);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-24 11:09:00 +00:00
|
|
|
if (_pvrrc == nullptr)
|
|
|
|
// Framebuffer render: no dithering
|
2023-09-25 16:23:26 +00:00
|
|
|
PostProcessShader::select(false,
|
2023-09-24 11:09:00 +00:00
|
|
|
SPG_CONTROL.interlace,
|
|
|
|
FB_R_CTRL.vclk_div == 1 && SPG_CONTROL.interlace == 0);
|
|
|
|
else
|
|
|
|
PostProcessShader::select(pvrrc.fb_W_CTRL.fb_dither == 1 && pvrrc.fb_W_CTRL.fb_packmode <= 3 && !config::EmulateFramebuffer,
|
|
|
|
SPG_CONTROL.interlace,
|
|
|
|
FB_R_CTRL.vclk_div == 1 && SPG_CONTROL.interlace == 0);
|
2023-10-10 09:37:00 +00:00
|
|
|
vertexArray.bind(vertexBuffer.get());
|
2021-07-06 19:37:47 +00:00
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, output_fbo);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2022-10-23 14:32:42 +00:00
|
|
|
glcache.BindTexture(GL_TEXTURE_2D, framebuffer->getTexture());
|
2021-07-06 19:37:47 +00:00
|
|
|
|
2023-03-27 14:31:57 +00:00
|
|
|
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
|
2021-07-06 19:37:47 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
2023-03-27 14:31:57 +00:00
|
|
|
GlVertexArray::unbind();
|
2021-07-06 19:37:47 +00:00
|
|
|
}
|
2023-03-27 14:31:57 +00:00
|
|
|
#endif // LIBRETRO
|