mirror of https://github.com/snes9xgit/snes9x.git
Rudimentary GLSL Shader support.
This commit is contained in:
parent
e0de36dbb6
commit
9da5a83550
|
@ -235,9 +235,11 @@ if OPENGL
|
|||
snes9x_gtk_SOURCES += \
|
||||
src/gtk_display_driver_opengl.cpp \
|
||||
src/gtk_display_driver_opengl.h \
|
||||
src/Cg/CGLCG.cpp \
|
||||
src/Cg/cgFunctions.cpp \
|
||||
src/Cg/CCGShader.cpp
|
||||
src/shaders/CGLCG.cpp \
|
||||
src/shaders/cgFunctions.cpp \
|
||||
src/shaders/CCGShader.cpp \
|
||||
src/shaders/glsl.cpp \
|
||||
src/shaders/shader_helpers.cpp
|
||||
endif
|
||||
|
||||
if XV
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "gtk_display.h"
|
||||
#include "gtk_display_driver_opengl.h"
|
||||
|
||||
#include "Cg/CGLCG.h"
|
||||
#include "shaders/CGLCG.h"
|
||||
|
||||
S9xOpenGLDisplayDriver::S9xOpenGLDisplayDriver (Snes9xWindow *window,
|
||||
Snes9xConfig *config)
|
||||
|
@ -48,7 +48,7 @@ S9xOpenGLDisplayDriver::update (int width, int height, int yoffset)
|
|||
|
||||
#endif
|
||||
|
||||
if (using_cg_shaders)
|
||||
if (using_cg_shaders || using_glsl_shaders)
|
||||
{
|
||||
glBindTexture (tex_target, texmap);
|
||||
}
|
||||
|
@ -233,7 +233,12 @@ S9xOpenGLDisplayDriver::update (int width, int height, int yoffset)
|
|||
texcoords[4] = texcoords[2];
|
||||
}
|
||||
|
||||
if (using_shaders && using_cg_shaders)
|
||||
if (using_shaders && using_glsl_shaders)
|
||||
{
|
||||
glsl_shader->render (texmap, width, height, w, h);
|
||||
glViewport (x, allocation.height - y - h, w, h);
|
||||
}
|
||||
else if (using_shaders && using_cg_shaders)
|
||||
{
|
||||
xySize texture_size, input_size, viewport_size;
|
||||
texture_size.x = texture_width;
|
||||
|
@ -376,8 +381,30 @@ S9xOpenGLDisplayDriver::load_shaders (const char *shader_file)
|
|||
xmlDoc *xml_doc = NULL;
|
||||
xmlNodePtr node = NULL;
|
||||
char *fragment = NULL, *vertex = NULL;
|
||||
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
|
||||
|
||||
int length = strlen (shader_file);
|
||||
|
||||
if ((length > 6 && !strcasecmp(shader_file + length - 6, ".glslp")) ||
|
||||
(length > 5 && !strcasecmp(shader_file + length - 5, ".glsl")))
|
||||
{
|
||||
if (shaders_available() && strstr(extensions, "non_power_of_two"))
|
||||
{
|
||||
glsl_shader = new GLSLShader;
|
||||
if (glsl_shader->load_shader ((char *) shader_file))
|
||||
{
|
||||
using_glsl_shaders = 1;
|
||||
return 1;
|
||||
}
|
||||
delete glsl_shader;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Need shader extensions and non-power-of-two-textures for GLSL.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ((length > 4 && !strcasecmp(shader_file + length - 4, ".cgp")) ||
|
||||
(length > 3 && !strcasecmp(shader_file + length - 3, ".cg")))
|
||||
{
|
||||
|
@ -480,6 +507,8 @@ S9xOpenGLDisplayDriver::load_shaders (const char *shader_file)
|
|||
int
|
||||
S9xOpenGLDisplayDriver::opengl_defaults (void)
|
||||
{
|
||||
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
|
||||
|
||||
using_pbos = 0;
|
||||
if (config->use_pbos)
|
||||
{
|
||||
|
@ -497,8 +526,11 @@ S9xOpenGLDisplayDriver::opengl_defaults (void)
|
|||
|
||||
using_shaders = 0;
|
||||
using_cg_shaders = 0;
|
||||
using_glsl_shaders = 0;
|
||||
cg_context = NULL;
|
||||
cg_shader = NULL;
|
||||
glsl_shader = NULL;
|
||||
|
||||
if (config->use_shaders)
|
||||
{
|
||||
if (!load_shaders (config->fragment_shader))
|
||||
|
@ -516,8 +548,6 @@ S9xOpenGLDisplayDriver::opengl_defaults (void)
|
|||
texture_height = 1024;
|
||||
dyn_resizing = FALSE;
|
||||
|
||||
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
|
||||
|
||||
if (extensions && config->npot_textures)
|
||||
{
|
||||
if (!using_shaders && strstr (extensions, "_texture_rectangle"))
|
||||
|
@ -800,7 +830,12 @@ S9xOpenGLDisplayDriver::deinit (void)
|
|||
if (!initialized)
|
||||
return;
|
||||
|
||||
if (using_shaders && using_cg_shaders)
|
||||
if (using_shaders && using_glsl_shaders)
|
||||
{
|
||||
glsl_shader->destroy();
|
||||
delete glsl_shader;
|
||||
}
|
||||
else if (using_shaders && using_cg_shaders)
|
||||
{
|
||||
delete cg_shader;
|
||||
cgDestroyContext (cg_context);
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
#include <epoxy/gl.h>
|
||||
#include <epoxy/glx.h>
|
||||
|
||||
#include "Cg/CGLCG.h"
|
||||
#include "shaders/CGLCG.h"
|
||||
#include "shaders/glsl.h"
|
||||
|
||||
#define PBO_FMT_16 0
|
||||
#define PBO_FMT_24 1
|
||||
|
@ -72,6 +73,9 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver
|
|||
CGcontext cg_context;
|
||||
CGLCG *cg_shader;
|
||||
|
||||
int using_glsl_shaders;
|
||||
GLSLShader *glsl_shader;
|
||||
|
||||
Display *display;
|
||||
Window xwindow;
|
||||
Colormap xcolormap;
|
||||
|
|
|
@ -196,6 +196,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "../gtk_display_driver_opengl.h"
|
||||
#include "shader_helpers.h"
|
||||
|
||||
static char* ReadShaderFileContents(const char *filename)
|
||||
{
|
||||
|
@ -541,7 +542,7 @@ bool CGLCG::LoadShader(const char* shaderFile)
|
|||
GL_UNSIGNED_SHORT_5_6_5,
|
||||
NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
prevPasses[i].textureSize.x = prevPasses[i].textureSize.y = prevPasses[i].textureSize.x = prevPasses[i].textureSize.y = 0;
|
||||
prevPasses[i].textureSize.x = prevPasses[i].textureSize.y = 0;
|
||||
memset(prevPasses[i].texCoords, 0, sizeof(prevPasses[i].texCoords));
|
||||
}
|
||||
|
||||
|
@ -721,10 +722,14 @@ void CGLCG::Render(GLuint& origTex,
|
|||
pass.tex = shaderPasses[0].tex;
|
||||
memcpy(pass.texCoords, shaderPasses[1].texcoords, sizeof(pass.texCoords));
|
||||
prevPasses.push_front(pass);
|
||||
GLint internal_format;
|
||||
glBindTexture(GL_TEXTURE_2D, pass.tex);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, origTex);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGB,
|
||||
internal_format,
|
||||
textureSize.x,
|
||||
textureSize.y,
|
||||
0,
|
||||
|
@ -741,10 +746,6 @@ void CGLCG::Render(GLuint& origTex,
|
|||
that will be used in the main ogl code
|
||||
*/
|
||||
|
||||
/* RECT
|
||||
displayRect=CalculateDisplayRect(shaderPasses.back().outputSize.x,shaderPasses.back().outputSize.y,windowSize.x,windowSize.y);
|
||||
glViewport(displayRect.left,windowSize.y-displayRect.bottom,displayRect.right-displayRect.left,displayRect.bottom-displayRect.top);
|
||||
*/
|
||||
setTexCoords(shaderPasses.size() - 1,
|
||||
shaderPasses.back().outputSize,
|
||||
shaderPasses.back().textureSize,
|
||||
|
@ -923,189 +924,3 @@ void CGLCG::resetAttribParams()
|
|||
cgAttribParams.clear();
|
||||
}
|
||||
|
||||
bool CGLCG::loadPngImage(const char* name,
|
||||
int& outWidth,
|
||||
int& outHeight,
|
||||
bool& outHasAlpha,
|
||||
GLubyte** outData)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
unsigned int sig_read = 0;
|
||||
FILE* fp;
|
||||
|
||||
if ((fp = fopen(name, "rb")) == NULL)
|
||||
return false;
|
||||
|
||||
/* Create and initialize the png_struct
|
||||
* with the desired error handler
|
||||
* functions. If you want to use the
|
||||
* default stderr and longjump method,
|
||||
* you can supply NULL for the last
|
||||
* three parameters. We also supply the
|
||||
* the compiler header file version, so
|
||||
* that we know if the application
|
||||
* was compiled with a compatible version
|
||||
* of the library. REQUIRED
|
||||
*/
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
|
||||
if (png_ptr == NULL) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Allocate/initialize the memory
|
||||
* for image information. REQUIRED. */
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL) {
|
||||
fclose(fp);
|
||||
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set error handling if you are
|
||||
* using the setjmp/longjmp method
|
||||
* (this is the normal method of
|
||||
* doing things with libpng).
|
||||
* REQUIRED unless you set up
|
||||
* your own error handlers in
|
||||
* the png_create_read_struct()
|
||||
* earlier.
|
||||
*/
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
/* Free all of the memory associated
|
||||
* with the png_ptr and info_ptr */
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
fclose(fp);
|
||||
/* If we get here, we had a
|
||||
* problem reading the file */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set up the output control if
|
||||
* you are using standard C streams */
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
/* If we have already
|
||||
* read some of the signature */
|
||||
png_set_sig_bytes(png_ptr, sig_read);
|
||||
|
||||
/*
|
||||
* If you have enough memory to read
|
||||
* in the entire image at once, and
|
||||
* you need to specify only
|
||||
* transforms that can be controlled
|
||||
* with one of the PNG_TRANSFORM_*
|
||||
* bits (this presently excludes
|
||||
* dithering, filling, setting
|
||||
* background, and doing gamma
|
||||
* adjustment), then you can read the
|
||||
* entire image (including pixels)
|
||||
* into the info structure with this
|
||||
* call
|
||||
*
|
||||
* PNG_TRANSFORM_STRIP_16 |
|
||||
* PNG_TRANSFORM_PACKING forces 8 bit
|
||||
* PNG_TRANSFORM_EXPAND forces to
|
||||
* expand a palette into RGB
|
||||
*/
|
||||
png_read_png(png_ptr,
|
||||
info_ptr,
|
||||
PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND,
|
||||
(png_voidp)NULL);
|
||||
|
||||
outWidth = png_get_image_width(png_ptr, info_ptr);
|
||||
outHeight = png_get_image_height(png_ptr, info_ptr);
|
||||
switch (png_get_color_type(png_ptr, info_ptr)) {
|
||||
case PNG_COLOR_TYPE_RGBA:
|
||||
outHasAlpha = true;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
outHasAlpha = false;
|
||||
break;
|
||||
default:
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||
*outData = (unsigned char*)malloc(row_bytes * outHeight);
|
||||
|
||||
png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);
|
||||
|
||||
for (int i = 0; i < outHeight; i++) {
|
||||
memcpy(*outData + (row_bytes * i), row_pointers[i], row_bytes);
|
||||
}
|
||||
|
||||
/* Clean up after the read,
|
||||
* and free any memory allocated */
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
|
||||
/* Close the file */
|
||||
fclose(fp);
|
||||
|
||||
/* That's it */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGLCG::loadTGA(const char* filename, STGA& tgaFile)
|
||||
{
|
||||
FILE* file;
|
||||
unsigned char type[4];
|
||||
unsigned char info[6];
|
||||
|
||||
file = fopen(filename, "rb");
|
||||
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
fread(&type, sizeof(char), 3, file);
|
||||
fseek(file, 12, SEEK_SET);
|
||||
fread(&info, sizeof(char), 6, file);
|
||||
|
||||
// image type either 2 (color) or 3 (greyscale)
|
||||
if (type[1] != 0 || (type[2] != 2 && type[2] != 3)) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
tgaFile.width = info[0] + info[1] * 256;
|
||||
tgaFile.height = info[2] + info[3] * 256;
|
||||
tgaFile.byteCount = info[4] / 8;
|
||||
|
||||
if (tgaFile.byteCount != 3 && tgaFile.byteCount != 4) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
long imageSize = tgaFile.width * tgaFile.height * tgaFile.byteCount;
|
||||
|
||||
// allocate memory for image data
|
||||
unsigned char* tempBuf = new unsigned char[imageSize];
|
||||
tgaFile.data = new unsigned char[tgaFile.width * tgaFile.height * 4];
|
||||
|
||||
// read in image data
|
||||
fread(tempBuf, sizeof(unsigned char), imageSize, file);
|
||||
|
||||
// swap line order and convert to RBGA
|
||||
for (int i = 0; i < tgaFile.height; i++) {
|
||||
unsigned char* source = tempBuf + tgaFile.width * (tgaFile.height - 1 - i) * tgaFile.byteCount;
|
||||
unsigned char* destination = tgaFile.data + tgaFile.width * i * 4;
|
||||
for (int j = 0; j < tgaFile.width; j++) {
|
||||
destination[0] = source[2];
|
||||
destination[1] = source[1];
|
||||
destination[2] = source[0];
|
||||
destination[3] = tgaFile.byteCount == 4 ? source[3] : 0xff;
|
||||
source += tgaFile.byteCount;
|
||||
destination += 4;
|
||||
}
|
||||
}
|
||||
delete[] tempBuf;
|
||||
tgaFile.byteCount = 4;
|
||||
|
||||
// close file
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -209,34 +209,6 @@ typedef struct _xySize
|
|||
class CGLCG
|
||||
{
|
||||
private:
|
||||
typedef struct _STGA
|
||||
{
|
||||
_STGA()
|
||||
{
|
||||
data = (unsigned char*)0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
byteCount = 0;
|
||||
}
|
||||
|
||||
~_STGA()
|
||||
{
|
||||
delete[] data;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
delete[] data;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
int width;
|
||||
int height;
|
||||
unsigned char byteCount;
|
||||
unsigned char* data;
|
||||
} STGA;
|
||||
|
||||
typedef struct _shaderPass
|
||||
{
|
||||
cgScaleParams scaleParams;
|
||||
|
@ -293,12 +265,6 @@ private:
|
|||
bool topdown = false);
|
||||
void setShaderVars(int pass);
|
||||
void resetAttribParams();
|
||||
bool loadPngImage(const char* name,
|
||||
int& outWidth,
|
||||
int& outHeight,
|
||||
bool& outHasAlpha,
|
||||
GLubyte** outData);
|
||||
bool loadTGA(const char* filename, STGA& tgaFile);
|
||||
|
||||
CGcontext cgContext;
|
||||
unsigned int frameCnt;
|
|
@ -0,0 +1,787 @@
|
|||
#include "glsl.h"
|
||||
#include "../../conffile.h"
|
||||
#include "shader_helpers.h"
|
||||
#include "../gtk_s9x.h"
|
||||
|
||||
static const GLfloat lut_coords[8] = { 0, 0, 1, 0, 1, 1, 0, 1 };
|
||||
static const GLfloat inv_coords[8] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
static const GLfloat tex_coords[8] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };
|
||||
static const GLfloat mvp_ortho[16] = { 2.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, -1.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 1.0f };
|
||||
|
||||
static void reduce_to_path(char* filename)
|
||||
{
|
||||
for (int i = strlen(filename); i >= 0; i--)
|
||||
{
|
||||
if (filename[i] == '\\' || filename[i] == '/')
|
||||
{
|
||||
filename[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char* read_file(const char *filename)
|
||||
{
|
||||
FILE* file = NULL;
|
||||
int size;
|
||||
char* contents;
|
||||
|
||||
file = fopen(filename, "rb");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
contents = new char[size + 1];
|
||||
fread(contents, size, 1, file);
|
||||
contents[size] = '\0';
|
||||
fclose(file);
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
static int scale_string_to_enum(const char *string, bool last)
|
||||
{
|
||||
if (!strcasecmp(string, "source"))
|
||||
{
|
||||
return GLSL_SOURCE;
|
||||
}
|
||||
else if (!strcasecmp(string, "viewport"))
|
||||
{
|
||||
return GLSL_VIEWPORT;
|
||||
}
|
||||
else if (!strcasecmp(string, "absolute"))
|
||||
{
|
||||
return GLSL_ABSOLUTE;
|
||||
}
|
||||
else if (last)
|
||||
return GLSL_VIEWPORT;
|
||||
else
|
||||
return GLSL_SOURCE;
|
||||
}
|
||||
|
||||
bool GLSLShader::load_shader_file (char *filename)
|
||||
{
|
||||
ConfigFile conf;
|
||||
char key[256];
|
||||
|
||||
if (strlen(filename) < 6 || strcasecmp(&filename[strlen(filename) - 6], ".glslp"))
|
||||
{
|
||||
GLSLPass pass;
|
||||
pass.scale_type_x = pass.scale_type_y = GLSL_NONE;
|
||||
pass.filter = GLSL_UNDEFINED;
|
||||
strcpy(pass.filename, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
conf.LoadFile(filename);
|
||||
}
|
||||
|
||||
int shader_count = conf.GetInt("::shaders", 0);
|
||||
|
||||
if (shader_count < 1)
|
||||
return false;
|
||||
|
||||
this->pass.push_back (GLSLPass());
|
||||
|
||||
for (int i = 0; i < shader_count; i++)
|
||||
{
|
||||
GLSLPass pass;
|
||||
|
||||
snprintf(key, 256, "::filter_linear%u", i);
|
||||
pass.filter = conf.Exists(key) ? conf.GetBool(key) ? GL_LINEAR : GL_NEAREST : GLSL_UNDEFINED;
|
||||
|
||||
sprintf(key, "::scale_type%u", i);
|
||||
const char* scaleType = conf.GetString(key, "");
|
||||
|
||||
if (!strcasecmp(scaleType, ""))
|
||||
{
|
||||
sprintf(key, "::scale_type_x%u", i);
|
||||
const char* scaleTypeX = conf.GetString(key, "");
|
||||
pass.scale_type_x = scale_string_to_enum(scaleTypeX, i == shader_count - 1);
|
||||
|
||||
sprintf(key, "::scale_type_y%u", i);
|
||||
const char* scaleTypeY = conf.GetString(key, "");
|
||||
pass.scale_type_y = scale_string_to_enum(scaleTypeY, i == shader_count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int scale_type = scale_string_to_enum(scaleType, i == shader_count - 1);
|
||||
pass.scale_type_x = scale_type;
|
||||
pass.scale_type_y = scale_type;
|
||||
}
|
||||
|
||||
sprintf(key, "::scale%u", i);
|
||||
const char* scaleFloat = conf.GetString(key, "");
|
||||
if (!strcasecmp(scaleFloat, ""))
|
||||
{
|
||||
sprintf(key, "::scale_x%u", i);
|
||||
const char* scaleFloatX = conf.GetString(key, "1.0");
|
||||
pass.scale_x = atof(scaleFloatX);
|
||||
sprintf(key, "::scale_y%u", i);
|
||||
const char* scaleFloatY = conf.GetString(key, "1.0");
|
||||
pass.scale_y = atof(scaleFloatY);
|
||||
}
|
||||
else
|
||||
{
|
||||
pass.scale_x = pass.scale_y = atof(scaleFloat);
|
||||
}
|
||||
|
||||
sprintf(key, "::shader%u", i);
|
||||
strcpy(pass.filename, conf.GetString(key, ""));
|
||||
|
||||
sprintf(key, "::frame_count_mod%u", i);
|
||||
pass.frame_count_mod = conf.GetInt(key, 0);
|
||||
|
||||
sprintf(key, "::float_framebuffer%u", i);
|
||||
pass.fp = conf.GetBool(key);
|
||||
|
||||
this->pass.push_back(pass);
|
||||
}
|
||||
|
||||
char* ids = conf.GetStringDup("::textures", "");
|
||||
|
||||
char* id = strtok(ids, ";");
|
||||
|
||||
while (id != NULL)
|
||||
{
|
||||
GLSLLut lut;
|
||||
|
||||
sprintf(key, "::%s", id);
|
||||
strcpy(lut.id, id);
|
||||
strcpy(lut.filename, conf.GetString(key, ""));
|
||||
sprintf(key, "::%s_linear", id);
|
||||
lut.filter = (conf.GetBool(key, false)) ? GL_LINEAR : GL_NEAREST;
|
||||
this->lut.push_back(lut);
|
||||
|
||||
id = strtok(NULL, ";");
|
||||
}
|
||||
free(ids);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void strip_parameter_pragmas(char *buffer)
|
||||
{
|
||||
/* #pragma parameter lines tend to have " characters in them,
|
||||
* which is not legal GLSL. */
|
||||
char *s = strstr(buffer, "#pragma parameter");
|
||||
|
||||
while (s)
|
||||
{
|
||||
/* #pragmas have to be on a single line,
|
||||
* so we can just replace the entire line with spaces. */
|
||||
while (*s != '\0' && *s != '\n')
|
||||
*s++ = ' ';
|
||||
s = strstr(s, "#pragma parameter");
|
||||
}
|
||||
}
|
||||
|
||||
static GLuint compile_shader (char *program, const char *defines, GLuint type, GLuint *out)
|
||||
{
|
||||
char info_log[1024];
|
||||
char *ptr = program;
|
||||
std::string complete_program = "";
|
||||
char version[32];
|
||||
const char *existing_version = strstr(ptr, "#version");
|
||||
|
||||
if (existing_version)
|
||||
{
|
||||
unsigned version_no = (unsigned) strtoul (existing_version + 8, &ptr, 10);
|
||||
snprintf (version, 32, "#version %u\n", version_no);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf (version, 32, "#version 150\n");
|
||||
}
|
||||
|
||||
complete_program += version;
|
||||
complete_program += defines;
|
||||
complete_program += ptr;
|
||||
|
||||
GLuint shader = glCreateShader (type);
|
||||
GLint status;
|
||||
GLint length = complete_program.length();
|
||||
GLchar *prog = (GLchar *) complete_program.c_str();
|
||||
|
||||
glShaderSource (shader, 1, &prog, &length);
|
||||
glCompileShader (shader);
|
||||
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
|
||||
|
||||
glGetShaderInfoLog (shader, 1024, NULL, info_log);
|
||||
if (info_log && *info_log)
|
||||
printf ("%s\n", info_log);
|
||||
*out = shader;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool GLSLShader::load_shader (char *filename)
|
||||
{
|
||||
char shader_path[PATH_MAX];
|
||||
char temp[PATH_MAX];
|
||||
GLint status;
|
||||
char log[1024];
|
||||
|
||||
if (this->pass.size())
|
||||
return false;
|
||||
|
||||
if (!filename || *filename == '\0')
|
||||
return false;
|
||||
|
||||
strcpy(shader_path, filename);
|
||||
reduce_to_path(shader_path);
|
||||
|
||||
chdir(shader_path);
|
||||
if (!load_shader_file(filename))
|
||||
return false;
|
||||
|
||||
for (unsigned int i = 1; i < pass.size(); i++)
|
||||
{
|
||||
GLSLPass *p = &pass[i];
|
||||
GLuint vertex_shader = 0, fragment_shader = 0;
|
||||
|
||||
realpath(p->filename, temp);
|
||||
|
||||
char *contents = read_file (temp);
|
||||
if (!contents)
|
||||
{
|
||||
printf("Couldn't read shader file %s\n", temp);
|
||||
return false;
|
||||
}
|
||||
|
||||
strip_parameter_pragmas(contents);
|
||||
|
||||
if (!compile_shader (contents,
|
||||
"#define VERTEX\n", // #define PARAMETER_UNIFORM\n",
|
||||
GL_VERTEX_SHADER,
|
||||
&vertex_shader) || !vertex_shader)
|
||||
{
|
||||
printf("Couldn't compile vertex shader.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!compile_shader (contents,
|
||||
"#define FRAGMENT\n", // #define PARAMETER_UNIFORM\n",
|
||||
GL_FRAGMENT_SHADER,
|
||||
&fragment_shader) || !fragment_shader)
|
||||
{
|
||||
printf("Couldn't compile fragment shader\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
p->program = glCreateProgram ();
|
||||
|
||||
glAttachShader (p->program, vertex_shader);
|
||||
glAttachShader (p->program, fragment_shader);
|
||||
|
||||
glLinkProgram (p->program);
|
||||
glGetProgramiv (p->program, GL_LINK_STATUS, &status);
|
||||
glGetProgramInfoLog(p->program, 1024, NULL, log);
|
||||
if (log && *log)
|
||||
printf ("%s\n", log);
|
||||
|
||||
glDeleteShader (vertex_shader);
|
||||
glDeleteShader (fragment_shader);
|
||||
|
||||
if (status != GL_TRUE)
|
||||
{
|
||||
printf ("Failed to link program\n");
|
||||
glDeleteProgram (p->program);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenFramebuffers(1, &p->fbo);
|
||||
glGenTextures(1, &p->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, p->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
p->frame_count = 0;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < lut.size(); i++)
|
||||
{
|
||||
GLSLLut *l = &lut[i];
|
||||
/* generate texture for the lut and apply specified filter setting
|
||||
*/
|
||||
glGenTextures(1, &l->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, l->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, l->filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, l->filter);
|
||||
|
||||
realpath(l->filename, temp);
|
||||
|
||||
// simple file extension png/tga decision
|
||||
int length = strlen(temp);
|
||||
if (length > 4)
|
||||
{
|
||||
if (!strcasecmp(&temp[length - 4], ".png"))
|
||||
{
|
||||
int width, height;
|
||||
bool hasAlpha;
|
||||
GLubyte* texData;
|
||||
|
||||
if (loadPngImage(temp, width, height, hasAlpha, &texData))
|
||||
{
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
hasAlpha ? GL_RGBA : GL_RGB,
|
||||
GL_UNSIGNED_BYTE,
|
||||
texData);
|
||||
free(texData);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Failed to load PNG LUT: %s\n", temp);
|
||||
}
|
||||
}
|
||||
else if (!strcasecmp(&temp[length - 4], ".tga"))
|
||||
{
|
||||
STGA stga;
|
||||
|
||||
if (loadTGA(temp, stga))
|
||||
{
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stga.width);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA,
|
||||
stga.width,
|
||||
stga.height,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
stga.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Failed to load TGA LUT: %s\n", temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glEnable(GL_TEXTURE_COORD_ARRAY);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, lut_coords);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
register_uniforms();
|
||||
|
||||
prev_frame.resize (max_prev_frame);
|
||||
|
||||
for (unsigned int i = 0; i < prev_frame.size(); i++)
|
||||
{
|
||||
glGenTextures(1, &(prev_frame[i].texture));
|
||||
prev_frame[i].width = prev_frame[i].height = 0;
|
||||
}
|
||||
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
frame_count = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLSLShader::render(GLuint &orig, int width, int height, int viewport_width, int viewport_height)
|
||||
{
|
||||
frame_count++;
|
||||
|
||||
/* set up our dummy pass for easier loop code
|
||||
*/
|
||||
pass[0].texture = orig;
|
||||
pass[0].width = width;
|
||||
pass[0].height = height;
|
||||
|
||||
/* loop through all real passes
|
||||
*/
|
||||
for (unsigned int i = 1; i < pass.size(); i++)
|
||||
{
|
||||
switch (pass[i].scale_type_x)
|
||||
{
|
||||
case GLSL_ABSOLUTE:
|
||||
pass[i].width = pass[i].scale_x;
|
||||
break;
|
||||
case GLSL_SOURCE:
|
||||
pass[i].width = pass[i-1].width * pass[i].scale_x;
|
||||
break;
|
||||
case GLSL_VIEWPORT:
|
||||
pass[i].width = viewport_width * pass[i].scale_x;
|
||||
break;
|
||||
default:
|
||||
pass[i].width = viewport_width;
|
||||
}
|
||||
|
||||
switch (pass[i].scale_type_y)
|
||||
{
|
||||
case GLSL_ABSOLUTE:
|
||||
pass[i].height = pass[i].scale_y;
|
||||
break;
|
||||
case GLSL_SOURCE:
|
||||
pass[i].height = pass[i - 1].height * pass[i].scale_y;
|
||||
break;
|
||||
case GLSL_VIEWPORT:
|
||||
pass[i].height = viewport_height * pass[i].scale_y;
|
||||
break;
|
||||
default:
|
||||
pass[i].height = viewport_height;
|
||||
}
|
||||
|
||||
/* set size of output texture
|
||||
*/
|
||||
glBindTexture(GL_TEXTURE_2D, pass[i].texture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
(pass[i].fp ? GL_RGBA32F : GL_RGBA),
|
||||
(unsigned int) pass[i].width,
|
||||
(unsigned int) pass[i].height,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_INT_8_8_8_8,
|
||||
NULL);
|
||||
|
||||
// viewport determines the area we render into the output texture
|
||||
glViewport(0, 0, pass[i].width, pass[i].height);
|
||||
|
||||
// set up framebuffer and attach output texture
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, pass[i].fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
pass[i].texture,
|
||||
0);
|
||||
|
||||
// set up input texture (output of previous pass) and apply filter settings
|
||||
glBindTexture(GL_TEXTURE_2D, pass[i - 1].texture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint) pass[i - 1].width);
|
||||
glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_MAG_FILTER,
|
||||
pass[i].filter == GLSL_UNDEFINED ? GL_NEAREST : pass[i].filter);
|
||||
glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_MIN_FILTER,
|
||||
pass[i].filter == GLSL_UNDEFINED ? GL_NEAREST : pass[i].filter);
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords);
|
||||
|
||||
glUseProgram (pass[i].program);
|
||||
set_shader_vars(i);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
/* reset client states enabled during setShaderVars
|
||||
*/
|
||||
clear_shader_vars();
|
||||
}
|
||||
|
||||
/* disable framebuffer
|
||||
*/
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUseProgram (0);
|
||||
|
||||
/* set last PREV texture as original, push current texture and
|
||||
sizes to the front of the PREV deque and make sure the new
|
||||
original texture has the same size as the old one
|
||||
*/
|
||||
|
||||
if (prev_frame.size() > 0)
|
||||
{
|
||||
GLint internal_format;
|
||||
orig = prev_frame.back().texture;
|
||||
prev_frame.pop_back();
|
||||
|
||||
GLSLPass newprevframe;
|
||||
newprevframe.width = width;
|
||||
newprevframe.height = height;
|
||||
newprevframe.texture = pass[0].texture;
|
||||
prev_frame.push_front(newprevframe);
|
||||
glBindTexture(GL_TEXTURE_2D, newprevframe.texture);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, orig);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
internal_format,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
GL_RGB,
|
||||
GL_UNSIGNED_BYTE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, pass.back().texture);
|
||||
glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_MAG_FILTER,
|
||||
pass.back().filter == GLSL_UNDEFINED ? (gui_config->bilinear_filter ? GL_LINEAR : GL_NEAREST) : pass.back().filter);
|
||||
glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_MIN_FILTER,
|
||||
pass.back().filter == GLSL_UNDEFINED ? (gui_config->bilinear_filter ? GL_LINEAR : GL_NEAREST) : pass.back().filter);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pass.back().width);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, inv_coords);
|
||||
}
|
||||
|
||||
void GLSLShader::register_uniforms ()
|
||||
{
|
||||
max_prev_frame = 0;
|
||||
char varname[100];
|
||||
|
||||
for (unsigned int i = 1; i < pass.size(); i++)
|
||||
{
|
||||
GLSLUniforms *u = &pass[i].unif;
|
||||
GLuint program = pass[i].program;
|
||||
|
||||
glUseProgram (program);
|
||||
|
||||
GLint mvp = glGetUniformLocation (program, "MVPMatrix");
|
||||
if (mvp > -1)
|
||||
glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho);
|
||||
|
||||
u->Texture = glGetUniformLocation (program, "Texture");
|
||||
u->InputSize = glGetUniformLocation (program, "InputSize");
|
||||
u->OutputSize = glGetUniformLocation (program, "OutputSize");
|
||||
u->TextureSize = glGetUniformLocation (program, "TextureSize");
|
||||
|
||||
u->TexCoord = glGetAttribLocation (program, "TexCoord");
|
||||
u->LUTTexCoord = glGetAttribLocation (program, "LUTTexCoord");
|
||||
u->VertexCoord = glGetAttribLocation (program, "VertexCoord");
|
||||
|
||||
u->FrameCount = glGetUniformLocation (program, "FrameCount");
|
||||
u->FrameDirection = glGetUniformLocation (program, "FrameDirection");
|
||||
|
||||
u->OrigTexture = glGetUniformLocation (program, "OrigTexture");
|
||||
u->OrigInputSize = glGetUniformLocation (program, "OrigInputSize");
|
||||
u->OrigTextureSize = glGetUniformLocation (program, "OrigTextureSize");
|
||||
u->OrigTexCoord = glGetAttribLocation (program, "OrigTexCoord");
|
||||
|
||||
u->Prev[0].Texture = glGetUniformLocation (program, "PrevTexture");
|
||||
u->Prev[0].InputSize = glGetUniformLocation (program, "PrevInputSize");
|
||||
u->Prev[0].TextureSize = glGetUniformLocation (program, "PrevTextureSize");
|
||||
u->Prev[0].TexCoord = glGetAttribLocation (program, "PrevTexCoord");
|
||||
|
||||
if (u->Prev[0].Texture > -1)
|
||||
max_prev_frame = 1;
|
||||
|
||||
for (unsigned int j = 1; j < 7; j++)
|
||||
{
|
||||
sprintf(varname, "Prev%dTexture", j);
|
||||
u->Prev[j].Texture = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "Prev%dInputSize", j);
|
||||
u->Prev[j].InputSize = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "Prev%dTextureSize", j);
|
||||
u->Prev[j].TextureSize = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "Prev%dTexCoord", j);
|
||||
u->Prev[j].TexCoord = glGetAttribLocation (program, varname);
|
||||
|
||||
if (u->Prev[j].Texture > -1)
|
||||
max_prev_frame = j + 1;
|
||||
}
|
||||
for (unsigned int j = 0; j < pass.size(); j++)
|
||||
{
|
||||
sprintf(varname, "Pass%dTexture", j);
|
||||
u->Pass[j].Texture = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "Pass%dInputSize", j);
|
||||
u->Pass[j].InputSize = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "Pass%dTextureSize", j);
|
||||
u->Pass[j].TextureSize = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "Pass%dTexCoord", j);
|
||||
u->Pass[j].TexCoord = glGetAttribLocation (program, varname);
|
||||
if (u->Pass[j].Texture)
|
||||
u->max_pass = j;
|
||||
|
||||
sprintf(varname, "PassPrev%dTexture", j);
|
||||
u->PassPrev[j].Texture = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "PassPrev%dInputSize", j);
|
||||
u->PassPrev[j].InputSize = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "PassPrev%dTextureSize", j);
|
||||
u->PassPrev[j].TextureSize = glGetUniformLocation (program, varname);
|
||||
sprintf(varname, "PassPrev%dTexCoord", j);
|
||||
u->PassPrev[j].TexCoord = glGetAttribLocation (program, varname);
|
||||
if (u->PassPrev[j].Texture > -1)
|
||||
u->max_prevpass = j;
|
||||
}
|
||||
|
||||
for (unsigned int j = 0; j < lut.size(); j++)
|
||||
{
|
||||
u->Lut[j] = glGetUniformLocation (program, lut[j].id);
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram (0);
|
||||
}
|
||||
|
||||
void GLSLShader::set_shader_vars (int p)
|
||||
{
|
||||
unsigned int texunit = 0;
|
||||
GLSLUniforms *u = &pass[p].unif;
|
||||
|
||||
GLint mvp = glGetUniformLocation (pass[p].program, "MVPMatrix");
|
||||
if (mvp > -1)
|
||||
glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho);
|
||||
|
||||
#define setUniform2fv(uni, val) if (uni > -1) glUniform2fv (uni, 1, val);
|
||||
#define setUniform1f(uni, val) if (uni > -1) glUniform1f (uni, val);
|
||||
#define setUniform1i(uni, val) if (uni > -1) glUniform1i (uni, val);
|
||||
#define setTexture1i(uni, val) \
|
||||
if (uni > -1) \
|
||||
{ \
|
||||
glActiveTexture (GL_TEXTURE0 + texunit); \
|
||||
glBindTexture (GL_TEXTURE_2D, val); \
|
||||
glUniform1i (uni, texunit); \
|
||||
texunit++; \
|
||||
}
|
||||
/* We use non-power-of-two textures,
|
||||
* so no need to mess with input size/texture size */
|
||||
#define setTexCoords(attr) \
|
||||
if (attr > -1) \
|
||||
{ \
|
||||
glEnableVertexAttribArray(attr); \
|
||||
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, NULL); \
|
||||
vaos.push_back (attr); \
|
||||
}
|
||||
|
||||
glBindBuffer (GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData (GL_ARRAY_BUFFER, sizeof (GLfloat) * 8, tex_coords, GL_STATIC_DRAW);
|
||||
|
||||
float inputSize[2] = { (float) pass[p - 1].width, (float) pass[p - 1].height };
|
||||
float outputSize[2] = { (float) pass[p].width, (float) pass[p].height };
|
||||
|
||||
setTexture1i (u->Texture, pass[p - 1].texture);
|
||||
setUniform2fv(u->InputSize, inputSize);
|
||||
setUniform2fv(u->OutputSize, outputSize);
|
||||
setUniform2fv(u->TextureSize, inputSize);
|
||||
|
||||
unsigned int shaderFrameCnt = frame_count;
|
||||
if (pass[p].frame_count_mod)
|
||||
shaderFrameCnt %= pass[p].frame_count_mod;
|
||||
setUniform1i(u->FrameCount, (float) shaderFrameCnt);
|
||||
setUniform1i(u->FrameDirection, top_level->user_rewind ? -1.0f : 1.0f);
|
||||
|
||||
setTexCoords (u->TexCoord);
|
||||
setTexCoords (u->LUTTexCoord);
|
||||
setTexCoords (u->VertexCoord);
|
||||
/* ORIG parameter
|
||||
*/
|
||||
float orig_videoSize[2] = { (float) pass[0].width, (float) pass[0].height };
|
||||
|
||||
setUniform2fv(u->OrigInputSize, orig_videoSize);
|
||||
setUniform2fv(u->OrigTextureSize, orig_videoSize);
|
||||
setTexture1i (u->OrigTexture, pass[0].texture);
|
||||
setTexCoords (u->OrigTexCoord);
|
||||
|
||||
/* PREV parameter
|
||||
*/
|
||||
if (max_prev_frame >= 1 && prev_frame[0].width > 0) {
|
||||
float prevSize[2] = { (float) prev_frame[0].width, (float) prev_frame[0].height };
|
||||
|
||||
setUniform2fv(u->Prev[0].InputSize, prevSize);
|
||||
setUniform2fv(u->Prev[0].TextureSize, prevSize);
|
||||
setTexture1i (u->Prev[0].Texture, prev_frame[0].texture);
|
||||
setTexCoords (u->Prev[0].TexCoord);
|
||||
}
|
||||
|
||||
/* PREV1-6 parameters
|
||||
*/
|
||||
for (unsigned int i = 1; i < prev_frame.size(); i++)
|
||||
{
|
||||
if (prev_frame[i].width <= 0)
|
||||
break;
|
||||
|
||||
float prevSize[2] = { (float) prev_frame[i].width, (float) prev_frame[i].height };
|
||||
|
||||
setUniform2fv(u->Prev[i].InputSize, prevSize);
|
||||
setUniform2fv(u->Prev[i].TextureSize, prevSize);
|
||||
setTexture1i (u->Prev[i].Texture, prev_frame[i].texture);
|
||||
setTexCoords (u->Prev[i].TexCoord);
|
||||
}
|
||||
|
||||
/* LUT parameters
|
||||
*/
|
||||
for (unsigned int i = 0; i < lut.size(); i++)
|
||||
{
|
||||
setTexture1i (u->Lut[i], lut[i].texture);
|
||||
}
|
||||
|
||||
/* PASSX parameters, only for third pass and up
|
||||
*/
|
||||
if (p > 2) {
|
||||
for (int i = 1; i < p - 1; i++) {
|
||||
float passSize[2] = { (float) pass[i].width, (float) pass[i].height };
|
||||
setUniform2fv(u->Pass[i].InputSize, passSize);
|
||||
setUniform2fv(u->Pass[i].TextureSize, passSize);
|
||||
setTexture1i (u->Pass[i].Texture, pass[i].texture);
|
||||
setTexCoords (u->Pass[i].TexCoord);
|
||||
}
|
||||
}
|
||||
|
||||
/* PASSPREV parameter */
|
||||
for (int i = 0; i < p; i++)
|
||||
{
|
||||
float passSize[2] = { (float) pass[i].width, (float) pass[i].height };
|
||||
setUniform2fv(u->PassPrev[p - i].InputSize, passSize);
|
||||
setUniform2fv(u->PassPrev[p - i].TextureSize, passSize);
|
||||
setTexture1i (u->PassPrev[p - i].Texture, pass[i].texture);
|
||||
setTexCoords (u->PassPrev[p - i].TexCoord);
|
||||
}
|
||||
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void GLSLShader::destroy (void)
|
||||
{
|
||||
glBindTexture (GL_TEXTURE_2D, 0);
|
||||
glUseProgram (0);
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
|
||||
for (unsigned int i = 1; i < pass.size(); i++)
|
||||
{
|
||||
glDeleteProgram (pass[i].program);
|
||||
glDeleteTextures (1, &pass[i].texture);
|
||||
glDeleteFramebuffers(1, &pass[i].fbo);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < lut.size(); i++)
|
||||
{
|
||||
glDeleteTextures (1, &lut[i].texture);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < prev_frame.size (); i++)
|
||||
{
|
||||
glDeleteTextures (1, &prev_frame[i].texture);
|
||||
}
|
||||
|
||||
pass.clear();
|
||||
lut.clear();
|
||||
prev_frame.clear();
|
||||
}
|
||||
|
||||
void GLSLShader::clear_shader_vars (void)
|
||||
{
|
||||
for (unsigned int i = 0; i < vaos.size(); i++)
|
||||
glDisableVertexAttribArray (vaos[i]);
|
||||
|
||||
vaos.clear();
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
#include <vector>
|
||||
#include <deque>
|
||||
#include <limits.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
enum GLSLScaleType
|
||||
{
|
||||
GLSL_NONE = 0,
|
||||
GLSL_SOURCE,
|
||||
GLSL_VIEWPORT,
|
||||
GLSL_ABSOLUTE
|
||||
};
|
||||
|
||||
enum GLSLFilter
|
||||
{
|
||||
GLSL_UNDEFINED = 0,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLint Texture;
|
||||
GLint InputSize;
|
||||
GLint TextureSize;
|
||||
GLint TexCoord;
|
||||
|
||||
} GLSLUniformMetrics;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLint Texture;
|
||||
GLint InputSize;
|
||||
GLint OutputSize;
|
||||
GLint TextureSize;
|
||||
|
||||
GLint FrameCount;
|
||||
GLint FrameDirection;
|
||||
|
||||
GLint TexCoord;
|
||||
GLint LUTTexCoord;
|
||||
GLint VertexCoord;
|
||||
|
||||
GLint OrigTexture;
|
||||
GLint OrigInputSize;
|
||||
GLint OrigTextureSize;
|
||||
GLint OrigTexCoord;
|
||||
|
||||
unsigned int max_pass;
|
||||
unsigned int max_prevpass;
|
||||
GLSLUniformMetrics Prev[7];
|
||||
GLSLUniformMetrics Pass[20];
|
||||
GLSLUniformMetrics PassPrev[20];
|
||||
GLint Lut[9];
|
||||
|
||||
} GLSLUniforms;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
int scale_type_x;
|
||||
int scale_type_y;
|
||||
float scale_x;
|
||||
float scale_y;
|
||||
bool fp;
|
||||
int frame_count_mod;
|
||||
unsigned int frame_count;
|
||||
|
||||
GLuint program;
|
||||
GLuint vertex_shader;
|
||||
GLuint fragment_shader;
|
||||
GLuint texture;
|
||||
GLuint fbo;
|
||||
GLuint width;
|
||||
GLuint height;
|
||||
GLuint filter;
|
||||
|
||||
GLSLUniforms unif;
|
||||
} GLSLPass;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char id[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
GLuint filter;
|
||||
GLuint texture;
|
||||
} GLSLLut;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[PATH_MAX];
|
||||
float min;
|
||||
float max;
|
||||
float def;
|
||||
float step;
|
||||
} GLSLParam;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool load_shader (char *filename);
|
||||
bool load_shader_file (char *filename);
|
||||
void render (GLuint &orig, int width, int height, int viewport_width, int viewport_height);
|
||||
void set_shader_vars (int pass);
|
||||
void clear_shader_vars (void);
|
||||
void destroy (void);
|
||||
void register_uniforms (void);
|
||||
|
||||
std::vector<GLSLPass> pass;
|
||||
std::vector<GLSLLut> lut;
|
||||
std::vector<GLSLParam> param;
|
||||
int max_prev_frame;
|
||||
std::deque<GLSLPass> prev_frame;
|
||||
std::vector<GLuint> vaos;
|
||||
|
||||
unsigned int frame_count;
|
||||
GLuint vbo;
|
||||
GLuint prev_fbo;
|
||||
GLfloat *fa;
|
||||
|
||||
} GLSLShader;
|
|
@ -0,0 +1,223 @@
|
|||
#include <epoxy/gl.h>
|
||||
#include <png.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "shader_helpers.h"
|
||||
|
||||
static void gl_error_callback( GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
const void* userParam )
|
||||
{
|
||||
if (type== GL_DEBUG_TYPE_ERROR)
|
||||
{
|
||||
fprintf( stderr, "GL: %s type = 0x%x, severity = 0x%x, \n %s\n",
|
||||
(type == GL_DEBUG_TYPE_ERROR ? "*ERROR*" : ""),
|
||||
type, severity, message );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "GL type = 0x%x, severity = 0x%x, \n %s\n",
|
||||
type, severity, message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glLogErrors (void)
|
||||
{
|
||||
glEnable (GL_DEBUG_OUTPUT);
|
||||
glDebugMessageCallback ((GLDEBUGPROC) gl_error_callback, 0);
|
||||
}
|
||||
|
||||
bool loadPngImage(const char* name,
|
||||
int& outWidth,
|
||||
int& outHeight,
|
||||
bool& outHasAlpha,
|
||||
GLubyte** outData)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
unsigned int sig_read = 0;
|
||||
FILE* fp;
|
||||
|
||||
if ((fp = fopen(name, "rb")) == NULL)
|
||||
return false;
|
||||
|
||||
/* Create and initialize the png_struct
|
||||
* with the desired error handler
|
||||
* functions. If you want to use the
|
||||
* default stderr and longjump method,
|
||||
* you can supply NULL for the last
|
||||
* three parameters. We also supply the
|
||||
* the compiler header file version, so
|
||||
* that we know if the application
|
||||
* was compiled with a compatible version
|
||||
* of the library. REQUIRED
|
||||
*/
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
|
||||
if (png_ptr == NULL) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Allocate/initialize the memory
|
||||
* for image information. REQUIRED. */
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL) {
|
||||
fclose(fp);
|
||||
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set error handling if you are
|
||||
* using the setjmp/longjmp method
|
||||
* (this is the normal method of
|
||||
* doing things with libpng).
|
||||
* REQUIRED unless you set up
|
||||
* your own error handlers in
|
||||
* the png_create_read_struct()
|
||||
* earlier.
|
||||
*/
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
/* Free all of the memory associated
|
||||
* with the png_ptr and info_ptr */
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
fclose(fp);
|
||||
/* If we get here, we had a
|
||||
* problem reading the file */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set up the output control if
|
||||
* you are using standard C streams */
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
/* If we have already
|
||||
* read some of the signature */
|
||||
png_set_sig_bytes(png_ptr, sig_read);
|
||||
|
||||
/*
|
||||
* If you have enough memory to read
|
||||
* in the entire image at once, and
|
||||
* you need to specify only
|
||||
* transforms that can be controlled
|
||||
* with one of the PNG_TRANSFORM_*
|
||||
* bits (this presently excludes
|
||||
* dithering, filling, setting
|
||||
* background, and doing gamma
|
||||
* adjustment), then you can read the
|
||||
* entire image (including pixels)
|
||||
* into the info structure with this
|
||||
* call
|
||||
*
|
||||
* PNG_TRANSFORM_STRIP_16 |
|
||||
* PNG_TRANSFORM_PACKING forces 8 bit
|
||||
* PNG_TRANSFORM_EXPAND forces to
|
||||
* expand a palette into RGB
|
||||
*/
|
||||
png_read_png(png_ptr,
|
||||
info_ptr,
|
||||
PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND,
|
||||
(png_voidp)NULL);
|
||||
|
||||
outWidth = png_get_image_width(png_ptr, info_ptr);
|
||||
outHeight = png_get_image_height(png_ptr, info_ptr);
|
||||
switch (png_get_color_type(png_ptr, info_ptr)) {
|
||||
case PNG_COLOR_TYPE_RGBA:
|
||||
outHasAlpha = true;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
outHasAlpha = false;
|
||||
break;
|
||||
default:
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||
*outData = (unsigned char*)malloc(row_bytes * outHeight);
|
||||
|
||||
png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);
|
||||
|
||||
for (int i = 0; i < outHeight; i++) {
|
||||
memcpy(*outData + (row_bytes * i), row_pointers[i], row_bytes);
|
||||
}
|
||||
|
||||
/* Clean up after the read,
|
||||
* and free any memory allocated */
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
|
||||
/* Close the file */
|
||||
fclose(fp);
|
||||
|
||||
/* That's it */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadTGA(const char* filename, STGA& tgaFile)
|
||||
{
|
||||
FILE* file;
|
||||
unsigned char type[4];
|
||||
unsigned char info[6];
|
||||
|
||||
file = fopen(filename, "rb");
|
||||
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
fread(&type, sizeof(char), 3, file);
|
||||
fseek(file, 12, SEEK_SET);
|
||||
fread(&info, sizeof(char), 6, file);
|
||||
|
||||
// image type either 2 (color) or 3 (greyscale)
|
||||
if (type[1] != 0 || (type[2] != 2 && type[2] != 3)) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
tgaFile.width = info[0] + info[1] * 256;
|
||||
tgaFile.height = info[2] + info[3] * 256;
|
||||
tgaFile.byteCount = info[4] / 8;
|
||||
|
||||
if (tgaFile.byteCount != 3 && tgaFile.byteCount != 4) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
long imageSize = tgaFile.width * tgaFile.height * tgaFile.byteCount;
|
||||
|
||||
// allocate memory for image data
|
||||
unsigned char* tempBuf = new unsigned char[imageSize];
|
||||
tgaFile.data = new unsigned char[tgaFile.width * tgaFile.height * 4];
|
||||
|
||||
// read in image data
|
||||
fread(tempBuf, sizeof(unsigned char), imageSize, file);
|
||||
|
||||
// swap line order and convert to RBGA
|
||||
for (int i = 0; i < tgaFile.height; i++) {
|
||||
unsigned char* source = tempBuf + tgaFile.width * (tgaFile.height - 1 - i) * tgaFile.byteCount;
|
||||
unsigned char* destination = tgaFile.data + tgaFile.width * i * 4;
|
||||
for (int j = 0; j < tgaFile.width; j++) {
|
||||
destination[0] = source[2];
|
||||
destination[1] = source[1];
|
||||
destination[2] = source[0];
|
||||
destination[3] = tgaFile.byteCount == 4 ? source[3] : 0xff;
|
||||
source += tgaFile.byteCount;
|
||||
destination += 4;
|
||||
}
|
||||
}
|
||||
delete[] tempBuf;
|
||||
tgaFile.byteCount = 4;
|
||||
|
||||
// close file
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef __IMAGE_FILE_FORMATS_H
|
||||
#define __IMAGE_FILE_FORMATS_H
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
typedef struct _STGA
|
||||
{
|
||||
_STGA()
|
||||
{
|
||||
data = (unsigned char*)0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
byteCount = 0;
|
||||
}
|
||||
|
||||
~_STGA()
|
||||
{
|
||||
delete[] data;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
delete[] data;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
int width;
|
||||
int height;
|
||||
unsigned char byteCount;
|
||||
unsigned char* data;
|
||||
} STGA;
|
||||
|
||||
bool loadPngImage(const char* name,
|
||||
int& outWidth,
|
||||
int& outHeight,
|
||||
bool& outHasAlpha,
|
||||
GLubyte** outData);
|
||||
|
||||
bool loadTGA(const char* filename, STGA& tgaFile);
|
||||
|
||||
void glLogErrors (void);
|
||||
|
||||
#endif // __IMAGE_FILE_FORMATS_H
|
Loading…
Reference in New Issue