2025-03-08 02:13:14 +06:00
|
|
|
#include "qt_renderercommon.hpp"
|
|
|
|
|
#include "qt_mainwindow.hpp"
|
|
|
|
|
|
2025-03-10 00:32:57 +06:00
|
|
|
#include <QCoreApplication>
|
2025-03-08 02:13:14 +06:00
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QWindow>
|
|
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QWidget>
|
|
|
|
|
#include <QEvent>
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QString>
|
|
|
|
|
|
|
|
|
|
#include <QOpenGLContext>
|
|
|
|
|
#include <QOpenGLFunctions>
|
|
|
|
|
#include <QOpenGLExtraFunctions>
|
|
|
|
|
#include <QOpenGLTexture>
|
|
|
|
|
#include <QOpenGLDebugLogger>
|
|
|
|
|
|
|
|
|
|
#include <QImage>
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
#include "qt_openglrenderer_pcem.hpp"
|
2025-03-09 01:39:07 +06:00
|
|
|
#include "qt_openglshadermanagerdialog.hpp"
|
2025-03-08 02:13:14 +06:00
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#include <86box/86box.h>
|
|
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/video.h>
|
|
|
|
|
#include <86box/path.h>
|
|
|
|
|
#include <86box/ini.h>
|
|
|
|
|
#include <86box/config.h>
|
|
|
|
|
#include <86box/qt-glslp-parser.h>
|
2025-03-08 20:18:27 +06:00
|
|
|
|
|
|
|
|
char gl3_shader_file[MAX_USER_SHADERS][512];
|
2025-03-08 02:13:14 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCALE_SOURCE 0
|
|
|
|
|
#define SCALE_VIEWPORT 1
|
|
|
|
|
#define SCALE_ABSOLUTE 2
|
|
|
|
|
|
|
|
|
|
static GLfloat matrix[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
|
|
|
|
|
|
|
|
|
|
extern int video_filter_method;
|
|
|
|
|
extern int video_vsync;
|
|
|
|
|
extern int video_focus_dim;
|
|
|
|
|
extern int video_refresh_rate;
|
|
|
|
|
|
|
|
|
|
const char *vertex_shader_default_tex_src = "#version 130\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"in vec4 VertexCoord;\n"
|
|
|
|
|
"in vec2 TexCoord;\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"out vec2 texCoord;\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"void main()\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
" gl_Position = VertexCoord;\n"
|
|
|
|
|
" texCoord = TexCoord;\n"
|
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
|
|
const char *fragment_shader_default_tex_src = "#version 130\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"in vec2 texCoord;\n"
|
|
|
|
|
"uniform sampler2D Texture;\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"out vec4 color;"
|
|
|
|
|
"\n"
|
|
|
|
|
"void main()\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
" color = texture(Texture, texCoord);\n"
|
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
|
|
const char *vertex_shader_default_color_src = "#version 130\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"in vec4 VertexCoord;\n"
|
|
|
|
|
"in vec4 Color;\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"out vec4 color;\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"void main()\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
" gl_Position = VertexCoord;\n"
|
|
|
|
|
" color = Color;\n"
|
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
|
|
const char *fragment_shader_default_color_src = "#version 130\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"in vec4 color;\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"out vec4 outColor;"
|
|
|
|
|
"\n"
|
|
|
|
|
"void main()\n"
|
|
|
|
|
"{\n"
|
|
|
|
|
" outColor = color;\n"
|
|
|
|
|
"}\n";
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
static inline int
|
2025-03-08 02:13:14 +06:00
|
|
|
next_pow2(unsigned int n)
|
|
|
|
|
{
|
|
|
|
|
n--;
|
|
|
|
|
n |= n >> 1; // Divide by 2^k for consecutive doublings of k up to 32,
|
|
|
|
|
n |= n >> 2; // and then or the results.
|
|
|
|
|
n |= n >> 4;
|
|
|
|
|
n |= n >> 8;
|
|
|
|
|
n |= n >> 16;
|
|
|
|
|
n++;
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
int
|
|
|
|
|
OpenGLRendererPCem::create_program(struct shader_program *program)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
GLint status;
|
|
|
|
|
program->id = glw.glCreateProgram();
|
|
|
|
|
glw.glAttachShader(program->id, program->vertex_shader);
|
|
|
|
|
glw.glAttachShader(program->id, program->fragment_shader);
|
|
|
|
|
|
|
|
|
|
glw.glLinkProgram(program->id);
|
|
|
|
|
|
|
|
|
|
glw.glDeleteShader(program->vertex_shader);
|
|
|
|
|
glw.glDeleteShader(program->fragment_shader);
|
|
|
|
|
|
|
|
|
|
program->vertex_shader = program->fragment_shader = 0;
|
|
|
|
|
|
|
|
|
|
glw.glGetProgramiv(program->id, GL_LINK_STATUS, &status);
|
|
|
|
|
|
|
|
|
|
if (!status) {
|
|
|
|
|
int maxLength;
|
|
|
|
|
int length;
|
|
|
|
|
glw.glGetProgramiv(program->id, GL_INFO_LOG_LENGTH, &maxLength);
|
|
|
|
|
char *log = (char *) malloc(maxLength);
|
|
|
|
|
glw.glGetProgramInfoLog(program->id, maxLength, &length, log);
|
2025-03-10 02:09:40 +06:00
|
|
|
throw opengl_init_error_pcem(tr("Program not linked:\n%1").arg(log));
|
2025-03-08 02:13:14 +06:00
|
|
|
// wx_simple_messagebox("GLSL Error", "Program not linked:\n%s", log);
|
|
|
|
|
free(log);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
int
|
|
|
|
|
OpenGLRendererPCem::compile_shader(GLenum shader_type, const char *prepend, const char *program, int *dst)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
const char *source[3];
|
|
|
|
|
char version[50];
|
|
|
|
|
int ver = 0;
|
|
|
|
|
char *version_loc = strstr(program, "#version");
|
|
|
|
|
if (version_loc)
|
|
|
|
|
ver = (int) strtol(version_loc + 8, (char **) &program, 10);
|
|
|
|
|
else {
|
|
|
|
|
ver = glsl_version[0] * 100 + glsl_version[1] * 10;
|
|
|
|
|
if (ver == 300)
|
|
|
|
|
ver = 130;
|
|
|
|
|
else if (ver == 310)
|
|
|
|
|
ver = 140;
|
|
|
|
|
else if (ver == 320)
|
|
|
|
|
ver = 150;
|
|
|
|
|
}
|
|
|
|
|
sprintf(version, "#version %d\n", ver);
|
|
|
|
|
source[0] = version;
|
|
|
|
|
source[1] = prepend ? prepend : "";
|
|
|
|
|
source[2] = program;
|
|
|
|
|
|
|
|
|
|
pclog("GLSL version %d\n", ver);
|
|
|
|
|
|
|
|
|
|
GLuint shader = glw.glCreateShader(shader_type);
|
|
|
|
|
glw.glShaderSource(shader, 3, source, NULL);
|
|
|
|
|
glw.glCompileShader(shader);
|
|
|
|
|
|
|
|
|
|
GLint status = 0;
|
|
|
|
|
glw.glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
|
|
|
if (!status) {
|
|
|
|
|
GLint length;
|
|
|
|
|
glw.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
|
|
|
|
|
char *log = (char *) malloc(length);
|
|
|
|
|
glw.glGetShaderInfoLog(shader, length, &length, log);
|
|
|
|
|
pclog("Could not compile shader: %s\n", log);
|
2025-03-10 02:09:40 +06:00
|
|
|
throw opengl_init_error_pcem(tr("Could not compile shader:\n%1").arg(log));
|
|
|
|
|
// wx_simple_messagebox("GLSL Error", "Could not compile shader:\n%s", log);
|
2025-03-08 02:13:14 +06:00
|
|
|
// pclog("Shader: %s\n", program);
|
|
|
|
|
|
|
|
|
|
free(log);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*dst = shader;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
GLuint
|
|
|
|
|
OpenGLRendererPCem::get_uniform(GLuint program, const char *name)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
return glw.glGetUniformLocation(program, name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
GLuint
|
|
|
|
|
OpenGLRendererPCem::get_attrib(GLuint program, const char *name)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
return glw.glGetAttribLocation(program, name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::find_uniforms(struct glsl_shader *glsl, int num_pass)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char s[50];
|
|
|
|
|
struct shader_pass *pass = &glsl->passes[num_pass];
|
|
|
|
|
int p = pass->program.id;
|
|
|
|
|
glw.glUseProgram(p);
|
|
|
|
|
|
|
|
|
|
struct shader_uniforms *u = &pass->uniforms;
|
|
|
|
|
|
|
|
|
|
u->mvp_matrix = get_uniform(p, "MVPMatrix");
|
|
|
|
|
u->vertex_coord = get_attrib(p, "VertexCoord");
|
|
|
|
|
u->tex_coord = get_attrib(p, "TexCoord");
|
|
|
|
|
u->color = get_attrib(p, "Color");
|
|
|
|
|
|
|
|
|
|
u->frame_count = get_uniform(p, "FrameCount");
|
|
|
|
|
u->frame_direction = get_uniform(p, "FrameDirection");
|
|
|
|
|
|
|
|
|
|
u->texture = get_uniform(p, "Texture");
|
|
|
|
|
u->input_size = get_uniform(p, "InputSize");
|
|
|
|
|
u->texture_size = get_uniform(p, "TextureSize");
|
|
|
|
|
u->output_size = get_uniform(p, "OutputSize");
|
|
|
|
|
|
|
|
|
|
u->orig.texture = get_uniform(p, "OrigTexture");
|
|
|
|
|
u->orig.input_size = get_uniform(p, "OrigInputSize");
|
|
|
|
|
u->orig.texture_size = get_uniform(p, "OrigTextureSize");
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < glsl->num_passes; ++i) {
|
|
|
|
|
sprintf(s, "Pass%dTexture", (i + 1));
|
|
|
|
|
u->pass[i].texture = get_uniform(p, s);
|
|
|
|
|
sprintf(s, "Pass%dInputSize", (i + 1));
|
|
|
|
|
u->pass[i].input_size = get_uniform(p, s);
|
|
|
|
|
sprintf(s, "Pass%dTextureSize", (i + 1));
|
|
|
|
|
u->pass[i].texture_size = get_uniform(p, s);
|
|
|
|
|
|
|
|
|
|
sprintf(s, "PassPrev%dTexture", num_pass - i);
|
|
|
|
|
u->prev_pass[i].texture = get_uniform(p, s);
|
|
|
|
|
sprintf(s, "PassPrev%dInputSize", num_pass - i);
|
|
|
|
|
u->prev_pass[i].input_size = get_uniform(p, s);
|
|
|
|
|
sprintf(s, "PassPrev%dTextureSize", num_pass - i);
|
|
|
|
|
u->prev_pass[i].texture_size = get_uniform(p, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u->prev[0].texture = get_uniform(p, "PrevTexture");
|
|
|
|
|
u->prev[0].tex_coord = get_attrib(p, "PrevTexCoord");
|
|
|
|
|
for (i = 1; i < MAX_PREV; ++i) {
|
|
|
|
|
sprintf(s, "Prev%dTexture", i);
|
|
|
|
|
u->prev[i].texture = get_uniform(p, s);
|
|
|
|
|
sprintf(s, "Prev%dTexCoord", i);
|
|
|
|
|
u->prev[i].tex_coord = get_attrib(p, s);
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < MAX_PREV; ++i)
|
|
|
|
|
if (u->prev[i].texture >= 0)
|
|
|
|
|
glsl->has_prev = 1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < glsl->num_lut_textures; ++i)
|
|
|
|
|
u->lut_textures[i] = get_uniform(p, glsl->lut_textures[i].name);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < glsl->num_parameters; ++i)
|
|
|
|
|
u->parameters[i] = get_uniform(p, glsl->parameters[i].id);
|
|
|
|
|
|
|
|
|
|
glw.glUseProgram(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
set_scale_mode(char *scale, int *dst)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(scale, "viewport"))
|
|
|
|
|
*dst = SCALE_VIEWPORT;
|
|
|
|
|
else if (!strcmp(scale, "absolute"))
|
|
|
|
|
*dst = SCALE_ABSOLUTE;
|
|
|
|
|
else
|
|
|
|
|
*dst = SCALE_SOURCE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
setup_scale(struct shader *shader, struct shader_pass *pass)
|
|
|
|
|
{
|
|
|
|
|
set_scale_mode(shader->scale_type_x, &pass->scale.mode[0]);
|
|
|
|
|
set_scale_mode(shader->scale_type_y, &pass->scale.mode[1]);
|
|
|
|
|
pass->scale.value[0] = shader->scale_x;
|
|
|
|
|
pass->scale.value[1] = shader->scale_y;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::create_texture(struct shader_texture *tex)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (tex->width > max_texture_size)
|
|
|
|
|
tex->width = max_texture_size;
|
|
|
|
|
if (tex->height > max_texture_size)
|
|
|
|
|
tex->height = max_texture_size;
|
|
|
|
|
pclog("Create texture with size %dx%d\n", tex->width, tex->height);
|
|
|
|
|
glw.glGenTextures(1, (GLuint *) &tex->id);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, tex->id);
|
|
|
|
|
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex->wrap_mode);
|
|
|
|
|
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex->wrap_mode);
|
|
|
|
|
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex->min_filter);
|
|
|
|
|
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex->mag_filter);
|
|
|
|
|
glw.glTexImage2D(GL_TEXTURE_2D, 0, tex->internal_format, tex->width, tex->height, 0, tex->format, tex->type, tex->data);
|
|
|
|
|
if (tex->mipmap)
|
|
|
|
|
glw.glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_texture(struct shader_texture *tex)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (tex->id > 0)
|
|
|
|
|
glw.glDeleteTextures(1, (GLuint *) &tex->id);
|
|
|
|
|
tex->id = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_fbo(struct shader_fbo *fbo)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (fbo->id >= 0) {
|
|
|
|
|
glw.glDeleteFramebuffers(1, (GLuint *) &fbo->id);
|
|
|
|
|
delete_texture(&fbo->texture);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_program(struct shader_program *program)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (program->vertex_shader)
|
|
|
|
|
glw.glDeleteShader(program->vertex_shader);
|
|
|
|
|
if (program->fragment_shader)
|
|
|
|
|
glw.glDeleteShader(program->fragment_shader);
|
|
|
|
|
glw.glDeleteProgram(program->id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_vbo(struct shader_vbo *vbo)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (vbo->color >= 0)
|
|
|
|
|
glw.glDeleteBuffers(1, (GLuint *) &vbo->color);
|
|
|
|
|
glw.glDeleteBuffers(1, (GLuint *) &vbo->vertex_coord);
|
|
|
|
|
glw.glDeleteBuffers(1, (GLuint *) &vbo->tex_coord);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_pass(struct shader_pass *pass)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
delete_fbo(&pass->fbo);
|
|
|
|
|
delete_vbo(&pass->vbo);
|
|
|
|
|
delete_program(&pass->program);
|
|
|
|
|
glw.glDeleteVertexArrays(1, (GLuint *) &pass->vertex_array);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_prev(struct shader_prev *prev)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
delete_fbo(&prev->fbo);
|
|
|
|
|
delete_vbo(&prev->vbo);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_shader(struct glsl_shader *glsl)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < glsl->num_passes; ++i)
|
|
|
|
|
delete_pass(&glsl->passes[i]);
|
|
|
|
|
if (glsl->has_prev) {
|
|
|
|
|
delete_pass(&glsl->prev_scene);
|
|
|
|
|
for (i = 0; i < MAX_PREV; ++i)
|
|
|
|
|
delete_prev(&glsl->prev[i]);
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < glsl->num_lut_textures; ++i)
|
|
|
|
|
delete_texture(&glsl->lut_textures[i].texture);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::delete_glsl(glsl_t *glsl)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < glsl->num_shaders; ++i)
|
|
|
|
|
delete_shader(&glsl->shaders[i]);
|
|
|
|
|
delete_pass(&glsl->scene);
|
|
|
|
|
delete_pass(&glsl->fs_color);
|
|
|
|
|
delete_pass(&glsl->final_pass);
|
|
|
|
|
#ifdef SDL2_SHADER_DEBUG
|
|
|
|
|
delete_pass(&glsl->debug);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::create_fbo(struct shader_fbo *fbo)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
create_texture(&fbo->texture);
|
|
|
|
|
|
|
|
|
|
glw.glGenFramebuffers(1, (GLuint *) &fbo->id);
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, fbo->id);
|
|
|
|
|
glw.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->texture.id, 0);
|
|
|
|
|
|
|
|
|
|
if (glw.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
|
|
pclog("Could not create framebuffer!\n");
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::setup_fbo(struct shader *shader, struct shader_fbo *fbo)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
fbo->texture.internal_format = GL_RGBA8;
|
|
|
|
|
fbo->texture.format = GL_RGBA;
|
|
|
|
|
fbo->texture.min_filter = fbo->texture.mag_filter = shader->filter_linear ? GL_LINEAR : GL_NEAREST;
|
2025-03-08 20:18:27 +06:00
|
|
|
fbo->texture.width = 2048;
|
|
|
|
|
fbo->texture.height = 2048;
|
2025-03-08 02:13:14 +06:00
|
|
|
fbo->texture.type = GL_UNSIGNED_BYTE;
|
|
|
|
|
if (!strcmp(shader->wrap_mode, "repeat"))
|
|
|
|
|
fbo->texture.wrap_mode = GL_REPEAT;
|
|
|
|
|
else if (!strcmp(shader->wrap_mode, "mirrored_repeat"))
|
|
|
|
|
fbo->texture.wrap_mode = GL_MIRRORED_REPEAT;
|
|
|
|
|
else if (!strcmp(shader->wrap_mode, "clamp_to_edge"))
|
|
|
|
|
fbo->texture.wrap_mode = GL_CLAMP_TO_EDGE;
|
|
|
|
|
else
|
|
|
|
|
fbo->texture.wrap_mode = GL_CLAMP_TO_BORDER;
|
|
|
|
|
fbo->srgb = 0;
|
|
|
|
|
if (shader->srgb_framebuffer) {
|
|
|
|
|
fbo->texture.internal_format = GL_SRGB8_ALPHA8;
|
|
|
|
|
fbo->srgb = 1;
|
|
|
|
|
} else if (shader->float_framebuffer) {
|
|
|
|
|
fbo->texture.internal_format = GL_RGBA32F;
|
|
|
|
|
fbo->texture.type = GL_FLOAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fbo->texture.mipmap)
|
|
|
|
|
fbo->texture.min_filter = shader->filter_linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
|
|
|
|
|
|
|
|
|
|
create_fbo(fbo);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::recreate_fbo(struct shader_fbo *fbo, int width, int height)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (width != fbo->texture.width || height != fbo->texture.height) {
|
|
|
|
|
glw.glDeleteFramebuffers(1, (GLuint *) &fbo->id);
|
|
|
|
|
glw.glDeleteTextures(1, (GLuint *) &fbo->texture.id);
|
|
|
|
|
fbo->texture.width = width;
|
|
|
|
|
fbo->texture.height = height;
|
|
|
|
|
create_fbo(fbo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
int
|
|
|
|
|
OpenGLRendererPCem::create_default_shader_tex(struct shader_pass *pass)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (!compile_shader(GL_VERTEX_SHADER, 0, vertex_shader_default_tex_src, &pass->program.vertex_shader) || !compile_shader(GL_FRAGMENT_SHADER, 0, fragment_shader_default_tex_src, &pass->program.fragment_shader) || !create_program(&pass->program))
|
|
|
|
|
return 0;
|
|
|
|
|
glw.glGenVertexArrays(1, (GLuint *) &pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_uniforms *u = &pass->uniforms;
|
|
|
|
|
int p = pass->program.id;
|
|
|
|
|
memset(u, -1, sizeof(struct shader_uniforms));
|
|
|
|
|
u->vertex_coord = get_attrib(p, "VertexCoord");
|
|
|
|
|
u->tex_coord = get_attrib(p, "TexCoord");
|
|
|
|
|
u->texture = get_uniform(p, "Texture");
|
|
|
|
|
pass->scale.mode[0] = pass->scale.mode[1] = SCALE_SOURCE;
|
|
|
|
|
pass->scale.value[0] = pass->scale.value[1] = 1.0f;
|
|
|
|
|
pass->fbo.id = -1;
|
|
|
|
|
pass->active = 1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
int
|
|
|
|
|
OpenGLRendererPCem::create_default_shader_color(struct shader_pass *pass)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
if (!compile_shader(GL_VERTEX_SHADER, 0, vertex_shader_default_color_src, &pass->program.vertex_shader) || !compile_shader(GL_FRAGMENT_SHADER, 0, fragment_shader_default_color_src, &pass->program.fragment_shader) || !create_program(&pass->program))
|
|
|
|
|
return 0;
|
|
|
|
|
glw.glGenVertexArrays(1, (GLuint *) &pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_uniforms *u = &pass->uniforms;
|
|
|
|
|
int p = pass->program.id;
|
|
|
|
|
memset(u, -1, sizeof(struct shader_uniforms));
|
|
|
|
|
u->vertex_coord = get_attrib(p, "VertexCoord");
|
|
|
|
|
u->color = get_attrib(p, "Color");
|
|
|
|
|
pass->scale.mode[0] = pass->scale.mode[1] = SCALE_SOURCE;
|
|
|
|
|
pass->scale.value[0] = pass->scale.value[1] = 1.0f;
|
|
|
|
|
pass->fbo.id = -1;
|
|
|
|
|
pass->active = 1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* create the default scene shader */
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::create_scene_shader()
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
struct shader scene_shader_conf;
|
|
|
|
|
memset(&scene_shader_conf, 0, sizeof(struct shader));
|
|
|
|
|
create_default_shader_tex(&active_shader->scene);
|
|
|
|
|
scene_shader_conf.filter_linear = video_filter_method;
|
|
|
|
|
if (active_shader->num_shaders > 0 && active_shader->shaders[0].input_filter_linear >= 0)
|
|
|
|
|
scene_shader_conf.filter_linear = active_shader->shaders[0].input_filter_linear;
|
|
|
|
|
setup_fbo(&scene_shader_conf, &active_shader->scene.fbo);
|
|
|
|
|
|
|
|
|
|
memset(&scene_shader_conf, 0, sizeof(struct shader));
|
|
|
|
|
create_default_shader_color(&active_shader->fs_color);
|
|
|
|
|
setup_fbo(&scene_shader_conf, &active_shader->fs_color.fbo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
load_texture(const char *f, struct shader_texture *tex)
|
|
|
|
|
{
|
|
|
|
|
QImage img;
|
|
|
|
|
if (!img.load(f))
|
|
|
|
|
return 0;
|
|
|
|
|
int width, height;
|
|
|
|
|
width = img.size().width();
|
|
|
|
|
height = img.size().height();
|
|
|
|
|
|
|
|
|
|
if (width != next_pow2(width) || height != next_pow2(height))
|
|
|
|
|
img = img.scaled(next_pow2(width), next_pow2(height));
|
|
|
|
|
|
|
|
|
|
width = img.size().width();
|
|
|
|
|
height = img.size().height();
|
|
|
|
|
|
|
|
|
|
img.convertTo(QImage::Format_RGBA8888);
|
|
|
|
|
|
|
|
|
|
const GLubyte *rgb = img.constBits();
|
|
|
|
|
|
|
|
|
|
int bpp = 4;
|
|
|
|
|
|
|
|
|
|
GLubyte *data = (GLubyte *) malloc(width * height * bpp);
|
|
|
|
|
|
|
|
|
|
int x, y, Y;
|
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
|
Y = height - y - 1;
|
|
|
|
|
for (x = 0; x < width; x++) {
|
|
|
|
|
data[(y * width + x) * bpp + 0] = rgb[(Y * width + x) * 3 + 0];
|
|
|
|
|
data[(y * width + x) * bpp + 1] = rgb[(Y * width + x) * 3 + 1];
|
|
|
|
|
data[(y * width + x) * bpp + 2] = rgb[(Y * width + x) * 3 + 2];
|
|
|
|
|
data[(y * width + x) * bpp + 3] = rgb[(Y * width + x) * 3 + 3];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tex->width = width;
|
|
|
|
|
tex->height = height;
|
|
|
|
|
tex->internal_format = GL_RGBA8;
|
|
|
|
|
tex->format = GL_RGBA;
|
|
|
|
|
tex->type = GL_UNSIGNED_BYTE;
|
|
|
|
|
tex->data = data;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
glsl_t *
|
|
|
|
|
OpenGLRendererPCem::load_glslp(glsl_t *glsl, int num_shader, const char *f)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
int i, j;
|
|
|
|
|
glslp_t *p = glslp_parse(f);
|
|
|
|
|
|
|
|
|
|
if (p) {
|
|
|
|
|
char path[512];
|
|
|
|
|
char file[1024];
|
|
|
|
|
int failed = 0;
|
|
|
|
|
strcpy(path, f);
|
|
|
|
|
char *filename = path_get_filename(path);
|
|
|
|
|
|
|
|
|
|
struct glsl_shader *gshader = &glsl->shaders[num_shader];
|
|
|
|
|
|
|
|
|
|
strcpy(gshader->name, p->name);
|
|
|
|
|
*filename = 0;
|
|
|
|
|
|
|
|
|
|
gshader->num_lut_textures = p->num_textures;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < p->num_textures; ++i) {
|
|
|
|
|
struct texture *texture = &p->textures[i];
|
|
|
|
|
|
|
|
|
|
sprintf(file, "%s%s", path, texture->path);
|
|
|
|
|
|
|
|
|
|
struct shader_lut_texture *tex = &gshader->lut_textures[i];
|
|
|
|
|
strcpy(tex->name, texture->name);
|
|
|
|
|
|
|
|
|
|
pclog("Load texture %s...\n", file);
|
|
|
|
|
|
|
|
|
|
if (!load_texture(file, &tex->texture)) {
|
2025-03-10 02:09:40 +06:00
|
|
|
throw opengl_init_error_pcem(tr("Could not load texture: %s").arg(file));
|
2025-03-08 02:13:14 +06:00
|
|
|
pclog("Could not load texture %s!\n", file);
|
|
|
|
|
failed = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strcmp(texture->wrap_mode, "repeat"))
|
|
|
|
|
tex->texture.wrap_mode = GL_REPEAT;
|
|
|
|
|
else if (!strcmp(texture->wrap_mode, "mirrored_repeat"))
|
|
|
|
|
tex->texture.wrap_mode = GL_MIRRORED_REPEAT;
|
|
|
|
|
else if (!strcmp(texture->wrap_mode, "clamp_to_edge"))
|
|
|
|
|
tex->texture.wrap_mode = GL_CLAMP_TO_EDGE;
|
|
|
|
|
else
|
|
|
|
|
tex->texture.wrap_mode = GL_CLAMP_TO_BORDER;
|
|
|
|
|
|
|
|
|
|
tex->texture.mipmap = texture->mipmap;
|
|
|
|
|
|
|
|
|
|
tex->texture.min_filter = tex->texture.mag_filter = texture->linear ? GL_LINEAR : GL_NEAREST;
|
|
|
|
|
if (tex->texture.mipmap)
|
|
|
|
|
tex->texture.min_filter = texture->linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
|
|
|
|
|
|
|
|
|
|
create_texture(&tex->texture);
|
|
|
|
|
free(tex->texture.data);
|
|
|
|
|
tex->texture.data = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!failed) {
|
|
|
|
|
gshader->input_filter_linear = p->input_filter_linear;
|
|
|
|
|
|
|
|
|
|
gshader->num_parameters = p->num_parameters;
|
|
|
|
|
for (j = 0; j < gshader->num_parameters; ++j)
|
|
|
|
|
memcpy(&gshader->parameters[j], &p->parameters[j], sizeof(struct shader_parameter));
|
|
|
|
|
|
|
|
|
|
gshader->num_passes = p->num_shaders;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < p->num_shaders; ++i) {
|
|
|
|
|
struct shader *shader = &p->shaders[i];
|
|
|
|
|
struct shader_pass *pass = &gshader->passes[i];
|
|
|
|
|
|
|
|
|
|
strcpy(pass->alias, shader->alias);
|
|
|
|
|
if (!strlen(pass->alias))
|
|
|
|
|
sprintf(pass->alias, "Pass %u", (i + 1));
|
|
|
|
|
|
|
|
|
|
pclog("Creating pass %u (%s)\n", (i + 1), pass->alias);
|
|
|
|
|
pclog("Loading shader %s...\n", shader->shader_fn);
|
|
|
|
|
if (!shader->shader_program) {
|
2025-03-10 02:09:40 +06:00
|
|
|
throw opengl_init_error_pcem(tr("Could not load shader: %1").arg(shader->shader_fn));
|
2025-03-08 02:13:14 +06:00
|
|
|
// wx_simple_messagebox("GLSL Error", "Could not load shader: %s", shader->shader_fn);
|
|
|
|
|
pclog("Could not load shader %s\n", shader->shader_fn);
|
|
|
|
|
failed = 1;
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
pclog("Shader %s loaded\n", shader->shader_fn);
|
|
|
|
|
failed = !compile_shader(GL_VERTEX_SHADER, "#define VERTEX\n#define PARAMETER_UNIFORM\n",
|
|
|
|
|
shader->shader_program, &pass->program.vertex_shader)
|
|
|
|
|
|| !compile_shader(GL_FRAGMENT_SHADER, "#define FRAGMENT\n#define PARAMETER_UNIFORM\n",
|
|
|
|
|
shader->shader_program, &pass->program.fragment_shader);
|
|
|
|
|
if (failed)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!create_program(&pass->program)) {
|
|
|
|
|
failed = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
pass->frame_count_mod = shader->frame_count_mod;
|
|
|
|
|
pass->fbo.mipmap_input = shader->mipmap_input;
|
|
|
|
|
|
|
|
|
|
glw.glGenVertexArrays(1, (GLuint *) &pass->vertex_array);
|
|
|
|
|
find_uniforms(gshader, i);
|
|
|
|
|
setup_scale(shader, pass);
|
|
|
|
|
if (i == p->num_shaders - 1) /* last pass may or may not be an fbo depending on scale */
|
|
|
|
|
{
|
|
|
|
|
if (num_shader == glsl->num_shaders - 1) {
|
|
|
|
|
pass->fbo.id = -1;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < 2; ++j) {
|
|
|
|
|
if (pass->scale.mode[j] != SCALE_SOURCE || pass->scale.value[j] != 1) {
|
|
|
|
|
setup_fbo(shader, &pass->fbo);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* check if next shaders' first pass wants the input mipmapped (will this ever
|
|
|
|
|
* happen?) */
|
|
|
|
|
pass->fbo.texture.mipmap = glsl->shaders[num_shader + 1].num_passes > 0 && glsl->shaders[num_shader + 1].passes[0].fbo.mipmap_input;
|
|
|
|
|
/* check if next shader wants the output of this pass to be filtered */
|
|
|
|
|
if (glsl->shaders[num_shader + 1].num_passes > 0 && glsl->shaders[num_shader + 1].input_filter_linear >= 0)
|
|
|
|
|
shader->filter_linear = glsl->shaders[num_shader + 1].input_filter_linear;
|
|
|
|
|
setup_fbo(shader, &pass->fbo);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* check if next pass wants the input mipmapped, if so we need to generate mipmaps of this
|
|
|
|
|
* pass */
|
|
|
|
|
pass->fbo.texture.mipmap = (i + 1) < p->num_shaders && p->shaders[i + 1].mipmap_input;
|
|
|
|
|
setup_fbo(shader, &pass->fbo);
|
|
|
|
|
}
|
|
|
|
|
if (pass->fbo.srgb)
|
|
|
|
|
glsl->srgb = 1;
|
|
|
|
|
pass->active = 1;
|
|
|
|
|
}
|
|
|
|
|
if (!failed) {
|
|
|
|
|
if (gshader->has_prev) {
|
|
|
|
|
struct shader scene_shader_conf;
|
|
|
|
|
memset(&scene_shader_conf, 0, sizeof(struct shader));
|
|
|
|
|
for (i = 0; i < MAX_PREV; ++i) {
|
|
|
|
|
setup_fbo(&scene_shader_conf, &gshader->prev[i].fbo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glslp_free(p);
|
|
|
|
|
|
|
|
|
|
return glsl;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
glsl_t *
|
|
|
|
|
OpenGLRendererPCem::load_shaders(int num, char shaders[MAX_USER_SHADERS][512])
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
glsl_t *glsl;
|
|
|
|
|
|
|
|
|
|
glsl = (glsl_t *) malloc(sizeof(glsl_t));
|
|
|
|
|
memset(glsl, 0, sizeof(glsl_t));
|
|
|
|
|
|
|
|
|
|
glsl->num_shaders = num;
|
|
|
|
|
int failed = 0;
|
|
|
|
|
for (i = num - 1; i >= 0; --i) {
|
|
|
|
|
const char *f = shaders[i];
|
|
|
|
|
if (f && strlen(f)) {
|
|
|
|
|
if (!load_glslp(glsl, i, f)) {
|
|
|
|
|
failed = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (failed) {
|
|
|
|
|
delete_glsl(glsl);
|
|
|
|
|
memset(glsl, 0, sizeof(glsl_t));
|
|
|
|
|
}
|
|
|
|
|
return glsl;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::read_shader_config()
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
char s[512];
|
|
|
|
|
int i, j;
|
|
|
|
|
for (i = 0; i < active_shader->num_shaders; ++i) {
|
|
|
|
|
struct glsl_shader *shader = &active_shader->shaders[i];
|
|
|
|
|
char *name = shader->name;
|
|
|
|
|
sprintf(s, "GL3 Shaders - %s", name);
|
|
|
|
|
// shader->shader_refresh_rate = config_get_float(CFG_MACHINE, s, "shader_refresh_rate", -1);
|
|
|
|
|
for (j = 0; j < shader->num_parameters; ++j) {
|
|
|
|
|
struct shader_parameter *param = &shader->parameters[j];
|
|
|
|
|
param->value = config_get_double(s, param->id, param->default_value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OpenGLRendererPCem::OpenGLRendererPCem(QWidget *parent)
|
|
|
|
|
: QWindow(parent->windowHandle())
|
|
|
|
|
, renderTimer(new QTimer(this))
|
|
|
|
|
, options(nullptr)
|
|
|
|
|
{
|
|
|
|
|
connect(renderTimer, &QTimer::timeout, this, [this]() { this->render(); } );
|
|
|
|
|
imagebufs[0] = std::unique_ptr<uint8_t>(new uint8_t[2048 * 2048 * 4]);
|
|
|
|
|
imagebufs[1] = std::unique_ptr<uint8_t>(new uint8_t[2048 * 2048 * 4]);
|
|
|
|
|
|
|
|
|
|
buf_usage = std::vector<std::atomic_flag>(2);
|
|
|
|
|
buf_usage[0].clear();
|
|
|
|
|
buf_usage[1].clear();
|
|
|
|
|
|
|
|
|
|
QSurfaceFormat format;
|
|
|
|
|
|
|
|
|
|
setSurfaceType(QWindow::OpenGLSurface);
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
|
format.setVersion(4, 1);
|
|
|
|
|
#else
|
|
|
|
|
format.setVersion(3, 2);
|
|
|
|
|
#endif
|
|
|
|
|
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
|
|
|
|
|
format.setOption(QSurfaceFormat::DebugContext);
|
|
|
|
|
|
|
|
|
|
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
|
|
|
|
|
format.setRenderableType(QSurfaceFormat::OpenGLES);
|
|
|
|
|
|
|
|
|
|
setFormat(format);
|
|
|
|
|
|
|
|
|
|
parentWidget = parent;
|
|
|
|
|
|
2025-03-09 13:06:58 +06:00
|
|
|
source.setRect(0, 0, 100, 100);
|
2025-03-08 02:13:14 +06:00
|
|
|
isInitialized = false;
|
|
|
|
|
isFinalized = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OpenGLRendererPCem::~OpenGLRendererPCem() { finalize(); }
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::initialize()
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
context = new QOpenGLContext(this);
|
|
|
|
|
|
|
|
|
|
context->setFormat(format());
|
|
|
|
|
|
|
|
|
|
if (!context->create())
|
|
|
|
|
throw opengl_init_error_pcem(tr("Couldn't create OpenGL context."));
|
|
|
|
|
|
|
|
|
|
if (!context->makeCurrent(this))
|
|
|
|
|
throw opengl_init_error_pcem(tr("Couldn't switch to OpenGL context."));
|
|
|
|
|
|
|
|
|
|
auto version = context->format().version();
|
|
|
|
|
|
|
|
|
|
if (version.first < 3)
|
|
|
|
|
throw opengl_init_error_pcem(tr("OpenGL version 3.0 or greater is required. Current version is %1.%2").arg(version.first).arg(version.second));
|
|
|
|
|
|
|
|
|
|
glw.initializeOpenGLFunctions();
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
pclog("OpenGL information: [%s] %s (%s)\n", glw.glGetString(GL_VENDOR), glw.glGetString(GL_RENDERER), glw.glGetString(GL_VERSION));
|
|
|
|
|
glsl_version[0] = glsl_version[1] = -1;
|
|
|
|
|
glw.glGetIntegerv(GL_MAJOR_VERSION, &glsl_version[0]);
|
|
|
|
|
glw.glGetIntegerv(GL_MINOR_VERSION, &glsl_version[1]);
|
|
|
|
|
if (glsl_version[0] < 3) {
|
|
|
|
|
throw opengl_init_error_pcem(tr("OpenGL version 3.0 or greater is required. Current GLSL version is %1.%2").arg(glsl_version[0]).arg(glsl_version[1]));
|
|
|
|
|
}
|
|
|
|
|
pclog("Using OpenGL %s\n", glw.glGetString(GL_VERSION));
|
|
|
|
|
pclog("Using Shading Language %s\n", glw.glGetString(GL_SHADING_LANGUAGE_VERSION));
|
2025-03-08 02:13:14 +06:00
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
glw.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
|
|
|
|
pclog("Max texture size: %dx%d\n", max_texture_size, max_texture_size);
|
2025-03-08 02:13:14 +06:00
|
|
|
|
|
|
|
|
glw.glEnable(GL_TEXTURE_2D);
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
//renderTimer->start(75);
|
2025-03-08 02:13:14 +06:00
|
|
|
|
|
|
|
|
scene_texture.data = NULL;
|
|
|
|
|
scene_texture.width = 2048;
|
|
|
|
|
scene_texture.height = 2048;
|
|
|
|
|
scene_texture.internal_format = GL_RGBA8;
|
|
|
|
|
scene_texture.format = GL_BGRA;
|
|
|
|
|
scene_texture.type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
|
|
|
scene_texture.wrap_mode = GL_CLAMP_TO_BORDER;
|
|
|
|
|
scene_texture.min_filter = scene_texture.mag_filter = video_filter_method ? GL_LINEAR : GL_NEAREST;
|
|
|
|
|
scene_texture.mipmap = 0;
|
|
|
|
|
|
|
|
|
|
create_texture(&scene_texture);
|
|
|
|
|
|
|
|
|
|
/* load shader */
|
|
|
|
|
// const char* shaders[1];
|
|
|
|
|
// shaders[0] = gl3_shader_file;
|
|
|
|
|
//
|
|
|
|
|
// active_shader = load_shaders(1, shaders);
|
|
|
|
|
|
|
|
|
|
// const char* shaders[3];
|
|
|
|
|
// shaders[0] = "/home/phantasy/git/glsl-shaders/ntsc/ntsc-320px.glslp";
|
|
|
|
|
// shaders[1] = "/home/phantasy/git/glsl-shaders/motionblur/motionblur-simple.glslp";
|
|
|
|
|
// shaders[2] = "/home/phantasy/git/glsl-shaders/crt/crt-lottes-multipass.glslp";
|
|
|
|
|
//
|
|
|
|
|
// active_shader = load_shaders(3, shaders);
|
|
|
|
|
int num_shaders = 0;
|
|
|
|
|
for (int i = 0; i < MAX_USER_SHADERS; ++i) {
|
|
|
|
|
if (strlen(gl3_shader_file[i]))
|
|
|
|
|
++num_shaders;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
active_shader = load_shaders(num_shaders, gl3_shader_file);
|
|
|
|
|
|
|
|
|
|
create_scene_shader();
|
|
|
|
|
|
|
|
|
|
/* read config */
|
|
|
|
|
read_shader_config();
|
|
|
|
|
|
|
|
|
|
/* buffers */
|
|
|
|
|
|
|
|
|
|
GLfloat vertex[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f };
|
|
|
|
|
|
|
|
|
|
GLfloat inv_vertex[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f };
|
|
|
|
|
|
|
|
|
|
GLfloat tex_coords[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
|
|
|
|
|
|
|
|
|
|
GLfloat colors[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
|
|
|
|
|
|
|
|
|
|
/* set the scene shader buffers */
|
|
|
|
|
{
|
|
|
|
|
glw.glBindVertexArray(active_shader->scene.vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_vbo *vbo = &active_shader->scene.vbo;
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(inv_vertex), inv_vertex, GL_STATIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(active_shader->scene.uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(active_shader->scene.uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set buffers for all passes */
|
|
|
|
|
for (int j = 0; j < active_shader->num_shaders; ++j) {
|
|
|
|
|
struct glsl_shader *shader = &active_shader->shaders[j];
|
|
|
|
|
for (int i = 0; i < shader->num_passes; ++i) {
|
|
|
|
|
struct shader_uniforms *u = &shader->passes[i].uniforms;
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(shader->passes[i].vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_vbo *vbo = &shader->passes[i].vbo;
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
|
|
glw.glVertexAttribPointer(u->vertex_coord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(u->tex_coord, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
if (u->color) {
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->color);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->color);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(u->color, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < active_shader->num_shaders; ++i) {
|
|
|
|
|
struct glsl_shader *shader = &active_shader->shaders[i];
|
|
|
|
|
if (shader->has_prev) {
|
|
|
|
|
struct shader_pass *prev_pass = &shader->prev_scene;
|
|
|
|
|
create_default_shader_tex(prev_pass);
|
|
|
|
|
|
|
|
|
|
struct shader_vbo *vbo = &prev_pass->vbo;
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(prev_pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(prev_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(prev_pass->uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < MAX_PREV; ++j) {
|
|
|
|
|
struct shader_prev *prev = &shader->prev[j];
|
|
|
|
|
struct shader_vbo *prev_vbo = &prev->vbo;
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &prev_vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, prev_vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &prev_vbo->tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, prev_vbo->tex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* create final pass */
|
|
|
|
|
if (active_shader->num_shaders == 0 || active_shader->shaders[active_shader->num_shaders - 1].passes[active_shader->shaders[active_shader->num_shaders - 1].num_passes - 1].fbo.id >= 0) {
|
|
|
|
|
struct shader_pass *final_pass = &active_shader->final_pass;
|
|
|
|
|
create_default_shader_tex(final_pass);
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(final_pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_vbo *vbo = &final_pass->vbo;
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(final_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(final_pass->uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
struct shader_pass *color_pass = &active_shader->fs_color;
|
|
|
|
|
create_default_shader_color(color_pass);
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(color_pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_vbo *vbo = &color_pass->vbo;
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(color_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
|
|
|
|
|
(GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, (GLuint *) &vbo->color);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->color);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_DYNAMIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(color_pass->uniforms.color, 4, GL_FLOAT, GL_TRUE, 4 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
}
|
|
|
|
|
#ifdef SDL2_SHADER_DEBUG
|
|
|
|
|
struct shader_pass *debug_pass = &active_shader->debug;
|
|
|
|
|
create_default_shader(debug_pass);
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(debug_pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
struct shader_vbo *vbo = &debug_pass->vbo;
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, &vbo->vertex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(debug_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
|
|
|
|
|
glw.glGenBuffers(1, &vbo->tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
|
|
|
|
|
glw.glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
|
|
|
|
|
glw.glVertexAttribPointer(debug_pass->uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
glw.glBindVertexArray(0);
|
|
|
|
|
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
isFinalized = false;
|
|
|
|
|
|
|
|
|
|
emit initialized();
|
|
|
|
|
|
|
|
|
|
glw.glClearColor(0, 0, 0, 1);
|
|
|
|
|
|
|
|
|
|
glw.glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
context->swapBuffers(this);
|
|
|
|
|
} catch (const opengl_init_error_pcem &e) {
|
|
|
|
|
/* Mark all buffers as in use */
|
|
|
|
|
for (auto &flag : buf_usage)
|
|
|
|
|
flag.test_and_set();
|
|
|
|
|
|
|
|
|
|
QMessageBox::critical((QWidget *) qApp->findChild<QWindow *>(), tr("Error initializing OpenGL"), e.what() + tr("\nFalling back to software rendering."));
|
|
|
|
|
|
|
|
|
|
context->doneCurrent();
|
|
|
|
|
isFinalized = true;
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
|
|
|
|
|
emit errorInitializing();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::finalize()
|
|
|
|
|
{
|
|
|
|
|
if (isFinalized)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
delete_texture(&scene_texture);
|
|
|
|
|
|
|
|
|
|
if (active_shader) {
|
|
|
|
|
delete_glsl(active_shader);
|
|
|
|
|
free(active_shader);
|
|
|
|
|
}
|
|
|
|
|
active_shader = NULL;
|
|
|
|
|
|
|
|
|
|
context->doneCurrent();
|
|
|
|
|
|
|
|
|
|
context = nullptr;
|
|
|
|
|
|
|
|
|
|
isFinalized = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::onBlit(int buf_idx, int x, int y, int w, int h)
|
|
|
|
|
{
|
|
|
|
|
if (notReady())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
|
glViewport(
|
|
|
|
|
destination.x() * devicePixelRatio(),
|
|
|
|
|
destination.y() * devicePixelRatio(),
|
|
|
|
|
destination.width() * devicePixelRatio(),
|
|
|
|
|
destination.height() * devicePixelRatio());
|
|
|
|
|
#endif
|
2025-03-09 13:06:58 +06:00
|
|
|
|
|
|
|
|
if (source.width() != w || source.height() != h) {
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, scene_texture.id);
|
|
|
|
|
glw.glTexImage2D(GL_TEXTURE_2D, 0, (GLenum) QOpenGLTexture::RGBA8_UNorm, w, h, 0, (GLenum) QOpenGLTexture::BGRA, (GLenum) QOpenGLTexture::UInt32_RGBA8_Rev, NULL);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 02:13:14 +06:00
|
|
|
source.setRect(x, y, w, h);
|
|
|
|
|
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, scene_texture.id);
|
|
|
|
|
glw.glPixelStorei(GL_UNPACK_ROW_LENGTH, 2048);
|
2025-03-09 13:06:58 +06:00
|
|
|
glw.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, (GLenum) QOpenGLTexture::BGRA, (GLenum) QOpenGLTexture::UInt32_RGBA8_Rev, (const void *) ((uintptr_t) imagebufs[buf_idx].get() + (uintptr_t) (2048 * 4 * y + x * 4)));
|
2025-03-08 02:13:14 +06:00
|
|
|
glw.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
|
|
buf_usage[buf_idx].clear();
|
|
|
|
|
source.setRect(x, y, w, h);
|
|
|
|
|
onResize(this->width(), this->height());
|
|
|
|
|
|
|
|
|
|
render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::tuple<uint8_t *, std::atomic_flag *>>
|
|
|
|
|
OpenGLRendererPCem::getBuffers()
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::tuple<uint8_t *, std::atomic_flag *>> buffers;
|
|
|
|
|
|
|
|
|
|
buffers.push_back(std::make_tuple(imagebufs[0].get(), &buf_usage[0]));
|
|
|
|
|
buffers.push_back(std::make_tuple(imagebufs[1].get(), &buf_usage[1]));
|
|
|
|
|
|
|
|
|
|
return buffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::exposeEvent(QExposeEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
|
|
|
|
if (!isInitialized)
|
|
|
|
|
initialize();
|
|
|
|
|
|
|
|
|
|
onResize(size().width(), size().height());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::resizeEvent(QResizeEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
|
|
|
|
onResize(event->size().width(), event->size().height());
|
|
|
|
|
|
|
|
|
|
if (notReady())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
glw.glViewport(
|
|
|
|
|
destination.x() * devicePixelRatio(),
|
|
|
|
|
destination.y() * devicePixelRatio(),
|
|
|
|
|
destination.width() * devicePixelRatio(),
|
|
|
|
|
destination.height() * devicePixelRatio());
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::render_pass(struct render_data *data)
|
2025-03-08 02:13:14 +06:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
GLuint texture_unit = 0;
|
|
|
|
|
|
|
|
|
|
// pclog("pass %d: %gx%g, %gx%g -> %gx%g, %gx%g, %gx%g\n", num_pass, pass->state.input_size[0],
|
|
|
|
|
// pass->state.input_size[1], pass->state.input_texture_size[0], pass->state.input_texture_size[1],
|
|
|
|
|
// pass->state.output_size[0], pass->state.output_size[1], pass->state.output_texture_size[0],
|
|
|
|
|
// pass->state.output_texture_size[1], output_size[0], output_size[1]);
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(data->shader_pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
GLint p = data->shader_pass->program.id;
|
|
|
|
|
struct shader_uniforms *u = &data->shader_pass->uniforms;
|
|
|
|
|
|
|
|
|
|
glw.glUseProgram(p);
|
|
|
|
|
|
|
|
|
|
if (data->texture) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, data->texture);
|
|
|
|
|
glw.glUniform1i(u->texture, texture_unit);
|
|
|
|
|
texture_unit++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->color >= 0)
|
|
|
|
|
glw.glEnableVertexAttribArray(u->color);
|
|
|
|
|
|
|
|
|
|
if (u->mvp_matrix >= 0)
|
|
|
|
|
glw.glUniformMatrix4fv(u->mvp_matrix, 1, 0, matrix);
|
|
|
|
|
if (u->frame_direction >= 0)
|
|
|
|
|
glw.glUniform1i(u->frame_direction, 1);
|
|
|
|
|
|
|
|
|
|
int framecnt = data->frame_count;
|
|
|
|
|
if (data->shader_pass->frame_count_mod > 0)
|
|
|
|
|
framecnt = framecnt % data->shader_pass->frame_count_mod;
|
|
|
|
|
if (u->frame_count >= 0)
|
|
|
|
|
glw.glUniform1i(u->frame_count, framecnt);
|
|
|
|
|
|
|
|
|
|
if (u->input_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->input_size, 1, data->shader_pass->state.input_size);
|
|
|
|
|
if (u->texture_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->texture_size, 1, data->shader_pass->state.input_texture_size);
|
|
|
|
|
if (u->output_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->output_size, 1, data->output_size);
|
|
|
|
|
|
|
|
|
|
if (data->shader) {
|
|
|
|
|
/* parameters */
|
|
|
|
|
for (i = 0; i < data->shader->num_parameters; ++i)
|
|
|
|
|
if (u->parameters[i] >= 0)
|
|
|
|
|
glw.glUniform1f(u->parameters[i], data->shader->parameters[i].value);
|
|
|
|
|
|
|
|
|
|
if (data->pass > 0) {
|
|
|
|
|
struct shader_pass *passes = data->shader->passes;
|
|
|
|
|
struct shader_pass *orig = data->orig_pass;
|
|
|
|
|
if (u->orig.texture >= 0) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, orig->fbo.texture.id);
|
|
|
|
|
glw.glUniform1i(u->orig.texture, texture_unit);
|
|
|
|
|
texture_unit++;
|
|
|
|
|
}
|
|
|
|
|
if (u->orig.input_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->orig.input_size, 1, orig->state.input_size);
|
|
|
|
|
if (u->orig.texture_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->orig.texture_size, 1, orig->state.input_texture_size);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < data->pass; ++i) {
|
|
|
|
|
if (u->pass[i].texture >= 0) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, passes[i].fbo.texture.id);
|
|
|
|
|
glw.glUniform1i(u->pass[i].texture, texture_unit);
|
|
|
|
|
texture_unit++;
|
|
|
|
|
}
|
|
|
|
|
if (u->pass[i].texture_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->pass[i].texture_size, 1, passes[i].state.input_texture_size);
|
|
|
|
|
if (u->pass[i].input_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->pass[i].input_size, 1, passes[i].state.input_size);
|
|
|
|
|
|
|
|
|
|
if (u->prev_pass[i].texture >= 0) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, passes[i].fbo.texture.id);
|
|
|
|
|
glw.glUniform1i(u->prev_pass[i].texture, texture_unit);
|
|
|
|
|
texture_unit++;
|
|
|
|
|
}
|
|
|
|
|
if (u->prev_pass[i].texture_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->prev_pass[i].texture_size, 1, passes[i].state.input_texture_size);
|
|
|
|
|
if (u->prev_pass[i].input_size >= 0)
|
|
|
|
|
glw.glUniform2fv(u->prev_pass[i].input_size, 1, passes[i].state.input_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data->shader->has_prev) {
|
|
|
|
|
/* loop through each previous frame */
|
|
|
|
|
for (i = 0; i < MAX_PREV; ++i) {
|
|
|
|
|
if (u->prev[i].texture >= 0) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, data->shader->prev[i].fbo.texture.id);
|
|
|
|
|
glw.glUniform1i(u->prev[i].texture, texture_unit);
|
|
|
|
|
texture_unit++;
|
|
|
|
|
}
|
|
|
|
|
if (u->prev[i].tex_coord >= 0) {
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, data->shader->prev[i].vbo.tex_coord);
|
|
|
|
|
glw.glVertexAttribPointer(u->prev[i].tex_coord, 2, GL_FLOAT, GL_TRUE,
|
|
|
|
|
2 * sizeof(GLfloat), (GLvoid *) 0);
|
|
|
|
|
glw.glEnableVertexAttribArray(u->prev[i].tex_coord);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < data->shader->num_lut_textures; ++i) {
|
|
|
|
|
if (u->lut_textures[i] >= 0) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0 + texture_unit);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, data->shader->lut_textures[i].texture.id);
|
|
|
|
|
glw.glUniform1i(u->lut_textures[i], texture_unit);
|
|
|
|
|
texture_unit++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glw.glEnableVertexAttribArray(u->vertex_coord);
|
|
|
|
|
glw.glEnableVertexAttribArray(u->tex_coord);
|
|
|
|
|
|
|
|
|
|
glw.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
|
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
|
|
glw.glDisableVertexAttribArray(data->shader_pass->uniforms.vertex_coord);
|
|
|
|
|
glw.glDisableVertexAttribArray(data->shader_pass->uniforms.tex_coord);
|
|
|
|
|
if (data->shader_pass->uniforms.color >= 0)
|
|
|
|
|
glw.glDisableVertexAttribArray(data->shader_pass->uniforms.color);
|
|
|
|
|
|
|
|
|
|
if (data->shader && data->shader->has_prev) {
|
|
|
|
|
for (i = 0; i < MAX_PREV; ++i) {
|
|
|
|
|
if (u->prev[i].tex_coord >= 0)
|
|
|
|
|
glw.glDisableVertexAttribArray(u->prev[i].tex_coord);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(0);
|
|
|
|
|
|
|
|
|
|
glw.glUseProgram(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
OpenGLRendererPCem::event(QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
|
|
|
|
bool res = false;
|
|
|
|
|
if (!eventDelegate(event, res))
|
|
|
|
|
return QWindow::event(event);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-09 01:39:07 +06:00
|
|
|
QDialog*
|
|
|
|
|
OpenGLRendererPCem::getOptions(QWidget *parent)
|
|
|
|
|
{
|
|
|
|
|
return new OpenGLShaderManagerDialog(parent);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 02:13:14 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRendererPCem::render()
|
|
|
|
|
{
|
|
|
|
|
if (!context)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int s, i, j;
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
uint32_t x, y, w, h;
|
|
|
|
|
} window_rect;
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
window_rect.x = destination.x() * devicePixelRatio();
|
|
|
|
|
window_rect.y = destination.y() * devicePixelRatio();
|
|
|
|
|
window_rect.w = destination.width() * devicePixelRatio();
|
|
|
|
|
window_rect.h = destination.height() * devicePixelRatio();
|
|
|
|
|
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, scene_texture.id);
|
|
|
|
|
scene_texture.min_filter = scene_texture.mag_filter = video_filter_method ? GL_LINEAR : GL_NEAREST;
|
|
|
|
|
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, video_filter_method ? GL_LINEAR : GL_NEAREST);
|
|
|
|
|
glw.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, video_filter_method ? GL_LINEAR : GL_NEAREST);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, 0);
|
2025-03-08 02:13:14 +06:00
|
|
|
|
|
|
|
|
GLfloat orig_output_size[] = { (GLfloat)window_rect.w, (GLfloat)window_rect.h };
|
|
|
|
|
|
|
|
|
|
if (active_shader->srgb)
|
|
|
|
|
glw.glEnable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
|
|
|
|
|
|
struct render_data data;
|
|
|
|
|
|
|
|
|
|
/* render scene to texture */
|
|
|
|
|
{
|
|
|
|
|
struct shader_pass *pass = &active_shader->scene;
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
uint32_t x, y, w, h;
|
|
|
|
|
} rect, video_rect;
|
2025-03-10 00:32:57 +06:00
|
|
|
rect.x = 0;
|
|
|
|
|
rect.y = 0;
|
|
|
|
|
rect.w = source.width();
|
|
|
|
|
rect.h = source.height();
|
2025-03-08 02:13:14 +06:00
|
|
|
|
|
|
|
|
video_rect.x = source.x();
|
|
|
|
|
video_rect.y = source.y();
|
|
|
|
|
video_rect.w = source.width();
|
|
|
|
|
video_rect.h = source.height();
|
|
|
|
|
|
|
|
|
|
pass->state.input_size[0] = pass->state.output_size[0] = rect.w;
|
|
|
|
|
pass->state.input_size[1] = pass->state.output_size[1] = rect.h;
|
|
|
|
|
|
|
|
|
|
pass->state.input_texture_size[0] = pass->state.output_texture_size[0] = next_pow2(pass->state.output_size[0]);
|
|
|
|
|
pass->state.input_texture_size[1] = pass->state.output_texture_size[1] = next_pow2(pass->state.output_size[1]);
|
|
|
|
|
|
|
|
|
|
recreate_fbo(&active_shader->scene.fbo, pass->state.output_texture_size[0], pass->state.output_texture_size[1]);
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, active_shader->scene.fbo.id);
|
|
|
|
|
glw.glClearColor(0, 0, 0, 1);
|
|
|
|
|
glw.glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
glw.glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
|
|
|
|
|
|
|
|
|
|
GLfloat minx = 0;
|
|
|
|
|
GLfloat miny = 0;
|
|
|
|
|
GLfloat maxx = pass->state.output_size[0] / (GLfloat) pass->state.output_texture_size[0];
|
|
|
|
|
GLfloat maxy = pass->state.output_size[1] / (GLfloat) pass->state.output_texture_size[1];
|
|
|
|
|
|
|
|
|
|
pass->state.tex_coords[0] = minx;
|
|
|
|
|
pass->state.tex_coords[1] = miny;
|
|
|
|
|
pass->state.tex_coords[2] = minx;
|
|
|
|
|
pass->state.tex_coords[3] = maxy;
|
|
|
|
|
pass->state.tex_coords[4] = maxx;
|
|
|
|
|
pass->state.tex_coords[5] = miny;
|
|
|
|
|
pass->state.tex_coords[6] = maxx;
|
|
|
|
|
pass->state.tex_coords[7] = maxy;
|
|
|
|
|
|
|
|
|
|
// create input tex coords
|
2025-03-09 13:06:58 +06:00
|
|
|
minx = 0;
|
|
|
|
|
miny = 0;
|
|
|
|
|
maxx = 1;
|
|
|
|
|
maxy = 1;
|
2025-03-08 02:13:14 +06:00
|
|
|
|
|
|
|
|
GLfloat tex_coords[] = { minx, miny, minx, maxy, maxx, miny, maxx, maxy };
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.tex_coord);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * sizeof(GLfloat), tex_coords);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct render_data));
|
|
|
|
|
data.pass = -1;
|
|
|
|
|
data.shader_pass = &active_shader->scene;
|
|
|
|
|
data.texture = scene_texture.id;
|
|
|
|
|
data.output_size = orig_output_size;
|
|
|
|
|
render_pass(&data);
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct shader_pass *orig = &active_shader->scene;
|
|
|
|
|
struct shader_pass *input = &active_shader->scene;
|
|
|
|
|
|
|
|
|
|
for (s = 0; s < active_shader->num_shaders; ++s) {
|
|
|
|
|
struct glsl_shader *shader = &active_shader->shaders[s];
|
|
|
|
|
|
|
|
|
|
int frame_count = frameCounter;
|
|
|
|
|
|
|
|
|
|
/* loop through each pass */
|
|
|
|
|
for (i = 0; i < shader->num_passes; ++i) {
|
|
|
|
|
struct shader_pass *pass = &shader->passes[i];
|
|
|
|
|
|
|
|
|
|
memcpy(pass->state.input_size, input->state.output_size, 2 * sizeof(GLfloat));
|
|
|
|
|
memcpy(pass->state.input_texture_size, input->state.output_texture_size, 2 * sizeof(GLfloat));
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < 2; ++j) {
|
|
|
|
|
if (pass->scale.mode[j] == SCALE_VIEWPORT)
|
|
|
|
|
pass->state.output_size[j] = orig_output_size[j] * pass->scale.value[j];
|
|
|
|
|
else if (pass->scale.mode[j] == SCALE_ABSOLUTE)
|
|
|
|
|
pass->state.output_size[j] = pass->scale.value[j];
|
|
|
|
|
else
|
|
|
|
|
pass->state.output_size[j] = pass->state.input_size[j] * pass->scale.value[j];
|
|
|
|
|
|
|
|
|
|
pass->state.output_texture_size[j] = next_pow2(pass->state.output_size[j]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pass->fbo.id >= 0) {
|
|
|
|
|
recreate_fbo(&pass->fbo, pass->state.output_texture_size[0], pass->state.output_texture_size[1]);
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, pass->fbo.id);
|
|
|
|
|
glw.glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
|
|
|
|
|
} else
|
|
|
|
|
glw.glViewport(window_rect.x, window_rect.y, window_rect.w, window_rect.h);
|
|
|
|
|
|
|
|
|
|
glw.glClearColor(0, 0, 0, 1);
|
|
|
|
|
glw.glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
GLfloat minx = 0;
|
|
|
|
|
GLfloat miny = 0;
|
|
|
|
|
GLfloat maxx = pass->state.output_size[0] / (GLfloat) pass->state.output_texture_size[0];
|
|
|
|
|
GLfloat maxy = pass->state.output_size[1] / (GLfloat) pass->state.output_texture_size[1];
|
|
|
|
|
|
|
|
|
|
pass->state.tex_coords[0] = minx;
|
|
|
|
|
pass->state.tex_coords[1] = miny;
|
|
|
|
|
pass->state.tex_coords[2] = minx;
|
|
|
|
|
pass->state.tex_coords[3] = maxy;
|
|
|
|
|
pass->state.tex_coords[4] = maxx;
|
|
|
|
|
pass->state.tex_coords[5] = miny;
|
|
|
|
|
pass->state.tex_coords[6] = maxx;
|
|
|
|
|
pass->state.tex_coords[7] = maxy;
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.tex_coord);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * sizeof(GLfloat), input->state.tex_coords);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct render_data));
|
|
|
|
|
data.shader = shader;
|
|
|
|
|
data.pass = i;
|
|
|
|
|
data.shader_pass = pass;
|
|
|
|
|
data.texture = input->fbo.texture.id;
|
|
|
|
|
data.output_size = orig_output_size;
|
|
|
|
|
data.orig_pass = orig;
|
|
|
|
|
data.frame_count = frame_count;
|
|
|
|
|
|
|
|
|
|
render_pass(&data);
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
|
|
|
|
|
if (pass->fbo.texture.mipmap) {
|
|
|
|
|
glw.glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, pass->fbo.texture.id);
|
|
|
|
|
glw.glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
|
glw.glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input = pass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shader->has_prev) {
|
|
|
|
|
/* shift array */
|
|
|
|
|
memmove(&shader->prev[1], &shader->prev[0], MAX_PREV * sizeof(struct shader_prev));
|
|
|
|
|
memcpy(&shader->prev[0], &shader->prev[MAX_PREV], sizeof(struct shader_prev));
|
|
|
|
|
|
|
|
|
|
struct shader_pass *pass = orig;
|
|
|
|
|
struct shader_pass *prev_pass = &shader->prev_scene;
|
|
|
|
|
struct shader_prev *prev = &shader->prev[0];
|
|
|
|
|
|
|
|
|
|
memcpy(&prev_pass->state, &pass->state, sizeof(struct shader_state));
|
|
|
|
|
|
|
|
|
|
recreate_fbo(&prev->fbo, prev_pass->state.output_texture_size[0],
|
|
|
|
|
prev_pass->state.output_texture_size[1]);
|
|
|
|
|
|
|
|
|
|
memcpy(&prev_pass->fbo, &prev->fbo, sizeof(struct shader_fbo));
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, prev->fbo.id);
|
|
|
|
|
glw.glClearColor(0, 0, 0, 1);
|
|
|
|
|
glw.glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
glw.glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(prev_pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, prev->vbo.tex_coord);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * sizeof(GLfloat), pass->state.tex_coords);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, prev_pass->vbo.tex_coord);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * sizeof(GLfloat), pass->state.tex_coords);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct render_data));
|
|
|
|
|
data.shader = shader;
|
|
|
|
|
data.pass = -10;
|
|
|
|
|
data.shader_pass = prev_pass;
|
|
|
|
|
data.texture = pass->fbo.texture.id;
|
|
|
|
|
data.output_size = orig_output_size;
|
|
|
|
|
|
|
|
|
|
render_pass(&data);
|
|
|
|
|
|
|
|
|
|
glw.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
orig = input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (active_shader->final_pass.active) {
|
|
|
|
|
struct shader_pass *pass = &active_shader->final_pass;
|
|
|
|
|
|
|
|
|
|
memcpy(pass->state.input_size, input->state.output_size, 2 * sizeof(GLfloat));
|
|
|
|
|
memcpy(pass->state.input_texture_size, input->state.output_texture_size, 2 * sizeof(GLfloat));
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < 2; ++j) {
|
|
|
|
|
if (pass->scale.mode[j] == SCALE_VIEWPORT)
|
|
|
|
|
pass->state.output_size[j] = orig_output_size[j] * pass->scale.value[j];
|
|
|
|
|
else if (pass->scale.mode[j] == SCALE_ABSOLUTE)
|
|
|
|
|
pass->state.output_size[j] = pass->scale.value[j];
|
|
|
|
|
else
|
|
|
|
|
pass->state.output_size[j] = pass->state.input_size[j] * pass->scale.value[j];
|
|
|
|
|
|
|
|
|
|
pass->state.output_texture_size[j] = next_pow2(pass->state.output_size[j]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glw.glViewport(window_rect.x, window_rect.y, window_rect.w, window_rect.h);
|
|
|
|
|
|
|
|
|
|
glw.glClearColor(0, 0, 0, 1);
|
|
|
|
|
glw.glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
GLfloat minx = 0;
|
|
|
|
|
GLfloat miny = 0;
|
|
|
|
|
GLfloat maxx = pass->state.output_size[0] / (GLfloat) pass->state.output_texture_size[0];
|
|
|
|
|
GLfloat maxy = pass->state.output_size[1] / (GLfloat) pass->state.output_texture_size[1];
|
|
|
|
|
|
|
|
|
|
pass->state.tex_coords[0] = minx;
|
|
|
|
|
pass->state.tex_coords[1] = miny;
|
|
|
|
|
pass->state.tex_coords[2] = minx;
|
|
|
|
|
pass->state.tex_coords[3] = maxy;
|
|
|
|
|
pass->state.tex_coords[4] = maxx;
|
|
|
|
|
pass->state.tex_coords[5] = miny;
|
|
|
|
|
pass->state.tex_coords[6] = maxx;
|
|
|
|
|
pass->state.tex_coords[7] = maxy;
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.tex_coord);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * sizeof(GLfloat), input->state.tex_coords);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct render_data));
|
|
|
|
|
data.pass = -2;
|
|
|
|
|
data.shader_pass = pass;
|
|
|
|
|
data.texture = input->fbo.texture.id;
|
|
|
|
|
data.output_size = orig_output_size;
|
|
|
|
|
data.orig_pass = orig;
|
|
|
|
|
|
|
|
|
|
render_pass(&data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (1) {
|
|
|
|
|
// if (video_focus_dim && !(SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS)) {
|
|
|
|
|
#if 0
|
|
|
|
|
if (0) {
|
|
|
|
|
struct shader_pass *pass = &active_shader->fs_color;
|
|
|
|
|
GLfloat r = 0;
|
|
|
|
|
GLfloat g = 0;
|
|
|
|
|
GLfloat b = 0;
|
|
|
|
|
GLfloat a = 0x80 / (float) 0xff;
|
|
|
|
|
|
|
|
|
|
GLfloat colors[] = { r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a };
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.color);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 16 * sizeof(GLfloat), colors);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct render_data));
|
|
|
|
|
data.pass = -3;
|
|
|
|
|
data.shader_pass = pass;
|
|
|
|
|
data.texture = 0;
|
|
|
|
|
data.output_size = orig_output_size;
|
|
|
|
|
data.orig_pass = orig;
|
|
|
|
|
|
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
|
render_pass(&data);
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flash.enabled) {
|
|
|
|
|
struct shader_pass *pass = &active_shader->fs_color;
|
|
|
|
|
GLfloat r = (flash.color[0] & 0xff) / (float)0xff;
|
|
|
|
|
GLfloat g = (flash.color[1] & 0xff) / (float)0xff;
|
|
|
|
|
GLfloat b = (flash.color[2] & 0xff) / (float)0xff;
|
|
|
|
|
GLfloat a = (flash.color[3] & 0xff) / (float)0xff;
|
|
|
|
|
|
|
|
|
|
GLfloat colors[] = {r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a};
|
|
|
|
|
|
|
|
|
|
glw.glBindVertexArray(pass->vertex_array);
|
|
|
|
|
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.color);
|
|
|
|
|
glw.glBufferSubData(GL_ARRAY_BUFFER, 0, 16 * sizeof(GLfloat), colors);
|
|
|
|
|
glw.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct render_data));
|
|
|
|
|
data.pass = -3;
|
|
|
|
|
data.shader_pass = pass;
|
|
|
|
|
data.texture = 0;
|
|
|
|
|
data.output_size = orig_output_size;
|
|
|
|
|
data.orig_pass = orig;
|
|
|
|
|
|
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
|
render_pass(&data);
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
} else {
|
|
|
|
|
#if 0
|
|
|
|
|
take_screenshot = 0;
|
|
|
|
|
|
|
|
|
|
int width = window_rect.w;
|
|
|
|
|
int height = window_rect.h;
|
|
|
|
|
|
|
|
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
|
|
|
|
|
|
|
|
unsigned char *rgba = (unsigned char *)malloc(width * height * 4);
|
|
|
|
|
|
|
|
|
|
glFinish();
|
|
|
|
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
|
|
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
|
unsigned char *rgb = (unsigned char *)malloc(width * height * 3);
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < width; ++x) {
|
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
|
rgb[(y * width + x) * 3 + 0] = rgba[((height - y - 1) * width + x) * 4 + 0];
|
|
|
|
|
rgb[(y * width + x) * 3 + 1] = rgba[((height - y - 1) * width + x) * 4 + 1];
|
|
|
|
|
rgb[(y * width + x) * 3 + 2] = rgba[((height - y - 1) * width + x) * 4 + 2];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
screenshot_taken(rgb, width, height);
|
|
|
|
|
|
|
|
|
|
free(rgb);
|
|
|
|
|
free(rgba);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glw.glDisable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
|
|
2025-03-08 20:18:27 +06:00
|
|
|
frameCounter++;
|
2025-03-08 02:13:14 +06:00
|
|
|
context->swapBuffers(this);
|
2025-03-09 01:39:07 +06:00
|
|
|
}
|