mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-11 10:04:25 +02:00
Update to v093 release.
byuu says: Changelog: - added Cocoa target: higan can now be compiled for OS X Lion [Cydrak, byuu] - SNES/accuracy profile hires color blending improvements - fixes Marvelous text [AWJ] - fixed a slight bug in SNES/SA-1 VBR support caused by a typo - added support for multi-pass shaders that can load external textures (requires OpenGL 3.2+) - added game library path (used by ananke->Import Game) to Settings->Advanced - system profiles, shaders and cheats database can be stored in "all users" shared folders now (eg /usr/share on Linux) - all configuration files are in BML format now, instead of XML (much easier to read and edit this way) - main window supports drag-and-drop of game folders (but not game files / ZIP archives) - audio buffer clears when entering a modal loop on Windows (prevents audio repetition with DirectSound driver) - a substantial amount of code clean-up (probably the biggest refactoring to date) One highly desired target for this release was to default to the optimal drivers instead of the safest drivers, but because AMD drivers don't seem to like my OpenGL 3.2 driver, I've decided to postpone that. AMD has too big a market share. Hopefully with v093 officially released, we can get some public input on what AMD doesn't like.
This commit is contained in:
103
ruby/video/opengl/bind.hpp
Normal file
103
ruby/video/opengl/bind.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#if !defined(PLATFORM_OSX)
|
||||
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
|
||||
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
|
||||
PFNGLCREATESHADERPROC glCreateShader = nullptr;
|
||||
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
|
||||
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
|
||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
|
||||
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
|
||||
PFNGLDETACHSHADERPROC glDetachShader = nullptr;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
|
||||
PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
|
||||
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
|
||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
|
||||
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
|
||||
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
|
||||
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
|
||||
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
|
||||
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
|
||||
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
|
||||
PFNGLBUFFERDATAPROC glBufferData = nullptr;
|
||||
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = nullptr;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
|
||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr;
|
||||
PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation = nullptr;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
|
||||
PFNGLGETUNIFORMIVPROC glGetUniformiv = nullptr;
|
||||
PFNGLUNIFORM1IPROC glUniform1i = nullptr;
|
||||
PFNGLUNIFORM1FPROC glUniform1f = nullptr;
|
||||
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
|
||||
PFNGLUNIFORM2FVPROC glUniform2fv = nullptr;
|
||||
PFNGLUNIFORM4FPROC glUniform4f = nullptr;
|
||||
PFNGLUNIFORM4FVPROC glUniform4fv = nullptr;
|
||||
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr;
|
||||
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
|
||||
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
|
||||
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
|
||||
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
|
||||
PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr;
|
||||
|
||||
static bool OpenGLBind() {
|
||||
#define bind(prototype, function) \
|
||||
function = (prototype)glGetProcAddress(#function); \
|
||||
if(function == nullptr) return false
|
||||
|
||||
bind(PFNGLCREATEPROGRAMPROC, glCreateProgram);
|
||||
bind(PFNGLDELETEPROGRAMPROC, glDeleteProgram);
|
||||
bind(PFNGLUSEPROGRAMPROC, glUseProgram);
|
||||
bind(PFNGLCREATESHADERPROC, glCreateShader);
|
||||
bind(PFNGLDELETESHADERPROC, glDeleteShader);
|
||||
bind(PFNGLSHADERSOURCEPROC, glShaderSource);
|
||||
bind(PFNGLCOMPILESHADERPROC, glCompileShader);
|
||||
bind(PFNGLGETSHADERIVPROC, glGetShaderiv);
|
||||
bind(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog);
|
||||
bind(PFNGLATTACHSHADERPROC, glAttachShader);
|
||||
bind(PFNGLDETACHSHADERPROC, glDetachShader);
|
||||
bind(PFNGLLINKPROGRAMPROC, glLinkProgram);
|
||||
bind(PFNGLVALIDATEPROGRAMPROC, glValidateProgram);
|
||||
bind(PFNGLGETPROGRAMIVPROC, glGetProgramiv);
|
||||
bind(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog);
|
||||
bind(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays);
|
||||
bind(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays);
|
||||
bind(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray);
|
||||
bind(PFNGLGENBUFFERSPROC, glGenBuffers);
|
||||
bind(PFNGLDELETEBUFFERSPROC, glDeleteBuffers);
|
||||
bind(PFNGLBINDBUFFERPROC, glBindBuffer);
|
||||
bind(PFNGLBUFFERDATAPROC, glBufferData);
|
||||
bind(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation);
|
||||
bind(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer);
|
||||
bind(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray);
|
||||
bind(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray);
|
||||
bind(PFNGLBINDFRAGDATALOCATIONPROC, glBindFragDataLocation);
|
||||
bind(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation);
|
||||
bind(PFNGLGETUNIFORMIVPROC, glGetUniformiv);
|
||||
bind(PFNGLUNIFORM1IPROC, glUniform1i);
|
||||
bind(PFNGLUNIFORM1FPROC, glUniform1f);
|
||||
bind(PFNGLUNIFORM2FPROC, glUniform2f);
|
||||
bind(PFNGLUNIFORM2FVPROC, glUniform2fv);
|
||||
bind(PFNGLUNIFORM4FPROC, glUniform4f);
|
||||
bind(PFNGLUNIFORM4FVPROC, glUniform4fv);
|
||||
bind(PFNGLUNIFORMMATRIX4FVPROC, glUniformMatrix4fv);
|
||||
bind(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers);
|
||||
bind(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers);
|
||||
bind(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer);
|
||||
bind(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D);
|
||||
bind(PFNGLACTIVETEXTUREPROC, glActiveTexture);
|
||||
|
||||
#undef bind
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static bool OpenGLBind() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
147
ruby/video/opengl/main.hpp
Normal file
147
ruby/video/opengl/main.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
void OpenGL::shader(const char* pathname) {
|
||||
for(auto& program : programs) program.release();
|
||||
programs.reset();
|
||||
|
||||
format = GL_RGBA8;
|
||||
filter = GL_LINEAR;
|
||||
wrap = GL_CLAMP_TO_BORDER;
|
||||
absoluteWidth = 0, absoluteHeight = 0;
|
||||
relativeWidth = 0, relativeHeight = 0;
|
||||
|
||||
if(pathname) {
|
||||
auto document = Markup::Document(file::read({pathname, "manifest.bml"}));
|
||||
for(auto& node : document.find("program")) {
|
||||
unsigned n = programs.size();
|
||||
programs(n).bind(this, node, pathname);
|
||||
}
|
||||
|
||||
bind(this, document["output"], pathname);
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGL::lock(uint32_t*& data, unsigned& pitch) {
|
||||
pitch = width * sizeof(uint32_t);
|
||||
return data = buffer;
|
||||
}
|
||||
|
||||
void OpenGL::clear() {
|
||||
for(auto& p : programs) {
|
||||
glUseProgram(p.program);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
glUseProgram(0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void OpenGL::refresh() {
|
||||
clear();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, inputFormat, buffer);
|
||||
|
||||
struct History {
|
||||
GLuint texture;
|
||||
unsigned width, height;
|
||||
GLuint filter, wrap;
|
||||
};
|
||||
vector<History> history;
|
||||
|
||||
unsigned sourceWidth = width, sourceHeight = height;
|
||||
history.prepend({texture, sourceWidth, sourceHeight, filter, wrap});
|
||||
|
||||
for(auto& p : programs) {
|
||||
unsigned targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
|
||||
unsigned targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
|
||||
if(p.relativeWidth) targetWidth = sourceWidth * p.relativeWidth;
|
||||
if(p.relativeHeight) targetHeight = sourceHeight * p.relativeHeight;
|
||||
|
||||
p.size(targetWidth, targetHeight);
|
||||
glUseProgram(p.program);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
||||
|
||||
glrUniform1i("phase", p.phase);
|
||||
glrUniform1i("sourceLength", history.size());
|
||||
glrUniform1i("pixmapLength", p.pixmaps.size());
|
||||
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
||||
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
||||
//glrUniform4f("targetActualSize", glrSize(targetWidth), glrSize(targetHeight), 1.0 / glrSize(targetWidth), 1.0 / glrSize(targetHeight));
|
||||
//glrUniform4f("outputActualSize", glrSize(outputWidth), glrSize(outputHeight), 1.0 / glrSize(outputWidth), 1.0 / glrSize(outputHeight));
|
||||
|
||||
unsigned aid = 0;
|
||||
for(auto& pixmap : history) {
|
||||
glrUniform1i({"source[", aid, "]"}, aid);
|
||||
glrUniform4f({"sourceSize[", aid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height);
|
||||
//glrUniform4f({"sourceActualSize[", aid, "]"}, glrSize(pixmap.width), glrSize(pixmap.height), 1.0 / glrSize(pixmap.width), 1.0 / glrSize(pixmap.height));
|
||||
glActiveTexture(GL_TEXTURE0 + (aid++));
|
||||
glBindTexture(GL_TEXTURE_2D, pixmap.texture);
|
||||
glrParameters(pixmap.filter, pixmap.wrap);
|
||||
}
|
||||
|
||||
unsigned bid = 0;
|
||||
for(auto& pixmap : p.pixmaps) {
|
||||
glrUniform1i({"pixmap[", bid, "]"}, aid + bid);
|
||||
glrUniform4f({"pixmapSize[", bid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height);
|
||||
//glrUniform4f({"pixmapActualSize[", bid, "]"}, glrSize(pixmap.width), glrSize(pixmap.height), 1.0 / glrSize(pixmap.width), 1.0 / glrSize(pixmap.height));
|
||||
glActiveTexture(GL_TEXTURE0 + aid + (bid++));
|
||||
glBindTexture(GL_TEXTURE_2D, pixmap.texture);
|
||||
glrParameters(pixmap.filter, pixmap.wrap);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glrParameters(p.filter, p.wrap);
|
||||
p.render(sourceWidth, sourceHeight, targetWidth, targetHeight);
|
||||
glBindTexture(GL_TEXTURE_2D, p.texture);
|
||||
|
||||
p.phase = (p.phase + 1) % p.modulo;
|
||||
sourceWidth = p.width, sourceHeight = p.height;
|
||||
history.prepend({p.texture, sourceWidth, sourceHeight, p.filter, p.wrap});
|
||||
}
|
||||
|
||||
unsigned targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
|
||||
unsigned targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
|
||||
if(relativeWidth) targetWidth = sourceWidth * relativeWidth;
|
||||
if(relativeHeight) targetHeight = sourceHeight * relativeHeight;
|
||||
|
||||
glUseProgram(program);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
glrUniform1i("source[0]", 0);
|
||||
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
||||
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
||||
|
||||
glrParameters(filter, wrap);
|
||||
render(sourceWidth, sourceHeight, outputWidth, outputHeight);
|
||||
}
|
||||
|
||||
bool OpenGL::init() {
|
||||
if(!OpenGLBind()) return false;
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_POLYGON_SMOOTH);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
glEnable(GL_DITHER);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
program = glCreateProgram();
|
||||
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLOutputVertexShader);
|
||||
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
|
||||
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
|
||||
OpenGLSurface::allocate();
|
||||
glrLinkProgram(program);
|
||||
|
||||
shader(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGL::term() {
|
||||
OpenGLSurface::release();
|
||||
if(buffer) { delete[] buffer; buffer = nullptr; }
|
||||
}
|
82
ruby/video/opengl/opengl.hpp
Normal file
82
ruby/video/opengl/opengl.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#if defined(PLATFORM_X)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
|
||||
#elif defined(PLATFORM_OSX)
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/gl3.h>
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#define glGetProcAddress(name) wglGetProcAddress(name)
|
||||
#else
|
||||
#error "ruby::OpenGL: unsupported platform"
|
||||
#endif
|
||||
|
||||
namespace ruby {
|
||||
|
||||
#include "bind.hpp"
|
||||
#include "shaders.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
struct OpenGL;
|
||||
|
||||
struct OpenGLTexture {
|
||||
GLuint texture = 0;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
GLuint format = GL_RGBA8;
|
||||
GLuint filter = GL_LINEAR;
|
||||
GLuint wrap = GL_CLAMP_TO_BORDER;
|
||||
};
|
||||
|
||||
struct OpenGLSurface : OpenGLTexture {
|
||||
GLuint program = 0;
|
||||
GLuint framebuffer = 0;
|
||||
GLuint vao = 0;
|
||||
GLuint vbo[3] = {0, 0, 0};
|
||||
GLuint vertex = 0;
|
||||
GLuint geometry = 0;
|
||||
GLuint fragment = 0;
|
||||
uint32_t* buffer = nullptr;
|
||||
|
||||
void allocate();
|
||||
void size(unsigned width, unsigned height);
|
||||
void release();
|
||||
void render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight);
|
||||
};
|
||||
|
||||
struct OpenGLProgram : OpenGLSurface {
|
||||
//configuration
|
||||
unsigned phase = 0; //frame counter
|
||||
unsigned modulo = 0; //frame counter modulus
|
||||
unsigned absoluteWidth = 0;
|
||||
unsigned absoluteHeight = 0;
|
||||
double relativeWidth = 0;
|
||||
double relativeHeight = 0;
|
||||
vector<OpenGLTexture> pixmaps;
|
||||
|
||||
void bind(OpenGL* instance, const Markup::Node& node, const string& pathname);
|
||||
void release();
|
||||
};
|
||||
|
||||
struct OpenGL : OpenGLProgram {
|
||||
vector<OpenGLProgram> programs;
|
||||
|
||||
GLuint inputFormat = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
unsigned outputWidth = 0;
|
||||
unsigned outputHeight = 0;
|
||||
|
||||
void shader(const char* pathname);
|
||||
bool lock(uint32_t*& data, unsigned& pitch);
|
||||
void clear();
|
||||
void refresh();
|
||||
bool init();
|
||||
void term();
|
||||
};
|
||||
|
||||
#include "surface.hpp"
|
||||
#include "program.hpp"
|
||||
#include "main.hpp"
|
||||
|
||||
}
|
87
ruby/video/opengl/program.hpp
Normal file
87
ruby/video/opengl/program.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const string& pathname) {
|
||||
filter = glrFilter(node["filter"].text());
|
||||
wrap = glrWrap(node["wrap"].text());
|
||||
modulo = glrModulo(node["modulo"].integer());
|
||||
|
||||
string w = node["width"].text(), h = node["height"].text();
|
||||
if(w.endswith("%")) relativeWidth = real(w.rtrim<1>("%")) / 100.0;
|
||||
else absoluteWidth = decimal(w);
|
||||
if(h.endswith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
|
||||
else absoluteHeight = decimal(h);
|
||||
|
||||
if(node.name != "program") return;
|
||||
format = glrFormat(node["format"].text());
|
||||
|
||||
program = glCreateProgram();
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
|
||||
if(file::exists({pathname, node["vertex"].text()})) {
|
||||
string source = file::read({pathname, node["vertex"].text()});
|
||||
vertex = glrCreateShader(program, GL_VERTEX_SHADER, source);
|
||||
} else {
|
||||
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLVertexShader);
|
||||
}
|
||||
|
||||
if(file::exists({pathname, node["geometry"].text()})) {
|
||||
string source = file::read({pathname, node["geometry"].text()});
|
||||
geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, source);
|
||||
} else {
|
||||
//geometry shaders, when attached, must pass all vertex output through to the fragment shaders
|
||||
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
|
||||
}
|
||||
|
||||
if(file::exists({pathname, node["fragment"].text()})) {
|
||||
string source = file::read({pathname, node["fragment"].text()});
|
||||
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, source);
|
||||
} else {
|
||||
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
|
||||
}
|
||||
|
||||
for(auto& leaf : node.find("pixmap")) {
|
||||
nall::image image({pathname, leaf.text()});
|
||||
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
|
||||
if(image.empty()) continue;
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
|
||||
unsigned n = pixmaps.size();
|
||||
pixmaps(n).texture = texture;
|
||||
pixmaps(n).width = image.width;
|
||||
pixmaps(n).height = image.height;
|
||||
pixmaps(n).format = format;
|
||||
pixmaps(n).filter = filter;
|
||||
pixmaps(n).wrap = wrap;
|
||||
if(leaf["format"].exists()) pixmaps(n).format = glrFormat(leaf["format"].text());
|
||||
if(leaf["filter"].exists()) pixmaps(n).filter = glrFilter(leaf["filter"].text());
|
||||
if(leaf["wrap"].exists()) pixmaps(n).wrap = glrWrap(leaf["wrap"].text());
|
||||
|
||||
unsigned w = glrSize(image.width), h = glrSize(image.height);
|
||||
uint32_t* buffer = new uint32_t[w * h]();
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width, image.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, image.data);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
OpenGLSurface::allocate();
|
||||
glrLinkProgram(program);
|
||||
}
|
||||
|
||||
void OpenGLProgram::release() {
|
||||
OpenGLSurface::release();
|
||||
for(auto& pixmap : pixmaps) glDeleteTextures(1, &pixmap.texture);
|
||||
pixmaps.reset();
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
format = GL_RGBA8;
|
||||
filter = GL_LINEAR;
|
||||
wrap = GL_CLAMP_TO_BORDER;
|
||||
phase = 0;
|
||||
modulo = 0;
|
||||
absoluteWidth = 0;
|
||||
absoluteHeight = 0;
|
||||
relativeWidth = 0;
|
||||
relativeHeight = 0;
|
||||
}
|
93
ruby/video/opengl/shaders.hpp
Normal file
93
ruby/video/opengl/shaders.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
static string OpenGLOutputVertexShader = R"(
|
||||
#version 150
|
||||
|
||||
uniform vec4 targetSize;
|
||||
uniform vec4 outputSize;
|
||||
|
||||
in vec2 texCoord;
|
||||
|
||||
out Vertex {
|
||||
vec4 position;
|
||||
vec2 texCoord;
|
||||
} vertexOut;
|
||||
|
||||
void main() {
|
||||
//center image within output window
|
||||
if(gl_VertexID == 0 || gl_VertexID == 2) {
|
||||
gl_Position.x = -(targetSize.x / outputSize.x);
|
||||
} else {
|
||||
gl_Position.x = +(targetSize.x / outputSize.x);
|
||||
}
|
||||
|
||||
//center and flip vertically (buffer[0, 0] = top-left; OpenGL[0, 0] = bottom-left)
|
||||
if(gl_VertexID == 0 || gl_VertexID == 1) {
|
||||
gl_Position.y = +(targetSize.y / outputSize.y);
|
||||
} else {
|
||||
gl_Position.y = -(targetSize.y / outputSize.y);
|
||||
}
|
||||
|
||||
//align image to even pixel boundary to prevent aliasing
|
||||
vec2 align = fract((outputSize.xy + targetSize.xy) / 2.0) * 2.0;
|
||||
gl_Position.xy -= align / outputSize.xy;
|
||||
gl_Position.zw = vec2(0.0, 1.0);
|
||||
|
||||
vertexOut.texCoord = texCoord;
|
||||
}
|
||||
)";
|
||||
|
||||
static string OpenGLVertexShader = R"(
|
||||
#version 150
|
||||
|
||||
in vec4 position;
|
||||
in vec2 texCoord;
|
||||
|
||||
out Vertex {
|
||||
vec4 position;
|
||||
vec2 texCoord;
|
||||
} vertexOut;
|
||||
|
||||
void main() {
|
||||
gl_Position = position;
|
||||
vertexOut.texCoord = texCoord;
|
||||
}
|
||||
)";
|
||||
|
||||
static string OpenGLGeometryShader = R"(
|
||||
#version 150
|
||||
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
} vertexIn[];
|
||||
|
||||
out Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
void main() {
|
||||
for(int i = 0; i < gl_in.length(); i++) {
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
texCoord = vertexIn[i].texCoord;
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
)";
|
||||
|
||||
static string OpenGLFragmentShader = R"(
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = texture(source[0], texCoord);
|
||||
}
|
||||
)";
|
114
ruby/video/opengl/surface.hpp
Normal file
114
ruby/video/opengl/surface.hpp
Normal file
@@ -0,0 +1,114 @@
|
||||
void OpenGLSurface::allocate() {
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
glGenBuffers(3, &vbo[0]);
|
||||
}
|
||||
|
||||
void OpenGLSurface::size(unsigned w, unsigned h) {
|
||||
if(width == w && height == h) return;
|
||||
width = w, height = h;
|
||||
w = glrSize(w), h = glrSize(h);
|
||||
|
||||
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
|
||||
if(buffer) { delete[] buffer; buffer = nullptr; }
|
||||
|
||||
buffer = new uint32_t[w * h]();
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
|
||||
if(framebuffer) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLSurface::release() {
|
||||
if(vbo[0]) { glDeleteBuffers(3, &vbo[0]); for(auto &o : vbo) o = 0; }
|
||||
if(vao) { glDeleteVertexArrays(1, &vao); vao = 0; }
|
||||
if(vertex) { glDetachShader(program, vertex); glDeleteShader(vertex); vertex = 0; }
|
||||
if(geometry) { glDetachShader(program, geometry); glDeleteShader(geometry); geometry = 0; }
|
||||
if(fragment) { glDetachShader(program, fragment); glDeleteShader(fragment); fragment = 0; }
|
||||
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
|
||||
if(framebuffer) { glDeleteFramebuffers(1, &framebuffer); framebuffer = 0; }
|
||||
if(program) { glDeleteProgram(program); program = 0; }
|
||||
width = 0, height = 0;
|
||||
}
|
||||
|
||||
void OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) {
|
||||
glViewport(0, 0, targetWidth, targetHeight);
|
||||
|
||||
float w = (float)sourceWidth / (float)glrSize(sourceWidth);
|
||||
float h = (float)sourceHeight / (float)glrSize(sourceHeight);
|
||||
float u = (float)targetWidth, v = (float)targetHeight;
|
||||
GLint location;
|
||||
|
||||
GLfloat modelView[] = {
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
|
||||
GLfloat projection[] = {
|
||||
2.0f/u, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f/v, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, -1.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
GLfloat modelViewProjection[4 * 4];
|
||||
Matrix::Multiply(modelViewProjection, modelView, 4, 4, projection, 4, 4);
|
||||
|
||||
GLfloat vertices[] = {
|
||||
0, 0, 0, 1,
|
||||
u, 0, 0, 1,
|
||||
0, v, 0, 1,
|
||||
u, v, 0, 1,
|
||||
};
|
||||
|
||||
GLfloat positions[4 * 4];
|
||||
for(unsigned n = 0; n < 16; n += 4) {
|
||||
Matrix::Multiply(&positions[n], &vertices[n], 1, 4, modelViewProjection, 4, 4);
|
||||
}
|
||||
|
||||
GLfloat texCoords[] = {
|
||||
0, 0,
|
||||
w, 0,
|
||||
0, h,
|
||||
w, h,
|
||||
};
|
||||
|
||||
glrUniformMatrix4fv("modelView", modelView);
|
||||
glrUniformMatrix4fv("projection", projection);
|
||||
glrUniformMatrix4fv("modelViewProjection", modelViewProjection);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
|
||||
GLuint locationVertex = glGetAttribLocation(program, "vertex");
|
||||
glEnableVertexAttribArray(locationVertex);
|
||||
glVertexAttribPointer(locationVertex, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), positions, GL_STATIC_DRAW);
|
||||
GLuint locationPosition = glGetAttribLocation(program, "position");
|
||||
glEnableVertexAttribArray(locationPosition);
|
||||
glVertexAttribPointer(locationPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), texCoords, GL_STATIC_DRAW);
|
||||
GLuint locationTexCoord = glGetAttribLocation(program, "texCoord");
|
||||
glEnableVertexAttribArray(locationTexCoord);
|
||||
glVertexAttribPointer(locationTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
glBindFragDataLocation(program, 0, "fragColor");
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableVertexAttribArray(locationVertex);
|
||||
glDisableVertexAttribArray(locationPosition);
|
||||
glDisableVertexAttribArray(locationTexCoord);
|
||||
}
|
104
ruby/video/opengl/utility.hpp
Normal file
104
ruby/video/opengl/utility.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
static unsigned glrSize(unsigned size) {
|
||||
return size;
|
||||
//return bit::round(size); //return nearest power of two
|
||||
}
|
||||
|
||||
static GLuint glrFormat(const string& format) {
|
||||
if(format == "rgba8" ) return GL_RGBA8;
|
||||
if(format == "rgb10a2") return GL_RGB10_A2;
|
||||
if(format == "rgba12" ) return GL_RGBA12;
|
||||
if(format == "rgba16" ) return GL_RGBA16;
|
||||
if(format == "rgba16f") return GL_RGBA16F;
|
||||
if(format == "rgba32f") return GL_RGBA32F;
|
||||
return GL_RGBA8;
|
||||
}
|
||||
|
||||
static GLuint glrFilter(const string& filter) {
|
||||
if(filter == "nearest") return GL_NEAREST;
|
||||
if(filter == "linear" ) return GL_LINEAR;
|
||||
return GL_LINEAR;
|
||||
}
|
||||
|
||||
static GLuint glrWrap(const string& wrap) {
|
||||
if(wrap == "border") return GL_CLAMP_TO_BORDER;
|
||||
if(wrap == "edge" ) return GL_CLAMP_TO_EDGE;
|
||||
if(wrap == "repeat") return GL_REPEAT;
|
||||
return GL_CLAMP_TO_BORDER;
|
||||
}
|
||||
|
||||
static unsigned glrModulo(unsigned modulo) {
|
||||
if(modulo) return modulo;
|
||||
return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150
|
||||
}
|
||||
|
||||
static GLuint glrProgram() {
|
||||
GLuint program = 0;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&program);
|
||||
return program;
|
||||
}
|
||||
|
||||
static void glrUniform1i(const string& name, GLint value) {
|
||||
GLint location = glGetUniformLocation(glrProgram(), name);
|
||||
glUniform1i(location, value);
|
||||
}
|
||||
|
||||
static void glrUniform4f(const string& name, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat value3) {
|
||||
GLint location = glGetUniformLocation(glrProgram(), name);
|
||||
glUniform4f(location, value0, value1, value2, value3);
|
||||
}
|
||||
|
||||
static void glrUniformMatrix4fv(const string& name, GLfloat *values) {
|
||||
GLint location = glGetUniformLocation(glrProgram(), name);
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, values);
|
||||
}
|
||||
|
||||
static void glrParameters(GLuint filter, GLuint wrap) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
|
||||
}
|
||||
|
||||
static GLuint glrCreateShader(GLuint program, GLuint type, const char* source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, 0);
|
||||
glCompileShader(shader);
|
||||
GLint result = GL_FALSE;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
|
||||
if(result == GL_FALSE) {
|
||||
GLint length = 0;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
|
||||
char text[length + 1];
|
||||
glGetShaderInfoLog(shader, length, &length, text);
|
||||
text[length] = 0;
|
||||
print("[ruby::OpenGL: shader compiler error]\n", (const char*)text, "\n\n");
|
||||
return 0;
|
||||
}
|
||||
glAttachShader(program, shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void glrLinkProgram(GLuint program) {
|
||||
glLinkProgram(program);
|
||||
GLint result = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &result);
|
||||
if(result == GL_FALSE) {
|
||||
GLint length = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
|
||||
char text[length + 1];
|
||||
glGetProgramInfoLog(program, length, &length, text);
|
||||
text[length] = 0;
|
||||
print("[ruby::OpenGL: shader linker error]\n", (const char*)text, "\n\n");
|
||||
}
|
||||
glValidateProgram(program);
|
||||
result = GL_FALSE;
|
||||
glGetProgramiv(program, GL_VALIDATE_STATUS, &result);
|
||||
if(result == GL_FALSE) {
|
||||
GLint length = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
|
||||
char text[length + 1];
|
||||
glGetProgramInfoLog(program, length, &length, text);
|
||||
text[length] = 0;
|
||||
print("[ruby::OpenGL: shader validation error]\n", (const char*)text, "\n\n");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user