support for GLSL shader on both SDL1 and SDL2 builds

This commit is contained in:
Wengier 2020-09-20 17:48:33 -04:00
parent 0df4c8dd39
commit a01ac1c168
12 changed files with 1146 additions and 38 deletions

View File

@ -1,4 +1,11 @@
0.83.6
- Added GLSL shader support by porting the feature
from DOSBox SVN. Worked for SDL1 and SDL2 builds.
The config option "glshader" (in [render] section)
can be used to specify a GLSL shader file or a
built-in GLSL shader when the output is set to
"opengl" or "openglnb". For example, you can use
"glshader=sharp" for pixel-perfect mode. (Wengier)
- Support for FluidSynth MIDI Synthesizer is now
activated by default for Windows Visual Studio
x86/x64 builds. (Wengier)

View File

@ -10,7 +10,7 @@
<category>Emulation</category>
</categories>
<releases>
<release version="@PACKAGE_VERSION@" date="2020-9-19"/>
<release version="@PACKAGE_VERSION@" date="2020-9-20"/>
</releases>
<screenshots>
<screenshot/>

View File

@ -737,6 +737,10 @@ pc-98 show graphics layer on initialize = true
# To fit a scaler in the resolution used at full screen may require a border or side bars.
# To fill the screen entirely, depending on your hardware, a different scaler/fullresolution might work.
# Possible values: none, normal2x, normal3x, normal4x, normal5x, advmame2x, advmame3x, advinterp2x, advinterp3x, hq2x, hq3x, 2xsai, super2xsai, supereagle, tv2x, tv3x, rgb2x, rgb3x, scan2x, scan3x, gray, gray2x, hardware_none, hardware2x, hardware3x, hardware4x, hardware5x, xbrz, xbrz_bilinear.
# glshader: Path to GLSL shader source to use with OpenGL output ("none" to disable).
# Can be either an absolute path, a file in the "glshaders" subdirectory of the DOSBox-X configuration directory,
# or one of the built-in shaders (use "sharp" for the pixel-perfect mode):
# advinterp2x, advinterp3x, advmame2x, advmame3x, rgb2x, rgb3x, scan2x, scan3x, tv2x, tv3x, sharp.
# xbrz slice: Number of screen lines to process in single xBRZ scaler taskset task, affects xBRZ performance, 16 is the default
# xbrz fixed scale factor: To use fixed xBRZ scale factor (i.e. to attune performance), set it to 2-6, 0 - use automatic calculation (default)
# xbrz max scale factor: To cap maximum xBRZ scale factor used (i.e. to attune performance), set it to 2-6, 0 - use scaler allowed maximum (default)
@ -753,6 +757,7 @@ char9 = true
euro = -1
doublescan = true
scaler = normal2x
glshader = none
xbrz slice = 16
xbrz fixed scale factor = 0
xbrz max scale factor = 0

View File

@ -1,4 +1,4 @@
/*auto-generated*/
#define UPDATED_STR "Sep 19, 2020 6:54:21pm"
#define GIT_COMMIT_HASH "9ea356c"
#define UPDATED_STR "Sep 20, 2020 7:16:36pm"
#define GIT_COMMIT_HASH "8bcc19e"
#define COPYRIGHT_END_YEAR "2020"

View File

@ -91,6 +91,9 @@ typedef struct {
Bit8u *cacheRead;
Bitu inHeight, inLine, outLine;
} scale;
#if C_OPENGL
char* shader_src;
#endif
RenderPal_t pal;
bool updating;
bool active;
@ -108,6 +111,7 @@ void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double scrn_ratio)
bool RENDER_StartUpdate(void);
void RENDER_EndUpdate(bool abort);
void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue);
bool RENDER_GetForceUpdate(void);
void RENDER_SetForceUpdate(bool);
#endif

View File

@ -60,7 +60,7 @@ Bitu GFX_GetBestMode(Bitu flags);
Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue);
Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback);
void GFX_TearDown(void);
void GFX_SetShader(const char* src);
void GFX_ResetScreen(void);
void GFX_RestoreMode(void);
void GFX_Start(void);

View File

