flycast/core/rend/gles/gles.cpp

1482 lines
40 KiB
C++
Raw Normal View History

#include "glcache.h"
2019-10-19 16:34:24 +00:00
#include "gles.h"
2020-03-28 16:58:01 +00:00
#include "hw/pvr/ta.h"
2021-07-05 17:44:08 +00:00
#ifndef LIBRETRO
2020-03-28 16:58:01 +00:00
#include "rend/gui.h"
2021-07-05 17:44:08 +00:00
#else
#include "vmu_xhair.h"
#endif
#include "rend/osd.h"
2020-03-28 16:58:01 +00:00
#include "rend/TexCache.h"
#include "rend/transform_matrix.h"
2020-03-28 16:58:01 +00:00
#include "wsi/gl_context.h"
#include "emulator.h"
#include "naomi2.h"
#include "rend/gles/postprocess.h"
2020-03-28 16:58:01 +00:00
2023-01-21 15:59:38 +00:00
#ifdef TEST_AUTOMATION
#include "cfg/cfg.h"
#endif
2020-03-28 16:58:01 +00:00
#include <cmath>
#include <memory>
2013-12-19 17:10:14 +00:00
#ifdef GLES
2018-10-28 23:31:24 +00:00
#ifndef GL_RED
#define GL_RED 0x1903
#endif
#ifndef GL_MAJOR_VERSION
#define GL_MAJOR_VERSION 0x821B
#endif
2021-07-05 17:44:08 +00:00
#ifndef GL_MINOR_VERSION
#define GL_MINOR_VERSION 0x821C
#endif
#endif
2013-12-19 17:10:14 +00:00
//Fragment and vertex shaders code
const char* ShaderCompatSource = R"(
#define GLES2 0
#define GLES3 1
#define GL2 2
#define GL3 3
#if TARGET_GL == GL2
#define highp
#define lowp
#define mediump
#endif
#if TARGET_GL == GLES3
out highp vec4 FragColor;
#define gl_FragColor FragColor
#define FOG_CHANNEL a
#elif TARGET_GL == GL3
out highp vec4 FragColor;
#define gl_FragColor FragColor
#define FOG_CHANNEL r
#else
#define texture texture2D
#define FOG_CHANNEL a
#endif
)";
const char *VertexCompatShader = R"(
#if TARGET_GL == GLES2 || TARGET_GL == GL2
#define in attribute
#define out varying
#endif
)";
const char *PixelCompatShader = R"(
#if TARGET_GL == GLES2 || TARGET_GL == GL2
#define in varying
#endif
)";
const char* GouraudSource = R"(
#if (TARGET_GL == GL3 || TARGET_GL == GLES3) && pp_Gouraud == 0
#define INTERPOLATION flat
#else
#define INTERPOLATION
#endif
2021-07-19 10:49:47 +00:00
)";
2021-07-19 10:49:47 +00:00
static const char* VertexShaderSource = R"(
/* Vertex constants*/
uniform highp vec4 depth_scale;
uniform highp mat4 ndcMat;
uniform highp float sp_FOG_DENSITY;
/* Vertex input */
in highp vec4 in_pos;
in lowp vec4 in_base;
in lowp vec4 in_offs;
in highp vec2 in_uv;
/* output */
INTERPOLATION out highp vec4 vtx_base;
INTERPOLATION out highp vec4 vtx_offs;
out highp vec3 vtx_uv;
void main()
{
highp vec4 vpos = ndcMat * in_pos;
vtx_base = in_base;
vtx_offs = in_offs;
#if TARGET_GL == GLES2
vtx_uv = vec3(in_uv, vpos.z * sp_FOG_DENSITY);
vpos.w = 1.0 / vpos.z;
vpos.z = depth_scale.x + depth_scale.y * vpos.w;
vpos.xy *= vpos.w;
#else
#if DIV_POS_Z == 1
vpos /= vpos.z;
vpos.z = vpos.w;
#endif
#if pp_Gouraud == 1 && DIV_POS_Z != 1
vtx_base *= vpos.z;
vtx_offs *= vpos.z;
#endif
vtx_uv = vec3(in_uv, vpos.z);
#if DIV_POS_Z != 1
vtx_uv.xy *= vpos.z;
vpos.w = 1.0;
vpos.z = 0.0;
#endif
#endif
gl_Position = vpos;
}
)";
2013-12-19 17:10:14 +00:00
2021-07-19 10:49:47 +00:00
const char* PixelPipelineShader = R"(
#define PI 3.1415926
/* Shader program params*/
/* gles has no alpha test stage, so its emulated on the shader */
uniform highp float cp_AlphaTestValue;
uniform lowp vec4 pp_ClipTest;
uniform lowp vec3 sp_FOG_COL_RAM,sp_FOG_COL_VERT;
uniform highp float sp_FOG_DENSITY;
uniform sampler2D tex,fog_table;
uniform lowp float trilinear_alpha;
uniform lowp vec4 fog_clamp_min;
uniform lowp vec4 fog_clamp_max;
#if pp_Palette == 1
uniform sampler2D palette;
uniform mediump int palette_index;
#endif
#if DITHERING == 1
uniform lowp vec4 ditherColorMax;
#endif
/* Vertex input*/
INTERPOLATION in highp vec4 vtx_base;
INTERPOLATION in highp vec4 vtx_offs;
in highp vec3 vtx_uv;
lowp float fog_mode2(highp float w)
{
highp float z = clamp(
#if TARGET_GL == GLES2
vtx_uv.z
#elif DIV_POS_Z == 1
sp_FOG_DENSITY / w
#else
sp_FOG_DENSITY * w
#endif
, 1.0, 255.9999);
mediump float exp = floor(log2(z));
highp float m = z * 16.0 / pow(2.0, exp) - 16.0;
mediump float idx = floor(m) + exp * 16.0 + 0.5;
highp vec4 fog_coef = texture(fog_table, vec2(idx / 128.0, 0.75 - (m - floor(m)) / 2.0));
return fog_coef.FOG_CHANNEL;
}
highp vec4 fog_clamp(lowp vec4 col)
{
#if FogClamping == 1
return clamp(col, fog_clamp_min, fog_clamp_max);
#else
return col;
#endif
}
#if pp_Palette == 1
lowp vec4 palettePixel(highp vec3 coords)
{
#if TARGET_GL == GLES2 || TARGET_GL == GL2
highp int color_idx = int(floor(texture(tex, coords.xy).FOG_CHANNEL * 255.0 + 0.5)) + palette_index;
highp vec2 c = vec2((mod(float(color_idx), 32.0) * 2.0 + 1.0) / 64.0, (float(color_idx / 32) * 2.0 + 1.0) / 64.0);
return texture(palette, c);
#else
#if DIV_POS_Z == 1
highp int color_idx = int(floor(texture(tex, coords.xy).FOG_CHANNEL * 255.0 + 0.5)) + palette_index;
#else
highp int color_idx = int(floor(textureProj(tex, coords).FOG_CHANNEL * 255.0 + 0.5)) + palette_index;
#endif
2021-07-19 10:49:47 +00:00
highp ivec2 c = ivec2(color_idx % 32, color_idx / 32);
return texelFetch(palette, c, 0);
#endif
}
#endif
#if TARGET_GL == GLES2
#define depth gl_FragCoord.w
#else
#define depth vtx_uv.z
#endif
void main()
{
// Clip inside the box
#if pp_ClipInside == 1
if (gl_FragCoord.x >= pp_ClipTest.x && gl_FragCoord.x <= pp_ClipTest.z
&& gl_FragCoord.y >= pp_ClipTest.y && gl_FragCoord.y <= pp_ClipTest.w)
discard;
#endif
highp vec4 color = vtx_base;
highp vec4 offset = vtx_offs;
#if pp_Gouraud == 1 && TARGET_GL != GLES2 && DIV_POS_Z != 1
color /= vtx_uv.z;
offset /= vtx_uv.z;
#endif
#if pp_UseAlpha==0
color.a=1.0;
#endif
#if pp_FogCtrl==3
color = vec4(sp_FOG_COL_RAM.rgb, fog_mode2(depth));
#endif
#if pp_Texture==1
{
#if pp_Palette == 0
#if TARGET_GL == GLES2 || TARGET_GL == GL2 || DIV_POS_Z == 1
lowp vec4 texcol = texture(tex, vtx_uv.xy);
#else
lowp vec4 texcol = textureProj(tex, vtx_uv);
#endif
#else
lowp vec4 texcol = palettePixel(vtx_uv);
#endif
#if pp_BumpMap == 1
highp float s = PI / 2.0 * (texcol.a * 15.0 * 16.0 + texcol.r * 15.0) / 255.0;
highp float r = 2.0 * PI * (texcol.g * 15.0 * 16.0 + texcol.b * 15.0) / 255.0;
texcol.a = clamp(offset.a + offset.r * sin(s) + offset.g * cos(s) * cos(r - 2.0 * PI * offset.b), 0.0, 1.0);
texcol.rgb = vec3(1.0, 1.0, 1.0);
#else
#if pp_IgnoreTexA==1
texcol.a=1.0;
#endif
#endif
#if pp_ShadInstr==0
{
color=texcol;
}
#endif
#if pp_ShadInstr==1
{
color.rgb*=texcol.rgb;
color.a=texcol.a;
}
#endif
#if pp_ShadInstr==2
{
color.rgb=mix(color.rgb,texcol.rgb,texcol.a);
}
#endif
#if pp_ShadInstr==3
{
color*=texcol;
}
#endif
#if pp_Offset==1 && pp_BumpMap == 0
color.rgb += offset.rgb;
#endif
}
#endif
color = fog_clamp(color);
#if pp_FogCtrl == 0
color.rgb = mix(color.rgb, sp_FOG_COL_RAM.rgb, fog_mode2(depth));
#endif
#if pp_FogCtrl == 1 && pp_Offset==1 && pp_BumpMap == 0
color.rgb = mix(color.rgb, sp_FOG_COL_VERT.rgb, offset.a);
#endif
#if pp_TriLinear == 1
color *= trilinear_alpha;
#endif
#if cp_AlphaTest == 1
color.a = floor(color.a * 255.0 + 0.5) / 255.0;
if (cp_AlphaTestValue > color.a)
discard;
color.a = 1.0;
#endif
//color.rgb = vec3(vtx_uv.z * sp_FOG_DENSITY / 128.0);
#if TARGET_GL != GLES2
#if DIV_POS_Z == 1
highp float w = 100000.0 / vtx_uv.z;
#else
highp float w = 100000.0 * vtx_uv.z;
#endif
gl_FragDepth = log2(1.0 + max(w, -0.999999)) / 34.0;
#if DITHERING == 1
mediump float ditherTable[16] = float[](
0.9375, 0.1875, 0.75, 0.,
0.4375, 0.6875, 0.25, 0.5,
0.8125, 0.0625, 0.875, 0.125,
0.3125, 0.5625, 0.375, 0.625
);
mediump float r = ditherTable[int(mod(gl_FragCoord.y, 4.)) * 4 + int(mod(gl_FragCoord.x, 4.))];
// 31 for 5-bit color, 63 for 6 bits, 15 for 4 bits
color += r / ditherColorMax;
// avoid rounding
color = floor(color * 255.) / 255.;
#endif
#endif
gl_FragColor = color;
}
)";
2013-12-19 17:10:14 +00:00
2021-07-19 10:49:47 +00:00
static const char* ModifierVolumeShader = R"(
uniform lowp float sp_ShaderColor;
2021-07-19 10:49:47 +00:00
/* Vertex input*/
in highp vec3 vtx_uv;
void main()
{
#if TARGET_GL != GLES2
#if DIV_POS_Z == 1
highp float w = 100000.0 / vtx_uv.z;
#else
highp float w = 100000.0 * vtx_uv.z;
#endif
gl_FragDepth = log2(1.0 + max(w, -0.999999)) / 34.0;
#endif
gl_FragColor=vec4(0.0, 0.0, 0.0, sp_ShaderColor);
}
)";
2013-12-19 17:10:14 +00:00
2021-07-19 10:49:47 +00:00
const char* OSD_VertexShader = R"(
uniform highp vec4 scale;
in highp vec4 in_pos;
in lowp vec4 in_base;
in mediump vec2 in_uv;
out lowp vec4 vtx_base;
out mediump vec2 vtx_uv;
void main()
{
vtx_base = in_base;
vtx_uv = in_uv;
highp vec4 vpos = in_pos;
vpos.w = 1.0;
vpos.z = vpos.w;
vpos.xy = vpos.xy * scale.xy - scale.zw;
gl_Position = vpos;
}
)";
2021-07-19 10:49:47 +00:00
const char* OSD_Shader = R"(
in lowp vec4 vtx_base;
in mediump vec2 vtx_uv;
uniform sampler2D tex;
void main()
{
gl_FragColor = vtx_base * texture(tex, vtx_uv);
}
)";
2013-12-19 17:10:14 +00:00
static void gl_free_osd_resources();
GLCache glcache;
2013-12-19 17:10:14 +00:00
gl_ctx gl;
2018-06-05 10:18:09 +00:00
GLuint fogTextureId;
GLuint paletteTextureId;
glm::mat4 ViewportMatrix;
#ifdef TEST_AUTOMATION
void do_swap_automation()
{
static FILE* video_file = fopen(cfgLoadStr("record", "rawvid","").c_str(), "wb");
extern bool do_screenshot;
GlFramebuffer *framebuffer = gl.ofbo2.ready ? gl.ofbo2.framebuffer.get() : gl.ofbo.framebuffer.get();
if (framebuffer == nullptr)
return;
int bytesz = framebuffer->getWidth() * framebuffer->getHeight() * 3;
if (video_file)
{
u8* img = new u8[bytesz];
framebuffer->bind(GL_READ_FRAMEBUFFER);
glReadPixels(0, 0, framebuffer->getWidth(), framebuffer->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, img);
fwrite(img, 1, bytesz, video_file);
delete[] img;
fflush(video_file);
}
if (do_screenshot)
{
u8* img = new u8[bytesz];
framebuffer->bind(GL_READ_FRAMEBUFFER);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, framebuffer->getWidth(), framebuffer->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, img);
dump_screenshot(img, framebuffer->getWidth(), framebuffer->getHeight());
delete[] img;
dc_exit();
2022-04-03 18:46:50 +00:00
flycast_term();
exit(0);
}
}
#endif
static void gl_delete_shaders()
{
for (const auto& it : gl.shaders)
{
if (it.second.program != 0)
glcache.DeleteProgram(it.second.program);
}
gl.shaders.clear();
glcache.DeleteProgram(gl.modvol_shader.program);
gl.modvol_shader.program = 0;
glcache.DeleteProgram(gl.n2ModVolShader.program);
gl.n2ModVolShader.program = 0;
}
void termGLCommon()
2019-02-17 23:38:11 +00:00
{
termQuad();
// palette, fog
glcache.DeleteTextures(1, &fogTextureId);
fogTextureId = 0;
glcache.DeleteTextures(1, &paletteTextureId);
paletteTextureId = 0;
// RTT
gl.rtt.framebuffer.reset();
gl_free_osd_resources();
gl.ofbo.framebuffer.reset();
glcache.DeleteTextures(1, &gl.dcfb.tex);
gl.dcfb.tex = 0;
gl.ofbo2.framebuffer.reset();
gl.fbscaling.framebuffer.reset();
gl.videorouting.framebuffer.reset();
#ifdef LIBRETRO
postProcessor.term();
termVmuLightgun();
#endif
}
static void gles_term()
{
gl.vbo.mainVAO.term();
gl.vbo.modvolVAO.term();
gl.vbo.geometry.reset();
gl.vbo.modvols.reset();
gl.vbo.idxs.reset();
termGLCommon();
gl_delete_shaders();
}
2013-12-19 17:10:14 +00:00
void findGLVersion()
{
gl.index_type = GL_UNSIGNED_INT;
gl.gl_major = theGLContext.getMajorVersion();
gl.gl_minor = theGLContext.getMinorVersion();
gl.is_gles = theGLContext.isGLES();
2019-10-19 16:34:24 +00:00
if (gl.is_gles)
{
gl.border_clamp_supported = false;
if (gl.gl_major >= 3)
{
gl.gl_version = "GLES3";
gl.glsl_version_header = "#version 300 es";
if (gl.gl_major > 3 || gl.gl_minor >= 2)
gl.border_clamp_supported = true;
gl.prim_restart_supported = false;
gl.prim_restart_fixed_supported = true;
}
else
{
gl.gl_version = "GLES2";
gl.glsl_version_header = "";
gl.index_type = GL_UNSIGNED_SHORT;
gl.prim_restart_supported = false;
gl.prim_restart_fixed_supported = false;
}
gl.single_channel_format = GL_ALPHA;
const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
if (strstr(extensions, "GL_OES_packed_depth_stencil") != NULL)
gl.GL_OES_packed_depth_stencil_supported = true;
if (strstr(extensions, "GL_OES_depth24") != NULL)
gl.GL_OES_depth24_supported = true;
if (!gl.GL_OES_packed_depth_stencil_supported && gl.gl_major < 3)
INFO_LOG(RENDERER, "Packed depth/stencil not supported: no modifier volumes when rendering to a texture");
2021-07-05 17:44:08 +00:00
GLint ranges[2];
GLint precision;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, ranges, &precision);
gl.highp_float_supported = (ranges[0] != 0 || ranges[1] != 0) && precision != 0;
if (!gl.border_clamp_supported)
gl.border_clamp_supported = strstr(extensions, "GL_EXT_texture_border_clamp") != nullptr;
}
else
{
if (gl.gl_major >= 3)
{
gl.gl_version = "GL3";
2020-04-26 08:03:57 +00:00
#if defined(__APPLE__)
2018-10-30 10:48:44 +00:00
gl.glsl_version_header = "#version 150";
#else
gl.glsl_version_header = "#version 130";
2018-10-30 10:48:44 +00:00
#endif
gl.single_channel_format = GL_RED;
gl.prim_restart_supported = gl.gl_major > 3 || gl.gl_minor >= 1; // 3.1 min
gl.prim_restart_fixed_supported = gl.gl_major > 4
|| (gl.gl_major == 4 && gl.gl_minor >= 3); // 4.3 min
}
else
{
gl.gl_version = "GL2";
gl.glsl_version_header = "#version 120";
gl.single_channel_format = GL_ALPHA;
gl.prim_restart_supported = false;
gl.prim_restart_fixed_supported = false;
}
2021-07-05 17:44:08 +00:00
gl.highp_float_supported = true;
gl.border_clamp_supported = true;
}
2021-07-05 17:44:08 +00:00
gl.max_anisotropy = 1.f;
#if !defined(GLES2)
2021-07-05 17:44:08 +00:00
if (gl.gl_major >= 3)
{
bool anisotropicExtension = false;
const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
// glGetString(GL_EXTENSIONS) is deprecated and might return NULL in core contexts.
// In that case, use glGetStringi instead
if (extensions == nullptr)
2021-07-05 17:44:08 +00:00
{
GLint n = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
for (GLint i = 0; i < n; i++)
2021-07-05 17:44:08 +00:00
{
const char* extension = (const char *)glGetStringi(GL_EXTENSIONS, i);
if (!strcmp(extension, "GL_EXT_texture_filter_anisotropic"))
{
anisotropicExtension = true;
break;
}
2021-07-05 17:44:08 +00:00
}
}
else if (strstr(extensions, "GL_EXT_texture_filter_anisotropic") != nullptr)
anisotropicExtension = true;
if (anisotropicExtension)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &gl.max_anisotropy);
2021-07-05 17:44:08 +00:00
}
#endif
gl.mesa_nouveau = strstr((const char *)glGetString(GL_VERSION), "Mesa") != nullptr && !strcmp((const char *)glGetString(GL_VENDOR), "nouveau");
NOTICE_LOG(RENDERER, "OpenGL%s version %d.%d", gl.is_gles ? " ES" : "", gl.gl_major, gl.gl_minor);
while (glGetError() != GL_NO_ERROR)
;
}
struct ShaderUniforms_t ShaderUniforms;
2013-12-19 17:10:14 +00:00
GLuint gl_CompileShader(const char* shader,GLuint type)
{
GLint result;
GLint compile_log_len;
GLuint rv=glCreateShader(type);
glShaderSource(rv, 1,&shader, NULL);
glCompileShader(rv);
//lets see if it compiled ...
glGetShaderiv(rv, GL_COMPILE_STATUS, &result);
glGetShaderiv(rv, GL_INFO_LOG_LENGTH, &compile_log_len);
if (!result && compile_log_len>0)
{
char* compile_log=(char*)malloc(compile_log_len);
*compile_log=0;
glGetShaderInfoLog(rv, compile_log_len, &compile_log_len, compile_log);
WARN_LOG(RENDERER, "Shader: %s \n%s", result ? "compiled!" : "failed to compile", compile_log);
2013-12-19 17:10:14 +00:00
free(compile_log);
}
return rv;
}
GLuint gl_CompileAndLink(const char *vertexShader, const char *fragmentShader)
2013-12-19 17:10:14 +00:00
{
//create shaders
GLuint vs = gl_CompileShader(vertexShader, GL_VERTEX_SHADER);
GLuint ps = gl_CompileShader(fragmentShader, GL_FRAGMENT_SHADER);
2013-12-19 17:10:14 +00:00
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, ps);
//bind vertex attribute to vbo inputs
glBindAttribLocation(program, VERTEX_POS_ARRAY, "in_pos");
glBindAttribLocation(program, VERTEX_COL_BASE_ARRAY, "in_base");
glBindAttribLocation(program, VERTEX_COL_OFFS_ARRAY, "in_offs");
glBindAttribLocation(program, VERTEX_UV_ARRAY, "in_uv");
// Per-pixel attributes
2018-10-04 08:29:23 +00:00
glBindAttribLocation(program, VERTEX_COL_BASE1_ARRAY, "in_base1");
glBindAttribLocation(program, VERTEX_COL_OFFS1_ARRAY, "in_offs1");
glBindAttribLocation(program, VERTEX_UV1_ARRAY, "in_uv1");
// Naomi 2
glBindAttribLocation(program, VERTEX_NORM_ARRAY, "in_normal");
2013-12-19 17:10:14 +00:00
2021-07-05 17:44:08 +00:00
#ifndef GLES
if (!gl.is_gles && gl.gl_major >= 3)
glBindFragDataLocation(program, 0, "FragColor");
#endif
2013-12-19 17:10:14 +00:00
glLinkProgram(program);
GLint result;
glGetProgramiv(program, GL_LINK_STATUS, &result);
GLint compile_log_len;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &compile_log_len);
if (!result && compile_log_len>0)
{
compile_log_len+= 1024;
2013-12-19 17:10:14 +00:00
char* compile_log=(char*)malloc(compile_log_len);
*compile_log=0;
glGetProgramInfoLog(program, compile_log_len, &compile_log_len, compile_log);
WARN_LOG(RENDERER, "Shader linking: %s \n (%d bytes), - %s -", result ? "linked" : "failed to link", compile_log_len, compile_log);
2013-12-19 17:10:14 +00:00
free(compile_log);
2018-07-16 14:52:11 +00:00
// Dump the shaders source for troubleshooting
INFO_LOG(RENDERER, "// VERTEX SHADER\n%s\n// END", vertexShader);
INFO_LOG(RENDERER, "// FRAGMENT SHADER\n%s\n// END", fragmentShader);
2013-12-19 17:10:14 +00:00
die("shader compile fail\n");
}
glDetachShader(program, vs);
glDetachShader(program, ps);
2013-12-19 17:10:14 +00:00
glDeleteShader(vs);
glDeleteShader(ps);
glcache.UseProgram(program);
2013-12-19 17:10:14 +00:00
return program;
}
PipelineShader *GetProgram(bool cp_AlphaTest, bool pp_InsideClipping,
bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr, bool pp_Offset,
u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear,
bool palette, bool naomi2, bool dithering)
2013-12-19 17:10:14 +00:00
{
u32 rv=0;
rv |= pp_InsideClipping;
rv <<= 1; rv |= cp_AlphaTest;
rv <<= 1; rv |= pp_Texture;
rv <<= 1; rv |= pp_UseAlpha;
rv <<= 1; rv |= pp_IgnoreTexA;
rv <<= 2; rv |= pp_ShadInstr;
rv <<= 1; rv |= pp_Offset;
rv <<= 2; rv |= pp_FogCtrl;
rv <<= 1; rv |= pp_Gouraud;
rv <<= 1; rv |= pp_BumpMap;
rv <<= 1; rv |= fog_clamping;
rv <<= 1; rv |= trilinear;
rv <<= 1; rv |= palette;
rv <<= 1; rv |= naomi2;
rv <<= 1, rv |= !settings.platform.isNaomi2() && config::NativeDepthInterpolation;
rv <<= 1; rv |= dithering;
2013-12-19 17:10:14 +00:00
PipelineShader *shader = &gl.shaders[rv];
if (shader->program == 0)
{
shader->cp_AlphaTest = cp_AlphaTest;
shader->pp_InsideClipping = pp_InsideClipping;
shader->pp_Texture = pp_Texture;
shader->pp_UseAlpha = pp_UseAlpha;
shader->pp_IgnoreTexA = pp_IgnoreTexA;
shader->pp_ShadInstr = pp_ShadInstr;
shader->pp_Offset = pp_Offset;
shader->pp_FogCtrl = pp_FogCtrl;
shader->pp_Gouraud = pp_Gouraud;
shader->pp_BumpMap = pp_BumpMap;
shader->fog_clamping = fog_clamping;
shader->trilinear = trilinear;
shader->palette = palette;
shader->naomi2 = naomi2;
shader->divPosZ = !settings.platform.isNaomi2() && config::NativeDepthInterpolation;
shader->dithering = dithering;
CompilePipelineShader(shader);
}
return shader;
2013-12-19 17:10:14 +00:00
}
2021-07-19 10:49:47 +00:00
class VertexSource : public OpenGlSource
{
2021-07-19 10:49:47 +00:00
public:
VertexSource(bool gouraud, bool divPosZ) : OpenGlSource() {
2021-07-19 10:49:47 +00:00
addConstant("pp_Gouraud", gouraud);
addConstant("DIV_POS_Z", divPosZ);
2021-07-19 10:49:47 +00:00
addSource(VertexCompatShader);
addSource(GouraudSource);
addSource(VertexShaderSource);
}
};
2021-07-19 10:49:47 +00:00
class FragmentShaderSource : public OpenGlSource
{
public:
FragmentShaderSource(const PipelineShader* s) : OpenGlSource()
{
addConstant("cp_AlphaTest", s->cp_AlphaTest);
addConstant("pp_ClipInside", s->pp_InsideClipping);
addConstant("pp_UseAlpha", s->pp_UseAlpha);
addConstant("pp_Texture", s->pp_Texture);
addConstant("pp_IgnoreTexA", s->pp_IgnoreTexA);
addConstant("pp_ShadInstr", s->pp_ShadInstr);
addConstant("pp_Offset", s->pp_Offset);
addConstant("pp_FogCtrl", s->pp_FogCtrl);
addConstant("pp_Gouraud", s->pp_Gouraud);
addConstant("pp_BumpMap", s->pp_BumpMap);
addConstant("FogClamping", s->fog_clamping);
addConstant("pp_TriLinear", s->trilinear);
addConstant("pp_Palette", s->palette);
addConstant("DIV_POS_Z", s->divPosZ);
addConstant("DITHERING", s->dithering);
2021-07-19 10:49:47 +00:00
addSource(PixelCompatShader);
addSource(GouraudSource);
addSource(PixelPipelineShader);
}
};
2013-12-19 17:10:14 +00:00
2021-07-19 10:49:47 +00:00
bool CompilePipelineShader(PipelineShader* s)
{
std::string vertexShader;
if (s->naomi2)
vertexShader = N2VertexSource(s->pp_Gouraud, false, s->pp_Texture).generate();
else
vertexShader = VertexSource(s->pp_Gouraud, s->divPosZ).generate();
2021-07-19 10:49:47 +00:00
FragmentShaderSource fragmentSource(s);
2013-12-19 17:10:14 +00:00
s->program = gl_CompileAndLink(vertexShader.c_str(), fragmentSource.generate().c_str());
2013-12-19 17:10:14 +00:00
//setup texture 0 as the input for the shader
GLint gu = glGetUniformLocation(s->program, "tex");
2013-12-19 17:10:14 +00:00
if (s->pp_Texture==1)
glUniform1i(gu,0);
//get the uniform locations
s->depth_scale = glGetUniformLocation(s->program, "depth_scale");
s->pp_ClipTest = glGetUniformLocation(s->program, "pp_ClipTest");
s->sp_FOG_DENSITY = glGetUniformLocation(s->program, "sp_FOG_DENSITY");
2013-12-19 17:10:14 +00:00
s->cp_AlphaTestValue= glGetUniformLocation(s->program, "cp_AlphaTestValue");
//FOG_COL_RAM,FOG_COL_VERT,FOG_DENSITY;
if (s->pp_FogCtrl==1 && s->pp_Texture==1)
s->sp_FOG_COL_VERT=glGetUniformLocation(s->program, "sp_FOG_COL_VERT");
else
s->sp_FOG_COL_VERT=-1;
if (s->pp_FogCtrl==0 || s->pp_FogCtrl==3)
{
s->sp_FOG_COL_RAM=glGetUniformLocation(s->program, "sp_FOG_COL_RAM");
}
else
{
s->sp_FOG_COL_RAM=-1;
}
2018-10-28 23:31:24 +00:00
// Setup texture 1 as the fog table
gu = glGetUniformLocation(s->program, "fog_table");
if (gu != -1)
glUniform1i(gu, 1);
// And texture 2 as palette
gu = glGetUniformLocation(s->program, "palette");
if (gu != -1)
glUniform1i(gu, 2);
s->palette_index = glGetUniformLocation(s->program, "palette_index");
s->trilinear_alpha = glGetUniformLocation(s->program, "trilinear_alpha");
2018-09-01 10:56:37 +00:00
if (s->fog_clamping)
{
s->fog_clamp_min = glGetUniformLocation(s->program, "fog_clamp_min");
s->fog_clamp_max = glGetUniformLocation(s->program, "fog_clamp_max");
}
else
{
s->fog_clamp_min = -1;
s->fog_clamp_max = -1;
}
s->ndcMat = glGetUniformLocation(s->program, "ndcMat");
s->ditherColorMax = glGetUniformLocation(s->program, "ditherColorMax");
2013-12-19 17:10:14 +00:00
if (s->naomi2)
initN2Uniforms(s);
2013-12-19 17:10:14 +00:00
ShaderUniforms.Set(s);
return true;
2013-12-19 17:10:14 +00:00
}
void OSDVertexArray::defineVtxAttribs()
{
glEnableVertexAttribArray(VERTEX_POS_ARRAY);
glVertexAttribPointer(VERTEX_POS_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, x));
glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY);
glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, r));
glEnableVertexAttribArray(VERTEX_UV_ARRAY);
glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, u));
glDisableVertexAttribArray(VERTEX_COL_OFFS_ARRAY);
}
2013-12-19 17:10:14 +00:00
#ifdef __ANDROID__
static void gl_load_osd_resources()
2018-10-04 12:49:20 +00:00
{
2021-07-19 10:49:47 +00:00
OpenGlSource vertexSource;
vertexSource.addSource(VertexCompatShader)
.addSource(OSD_VertexShader);
OpenGlSource fragmentSource;
fragmentSource.addSource(PixelCompatShader)
.addSource(OSD_Shader);
gl.OSD_SHADER.program = gl_CompileAndLink(vertexSource.generate().c_str(), fragmentSource.generate().c_str());
gl.OSD_SHADER.scale = glGetUniformLocation(gl.OSD_SHADER.program, "scale");
glUniform1i(glGetUniformLocation(gl.OSD_SHADER.program, "tex"), 0); //bind osd texture to slot 0
2018-10-04 12:49:20 +00:00
if (gl.OSD_SHADER.osd_tex == 0)
{
int width, height;
u8 *image_data = loadOSDButtons(width, height);
//Now generate the OpenGL texture object
gl.OSD_SHADER.osd_tex = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, gl.OSD_SHADER.osd_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)image_data);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
delete[] image_data;
}
gl.OSD_SHADER.geometry = std::make_unique<GlBuffer>(GL_ARRAY_BUFFER);
2018-10-04 12:49:20 +00:00
}
#endif
2013-12-19 17:10:14 +00:00
static void gl_free_osd_resources()
{
2020-03-28 11:33:48 +00:00
if (gl.OSD_SHADER.program != 0)
{
glcache.DeleteProgram(gl.OSD_SHADER.program);
gl.OSD_SHADER.program = 0;
}
if (gl.OSD_SHADER.osd_tex != 0) {
glcache.DeleteTextures(1, &gl.OSD_SHADER.osd_tex);
gl.OSD_SHADER.osd_tex = 0;
}
gl.OSD_SHADER.geometry.reset();
gl.OSD_SHADER.vao.term();
}
static void create_modvol_shader()
{
if (gl.modvol_shader.program != 0)
return;
VertexSource vertexShader(false, config::NativeDepthInterpolation);
2021-07-19 10:49:47 +00:00
OpenGlSource fragmentShader;
fragmentShader.addConstant("pp_Gouraud", 0)
.addConstant("DIV_POS_Z", config::NativeDepthInterpolation)
.addSource(PixelCompatShader)
.addSource(GouraudSource)
2021-07-19 10:49:47 +00:00
.addSource(ModifierVolumeShader);
2021-07-19 10:49:47 +00:00
gl.modvol_shader.program = gl_CompileAndLink(vertexShader.generate().c_str(), fragmentShader.generate().c_str());
gl.modvol_shader.ndcMat = glGetUniformLocation(gl.modvol_shader.program, "ndcMat");
gl.modvol_shader.sp_ShaderColor = glGetUniformLocation(gl.modvol_shader.program, "sp_ShaderColor");
gl.modvol_shader.depth_scale = glGetUniformLocation(gl.modvol_shader.program, "depth_scale");
if (gl.gl_major >= 3)
{
N2VertexSource n2vertexShader(false, true, false);
fragmentShader.setConstant("DIV_POS_Z", false);
gl.n2ModVolShader.program = gl_CompileAndLink(n2vertexShader.generate().c_str(), fragmentShader.generate().c_str());
gl.n2ModVolShader.ndcMat = glGetUniformLocation(gl.n2ModVolShader.program, "ndcMat");
gl.n2ModVolShader.sp_ShaderColor = glGetUniformLocation(gl.n2ModVolShader.program, "sp_ShaderColor");
gl.n2ModVolShader.depth_scale = glGetUniformLocation(gl.n2ModVolShader.program, "depth_scale");
gl.n2ModVolShader.mvMat = glGetUniformLocation(gl.n2ModVolShader.program, "mvMat");
gl.n2ModVolShader.projMat = glGetUniformLocation(gl.n2ModVolShader.program, "projMat");
}
}
static void gl_create_resources()
2013-12-19 17:10:14 +00:00
{
if (gl.vbo.geometry != nullptr)
// Assume the resources have already been created
return;
findGLVersion();
if (gl.gl_major >= 3)
// will be used later. Better fail fast
verify(glGenVertexArrays != nullptr);
2013-12-19 17:10:14 +00:00
//create vbos
gl.vbo.geometry = std::make_unique<GlBuffer>(GL_ARRAY_BUFFER);
gl.vbo.modvols = std::make_unique<GlBuffer>(GL_ARRAY_BUFFER);
gl.vbo.idxs = std::make_unique<GlBuffer>(GL_ELEMENT_ARRAY_BUFFER);
2013-12-19 17:10:14 +00:00
initQuad();
2013-12-19 17:10:14 +00:00
}
GLuint gl_CompileShader(const char* shader,GLuint type);
//setup
2020-07-08 16:27:42 +00:00
#ifndef __APPLE__
void gl_DebugOutput(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const void *userParam)
{
if (id == 131185)
return;
switch (severity)
{
default:
case GL_DEBUG_SEVERITY_NOTIFICATION:
case GL_DEBUG_SEVERITY_LOW:
DEBUG_LOG(RENDERER, "opengl:[%d] %s", id, message);
break;
case GL_DEBUG_SEVERITY_MEDIUM:
INFO_LOG(RENDERER, "opengl:[%d] %s", id, message);
break;
case GL_DEBUG_SEVERITY_HIGH:
WARN_LOG(RENDERER, "opengl:[%d] %s", id, message);
break;
}
}
2020-07-08 16:27:42 +00:00
#endif
2013-12-19 17:10:14 +00:00
bool OpenGLRenderer::Init()
2013-12-19 17:10:14 +00:00
{
2018-10-05 13:07:30 +00:00
glcache.EnableCache();
gl_create_resources();
2013-12-19 17:10:14 +00:00
#if 0
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
#ifdef GLES
glDebugMessageCallback((RGLGENGLDEBUGPROC)gl_DebugOutput, NULL);
#else
glDebugMessageCallback(gl_DebugOutput, NULL);
#endif
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
#endif
2018-10-04 08:29:23 +00:00
#if defined(GL_GENERATE_MIPMAP_HINT) && !defined(__SWITCH__)
if (gl.is_gles)
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
2018-05-15 20:37:40 +00:00
#endif
glCheck();
2018-05-15 20:37:40 +00:00
if (config::TextureUpscale > 1)
2013-12-19 17:10:14 +00:00
{
2018-08-01 17:43:01 +00:00
// Trick to preload the tables used by xBRZ
u32 src[] { 0x11111111, 0x22222222, 0x33333333, 0x44444444 };
u32 dst[16];
UpscalexBRZ(2, src, dst, 2, 2, false);
2013-12-19 17:10:14 +00:00
}
2018-10-05 13:07:30 +00:00
fog_needs_update = true;
forcePaletteUpdate();
2021-04-12 20:49:04 +00:00
TextureCacheData::SetDirectXColorOrder(false);
2013-12-19 17:10:14 +00:00
return true;
}
static void updateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format)
2013-12-19 17:10:14 +00:00
{
2018-10-28 23:31:24 +00:00
glActiveTexture(texture_slot);
if (fogTextureId == 0)
2013-12-19 17:10:14 +00:00
{
2018-06-05 10:18:09 +00:00
fogTextureId = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, fogTextureId);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2013-12-19 17:10:14 +00:00
}
2018-10-28 23:31:24 +00:00
else
glcache.BindTexture(GL_TEXTURE_2D, fogTextureId);
2013-12-19 17:10:14 +00:00
2018-10-28 23:31:24 +00:00
u8 temp_tex_buffer[256];
2019-10-06 15:02:17 +00:00
MakeFogTexture(temp_tex_buffer);
2018-10-28 23:31:24 +00:00
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, fog_image_format, 128, 2, 0, fog_image_format, GL_UNSIGNED_BYTE, temp_tex_buffer);
glCheck();
2013-12-19 17:10:14 +00:00
2018-10-28 23:31:24 +00:00
glActiveTexture(GL_TEXTURE0);
}
2013-12-19 17:10:14 +00:00
static void updatePaletteTexture(GLenum texture_slot)
{
glActiveTexture(texture_slot);
if (paletteTextureId == 0)
{
paletteTextureId = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, paletteTextureId);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
else
glcache.BindTexture(GL_TEXTURE_2D, paletteTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, palette32_ram);
glCheck();
glActiveTexture(GL_TEXTURE0);
}
void OpenGLRenderer::DrawOSD(bool clear_screen)
2013-12-19 17:10:14 +00:00
{
2021-07-05 17:44:08 +00:00
#ifdef LIBRETRO
void DrawVmuTexture(u8 vmu_screen_number, int width, int height);
void DrawGunCrosshair(u8 port, int width, int height);
2021-07-05 17:44:08 +00:00
if (settings.platform.isConsole())
2021-07-05 17:44:08 +00:00
{
for (int vmu_screen_number = 0 ; vmu_screen_number < 4 ; vmu_screen_number++)
if (vmu_lcd_status[vmu_screen_number * 2])
DrawVmuTexture(vmu_screen_number, width, height);
2021-07-05 17:44:08 +00:00
}
for (int lightgun_port = 0 ; lightgun_port < 4 ; lightgun_port++)
DrawGunCrosshair(lightgun_port, width, height);
2021-07-05 17:44:08 +00:00
#else
gui_display_osd();
#ifdef __ANDROID__
if (gl.OSD_SHADER.osd_tex == 0)
gl_load_osd_resources();
if (gl.OSD_SHADER.osd_tex != 0)
2013-12-19 17:10:14 +00:00
{
glcache.Disable(GL_SCISSOR_TEST);
2021-09-27 18:29:23 +00:00
glViewport(0, 0, settings.display.width, settings.display.height);
if (clear_screen)
{
glcache.ClearColor(0.7f, 0.7f, 0.7f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
renderLastFrame();
2021-09-27 18:29:23 +00:00
glViewport(0, 0, settings.display.width, settings.display.height);
}
glcache.UseProgram(gl.OSD_SHADER.program);
2013-12-19 17:10:14 +00:00
2021-09-27 18:29:23 +00:00
float scale_h = settings.display.height / 480.f;
float offs_x = (settings.display.width - scale_h * 640.f) / 2.f;
float scale[4];
2021-09-27 18:29:23 +00:00
scale[0] = 2.f / (settings.display.width / scale_h);
scale[1]= -2.f / 480.f;
2021-09-27 18:29:23 +00:00
scale[2]= 1.f - 2.f * offs_x / settings.display.width;
scale[3]= -1.f;
glUniform4fv(gl.OSD_SHADER.scale, 1, scale);
2014-01-21 15:39:05 +00:00
glActiveTexture(GL_TEXTURE0);
glcache.BindTexture(GL_TEXTURE_2D, gl.OSD_SHADER.osd_tex);
2014-01-21 15:39:05 +00:00
2021-08-06 08:30:30 +00:00
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
const std::vector<OSDVertex>& osdVertices = GetOSDVertices();
gl.OSD_SHADER.geometry->update(osdVertices.data(), osdVertices.size() * sizeof(OSDVertex));
gl.OSD_SHADER.vao.bind(gl.OSD_SHADER.geometry.get());
2014-01-21 15:39:05 +00:00
glcache.Enable(GL_BLEND);
glcache.Disable(GL_DEPTH_TEST);
glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2013-12-19 17:10:14 +00:00
glcache.DepthMask(false);
glcache.DepthFunc(GL_ALWAYS);
2013-12-19 17:10:14 +00:00
glcache.Disable(GL_CULL_FACE);
int dfa = osdVertices.size() / 4;
2013-12-19 17:10:14 +00:00
2019-02-18 23:49:24 +00:00
for (int i = 0; i < dfa; i++)
glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4);
glCheck();
imguiDriver->setFrameRendered();
}
2021-07-05 17:44:08 +00:00
#endif
#endif
GlVertexArray::unbind();
2013-12-19 17:10:14 +00:00
}
void OpenGLRenderer::Process(TA_context* ctx)
{
if (gl.gl_major < 3 && settings.platform.isNaomi2())
throw FlycastException("OpenGL ES 3.0+ required for Naomi 2");
if (KillTex)
TexCache.Clear();
TexCache.Cleanup();
if (fog_needs_update && config::Fog)
2018-08-26 14:58:10 +00:00
{
fog_needs_update = false;
updateFogTexture((u8 *)FOG_TABLE, getFogTextureSlot(), gl.single_channel_format);
}
if (palette_updated)
2018-08-26 14:58:10 +00:00
{
updatePaletteTexture(getPaletteTextureSlot());
palette_updated = false;
}
ta_parse(ctx, gl.prim_restart_fixed_supported || gl.prim_restart_supported);
}
static void upload_vertex_indices()
2013-12-19 17:10:14 +00:00
{
if (gl.index_type == GL_UNSIGNED_SHORT)
{
static std::vector<u16> short_idx;
short_idx.clear();
short_idx.reserve(pvrrc.idx.size());
for (u32 i : pvrrc.idx)
short_idx.push_back(i);
gl.vbo.idxs->update(short_idx.data(), short_idx.size() * sizeof(u16));
}
else
gl.vbo.idxs->update(pvrrc.idx.data(), pvrrc.idx.size() * sizeof(decltype(*pvrrc.idx.data())));
glCheck();
}
bool OpenGLRenderer::renderFrame(int width, int height)
{
initVideoRoutingFrameBuffer();
bool is_rtt = pvrrc.isRTT;
2013-12-19 17:10:14 +00:00
float vtx_max_fZ = pvrrc.fZ_max;
2013-12-19 17:10:14 +00:00
//sanitise the values, now with NaN detection (for omap)
//0x49800000 is 1024*1024. Using integer math to avoid issues w/ infs and nans
if ((s32&)vtx_max_fZ < 0 || (u32&)vtx_max_fZ > 0x49800000)
vtx_max_fZ = 10 * 1024;
2013-12-19 17:10:14 +00:00
//add some extra range to avoid clipping border cases
vtx_max_fZ *= 1.001f;
2013-12-19 17:10:14 +00:00
TransformMatrix<COORD_OPENGL> matrices(pvrrc, is_rtt ? pvrrc.getFramebufferWidth() : width,
is_rtt ? pvrrc.getFramebufferHeight() : height);
ShaderUniforms.ndcMat = matrices.GetNormalMatrix();
const glm::mat4& scissor_mat = matrices.GetScissorMatrix();
ViewportMatrix = matrices.GetViewportMatrix();
2013-12-19 17:10:14 +00:00
ShaderUniforms.depth_coefs[0] = 2.f / vtx_max_fZ;
ShaderUniforms.depth_coefs[1] = -1.f;
ShaderUniforms.depth_coefs[2] = 0;
ShaderUniforms.depth_coefs[3] = 0;
2013-12-19 17:10:14 +00:00
//VERT and RAM fog color constants
FOG_COL_VERT.getRGBColor(ShaderUniforms.ps_FOG_COL_VERT);
FOG_COL_RAM.getRGBColor(ShaderUniforms.ps_FOG_COL_RAM);
2013-12-19 17:10:14 +00:00
//Fog density constant
2021-11-26 17:08:41 +00:00
ShaderUniforms.fog_den_float = FOG_DENSITY.get() * config::ExtraDepthScale;
2013-12-19 17:10:14 +00:00
pvrrc.fog_clamp_min.getRGBAColor(ShaderUniforms.fog_clamp_min);
pvrrc.fog_clamp_max.getRGBAColor(ShaderUniforms.fog_clamp_max);
2018-09-01 10:56:37 +00:00
if (config::ModifierVolumes)
{
create_modvol_shader();
glcache.UseProgram(gl.modvol_shader.program);
if (gl.modvol_shader.depth_scale != -1)
glUniform4fv(gl.modvol_shader.depth_scale, 1, ShaderUniforms.depth_coefs);
glUniformMatrix4fv(gl.modvol_shader.ndcMat, 1, GL_FALSE, &ShaderUniforms.ndcMat[0][0]);
glUniform1f(gl.modvol_shader.sp_ShaderColor, 1 - FPU_SHAD_SCALE.scale_factor / 256.f);
glcache.UseProgram(gl.n2ModVolShader.program);
if (gl.n2ModVolShader.depth_scale != -1)
glUniform4fv(gl.n2ModVolShader.depth_scale, 1, ShaderUniforms.depth_coefs);
glUniformMatrix4fv(gl.n2ModVolShader.ndcMat, 1, GL_FALSE, &ShaderUniforms.ndcMat[0][0]);
glUniform1f(gl.n2ModVolShader.sp_ShaderColor, 1 - FPU_SHAD_SCALE.scale_factor / 256.f);
}
2013-12-19 17:10:14 +00:00
ShaderUniforms.PT_ALPHA=(PT_ALPHA_REF&0xFF)/255.0f;
if (config::EmulateFramebuffer && pvrrc.fb_W_CTRL.fb_dither && pvrrc.fb_W_CTRL.fb_packmode <= 3)
{
ShaderUniforms.dithering = true;
switch (pvrrc.fb_W_CTRL.fb_packmode)
{
case 0: // 0555 KRGB 16 bit
case 3: // 1555 ARGB 16 bit
ShaderUniforms.ditherColorMax[0] = ShaderUniforms.ditherColorMax[1] = ShaderUniforms.ditherColorMax[2] = 31.f;
ShaderUniforms.ditherColorMax[3] = 255.f;
break;
case 1: // 565 RGB 16 bit
ShaderUniforms.ditherColorMax[0] = ShaderUniforms.ditherColorMax[2] = 31.f;
ShaderUniforms.ditherColorMax[1] = 63.f;
ShaderUniforms.ditherColorMax[3] = 255.f;
break;
case 2: // 4444 ARGB 16 bit
ShaderUniforms.ditherColorMax[0] = ShaderUniforms.ditherColorMax[1]
= ShaderUniforms.ditherColorMax[2] = ShaderUniforms.ditherColorMax[3] = 15.f;
break;
default:
break;
}
}
else
{
ShaderUniforms.dithering = false;
}
for (auto& it : gl.shaders)
{
glcache.UseProgram(it.second.program);
ShaderUniforms.Set(&it.second);
resetN2UniformCache(&it.second);
}
#ifndef GLES2
if (gl.prim_restart_fixed_supported)
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
#ifndef GLES
else if (gl.prim_restart_supported) {
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(-1);
}
#endif
#endif
2013-12-19 17:10:14 +00:00
//setup render target first
if (is_rtt)
{
if (BindRTT() == 0)
return false;
2013-12-19 17:10:14 +00:00
}
else
{
this->width = width;
this->height = height;
getVideoShift(gl.ofbo.shiftX, gl.ofbo.shiftY);
2021-07-05 17:44:08 +00:00
#ifdef LIBRETRO
if (config::EmulateFramebuffer)
{
if (init_output_framebuffer(width, height) == 0)
return false;
}
2021-07-06 19:37:47 +00:00
else
{
glBindFramebuffer(GL_FRAMEBUFFER, postProcessor.getFramebuffer(width, height));
glViewport(0, 0, width, height);
}
2021-07-05 17:44:08 +00:00
#else
if (init_output_framebuffer(width, height) == 0)
return false;
2021-07-05 17:44:08 +00:00
#endif
2013-12-19 17:10:14 +00:00
}
bool wide_screen_on = !is_rtt && config::Widescreen && !matrices.IsClipped()
&& !config::Rotate90 && !config::EmulateFramebuffer;
2013-12-19 17:10:14 +00:00
//Color is cleared by the background plane
2013-12-19 17:10:14 +00:00
glcache.Disable(GL_SCISSOR_TEST);
2013-12-19 17:10:14 +00:00
glcache.DepthMask(GL_TRUE);
glClearDepthf(0.0);
glStencilMask(0xFF); glCheck();
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCheck();
if (!is_rtt)
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
else
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);
2013-12-19 17:10:14 +00:00
if (is_rtt || pvrrc.clearFramebuffer)
glClear(GL_COLOR_BUFFER_BIT);
//move vertex to gpu
//Main VBO
gl.vbo.geometry->update(&pvrrc.verts[0], pvrrc.verts.size() * sizeof(decltype(pvrrc.verts[0])));
2015-05-08 15:44:27 +00:00
upload_vertex_indices();
2015-05-08 15:44:27 +00:00
//Modvol VBO
if (!pvrrc.modtrig.empty())
gl.vbo.modvols->update(&pvrrc.modtrig[0], pvrrc.modtrig.size() * sizeof(decltype(pvrrc.modtrig[0])));
2013-12-19 17:10:14 +00:00
if (!wide_screen_on)
{
float fWidth;
float fHeight;
float min_x;
float min_y;
if (!is_rtt)
2019-02-09 22:59:16 +00:00
{
glm::vec4 clip_min(pvrrc.fb_X_CLIP.min, pvrrc.fb_Y_CLIP.min, 0, 1);
glm::vec4 clip_dim(pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1,
pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1, 0, 0);
clip_min = scissor_mat * clip_min;
clip_dim = scissor_mat * clip_dim;
min_x = clip_min[0];
min_y = clip_min[1];
fWidth = clip_dim[0];
fHeight = clip_dim[1];
if (fWidth < 0)
2018-08-26 14:58:10 +00:00
{
min_x += fWidth;
fWidth = -fWidth;
2018-08-26 14:58:10 +00:00
}
if (fHeight < 0)
2018-08-26 14:58:10 +00:00
{
min_y += fHeight;
fHeight = -fHeight;
}
if (matrices.GetSidebarWidth() > 0)
{
float scaled_offs_x = matrices.GetSidebarWidth();
glcache.Enable(GL_SCISSOR_TEST);
glcache.Scissor(0, 0, (GLsizei)lroundf(scaled_offs_x), (GLsizei)height);
glClear(GL_COLOR_BUFFER_BIT);
glcache.Scissor(width - scaled_offs_x, 0, (GLsizei)lroundf(scaled_offs_x + 1.f), (GLsizei)height);
glClear(GL_COLOR_BUFFER_BIT);
2018-08-26 14:58:10 +00:00
}
}
else
{
min_x = (float)pvrrc.getFramebufferMinX();
min_y = (float)pvrrc.getFramebufferMinY();
fWidth = (float)pvrrc.getFramebufferWidth() - min_x;
fHeight = (float)pvrrc.getFramebufferHeight() - min_y;
if (config::RenderResolution > 480 && !config::RenderToTextureBuffer)
{
float scale = config::RenderResolution / 480.f;
min_x *= scale;
min_y *= scale;
fWidth *= scale;
fHeight *= scale;
}
}
ShaderUniforms.base_clipping.enabled = true;
ShaderUniforms.base_clipping.x = (int)lroundf(min_x);
ShaderUniforms.base_clipping.y = (int)lroundf(min_y);
ShaderUniforms.base_clipping.width = (int)lroundf(fWidth);
ShaderUniforms.base_clipping.height = (int)lroundf(fHeight);
glcache.Scissor(ShaderUniforms.base_clipping.x, ShaderUniforms.base_clipping.y, ShaderUniforms.base_clipping.width, ShaderUniforms.base_clipping.height);
glcache.Enable(GL_SCISSOR_TEST);
}
else
{
ShaderUniforms.base_clipping.enabled = false;
}
2013-12-19 17:10:14 +00:00
DrawStrips();
2021-07-06 19:37:47 +00:00
#ifdef LIBRETRO
if (!is_rtt && !config::EmulateFramebuffer)
postProcessor.render(glsm_get_current_framebuffer());
2021-07-06 19:37:47 +00:00
#endif
2013-12-19 17:10:14 +00:00
2018-05-08 16:47:00 +00:00
if (is_rtt)
ReadRTTBuffer();
else if (config::EmulateFramebuffer)
writeFramebufferToVRAM();
2021-07-05 17:44:08 +00:00
#ifndef LIBRETRO
else {
gl.ofbo.aspectRatio = getOutputFramebufferAspectRatio();
gl.ofbo2.ready = false;
renderLastFrame();
}
2021-07-05 17:44:08 +00:00
#endif
GlVertexArray::unbind();
2013-12-19 17:10:14 +00:00
return !is_rtt;
}
void OpenGLRenderer::initVideoRoutingFrameBuffer()
{
#ifdef VIDEO_ROUTING
if (config::VideoRouting)
{
int targetWidth = (config::VideoRoutingScale ? config::VideoRoutingVRes * settings.display.width / settings.display.height : settings.display.width);
int targetHeight = (config::VideoRoutingScale ? config::VideoRoutingVRes : settings.display.height);
if (gl.videorouting.framebuffer != nullptr
&& (gl.videorouting.framebuffer->getWidth() != targetWidth || gl.videorouting.framebuffer->getHeight() != targetHeight))
gl.videorouting.framebuffer.reset();
if (gl.videorouting.framebuffer == nullptr)
gl.videorouting.framebuffer = std::make_unique<GlFramebuffer>(targetWidth, targetHeight, true, true);
}
#endif
}
void OpenGLRenderer::Term()
{
TexCache.Clear();
gles_term();
}
bool OpenGLRenderer::Render()
{
saveCurrentFramebuffer();
renderFrame(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
if (pvrrc.isRTT) {
restoreCurrentFramebuffer();
return false;
}
if (!config::EmulateFramebuffer)
{
DrawOSD(false);
frameRendered = true;
}
renderVideoRouting();
restoreCurrentFramebuffer();
return true;
}
2013-12-19 17:10:14 +00:00
void OpenGLRenderer::renderVideoRouting()
{
#ifdef VIDEO_ROUTING
if (config::VideoRouting)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.origFbo);
gl.videorouting.framebuffer->bind(GL_DRAW_FRAMEBUFFER);
glcache.Disable(GL_SCISSOR_TEST);
int targetWidth = (config::VideoRoutingScale ? config::VideoRoutingVRes * settings.display.width / settings.display.height : settings.display.width);
int targetHeight = (config::VideoRoutingScale ? config::VideoRoutingVRes : settings.display.height);
glBlitFramebuffer(0, 0, settings.display.width, settings.display.height,
0, 0, targetWidth, targetHeight,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
extern void os_VideoRoutingPublishFrameTexture(GLuint texID, GLuint texTarget, float w, float h);
os_VideoRoutingPublishFrameTexture(gl.videorouting.framebuffer->getTexture(), GL_TEXTURE_2D, targetWidth, targetHeight);
}
#endif
}
Renderer* rend_GLES2()
{
return new OpenGLRenderer();
}