LCOV - code coverage report
Current view: top level - engine/render - shader.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 22.3 % 1169 261
Test Date: 2026-07-09 06:56:18 Functions: 43.2 % 95 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              : static VAR(maxfsuniforms, 1, 0, 0);
      48              : static VAR(mintexoffset, 1, 0, 0);
      49              : static VAR(maxtexoffset, 1, 0, 0);
      50              : static 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 :     std::unordered_map<std::string, Shader>::iterator 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              :     std::array<const char *, 16> parts;
     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(size_t i = 0; i < 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.data()), 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.data(), 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.data(), 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(size_t 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(size_t j = 0; j < uniformlocs.size(); j++)
     464              :     {
     465            0 :         if(uniformlocs[j].loc == loc)
     466              :         {
     467            0 :             return;
     468              :         }
     469              :     }
     470            0 :     for(size_t j = 0; j < globalparams.size(); j++)
     471              :     {
     472            0 :         if(globalparams[j].loc == loc)
     473              :         {
     474            0 :             return;
     475              :         }
     476              :     }
     477            0 :     for(size_t 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 : void GlobalShaderParamState::changed()
     556              : {
     557            0 :     if(++nextversion < 0)
     558              :     {
     559            0 :         resetversions();
     560              :     }
     561            0 :     version = nextversion;
     562            0 : }
     563              : 
     564            0 : static const float *findslotparam(const Slot &s, const char *name, const float *noval = nullptr)
     565              : {
     566            0 :     for(const SlotShaderParam &param : s.params)
     567              :     {
     568            0 :         if(name == param.name)
     569              :         {
     570            0 :             return &param.val[0];
     571              :         }
     572              :     }
     573            0 :     for(const SlotShaderParamState &param : s.shader->defaultparams)
     574              :     {
     575            0 :         if(name == param.name)
     576              :         {
     577            0 :             return &param.val[0];
     578              :         }
     579              :     }
     580            0 :     return noval;
     581              : }
     582              : 
     583            0 : static const float *findslotparam(const VSlot &s, const char *name, const float *noval = nullptr)
     584              : {
     585            0 :     for(const SlotShaderParam &param : s.params)
     586              :     {
     587            0 :         if(name == param.name)
     588              :         {
     589            0 :             return &param.val[0];
     590              :         }
     591              :     }
     592            0 :     return findslotparam(*s.slot, name, noval);
     593              : }
     594              : 
     595            0 : static void setslotparam(const SlotShaderParamState &l, const float *val)
     596              : {
     597            0 :     switch(l.format)
     598              :     {
     599            0 :         case GL_BOOL:
     600              :         case GL_FLOAT:
     601              :         {
     602            0 :             glUniform1fv(l.loc, 1, val);
     603            0 :             break;
     604              :         }
     605            0 :         case GL_BOOL_VEC2:
     606              :         case GL_FLOAT_VEC2:
     607              :         {
     608            0 :             glUniform2fv(l.loc, 1, val);
     609            0 :             break;
     610              :         }
     611            0 :         case GL_BOOL_VEC3:
     612              :         case GL_FLOAT_VEC3:
     613              :         {
     614            0 :             glUniform3fv(l.loc, 1, val);
     615            0 :             break;
     616              :         }
     617            0 :         case GL_BOOL_VEC4:
     618              :         case GL_FLOAT_VEC4:
     619              :         {
     620            0 :             glUniform4fv(l.loc, 1, val);
     621            0 :             break;
     622              :         }
     623            0 :         case GL_INT:
     624              :         {
     625            0 :             glUniform1i(l.loc, static_cast<int>(val[0]));
     626            0 :             break;
     627              :         }
     628            0 :         case GL_INT_VEC2:
     629              :         {
     630            0 :             glUniform2i(l.loc, static_cast<int>(val[0]), static_cast<int>(val[1]));
     631            0 :             break;
     632              :         }
     633            0 :         case GL_INT_VEC3:
     634              :         {
     635            0 :             glUniform3i(l.loc, static_cast<int>(val[0]), static_cast<int>(val[1]), static_cast<int>(val[2]));
     636            0 :             break;
     637              :         }
     638            0 :         case GL_INT_VEC4:
     639              :         {
     640            0 :             glUniform4i(l.loc, static_cast<int>(val[0]), static_cast<int>(val[1]), static_cast<int>(val[2]), static_cast<int>(val[3]));
     641            0 :             break;
     642              :         }
     643            0 :         case GL_UNSIGNED_INT:
     644              :         {
     645            0 :             glUniform1ui(l.loc, static_cast<uint>(val[0]));
     646            0 :             break;
     647              :         }
     648            0 :         case GL_UNSIGNED_INT_VEC2:
     649              :         {
     650            0 :             glUniform2ui(l.loc, static_cast<uint>(val[0]), static_cast<uint>(val[1]));
     651            0 :             break;
     652              :         }
     653            0 :         case GL_UNSIGNED_INT_VEC3:
     654              :         {
     655            0 :             glUniform3ui(l.loc, static_cast<uint>(val[0]), static_cast<uint>(val[1]), static_cast<uint>(val[2]));
     656            0 :             break;
     657              :         }
     658            0 :         case GL_UNSIGNED_INT_VEC4:
     659              :         {
     660            0 :             glUniform4ui(l.loc, static_cast<uint>(val[0]), static_cast<uint>(val[1]), static_cast<uint>(val[2]), static_cast<uint>(val[3]));
     661            0 :             break;
     662              :         }
     663              :     }
     664            0 : }
     665              : 
     666            0 : static void setslotparam(const SlotShaderParamState& l, uint& mask, uint i, const float* val)
     667              : {
     668            0 :     if(!(mask&(1<<i)))
     669              :     {
     670            0 :         mask |= 1<<i;
     671            0 :         setslotparam(l, val);
     672              :     }
     673            0 : }
     674              : 
     675            0 : static void setslotparams(uint& unimask, const std::vector<SlotShaderParamState>& defaultparams)
     676              : {
     677            0 :     for(const SlotShaderParam &p : slotparams)
     678              :     {
     679            0 :         if(!(defaultparams.size() > p.loc))
     680              :         {
     681            0 :             continue;
     682              :         }
     683            0 :         const SlotShaderParamState &l = defaultparams.at(p.loc);
     684            0 :         setslotparam(l, unimask, p.loc, p.val);
     685              :     }
     686            0 : }
     687              : 
     688            0 : static void setdefaultparams(const std::vector<SlotShaderParamState>& defaultparams, uint& unimask)
     689              : {
     690            0 :     for(size_t i = 0; i < defaultparams.size(); i++)
     691              :     {
     692            0 :         const SlotShaderParamState &l = defaultparams.at(i);
     693            0 :         setslotparam(l, unimask, i, &l.val[0]);
     694              :     }
     695            0 : }
     696              : 
     697              : //shader
     698              : 
     699            2 : 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)
     700              : {
     701            2 : }
     702              : 
     703            4 : Shader::~Shader()
     704              : {
     705            4 :     delete[] name;
     706            4 :     delete[] vsstr;
     707            4 :     delete[] psstr;
     708            4 :     delete[] defer;
     709            4 :     delete[] variantrows;
     710            4 : }
     711              : 
     712            0 : void Shader::flushparams()
     713              : {
     714            0 :     if(!used)
     715              :     {
     716            0 :         allocglslactiveuniforms();
     717            0 :         used = true;
     718              :     }
     719            0 :     for(GlobalShaderParamUse &i : globalparams)
     720              :     {
     721            0 :         i.flush();
     722              :     }
     723            0 : }
     724              : 
     725            0 : bool Shader::invalid() const
     726              : {
     727            0 :     return (type & Shader_Invalid) != 0;
     728              : }
     729            0 : bool Shader::deferred() const
     730              : {
     731            0 :     return (type & Shader_Deferred) != 0;
     732              : }
     733            5 : bool Shader::loaded() const
     734              : {
     735            5 :     return !(type&(Shader_Deferred | Shader_Invalid));
     736              : }
     737              : 
     738            0 : bool Shader::isdynamic() const
     739              : {
     740            0 :     return (type & Shader_Dynamic) != 0;
     741              : }
     742              : 
     743            0 : int Shader::numvariants(int row) const
     744              : {
     745            0 :     if(row < 0 || row >= maxvariantrows || !variantrows)
     746              :     {
     747            0 :         return 0;
     748              :     }
     749            0 :     return variantrows[row+1] - variantrows[row];
     750              : }
     751              : 
     752            0 : const Shader *Shader::getvariant(int col, int row) const
     753              : {
     754            0 :     if(row < 0 || row >= maxvariantrows || col < 0 || !variantrows)
     755              :     {
     756            0 :         return nullptr;
     757              :     }
     758            0 :     int start = variantrows[row],
     759            0 :         end = variantrows[row+1];
     760            0 :     return col < end - start ? variants[start + col] : nullptr;
     761              : }
     762              : 
     763            0 : void Shader::addvariant(int row, Shader *s)
     764              : {
     765            0 :     if(row < 0 || row >= maxvariantrows || variants.size() >= USHRT_MAX)
     766              :     {
     767            0 :         return;
     768              :     }
     769            0 :     if(!variantrows)
     770              :     {
     771            0 :         variantrows = new ushort[maxvariantrows+1];
     772            0 :         std::memset(variantrows, 0, (maxvariantrows+1)*sizeof(ushort));
     773              :     }
     774            0 :     variants.insert(variants.begin() + variantrows[row+1], s);
     775            0 :     for(int i = row+1; i <= maxvariantrows; ++i)
     776              :     {
     777            0 :         ++variantrows[i];
     778              :     }
     779              : }
     780              : 
     781            0 : void Shader::setvariant_(int col, int row)
     782              : {
     783            0 :     Shader *s = this;
     784            0 :     if(variantrows)
     785              :     {
     786            0 :         int start = variantrows[row],
     787            0 :             end   = variantrows[row+1];
     788            0 :         for(col = std::min(start + col, end-1); col >= start; --col)
     789              :         {
     790            0 :             if(!variants[col]->invalid())
     791              :             {
     792            0 :                 s = variants[col];
     793            0 :                 break;
     794              :             }
     795              :         }
     796              :     }
     797            0 :     if(lastshader!=s)
     798              :     {
     799            0 :         s->bindprograms();
     800              :     }
     801            0 : }
     802              : 
     803            0 : void Shader::setvariant(int col, int row)
     804              : {
     805            0 :     if(!loaded())
     806              :     {
     807            0 :         return;
     808              :     }
     809            0 :     setvariant_(col, row);
     810            0 :     lastshader->flushparams();
     811              : }
     812              : 
     813            0 : void Shader::setvariantandslot(int col, int row)
     814              : {
     815            0 :     if(!loaded())
     816              :     {
     817            0 :         return;
     818              :     }
     819            0 :     setvariant_(col, row);
     820            0 :     lastshader->flushparams();
     821            0 :     lastshader->setslotparams();
     822              : }
     823              : 
     824            0 : void Shader::setvariant(int col, int row, const Slot &slot, const VSlot &vslot)
     825              : {
     826            0 :     if(!loaded())
     827              :     {
     828            0 :         return;
     829              :     }
     830            0 :     setvariant_(col, row);
     831            0 :     lastshader->flushparams();
     832            0 :     lastshader->setslotparams(slot, vslot);
     833              : }
     834              : 
     835            0 : void Shader::set_()
     836              : {
     837            0 :     if(lastshader!=this)
     838              :     {
     839            0 :         bindprograms();
     840              :     }
     841            0 : }
     842              : 
     843            0 : void Shader::set()
     844              : {
     845            0 :     if(!loaded())
     846              :     {
     847            0 :         return;
     848              :     }
     849            0 :     set_();
     850            0 :     lastshader->flushparams();
     851              : }
     852              : 
     853            0 : void Shader::setslot()
     854              : {
     855            0 :     if(!loaded())
     856              :     {
     857            0 :         return;
     858              :     }
     859            0 :     set_();
     860            0 :     lastshader->flushparams();
     861            0 :     lastshader->setslotparams();
     862              : }
     863              : 
     864            0 : void Shader::set(const Slot &slot, const VSlot &vslot)
     865              : {
     866            0 :     if(!loaded())
     867              :     {
     868            0 :         return;
     869              :     }
     870            0 :     set_();
     871            0 :     lastshader->flushparams();
     872            0 :     lastshader->setslotparams(slot, vslot);
     873              : }
     874              : 
     875            0 : void Shader::setslotparams()
     876              : {
     877            0 :     uint unimask = 0;
     878            0 :     ::setslotparams(unimask, defaultparams);
     879            0 :     setdefaultparams(defaultparams, unimask);
     880            0 : }
     881              : 
     882            0 : void Shader::setslotparams(const Slot &slot, const VSlot &vslot)
     883              : {
     884              :     static bool thrown = false; //only throw error message once (will spam per frame otherwise)
     885            0 :     uint unimask = 0;
     886            0 :     if(vslot.slot == &slot)
     887              :     {
     888            0 :         ::setslotparams(unimask, defaultparams);
     889            0 :         for(size_t i = 0; i < slot.params.size(); i++)
     890              :         {
     891            0 :             const SlotShaderParam &p = slot.params.at(i);
     892            0 :             if(!(defaultparams.size() > p.loc))
     893              :             {
     894            0 :                 continue;
     895              :             }
     896            0 :             if(p.loc == SIZE_MAX)
     897              :             {
     898            0 :                 if(!thrown)
     899              :                 {
     900            0 :                     std::printf("Invalid slot shader param index: some slot shaders may not be in use\n");
     901            0 :                     thrown = true;
     902              :                 }
     903              :             }
     904            0 :             else if(!(unimask&(1<<p.loc)))
     905              :             {
     906            0 :                 const SlotShaderParamState &l = defaultparams.at(p.loc);
     907            0 :                 unimask |= 1<<p.loc;
     908            0 :                 setslotparam(l, p.val);
     909              :             }
     910              :         }
     911            0 :         setdefaultparams(defaultparams, unimask);
     912              :     }
     913              :     else
     914              :     {
     915            0 :         ::setslotparams(unimask, defaultparams);
     916            0 :         for(size_t i = 0; i < defaultparams.size(); i++)
     917              :         {
     918            0 :             const SlotShaderParamState &l = defaultparams.at(i);
     919            0 :             setslotparam(l, unimask, i, l.flags&SlotShaderParam::REUSE ? findslotparam(vslot, l.name.c_str(), &l.val[0]) : &l.val[0]);
     920              :         }
     921              :     }
     922            0 : }
     923              : 
     924            0 : void Shader::bindprograms()
     925              : {
     926            0 :     if(this == lastshader || !loaded())
     927              :     {
     928            0 :         return;
     929              :     }
     930            0 :     glUseProgram(program);
     931            0 :     lastshader = this;
     932              : }
     933              : 
     934            2 : bool Shader::compile()
     935              : {
     936            2 :     if(!vsstr)
     937              :     {
     938            0 :         vsobj = !reusevs || reusevs->invalid() ? 0 : reusevs->vsobj;
     939              :     }
     940              :     else
     941              :     {
     942            2 :         compileglslshader(GL_VERTEX_SHADER,   vsobj, vsstr, name, debugshader || !variantshader);
     943              :     }
     944            2 :     if(!psstr)
     945              :     {
     946            0 :         psobj = !reuseps || reuseps->invalid() ? 0 : reuseps->psobj;
     947              :     }
     948              :     else
     949              :     {
     950            2 :         compileglslshader(GL_FRAGMENT_SHADER, psobj, psstr, name, debugshader || !variantshader);
     951              :     }
     952            2 :     linkglslprogram(!variantshader);
     953            2 :     return program!=0;
     954              : }
     955              : 
     956            2 : void Shader::cleanup(bool full)
     957              : {
     958            2 :     used = false;
     959            2 :     if(vsobj)
     960              :     {
     961            0 :         if(!reusevs)
     962              :         {
     963            0 :             glDeleteShader(vsobj); vsobj = 0;
     964              :         }
     965              :     }
     966            2 :     if(psobj)
     967              :     {
     968            0 :         if(!reuseps)
     969              :         {
     970            0 :             glDeleteShader(psobj);
     971            0 :             psobj = 0;
     972              :         }
     973              :     }
     974            2 :     if(program)
     975              :     {
     976            0 :         glDeleteProgram(program);
     977            0 :         program = 0;
     978              :     }
     979            2 :     localparams.clear();
     980            2 :     localparamremap.clear();
     981            2 :     globalparams.clear();
     982            2 :     if(standard || full)
     983              :     {
     984            2 :         type = Shader_Invalid;
     985              : 
     986            2 :         delete[] vsstr;
     987            2 :         delete[] psstr;
     988            2 :         delete[] defer;
     989              : 
     990            2 :         vsstr = nullptr;
     991            2 :         psstr = nullptr;
     992            2 :         defer = nullptr;
     993              : 
     994            2 :         variants.clear();
     995              : 
     996            2 :         delete[] variantrows;
     997            2 :         variantrows = nullptr;
     998              : 
     999            2 :         defaultparams.clear();
    1000            2 :         attriblocs.clear();
    1001            2 :         uniformlocs.clear();
    1002            2 :         reusevs = reuseps = nullptr;
    1003              :     }
    1004              :     else
    1005              :     {
    1006            0 :         for(SlotShaderParamState &i : defaultparams)
    1007              :         {
    1008            0 :             i.loc = -1;
    1009              :         }
    1010              :     }
    1011            2 : }
    1012              : //ShaderParamBinding
    1013              : 
    1014            1 : ShaderParamBinding::ShaderParamBinding(GLint loc, GLsizei size, GLenum format) :
    1015            1 :     loc(loc), size(size), format(format)
    1016              :     {
    1017            1 :     }
    1018              : 
    1019              : // globalshaderparamuse
    1020              : 
    1021            0 : GlobalShaderParamUse::GlobalShaderParamUse(GLint loc, GLsizei size, GLenum format, const GlobalShaderParamState *param, int version) :
    1022            0 :     ShaderParamBinding(loc, size, format),  param(param), version(version)
    1023              :     {
    1024            0 :     }
    1025              : 
    1026            0 : void GlobalShaderParamUse::flush()
    1027              : {
    1028            0 :     if(version == param->version)
    1029              :     {
    1030            0 :         return;
    1031              :     }
    1032            0 :     switch(format)
    1033              :     {
    1034            0 :         case GL_BOOL:
    1035              :         case GL_FLOAT:
    1036              :         {
    1037            0 :             glUniform1fv(loc, size, param->fval.data());
    1038            0 :             break;
    1039              :         }
    1040            0 :         case GL_BOOL_VEC2:
    1041              :         case GL_FLOAT_VEC2:
    1042              :         {
    1043            0 :             glUniform2fv(loc, size, param->fval.data());
    1044            0 :             break;
    1045              :         }
    1046            0 :         case GL_BOOL_VEC3:
    1047              :         case GL_FLOAT_VEC3:
    1048              :         {
    1049            0 :             glUniform3fv(loc, size, param->fval.data());
    1050            0 :             break;
    1051              :         }
    1052            0 :         case GL_BOOL_VEC4:
    1053              :         case GL_FLOAT_VEC4:
    1054              :         {
    1055            0 :             glUniform4fv(loc, size, param->fval.data());
    1056            0 :             break;
    1057              :         }
    1058            0 :         case GL_INT:
    1059              :         {
    1060            0 :             glUniform1iv(loc, size, param->ival.data());
    1061            0 :             break;
    1062              :         }
    1063            0 :         case GL_INT_VEC2:
    1064              :         {
    1065            0 :             glUniform2iv(loc, size, param->ival.data());
    1066            0 :             break;
    1067              :         }
    1068            0 :         case GL_INT_VEC3:
    1069              :         {
    1070            0 :             glUniform3iv(loc, size, param->ival.data());
    1071            0 :             break;
    1072              :         }
    1073            0 :         case GL_INT_VEC4:
    1074              :         {
    1075            0 :             glUniform4iv(loc, size, param->ival.data());
    1076            0 :             break;
    1077              :         }
    1078            0 :         case GL_UNSIGNED_INT:
    1079              :         {
    1080            0 :             glUniform1uiv(loc, size, param->uval.data());
    1081            0 :             break;
    1082              :         }
    1083            0 :         case GL_UNSIGNED_INT_VEC2:
    1084              :         {
    1085            0 :             glUniform2uiv(loc, size, param->uval.data());
    1086            0 :             break;
    1087              :         }
    1088            0 :         case GL_UNSIGNED_INT_VEC3:
    1089              :         {
    1090            0 :             glUniform3uiv(loc, size, param->uval.data());
    1091            0 :             break;
    1092              :         }
    1093            0 :         case GL_UNSIGNED_INT_VEC4:
    1094              :         {
    1095            0 :             glUniform4uiv(loc, size, param->uval.data());
    1096            0 :             break;
    1097              :         }
    1098            0 :         case GL_FLOAT_MAT2:
    1099              :         {
    1100            0 :             glUniformMatrix2fv(loc, 1, GL_FALSE, param->fval.data());
    1101            0 :             break;
    1102              :         }
    1103            0 :         case GL_FLOAT_MAT3:
    1104              :         {
    1105            0 :             glUniformMatrix3fv(loc, 1, GL_FALSE, param->fval.data());
    1106            0 :             break;
    1107              :         }
    1108            0 :         case GL_FLOAT_MAT4:
    1109              :         {
    1110            0 :             glUniformMatrix4fv(loc, 1, GL_FALSE, param->fval.data());
    1111            0 :             break;
    1112              :         }
    1113              :     }
    1114            0 :     version = param->version;
    1115              : }
    1116              : 
    1117              : //localshaderparamstate
    1118              : 
    1119            1 : LocalShaderParamState::LocalShaderParamState(GLint loc, GLsizei size, GLenum format) :
    1120            1 :     ShaderParamBinding(loc, size, format)
    1121              : {
    1122            1 : }
    1123              : 
    1124              : 
    1125            2 : void Shader::genattriblocs(const char *vs, const Shader *reusevs)
    1126              : {
    1127              :     static int len = std::strlen("//:attrib");
    1128              :     string name;
    1129              :     int loc;
    1130            2 :     if(reusevs)
    1131              :     {
    1132            0 :         attriblocs = reusevs->attriblocs;
    1133              :     }
    1134              :     else
    1135              :     {
    1136            2 :         while((vs = std::strstr(vs, "//:attrib")))
    1137              :         {
    1138            0 :             if(std::sscanf(vs, "//:attrib %100s %d", name, &loc) == 2)
    1139              :             {
    1140            0 :                 attriblocs.emplace_back(Shader::AttribLoc(getshaderparamname(name), loc));
    1141              :             }
    1142            0 :             vs += len;
    1143              :         }
    1144              :     }
    1145            2 : }
    1146              : 
    1147              : // adds to uniformlocs vector defined uniformlocs
    1148            2 : void Shader::genuniformlocs(const char *vs, const Shader *reusevs)
    1149              : {
    1150              :     static int len = std::strlen("//:uniform");
    1151              :     string name, blockname;
    1152              :     int binding, stride;
    1153            2 :     if(reusevs)
    1154              :     {
    1155            0 :         uniformlocs = reusevs->uniformlocs;
    1156              :     }
    1157              :     else
    1158              :     {
    1159            2 :         while((vs = std::strstr(vs, "//:uniform")))
    1160              :         {
    1161            0 :             int numargs = std::sscanf(vs, "//:uniform %100s %100s %d %d", name, blockname, &binding, &stride);
    1162            0 :             if(numargs >= 3)
    1163              :             {
    1164            0 :                 uniformlocs.emplace_back(UniformLoc(getshaderparamname(name), getshaderparamname(blockname), binding, numargs >= 4 ? stride : 0));
    1165              :             }
    1166            0 :             else if(numargs >= 1)
    1167              :             {
    1168            0 :                 uniformlocs.emplace_back(UniformLoc(getshaderparamname(name)));
    1169              :             }
    1170            0 :             vs += len;
    1171              :         }
    1172              :     }
    1173            2 : }
    1174              : 
    1175            2 : static Shader *newshader(int type, std::string_view name, std::string_view vs, std::string_view ps, Shader *variant = nullptr, int row = 0)
    1176              : {
    1177            2 :     if(Shader::lastshader)
    1178              :     {
    1179            0 :         glUseProgram(0);
    1180            0 :         Shader::lastshader = nullptr;
    1181              :     }
    1182            2 :     std::unordered_map<std::string, Shader>::iterator itr = shaders.find(name.data());
    1183            2 :     Shader *exists = (itr != shaders.end()) ? &(*itr).second : nullptr;
    1184            2 :     char *rname = exists ? exists->name : newstring(name.data());
    1185            2 :     if(!exists)
    1186              :     {
    1187            0 :         itr = shaders.insert( { rname, Shader() } ).first;
    1188              :     }
    1189            2 :     Shader *retval = (*itr).second.setupshader(type, rname, ps.data(), vs.data(), variant, row);
    1190            2 :     return retval; //can be nullptr or s
    1191              : }
    1192              : 
    1193            2 : Shader *Shader::setupshader(int newtype, char *rname, const char *ps, const char *vs, Shader *variant, int row)
    1194              : {
    1195            2 :     name = rname;
    1196            2 :     vsstr = newstring(vs);
    1197            2 :     psstr = newstring(ps);
    1198              : 
    1199            2 :     delete[] defer;
    1200            2 :     defer = nullptr;
    1201              : 
    1202            2 :     type = newtype & ~(Shader_Invalid | Shader_Deferred);
    1203            2 :     variantshader = variant;
    1204            2 :     standard = standardshaders;
    1205            2 :     if(forceshaders)
    1206              :     {
    1207            2 :         forced = true;
    1208              :     }
    1209            2 :     reusevs = reuseps = nullptr;
    1210            2 :     if(variant)
    1211              :     {
    1212            0 :         int row = 0,
    1213            0 :             col = 0;
    1214            0 :         if(!vs[0] || std::sscanf(vs, "%d , %d", &row, &col) >= 1)
    1215              :         {
    1216            0 :             delete[] vsstr;
    1217            0 :             vsstr = nullptr;
    1218            0 :             reusevs = !vs[0] ? variant : variant->getvariant(col, row);
    1219              :         }
    1220            0 :         row = col = 0;
    1221            0 :         if(!ps[0] || std::sscanf(ps, "%d , %d", &row, &col) >= 1)
    1222              :         {
    1223            0 :             delete[] psstr;
    1224            0 :             psstr = nullptr;
    1225            0 :             reuseps = !ps[0] ? variant : variant->getvariant(col, row);
    1226              :         }
    1227              :     }
    1228            2 :     if(variant)
    1229              :     {
    1230            0 :         for(size_t i = 0; i < variant->defaultparams.size(); i++)
    1231              :         {
    1232            0 :             defaultparams.emplace_back(variant->defaultparams[i]);
    1233              :         }
    1234              :     }
    1235              :     else
    1236              :     {
    1237            3 :         for(size_t i = 0; i < slotparams.size(); i++)
    1238              :         {
    1239            1 :             defaultparams.emplace_back(slotparams[i]);
    1240              :         }
    1241              :     }
    1242            2 :     attriblocs.clear();
    1243            2 :     uniformlocs.clear();
    1244            2 :     genattriblocs(vs, reusevs);
    1245            2 :     genuniformlocs(vs, reusevs);
    1246            2 :     if(!compile())
    1247              :     {
    1248            2 :         cleanup(true);
    1249            2 :         if(variant)
    1250              :         {
    1251            0 :             shaders.erase(rname);
    1252              :         }
    1253            2 :         return nullptr;
    1254              :     }
    1255            0 :     if(variant)
    1256              :     {
    1257            0 :         variant->addvariant(row, this);
    1258              :     }
    1259            0 :     return this;
    1260              : }
    1261              : 
    1262              : /**
    1263              :  * @brief Returns the location of the start of the line containing "main"
    1264              :  *
    1265              :  * Returns std::string::npos if no `main` is defined for the glsl code passed.
    1266              :  *
    1267              :  * Returns the beginning of the `main` call and all following lines until the closing '}'
    1268              :  *
    1269              :  * @param s the string to parse
    1270              :  *
    1271              :  * @return std::string::npos if no main() found, or the index preceeding 'main'
    1272              :  */
    1273            2 : static size_t findglslmain(const std::string &s)
    1274              : {
    1275            2 :     size_t main = s.find("main");
    1276            2 :     if(main == std::string::npos)
    1277              :     {
    1278            2 :         return std::string::npos;
    1279              :     }
    1280            0 :     for(;main != static_cast<size_t>(-1); main--) //note reverse iteration
    1281              :     {
    1282            0 :         switch(s[main])
    1283              :         {
    1284            0 :             case '\r':
    1285              :             case '\n':
    1286              :             case ';':
    1287              :             {
    1288            0 :                 return main + 1;
    1289              :             }
    1290              :         }
    1291              :     }
    1292            0 :     return 0;
    1293              : }
    1294              : 
    1295            0 : static void gengenericvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 0)
    1296              : {
    1297            0 :     int rowoffset = 0;
    1298            0 :     bool vschanged = false,
    1299            0 :          pschanged = false;
    1300            0 :     std::vector<char> vsv, psv;
    1301            0 :     for(size_t i = 0; i < std::strlen(vs)+1; ++i)
    1302              :     {
    1303            0 :         vsv.push_back(vs[i]);
    1304              :     }
    1305            0 :     for(size_t i = 0; i < std::strlen(ps)+1; ++i)
    1306              :     {
    1307            0 :         psv.push_back(ps[i]);
    1308              :     }
    1309              : 
    1310              :     //cannot be constexpr-- strlen is not compile time
    1311              :     static const int len  = std::strlen("//:variant"),
    1312              :                      olen = std::strlen("override");
    1313            0 :     for(char *vspragma = vsv.data();; vschanged = true)
    1314              :     {
    1315            0 :         vspragma = std::strstr(vspragma, "//:variant");
    1316            0 :         if(!vspragma)
    1317              :         {
    1318            0 :             break;
    1319              :         }
    1320            0 :         if(std::sscanf(vspragma + len, "row %d", &rowoffset) == 1)
    1321              :         {
    1322            0 :             continue;
    1323              :         }
    1324            0 :         std::memset(vspragma, ' ', len);
    1325            0 :         vspragma += len;
    1326            0 :         if(!std::strncmp(vspragma, "override", olen))
    1327              :         {
    1328            0 :             std::memset(vspragma, ' ', olen);
    1329            0 :             vspragma += olen;
    1330            0 :             char *end = vspragma + std::strcspn(vspragma, "\n\r");
    1331            0 :             end += std::strspn(end, "\n\r");
    1332            0 :             int endlen = std::strcspn(end, "\n\r");
    1333            0 :             std::memset(end, ' ', endlen);
    1334              :         }
    1335            0 :     }
    1336            0 :     for(char *pspragma = psv.data();; pschanged = true)
    1337              :     {
    1338            0 :         pspragma = std::strstr(pspragma, "//:variant");
    1339            0 :         if(!pspragma)
    1340              :         {
    1341            0 :             break;
    1342              :         }
    1343            0 :         if(std::sscanf(pspragma + len, "row %d", &rowoffset) == 1)
    1344              :         {
    1345            0 :             continue;
    1346              :         }
    1347            0 :         std::memset(pspragma, ' ', len);
    1348            0 :         pspragma += len;
    1349            0 :         if(!std::strncmp(pspragma, "override", olen))
    1350              :         {
    1351            0 :             std::memset(pspragma, ' ', olen);
    1352            0 :             pspragma += olen;
    1353            0 :             char *end = pspragma + std::strcspn(pspragma, "\n\r");
    1354            0 :             end += std::strspn(end, "\n\r");
    1355            0 :             int endlen = std::strcspn(end, "\n\r");
    1356            0 :             std::memset(end, ' ', endlen);
    1357              :         }
    1358            0 :     }
    1359            0 :     row += rowoffset;
    1360            0 :     if(row < 0 || row >= maxvariantrows)
    1361              :     {
    1362            0 :         return;
    1363              :     }
    1364            0 :     int col = s.numvariants(row);
    1365            0 :     DEF_FORMAT_STRING(varname, "<variant:%d,%d>%s", col, row, sname);
    1366              :     string reuse;
    1367            0 :     if(col)
    1368              :     {
    1369            0 :         formatstring(reuse, "%d", row);
    1370              :     }
    1371              :     else
    1372              :     {
    1373            0 :         copystring(reuse, "");
    1374              :     }
    1375            0 :     newshader(s.type, varname, vschanged ? vsv.data() : reuse, pschanged ? psv.data() : reuse, &s, row);
    1376            0 : }
    1377              : 
    1378            0 : static void genfogshader(std::string &vs, std::string &ps)
    1379              : {
    1380              :     //unused PRAGMA_LEN
    1381              :     //constexpr int PRAGMA_LEN = std::string_view("//:fog").size() + 1;
    1382              : 
    1383            0 :     size_t vspragma = vs.find("//:fog"),
    1384            0 :            pspragma = ps.find("//:fog");
    1385              : 
    1386            0 :     if(vspragma == std::string::npos && pspragma == std::string::npos)
    1387              :     {
    1388            0 :         return;
    1389              :     }
    1390              : 
    1391            0 :     size_t vsmain = findglslmain(vs),
    1392            0 :            vsend  = vs.rfind('}');
    1393              : 
    1394            0 :     if(vsmain != std::string::npos && vsend != std::string::npos)
    1395              :     {
    1396            0 :         if(vs.find("lineardepth") == std::string::npos)
    1397              :         {
    1398            0 :             constexpr std::string_view FOG_PARAMS = "\nuniform vec2 lineardepthscale;\nvarying float lineardepth;\n";
    1399            0 :             constexpr std::string_view VS_FOG = "\nlineardepth = dot(lineardepthscale, gl_Position.zw);\n";
    1400              : 
    1401            0 :             vs.insert(vsend, VS_FOG);
    1402            0 :             vs.insert(vsmain, FOG_PARAMS);
    1403              :         }
    1404              :     }
    1405              : 
    1406            0 :     size_t psmain = findglslmain(ps),
    1407            0 :            psend  = ps.rfind('}');
    1408              : 
    1409            0 :     if(psmain != std::string::npos && psend != std::string::npos)
    1410              :     {
    1411            0 :         std::string params;
    1412              : 
    1413            0 :         if(ps.find("lineardepth") == std::string::npos)
    1414              :         {
    1415            0 :             params = "\nvarying float lineardepth;\n";
    1416              :         }
    1417              : 
    1418              :         std::string fogparams =
    1419              :             "\nuniform vec3 fogcolor;\n"
    1420              :             "uniform vec2 fogdensity;\n"
    1421              :             "uniform vec4 radialfogscale;\n"
    1422            0 :             "#define fogcoord lineardepth*length(vec3(gl_FragCoord.xy*radialfogscale.xy + radialfogscale.zw, 1.0))\n";
    1423              : 
    1424            0 :         params += fogparams;
    1425              : 
    1426            0 :         std::string psfog = "\nfragcolor.rgb = mix((fogcolor).rgb, fragcolor.rgb, clamp(exp2(fogcoord*-fogdensity.x)*fogdensity.y, 0.0, 1.0));\n";
    1427            0 :         ps.insert(psend, psfog);
    1428            0 :         ps.insert(psmain, params);
    1429            0 :     }
    1430              : }
    1431              : 
    1432            1 : static void genuniformdefs(std::string &vs, std::string &ps, const Shader *variant = nullptr)
    1433              : {
    1434            1 :     if(variant ? variant->defaultparams.empty() : slotparams.empty())
    1435              :     {
    1436            1 :         return;
    1437              :     }
    1438              : 
    1439            1 :     size_t vsmain = findglslmain(vs),
    1440            1 :            psmain = findglslmain(ps);
    1441              : 
    1442            1 :     if(vsmain == std::string::npos || psmain == std::string::npos)
    1443              :     {
    1444            1 :         return;
    1445              :     }
    1446              : 
    1447            0 :     std::string params;
    1448            0 :     if(variant)
    1449              :     {
    1450            0 :         for(const auto &param : variant->defaultparams)
    1451              :         {
    1452            0 :             DEF_FORMAT_STRING(uni, "\nuniform vec4 %s;\n", param.name.c_str());
    1453            0 :             params += uni;
    1454              :         }
    1455              :     }
    1456              :     else
    1457              :     {
    1458            0 :         for(const auto &param : slotparams)
    1459              :         {
    1460            0 :             DEF_FORMAT_STRING(uni, "\nuniform vec4 %s;\n", param.name);
    1461            0 :             params += uni;
    1462              :         }
    1463              :     }
    1464              : 
    1465            0 :     vs.insert(vsmain, params);
    1466            0 :     ps.insert(psmain, params);
    1467            0 : }
    1468              : 
    1469            0 : void setupshaders()
    1470              : {
    1471            0 :     if(!glslversion)
    1472              :     {
    1473            0 :         conoutf(Console_Error, "Cannot setup GLSL shaders without GLSL initialized, operation not performed");
    1474            0 :         return;
    1475              :     }
    1476              :     GLint val;
    1477            0 :     glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &val);
    1478            0 :     maxvsuniforms = val/4;
    1479            0 :     glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &val);
    1480            0 :     maxfsuniforms = val/4;
    1481              : 
    1482            0 :     glGetIntegerv(GL_MIN_PROGRAM_TEXEL_OFFSET, &val);
    1483            0 :     mintexoffset = val;
    1484            0 :     glGetIntegerv(GL_MAX_PROGRAM_TEXEL_OFFSET, &val);
    1485            0 :     maxtexoffset = val;
    1486              : 
    1487            0 :     standardshaders = true;
    1488            0 :     nullshader = newshader(0, "<init>null",
    1489              :         "attribute vec4 vvertex;\n"
    1490              :         "void main(void) {\n"
    1491              :         "   gl_Position = vvertex;\n"
    1492              :         "}\n",
    1493              :         "fragdata(0) vec4 fragcolor;\n"
    1494              :         "void main(void) {\n"
    1495              :         "   fragcolor = vec4(1.0, 0.0, 1.0, 1.0);\n"
    1496              :         "}\n");
    1497            0 :     hudshader = newshader(0, "<init>hud",
    1498              :         "attribute vec4 vvertex, vcolor;\n"
    1499              :         "attribute vec2 vtexcoord0;\n"
    1500              :         "uniform mat4 hudmatrix;\n"
    1501              :         "varying vec2 texcoord0;\n"
    1502              :         "varying vec4 colorscale;\n"
    1503              :         "void main(void) {\n"
    1504              :         "    gl_Position = hudmatrix * vvertex;\n"
    1505              :         "    texcoord0 = vtexcoord0;\n"
    1506              :         "    colorscale = vcolor;\n"
    1507              :         "}\n",
    1508              :         "uniform sampler2D tex0;\n"
    1509              :         "varying vec2 texcoord0;\n"
    1510              :         "varying vec4 colorscale;\n"
    1511              :         "fragdata(0) vec4 fragcolor;\n"
    1512              :         "void main(void) {\n"
    1513              :         "    vec4 color = texture2D(tex0, texcoord0);\n"
    1514              :         "    fragcolor = colorscale * color;\n"
    1515              :         "}\n");
    1516            0 :     hudtextshader = newshader(0, "<init>hudtext",
    1517              :         "attribute vec4 vvertex, vcolor;\n"
    1518              :         "attribute vec2 vtexcoord0;\n"
    1519              :         "uniform mat4 hudmatrix;\n"
    1520              :         "varying vec2 texcoord0;\n"
    1521              :         "varying vec4 colorscale;\n"
    1522              :         "void main(void) {\n"
    1523              :         "    gl_Position = hudmatrix * vvertex;\n"
    1524              :         "    texcoord0 = vtexcoord0;\n"
    1525              :         "    colorscale = vcolor;\n"
    1526              :         "}\n",
    1527              :         "uniform sampler2D tex0;\n"
    1528              :         "uniform vec4 textparams;\n"
    1529              :         "varying vec2 texcoord0;\n"
    1530              :         "varying vec4 colorscale;\n"
    1531              :         "fragdata(0) vec4 fragcolor;\n"
    1532              :         "void main(void) {\n"
    1533              :         "    float dist = texture2D(tex0, texcoord0).r;\n"
    1534              :         "    float border = smoothstep(textparams.x, textparams.y, dist);\n"
    1535              :         "    float outline = smoothstep(textparams.z, textparams.w, dist);\n"
    1536              :         "    fragcolor = vec4(colorscale.rgb * outline, colorscale.a * border);\n"
    1537              :         "}\n");
    1538            0 :     hudnotextureshader = newshader(0, "<init>hudnotexture",
    1539              :         "attribute vec4 vvertex, vcolor;\n"
    1540              :         "uniform mat4 hudmatrix;"
    1541              :         "varying vec4 color;\n"
    1542              :         "void main(void) {\n"
    1543              :         "    gl_Position = hudmatrix * vvertex;\n"
    1544              :         "    color = vcolor;\n"
    1545              :         "}\n",
    1546              :         "varying vec4 color;\n"
    1547              :         "fragdata(0) vec4 fragcolor;\n"
    1548              :         "void main(void) {\n"
    1549              :         "    fragcolor = color;\n"
    1550              :         "}\n");
    1551            0 :     standardshaders = false;
    1552            0 :     if(!nullshader || !hudshader || !hudtextshader || !hudnotextureshader)
    1553              :     {
    1554            0 :         fatal("failed to setup shaders");
    1555              :     }
    1556            0 :     dummyslot.shader = nullshader;
    1557              : }
    1558              : 
    1559              : static VAR(defershaders, 0, 1, 1);
    1560              : 
    1561            1 : static void defershader(const int *type, const char *name, const char *contents)
    1562              : {
    1563            1 :     std::unordered_map<std::string, Shader>::iterator itr = shaders.find(name);
    1564            1 :     const Shader *exists = (itr != shaders.end()) ? &(*itr).second : nullptr;
    1565            1 :     if(exists && !exists->invalid())
    1566              :     {
    1567            0 :         return;
    1568              :     }
    1569            1 :     if(!defershaders)
    1570              :     {
    1571            0 :         execute(contents);
    1572            0 :         return;
    1573              :     }
    1574            1 :     char *rname = exists ? exists->name : newstring(name);
    1575            1 :     if(!exists)
    1576              :     {
    1577            1 :         itr = shaders.insert( { rname, Shader() } ).first;
    1578              :     }
    1579            1 :     Shader &s = (*itr).second;
    1580            1 :     s.name = rname;
    1581            1 :     delete[] s.defer;
    1582            1 :     s.defer = newstring(contents);
    1583            1 :     s.type = Shader_Deferred | (*type & ~Shader_Invalid);
    1584            1 :     s.standard = standardshaders;
    1585              : }
    1586              : 
    1587              : 
    1588            0 : void Shader::force()
    1589              : {
    1590            0 :     if(!deferred() || !defer)
    1591              :     {
    1592            0 :         return;
    1593              :     }
    1594            0 :     char *cmd = defer;
    1595            0 :     defer = nullptr;
    1596            0 :     bool wasstandard = standardshaders,
    1597            0 :          wasforcing = forceshaders;
    1598            0 :     int oldflags = identflags;
    1599            0 :     standardshaders = standard;
    1600            0 :     forceshaders = false;
    1601            0 :     identflags &= ~Idf_Persist;
    1602            0 :     slotparams.clear();
    1603            0 :     execute(cmd);
    1604            0 :     identflags = oldflags;
    1605            0 :     forceshaders = wasforcing;
    1606            0 :     standardshaders = wasstandard;
    1607            0 :     delete[] cmd;
    1608              : 
    1609            0 :     if(deferred())
    1610              :     {
    1611            0 :         delete[] defer;
    1612            0 :         defer = nullptr;
    1613            0 :         type = Shader_Invalid;
    1614              :     }
    1615              : }
    1616              : 
    1617            0 : int Shader::uniformlocversion()
    1618              : {
    1619              :     static int version = 0;
    1620            0 :     if(++version >= 0)
    1621              :     {
    1622            0 :         return version;
    1623              :     }
    1624            0 :     version = 0;
    1625            0 :     for(auto &[k, s] : shaders)
    1626              :     {
    1627            0 :         for(UniformLoc &j : s.uniformlocs)
    1628              :         {
    1629            0 :             j.version = -1;
    1630              :         }
    1631              :     }
    1632            0 :     return version;
    1633              : }
    1634              : 
    1635            1 : Shader *useshaderbyname(std::string_view name)
    1636              : {
    1637            1 :     std::unordered_map<std::string, Shader>::iterator itr = shaders.find(name.data());
    1638            1 :     if(itr == shaders.end())
    1639              :     {
    1640            1 :         return nullptr;
    1641              :     }
    1642            0 :     Shader *s = &(*itr).second;
    1643            0 :     if(s->deferred())
    1644              :     {
    1645            0 :         s->force();
    1646              :     }
    1647            0 :     s->forced = true;
    1648            0 :     return s;
    1649              : }
    1650              : 
    1651            1 : static void shader(const int *type, const char *name, const char *vs, const char *ps)
    1652              : {
    1653            1 :     if(lookupshaderbyname(name))
    1654              :     {
    1655            0 :         return;
    1656              :     }
    1657            1 :     DEF_FORMAT_STRING(info, "shader %s", name);
    1658            1 :     renderprogress(loadprogress, info);
    1659            3 :     std::string vs_string(vs), ps_string(ps);
    1660              : 
    1661            1 :     if(!slotparams.empty())
    1662              :     {
    1663            1 :         genuniformdefs(vs_string, ps_string);
    1664              :     }
    1665              : 
    1666            1 :     if(vs_string.find("//:fog") != std::string::npos || ps_string.find("//:fog") != std::string::npos)
    1667              :     {
    1668            0 :         genfogshader(vs_string, ps_string);
    1669              :     }
    1670              : 
    1671            1 :     Shader *s = newshader(*type, name, vs_string.c_str(), ps_string.c_str());
    1672            1 :     if(s)
    1673              :     {
    1674            0 :         if(vs_string.find("//:variant") != std::string::npos || ps_string.find("//:variant") != std::string::npos)
    1675              :         {
    1676            0 :             gengenericvariant(*s, name, vs_string.c_str(), ps_string.c_str());
    1677              :         }
    1678              :     }
    1679            1 :     slotparams.clear();
    1680            1 : }
    1681              : 
    1682              : namespace ShaderMgr
    1683              : {
    1684              :     static bool adding_shader = false;
    1685              :     static std::vector<std::pair<std::string, std::string>> shader_defines;
    1686              :     static std::vector<std::string> shader_includes_vs, shader_includes_fs;
    1687              :     static std::string shader_path_vs, shader_path_fs;
    1688              : 
    1689            1 :     static std::string shader_make_defines()
    1690              :     {
    1691            1 :         std::string defines;
    1692              : 
    1693            1 :         for(const std::pair<std::string, std::string> &define : shader_defines)
    1694              :         {
    1695            0 :             defines += "#define " + define.first + " " + define.second + "\n";
    1696              :         }
    1697              : 
    1698            1 :         return defines;
    1699            0 :     }
    1700              : 
    1701            1 :     static void shader_clear_defines()
    1702              :     {
    1703            1 :         shader_defines.clear();
    1704            1 :         shader_includes_vs.clear();
    1705            1 :         shader_includes_fs.clear();
    1706            1 :         shader_path_vs.clear();
    1707            1 :         shader_path_fs.clear();
    1708            1 :     }
    1709              : 
    1710              :     //loads the vertex and pixel shaders at the indicated paths and returns them to vs, ps
    1711            1 :     static void shader_assemble(std::string vs_path, std::string fs_path, std::string &vs, std::string &ps)
    1712              :     {
    1713            1 :         std::string defines;
    1714              : 
    1715            1 :         defines = shader_make_defines();
    1716              : 
    1717            1 :         if(!shader_path_vs.empty())
    1718              :         {
    1719            0 :             const char *vs_file = loadfile(path(vs_path).c_str(), nullptr);
    1720            0 :             if(!vs_file)
    1721              :             {
    1722            0 :                 conoutf(Console_Error, "could not load vertex shader %s", vs_path.c_str());
    1723            0 :                 adding_shader = false;
    1724            0 :                 return;
    1725              :             }
    1726              : 
    1727            0 :             vs = vs_file;
    1728              : 
    1729            0 :             std::string includes;
    1730            0 :             for(const std::string &include : shader_includes_vs)
    1731              :             {
    1732            0 :                 const char *vs_include = loadfile(path(include).c_str(), nullptr);
    1733              : 
    1734            0 :                 if(!vs_include)
    1735              :                 {
    1736            0 :                     conoutf(Console_Error, "could not load vertex shader include %s", include.c_str());
    1737            0 :                     adding_shader = false;
    1738            0 :                     return;
    1739              :                 }
    1740              : 
    1741            0 :                 includes += std::string(vs_include) + "\n";
    1742              :             }
    1743              : 
    1744            0 :             vs = defines + includes + vs;
    1745            0 :         }
    1746              : 
    1747            1 :         if(!fs_path.empty())
    1748              :         {
    1749            0 :             char *ps_file = loadfile(path(fs_path).c_str(), nullptr);
    1750            0 :             if(!ps_file)
    1751              :             {
    1752            0 :                 conoutf(Console_Error, "could not load fragment shader %s", fs_path.c_str());
    1753            0 :                 adding_shader = false;
    1754            0 :                 return;
    1755              :             }
    1756              : 
    1757            0 :             ps = ps_file;
    1758              : 
    1759            0 :             std::string includes;
    1760            0 :             for(const std::string &include : shader_includes_fs)
    1761              :             {
    1762            0 :                 char *ps_include = loadfile(path(include).c_str(), nullptr);
    1763              : 
    1764            0 :                 if(!ps_include)
    1765              :                 {
    1766            0 :                     conoutf(Console_Error, "could not load fragment shader include %s", include.c_str());
    1767            0 :                     adding_shader = false;
    1768            0 :                     return;
    1769              :                 }
    1770              : 
    1771            0 :                 includes += std::string(ps_include) + "\n";
    1772              :             }
    1773              : 
    1774            0 :             ps = defines + includes + ps;
    1775            0 :         }
    1776            1 :     }
    1777              : 
    1778            1 :     static void shader_new(const int *type, const char *name, const uint *code)
    1779              :     {
    1780            1 :         if(lookupshaderbyname(name))
    1781              :         {
    1782            0 :             return;
    1783              :         }
    1784              : 
    1785            1 :         adding_shader = true;
    1786            1 :         shader_clear_defines();
    1787              : 
    1788            1 :         execute(code);
    1789              : 
    1790            1 :         std::string vs, ps;
    1791            1 :         shader_assemble(shader_path_vs, shader_path_fs, vs, ps);
    1792              : 
    1793            1 :         DEF_FORMAT_STRING(info, "shader %s", name);
    1794            1 :         renderprogress(loadprogress, info);
    1795              : 
    1796            1 :         if(!slotparams.empty())
    1797              :         {
    1798            0 :             genuniformdefs(vs, ps);
    1799              :         }
    1800              : 
    1801            1 :         if(vs.find("//:fog") != std::string::npos || ps.find("//:fog") != std::string::npos)
    1802              :         {
    1803            0 :             genfogshader(vs, ps);
    1804              :         }
    1805              : 
    1806            1 :         Shader *s = newshader(*type, name, vs.c_str(), ps.c_str());
    1807            1 :         if(s)
    1808              :         {
    1809            0 :             if(vs.find("//:variant") != std::string::npos || ps.find("//:variant") != std::string::npos)
    1810              :             {
    1811            0 :                 gengenericvariant(*s, name, vs.c_str(), ps.c_str());
    1812              :             }
    1813              :         }
    1814            1 :         slotparams.clear();
    1815              : 
    1816            1 :         adding_shader = false;
    1817            1 :     }
    1818              : 
    1819            1 :     static void shader_define(std::string_view name, std::string_view value)
    1820              :     {
    1821            1 :         if(!adding_shader)
    1822              :         {
    1823            1 :             return;
    1824              :         }
    1825              : 
    1826            0 :         shader_defines.emplace_back(name, value);
    1827              :     }
    1828              : 
    1829            1 :     static void shader_get_defines()
    1830              :     {
    1831            1 :         if(!adding_shader)
    1832              :         {
    1833            1 :             return;
    1834              :         }
    1835              : 
    1836            0 :         std::string res;
    1837              : 
    1838            0 :         for(auto &[name, value] : shader_defines)
    1839              :         {
    1840            0 :             res += " [" + name + " " + value + "]";
    1841              :         }
    1842              : 
    1843            0 :         result(res.c_str());
    1844            0 :     }
    1845              : 
    1846            1 :     static void shader_include_vs(std::string_view path)
    1847              :     {
    1848            1 :         if(!adding_shader)
    1849              :         {
    1850            1 :             return;
    1851              :         }
    1852              : 
    1853            0 :         shader_includes_vs.emplace_back(path);
    1854              :     }
    1855              : 
    1856            1 :     static void shader_get_includes_vs()
    1857              :     {
    1858            1 :         if(!adding_shader)
    1859              :         {
    1860            1 :             return;
    1861              :         }
    1862              : 
    1863            0 :         std::string res;
    1864              : 
    1865            0 :         for(const std::string &include : shader_includes_vs)
    1866              :         {
    1867            0 :             res += " \"" + include + "\"";
    1868              :         }
    1869              : 
    1870            0 :         result(res.c_str());
    1871            0 :     }
    1872              : 
    1873            1 :     static void shader_include_fs(std::string_view path)
    1874              :     {
    1875            1 :         if(!adding_shader)
    1876              :         {
    1877            1 :             return;
    1878              :         }
    1879              : 
    1880            0 :         shader_includes_fs.emplace_back(path);
    1881              :     }
    1882              : 
    1883            1 :     static void shader_get_includes_fs()
    1884              :     {
    1885            1 :         if(!adding_shader)
    1886              :         {
    1887            1 :             return;
    1888              :         }
    1889              : 
    1890            0 :         std::string res;
    1891              : 
    1892            0 :         for(const std::string &include : shader_includes_fs)
    1893              :         {
    1894            0 :             res += " \"" + include + "\"";
    1895              :         }
    1896              : 
    1897            0 :         result(res.c_str());
    1898            0 :     }
    1899              : 
    1900            1 :     static void shader_source(std::string_view vs, std::string_view fs)
    1901              :     {
    1902            1 :         if(!adding_shader)
    1903              :         {
    1904            1 :             return;
    1905              :         }
    1906              : 
    1907            0 :         shader_path_vs = vs;
    1908            0 :         shader_path_fs = fs;
    1909              :     }
    1910              : 
    1911            1 :     static void variantshader(const int *type, const char *name, const int *row, const char *vs, const char *ps, const int *maxvariants)
    1912              :     {
    1913            1 :         if(*row < 0)
    1914              :         {
    1915            0 :             shader(type, name, vs, ps);
    1916            1 :             return;
    1917              :         }
    1918            1 :         else if(*row >= maxvariantrows)
    1919              :         {
    1920            0 :             return;
    1921              :         }
    1922            1 :         Shader *s = lookupshaderbyname(name);
    1923            1 :         if(!s)
    1924              :         {
    1925            1 :             return;
    1926              :         }
    1927            0 :         DEF_FORMAT_STRING(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
    1928            0 :         if(*maxvariants > 0)
    1929              :         {
    1930            0 :             DEF_FORMAT_STRING(info, "shader %s", name);
    1931            0 :             renderprogress(std::min(s->variants.size() / static_cast<float>(*maxvariants), 1.0f), info);
    1932              :         }
    1933              : 
    1934            0 :         std::string vs_string(vs), ps_string(ps);
    1935              : 
    1936            0 :         if(!s->defaultparams.empty())
    1937              :         {
    1938            0 :             genuniformdefs(vs_string, ps_string, s);
    1939              :         }
    1940              : 
    1941            0 :         if(vs_string.find("//:fog") != std::string::npos || ps_string.find("//:fog") != std::string::npos)
    1942              :         {
    1943            0 :             genfogshader(vs_string, ps_string);
    1944              :         }
    1945              : 
    1946            0 :         const Shader *v = newshader(*type, varname, vs_string.c_str(), ps_string.c_str(), s, *row);
    1947            0 :         if(v)
    1948              :         {
    1949            0 :             if(vs_string.find("//:variant") != std::string::npos || ps_string.find("//:variant") != std::string::npos)
    1950              :             {
    1951            0 :                 gengenericvariant(*s, varname, vs_string.c_str(), ps_string.c_str(), *row);
    1952              :             }
    1953              :         }
    1954            0 :     }
    1955              : 
    1956            1 :     static void variantshader_new(const int *type, const char *name, const int *row, const int *maxvariants, const uint *code)
    1957              :     {
    1958            1 :         if(*row < 0)
    1959              :         {
    1960            0 :             shader_new(type, name, code);
    1961            1 :             return;
    1962              :         }
    1963            1 :         else if(*row >= maxvariantrows)
    1964              :         {
    1965            0 :             return;
    1966              :         }
    1967            1 :         Shader *s = lookupshaderbyname(name);
    1968            1 :         if(!s)
    1969              :         {
    1970            1 :             return;
    1971              :         }
    1972              : 
    1973            0 :         adding_shader = true;
    1974            0 :         shader_clear_defines();
    1975              : 
    1976            0 :         execute(code);
    1977              : 
    1978            0 :         std::string vs, ps;
    1979            0 :         shader_assemble(shader_path_vs, shader_path_fs, vs, ps);
    1980              : 
    1981            0 :         DEF_FORMAT_STRING(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
    1982            0 :         if(*maxvariants > 0)
    1983              :         {
    1984            0 :             DEF_FORMAT_STRING(info, "shader %s", name);
    1985            0 :             renderprogress(std::min(s->variants.size() / static_cast<float>(*maxvariants), 1.0f), info);
    1986              :         }
    1987              : 
    1988            0 :         if(!s->defaultparams.empty())
    1989              :         {
    1990            0 :             genuniformdefs(vs, ps, s);
    1991              :         }
    1992              : 
    1993            0 :         if(vs.find("//:fog") != std::string::npos || ps.find("//:fog") != std::string::npos)
    1994              :         {
    1995            0 :             genfogshader(vs, ps);
    1996              :         }
    1997              : 
    1998            0 :         const Shader *v = newshader(*type, varname, vs.c_str(), ps.c_str(), s, *row);
    1999            0 :         if(v)
    2000              :         {
    2001            0 :             if(vs.find("//:variant") != std::string::npos || ps.find("//:variant") != std::string::npos)
    2002              :             {
    2003            0 :                 gengenericvariant(*s, varname, vs.c_str(), ps.c_str(), *row);
    2004              :             }
    2005              :         }
    2006              : 
    2007            0 :         adding_shader = false;
    2008            0 :     }
    2009              : }
    2010              : 
    2011              : //==============================================================================
    2012              : 
    2013            1 : static void setshader(const char *name)
    2014              : {
    2015            1 :     slotparams.clear();
    2016            1 :     std::unordered_map<std::string, Shader>::iterator itr = shaders.find(name);
    2017            1 :     if(itr == shaders.end())
    2018              :     {
    2019            0 :         conoutf(Console_Error, "no such shader: %s", name);
    2020              :     }
    2021              :     else
    2022              :     {
    2023            1 :         slotshader = &(*itr).second;
    2024              :     }
    2025            1 : }
    2026              : 
    2027            0 : void resetslotshader()
    2028              : {
    2029            0 :     slotshader = nullptr;
    2030            0 :     slotparams.clear();
    2031            0 : }
    2032              : 
    2033            0 : void setslotshader(Slot &s)
    2034              : {
    2035            0 :     s.shader = slotshader;
    2036            0 :     if(!s.shader)
    2037              :     {
    2038            0 :         s.shader = stdworldshader;
    2039            0 :         return;
    2040              :     }
    2041            0 :     for(size_t i = 0; i < slotparams.size(); i++)
    2042              :     {
    2043            0 :         s.params.push_back(slotparams[i]);
    2044              :     }
    2045              : }
    2046              : 
    2047            0 : static void linkslotshaderparams(std::vector<SlotShaderParam> &params, const Shader &sh, bool load)
    2048              : {
    2049            0 :     if(sh.loaded())
    2050              :     {
    2051            0 :         for(SlotShaderParam &param : params)
    2052              :         {
    2053            0 :             int loc = -1;
    2054            0 :             for(size_t j = 0; j < sh.defaultparams.size(); j++)
    2055              :             {
    2056            0 :                 const SlotShaderParamState &dparam = sh.defaultparams[j];
    2057            0 :                 if(dparam.name==param.name)
    2058              :                 {
    2059            0 :                     if(std::memcmp(param.val, &dparam.val[0], sizeof(param.val)))
    2060              :                     {
    2061            0 :                         loc = j;
    2062              :                     }
    2063            0 :                     break;
    2064              :                 }
    2065              :             }
    2066            0 :             param.loc = loc;
    2067              :         }
    2068              :     }
    2069            0 :     else if(load)
    2070              :     {
    2071            0 :         for(SlotShaderParam &p : params)
    2072              :         {
    2073            0 :             p.loc = SIZE_MAX;
    2074              :         }
    2075              :     }
    2076            0 : }
    2077              : 
    2078            0 : void linkslotshader(Slot &s, bool load)
    2079              : {
    2080            0 :     if(!s.shader)
    2081              :     {
    2082            0 :         return;
    2083              :     }
    2084            0 :     if(load && s.shader->deferred())
    2085              :     {
    2086            0 :         s.shader->force();
    2087              :     }
    2088            0 :     linkslotshaderparams(s.params, *s.shader, load);
    2089              : }
    2090              : 
    2091            0 : void linkvslotshader(VSlot &s, bool load)
    2092              : {
    2093            0 :     if(!s.slot->shader)
    2094              :     {
    2095            0 :         return;
    2096              :     }
    2097            0 :     linkslotshaderparams(s.params, *(s.slot->shader), load);
    2098            0 :     if(!s.slot->shader->loaded())
    2099              :     {
    2100            0 :         return;
    2101              :     }
    2102            0 :     if(s.slot->texmask&(1 << Tex_Glow))
    2103              :     {
    2104            0 :         static const char *paramname = getshaderparamname("glowcolor");
    2105            0 :         const float *param = findslotparam(s, paramname);
    2106            0 :         if(param)
    2107              :         {
    2108            0 :             s.glowcolor = vec(param).clamp(0, 1);
    2109              :         }
    2110              :     }
    2111              : }
    2112              : 
    2113            0 : bool shouldreuseparams(const Slot &s, const VSlot &p)
    2114              : {
    2115            0 :     if(!s.shader)
    2116              :     {
    2117            0 :         return false;
    2118              :     }
    2119            0 :     const Shader &sh = *s.shader;
    2120            0 :     for(const SlotShaderParamState &param : sh.defaultparams)
    2121              :     {
    2122            0 :         if(param.flags & SlotShaderParam::REUSE)
    2123              :         {
    2124            0 :             const float *val = findslotparam(p, param.name.c_str());
    2125            0 :             if(val && std::memcmp(&param.val[0], val, param.val.size()))
    2126              :             {
    2127            0 :                 for(const SlotShaderParam &j : s.params)
    2128              :                 {
    2129            0 :                     if(j.name == param.name)
    2130              :                     {
    2131            0 :                         goto notreused; //bail out of for loop
    2132              :                     }
    2133              :                 }
    2134            0 :                 return true;
    2135            0 :             notreused:;
    2136              :             }
    2137              :         }
    2138              :     }
    2139            0 :     return false;
    2140              : }
    2141              : 
    2142              : 
    2143              : static std::unordered_set<std::string> shaderparamnames;
    2144              : 
    2145            2 : const char *getshaderparamname(const char *name, bool insert)
    2146              : {
    2147            2 :     auto itr = shaderparamnames.find(name);
    2148            2 :     if(itr != shaderparamnames.end() || !insert)
    2149              :     {
    2150            1 :         return (*itr).c_str();
    2151              :     }
    2152            2 :     return (*shaderparamnames.insert(name).first).c_str();
    2153              : }
    2154              : 
    2155            2 : void addslotparam(const char *name, float x, float y, float z, float w, int flags = 0)
    2156              : {
    2157            2 :     if(name)
    2158              :     {
    2159            2 :         name = getshaderparamname(name);
    2160              :     }
    2161            2 :     for(SlotShaderParam &i : slotparams)
    2162              :     {
    2163            1 :         if(i.name==name)
    2164              :         {
    2165            1 :             i.val[0] = x;
    2166            1 :             i.val[1] = y;
    2167            1 :             i.val[2] = z;
    2168            1 :             i.val[3] = w;
    2169            1 :             i.flags |= flags;
    2170            1 :             return;
    2171              :         }
    2172              :     }
    2173            1 :     SlotShaderParam param = {name, SIZE_MAX, flags, {x, y, z, w}};
    2174            1 :     slotparams.push_back(param);
    2175              : }
    2176              : 
    2177            0 : void cleanupshaders()
    2178              : {
    2179            0 :     cleanuppostfx(true);
    2180              : 
    2181            0 :     loadedshaders = false;
    2182            0 :     nullshader = hudshader = hudnotextureshader = nullptr;
    2183            0 :     for(auto &[k, s] : shaders)
    2184              :     {
    2185            0 :         s.cleanup();
    2186              :     }
    2187            0 :     Shader::lastshader = nullptr;
    2188            0 :     glUseProgram(0);
    2189            0 : }
    2190              : 
    2191            0 : void Shader::reusecleanup()
    2192              : {
    2193            0 :     if((reusevs && reusevs->invalid()) ||
    2194            0 :        (reuseps && reuseps->invalid()) ||
    2195            0 :        !compile())
    2196              :     {
    2197            0 :         cleanup(true);
    2198              :     }
    2199            0 : }
    2200              : 
    2201            0 : void reloadshaders()
    2202              : {
    2203            0 :     identflags &= ~Idf_Persist;
    2204            0 :     loadshaders();
    2205            0 :     identflags |= Idf_Persist;
    2206            0 :     linkslotshaders();
    2207            0 :     for(auto &[k, s] : shaders)
    2208              :     {
    2209            0 :         if(!s.standard && s.loaded() && !s.variantshader)
    2210              :         {
    2211            0 :             DEF_FORMAT_STRING(info, "shader %s", s.name);
    2212            0 :             renderprogress(0.0, info);
    2213            0 :             if(!s.compile())
    2214              :             {
    2215            0 :                 s.cleanup(true);
    2216              :             }
    2217            0 :             for(Shader *&v : s.variants)
    2218              :             {
    2219            0 :                 v->reusecleanup();
    2220              :             }
    2221              :         }
    2222            0 :         if(s.forced && s.deferred())
    2223              :         {
    2224            0 :             s.force();
    2225              :         }
    2226              :     }
    2227            0 : }
    2228              : 
    2229            1 : void resetshaders()
    2230              : {
    2231            1 :     if(!glslversion)
    2232              :     {
    2233            1 :         conoutf(Console_Error, "Cannot reset GLSL shaders without GLSL initialized, operation not performed");
    2234            1 :         return;
    2235              :     }
    2236            0 :     clearchanges(Change_Shaders);
    2237              : 
    2238            0 :     cleanuplights();
    2239            0 :     cleanupmodels();
    2240            0 :     cleanupshaders();
    2241            0 :     setupshaders();
    2242            0 :     initgbuffer();
    2243            0 :     reloadshaders();
    2244            0 :     rootworld.allchanged(true);
    2245            0 :     glerror();
    2246              : }
    2247              : 
    2248              : FVAR(blursigma, 0.005f, 0.5f, 2.0f);
    2249              : 
    2250            0 : void setupblurkernel(int radius, std::array<float, maxblurradius + 1> &weights, std::array<float, maxblurradius + 1> &offsets)
    2251              : {
    2252            0 :     if(radius<1 || radius>maxblurradius)
    2253              :     {
    2254            0 :         return;
    2255              :     }
    2256            0 :     float sigma = blursigma*2*radius,
    2257            0 :           total = 1.0f/sigma;
    2258            0 :     weights[0] = total;
    2259            0 :     offsets[0] = 0;
    2260              :     // rely on bilinear filtering to sample 2 pixels at once
    2261              :     // transforms a*X + b*Y into (u+v)*[X*u/(u+v) + Y*(1 - u/(u+v))]
    2262            0 :     for(int i = 0; i < radius; ++i)
    2263              :     {
    2264            0 :         float weight1 = std::exp(-((2*i)*(2*i)) / (2*sigma*sigma)) / sigma,
    2265            0 :               weight2 = std::exp(-((2*i+1)*(2*i+1)) / (2*sigma*sigma)) / sigma,
    2266            0 :               scale = weight1 + weight2,
    2267            0 :               offset = 2*i+1 + weight2 / scale;
    2268            0 :         weights[i+1] = scale;
    2269            0 :         offsets[i+1] = offset;
    2270            0 :         total += 2*scale;
    2271              :     }
    2272            0 :     for(int i = 0; i < radius+1; ++i)
    2273              :     {
    2274            0 :         weights[i] /= total;
    2275              :     }
    2276            0 :     for(int i = radius+1; i <= maxblurradius; i++)
    2277              :     {
    2278            0 :         weights[i] = offsets[i] = 0;
    2279              :     }
    2280              : }
    2281              : 
    2282            0 : void setblurshader(int pass, int size, int radius, std::array<float, maxblurradius + 1> &weights, std::array<float, maxblurradius + 1> &offsets, GLenum target)
    2283              : {
    2284            0 :     if(radius<1 || radius>maxblurradius)
    2285              :     {
    2286            0 :         return;
    2287              :     }
    2288              :     static Shader *blurshader[7][2] = { { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr } },
    2289              :                   *blurrectshader[7][2] = { { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr }, { nullptr, nullptr } };
    2290            0 :     Shader *&s = (target == GL_TEXTURE_RECTANGLE ? blurrectshader : blurshader)[radius-1][pass];
    2291            0 :     if(!s)
    2292              :     {
    2293            0 :         DEF_FORMAT_STRING(name, "blur%c%d%s", 'x'+pass, radius, target == GL_TEXTURE_RECTANGLE ? "rect" : "");
    2294            0 :         s = lookupshaderbyname(name);
    2295              :     }
    2296            0 :     s->set();
    2297            0 :     LOCALPARAMV(weights.data(), weights.data(), maxblurradius+1);
    2298              :     std::array<float, maxblurradius+1> scaledoffsets;
    2299            0 :     for(int k = 0; k < maxblurradius+1; ++k)
    2300              :     {
    2301            0 :         scaledoffsets[k] = offsets[k]/size;
    2302              :     }
    2303            0 :     LOCALPARAMV(offsets.data(), scaledoffsets.data(), maxblurradius+1);
    2304              : }
    2305              : 
    2306            1 : void initshadercmds()
    2307              : {
    2308            1 :     addcommand("defershader", reinterpret_cast<identfun>(defershader), "iss", Id_Command);
    2309            2 :     addcommand("forceshader", reinterpret_cast<identfun>(+[](const char *name){useshaderbyname(name);}), "s", Id_Command);
    2310            1 :     addcommand("shader", reinterpret_cast<identfun>(shader), "isss", Id_Command);
    2311            1 :     addcommand("variantshader", reinterpret_cast<identfun>(ShaderMgr::variantshader), "isissi", Id_Command);
    2312            1 :     addcommand("setshader", reinterpret_cast<identfun>(setshader), "s", Id_Command);
    2313            2 :     addcommand("isshaderdefined", reinterpret_cast<identfun>(+[](const char *name){intret(lookupshaderbyname(name) ? 1 : 0);}), "s", Id_Command);
    2314            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);
    2315            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);
    2316            1 :     addcommand("resetshaders", reinterpret_cast<identfun>(resetshaders), "", Id_Command);
    2317              : 
    2318            1 :     addcommand("variantshader_new", reinterpret_cast<identfun>(ShaderMgr::variantshader_new), "isiie", Id_Command);
    2319            1 :     addcommand("shader_new", reinterpret_cast<identfun>(ShaderMgr::shader_new), "ise", Id_Command);
    2320            1 :     addcommand("shader_define", reinterpret_cast<identfun>(ShaderMgr::shader_define), "ss", Id_Command);
    2321            1 :     addcommand("shader_source", reinterpret_cast<identfun>(ShaderMgr::shader_source), "ss", Id_Command);
    2322            1 :     addcommand("shader_include_vs", reinterpret_cast<identfun>(ShaderMgr::shader_include_vs), "s", Id_Command);
    2323            1 :     addcommand("shader_include_fs", reinterpret_cast<identfun>(ShaderMgr::shader_include_fs), "s", Id_Command);
    2324            1 :     addcommand("shader_get_defines", reinterpret_cast<identfun>(ShaderMgr::shader_get_defines), "", Id_Command);
    2325            1 :     addcommand("shader_get_includes_vs", reinterpret_cast<identfun>(ShaderMgr::shader_get_includes_vs), "", Id_Command);
    2326            1 :     addcommand("shader_get_includes_fs", reinterpret_cast<identfun>(ShaderMgr::shader_get_includes_fs), "", Id_Command);
    2327              : 
    2328            1 :     initpostfxcmds();
    2329            1 : }
        

Generated by: LCOV version 2.0-1