diff --git a/include/render.h b/include/render.h index 291ef418..f81851e7 100644 --- a/include/render.h +++ b/include/render.h @@ -80,11 +80,15 @@ typedef struct { Bit8u *cacheRead; Bitu inHeight, inLine, outLine; } scale; +#if C_OPENGL + char* shader_src; +#endif RenderPal_t pal; bool updating; bool active; bool aspect; bool fullFrame; + bool forceUpdate; } Render_t; extern Render_t render; @@ -93,6 +97,8 @@ void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool 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 diff --git a/include/video.h b/include/video.h index 3696ced7..9461330a 100644 --- a/include/video.h +++ b/include/video.h @@ -59,6 +59,7 @@ void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries); 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 cb); +void GFX_SetShader(const char* src); void GFX_ResetScreen(void); void GFX_Start(void); diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 1e48915c..8ae1e4df 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -472,6 +472,14 @@ void DOSBOX_Init(void) { const char* force[] = { "", "forced", 0 }; 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\n" + "of the DOSBox configuration directory, or one of the built-in shaders:\n" + "advinterp2x, advinterp3x, advmame2x, advmame3x, rgb2x, rgb3x, scan2x,\n" + "scan3x, tv2x, tv3x, sharp."); +#endif secprop=control->AddSection_prop("cpu",&CPU_Init,true);//done const char* cores[] = { "auto", diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index 3fed5e68..84b22b14 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -7,5 +7,5 @@ libgui_a_SOURCES = sdlmain.cpp sdl_mapper.cpp dosbox_logo.h \ render_templates_sai.h render_templates_hq.h \ render_templates_hq2x.h render_templates_hq3x.h \ midi.cpp midi_win32.h midi_oss.h midi_coreaudio.h midi_alsa.h \ - midi_coremidi.h sdl_gui.cpp dosbox_splash.h + midi_coremidi.h sdl_gui.cpp dosbox_splash.h render_glsl.h diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 39596c70..fad9648b 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "dosbox.h" #include "video.h" @@ -30,8 +32,10 @@ #include "cross.h" #include "hardware.h" #include "support.h" +#include "shell.h" #include "render_scalers.h" +#include "render_glsl.h" Render_t render; ScalerLineHandler_t RENDER_DrawLine; @@ -229,6 +233,7 @@ void RENDER_EndUpdate( bool abort ) { total += render.frameskip.hadSkip[i]; LOG_MSG( "Skipped frame %d %d", PIC_Ticks, (total * 100) / RENDER_SKIP_CACHE ); #endif + if (RENDER_GetForceUpdate()) GFX_EndUpdate(0); } render.frameskip.index = (render.frameskip.index + 1) & (RENDER_SKIP_CACHE - 1); render.updating=false; @@ -428,6 +433,9 @@ forcenormal: } } /* 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; @@ -572,6 +580,76 @@ static void ChangeScaler(bool pressed) { RENDER_CallBack( GFX_CallBackReset ); } */ +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* 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 = s.find('='); + if (brk == std::string::npos) continue; + env[brk] = ' '; + pre_defs += "#define " + env.substr(0) + '\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 (render.shader_src==NULL || s != render.shader_src) { + src = strdup(s.c_str()); + if (src==NULL) LOG_MSG("WARNING: Couldn't copy shader source"); + } else { + src = render.shader_src; + render.shader_src = NULL; + } + } else src = NULL; + free(render.shader_src); + render.shader_src = src; + return src != NULL; +} +#endif + void RENDER_Init(Section * sec) { Section_prop * section=static_cast(sec); @@ -625,10 +703,31 @@ void RENDER_Init(Section * sec) { else if (scaler == "scan3x"){ render.scale.op = scalerOpScan;render.scale.size = 3; } #endif +#if C_OPENGL + char* shader_src = render.shader_src; + Prop_path *sh = section->Get_path("glshader"); + f = (std::string)sh->GetValue(); + if (f.empty() || f=="none") { + free(render.shader_src); + render.shader_src = NULL; + } else if (!RENDER_GetShader(sh->realpath)) { + std::string path; + Cross::GetPlatformConfigDir(path); + path = path + "glshaders" + CROSS_FILESPLIT + f; + if (!RENDER_GetShader(path) && (sh->realpath==f || !RENDER_GetShader(f))) { + sh->SetValue("none"); + LOG_MSG("Shader file \"%s\" not found", f.c_str()); + } + } +#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 ); diff --git a/src/gui/render_glsl.h b/src/gui/render_glsl.h new file mode 100644 index 00000000..7d253a33 --- /dev/null +++ b/src/gui/render_glsl.h @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2002-2019 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 \ No newline at end of file diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 83d2fe40..66fd8c60 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -49,6 +49,7 @@ #include "cpu.h" #include "cross.h" #include "control.h" +#include "render.h" #define MAPPERFILE "mapper-" VERSION ".map" //#define DISABLE_JOYSTICK @@ -92,6 +93,53 @@ PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +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); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*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); +#endif + +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 glShaderSource = NULL; +PFNGLUNIFORM2FPROC glUniform2f = NULL; +PFNGLUNIFORM1IPROC glUniform1i = NULL; +PFNGLUSEPROGRAMPROC glUseProgram = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL; + +#ifndef GL_SHADER_COMPILER +#define GL_SHADER_COMPILER 0x8DFA +#endif + #endif //C_OPENGL #if !(ENVIRON_INCLUDED) @@ -190,6 +238,18 @@ struct SDL_Block { bool packed_pixel; bool paletted_texture; bool pixel_buffer_object; + + 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]; } opengl; #endif struct { @@ -229,6 +289,23 @@ struct SDL_Block { static SDL_Block sdl; #if C_OPENGL +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"; + #ifdef DB_OPENGL_ERROR void OPENGL_ERROR(const char* message) { GLenum r = glGetError(); @@ -583,6 +660,76 @@ void GFX_TearDown(void) { } } +#if C_OPENGL +/* 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) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + + if (infoLen>1) { + char* infoLog = (char*)malloc(infoLen); + glGetShaderInfoLog(shader, infoLen, NULL, infoLog); + LOG_MSG("Error compiling shader: %s", infoLog); + free(infoLog); + } + + glDeleteShader(shader); + return 0; + } + + return shader; +} +static bool GFX_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; +} +#endif + Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) { if (sdl.updating) GFX_EndUpdate( 0 ); @@ -760,11 +907,106 @@ dosurface: #if SDL_VERSION_ATLEAST(1, 2, 11) SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 ); #endif - GFX_SetupSurfaceScaled(SDL_OPENGL,0); + // try 32 bits first then 16 + if (GFX_SetupSurfaceScaled(SDL_OPENGL,32)==NULL) GFX_SetupSurfaceScaled(SDL_OPENGL,16); if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) { LOG_MSG("SDL:OPENGL: Can't open drawing surface, are you running in 16bpp (or higher) mode?"); goto dosurface; } + + if (sdl.opengl.use_shader) { + GLboolean t; + // confirm current context supports shaders + glGetBooleanv(GL_SHADER_COMPILER, &t); + if (t) { + // check if existing program is valid + if (sdl.opengl.program_object) { + // reset error + glGetError(); + 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 && !GFX_LoadGLShaders(src, &vertexShader, &fragmentShader)) { + LOG_MSG("SDL:OPENGL:Failed to compile shader, falling back to default"); + src = NULL; + } + if (src == NULL && !GFX_LoadGLShaders(shader_src_default, &vertexShader, &fragmentShader)) { + LOG_MSG("SDL:OPENGL:Failed to compile default shader!"); + goto dosurface; + } + + 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"); + goto dosurface; + } + 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) { + GLint infoLen = 0; + + glGetProgramiv(sdl.opengl.program_object, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen>1) { + char *infoLog = (char*)malloc(infoLen); + glGetProgramInfoLog(sdl.opengl.program_object, infoLen, NULL, infoLog); + LOG_MSG("SDL:OPENGL:Error link prograram:\n %s", infoLog); + free(infoLog); + } + + glDeleteProgram(sdl.opengl.program_object); + sdl.opengl.program_object = 0; + goto dosurface; + } + + 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) { glGenBuffersARB(1, &sdl.opengl.buffer); @@ -781,9 +1023,7 @@ dosurface: glViewport((sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); } else { glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h); - } - - glMatrixMode (GL_PROJECTION); + } if (sdl.opengl.texture > 0) { glDeleteTextures(1,&sdl.opengl.texture); @@ -810,32 +1050,42 @@ dosurface: glClear(GL_COLOR_BUFFER_BIT); SDL_GL_SwapBuffers(); glClear(GL_COLOR_BUFFER_BIT); - glShadeModel (GL_FLAT); glDisable (GL_DEPTH_TEST); glDisable (GL_LIGHTING); glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); - glMatrixMode (GL_MODELVIEW); - glLoadIdentity (); - GLfloat tex_width=((GLfloat)(width)/(GLfloat)texsize); - GLfloat tex_height=((GLfloat)(height)/(GLfloat)texsize); + 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)width, (float)height); + 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 { + GLfloat tex_width=((GLfloat)(width)/(GLfloat)texsize); + GLfloat tex_height=((GLfloat)(height)/(GLfloat)texsize); - if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1); - sdl.opengl.displaylist = glGenLists(1); - glNewList(sdl.opengl.displaylist, GL_COMPILE); - glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + glShadeModel(GL_FLAT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - glBegin(GL_TRIANGLES); - // upper left - glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f); - // lower left - glTexCoord2f(0,tex_height*2); glVertex2f(-1.0f,-3.0f); - // upper right - glTexCoord2f(tex_width*2,0); glVertex2f(3.0f, 1.0f); - glEnd(); + if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1); + sdl.opengl.displaylist = glGenLists(1); + glNewList(sdl.opengl.displaylist, GL_COMPILE); + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); - glEndList(); + glBegin(GL_TRIANGLES); + // upper left + glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f); + // lower left + glTexCoord2f(0,tex_height*2); glVertex2f(-1.0f,-3.0f); + // upper right + glTexCoord2f(tex_width*2,0); glVertex2f(3.0f, 1.0f); + glEnd(); + + glEndList(); + } OPENGL_ERROR("End of setsize"); @@ -843,7 +1093,7 @@ dosurface: retFlags = GFX_CAN_32 | GFX_SCALING; if (sdl.opengl.pixel_buffer_object) retFlags |= GFX_HARDWARE; - break; + break; }//OPENGL #endif //C_OPENGL default: @@ -856,6 +1106,19 @@ dosurface: return retFlags; } +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_CaptureMouse(void) { sdl.mouse.locked=!sdl.mouse.locked; if (sdl.mouse.locked) { @@ -1019,8 +1282,9 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { #if C_DDRAW int ret; #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) { case SCREEN_SURFACE: @@ -1085,6 +1349,15 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { 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; + } glClearColor (0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); if (sdl.opengl.pixel_buffer_object) { @@ -1094,8 +1367,6 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { sdl.draw.width, sdl.draw.height, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, 0); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); - glCallList(sdl.opengl.displaylist); - SDL_GL_SwapBuffers(); } else if (changedLines) { Bitu y = 0, index = 0; glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); @@ -1112,9 +1383,14 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { } index++; } - glCallList(sdl.opengl.displaylist); - SDL_GL_SwapBuffers(); - } + } 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); + SDL_GL_SwapBuffers(); break; #endif default: @@ -1438,6 +1714,31 @@ static void GUI_StartUp(Section * sec) { 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)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;