LCOV - code coverage report
Current view: top level - engine/render - shader.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 22.4 % 1164 261
Test Date: 2025-02-21 06:59:27 Functions: 43.6 % 94 41

            Line data    Source code
       1              : // shader.cpp: OpenGL GLSL shader management
       2              : 
       3              : #include "../libprimis-headers/cube.h"
       4              : #include "../../shared/geomexts.h"
       5              : #include "../../shared/glemu.h"
       6              : #include "../../shared/glexts.h"
       7              : #include "../../shared/stream.h"
       8              : 
       9              : #include "octarender.h"
      10              : #include "postfx.h"
      11              : #include "rendergl.h"
      12              : #include "renderlights.h"
      13              : #include "rendermodel.h"
      14              : #include "rendertimers.h"
      15              : #include "renderwindow.h"
      16              : #include "shaderparam.h"
      17              : #include "shader.h"
      18              : #include "texture.h"
      19              : 
      20              : #include "interface/console.h"
      21              : #include "interface/control.h"
      22              : #include "interface/menus.h"
      23              : 
      24              : Shader *Shader::lastshader = nullptr;
      25              : 
      26              : Shader *nullshader            = nullptr,
      27              :        *hudshader             = nullptr,
      28              :        *hudtextshader         = nullptr,
      29              :        *hudnotextureshader    = nullptr,
      30              :        *nocolorshader         = nullptr,
      31              :        *foggedshader          = nullptr,
      32              :        *foggednotextureshader = nullptr,
      33              :        *ldrshader             = nullptr,
      34              :        *ldrnotextureshader    = nullptr,
      35              :        *stdworldshader        = nullptr;
      36              : 
      37              : static std::unordered_map<std::string, int> localparams;
      38              : static std::unordered_map<std::string, Shader> shaders;
      39              : static Shader *slotshader = nullptr;
      40              : static std::vector<SlotShaderParam> slotparams;
      41              : static bool standardshaders = false,
      42              :             forceshaders = true,
      43              :             loadedshaders = false;
      44              : constexpr int maxvariantrows = 32;
      45              : 
      46              : VAR(maxvsuniforms, 1, 0, 0);
      47              : VAR(maxfsuniforms, 1, 0, 0);
      48              : VAR(mintexoffset, 1, 0, 0);
      49              : VAR(maxtexoffset, 1, 0, 0);
      50              : VAR(debugshader, 0, 1, 2);
      51              : 
      52            0 : void loadshaders()
      53              : {
      54            0 :     standardshaders = true;
      55            0 :     execfile("config/glsl.cfg");
      56            0 :     standardshaders = false;
      57              : 
      58            0 :     nullshader = lookupshaderbyname("null");
      59            0 :     hudshader = lookupshaderbyname("hud");
      60            0 :     hudtextshader = lookupshaderbyname("hudtext");
      61            0 :     hudnotextureshader = lookupshaderbyname("hudnotexture");
      62            0 :     stdworldshader = lookupshaderbyname("stdworld");
      63            0 :     if(!nullshader || !hudshader || !hudtextshader || !hudnotextureshader || !stdworldshader)
      64              :     {
      65            0 :         fatal("cannot find shader definitions");
      66              :     }
      67            0 :     dummyslot.shader = stdworldshader;
      68            0 :     dummydecalslot.shader = nullshader;
      69              : 
      70            0 :     nocolorshader = lookupshaderbyname("nocolor");
      71            0 :     foggedshader = lookupshaderbyname("fogged");
      72            0 :     foggednotextureshader = lookupshaderbyname("foggednotexture");
      73            0 :     ldrshader = lookupshaderbyname("ldr");
      74            0 :     ldrnotextureshader = lookupshaderbyname("ldrnotexture");
      75              : 
      76            0 :     nullshader->set();
      77              : 
      78            0 :     loadedshaders = true;
      79            0 : }
      80              : 
      81            5 : Shader *lookupshaderbyname(std::string_view name)
      82              : {
      83            5 :     auto itr = shaders.find(name.data());
      84            5 :     if(itr != shaders.end())
      85              :     {
      86            5 :         return (*itr).second.loaded() ? &(*itr).second : nullptr;
      87              :     }
      88            0 :     return nullptr;
      89              : }
      90              : 
      91            0 : Shader *generateshader(std::string_view name, const char *fmt, ...)
      92              : {
      93            0 :     if(!loadedshaders)
      94              :     {
      95            0 :         return nullptr;
      96              :     }
      97            0 :     Shader *s = name.size() ? lookupshaderbyname(name) : nullptr;
      98            0 :     if(!s)
      99              :     {
     100            0 :         DEFV_FORMAT_STRING(cmd, fmt, fmt);
     101            0 :         bool wasstandard = standardshaders;
     102            0 :         standardshaders = true;
     103            0 :         execute(cmd);
     104            0 :         standardshaders = wasstandard;
     105            0 :         s = name.size() ? lookupshaderbyname(name) : nullptr;
     106            0 :         if(!s)
     107              :         {
     108            0 :             s = nullshader;
     109              :         }
     110              :     }
     111            0 :     return s;
     112              : }
     113              : 
     114            0 : static void showglslinfo(GLenum type, GLuint obj, const char *name, const char **parts = nullptr, int numparts = 0)
     115              : {
     116            0 :     GLint length = 0;
     117            0 :     if(type)
     118              :     {
     119            0 :         glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &length);
     120              :     }
     121              :     else
     122              :     {
     123            0 :         glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &length);
     124              :     }
     125            0 :     if(length > 1)
     126              :     {
     127            0 :         conoutf(Console_Error, "GLSL ERROR (%s:%s)", type == GL_VERTEX_SHADER ? "Vertex shader" : (type == GL_FRAGMENT_SHADER ? "Fragment shader" : "Program"), name);
     128            0 :         FILE *l = getlogfile();
     129            0 :         if(l)
     130              :         {
     131            0 :             GLchar *log = new GLchar[length];
     132            0 :             if(type)
     133              :             {
     134            0 :                 glGetShaderInfoLog(obj, length, &length, log);
     135              :             }
     136              :             else
     137              :             {
     138            0 :                 glGetProgramInfoLog(obj, length, &length, log);
     139              :             }
     140            0 :             std::fprintf(l, "%s\n", log);
     141            0 :             bool partlines = log[0] != '0';
     142            0 :             int line = 0;
     143            0 :             for(int i = 0; i < numparts; ++i)
     144              :             {
     145            0 :                 const char *part = parts[i];
     146            0 :                 int startline = line;
     147            0 :                 while(*part)
     148              :                 {
     149            0 :                     const char *next = std::strchr(part, '\n');
     150            0 :                     if(++line > 1000)
     151              :                     {
     152            0 :                         goto done;
     153              :                     }
     154            0 :                     if(partlines)
     155              :                     {
     156            0 :                         std::fprintf(l, "%d(%d): ", i, line - startline);
     157              :                     }
     158              :                     else
     159              :                     {
     160            0 :                         std::fprintf(l, "%d: ", line);
     161              :                     }
     162            0 :                     std::fwrite(part, 1, next ? next - part + 1 : std::strlen(part), l);
     163            0 :                     if(!next)
     164              :                     {
     165            0 :                         std::fputc('\n', l);
     166            0 :                         break;
     167              :                     }
     168            0 :                     part = next + 1;
     169              :                 }
     170              :             }
     171            0 :         done:
     172            0 :             delete[] log;
     173              :         }
     174              :     }
     175            0 : }
     176              : 
     177            4 : static void compileglslshader(GLenum type, GLuint &obj, const char *def, const char *name, bool msg = true)
     178              : {
     179            4 :     if(!glslversion)
     180              :     {
     181            4 :         conoutf(Console_Error, "Cannot compile GLSL shader without GLSL initialized");
     182            4 :         return;
     183              :     }
     184            0 :     const char *source = def + std::strspn(def, " \t\r\n");
     185            0 :     char *modsource = nullptr;
     186              :     const char *parts[16];
     187            0 :     int numparts = 0;
     188              :     static const struct { int version; const char * const header; } glslversions[] =
     189              :     {
     190              :         { 400, "#version 400\n" }, //OpenGL 4.0
     191              :         { 330, "#version 330\n" }, //OpenGL 3.3 (not supported)
     192              :         { 150, "#version 150\n" }, //OpenGL 3.2 (not supported)
     193              :         { 140, "#version 140\n" }, //OpenGL 3.1 (not supported)
     194              :         { 130, "#version 130\n" }, //OpenGL 3.0 (not supported)
     195              :         { 120, "#version 120\n" }  //OpenGL 2.1 (not supported)
     196              :     };
     197            0 :     for(int i = 0; i < static_cast<int>(sizeof(glslversions)/sizeof(glslversions[0])); ++i)
     198              :     {
     199            0 :         if(glslversion >= glslversions[i].version)
     200              :         {
     201            0 :             parts[numparts++] = glslversions[i].header;
     202            0 :             break;
     203              :         }
     204              :     }
     205              : 
     206            0 :         parts[numparts++] = "#extension GL_ARB_explicit_attrib_location : enable\n";
     207              :     //glsl 1.5
     208            0 :     if(type == GL_VERTEX_SHADER) parts[numparts++] =
     209              :         "#define attribute in\n"
     210              :         "#define varying out\n";
     211            0 :     else if(type == GL_FRAGMENT_SHADER)
     212              :     {
     213            0 :         parts[numparts++] = "#define varying in\n";
     214            0 :         parts[numparts++] =
     215              :             "#define fragdata(loc) layout(location = loc) out\n"
     216              :             "#define fragblend(loc) layout(location = loc, index = 1) out\n";
     217              :     }
     218            0 :     parts[numparts++] =
     219              :         "#define texture1D(sampler, coords) texture(sampler, coords)\n"
     220              :         "#define texture2D(sampler, coords) texture(sampler, coords)\n"
     221              :         "#define texture2DOffset(sampler, coords, offset) textureOffset(sampler, coords, offset)\n"
     222              :         "#define texture2DProj(sampler, coords) textureProj(sampler, coords)\n"
     223              :         "#define shadow2D(sampler, coords) texture(sampler, coords)\n"
     224              :         "#define shadow2DOffset(sampler, coords, offset) textureOffset(sampler, coords, offset)\n"
     225              :         "#define texture3D(sampler, coords) texture(sampler, coords)\n"
     226              :         "#define textureCube(sampler, coords) texture(sampler, coords)\n";
     227              :     //glsl 1.4
     228            0 :     parts[numparts++] =
     229              :         "#define texture2DRect(sampler, coords) texture(sampler, coords)\n"
     230              :         "#define texture2DRectProj(sampler, coords) textureProj(sampler, coords)\n"
     231              :         "#define shadow2DRect(sampler, coords) texture(sampler, coords)\n";
     232            0 :     parts[numparts++] =
     233              :         "#define texture2DRectOffset(sampler, coords, offset) textureOffset(sampler, coords, offset)\n"
     234              :         "#define shadow2DRectOffset(sampler, coords, offset) textureOffset(sampler, coords, offset)\n";
     235            0 :     parts[numparts++] = modsource ? modsource : source;
     236              :     //end glsl 1.4
     237            0 :     obj = glCreateShader(type);
     238            0 :     glShaderSource(obj, numparts, static_cast<const GLchar **>(parts), nullptr);
     239            0 :     glCompileShader(obj);
     240              :     GLint success;
     241            0 :     glGetShaderiv(obj, GL_COMPILE_STATUS, &success);
     242            0 :     if(!success)
     243              :     {
     244            0 :         if(msg)
     245              :         {
     246            0 :             showglslinfo(type, obj, name, parts, numparts);
     247              :         }
     248            0 :         glDeleteShader(obj);
     249            0 :         obj = 0;
     250              :     }
     251            0 :     else if(debugshader > 1 && msg)
     252              :     {
     253            0 :         showglslinfo(type, obj, name, parts, numparts);
     254              :     }
     255            0 :     if(modsource)
     256              :     {
     257            0 :         delete[] modsource;
     258              :     }
     259              : }
     260              : 
     261            0 : static void bindglsluniform(const Shader &s, UniformLoc &u)
     262              : {
     263            0 :     static VAR(debugubo, 0, 0, 1); //print out to console information about ubos when bindglsluniform is called
     264              : 
     265            0 :     u.loc = glGetUniformLocation(s.program, u.name);
     266            0 :     if(!u.blockname)
     267              :     {
     268            0 :         return;
     269              :     }
     270            0 :     GLuint bidx = glGetUniformBlockIndex(s.program, u.blockname),
     271            0 :            uidx = GL_INVALID_INDEX;
     272            0 :     glGetUniformIndices(s.program, 1, &u.name, &uidx);
     273            0 :     if(bidx != GL_INVALID_INDEX && uidx != GL_INVALID_INDEX)
     274              :     {
     275            0 :         GLint sizeval   = 0,
     276            0 :               offsetval = 0,
     277            0 :               strideval = 0;
     278            0 :         glGetActiveUniformBlockiv(s.program, bidx, GL_UNIFORM_BLOCK_DATA_SIZE, &sizeval);
     279            0 :         if(sizeval <= 0)
     280              :         {
     281            0 :             return;
     282              :         }
     283            0 :         glGetActiveUniformsiv(s.program, 1, &uidx, GL_UNIFORM_OFFSET, &offsetval);
     284            0 :         if(u.stride > 0)
     285              :         {
     286            0 :             glGetActiveUniformsiv(s.program, 1, &uidx, GL_UNIFORM_ARRAY_STRIDE, &strideval);
     287            0 :             if(strideval > u.stride)
     288              :             {
     289            0 :                 return;
     290              :             }
     291              :         }
     292            0 :         u.offset = offsetval;
     293            0 :         u.size = sizeval;
     294            0 :         glUniformBlockBinding(s.program, bidx, u.binding);
     295            0 :         if(debugubo)
     296              :         {
     297            0 :             conoutf(Console_Debug, "UBO: %s:%s:%d, offset: %d, size: %d, stride: %d", u.name, u.blockname, u.binding, offsetval, sizeval, strideval);
     298              :         }
     299              :     }
     300              : }
     301              : 
     302            0 : void Shader::uniformtex(std::string_view name, int tmu) const
     303              : {
     304            0 :     int loc = glGetUniformLocation(program, name.data());
     305            0 :     if(loc != -1)
     306              :     {
     307            0 :         glUniform1i(loc, tmu);
     308              :     }
     309            0 : }
     310              : 
     311            2 : void Shader::linkglslprogram(bool msg)
     312              : {
     313            2 :     program = vsobj && psobj ? glCreateProgram() : 0;
     314            2 :     GLint success = 0;
     315            2 :     if(program)
     316              :     {
     317            0 :         glAttachShader(program, vsobj);
     318            0 :         glAttachShader(program, psobj);
     319            0 :         uint attribs = 0;
     320            0 :         for(const Shader::AttribLoc &a : attriblocs)
     321              :         {
     322            0 :             glBindAttribLocation(program, a.loc, a.name);
     323            0 :             attribs |= 1<<a.loc;
     324              :         }
     325            0 :         for(int i = 0; i < gle::Attribute_NumAttributes; ++i)
     326              :         {
     327            0 :             if(!(attribs&(1<<i)))
     328              :             {
     329            0 :                 glBindAttribLocation(program, i, gle::attribnames[i]);
     330              :             }
     331              :         }
     332            0 :         glLinkProgram(program);
     333            0 :         glGetProgramiv(program, GL_LINK_STATUS, &success);
     334              :     }
     335            2 :     if(success)
     336              :     {
     337            0 :         glUseProgram(program);
     338            0 :         static std::array<std::string, 16> texnames = { "tex0", "tex1", "tex2", "tex3", "tex4", "tex5", "tex6", "tex7", "tex8", "tex9", "tex10", "tex11", "tex12", "tex13", "tex14", "tex15" };
     339            0 :         for(int i = 0; i < 16; ++i)
     340              :         {
     341            0 :             GLint loc = glGetUniformLocation(program, texnames[i].c_str());
     342            0 :             if(loc != -1)
     343              :             {
     344            0 :                 glUniform1i(loc, i);
     345              :             }
     346              :         }
     347            0 :         if(type & Shader_World)
     348              :         {
     349            0 :             uniformtex("diffusemap", Tex_Diffuse);
     350            0 :             uniformtex("normalmap", Tex_Normal);
     351            0 :             uniformtex("glowmap", Tex_Glow);
     352            0 :             uniformtex("blendmap", 7);
     353            0 :             uniformtex("refractmask", 7);
     354            0 :             uniformtex("refractlight", 8);
     355              :         }
     356            0 :         for(SlotShaderParamState &param : defaultparams)
     357              :         {
     358            0 :             param.loc = glGetUniformLocation(program, param.name.c_str());
     359              :         }
     360            0 :         for(UniformLoc &loc : uniformlocs)
     361              :         {
     362            0 :             bindglsluniform(*this, loc);
     363              :         }
     364            0 :         glUseProgram(0);
     365              :     }
     366            2 :     else if(program)
     367              :     {
     368            0 :         if(msg)
     369              :         {
     370            0 :             showglslinfo(GL_FALSE, program, name);
     371              :         }
     372            0 :         glDeleteProgram(program);
     373            0 :         program = 0;
     374              :     }
     375            2 : }
     376              : 
     377            0 : size_t getlocalparam(const std::string &name)
     378              : {
     379            0 :     auto itr = localparams.find(name);
     380            0 :     if(itr != localparams.end())
     381              :     {
     382            0 :         return (*itr).second;
     383              :     }
     384            0 :     size_t size = localparams.size();
     385            0 :     localparams.insert( { name, size } );
     386            0 :     return size;
     387              : }
     388              : 
     389            0 : static int addlocalparam(Shader &s, const char *name, GLint loc, GLsizei size, GLenum format)
     390              : {
     391            0 :     size_t idx = getlocalparam(name);
     392            0 :     if(idx >= s.localparamremap.size())
     393              :     {
     394            0 :         int n = idx + 1 - s.localparamremap.size();
     395            0 :         for(int i = 0; i < n; ++i)
     396              :         {
     397            0 :             s.localparamremap.push_back(0xFF);
     398              :         }
     399              :     }
     400            0 :     s.localparamremap[idx] = s.localparams.size();
     401            0 :     LocalShaderParamState l;
     402            0 :     l.name = name;
     403            0 :     l.loc = loc;
     404            0 :     l.size = size;
     405            0 :     l.format = format;
     406            0 :     s.localparams.push_back(l);
     407            0 :     return idx;
     408            0 : }
     409              : 
     410            0 : static void addglobalparam(Shader &s, const GlobalShaderParamState *param, GLint loc, GLsizei size, GLenum format)
     411              : {
     412            0 :     s.globalparams.emplace_back(loc, size, format, param, -2);
     413            0 : }
     414              : 
     415            0 : void Shader::setglsluniformformat(const char *name, GLenum format, GLsizei size)
     416              : {
     417            0 :     switch(format)
     418              :     {
     419            0 :         case GL_FLOAT:
     420              :         case GL_FLOAT_VEC2:
     421              :         case GL_FLOAT_VEC3:
     422              :         case GL_FLOAT_VEC4:
     423              :         case GL_INT:
     424              :         case GL_INT_VEC2:
     425              :         case GL_INT_VEC3:
     426              :         case GL_INT_VEC4:
     427              :         case GL_UNSIGNED_INT:
     428              :         case GL_UNSIGNED_INT_VEC2:
     429              :         case GL_UNSIGNED_INT_VEC3:
     430              :         case GL_UNSIGNED_INT_VEC4:
     431              :         case GL_BOOL:
     432              :         case GL_BOOL_VEC2:
     433              :         case GL_BOOL_VEC3:
     434              :         case GL_BOOL_VEC4:
     435              :         case GL_FLOAT_MAT2:
     436              :         case GL_FLOAT_MAT3:
     437              :         case GL_FLOAT_MAT4:
     438              :         {
     439            0 :             break;
     440              :         }
     441            0 :         default:
     442              :         {
     443            0 :             return;
     444              :         }
     445              :     }
     446            0 :     if(!std::strncmp(name, "gl_", 3))
     447              :     {
     448            0 :         return;
     449              :     }
     450            0 :     GLint loc = glGetUniformLocation(program, name);
     451            0 :     if(loc < 0)
     452              :     {
     453            0 :         return;
     454              :     }
     455            0 :     for(uint j = 0; j < defaultparams.size(); j++)
     456              :     {
     457            0 :         if(defaultparams[j].loc == loc)
     458              :         {
     459            0 :             defaultparams[j].format = format;
     460            0 :             return;
     461              :         }
     462              :     }
     463            0 :     for(uint j = 0; j < uniformlocs.size(); j++)
     464              :     {
     465            0 :         if(uniformlocs[j].loc == loc)
     466              :         {
     467            0 :             return;
     468              :         }
     469              :     }
     470            0 :     for(uint j = 0; j < globalparams.size(); j++)
     471              :     {
     472            0 :         if(globalparams[j].loc == loc)
     473              :         {
     474            0 :             return;
     475              :         }
     476              :     }
     477            0 :     for(uint j = 0; j < localparams.size(); j++)
     478              :     {
     479            0 :         if(localparams[j].loc == loc)
     480              :         {
     481            0 :             return;
     482              :         }
     483              :     }
     484              : 
     485            0 :     name = getshaderparamname(name);
     486              :     //must explicitly enumerate scope because globalparams is a field & gvar :(
     487            0 :     auto itr = ::globalparams.find(name);
     488            0 :     if(itr != ::globalparams.end())
     489              :     {
     490            0 :         addglobalparam(*this, &((*itr).second), loc, size, format);
     491              :     }
     492              :     else
     493              :     {
     494            0 :         addlocalparam(*this, name, loc, size, format);
     495              :     }
     496              : }
     497              : 
     498            0 : void Shader::allocglslactiveuniforms()
     499              : {
     500            0 :     GLint numactive = 0;
     501            0 :     glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numactive);
     502              :     string name;
     503            0 :     for(int i = 0; i < numactive; ++i)
     504              :     {
     505            0 :         GLsizei namelen = 0;
     506            0 :         GLint size = 0;
     507            0 :         GLenum format = GL_FLOAT_VEC4;
     508            0 :         name[0] = '\0';
     509            0 :         glGetActiveUniform(program, i, sizeof(name)-1, &namelen, &size, &format, name);
     510            0 :         if(namelen <= 0 || size <= 0)
     511              :         {
     512            0 :             continue;
     513              :         }
     514            0 :         name[std::clamp(static_cast<int>(namelen), 0, static_cast<int>(sizeof(name))-2)] = '\0';
     515            0 :         char *brak = std::strchr(name, '[');
     516            0 :         if(brak)
     517              :         {
     518            0 :             *brak = '\0';
     519              :         }
     520            0 :         setglsluniformformat(name, format, size);
     521              :     }
     522            0 : }
     523              : 
     524              : int GlobalShaderParamState::nextversion = 0;
     525              : 
     526            0 : void GlobalShaderParamState::resetversions()
     527              : {
     528            0 :     for(auto &[k, s] : shaders)
     529              :     {
     530            0 :         for(GlobalShaderParamUse &u : s.globalparams)
     531              :         {
     532            0 :             if(u.version != u.param->version)
     533              :             {
     534            0 :                 u.version = -2;
     535              :             }
     536              :         }
     537              :     }
     538            0 :     nextversion = 0;
     539            0 :     for(auto& [k, g] : globalparams)
     540              :     {
     541            0 :         g.version = ++nextversion;
     542              :     }
     543            0 :     for(auto &[k, s] : shaders)
     544              :     {
     545            0 :         for(GlobalShaderParamUse &u : s.globalparams)
     546              :         {
     547            0 :             if(u.version >= 0)
     548              :             {
     549            0 :                 u.version = u.param->version;
     550              :             }
     551              :         }
     552              :     }
     553            0 : }
     554              : 
     555            0 : static const float *findslotparam(const Slot &s, const char *name, const float *noval = nullptr)
     556              : {
     557            0 :     for(const SlotShaderParam &param : s.params)
     558              :     {
     559            0 :         if(name == param.name)
     560              :         {
     561            0 :             return &param.val[0];
     562              :         }
     563              :     }
     564            0 :     for(const SlotShaderParamState &param : s.shader->defaultparams)
     565              :     {
     566            0 :         if(name == param.name)
     567              :         {
     568            0 :             return &param.val[0];
     569              :         }
     570              :     }
     571            0 :     return noval;
     572              : }
     573              : 
     574            0 : static const float *findslotparam(const VSlot &s, const char *name, const float *noval = nullptr)
     575              : {
     576            0 :     for(const SlotShaderParam &param : s.params)
     577              :     {
     578            0 :         if(name == param.name)
     579              :         {
     580            0 :             return &param.val[0];
     581              :         }
     582              :     }
     583            0 :     return findslotparam(*s.slot, name, noval);
     584              : }
     585              : 
     586            0 : static void setslotparam(const SlotShaderParamState &l, const float *val)
     587              : {
     588            0 :     switch(l.format)
     589              :     {
     590            0 :         case GL_BOOL:
     591              :         case GL_FLOAT:
     592              :         {
     593            0 :             glUniform1fv(l.loc, 1, val);
     594            0 :             break;
     595              :         }
     596            0 :         case GL_BOOL_VEC2:
     597              :         case GL_FLOAT_VEC2:
     598              :         {
     599            0 :             glUniform2fv(l.loc, 1, val);
     600            0 :             break;
     601              :         }
     602            0 :         case GL_BOOL_VEC3:
     603              :         case GL_FLOAT_VEC3:
     604              :         {
     605            0 :             glUniform3fv(l.loc, 1, val);
     606            0 :             break;
     607              :         }
     608            0 :         case GL_BOOL_VEC4:
     609              :         case GL_FLOAT_VEC4:
     610              :         {
     611            0 :             glUniform4fv(l.loc, 1, val);
     612            0 :             break;
     613              :         }
     614            0 :         case GL_INT:
     615              :         {
     616            0 :             glUniform1i(l.loc, static_cast<int>(val[0]));
     617            0 :             break;
     618              :         }
     619            0 :         case GL_INT_VEC2:
     620              :         {
     621            0 :             glUniform2i(l.loc, static_cast<int>(val[0]), static_cast<int>(val[1]));
     622            0 :             break;
     623              :         }
     624            0 :         case GL_INT_VEC3:
     625              :         {
     626            0 :             glUniform3i(l.loc, static_cast<int>(val[0]), static_cast<int>(val[1]), static_cast<int>(val[2]));
     627            0 :             break;
     628              :         }
     629            0 :         case GL_INT_VEC4:
     630              :         {
     631            0 :             glUniform4i(l.loc, static_cast<int>(val[0]), static_cast<int>(val[1]), static_cast<int>(val[2]), static_cast<int>(val[3]));
     632            0 :             break;
     633              :         }
     634            0 :         case GL_UNSIGNED_INT:
     635              :         {
     636            0 :             glUniform1ui(l.loc, static_cast<uint>(val[0]));
     637            0 :             break;
     638              :         }
     639            0 :         case GL_UNSIGNED_INT_VEC2:
     640              :         {
     641            0 :             glUniform2ui(l.loc, static_cast<uint>(val[0]), static_cast<uint>(val[1]));
     642            0 :             break;
     643              :         }
     644            0 :         case GL_UNSIGNED_INT_VEC3:
     645              :         {
     646            0 :             glUniform3ui(l.loc, static_cast<uint>(val[0]), static_cast<uint>(val[1]), static_cast<uint>(val[2]));
     647            0 :             break;
     648              :         }
     649            0 :         case GL_UNSIGNED_INT_VEC4:
     650              :         {
     651            0 :             glUniform4ui(l.loc, static_cast<uint>(val[0]), static_cast<uint>(val[1]), static_cast<uint>(val[2]), static_cast<uint>(val[3]));
     652            0 :             break;
     653              :         }
     654              :     }
     655            0 : }
     656              : 
     657            0 : static void setslotparam(const SlotShaderParamState& l, uint& mask, uint i, const float* val)
     658              : {
     659            0 :     if(!(mask&(1<<i)))
     660              :     {
     661            0 :         mask |= 1<<i;
     662            0 :         setslotparam(l, val);
     663              :     }
     664            0 : }
     665              : 
     666            0 : static void setslotparams(uint& unimask, const std::vector<SlotShaderParamState>& defaultparams)
     667              : {
     668            0 :     for(const SlotShaderParam &p : slotparams)
     669              :     {
     670            0 :         if(!(defaultparams.size() > p.loc))
     671              :         {
     672            0 :             continue;
     673              :         }
     674            0 :         const SlotShaderParamState &l = defaultparams.at(p.loc);
     675            0 :         setslotparam(l, unimask, p.loc, p.val);
     676              :     }
     677            0 : }
     678              : 
     679            0 : static void setdefaultparams(const std::vector<SlotShaderParamState>& defaultparams, uint& unimask)
     680              : {
     681            0 :     for(uint i = 0; i < defaultparams.size(); i++)
     682              :     {
     683            0 :         const SlotShaderParamState &l = defaultparams.at(i);
     684            0 :         setslotparam(l, unimask, i, &l.val[0]);
     685              :     }
     686            0 : }
     687              : 
     688              : //shader
     689              : 
     690            1 : Shader::Shader() : name(nullptr), defer(nullptr), type(Shader_Default), program(0), variantshader(nullptr), standard(false), forced(false), owner(nullptr), vsstr(nullptr), psstr(nullptr), vsobj(0), psobj(0), reusevs(nullptr), reuseps(nullptr), variantrows(nullptr), used(false)
     691              : {
     692            1 : }
     693              : 
     694            3 : Shader::~Shader()
     695              : {
     696            3 :     delete[] name;
     697            3 :     delete[] vsstr;
     698            3 :     delete[] psstr;
     699            3 :     delete[] defer;
     700            3 :     delete[] variantrows;
     701            3 : }
     702              : 
     703            0 : void Shader::flushparams()
     704              : {
     705            0 :     if(!used)
     706              :     {
     707            0 :         allocglslactiveuniforms();
     708            0 :         used = true;
     709              :     }
     710            0 :     for(GlobalShaderParamUse &i : globalparams)
     711              :     {
     712            0 :         i.flush();
     713              :     }
     714            0 : }
     715              : 
     716            0 : bool Shader::invalid() const
     717              : {
     718            0 :     return (type & Shader_Invalid) != 0;
     719              : }
     720            0 : bool Shader::deferred() const
     721              : {
     722            0 :     return (type & Shader_Deferred) != 0;
     723              : }
     724            5 : bool Shader::loaded() const
     725              : {
     726            5 :     return !(type&(Shader_Deferred | Shader_Invalid));
     727              : }
     728              : 
     729            0 : bool Shader::isdynamic() const
     730              : {
     731            0 :     return (type & Shader_Dynamic) != 0;
     732              : }
     733              : 
     734            0 : int Shader::numvariants(int row) const
     735              : {
     736            0 :     if(row < 0 || row >= maxvariantrows || !variantrows)
     737              :     {
     738            0 :         return 0;
     739              :     }
     740            0 :     return variantrows[row+1] - variantrows[row];
     741              : }
     742              : 
     743            0 : const Shader *Shader::getvariant(int col, int row) const
     744              : {
     745            0 :     if(row < 0 || row >= maxvariantrows || col < 0 || !variantrows)
     746              :     {
     747            0 :         return nullptr;
     748              :     }
     749            0 :     int start = variantrows[row],
     750            0 :         end = variantrows[row+1];
     751            0 :     return col < end - start ? variants[start + col] : nullptr;
     752              : }
     753              : 
     754            0 : void Shader::addvariant(int row, Shader *s)
     755              : {
     756            0 :     if(row < 0 || row >= maxvariantrows || variants.size() >= USHRT_MAX)
     757              :     {
     758            0 :         return;
     759              :     }
     760            0 :     if(!variantrows)
     761              :     {
     762            0 :         variantrows = new ushort[maxvariantrows+1];
     763            0 :         std::memset(variantrows, 0, (maxvariantrows+1)*sizeof(ushort));
     764              :     }
     765            0 :     variants.insert(variants.begin() + variantrows[row+1], s);
     766            0 :     for(int i = row+1; i <= maxvariantrows; ++i)
     767              :     {
     768            0 :         ++variantrows[i];
     769              :     }
     770              : }
     771              : 
     772            0 : void Shader::setvariant_(int col, int row)
     773              : {
     774            0 :     Shader *s = this;
     775            0 :     if(variantrows)
     776              :     {
     777            0 :         int start = variantrows[row],
     778            0 :             end   = variantrows[row+1];
     779            0 :         for(col = std::min(start + col, end-1); col >= start; --col)
     780              :         {
     781            0 :             if(!variants[col]->invalid())
     782              :             {
     783            0 :                 s = variants[col];
     784            0 :                 break;
     785              :             }
     786              :         }
     787              :     }
     788            0 :     if(lastshader!=s)
     789              :     {
     790            0 :         s->bindprograms();
     791              :     }
     792            0 : }
     793              : 
     794            0 : void Shader::setvariant(int col, int row)
     795              : {
     796            0 :     if(!loaded())
     797              :     {
     798            0 :         return;
     799              :     }
     800            0 :     setvariant_(col, row);
     801            0 :     lastshader->flushparams();
     802              : }
     803              : 
     804            0 : void Shader::setvariantandslot(int col, int row)
     805              : {
     806            0 :     if(!loaded())
     807              :     {
     808            0 :         return;
     809              :     }
     810            0 :     setvariant_(col, row);
     811            0 :     lastshader->flushparams();
     812            0 :     lastshader->setslotparams();
     813              : }
     814              : 
     815            0 : void Shader::setvariant(int col, int row, const Slot &slot, const VSlot &vslot)
     816              : {
     817            0 :     if(!loaded())
     818              :     {
     819            0 :         return;
     820              :     }
     821            0 :     setvariant_(col, row);
     822            0 :     lastshader->flushparams();
     823            0 :     lastshader->setslotparams(slot, vslot);
     824              : }
     825              : 
     826            0 : void Shader::set_()
     827              : {
     828            0 :     if(lastshader!=this)
     829              :     {
     830            0 :         bindprograms();
     831              :     }
     832            0 : }
     833              : 
     834            0 : void Shader::set()
     835              : {
     836            0 :     if(!loaded())
     837              :     {
     838            0 :         return;
     839              :     }
     840            0 :     set_();
     841            0 :     lastshader->flushparams();
     842              : }
     843              : 
     844            0 : void Shader::setslot()
     845              : {
     846            0 :     if(!loaded())
     847              :     {
     848            0 :         return;
     849              :     }
     850            0 :     set_();
     851            0 :     lastshader->flushparams();
     852            0 :     lastshader->setslotparams();
     853              : }
     854              : 
     855            0 : void Shader::set(const Slot &slot, const VSlot &vslot)
     856              : {
     857            0 :     if(!loaded())
     858              :     {
     859            0 :         return;
     860              :     }
     861            0 :     set_();
     862            0 :     lastshader->flushparams();
     863            0 :     lastshader->setslotparams(slot, vslot);
     864              : }
     865              : 
     866            0 : void Shader::setslotparams()
     867              : {
     868            0 :     uint unimask = 0;
     869            0 :     ::setslotparams(unimask, defaultparams);
     870            0 :     setdefaultparams(defaultparams, unimask);
     871            0 : }
     872              : 
     873            0 : void Shader::setslotparams(const Slot &slot, const VSlot &vslot)
     874              : {
     875              :     static bool thrown = false; //only throw error message once (will spam per frame otherwise)
     876            0 :     uint unimask = 0;
     877            0 :     if(vslot.slot == &slot)
     878              :     {
     879            0 :         ::setslotparams(unimask, defaultparams);
     880            0 :         for(size_t i = 0; i < slot.params.size(); i++)
     881              :         {
     882            0 :             const SlotShaderParam &p = slot.params.at(i);
     883            0 :             if(!(defaultparams.size() > p.loc))
     884              :             {
     885            0 :                 continue;
     886              :             }
     887            0 :             if(p.loc == SIZE_MAX)
     888              :             {
     889            0 :                 if(!thrown)
     890              :                 {
     891            0 :                     std::printf("Invalid slot shader param index: some slot shaders may not be in use\n");
     892            0 :                     thrown = true;
     893              :                 }
     894              :             }
     895            0 :             else if(!(unimask&(1<<p.loc)))
     896              :             {
     897            0 :                 const SlotShaderParamState &l = defaultparams.at(p.loc);
     898            0 :                 unimask |= 1<<p.loc;
     899            0 :                 setslotparam(l, p.val);
     900              :             }
     901              :         }
     902            0 :         setdefaultparams(defaultparams, unimask);
     903              :     }
     904              :     else
     905              :     {
     906            0 :         ::setslotparams(unimask, defaultparams);
     907            0 :         for(uint i = 0; i < defaultparams.size(); i++)
     908              :         {
     909            0 :             const SlotShaderParamState &l = defaultparams.at(i);
     910            0 :             setslotparam(l, unimask, i, l.flags&SlotShaderParam::REUSE ? findslotparam(vslot, l.name.c_str(), &l.val[0]) : &l.val[0]);
     911              :         }
     912              :     }
     913            0 : }
     914              : 
     915            0 : void Shader::bindprograms()
     916              : {
     917            0 :     if(this == lastshader || !loaded())
     918              :     {
     919            0 :         return;
     920              :     }
     921            0 :     glUseProgram(program);
     922            0 :     lastshader = this;
     923              : }
     924              : 
     925            2 : bool Shader::compile()
     926              : {
     927            2 :     if(!vsstr)
     928              :     {
     929            0 :         vsobj = !reusevs || reusevs->invalid() ? 0 : reusevs->vsobj;
     930              :     }
     931              :     else
     932              :     {
     933            2 :         compileglslshader(GL_VERTEX_SHADER,   vsobj, vsstr, name, debugshader || !variantshader);
     934              :     }
     935            2 :     if(!psstr)
     936              :     {
     937            0 :         psobj = !reuseps || reuseps->invalid() ? 0 : reuseps->psobj;
     938              :     }
     939              :     else
     940              :     {
     941            2 :         compileglslshader(GL_FRAGMENT_SHADER, psobj, psstr, name, debugshader || !variantshader);
     942              :     }
     943            2 :     linkglslprogram(!variantshader);
     944            2 :     return program!=0;
     945              : }
     946              : 
     947            2 : void Shader::cleanup(bool full)
     948              : {
     949            2 :     used = false;
     950            2 :     if(vsobj)
     951              :     {
     952            0 :         if(!reusevs)
     953              :         {
     954            0 :             glDeleteShader(vsobj); vsobj = 0;
     955              :         }
     956              :     }
     957            2 :     if(psobj)
     958              :     {
     959            0 :         if(!reuseps)
     960              :         {
     961            0 :             glDeleteShader(psobj);
     962            0 :             psobj = 0;
     963              :         }
     964              :     }
     965            2 :     if(program)
     966              :     {
     967            0 :         glDeleteProgram(program);
     968            0 :         program = 0;
     969              :     }
     970            2 :     localparams.clear();
     971            2 :     localparamremap.clear();
     972            2 :     globalparams.clear();
     973            2 :     if(standard || full)
     974              :     {
     975            2 :         type = Shader_Invalid;
     976              : 
     977            2 :         delete[] vsstr;
     978            2 :         delete[] psstr;
     979            2 :         delete[] defer;
     980              : 
     981            2 :         vsstr = nullptr;
     982            2 :         psstr = nullptr;
     983            2 :         defer = nullptr;
     984              : 
     985            2 :         variants.clear();
     986              : 
     987            2 :         delete[] variantrows;
     988            2 :         variantrows = nullptr;
     989              : 
     990            2 :         defaultparams.clear();
     991            2 :         attriblocs.clear();
     992            2 :         uniformlocs.clear();
     993            2 :         reusevs = reuseps = nullptr;
     994              :     }
     995              :     else
     996              :     {
     997            0 :         for(uint i = 0; i < defaultparams.size(); i++)
     998              :         {
     999            0 :             defaultparams[i].loc = -1;
    1000              :         }
    1001              :     }
    1002            2 : }
    1003              : //ShaderParamBinding
    1004              : 
    1005            1 : ShaderParamBinding::ShaderParamBinding(GLint loc, GLsizei size, GLenum format) :
    1006            1 :     loc(loc), size(size), format(format)
    1007              :     {
    1008            1 :     }
    1009              : 
    1010              : // globalshaderparamuse
    1011              : 
    1012            0 : GlobalShaderParamUse::GlobalShaderParamUse(GLint loc, GLsizei size, GLenum format, const GlobalShaderParamState *param, int version) :
    1013            0 :     ShaderParamBinding(loc, size, format),  param(param), version(version)
    1014              :     {
    1015            0 :     }
    1016              : 
    1017            0 : void GlobalShaderParamUse::flush()
    1018              : {
    1019            0 :     if(version == param->version)
    1020              :     {
    1021            0 :         return;
    1022              :     }
    1023            0 :     switch(format)
    1024              :     {
    1025            0 :         case GL_BOOL:
    1026              :         case GL_FLOAT:
    1027              :         {
    1028            0 :             glUniform1fv(loc, size, param->fval);
    1029            0 :             break;
    1030              :         }
    1031            0 :         case GL_BOOL_VEC2:
    1032              :         case GL_FLOAT_VEC2:
    1033              :         {
    1034            0 :             glUniform2fv(loc, size, param->fval);
    1035            0 :             break;
    1036              :         }
    1037            0 :         case GL_BOOL_VEC3:
    1038              :         case GL_FLOAT_VEC3:
    1039              :         {
    1040            0 :             glUniform3fv(loc, size, param->fval);
    1041            0 :             break;
    1042              :         }
    1043            0 :         case GL_BOOL_VEC4:
    1044              :         case GL_FLOAT_VEC4:
    1045              :         {
    1046            0 :             glUniform4fv(loc, size, param->fval);
    1047            0 :             break;
    1048              :         }
    1049            0 :         case GL_INT:
    1050              :         {
    1051            0 :             glUniform1iv(loc, size, param->ival);
    1052            0 :             break;
    1053              :         }
    1054            0 :         case GL_INT_VEC2:
    1055              :         {
    1056            0 :             glUniform2iv(loc, size, param->ival);
    1057            0 :             break;
    1058              :         }
    1059            0 :         case GL_INT_VEC3:
    1060              :         {
    1061            0 :             glUniform3iv(loc, size, param->ival);
    1062            0 :             break;
    1063              :         }
    1064            0 :         case GL_INT_VEC4:
    1065              :         {
    1066            0 :             glUniform4iv(loc, size, param->ival);
    1067            0 :             break;
    1068              :         }
    1069            0 :         case GL_UNSIGNED_INT:
    1070              :         {
    1071            0 :             glUniform1uiv(loc, size, param->uval);
    1072            0 :             break;
    1073              :         }
    1074            0 :         case GL_UNSIGNED_INT_VEC2:
    1075              :         {
    1076            0 :             glUniform2uiv(loc, size, param->uval);
    1077            0 :             break;
    1078              :         }
    1079            0 :         case GL_UNSIGNED_INT_VEC3:
    1080              :         {
    1081            0 :             glUniform3uiv(loc, size, param->uval);
    1082            0 :             break;
    1083              :         }
    1084            0 :         case GL_UNSIGNED_INT_VEC4:
    1085              :         {
    1086            0 :             glUniform4uiv(loc, size, param->uval);
    1087            0 :             break;
    1088              :         }
    1089            0 :         case GL_FLOAT_MAT2:
    1090              :         {
    1091            0 :             glUniformMatrix2fv(loc, 1, GL_FALSE, param->fval);
    1092            0 :             break;
    1093              :         }
    1094            0 :         case GL_FLOAT_MAT3:
    1095              :         {
    1096            0 :             glUniformMatrix3fv(loc, 1, GL_FALSE, param->fval);
    1097            0 :             break;
    1098              :         }
    1099            0 :         case GL_FLOAT_MAT4:
    1100              :         {
    1101            0 :             glUniformMatrix4fv(loc, 1, GL_FALSE, param->fval);
    1102            0 :             break;
    1103              :         }
    1104              :     }
    1105            0 :     version = param->version;
    1106              : }
    1107              : 
    1108              : //localshaderparamstate
    1109              : 
    1110            1 : LocalShaderParamState::LocalShaderParamState(GLint loc, GLsizei size, GLenum format) :
    1111            1 :     ShaderParamBinding(loc, size, format)
    1112              : {
    1113            1 : }
    1114              : 
    1115              : 
    1116            2 : void Shader::genattriblocs(const char *vs, const Shader *reusevs)
    1117              : {
    1118              :     static int len = std::strlen("//:attrib");
    1119              :     string name;
    1120              :     int loc;
    1121            2 :     if(reusevs)
    1122              :     {
    1123            0 :         attriblocs = reusevs->attriblocs;
    1124              :     }
    1125              :     else
    1126              :     {
    1127            2 :         while((vs = std::strstr(vs, "//:attrib")))
    1128              :         {
    1129            0 :             if(std::sscanf(vs, "//:attrib %100s %d", name, &loc) == 2)
    1130              :             {
    1131            0 :                 attriblocs.emplace_back(Shader::AttribLoc(getshaderparamname(name), loc));
    1132              :             }
    1133            0 :             vs += len;
    1134              :         }
    1135              :     }
    1136            2 : }
    1137              : 
    1138              : // adds to uniformlocs vector defined uniformlocs
    1139            2 : void Shader::genuniformlocs(const char *vs, const Shader *reusevs)
    1140              : {
    1141              :     static int len = std::strlen("//:uniform");
    1142              :     string name, blockname;
    1143              :     int binding, stride;
    1144            2 :     if(reusevs)
    1145              :     {
    1146            0 :         uniformlocs = reusevs->uniformlocs;
    1147              :     }
    1148              :     else
    1149              :     {
    1150            2 :         while((vs = std::strstr(vs, "//:uniform")))
    1151              :         {
    1152            0 :             int numargs = std::sscanf(vs, "//:uniform %100s %100s %d %d", name, blockname, &binding, &stride);
    1153            0 :             if(numargs >= 3)
    1154              :             {
    1155            0 :                 uniformlocs.emplace_back(UniformLoc(getshaderparamname(name), getshaderparamname(blockname), binding, numargs >= 4 ? stride : 0));
    1156              :             }
    1157            0 :             else if(numargs >= 1)
    1158              :             {
    1159            0 :                 uniformlocs.emplace_back(UniformLoc(getshaderparamname(name)));
    1160              :             }
    1161            0 :             vs += len;
    1162              :         }
    1163              :     }
    1164            2 : }
    1165              : 
    1166            2 : static Shader *newshader(int type, std::string_view name, std::string_view vs, std::string_view ps, Shader *variant = nullptr, int row = 0)
    1167              : {
    1168            2 :     if(Shader::lastshader)
    1169              :     {
    1170            0 :         glUseProgram(0);
    1171            0 :         Shader::lastshader = nullptr;
    1172              :     }
    1173            2 :     auto itr = shaders.find(name.data());
    1174            2 :     Shader *exists = (itr != shaders.end()) ? &(*itr).second : nullptr;
    1175            2 :     char *rname = exists ? exists->name : newstring(name.data());
    1176            2 :     if(!exists)
    1177              :     {
    1178            0 :         itr = shaders.insert( { rname, Shader() } ).first;
    1179              :     }
    1180            2 :     Shader *retval = (*itr).second.setupshader(type, rname, ps.data(), vs.data(), variant, row);
    1181            2 :     return retval; //can be nullptr or s
    1182              : }
    1183              : 
    1184            2 : Shader *Shader::setupshader(int newtype, char *rname, const char *ps, const char *vs, Shader *variant, int row)
    1185              : {
    1186            2 :     name = rname;
    1187            2 :     vsstr = newstring(vs);
    1188            2 :     psstr = newstring(ps);
    1189              : 
    1190            2 :     delete[] defer;
    1191            2 :     defer = nullptr;
    1192              : 
    1193            2 :     type = newtype & ~(Shader_Invalid | Shader_Deferred);
    1194            2 :     variantshader = variant;
    1195            2 :     standard = standardshaders;
    1196            2 :     if(forceshaders)
    1197              :     {
    1198            2 :         forced = true;
    1199              :     }
    1200            2 :     reusevs = reuseps = nullptr;
    1201            2 :     if(variant)
    1202              :     {
    1203            0 :         int row = 0,
    1204            0 :             col = 0;
    1205            0 :         if(!vs[0] || std::sscanf(vs, "%d , %d", &row, &col) >= 1)
    1206              :         {
    1207            0 :             delete[] vsstr;
    1208            0 :             vsstr = nullptr;
    1209            0 :             reusevs = !vs[0] ? variant : variant->getvariant(col, row);
    1210              :         }
    1211            0 :         row = col = 0;
    1212            0 :         if(!ps[0] || std::sscanf(ps, "%d , %d", &row, &col) >= 1)
    1213              :         {
    1214            0 :             delete[] psstr;
    1215            0 :             psstr = nullptr;
    1216            0 :             reuseps = !ps[0] ? variant : variant->getvariant(col, row);
    1217              :         }
    1218              :     }
    1219            2 :     if(variant)
    1220              :     {
    1221            0 :         for(uint i = 0; i < variant->defaultparams.size(); i++)
    1222              :         {
    1223            0 :             defaultparams.emplace_back(variant->defaultparams[i]);
    1224              :         }
    1225              :     }
    1226              :     else
    1227              :     {
    1228            3 :         for(uint i = 0; i < slotparams.size(); i++)
    1229              :         {
    1230            1 :             defaultparams.emplace_back(slotparams[i]);
    1231              :         }
    1232              :     }
    1233            2 :     attriblocs.clear();
    1234            2 :     uniformlocs.clear();
    1235            2 :     genattriblocs(vs, reusevs);
    1236            2 :     genuniformlocs(vs, reusevs);
    1237            2 :     if(!compile())
    1238              :     {
    1239            2 :         cleanup(true);
    1240            2 :         if(variant)
    1241              :         {
    1242            0 :             shaders.erase(rname);
    1243              :         }
    1244            2 :         return nullptr;
    1245              :     }
    1246            0 :     if(variant)
    1247              :     {
    1248            0 :         variant->addvariant(row, this);
    1249              :     }
    1250            0 :     return this;
    1251              : }
    1252              : 
    1253              : /**
    1254              :  * @brief Returns the location of the start of the line containing "main"
    1255              :  *
    1256              :  * Returns std::string::npos if no `main` is defined for the glsl code passed.
    1257              :  *
    1258              :  * Returns the beginning of the `main` call and all following lines until the closing '}'
    1259              :  *
    1260              :  * @param s the string to parse
    1261              :  *
    1262              :  * @return std::string::npos if no main() found, or the index preceeding 'main'
    1263              :  */
    1264            2 : static size_t findglslmain(const std::string &s)
    1265              : {
    1266            2 :     size_t main = s.find("main");
    1267            2 :     if(main == std::string::npos)
    1268              :     {
    1269            2 :         return std::string::npos;
    1270              :     }
    1271            0 :     for(;main != static_cast<size_t>(-1); main--) //note reverse iteration
    1272              :     {
    1273            0 :         switch(s[main])
    1274              :         {
    1275            0 :             case '\r':
    1276              :             case '\n':
    1277              :             case ';':
    1278              :             {
    1279            0 :                 return main + 1;
    1280              :             }
    1281              :         }
    1282              :     }
    1283            0 :     return 0;
    1284              : }
    1285              : 
    1286            0 : static void gengenericvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 0)
    1287              : {
    1288            0 :     int rowoffset = 0;
    1289            0 :     bool vschanged = false,
    1290            0 :          pschanged = false;
    1291            0 :     std::vector<char> vsv, psv;
    1292            0 :     for(uint i = 0; i < std::strlen(vs)+1; ++i)
    1293              :     {
    1294            0 :         vsv.push_back(vs[i]);
    1295              :     }
    1296            0 :     for(uint i = 0; i < std::strlen(ps)+1; ++i)
    1297              :     {
    1298            0 :         psv.push_back(ps[i]);
    1299              :     }
    1300              : 
    1301              :     //cannot be constexpr-- strlen is not compile time
    1302              :     static const int len  = std::strlen("//:variant"),
    1303              :                      olen = std::strlen("override");
    1304            0 :     for(char *vspragma = vsv.data();; vschanged = true)
    1305              :     {
    1306            0 :         vspragma = std::strstr(vspragma, "//:variant");
    1307            0 :         if(!vspragma)
    1308              :         {
    1309            0 :             break;
    1310              :         }
    1311            0 :         if(std::sscanf(vspragma + len, "row %d", &rowoffset) == 1)
    1312              :         {
    1313            0 :             continue;
    1314              :         }
    1315            0 :         std::memset(vspragma, ' ', len);
    1316            0 :         vspragma += len;
    1317            0 :         if(!std::strncmp(vspragma, "override", olen))
    1318              :         {
    1319            0 :             std::memset(vspragma, ' ', olen);
    1320            0 :             vspragma += olen;
    1321            0 :             char *end = vspragma + std::strcspn(vspragma, "\n\r");
    1322            0 :             end += std::strspn(end, "\n\r");
    1323            0 :             int endlen = std::strcspn(end, "\n\r");
    1324            0 :             std::memset(end, ' ', endlen);
    1325              :         }
    1326            0 :     }
    1327            0 :     for(char *pspragma = psv.data();; pschanged = true)
    1328              :     {
    1329            0 :         pspragma = std::strstr(pspragma, "//:variant");
    1330            0 :         if(!pspragma)
    1331              :         {
    1332            0 :             break;
    1333              :         }
    1334            0 :         if(std::sscanf(pspragma + len, "row %d", &rowoffset) == 1)
    1335              :         {
    1336            0 :             continue;
    1337              :         }
    1338            0 :         std::memset(pspragma, ' ', len);
    1339            0 :         pspragma += len;
    1340            0 :         if(!std::strncmp(pspragma, "override", olen))
    1341              :         {
    1342            0 :             std::memset(pspragma, ' ', olen);
    1343            0 :             pspragma += olen;
    1344            0 :             char *end = pspragma + std::strcspn(pspragma, "\n\r");
    1345            0 :             end += std::strspn(end, "\n\r");
    1346            0 :             int endlen = std::strcspn(end, "\n\r");
    1347            0 :             std::memset(end, ' ', endlen);
    1348              :         }
    1349            0 :     }
    1350            0 :     row += rowoffset;
    1351            0 :     if(row < 0 || row >= maxvariantrows)
    1352              :     {
    1353            0 :         return;
    1354              :     }
    1355            0 :     int col = s.numvariants(row);
    1356            0 :     DEF_FORMAT_STRING(varname, "<variant:%d,%d>%s", col, row, sname);
    1357              :     string reuse;
    1358            0 :     if(col)
    1359              :     {
    1360            0 :         formatstring(reuse, "%d", row);
    1361              :     }
    1362              :     else
    1363              :     {
    1364            0 :         copystring(reuse, "");
    1365              :     }
    1366            0 :     newshader(s.type, varname, vschanged ? vsv.data() : reuse, pschanged ? psv.data() : reuse, &s, row);
    1367            0 : }
    1368              : 
    1369            0 : static void genfogshader(std::string &vs, std::string &ps)
    1370              : {
    1371              :     //unused PRAGMA_LEN
    1372              :     //constexpr int PRAGMA_LEN = std::string_view("//:fog").size() + 1;
    1373              : 
    1374            0 :     size_t vspragma = vs.find("//:fog"),
    1375            0 :            pspragma = ps.find("//:fog");
    1376              : 
    1377            0 :     if(vspragma == std::string::npos && pspragma == std::string::npos)
    1378              :     {
    1379            0 :         return;
    1380              :     }
    1381              : 
    1382            0 :     size_t vsmain = findglslmain(vs),
    1383            0 :            vsend  = vs.rfind('}');
    1384              : 
    1385            0 :     if(vsmain != std::string::npos && vsend != std::string::npos)
    1386              :     {
    1387            0 :         if(vs.find("lineardepth") == std::string::npos)
    1388              :         {
    1389            0 :             constexpr std::string_view FOG_PARAMS = "\nuniform vec2 lineardepthscale;\nvarying float lineardepth;\n";
    1390            0 :             constexpr std::string_view VS_FOG = "\nlineardepth = dot(lineardepthscale, gl_Position.zw);\n";
    1391              : 
    1392            0 :             vs.insert(vsend, VS_FOG);
    1393            0 :             vs.insert(vsmain, FOG_PARAMS);
    1394              :         }
    1395              :     }
    1396              : 
    1397            0 :     size_t psmain = findglslmain(ps),
    1398            0 :            psend  = ps.rfind('}');
    1399              : 
    1400            0 :     if(psmain != std::string::npos && psend != std::string::npos)
    1401              :     {
    1402            0 :         std::string params;
    1403              : 
    1404            0 :         if(ps.find("lineardepth") == std::string::npos)
    1405              :         {
    1406            0 :             params = "\nvarying float lineardepth;\n";
    1407              :         }
    1408              : 
    1409              :         std::string fogparams =
    1410              :             "\nuniform vec3 fogcolor;\n"
    1411              :             "uniform vec2 fogdensity;\n"
    1412              :             "uniform vec4 radialfogscale;\n"
    1413            0 :             "#define fogcoord lineardepth*length(vec3(gl_FragCoord.xy*radialfogscale.xy + radialfogscale.zw, 1.0))\n";
    1414              : 
    1415            0 :         params += fogparams;
    1416              : 
    1417            0 :         std::string psfog = "\nfragcolor.rgb = mix((fogcolor).rgb, fragcolor.rgb, clamp(exp2(fogcoord*-fogdensity.x)*fogdensity.y, 0.0, 1.0));\n";
    1418            0 :         ps.insert(psend, psfog);
    1419            0 :         ps.insert(psmain, params);
    1420            0 :     }
    1421              : }
    1422              : 
    1423            1 : static void genuniformdefs(std::string &vs, std::string &ps, const Shader *variant = nullptr)
    1424              : {
    1425            1 :     if(variant ? variant->defaultparams.empty() : slotparams.empty())
    1426              :     {
    1427            1 :         return;
    1428              :     }
    1429              : 
    1430            1 :     size_t vsmain = findglslmain(vs),
    1431            1 :            psmain = findglslmain(ps);
    1432              : 
    1433            1 :     if(vsmain == std::string::npos || psmain == std::string::npos)
    1434              :     {
    1435            1 :         return;
    1436              :     }
    1437              : 
    1438            0 :     std::string params;
    1439            0 :     if(variant)
    1440              :     {
    1441            0 :         for(const auto &param : variant->defaultparams)
    1442              :         {
    1443            0 :             DEF_FORMAT_STRING(uni, "\nuniform vec4 %s;\n", param.name.c_str());
    1444            0 :             params += uni;
    1445              :         }
    1446              :     }
    1447              :     else
    1448              :     {
    1449            0 :         for(const auto &param : slotparams)
    1450              :         {
    1451            0 :             DEF_FORMAT_STRING(uni, "\nuniform vec4 %s;\n", param.name);
    1452            0 :             params += uni;
    1453              :         }
    1454              :     }
    1455              : 
    1456            0 :     vs.insert(vsmain, params);
    1457            0 :     ps.insert(psmain, params);
    1458            0 : }
    1459              : 
    1460            0 : void setupshaders()
    1461              : {
    1462            0 :     if(!glslversion)
    1463              :     {
    1464            0 :         conoutf(Console_Error, "Cannot setup GLSL shaders without GLSL initialized, operation not performed");
    1465            0 :         return;
    1466              :     }
    1467              :     GLint val;
    1468            0 :     glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &val);
    1469            0 :     maxvsuniforms = val/4;
    1470            0 :     glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &val);
    1471            0 :     maxfsuniforms = val/4;
    1472              : 
    1473            0 :     glGetIntegerv(GL_MIN_PROGRAM_TEXEL_OFFSET, &val);
    1474            0 :     mintexoffset = val;
    1475            0 :     glGetIntegerv(GL_MAX_PROGRAM_TEXEL_OFFSET, &val);
    1476            0 :     maxtexoffset = val;
    1477              : 
    1478            0 :     standardshaders = true;
    1479            0 :     nullshader = newshader(0, "<init>null",
    1480              :         "attribute vec4 vvertex;\n"
    1481              :         "void main(void) {\n"
    1482              :         "   gl_Position = vvertex;\n"
    1483              :         "}\n",
    1484              :         "fragdata(0) vec4 fragcolor;\n"
    1485              :         "void main(void) {\n"
    1486              :         "   fragcolor = vec4(1.0, 0.0, 1.0, 1.0);\n"
    1487              :         "}\n");
    1488            0 :     hudshader = newshader(0, "<init>hud",
    1489              :         "attribute vec4 vvertex, vcolor;\n"
    1490              :         "attribute vec2 vtexcoord0;\n"
    1491              :         "uniform mat4 hudmatrix;\n"
    1492              :         "varying vec2 texcoord0;\n"
    1493              :         "varying vec4 colorscale;\n"
    1494              :         "void main(void) {\n"
    1495              :         "    gl_Position = hudmatrix * vvertex;\n"
    1496              :         "    texcoord0 = vtexcoord0;\n"
    1497              :         "    colorscale = vcolor;\n"
    1498              :         "}\n",
    1499              :         "uniform sampler2D tex0;\n"
    1500              :         "varying vec2 texcoord0;\n"
    1501              :         "varying vec4 colorscale;\n"
    1502              :         "fragdata(0) vec4 fragcolor;\n"
    1503              :         "void main(void) {\n"
    1504              :         "    vec4 color = texture2D(tex0, texcoord0);\n"
    1505              :         "    fragcolor = colorscale * color;\n"
    1506              :         "}\n");
    1507            0 :     hudtextshader = newshader(0, "<init>hudtext",
    1508              :         "attribute vec4 vvertex, vcolor;\n"
    1509              :         "attribute vec2 vtexcoord0;\n"
    1510              :         "uniform mat4 hudmatrix;\n"
    1511              :         "varying vec2 texcoord0;\n"
    1512              :         "varying vec4 colorscale;\n"
    1513              :         "void main(void) {\n"
    1514              :         "    gl_Position = hudmatrix * vvertex;\n"
    1515              :         "    texcoord0 = vtexcoord0;\n"
    1516              :         "    colorscale = vcolor;\n"
    1517              :         "}\n",
    1518              :         "uniform sampler2D tex0;\n"
    1519              :         "uniform vec4 textparams;\n"
    1520              :         "varying vec2 texcoord0;\n"
    1521              :         "varying vec4 colorscale;\n"
    1522              :         "fragdata(0) vec4 fragcolor;\n"
    1523              :         "void main(void) {\n"
    1524              :         "    float dist = texture2D(tex0, texcoord0).r;\n"
    1525              :         "    float border = smoothstep(textparams.x, textparams.y, dist);\n"
    1526              :         "    float outline = smoothstep(textparams.z, textparams.w, dist);\n"
    1527              :         "    fragcolor = vec4(colorscale.rgb * outline, colorscale.a * border);\n"
    1528              :         "}\n");
    1529            0 :     hudnotextureshader = newshader(0, "<init>hudnotexture",
    1530              :         "attribute vec4 vvertex, vcolor;\n"
    1531              :         "uniform mat4 hudmatrix;"
    1532              :         "varying vec4 color;\n"
    1533              :         "void main(void) {\n"
    1534              :         "    gl_Position = hudmatrix * vvertex;\n"
    1535              :         "    color = vcolor;\n"
    1536              :         "}\n",
    1537              :         "varying vec4 color;\n"
    1538              :         "fragdata(0) vec4 fragcolor;\n"
    1539              :         "void main(void) {\n"
    1540              :         "    fragcolor = color;\n"
    1541              :         "}\n");
    1542            0 :     standardshaders = false;
    1543            0 :     if(!nullshader || !hudshader || !hudtextshader || !hudnotextureshader)
    1544              :     {
    1545            0 :         fatal("failed to setup shaders");
    1546              :     }
    1547            0 :     dummyslot.shader = nullshader;
    1548              : }
    1549              : 
    1550              : VAR(defershaders, 0, 1, 1);
    1551              : 
    1552            1 : void defershader(const int *type, const char *name, const char *contents)
    1553              : {
    1554            1 :     auto itr = shaders.find(name);
    1555            1 :     const Shader *exists = (itr != shaders.end()) ? &(*itr).second : nullptr;
    1556            1 :     if(exists && !exists->invalid())
    1557              :     {
    1558            0 :         return;
    1559              :     }
    1560            1 :     if(!defershaders)
    1561              :     {
    1562            0 :         execute(contents);
    1563            0 :         return;
    1564              :     }
    1565            1 :     char *rname = exists ? exists->name : newstring(name);
    1566            1 :     if(!exists)
    1567              :     {
    1568            1 :         itr = shaders.insert( { rname, Shader() } ).first;
    1569              :     }
    1570            1 :     Shader &s = (*itr).second;
    1571            1 :     s.name = rname;
    1572            1 :     delete[] s.defer;
    1573            1 :     s.defer = newstring(contents);
    1574            1 :     s.type = Shader_Deferred | (*type & ~Shader_Invalid);
    1575            1 :     s.standard = standardshaders;
    1576              : }
    1577              : 
    1578              : 
    1579            0 : void Shader::force()
    1580              : {
    1581            0 :     if(!deferred() || !defer)
    1582              :     {
    1583            0 :         return;
    1584              :     }
    1585            0 :     char *cmd = defer;
    1586            0 :     defer = nullptr;
    1587            0 :     bool wasstandard = standardshaders,
    1588            0 :          wasforcing = forceshaders;
    1589            0 :     int oldflags = identflags;
    1590            0 :     standardshaders = standard;
    1591            0 :     forceshaders = false;
    1592            0 :     identflags &= ~Idf_Persist;
    1593            0 :     slotparams.clear();
    1594            0 :     execute(cmd);
    1595            0 :     identflags = oldflags;
    1596            0 :     forceshaders = wasforcing;
    1597            0 :     standardshaders = wasstandard;
    1598            0 :     delete[] cmd;
    1599              : 
    1600            0 :     if(deferred())
    1601              :     {
    1602            0 :         delete[] defer;
    1603            0 :         defer = nullptr;
    1604            0 :         type = Shader_Invalid;
    1605              :     }
    1606              : }
    1607              : 
    1608            0 : int Shader::uniformlocversion()
    1609              : {
    1610              :     static int version = 0;
    1611            0 :     if(++version >= 0)
    1612              :     {
    1613            0 :         return version;
    1614              :     }
    1615            0 :     version = 0;
    1616            0 :     for(auto &[k, s] : shaders)
    1617              :     {
    1618            0 :         for(UniformLoc &j : s.uniformlocs)
    1619              :         {
    1620            0 :             j.version = -1;
    1621              :         }
    1622              :     }
    1623            0 :     return version;
    1624              : }
    1625              : 
    1626            1 : Shader *useshaderbyname(std::string_view name)
    1627              : {
    1628            1 :     auto itr = shaders.find(name.data());
    1629            1 :     if(itr == shaders.end())
    1630              :     {
    1631            1 :         return nullptr;
    1632              :     }
    1633            0 :     Shader *s = &(*itr).second;
    1634            0 :     if(s->deferred())
    1635              :     {
    1636            0 :         s->force();
    1637              :     }
    1638            0 :     s->forced = true;
    1639            0 :     return s;
    1640              : }
    1641              : 
    1642            1 : void shader(const int *type, const char *name, const char *vs, const char *ps)
    1643              : {
    1644            1 :     if(lookupshaderbyname(name))
    1645              :     {
    1646            0 :         return;
    1647              :     }
    1648            1 :     DEF_FORMAT_STRING(info, "shader %s", name);
    1649            1 :     renderprogress(loadprogress, info);
    1650            3 :     std::string vs_string(vs), ps_string(ps);
    1651              : 
    1652            1 :     if(!slotparams.empty())
    1653              :     {
    1654            1 :         genuniformdefs(vs_string, ps_string);
    1655              :     }
    1656              : 
    1657            1 :     if(vs_string.find("//:fog") != std::string::npos || ps_string.find("//:fog") != std::string::npos)
    1658              :     {
    1659            0 :         genfogshader(vs_string, ps_string);
    1660              :     }
    1661              : 
    1662            1 :     Shader *s = newshader(*type, name, vs_string.c_str(), ps_string.c_str());
    1663            1 :     if(s)
    1664              :     {
    1665            0 :         if(vs_string.find("//:variant") != std::string::npos || ps_string.find("//:variant") != std::string::npos)
    1666              :         {
    1667            0 :             gengenericvariant(*s, name, vs_string.c_str(), ps_string.c_str());
    1668              :         }
    1669              :     }
    1670            1 :     slotparams.clear();
    1671            1 : }
    1672              : 
    1673              : static bool adding_shader = false;
    1674              : static std::vector<std::pair<std::string, std::string>> shader_defines;
    1675              : static std::vector<std::string> shader_includes_vs, shader_includes_fs;
    1676              : static std::string shader_path_vs, shader_path_fs;
    1677              : 
    1678            1 : static std::string shader_make_defines()
    1679              : {
    1680            1 :     std::string defines;
    1681              : 
    1682            1 :     for(const std::pair<std::string, std::string> &define : shader_defines)
    1683              :     {
    1684            0 :         defines += "#define " + define.first + " " + define.second + "\n";
    1685              :     }
    1686              : 
    1687            1 :     return defines;
    1688            0 : }
    1689              : 
    1690            1 : static void shader_clear_defines()
    1691              : {
    1692            1 :     shader_defines.clear();
    1693            1 :     shader_includes_vs.clear();
    1694            1 :     shader_includes_fs.clear();
    1695            1 :     shader_path_vs.clear();
    1696            1 :     shader_path_fs.clear();
    1697            1 : }
    1698              : 
    1699              : //loads the vertex and pixel shaders at the indicated paths and returns them to vs, ps
    1700            1 : static void shader_assemble(std::string vs_path, std::string fs_path, std::string &vs, std::string &ps)
    1701              : {
    1702            1 :     std::string defines;
    1703              : 
    1704            1 :     defines = shader_make_defines();
    1705              : 
    1706            1 :     if(!shader_path_vs.empty())
    1707              :     {
    1708            0 :         const char *vs_file = loadfile(path(vs_path).c_str(), nullptr);
    1709            0 :         if(!vs_file)
    1710              :         {
    1711            0 :             conoutf(Console_Error, "could not load vertex shader %s", vs_path.c_str());
    1712            0 :             adding_shader = false;
    1713            0 :             return;
    1714              :         }
    1715              : 
    1716            0 :         vs = vs_file;
    1717              : 
    1718            0 :         std::string includes;
    1719            0 :         for(const std::string &include : shader_includes_vs)
    1720              :         {
    1721            0 :             const char *vs_include = loadfile(path(include).c_str(), nullptr);
    1722              : 
    1723            0 :             if(!vs_include)
    1724              :             {
    1725            0 :                 conoutf(Console_Error, "could not load vertex shader include %s", include.c_str());
    1726            0 :                 adding_shader = false;
    1727            0 :                 return;
    1728              :             }
    1729              : 
    1730            0 :             includes += std::string(vs_include) + "\n";
    1731              :         }
    1732              : 
    1733            0 :         vs = defines + includes + vs;
    1734            0 :     }
    1735              : 
    1736            1 :     if(!fs_path.empty())
    1737              :     {
    1738            0 :         char *ps_file = loadfile(path(fs_path).c_str(), nullptr);
    1739            0 :         if(!ps_file)
    1740              :         {
    1741            0 :             conoutf(Console_Error, "could not load fragment shader %s", fs_path.c_str());
    1742            0 :             adding_shader = false;
    1743            0 :             return;
    1744              :         }
    1745              : 
    1746            0 :         ps = ps_file;
    1747              : 
    1748            0 :         std::string includes;
    1749            0 :         for(const std::string &include : shader_includes_fs)
    1750              :         {
    1751            0 :             char *ps_include = loadfile(path(include).c_str(), nullptr);
    1752              : 
    1753            0 :             if(!ps_include)
    1754              :             {
    1755            0 :                 conoutf(Console_Error, "could not load fragment shader include %s", include.c_str());
    1756            0 :                 adding_shader = false;
    1757            0 :                 return;
    1758              :             }
    1759              : 
    1760            0 :             includes += std::string(ps_include) + "\n";
    1761              :         }
    1762              : 
    1763            0 :         ps = defines + includes + ps;
    1764            0 :     }
    1765            1 : }
    1766              : 
    1767            1 : static void shader_new(const int *type, const char *name, const uint *code)
    1768              : {
    1769            1 :     if(lookupshaderbyname(name))
    1770              :     {
    1771            0 :         return;
    1772              :     }
    1773              : 
    1774            1 :     adding_shader = true;
    1775            1 :     shader_clear_defines();
    1776              : 
    1777            1 :     execute(code);
    1778              : 
    1779            1 :     std::string vs, ps;
    1780            1 :     shader_assemble(shader_path_vs, shader_path_fs, vs, ps);
    1781              : 
    1782            1 :     DEF_FORMAT_STRING(info, "shader %s", name);
    1783            1 :     renderprogress(loadprogress, info);
    1784              : 
    1785            1 :     if(!slotparams.empty())
    1786              :     {
    1787            0 :         genuniformdefs(vs, ps);
    1788              :     }
    1789              : 
    1790            1 :     if(vs.find("//:fog") != std::string::npos || ps.find("//:fog") != std::string::npos)
    1791              :     {
    1792            0 :         genfogshader(vs, ps);
    1793              :     }
    1794              : 
    1795            1 :     Shader *s = newshader(*type, name, vs.c_str(), ps.c_str());
    1796            1 :     if(s)
    1797              :     {
    1798            0 :         if(vs.find("//:variant") != std::string::npos || ps.find("//:variant") != std::string::npos)
    1799              :         {
    1800            0 :             gengenericvariant(*s, name, vs.c_str(), ps.c_str());
    1801              :         }
    1802              :     }
    1803            1 :     slotparams.clear();
    1804              : 
    1805            1 :     adding_shader = false;
    1806            1 : }
    1807              : 
    1808            1 : static void shader_define(const char *name, const char *value)
    1809              : {
    1810            1 :     if(!adding_shader)
    1811              :     {
    1812            1 :         return;
    1813              :     }
    1814              : 
    1815            0 :     shader_defines.emplace_back(name, value);
    1816              : }
    1817              : 
    1818            1 : static void shader_get_defines()
    1819              : {
    1820            1 :     if(!adding_shader)
    1821              :     {
    1822            1 :         return;
    1823              :     }
    1824              : 
    1825            0 :     std::string res;
    1826              : 
    1827            0 :     for(const std::pair<std::string, std::string> &define : shader_defines)
    1828              :     {
    1829            0 :         res += " [" + define.first + " " + define.second + "]";
    1830              :     }
    1831              : 
    1832            0 :     result(res.c_str());
    1833            0 : }
    1834              : 
    1835            1 : static void shader_include_vs(const char *path)
    1836              : {
    1837            1 :     if(!adding_shader)
    1838              :     {
    1839            1 :         return;
    1840              :     }
    1841              : 
    1842            0 :     shader_includes_vs.emplace_back(path);
    1843              : }
    1844              : 
    1845            1 : static void shader_get_includes_vs()
    1846              : {
    1847            1 :     if(!adding_shader)
    1848              :     {
    1849            1 :         return;
    1850              :     }
    1851              : 
    1852            0 :     std::string res;
    1853              : 
    1854            0 :     for(const std::string &include : shader_includes_vs)
    1855              :     {
    1856            0 :         res += " \"" + include + "\"";
    1857              :     }
    1858              : 
    1859            0 :     result(res.c_str());
    1860            0 : }
    1861              : 
    1862            1 : static void shader_include_fs(const char *path)
    1863              : {
    1864            1 :     if(!adding_shader)
    1865              :     {
    1866            1 :         return;
    1867              :     }
    1868              : 
    1869            0 :     shader_includes_fs.emplace_back(path);
    1870              : }
    1871              : 
    1872            1 : static void shader_get_includes_fs()
    1873              : {
    1874            1 :     if(!adding_shader)
    1875              :     {
    1876            1 :         return;
    1877              :     }
    1878              : 
    1879            0 :     std::string res;
    1880              : 
    1881            0 :     for(const std::string &include : shader_includes_fs)
    1882              :     {
    1883            0 :         res += " \"" + include + "\"";
    1884              :     }
    1885              : 
    1886            0 :     result(res.c_str());
    1887            0 : }
    1888              : 
    1889            1 : static void shader_source(const char *vs, const char *fs)
    1890              : {
    1891            1 :     if(!adding_shader)
    1892              :     {
    1893            1 :         return;
    1894              :     }
    1895              : 
    1896            0 :     shader_path_vs = vs;
    1897            0 :     shader_path_fs = fs;
    1898              : }
    1899              : 
    1900            1 : void variantshader(const int *type, const char *name, const int *row, const char *vs, const char *ps, const int *maxvariants)
    1901              : {
    1902            1 :     if(*row < 0)
    1903              :     {
    1904            0 :         shader(type, name, vs, ps);
    1905            1 :         return;
    1906              :     }
    1907            1 :     else if(*row >= maxvariantrows)
    1908              :     {
    1909            0 :         return;
    1910              :     }
    1911            1 :     Shader *s = lookupshaderbyname(name);
    1912            1 :     if(!s)
    1913              :     {
    1914            1 :         return;
    1915              :     }
    1916            0 :     DEF_FORMAT_STRING(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
    1917            0 :     if(*maxvariants > 0)
    1918              :     {
    1919            0 :         DEF_FORMAT_STRING(info, "shader %s", name);
    1920            0 :         renderprogress(std::min(s->variants.size() / static_cast<float>(*maxvariants), 1.0f), info);
    1921              :     }
    1922              : 
    1923            0 :     std::string vs_string(vs), ps_string(ps);
    1924              : 
    1925            0 :     if(!s->defaultparams.empty())
    1926              :     {
    1927            0 :         genuniformdefs(vs_string, ps_string, s);
    1928              :     }
    1929              : 
    1930            0 :     if(vs_string.find("//:fog") != std::string::npos || ps_string.find("//:fog") != std::string::npos)
    1931              :     {
    1932            0 :         genfogshader(vs_string, ps_string);
    1933              :     }
    1934              : 
    1935            0 :     const Shader *v = newshader(*type, varname, vs_string.c_str(), ps_string.c_str(), s, *row);
    1936            0 :     if(v)
    1937              :     {
    1938            0 :         if(vs_string.find("//:variant") != std::string::npos || ps_string.find("//:variant") != std::string::npos)
    1939              :         {
    1940            0 :             gengenericvariant(*s, varname, vs_string.c_str(), ps_string.c_str(), *row);
    1941              :         }
    1942              :     }
    1943            0 : }
    1944              : 
    1945            1 : void variantshader_new(const int *type, const char *name, const int *row, const int *maxvariants, const uint *code)
    1946              : {
    1947            1 :     if(*row < 0)
    1948              :     {
    1949            0 :         shader_new(type, name, code);
    1950            1 :         return;
    1951              :     }
    1952            1 :     else if(*row >= maxvariantrows)
    1953              :     {
    1954            0 :         return;
    1955              :     }
    1956            1 :     Shader *s = lookupshaderbyname(name);
    1957            1 :     if(!s)
    1958              :     {
    1959            1 :         return;
    1960              :     }
    1961              : 
    1962            0 :     adding_shader = true;
    1963            0 :     shader_clear_defines();
    1964              : 
    1965            0 :     execute(code);
    1966              : 
    1967            0 :     std::string vs, ps;
    1968            0 :     shader_assemble(shader_path_vs, shader_path_fs, vs, ps);
    1969              : 
    1970            0 :     DEF_FORMAT_STRING(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
    1971            0 :     if(*maxvariants > 0)
    1972              :     {
    1973            0 :         DEF_FORMAT_STRING(info, "shader %s", name);
    1974            0 :         renderprogress(std::min(s->variants.size() / static_cast<float>(*maxvariants), 1.0f), info);
    1975              :     }
    1976              : 
    1977            0 :     if(!s->defaultparams.empty())
    1978              :     {
    1979            0 :         genuniformdefs(vs, ps, s);
    1980              :     }
    1981              : 
    1982            0 :     if(vs.find("//:fog") != std::string::npos || ps.find("//:fog") != std::string::npos)
    1983              :     {
    1984            0 :         genfogshader(vs, ps);
    1985              :     }
    1986              : 
    1987            0 :     const Shader *v = newshader(*type, varname, vs.c_str(), ps.c_str(), s, *row);
    1988            0 :     if(v)
    1989              :     {
    1990            0 :         if(vs.find("//:variant") != std::string::npos || ps.find("//:variant") != std::string::npos)
    1991              :         {
    1992            0 :             gengenericvariant(*s, varname, vs.c_str(), ps.c_str(), *row);
    1993              :         }
    1994              :     }
    1995              : 
    1996            0 :     adding_shader = false;
    1997            0 : }
    1998              : 
    1999              : //==============================================================================
    2000              : 
    2001              : 
    2002            1 : void setshader(const char *name)
    2003              : {
    2004            1 :     slotparams.clear();
    2005            1 :     auto itr = shaders.find(name);
    2006            1 :     if(itr == shaders.end())
    2007              :     {
    2008            0 :         conoutf(Console_Error, "no such shader: %s", name);
    2009              :     }
    2010              :     else
    2011              :     {
    2012            1 :         slotshader = &(*itr).second;
    2013              :     }
    2014            1 : }
    2015              : 
    2016              : 
    2017            0 : void resetslotshader()
    2018              : {
    2019            0 :     slotshader = nullptr;
    2020            0 :     slotparams.clear();
    2021            0 : }
    2022              : 
    2023            0 : void setslotshader(Slot &s)
    2024              : {
    2025            0 :     s.shader = slotshader;
    2026            0 :     if(!s.shader)
    2027              :     {
    2028            0 :         s.shader = stdworldshader;
    2029            0 :         return;
    2030              :     }
    2031            0 :     for(uint i = 0; i < slotparams.size(); i++)
    2032              :     {
    2033            0 :         s.params.push_back(slotparams[i]);
    2034              :     }
    2035              : }
    2036              : 
    2037            0 : static void linkslotshaderparams(std::vector<SlotShaderParam> &params, const Shader &sh, bool load)
    2038              : {
    2039            0 :     if(sh.loaded())
    2040              :     {
    2041            0 :         for(SlotShaderParam &param : params)
    2042              :         {
    2043            0 :             int loc = -1;
    2044            0 :             for(uint j = 0; j < sh.defaultparams.size(); j++)
    2045              :             {
    2046            0 :                 const SlotShaderParamState &dparam = sh.defaultparams[j];
    2047            0 :                 if(dparam.name==param.name)
    2048              :                 {
    2049            0 :                     if(std::memcmp(param.val, &dparam.val[0], sizeof(param.val)))
    2050              :                     {
    2051            0 :                         loc = j;
    2052              :                     }
    2053            0 :                     break;
    2054              :                 }
    2055              :             }
    2056            0 :             param.loc = loc;
    2057              :         }
    2058              :     }
    2059            0 :     else if(load)
    2060              :     {
    2061            0 :         for(SlotShaderParam &p : params)
    2062              :         {
    2063            0 :             p.loc = SIZE_MAX;
    2064              :         }
    2065              :     }
    2066            0 : }
    2067              : 
    2068            0 : void linkslotshader(Slot &s, bool load)
    2069              : {
    2070            0 :     if(!s.shader)
    2071              :     {
    2072            0 :         return;
    2073              :     }
    2074            0 :     if(load && s.shader->deferred())
    2075              :     {
    2076            0 :         s.shader->force();
    2077              :     }
    2078            0 :     linkslotshaderparams(s.params, *s.shader, load);
    2079              : }
    2080              : 
    2081            0 : void linkvslotshader(VSlot &s, bool load)
    2082              : {
    2083            0 :     if(!s.slot->shader)
    2084              :     {
    2085            0 :         return;
    2086              :     }
    2087            0 :     linkslotshaderparams(s.params, *(s.slot->shader), load);
    2088            0 :     if(!s.slot->shader->loaded())
    2089              :     {
    2090            0 :         return;
    2091              :     }
    2092            0 :     if(s.slot->texmask&(1 << Tex_Glow))
    2093              :     {
    2094            0 :         static const char *paramname = getshaderparamname("glowcolor");
    2095            0 :         const float *param = findslotparam(s, paramname);
    2096            0 :         if(param)
    2097              :         {
    2098            0 :             s.glowcolor = vec(param).clamp(0, 1);
    2099              :         }
    2100              :     }
    2101              : }
    2102              : 
    2103            0 : bool shouldreuseparams(const Slot &s, const VSlot &p)
    2104              : {
    2105            0 :     if(!s.shader)
    2106              :     {
    2107            0 :         return false;
    2108              :     }
    2109            0 :     const Shader &sh = *s.shader;
    2110            0 :     for(const SlotShaderParamState &param : sh.defaultparams)
    2111              :     {
    2112            0 :         if(param.flags & SlotShaderParam::REUSE)
    2113              :         {
    2114            0 :             const float *val = findslotparam(p, param.name.c_str());
    2115            0 :             if(val && std::memcmp(&param.val[0], val, param.val.size()))
    2116              :             {
    2117            0 :                 for(const SlotShaderParam &j : s.params)
    2118              :                 {
    2119            0 :                     if(j.name == param.name)
    2120              :                     {
    2121            0 :                         goto notreused; //bail out of for loop
    2122              :                     }
    2123              :                 }
    2124            0 :                 return true;
    2125            0 :             notreused:;
    2126              :             }
    2127              :         }
    2128              :     }
    2129            0 :     return false;
    2130              : }
    2131              : 
    2132              : 
    2133              : static std::unordered_set<std::string> shaderparamnames;
    2134              : 
    2135            2 : const char *getshaderparamname(const char *name, bool insert)
    2136              : {
    2137            2 :     auto itr = shaderparamnames.find(name);
    2138            2 :     if(itr != shaderparamnames.end() || !insert)
    2139              :     {
    2140            1 :         return (*itr).c_str();
    2141              :     }
    2142            2 :     return (*shaderparamnames.insert(name).first).c_str();
    2143              : }
    2144              : 
    2145            2 : void addslotparam(const char *name, float x, float y, float z, float w, int flags = 0)
    2146              : {
    2147            2 :     if(name)
    2148              :     {
    2149            2 :         name = getshaderparamname(name);
    2150              :     }
    2151            2 :     for(SlotShaderParam &i : slotparams)
    2152              :     {
    2153            1 :         if(i.name==name)
    2154              :         {
    2155            1 :             i.val[0] = x;
    2156            1 :             i.val[1] = y;
    2157            1 :             i.val[2] = z;
    2158            1 :             i.val[3] = w;
    2159            1 :             i.flags |= flags;
    2160            1 :             return;
    2161              :         }
    2162              :     }
    2163            1 :     SlotShaderParam param = {name, SIZE_MAX, flags, {x, y, z, w}};
    2164            1 :     slotparams.push_back(param);
    2165              : }
    2166              : 
    2167            0 : void cleanupshaders()
    2168              : {
    2169            0 :     cleanuppostfx(true);
    2170              : 
    2171            0 :     loadedshaders = false;
    2172            0 :     nullshader = hudshader = hudnotextureshader = nullptr;
    2173            0 :     for(auto &[k, s] : shaders)
    2174              :     {
    2175            0 :         s.cleanup();
    2176              :     }
    2177            0 :     Shader::lastshader = nullptr;
    2178            0 :     glUseProgram(0);
    2179            0 : }
    2180              : 
    2181            0 : void Shader::reusecleanup()
    2182              : {
    2183            0 :     if((reusevs && reusevs->invalid()) ||
    2184            0 :        (reuseps && reuseps->invalid()) ||
    2185            0 :        !compile())
    2186              :     {
    2187            0 :         cleanup(true);
    2188              :     }
    2189            0 : }
    2190              : 
    2191            0 : void reloadshaders()
    2192              : {
    2193            0 :     identflags &= ~Idf_Persist;
    2194            0 :     loadshaders();
    2195            0 :     identflags |= Idf_Persist;
    2196            0 :     linkslotshaders();
    2197            0 :     for(auto &[k, s] : shaders)
    2198              :     {
    2199            0 :         if(!s.standard && s.loaded() && !s.variantshader)
    2200              :         {
    2201            0 :             DEF_FORMAT_STRING(info, "shader %s", s.name);
    2202            0 :             renderprogress(0.0, info);
    2203            0 :             if(!s.compile())
    2204              :             {
    2205            0 :                 s.cleanup(true);
    2206              :             }
    2207            0 :             for(Shader *&v : s.variants)
    2208              :             {
    2209            0 :                 v->reusecleanup();
    2210              :             }
    2211              :         }
    2212            0 :         if(s.forced && s.deferred())
    2213              :         {
    2214            0 :             s.force();
    2215              :         }
    2216              :     }
    2217            0 : }
    2218              : 
    2219            1 : void resetshaders()
    2220              : {
    2221            1 :     if(!glslversion)
    2222              :     {
    2223            1 :         conoutf(Console_Error, "Cannot reset GLSL shaders without GLSL initialized, operation not performed");
    2224            1 :         return;
    2225              :     }
    2226            0 :     clearchanges(Change_Shaders);
    2227              : 
    2228            0 :     cleanuplights();
    2229            0 :     cleanupmodels();
    2230            0 :     cleanupshaders();
    2231            0 :     setupshaders();
    2232            0 :     initgbuffer();
    2233            0 :     reloadshaders();
    2234            0 :     rootworld.allchanged(true);
    2235            0 :     glerror();
    2236              : }
    2237              : 
    2238              : FVAR(blursigma, 0.005f, 0.5f, 2.0f);
    2239              : 
    2240              : /*
    2241              :  * radius: sets number of weights & offsets for blurring to be made
    2242              :  * weights: array of length at least radius + 1
    2243              :  * offsets: array of length at least radius + 1
    2244              :  */
    2245            0 : void setupblurkernel(int radius, float *weights, float *offsets)
    2246              : {
    2247            0 :     if(radius<1 || radius>maxblurradius)
    2248              :     {
    2249            0 :         return;
    2250              :     }
    2251            0 :     float sigma = blursigma*2*radius,
    2252            0 :           total = 1.0f/sigma;
    2253            0 :     weights[0] = total;
    2254            0 :     offsets[0] = 0;
    2255              :     // rely on bilinear filtering to sample 2 pixels at once
    2256              :     // transforms a*X + b*Y into (u+v)*[X*u/(u+v) + Y*(1 - u/(u+v))]
    2257            0 :     for(int i = 0; i < radius; ++i)
    2258              :     {
    2259            0 :         float weight1 = std::exp(-((2*i)*(2*i)) / (2*sigma*sigma)) / sigma,
    2260            0 :               weight2 = std::exp(-((2*i+1)*(2*i+1)) / (2*sigma*sigma)) / sigma,
    2261            0 :               scale = weight1 + weight2,
    2262            0 :               offset = 2*i+1 + weight2 / scale;
    2263            0 :         weights[i+1] = scale;
    2264            0 :         offsets[i+1] = offset;
    2265            0 :         total += 2*scale;
    2266              :     }
    2267            0 :     for(int i = 0; i < radius+1; ++i)
    2268              :     {
    2269            0 :         weights[i] /= total;
    2270              :     }
    2271            0 :     for(int i = radius+1; i <= maxblurradius; i++)
    2272              :     {
    2273            0 :         weights[i] = offsets[i] = 0;
    2274              :     }
    2275              : }
    2276              : 
    2277            0 : void setblurshader(int pass, int size, int radius, const float *weights, const float *offsets, GLenum target)
    2278              : {
    2279            0 :     if(radius<1 || radius>maxblurradius)
    2280              :     {
    2281            0 :         return;
    2282              :     }
    2283              :     static Shader *blurshader[7][2] = { { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr } },
    2284              :                   *blurrectshader[7][2] = { { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr } };
    2285            0 :     Shader *&s = (target == GL_TEXTURE_RECTANGLE ? blurrectshader : blurshader)[radius-1][pass];
    2286            0 :     if(!s)
    2287              :     {
    2288            0 :         DEF_FORMAT_STRING(name, "blur%c%d%s", 'x'+pass, radius, target == GL_TEXTURE_RECTANGLE ? "rect" : "");
    2289            0 :         s = lookupshaderbyname(name);
    2290              :     }
    2291            0 :     s->set();
    2292            0 :     LOCALPARAMV(weights, weights, maxblurradius+1);
    2293              :     std::array<float, maxblurradius+1> scaledoffsets;
    2294            0 :     for(int k = 0; k < maxblurradius+1; ++k)
    2295              :     {
    2296            0 :         scaledoffsets[k] = offsets[k]/size;
    2297              :     }
    2298            0 :     LOCALPARAMV(offsets, scaledoffsets.data(), maxblurradius+1);
    2299              : }
    2300              : 
    2301            1 : void initshadercmds()
    2302              : {
    2303            1 :     addcommand("defershader", reinterpret_cast<identfun>(defershader), "iss", Id_Command);
    2304            2 :     addcommand("forceshader", reinterpret_cast<identfun>(+[](const char *name){useshaderbyname(name);}), "s", Id_Command);
    2305            1 :     addcommand("shader", reinterpret_cast<identfun>(shader), "isss", Id_Command);
    2306            1 :     addcommand("variantshader", reinterpret_cast<identfun>(variantshader), "isissi", Id_Command);
    2307            1 :     addcommand("setshader", reinterpret_cast<identfun>(setshader), "s", Id_Command);
    2308            2 :     addcommand("isshaderdefined", reinterpret_cast<identfun>(+[](const char *name){intret(lookupshaderbyname(name) ? 1 : 0);}), "s", Id_Command);
    2309            2 :     addcommand("setshaderparam", reinterpret_cast<identfun>(+[](char *name, float *x, float *y, float *z, float *w){addslotparam(name, *x, *y, *z, *w);}), "sfFFf", Id_Command);
    2310            2 :     addcommand("reuseuniformparam", reinterpret_cast<identfun>(+[](char *name, float *x, float *y, float *z, float *w){addslotparam(name, *x, *y, *z, *w, SlotShaderParam::REUSE);}), "sfFFf", Id_Command);
    2311            1 :     addcommand("resetshaders", reinterpret_cast<identfun>(resetshaders), "", Id_Command);
    2312              : 
    2313            1 :     addcommand("variantshader_new", reinterpret_cast<identfun>(variantshader_new), "isiie", Id_Command);
    2314            1 :     addcommand("shader_new", reinterpret_cast<identfun>(shader_new), "ise", Id_Command);
    2315            1 :     addcommand("shader_define", reinterpret_cast<identfun>(shader_define), "ss", Id_Command);
    2316            1 :     addcommand("shader_source", reinterpret_cast<identfun>(shader_source), "ss", Id_Command);
    2317            1 :     addcommand("shader_include_vs", reinterpret_cast<identfun>(shader_include_vs), "s", Id_Command);
    2318            1 :     addcommand("shader_include_fs", reinterpret_cast<identfun>(shader_include_fs), "s", Id_Command);
    2319            1 :     addcommand("shader_get_defines", reinterpret_cast<identfun>(shader_get_defines), "", Id_Command);
    2320            1 :     addcommand("shader_get_includes_vs", reinterpret_cast<identfun>(shader_get_includes_vs), "", Id_Command);
    2321            1 :     addcommand("shader_get_includes_fs", reinterpret_cast<identfun>(shader_get_includes_fs), "", Id_Command);
    2322              : 
    2323            1 :     initpostfxcmds();
    2324            1 : }
        

Generated by: LCOV version 2.0-1