LCOV - code coverage report
Current view: top level - engine/render - shader.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 261 1163 22.4 %
Date: 2024-11-22 05:07:59 Functions: 40 93 43.0 %

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

Generated by: LCOV version 1.14