@ -2235,6 +2235,14 @@ void DOSBOX_SetupConfigSections(void) {
Pstring = Pmulti->GetSection()->Add_string("force",Property::Changeable::Always,"");
Pstring->Set_values(force);
#if C_OPENGL
Pstring = secprop->Add_path("glshader",Property::Changeable::Always,"none");
Pstring->Set_help("Path to GLSL shader source to use with OpenGL output (\"none\" to disable).\n"
"Can be either an absolute path, a file in the \"glshaders\" subdirectory of the DOSBox-X configuration directory,\n"
"or one of the built-in shaders (use \"sharp\" for the pixel-perfect mode):\n"
"advinterp2x, advinterp3x, advmame2x, advmame3x, rgb2x, rgb3x, scan2x, scan3x, tv2x, tv3x, sharp.");
#endif
#if C_XBRZ
Pint = secprop->Add_int("xbrz slice",Property::Changeable::OnlyAtStart,16);
Pint->SetMinMax(1,1024);

View File

@ -20,6 +20,8 @@
#include <sys/types.h>
#include <assert.h>
#include <math.h>
#include <fstream>
#include <sstream>
#include "dosbox.h"
#include "video.h"
@ -31,8 +33,10 @@
#include "hardware.h"
#include "support.h"
#include "sdlmain.h"
#include "shell.h"
#include "render_scalers.h"
#include "render_glsl.h"
#if defined(__SSE__)
#include <xmmintrin.h>
#include <emmintrin.h>
@ -366,7 +370,7 @@ void RENDER_EndUpdate( bool abort ) {
#endif
// Force output to update the screen even if nothing changed...
// works only with Direct3D output (GFX_StartUpdate() was probably not even called)
if (render.forceUpdate) GFX_EndUpdate( 0 );
if (RENDER_GetForceUpdate()) GFX_EndUpdate( 0 );
}
render.frameskip.index = (render.frameskip.index + 1) & (RENDER_SKIP_CACHE - 1);
render.updating=false;
@ -666,6 +670,9 @@ forcenormal:
sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x;
LOG_MSG("Aspect ratio: %u x %u xToY=%.3f yToX=%.3f",sdl.srcAspect.x,sdl.srcAspect.y,sdl.srcAspect.xToY,sdl.srcAspect.yToX);
/* Setup the scaler variables */
#if C_OPENGL
GFX_SetShader(render.shader_src);
#endif
gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
if (gfx_flags & GFX_CAN_8)
render.scale.outMode = scalerMode8;
@ -846,10 +853,72 @@ static void ChangeScaler(bool pressed) {
void RENDER_UpdateFromScalerSetting(void);
bool RENDER_GetForceUpdate(void) {
return render.forceUpdate;
}
void RENDER_SetForceUpdate(bool f) {
render.forceUpdate = f;
}
#if C_OPENGL
static bool RENDER_GetShader(std::string& shader_path, char *old_src) {
char* src;
std::stringstream buf;
std::ifstream fshader(shader_path.c_str(), std::ios_base::binary);
if (!fshader.is_open()) fshader.open((shader_path + ".glsl").c_str(), std::ios_base::binary);
if (fshader.is_open()) {
buf << fshader.rdbuf();
fshader.close();
}
else if (shader_path == "advinterp2x") buf << advinterp2x_glsl;
else if (shader_path == "advinterp3x") buf << advinterp3x_glsl;
else if (shader_path == "advmame2x") buf << advmame2x_glsl;
else if (shader_path == "advmame3x") buf << advmame3x_glsl;
else if (shader_path == "rgb2x") buf << rgb2x_glsl;
else if (shader_path == "rgb3x") buf << rgb3x_glsl;
else if (shader_path == "scan2x") buf << scan2x_glsl;
else if (shader_path == "scan3x") buf << scan3x_glsl;
else if (shader_path == "tv2x") buf << tv2x_glsl;
else if (shader_path == "tv3x") buf << tv3x_glsl;
else if (shader_path == "sharp") buf << sharp_glsl;
if (!buf.str().empty()) {
std::string s = buf.str();
if (first_shell) {
std::string pre_defs;
Bitu count = first_shell->GetEnvCount();
for (Bitu i=0; i < count; i++) {
std::string env;
if (!first_shell->GetEnvNum(i, env))
continue;
if (env.compare(0, 9, "GLSHADER_")==0) {
size_t brk = env.find('=');
if (brk == std::string::npos) continue;
env[brk] = ' ';
pre_defs += "#define " + env.substr(9) + '\n';
}
}
if (!pre_defs.empty()) {
// if "#version" occurs it must be before anything except comments and whitespace
size_t pos = buf.str().find("#version ");
if (pos != std::string::npos) pos = buf.str().find('\n', pos+9);
if (pos == std::string::npos) pos = 0;
else ++pos;
s = buf.str().insert(pos, pre_defs);
}
}
// keep the same buffer if contents aren't different
if (old_src==NULL || s != old_src) {
src = strdup(s.c_str());
if (src==NULL) LOG_MSG("WARNING: Couldn't copy shader source");
} else src = old_src;
} else src = NULL;
render.shader_src = src;
return src != NULL;
}
#endif
void RENDER_UpdateFrameskipMenu(void) {
char tmp[64];
@ -1091,10 +1160,31 @@ void RENDER_Init() {
render.autofit=section->Get_bool("autofit");
#if C_OPENGL
char* shader_src = render.shader_src;
Prop_path *sh = section->Get_path("glshader");
std::string f = (std::string)sh->GetValue();
if (f.empty() || f=="none") render.shader_src = NULL;
else if (!RENDER_GetShader(sh->realpath,shader_src)) {
std::string path;
Cross::GetPlatformConfigDir(path);
path = path + "glshaders" + CROSS_FILESPLIT + f;
if (!RENDER_GetShader(path,shader_src) && (sh->realpath==f || !RENDER_GetShader(f,shader_src))) {
sh->SetValue("none");
LOG_MSG("Shader file \"%s\" not found", f.c_str());
}
} else
LOG_MSG("Loaded GLSL shader: %s\n", f.c_str());
if (shader_src!=render.shader_src) free(shader_src);
#endif
//If something changed that needs a ReInit
// Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet)
if(running && render.src.bpp && ((render.aspect != aspect) || (render.scale.op != scaleOp) ||
(render.scale.size != scalersize) || (render.scale.forced != scalerforced) ||
#if C_OPENGL
(render.shader_src != shader_src) ||
#endif
render.scale.forced))
RENDER_CallBack( GFX_CallBackReset );

576
src/gui/render_glsl.h Normal file
View File

@ -0,0 +1,576 @@
/*
* Copyright (C) 2002-2020 The DOSBox Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#if C_OPENGL
static const char advinterp2x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform sampler2D rubyTexture;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
""
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)*rubyInputSize;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
""
"vec3 getadvinterp2xtexel(vec2 coord) {\n"
" vec2 base = floor(coord/vec2(2.0))+vec2(0.5);\n"
" vec3 c4 = texture2D(rubyTexture, base/rubyTextureSize).xyz;\n"
" vec3 c1 = texture2D(rubyTexture, (base-vec2(0.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c7 = texture2D(rubyTexture, (base+vec2(0.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c3 = texture2D(rubyTexture, (base-vec2(1.0,0.0))/rubyTextureSize).xyz;\n"
" vec3 c5 = texture2D(rubyTexture, (base+vec2(1.0,0.0))/rubyTextureSize).xyz;\n"
""
" bool outer = c1 != c7 && c3 != c5;\n"
" bool c3c1 = outer && c3==c1;\n"
" bool c1c5 = outer && c1==c5;\n"
" bool c3c7 = outer && c3==c7;\n"
" bool c7c5 = outer && c7==c5;\n"
""
" vec3 l00 = mix(c3,c4,c3c1?3.0/8.0:1.0);\n"
" vec3 l01 = mix(c5,c4,c1c5?3.0/8.0:1.0);\n"
" vec3 l10 = mix(c3,c4,c3c7?3.0/8.0:1.0);\n"
" vec3 l11 = mix(c5,c4,c7c5?3.0/8.0:1.0);\n"
""
" coord = max(floor(mod(coord, 2.0)), 0.0);\n"
" /* 2x2 output:\n"
" * |x=0|x=1\n"
" * y=0|l00|l01\n"
" * y=1|l10|l11\n"
" */\n"
""
" return mix(mix(l00,l01,coord.x), mix(l10,l11,coord.x), coord.y);\n"
"}\n"
""
"void main()\n"
"{\n"
" vec2 coord = v_texCoord;\n"
"#if defined(OPENGLNB)\n"
" gl_FragColor = getadvinterp2xtexel(coord);\n"
"#else\n"
" coord -= 0.5;\n"
" vec3 c0 = getadvinterp2xtexel(coord);\n"
" vec3 c1 = getadvinterp2xtexel(coord+vec2(1.0,0.0));\n"
" vec3 c2 = getadvinterp2xtexel(coord+vec2(0.0,1.0));\n"
" vec3 c3 = getadvinterp2xtexel(coord+vec2(1.0));\n"
""
" coord = fract(max(coord,0.0));\n"
" gl_FragColor = vec4(mix(mix(c0,c1,coord.x), mix(c2,c3,coord.x), coord.y), 1.0);\n"
"#endif\n"
"}\n"
"#endif";
static const char advinterp3x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform sampler2D rubyTexture;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
""
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize*3.0;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
""
"vec3 getadvinterp3xtexel(vec2 coord) {\n"
" vec2 base = floor(coord/vec2(3.0))+vec2(0.5);\n"
" vec3 c0 = texture2D(rubyTexture, (base-vec2(1.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c1 = texture2D(rubyTexture, (base-vec2(0.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c2 = texture2D(rubyTexture, (base-vec2(-1.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c3 = texture2D(rubyTexture, (base-vec2(1.0,0.0))/rubyTextureSize).xyz;\n"
" vec3 c4 = texture2D(rubyTexture, base/rubyTextureSize).xyz;\n"
" vec3 c5 = texture2D(rubyTexture, (base+vec2(1.0,0.0))/rubyTextureSize).xyz;\n"
" vec3 c6 = texture2D(rubyTexture, (base+vec2(-1.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c7 = texture2D(rubyTexture, (base+vec2(0.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c8 = texture2D(rubyTexture, (base+vec2(1.0,1.0))/rubyTextureSize).xyz;\n"
""
" bool outer = c1 != c7 && c3 != c5;\n"
""
" vec3 l00 = mix(c3,c4,(outer && c3==c1) ? 3.0/8.0:1.0);\n"
" vec3 l01 = (outer && ((c3==c1&&c4!=c2)||(c5==c1&&c4!=c0))) ? c1 : c4;\n"
" vec3 l02 = mix(c5,c4,(outer && c5==c1) ? 3.0/8.0:1.0);\n"
" vec3 l10 = (outer && ((c3==c1&&c4!=c6)||(c3==c7&&c4!=c0))) ? c3 : c4;\n"
" vec3 l11 = c4;\n"
" vec3 l12 = (outer && ((c5==c1&&c4!=c8)||(c5==c7&&c4!=c2))) ? c5 : c4;\n"
" vec3 l20 = mix(c3,c4,(outer && c3==c7) ? 3.0/8.0:1.0);\n"
" vec3 l21 = (outer && ((c3==c7&&c4!=c8)||(c5==c7&&c4!=c6))) ? c7 : c4;\n"
" vec3 l22 = mix(c5,c4,(outer && c5==c7) ? 3.0/8.0:1.0);\n"
""
" coord = mod(coord, 3.0);\n"
" bvec2 l = lessThan(coord, vec2(1.0));\n"
" bvec2 h = greaterThanEqual(coord, vec2(2.0));\n"
""
" if (h.x) {\n"
" l01 = l02;\n"
" l11 = l12;\n"
" l21 = l22;\n"
" }\n"
" if (h.y) {\n"
" l10 = l20;\n"
" l11 = l21;\n"
" }\n"
" if (l.x) {\n"
" l01 = l00;\n"
" l11 = l10;\n"
" }\n"
" return l.y ? l01 : l11;\n"
"}\n"
""
"void main()\n"
"{\n"
" vec2 coord = v_texCoord;\n"
"#if defined(OPENGLNB)\n"
" gl_FragColor = getadvinterp3xtexel(coord);\n"
"#else\n"
" coord -= 0.5;\n"
" vec3 c0 = getadvinterp3xtexel(coord);\n"
" vec3 c1 = getadvinterp3xtexel(coord+vec2(1.0,0.0));\n"
" vec3 c2 = getadvinterp3xtexel(coord+vec2(0.0,1.0));\n"
" vec3 c3 = getadvinterp3xtexel(coord+vec2(1.0));\n"
""
" coord = fract(max(coord,0.0));\n"
" gl_FragColor = vec4(mix(mix(c0,c1,coord.x), mix(c2,c3,coord.x), coord.y), 1.0);\n"
"#endif\n"
"}\n"
"#endif";
static const char advmame2x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform sampler2D rubyTexture;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
""
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)*rubyInputSize;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
""
"vec3 getadvmame2xtexel(vec2 coord) {\n"
" vec2 base = floor(coord/vec2(2.0))+vec2(0.5);\n"
" vec3 c4 = texture2D(rubyTexture, base/rubyTextureSize).xyz;\n"
" vec3 c1 = texture2D(rubyTexture, (base-vec2(0.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c7 = texture2D(rubyTexture, (base+vec2(0.0,1.0))/rubyTextureSize).xyz;\n"
" vec3 c3 = texture2D(rubyTexture, (base-vec2(1.0,0.0))/rubyTextureSize).xyz;\n"
" vec3 c5 = texture2D(rubyTexture, (base+vec2(1.0,0.0))/rubyTextureSize).xyz;\n"
""
" bool outer = c1 != c7 && c3 != c5;\n"
" bool c3c1 = outer && c3==c1;\n"
" bool c1c5 = outer && c1==c5;\n"
" bool c3c7 = outer && c3==c7;\n"
" bool c7c5 = outer && c7==c5;\n"
""
" vec3 l00 = mix(c4,c3,c3c1?1.0:0.0);\n"
" vec3 l01 = mix(c4,c5,c1c5?1.0:0.0);\n"
" vec3 l10 = mix(c4,c3,c3c7?1.0:0.0);\n"
" vec3 l11 = mix(c4,c5,c7c5?1.0:0.0);\n"
""
" coord = max(floor(mod(coord, 2.0)), 0.0);\n"
" /* 2x2 output:\n"
" * |x=0|x=1\n"
" * y=0|l00|l01\n"
" * y=1|l10|l11\n"
" */\n"
""
" return mix(mix(l00,l01,coord.x), mix(l10,l11,coord.x), coord.y);\n"
"}\n"
""
"void main()\n"
"{\n"
" vec2 coord = v_texCoord;\n"
"#if defined(OPENGLNB)\n"
" gl_FragColor = getadvmame2xtexel(coord);\n"
"#else\n"
" coord -= 0.5;\n"
" vec3 c0 = getadvmame2xtexel(coord);\n"
" vec3 c1 = getadvmame2xtexel(coord+vec2(1.0,0.0));\n"
" vec3 c2 = getadvmame2xtexel(coord+vec2(0.0,1.0));\n"
" vec3 c3 = getadvmame2xtexel(coord+vec2(1.0));\n"
""
" coord = fract(max(coord,0.0));\n"
" gl_FragColor = vec4(mix(mix(c0,c1,coord.x), mix(c2,c3,coord.x), coord.y), 1.0);\n"
"#endif\n"
"}\n"
"#endif";
static const char advmame3x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform sampler2D rubyTexture;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
""
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize*3.0;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
""
"vec3 getadvmame3xtexel(vec2 coord) {\n"
" vec2 base = floor(coord/vec2(3.0))+vec2(0.5);\n"
" coord = mod(coord, 3.0);\n"
" bvec2 l = lessThan(coord, vec2(1.0));\n"
" bvec2 h = greaterThanEqual(coord, vec2(2.0));\n"
" bvec2 m = equal(l,h);\n"
""
" vec2 left = vec2(h.x?1.0:-1.0, 0.0);\n"
" vec2 up = vec2(0.0, h.y?1.0:-1.0);\n"
" if (l==h) left.x = 0.0; // hack for center pixel, will ensure outer==false\n"
" if (m.y) {\n"
" // swap\n"
" left -= up;\n"
" up += left;\n"
" left = up - left;\n"
" }\n"
""
" vec3 c0 = texture2D(rubyTexture, (base+up+left)/rubyTextureSize).xyz;\n"
" vec3 c1 = texture2D(rubyTexture, (base+up)/rubyTextureSize).xyz;\n"
" vec3 c2 = texture2D(rubyTexture, (base+up-left)/rubyTextureSize).xyz;\n"
" vec3 c3 = texture2D(rubyTexture, (base+left)/rubyTextureSize).xyz;\n"
" vec3 c4 = texture2D(rubyTexture, base/rubyTextureSize).xyz;\n"
" vec3 c5 = texture2D(rubyTexture, (base-left)/rubyTextureSize).xyz;\n"
" vec3 c7 = texture2D(rubyTexture, (base-up)/rubyTextureSize).xyz;\n"
""
" bool outer = c1 != c7 && c3 != c5;\n"
" bool check1 = c3==c1 && (!any(m) || c4!=c2);\n"
" bool check2 = any(m) && c5==c1 && c4!=c0;\n"
""
" return (outer && (check1 || check2)) ? c1 : c4;\n"
"}\n"
""
"void main()\n"
"{\n"
" vec2 coord = v_texCoord;\n"
"#if defined(OPENGLNB)\n"
" gl_FragColor = getadvmame3xtexel(coord);\n"
"#else\n"
" coord -= 0.5;\n"
" vec3 c0 = getadvmame3xtexel(coord);\n"
" vec3 c1 = getadvmame3xtexel(coord+vec2(1.0,0.0));\n"
" vec3 c2 = getadvmame3xtexel(coord+vec2(0.0,1.0));\n"
" vec3 c3 = getadvmame3xtexel(coord+vec2(1.0));\n"
""
" coord = fract(max(coord,0.0));\n"
" gl_FragColor = vec4(mix(mix(c0,c1,coord.x), mix(c2,c3,coord.x), coord.y), 1.0);\n"
"#endif\n"
"}\n"
"#endif";
static const char rgb2x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform sampler2D rubyTexture;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
""
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize * 2.0;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
""
"vec4 getRGB2xtexel(vec2 coord) {\n"
" vec2 mid = vec2(0.5);\n"
" vec4 s = texture2D(rubyTexture, (floor(coord/vec2(2.0))+mid)/rubyTextureSize);\n"
""
" coord = max(floor(mod(coord, 2.0)), 0.0);\n"
" /* 2x2 output:\n"
" * |x=0|x=1\n"
" * y=0| r | g\n"
" * y=1| b |rgb\n"
" */\n"
""
" s.r *= 1.0 - abs(coord.x - coord.y);\n"
" s.g *= coord.x;\n"
" s.b *= coord.y;\n"
" return s;\n"
"}\n"
""
"void main()\n"
"{\n"
" vec2 coord = v_texCoord;\n"
"#if defined(OPENGLNB)\n"
" gl_FragColor = getRGB2xtexel(coord);\n"
"#else\n"
" coord -= 0.5;\n"
" vec4 c0 = getRGB2xtexel(coord);\n"
" vec4 c1 = getRGB2xtexel(coord+vec2(1.0,0.0));\n"
" vec4 c2 = getRGB2xtexel(coord+vec2(0.0,1.0));\n"
" vec4 c3 = getRGB2xtexel(coord+vec2(1.0));\n"
""
" coord = fract(max(coord,0.0));\n"
" gl_FragColor = mix(mix(c0,c1,coord.x), mix(c2,c3,coord.x), coord.y);\n"
"#endif\n"
"}\n"
"#endif";
static const char rgb3x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform sampler2D rubyTexture;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
""
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize * 3.0;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
""
"vec4 getRGB3xtexel(vec2 coord) {\n"
" vec2 mid = vec2(0.5);\n"
" vec4 s = texture2D(rubyTexture, (floor(coord/vec2(3.0))+mid)/rubyTextureSize);\n"
""
" coord = max(floor(mod(coord, 3.0)), 0.0);\n"
" /* 3x3 output:\n"
" * | l | m | h\n"
" * l|rgb| g | b\n"
" * m| g | r |rgb\n"
" * h|rgb| b | r\n"
" */\n"
" vec2 l = step(0.0, -coord);\n"
" vec2 m = vec2(1.0) - abs(coord-1.0);\n"
" vec2 h = step(2.0, coord);\n"
""
" s.r *= l.x + m.y - 2.0*l.x*m.y + h.x*h.y;\n"
" s.g *= l.x + l.y*m.x + h.x*m.y;\n"
" s.b *= l.x*l.y + h.x + h.y - 2.0*h.x*h.y;\n"
" return s;\n"
"}\n"
""
"void main()\n"
"{\n"
" vec2 coord = v_texCoord;\n"
"#if defined(OPENGLNB)\n"
" gl_FragColor = getRGB3xtexel(coord);\n"
"#else\n"
" coord -= 0.5;\n"
" vec4 c0 = getRGB3xtexel(coord);\n"
" vec4 c1 = getRGB3xtexel(coord+vec2(1.0,0.0));\n"
" vec4 c2 = getRGB3xtexel(coord+vec2(0.0,1.0));\n"
" vec4 c3 = getRGB3xtexel(coord+vec2(1.0));\n"
""
" coord = fract(max(coord,0.0));\n"
" gl_FragColor = mix(mix(c0,c1,coord.x), mix(c2,c3,coord.x), coord.y);\n"
"#endif\n"
"}\n"
"#endif";
static const char scan2x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
"uniform sampler2D rubyTexture;\n"
""
"void main() {\n"
" vec2 prescale = vec2(2.0);\n"
" vec2 texel = v_texCoord;\n"
" vec2 texel_floored = floor(texel);\n"
" vec2 s = fract(texel);\n"
" vec2 region_range = vec2(0.5) - vec2(0.5) / prescale;\n"
""
" vec2 center_dist = s - vec2(0.5);\n"
" vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + vec2(0.5);\n"
""
" vec2 mod_texel = min(texel_floored + f, rubyInputSize-0.5);\n"
" vec4 p = texture2D(rubyTexture, mod_texel/rubyTextureSize);\n"
" float ss = abs(s.y*2.0-1.0);\n"
" p -= p*ss;\n"
""
" gl_FragColor = p;\n"
"}\n"
"#endif";
static const char scan3x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
"uniform sampler2D rubyTexture;\n"
""
"void main() {\n"
" vec2 prescale = vec2(3.0);\n"
" vec2 texel = v_texCoord;\n"
" vec2 texel_floored = floor(texel);\n"
" vec2 s = fract(texel);\n"
" vec2 region_range = vec2(0.5) - vec2(0.5) / prescale;\n"
""
" vec2 center_dist = s - 0.5;\n"
" vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + vec2(0.5);\n"
""
" vec2 mod_texel = min(texel_floored + f, rubyInputSize-0.5);\n"
" vec4 p = texture2D(rubyTexture, mod_texel/rubyTextureSize);\n"
" float m = s.y*6.0;\n"
" m -= clamp(m, 2.0, 4.0);\n"
" m = abs(m/2.0);\n"
" gl_FragColor = p - p*m;\n"
"}\n"
"#endif";
static const char tv2x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
"uniform sampler2D rubyTexture;\n"
""
"void main() {\n"
" vec2 prescale = vec2(2.0);\n"
" vec2 texel = v_texCoord;\n"
" vec2 texel_floored = floor(texel);\n"
" vec2 s = fract(texel);\n"
" vec2 region_range = vec2(0.5) - vec2(0.5) / prescale;\n"
""
" vec2 center_dist = s - 0.5;\n"
" vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + vec2(0.5);\n"
""
" vec2 mod_texel = min(texel_floored + f, rubyInputSize-0.5);\n"
" vec4 p = texture2D(rubyTexture, mod_texel/rubyTextureSize);\n"
" float ss = abs(s.y*2.0-1.0);\n"
" p -= p*ss*3.0/8.0;\n"
""
" gl_FragColor = p;\n"
"}\n"
"#endif";
static const char tv3x_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize;\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
"uniform sampler2D rubyTexture;\n"
""
"void main() {\n"
" vec2 prescale = vec2(3.0);\n"
" vec2 texel = v_texCoord;\n"
" vec2 texel_floored = floor(texel);\n"
" vec2 s = fract(texel);\n"
" vec2 region_range = vec2(0.5) - vec2(0.5) / prescale;\n"
""
" vec2 center_dist = s - 0.5;\n"
" vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + vec2(0.5);\n"
""
" vec2 mod_texel = min(texel_floored + f, rubyInputSize-0.5);\n"
" vec4 p = texture2D(rubyTexture, mod_texel/rubyTextureSize);\n"
" float ss = abs(s.y*2.0-1.0);\n"
" p -= p*ss*11.0/16.0;\n"
""
" gl_FragColor = p;\n"
"}\n"
"#endif";
static const char sharp_glsl[] =
"varying vec2 v_texCoord;\n"
"uniform vec2 rubyInputSize;\n"
"uniform vec2 rubyOutputSize;\n"
"uniform vec2 rubyTextureSize;\n"
"varying vec2 prescale; // const set by vertex shader\n"
""
"#if defined(VERTEX)\n"
"attribute vec4 a_position;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize;\n"
" prescale = ceil(rubyOutputSize / rubyInputSize);\n"
"}\n"
""
"#elif defined(FRAGMENT)\n"
"uniform sampler2D rubyTexture;\n"
""
"void main() {\n"
" const vec2 halfp = vec2(0.5);\n"
" vec2 texel_floored = floor(v_texCoord);\n"
" vec2 s = fract(v_texCoord);\n"
" vec2 region_range = halfp - halfp / prescale;\n"
""
" vec2 center_dist = s - halfp;\n"
" vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + halfp;\n"
""
" vec2 mod_texel = min(texel_floored + f, rubyInputSize-halfp);\n"
" gl_FragColor = texture2D(rubyTexture, mod_texel / rubyTextureSize);\n"
"}\n"
"#endif";
#endif

View File

@ -174,6 +174,48 @@ typedef enum PROCESS_DPI_AWARENESS {
#include "sdlmain.h"
#include "build_timestamp.h"
namespace gl2 {
extern PFNGLATTACHSHADERPROC glAttachShader;
extern PFNGLCOMPILESHADERPROC glCompileShader;
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
extern PFNGLCREATESHADERPROC glCreateShader;
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
extern PFNGLDELETESHADERPROC glDeleteShader;
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
extern PFNGLGETSHADERIVPROC glGetShaderiv;
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
extern PFNGLLINKPROGRAMPROC glLinkProgram;
extern PFNGLSHADERSOURCEPROC_NP glShaderSource;
extern PFNGLUNIFORM2FPROC glUniform2f;
extern PFNGLUNIFORM1IPROC glUniform1i;
extern PFNGLUSEPROGRAMPROC glUseProgram;
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
}
#define glAttachShader gl2::glAttachShader
#define glCompileShader gl2::glCompileShader
#define glCreateProgram gl2::glCreateProgram
#define glCreateShader gl2::glCreateShader
#define glDeleteProgram gl2::glDeleteProgram
#define glDeleteShader gl2::glDeleteShader
#define glEnableVertexAttribArray gl2::glEnableVertexAttribArray
#define glGetAttribLocation gl2::glGetAttribLocation
#define glGetProgramiv gl2::glGetProgramiv
#define glGetProgramInfoLog gl2::glGetProgramInfoLog
#define glGetShaderiv gl2::glGetShaderiv
#define glGetShaderInfoLog gl2::glGetShaderInfoLog
#define glGetUniformLocation gl2::glGetUniformLocation
#define glLinkProgram gl2::glLinkProgram
#define glShaderSource gl2::glShaderSource
#define glUniform2f gl2::glUniform2f
#define glUniform1i gl2::glUniform1i
#define glUseProgram gl2::glUseProgram
#define glVertexAttribPointer gl2::glVertexAttribPointer
#ifdef MACOSX
extern bool has_touch_bar_support;
bool osx_detect_nstouchbar(void);
@ -2618,6 +2660,19 @@ void DoExtendedKeyboardHook(bool enable) {
#endif
}
void GFX_SetShader(const char* src) {
#if C_OPENGL
if (!sdl_opengl.use_shader || src == sdl_opengl.shader_src)
return;
sdl_opengl.shader_src = src;
if (sdl_opengl.program_object) {
glDeleteProgram(sdl_opengl.program_object);
sdl_opengl.program_object = 0;
}
#endif
}
void GFX_ReleaseMouse(void) {
if (sdl.mouse.locked)
GFX_CaptureMouse();
@ -3262,9 +3317,9 @@ void GFX_EndUpdate(const Bit16u *changedLines) {
if (d3d && d3d->getForceUpdate());
else
#endif
if (!sdl.updating)
if (((sdl.desktop.type != SCREEN_OPENGL) || !RENDER_GetForceUpdate()) && !sdl.updating)
return;
bool actually_updating = sdl.updating;
sdl.updating = false;
switch (sdl.desktop.type)
{
@ -3274,6 +3329,17 @@ void GFX_EndUpdate(const Bit16u *changedLines) {
#if C_OPENGL
case SCREEN_OPENGL:
// Clear drawing area. Some drivers (on Linux) have more than 2 buffers and the screen might
// be dirty because of other programs.
if (!actually_updating) {
/* Don't really update; Just increase the frame counter.
* If we tried to update it may have not worked so well
* with VSync...
* (Think of 60Hz on the host with 70Hz on the client.)
*/
sdl_opengl.actual_frame_count++;
return;
}
OUTPUT_OPENGL_EndUpdate(changedLines);
break;
#endif
@ -3834,8 +3900,77 @@ static void GUI_StartUp() {
sdl.overscan_width=(unsigned int)section->Get_int("overscan");
// sdl.overscan_color=section->Get_int("overscancolor");
#if C_OPENGL
if (sdl.desktop.want_type == SCREEN_OPENGL) { /* OPENGL is requested */
#if defined(C_SDL2)
GFX_SetResizeable(true);
if (!(sdl.window = GFX_SetSDLWindowMode(640,400, SCREEN_OPENGL)) || !(sdl_opengl.context = SDL_GL_CreateContext(sdl.window))) {
#else
sdl.surface = SDL_SetVideoMode(640,400,0,SDL_OPENGL);
if (sdl.surface == NULL) {
#endif
LOG_MSG("Could not initialize OpenGL, switching back to surface");
sdl.desktop.want_type = SCREEN_SURFACE;
} else {
sdl_opengl.program_object = 0;
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram");
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray");
glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)SDL_GL_GetProcAddress("glGetAttribLocation");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation");
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
glShaderSource = (PFNGLSHADERSOURCEPROC_NP)SDL_GL_GetProcAddress("glShaderSource");
glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f");
glUniform1i = (PFNGLUNIFORM1IPROC)SDL_GL_GetProcAddress("glUniform1i");
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)SDL_GL_GetProcAddress("glVertexAttribPointer");
sdl_opengl.use_shader = (glAttachShader && glCompileShader && glCreateProgram && glDeleteProgram && glDeleteShader && \
glEnableVertexAttribArray && glGetAttribLocation && glGetProgramiv && glGetProgramInfoLog && \
glGetShaderiv && glGetShaderInfoLog && glGetUniformLocation && glLinkProgram && glShaderSource && \
glUniform2f && glUniform1i && glUseProgram && glVertexAttribPointer);
sdl_opengl.buffer=0;
sdl_opengl.framebuf=0;
sdl_opengl.texture=0;
sdl_opengl.displaylist=0;
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &sdl_opengl.max_texsize);
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)SDL_GL_GetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC)SDL_GL_GetProcAddress("glBindBufferARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)SDL_GL_GetProcAddress("glDeleteBuffersARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC)SDL_GL_GetProcAddress("glBufferDataARB");
glMapBufferARB = (PFNGLMAPBUFFERARBPROC)SDL_GL_GetProcAddress("glMapBufferARB");
glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)SDL_GL_GetProcAddress("glUnmapBufferARB");
const char * gl_ext = (const char *)glGetString (GL_EXTENSIONS);
if(gl_ext && *gl_ext){
sdl_opengl.packed_pixel=(strstr(gl_ext,"EXT_packed_pixels") != NULL);
sdl_opengl.paletted_texture=(strstr(gl_ext,"EXT_paletted_texture") != NULL);
sdl_opengl.pixel_buffer_object=(strstr(gl_ext,"GL_ARB_pixel_buffer_object") != NULL ) &&
glGenBuffersARB && glBindBufferARB && glDeleteBuffersARB && glBufferDataARB &&
glMapBufferARB && glUnmapBufferARB;
} else {
sdl_opengl.packed_pixel = false;
sdl_opengl.paletted_texture = false;
sdl_opengl.pixel_buffer_object = false;
}
#ifdef DB_DISABLE_DBO
sdl_opengl.pixel_buffer_object = false;
#endif
LOG_MSG("OpenGL extension: pixel_bufer_object %d",sdl_opengl.pixel_buffer_object);
}
} /* OPENGL is requested end */
#endif //OPENGL
/* Initialize screen for first time */
#if defined(C_SDL2)
/* Initialize screen for first time */
GFX_SetResizeable(true);
if (!GFX_SetSDLSurfaceWindow(640,400))
E_Exit("Could not initialize video: %s",SDL_GetError());
@ -3847,7 +3982,6 @@ static void GUI_StartUp() {
if (SDL_BITSPERPIXEL(sdl.desktop.pixelFormat) == 24)
LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!");
#else
/* Initialize screen for first time */
sdl.surface=SDL_SetVideoMode(640,400,0,SDL_RESIZABLE);
if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError());
sdl.deferred_resize = false;

View File

@ -14,6 +14,7 @@
#include <algorithm>
#include "sdlmain.h"
#include "render.h"
using namespace std;
@ -27,6 +28,56 @@ PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL;
PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL;
PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL;
/* Apple defines these functions in their GL header (as core functions)
* so we can't use their names as function pointers. We can't link
* directly as some platforms may not have them. So they get their own
* namespace here to keep the official names but avoid collisions.
*/
namespace gl2 {
PFNGLATTACHSHADERPROC glAttachShader = NULL;
PFNGLCOMPILESHADERPROC glCompileShader = NULL;
PFNGLCREATEPROGRAMPROC glCreateProgram = NULL;
PFNGLCREATESHADERPROC glCreateShader = NULL;
PFNGLDELETEPROGRAMPROC glDeleteProgram = NULL;
PFNGLDELETESHADERPROC glDeleteShader = NULL;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = NULL;
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = NULL;
PFNGLGETPROGRAMIVPROC glGetProgramiv = NULL;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = NULL;
PFNGLGETSHADERIVPROC glGetShaderiv = NULL;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = NULL;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL;
PFNGLLINKPROGRAMPROC glLinkProgram = NULL;
PFNGLSHADERSOURCEPROC_NP glShaderSource = NULL;
PFNGLUNIFORM2FPROC glUniform2f = NULL;
PFNGLUNIFORM1IPROC glUniform1i = NULL;
PFNGLUSEPROGRAMPROC glUseProgram = NULL;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL;
}
/* "using" is meant to hide identical names declared in outer scope
* but is unreliable, so just redefine instead.
*/
#define glAttachShader gl2::glAttachShader
#define glCompileShader gl2::glCompileShader
#define glCreateProgram gl2::glCreateProgram
#define glCreateShader gl2::glCreateShader
#define glDeleteProgram gl2::glDeleteProgram
#define glDeleteShader gl2::glDeleteShader
#define glEnableVertexAttribArray gl2::glEnableVertexAttribArray
#define glGetAttribLocation gl2::glGetAttribLocation
#define glGetProgramiv gl2::glGetProgramiv
#define glGetProgramInfoLog gl2::glGetProgramInfoLog
#define glGetShaderiv gl2::glGetShaderiv
#define glGetShaderInfoLog gl2::glGetShaderInfoLog
#define glGetUniformLocation gl2::glGetUniformLocation
#define glLinkProgram gl2::glLinkProgram
#define glShaderSource gl2::glShaderSource
#define glUniform2f gl2::glUniform2f
#define glUniform1i gl2::glUniform1i
#define glUseProgram gl2::glUseProgram
#define glVertexAttribPointer gl2::glVertexAttribPointer
#if C_OPENGL && DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
extern unsigned int SDLDrawGenFontTextureUnitPerRow;
extern unsigned int SDLDrawGenFontTextureRows;
@ -221,6 +272,76 @@ Bitu OUTPUT_OPENGL_GetBestMode(Bitu flags)
return flags;
}
/* Create a GLSL shader object, load the shader source, and compile the shader. */
static GLuint BuildShader ( GLenum type, const char *shaderSrc ) {
GLuint shader;
GLint compiled;
const char* src_strings[2];
std::string top;
// look for "#version" because it has to occur first
const char *ver = strstr(shaderSrc, "#version ");
if (ver) {
const char *endline = strchr(ver+9, '\n');
if (endline) {
top.assign(shaderSrc, endline-shaderSrc+1);
shaderSrc = endline+1;
}
}
top += (type==GL_VERTEX_SHADER) ? "#define VERTEX 1\n":"#define FRAGMENT 1\n";
if (!sdl_opengl.bilinear)
top += "#define OPENGLNB 1\n";
src_strings[0] = top.c_str();
src_strings[1] = shaderSrc;
// Create the shader object
shader = glCreateShader(type);
if (shader == 0) return 0;
// Load the shader source
glShaderSource(shader, 2, src_strings, NULL);
// Compile the shader
glCompileShader(shader);
// Check the compile status
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
char* infoLog = NULL;
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen>1) infoLog = (char*)malloc(infoLen);
if (infoLog) {
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
LOG_MSG("Error compiling shader: %s", infoLog);
free(infoLog);
} else LOG_MSG("Error getting shader compilation log");
glDeleteShader(shader);
return 0;
}
return shader;
}
static bool LoadGLShaders(const char *src, GLuint *vertex, GLuint *fragment) {
GLuint s = BuildShader(GL_VERTEX_SHADER, src);
if (s) {
*vertex = s;
s = BuildShader(GL_FRAGMENT_SHADER, src);
if (s) {
*fragment = s;
return true;
}
glDeleteShader(*vertex);
}
return false;
}
Bitu OUTPUT_OPENGL_SetSize()
{
Bitu retFlags = 0;
@ -256,10 +377,11 @@ Bitu OUTPUT_OPENGL_SetSize()
# endif
#endif
// try 32 bits first then 16
#if defined(C_SDL2)
SetupSurfaceScaledOpenGL(SDL_WINDOW_RESIZABLE, 0);
if (SetupSurfaceScaledOpenGL(SDL_WINDOW_RESIZABLE,32)==NULL) SetupSurfaceScaledOpenGL(SDL_WINDOW_RESIZABLE,16);
#else
SetupSurfaceScaledOpenGL(SDL_RESIZABLE, 0);
if (SetupSurfaceScaledOpenGL(SDL_RESIZABLE,32)==NULL) SetupSurfaceScaledOpenGL(SDL_RESIZABLE,16);
#endif
if (!sdl.surface || sdl.surface->format->BitsPerPixel < 15)
{
@ -290,6 +412,100 @@ Bitu OUTPUT_OPENGL_SetSize()
return 0;
}
if (sdl_opengl.use_shader) {
GLuint prog=0;
// reset error
glGetError();
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&prog);
// if there was an error this context doesn't support shaders
if (glGetError()==GL_NO_ERROR && (sdl_opengl.program_object==0 || prog!=sdl_opengl.program_object)) {
// check if existing program is valid
if (sdl_opengl.program_object) {
glUseProgram(sdl_opengl.program_object);
if (glGetError() != GL_NO_ERROR) {
// program is not usable (probably new context), purge it
glDeleteProgram(sdl_opengl.program_object);
sdl_opengl.program_object = 0;
}
}
// does program need to be rebuilt?
if (sdl_opengl.program_object == 0) {
GLuint vertexShader, fragmentShader;
const char *src = sdl_opengl.shader_src;
if (src && !LoadGLShaders(src, &vertexShader, &fragmentShader)) {
LOG_MSG("SDL:OPENGL:Failed to compile shader, falling back to default");
src = NULL;
}
if (src == NULL && !LoadGLShaders(shader_src_default, &vertexShader, &fragmentShader)) {
LOG_MSG("SDL:OPENGL:Failed to compile default shader!");
return 0;
}
sdl_opengl.program_object = glCreateProgram();
if (!sdl_opengl.program_object) {
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
LOG_MSG("SDL:OPENGL:Can't create program object, falling back to surface");
return 0;
}
glAttachShader(sdl_opengl.program_object, vertexShader);
glAttachShader(sdl_opengl.program_object, fragmentShader);
// Link the program
glLinkProgram(sdl_opengl.program_object);
// Even if we *are* successful, we may delete the shader objects
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// Check the link status
GLint isProgramLinked;
glGetProgramiv(sdl_opengl.program_object, GL_LINK_STATUS, &isProgramLinked);
if (!isProgramLinked) {
char * infoLog = NULL;
GLint infoLen = 0;
glGetProgramiv(sdl_opengl.program_object, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen>1) infoLog = (char*)malloc(infoLen);
if (infoLog) {
glGetProgramInfoLog(sdl_opengl.program_object, infoLen, NULL, infoLog);
LOG_MSG("SDL:OPENGL:Error linking program:\n %s", infoLog);
free(infoLog);
} else LOG_MSG("SDL:OPENGL:Failed to retrieve program link log");
glDeleteProgram(sdl_opengl.program_object);
sdl_opengl.program_object = 0;
return 0;
}
glUseProgram(sdl_opengl.program_object);
GLint u = glGetAttribLocation(sdl_opengl.program_object, "a_position");
// upper left
sdl_opengl.vertex_data[0] = -1.0f;
sdl_opengl.vertex_data[1] = 1.0f;
// lower left
sdl_opengl.vertex_data[2] = -1.0f;
sdl_opengl.vertex_data[3] = -3.0f;
// upper right
sdl_opengl.vertex_data[4] = 3.0f;
sdl_opengl.vertex_data[5] = 1.0f;
// Load the vertex positions
glVertexAttribPointer(u, 2, GL_FLOAT, GL_FALSE, 0, sdl_opengl.vertex_data);
glEnableVertexAttribArray(u);
u = glGetUniformLocation(sdl_opengl.program_object, "rubyTexture");
glUniform1i(u, 0);
sdl_opengl.ruby.texture_size = glGetUniformLocation(sdl_opengl.program_object, "rubyTextureSize");
sdl_opengl.ruby.input_size = glGetUniformLocation(sdl_opengl.program_object, "rubyInputSize");
sdl_opengl.ruby.output_size = glGetUniformLocation(sdl_opengl.program_object, "rubyOutputSize");
sdl_opengl.ruby.frame_count = glGetUniformLocation(sdl_opengl.program_object, "rubyFrameCount");
// Don't force updating unless a shader depends on frame_count
RENDER_SetForceUpdate(sdl_opengl.ruby.frame_count != (GLint)-1);
}
}
}
/* Create the texture and display list */
if (sdl_opengl.pixel_buffer_object)
{
@ -341,7 +557,7 @@ Bitu OUTPUT_OPENGL_SetSize()
glClear(GL_COLOR_BUFFER_BIT);
// SDL_GL_SwapBuffers();
// glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_FLAT);
//glShadeModel(GL_FLAT);
glBlendFunc(GL_ONE, GL_ZERO);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
@ -353,6 +569,14 @@ Bitu OUTPUT_OPENGL_SetSize()
glDisable(GL_STENCIL_TEST);
glEnable(GL_TEXTURE_2D);
if (sdl_opengl.program_object) {
// Set shader variables
glUniform2f(sdl_opengl.ruby.texture_size, (float)texsize, (float)texsize);
glUniform2f(sdl_opengl.ruby.input_size, (float)adjTexWidth, (float)adjTexHeight);
glUniform2f(sdl_opengl.ruby.output_size, sdl.clip.w, sdl.clip.h);
// The following uniform is *not* set right now
sdl_opengl.actual_frame_count = 0;
} else {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@ -383,6 +607,7 @@ Bitu OUTPUT_OPENGL_SetSize()
glEndList();
glBindTexture(GL_TEXTURE_2D, 0);
}
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
void GFX_DrawSDLMenu(DOSBoxMenu &menu, DOSBoxMenu::displaylist &dl);
@ -591,8 +816,8 @@ void OUTPUT_OPENGL_EndUpdate(const Bit16u *changedLines)
#endif
(void*)0);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
glCallList(sdl_opengl.displaylist);
SDL_GL_SwapBuffers();
//glCallList(sdl_opengl.displaylist);
//SDL_GL_SwapBuffers();
}
else if (changedLines)
{
@ -625,6 +850,12 @@ void OUTPUT_OPENGL_EndUpdate(const Bit16u *changedLines)
}
index++;
}
} else
return;
if (sdl_opengl.program_object) {
glUniform1i(sdl_opengl.ruby.frame_count, sdl_opengl.actual_frame_count++);
glDrawArrays(GL_TRIANGLES, 0, 3);
} else
glCallList(sdl_opengl.displaylist);
#if 0 /* DEBUG Prove to me that you're drawing the damn texture */
@ -659,7 +890,6 @@ void OUTPUT_OPENGL_EndUpdate(const Bit16u *changedLines)
#endif
SDL_GL_SwapBuffers();
}
if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++;
}

View File

@ -38,6 +38,32 @@ extern PFNGLBUFFERDATAARBPROC glBufferDataARB;
extern PFNGLMAPBUFFERARBPROC glMapBufferARB;
extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB;
/* Don't guard these with GL_VERSION_2_0 - Apple defines it but not these typedefs.
* If they're already defined they should match these definitions, so no conflicts.
*/
typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
//Change to NP, as Khronos changes include guard :(
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC_NP) (GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);
typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
#if defined(C_SDL2)
# include <SDL_video.h>
#endif
@ -56,11 +82,39 @@ struct SDL_OpenGL {
bool pixel_buffer_object;
int menudraw_countdown;
int clear_countdown;
bool use_shader;
GLuint program_object;
const char *shader_src;
struct {
GLint texture_size;
GLint input_size;
GLint output_size;
GLint frame_count;
} ruby;
GLuint actual_frame_count;
GLfloat vertex_data[2*3];
#if defined(C_SDL2)
SDL_GLContext context;
#endif
};
static char const shader_src_default[] =
"varying vec2 v_texCoord;\n"
"#if defined(VERTEX)\n"
"uniform vec2 rubyTextureSize;\n"
"uniform vec2 rubyInputSize;\n"
"attribute vec4 a_position;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize/rubyTextureSize;\n"
"}\n"
"#elif defined(FRAGMENT)\n"
"uniform sampler2D rubyTexture;\n\n"
"void main() {\n"
" gl_FragColor = texture2D(rubyTexture, v_texCoord);\n"
"}\n"
"#endif\n";
extern SDL_OpenGL sdl_opengl;
// output API