// //--------------------------------------------------------------------------- // // Copyright(C) 2004-2016 Christoph Oelckers // All rights reserved. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // //-------------------------------------------------------------------------- // /* ** gl_shader.cpp ** ** GLSL shader handling ** */ #include "gles_system.h" #include "c_cvars.h" #include "v_video.h" #include "filesystem.h" #include "engineerrors.h" #include "cmdlib.h" #include "md5.h" #include "gles_shader.h" #include "hw_shaderpatcher.h" #include "shaderuniforms.h" #include "hw_viewpointuniforms.h" #include "hw_lightbuffer.h" #include "i_specialpaths.h" #include "printf.h" #include "version.h" #include "matrix.h" #include "gles_renderer.h" #include #include namespace OpenGLESRenderer { CVAR(Int, gles_glsl_precision, 2, 0); // 0 = low, 1 = medium, 2 = high FString GetGLSLPrecision() { FString str = "precision highp int;\n \ precision highp float;\n"; if (gles_glsl_precision == 0) str.Substitute("highp", "lowp"); else if (gles_glsl_precision == 1) str.Substitute("highp", "mediump"); return str; } struct ProgramBinary { uint32_t format; TArray data; }; static const char *ShaderMagic = "ZDSC"; static std::map> ShaderCache; // Not a TMap because it doesn't support unique_ptr move semantics bool IsShaderCacheActive() { static bool active = true; static bool firstcall = true; if (firstcall) { const char *vendor = (const char *)glGetString(GL_VENDOR); active = !(strstr(vendor, "Intel") == nullptr); firstcall = false; } return active; } static FString CalcProgramBinaryChecksum(const FString &vertex, const FString &fragment) { const GLubyte *vendor = glGetString(GL_VENDOR); const GLubyte *renderer = glGetString(GL_RENDERER); const GLubyte *version = glGetString(GL_VERSION); uint8_t digest[16]; MD5Context md5; md5.Update(vendor, (unsigned int)strlen((const char*)vendor)); md5.Update(renderer, (unsigned int)strlen((const char*)renderer)); md5.Update(version, (unsigned int)strlen((const char*)version)); md5.Update((const uint8_t *)vertex.GetChars(), (unsigned int)vertex.Len()); md5.Update((const uint8_t *)fragment.GetChars(), (unsigned int)fragment.Len()); md5.Final(digest); char hexdigest[33]; for (int i = 0; i < 16; i++) { int v = digest[i] >> 4; hexdigest[i * 2] = v < 10 ? ('0' + v) : ('a' + v - 10); v = digest[i] & 15; hexdigest[i * 2 + 1] = v < 10 ? ('0' + v) : ('a' + v - 10); } hexdigest[32] = 0; return hexdigest; } static FString CreateProgramCacheName(bool create) { FString path = M_GetCachePath(create); if (create) CreatePath(path); path << "/shadercache.zdsc"; return path; } static void LoadShaders() { static bool loaded = false; if (loaded) return; loaded = true; try { FString path = CreateProgramCacheName(false); FileReader fr; if (!fr.OpenFile(path)) I_Error("Could not open shader file"); char magic[4]; fr.Read(magic, 4); if (memcmp(magic, ShaderMagic, 4) != 0) I_Error("Not a shader cache file"); uint32_t count = fr.ReadUInt32(); if (count > 512) I_Error("Too many shaders cached"); for (uint32_t i = 0; i < count; i++) { char hexdigest[33]; if (fr.Read(hexdigest, 32) != 32) I_Error("Read error"); hexdigest[32] = 0; std::unique_ptr binary(new ProgramBinary()); binary->format = fr.ReadUInt32(); uint32_t size = fr.ReadUInt32(); if (size > 1024 * 1024) I_Error("Shader too big, probably file corruption"); binary->data.Resize(size); if (fr.Read(binary->data.Data(), binary->data.Size()) != binary->data.Size()) I_Error("Read error"); ShaderCache[hexdigest] = std::move(binary); } } catch (...) { ShaderCache.clear(); } } static void SaveShaders() { FString path = CreateProgramCacheName(true); std::unique_ptr fw(FileWriter::Open(path)); if (fw) { uint32_t count = (uint32_t)ShaderCache.size(); fw->Write(ShaderMagic, 4); fw->Write(&count, sizeof(uint32_t)); for (const auto &it : ShaderCache) { uint32_t size = it.second->data.Size(); fw->Write(it.first.GetChars(), 32); fw->Write(&it.second->format, sizeof(uint32_t)); fw->Write(&size, sizeof(uint32_t)); fw->Write(it.second->data.Data(), it.second->data.Size()); } } } TArray LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat) { LoadShaders(); auto it = ShaderCache.find(CalcProgramBinaryChecksum(vertex, fragment)); if (it != ShaderCache.end()) { binaryFormat = it->second->format; return it->second->data; } else { binaryFormat = 0; return {}; } } void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray &binary, uint32_t binaryFormat) { auto &entry = ShaderCache[CalcProgramBinaryChecksum(vertex, fragment)]; entry.reset(new ProgramBinary()); entry->format = binaryFormat; entry->data = binary; SaveShaders(); } bool FShader::Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines) { mVertProg = vert_prog_lump; mFragProg = fragprog; mFragProg2 = fragprog2; mLightProg = light_fragprog; mDefinesBase = defines; return true; } void FShader::LoadVariant() { //mDefinesBase Load(mName.GetChars(), mVertProg, mFragProg, mFragProg2, mLightProg, mDefinesBase); } bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * frag_prog_lump_, const char * proc_prog_lump_, const char * light_fragprog_, const char * defines) { ShaderVariantData* shaderData = new ShaderVariantData(); FString vert_prog_lump = vert_prog_lump_; FString frag_prog_lump = frag_prog_lump_; FString proc_prog_lump = proc_prog_lump_; FString light_fragprog = light_fragprog_; vert_prog_lump.Substitute("shaders/", "shaders_gles/"); frag_prog_lump.Substitute("shaders/", "shaders_gles/"); proc_prog_lump.Substitute("shaders/", "shaders_gles/"); light_fragprog.Substitute("shaders/", "shaders_gles/"); //light_fragprog.Substitute("material_pbr", "material_normal"); if(light_fragprog.Len()) light_fragprog = "shaders_gles/glsl/material_normal.fp"; // NOTE: Always use normal material for now, ignore others static char buffer[10000]; FString error; FString i_data = GetGLSLPrecision(); i_data += R"( // light buffers uniform vec4 lights[MAXIMUM_LIGHT_VECTORS]; uniform mat4 ProjectionMatrix; uniform mat4 ViewMatrix; uniform mat4 NormalViewMatrix; uniform vec4 uCameraPos; uniform vec4 uClipLine; uniform float uGlobVis; // uGlobVis = R_GetGlobVis(r_visibility) / 32.0 uniform int uPalLightLevels; uniform int uViewHeight; // Software fuzz scaling uniform float uClipHeight; uniform float uClipHeightDirection; uniform int uShadowmapFilter; uniform int uTextureMode; uniform vec2 uClipSplit; uniform float uAlphaThreshold; // colors uniform vec4 uObjectColor; uniform vec4 uObjectColor2; uniform vec4 uDynLightColor; uniform vec4 uAddColor; uniform vec4 uTextureBlendColor; uniform vec4 uTextureModulateColor; uniform vec4 uTextureAddColor; uniform vec4 uFogColor; uniform float uDesaturationFactor; uniform float uInterpolationFactor; // Glowing walls stuff uniform vec4 uGlowTopPlane; uniform vec4 uGlowTopColor; uniform vec4 uGlowBottomPlane; uniform vec4 uGlowBottomColor; uniform vec4 uGradientTopPlane; uniform vec4 uGradientBottomPlane; uniform vec4 uSplitTopPlane; uniform vec4 uSplitBottomPlane; uniform vec4 uDetailParms; // Lighting + Fog uniform vec4 uLightAttr; #define uLightLevel uLightAttr.a #define uFogDensity uLightAttr.b #define uLightFactor uLightAttr.g #define uLightDist uLightAttr.r //uniform int uFogEnabled; // dynamic lights uniform ivec4 uLightRange; // Blinn glossiness and specular level uniform vec2 uSpecularMaterial; // matrices uniform mat4 ModelMatrix; uniform mat4 NormalModelMatrix; uniform mat4 TextureMatrix; uniform vec4 uFixedColormapStart; uniform vec4 uFixedColormapRange; // textures uniform sampler2D tex; uniform sampler2D ShadowMap; uniform sampler2D texture2; uniform sampler2D texture3; uniform sampler2D texture4; uniform sampler2D texture5; uniform sampler2D texture6; uniform sampler2D texture7; uniform sampler2D texture8; uniform sampler2D texture9; uniform sampler2D texture10; uniform sampler2D texture11; // timer data uniform float timer; // material types #if defined(SPECULAR) #define normaltexture texture2 #define speculartexture texture3 #define brighttexture texture4 #define detailtexture texture5 #define glowtexture texture6 #elif defined(PBR) #define normaltexture texture2 #define metallictexture texture3 #define roughnesstexture texture4 #define aotexture texture5 #define brighttexture texture6 #define detailtexture texture7 #define glowtexture texture8 #else #define brighttexture texture2 #define detailtexture texture3 #define glowtexture texture4 #endif )"; #ifdef NPOT_EMULATION i_data += "#define NPOT_EMULATION\nuniform vec2 uNpotEmulation;\n"; #endif int vp_lump = fileSystem.CheckNumForFullName(vert_prog_lump, 0); if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump.GetChars()); FileData vp_data = fileSystem.ReadFile(vp_lump); int fp_lump = fileSystem.CheckNumForFullName(frag_prog_lump, 0); if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump.GetChars()); FileData fp_data = fileSystem.ReadFile(fp_lump); // // The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break! // FString vp_comb; assert(screen->mLights != NULL); unsigned int lightbuffersize = screen->mLights->GetBlockSize(); vp_comb.Format("#version 100\n#define NUM_UBO_LIGHTS %d\n#define NO_CLIPDISTANCE_SUPPORT\n", lightbuffersize); FString fp_comb = vp_comb; vp_comb << defines << i_data.GetChars(); fp_comb << "$placeholder$\n" << defines << i_data.GetChars(); vp_comb << "#line 1\n"; fp_comb << "#line 1\n"; vp_comb << RemoveLayoutLocationDecl(vp_data.GetString(), "out").GetChars() << "\n"; fp_comb << RemoveLayoutLocationDecl(fp_data.GetString(), "in").GetChars() << "\n"; FString placeholder = "\n"; if (proc_prog_lump.Len()) { fp_comb << "#line 1\n"; if (*proc_prog_lump != '#') { int pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump); if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump.GetChars()); FileData pp_data = fileSystem.ReadFile(pp_lump); if (pp_data.GetString().IndexOf("ProcessMaterial") < 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0) { // this looks like an old custom hardware shader. if (pp_data.GetString().IndexOf("GetTexCoord") >= 0) { int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat2.fp", 0); if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat2.fp"); FileData pl_data = fileSystem.ReadFile(pl_lump); fp_comb << "\n" << pl_data.GetString().GetChars(); } else { int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat.fp", 0); if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat.fp"); FileData pl_data = fileSystem.ReadFile(pl_lump); fp_comb << "\n" << pl_data.GetString().GetChars(); if (pp_data.GetString().IndexOf("ProcessTexel") < 0) { // this looks like an even older custom hardware shader. // We need to replace the ProcessTexel call to make it work. fp_comb.Substitute("material.Base = ProcessTexel();", "material.Base = Process(vec4(1.0));"); } } if (pp_data.GetString().IndexOf("ProcessLight") >= 0) { // The ProcessLight signatured changed. Forward to the old one. fp_comb << "\nvec4 ProcessLight(vec4 color);\n"; fp_comb << "\nvec4 ProcessLight(Material material, vec4 color) { return ProcessLight(color); }\n"; } } fp_comb << RemoveLegacyUserUniforms(pp_data.GetString()).GetChars(); fp_comb.Substitute("gl_TexCoord[0]", "vTexCoord"); // fix old custom shaders. if (pp_data.GetString().IndexOf("ProcessLight") < 0) { int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultlight.fp", 0); if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultlight.fp"); FileData pl_data = fileSystem.ReadFile(pl_lump); fp_comb << "\n" << pl_data.GetString().GetChars(); } // ProcessMaterial must be considered broken because it requires the user to fill in data they possibly cannot know all about. if (pp_data.GetString().IndexOf("ProcessMaterial") >= 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0) { // This reactivates the old logic and disables all features that cannot be supported with that method. placeholder << "#define LEGACY_USER_SHADER\n"; } } else { // Proc_prog_lump is not a lump name but the source itself (from generated shaders) fp_comb << proc_prog_lump.GetChars() + 1; } } fp_comb.Substitute("$placeholder$", placeholder); if (light_fragprog.Len()) { int pp_lump = fileSystem.CheckNumForFullName(light_fragprog, 0); if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog.GetChars()); FileData pp_data = fileSystem.ReadFile(pp_lump); fp_comb << pp_data.GetString().GetChars() << "\n"; } if (gles.flags & RFL_NO_CLIP_PLANES) { // On ATI's GL3 drivers we have to disable gl_ClipDistance because it's hopelessly broken. // This will cause some glitches and regressions but is the only way to avoid total display garbage. vp_comb.Substitute("gl_ClipDistance", "//"); } shaderData->hShader = glCreateProgram(); uint32_t binaryFormat = 0; TArray binary; if (IsShaderCacheActive()) binary = LoadCachedProgramBinary(vp_comb, fp_comb, binaryFormat); bool linked = false; if (!linked) { shaderData->hVertProg = glCreateShader(GL_VERTEX_SHADER); shaderData->hFragProg = glCreateShader(GL_FRAGMENT_SHADER); int vp_size = (int)vp_comb.Len(); int fp_size = (int)fp_comb.Len(); const char *vp_ptr = vp_comb.GetChars(); const char *fp_ptr = fp_comb.GetChars(); glShaderSource(shaderData->hVertProg, 1, &vp_ptr, &vp_size); glShaderSource(shaderData->hFragProg, 1, &fp_ptr, &fp_size); glCompileShader(shaderData->hVertProg); glCompileShader(shaderData->hFragProg); glAttachShader(shaderData->hShader, shaderData->hVertProg); glAttachShader(shaderData->hShader, shaderData->hFragProg); glBindAttribLocation(shaderData->hShader, VATTR_VERTEX, "aPosition"); glBindAttribLocation(shaderData->hShader, VATTR_TEXCOORD, "aTexCoord"); glBindAttribLocation(shaderData->hShader, VATTR_COLOR, "aColor"); glBindAttribLocation(shaderData->hShader, VATTR_VERTEX2, "aVertex2"); glBindAttribLocation(shaderData->hShader, VATTR_NORMAL, "aNormal"); glBindAttribLocation(shaderData->hShader, VATTR_NORMAL2, "aNormal2"); glLinkProgram(shaderData->hShader); glGetShaderInfoLog(shaderData->hVertProg, 10000, NULL, buffer); if (*buffer) { error << "Vertex shader:\n" << buffer << "\n"; } glGetShaderInfoLog(shaderData->hFragProg, 10000, NULL, buffer); if (*buffer) { error << "Fragment shader:\n" << buffer << "\n"; } glGetProgramInfoLog(shaderData->hShader, 10000, NULL, buffer); if (*buffer) { error << "Linking:\n" << buffer << "\n"; } GLint status = 0; glGetProgramiv(shaderData->hShader, GL_LINK_STATUS, &status); linked = (status == GL_TRUE); if (!linked) { // only print message if there's an error. I_Error("Init Shader '%s':\n%s\n", name, error.GetChars()); } } else { shaderData->hVertProg = 0; shaderData->hFragProg = 0; } shaderData->muProjectionMatrix.Init(shaderData->hShader, "ProjectionMatrix"); shaderData->muViewMatrix.Init(shaderData->hShader, "ViewMatrix"); shaderData->muNormalViewMatrix.Init(shaderData->hShader, "NormalViewMatrix"); //shaderData->ProjectionMatrix_index = glGetUniformLocation(shaderData->hShader, "ProjectionMatrix"); //shaderData->ViewMatrix_index = glGetUniformLocation(shaderData->hShader, "ViewMatrix"); //shaderData->NormalViewMatrix_index = glGetUniformLocation(shaderData->hShader, "NormalViewMatrix"); shaderData->muCameraPos.Init(shaderData->hShader, "uCameraPos"); shaderData->muClipLine.Init(shaderData->hShader, "uClipLine"); shaderData->muGlobVis.Init(shaderData->hShader, "uGlobVis"); shaderData->muPalLightLevels.Init(shaderData->hShader, "uPalLightLevels"); shaderData->muViewHeight.Init(shaderData->hShader, "uViewHeight"); shaderData->muClipHeight.Init(shaderData->hShader, "uClipHeight"); shaderData->muClipHeightDirection.Init(shaderData->hShader, "uClipHeightDirection"); shaderData->muShadowmapFilter.Init(shaderData->hShader, "uShadowmapFilter"); //// shaderData->muDesaturation.Init(shaderData->hShader, "uDesaturationFactor"); shaderData->muFogEnabled.Init(shaderData->hShader, "uFogEnabled"); shaderData->muTextureMode.Init(shaderData->hShader, "uTextureMode"); shaderData->muLightParms.Init(shaderData->hShader, "uLightAttr"); shaderData->muClipSplit.Init(shaderData->hShader, "uClipSplit"); shaderData->muLightRange.Init(shaderData->hShader, "uLightRange"); shaderData->muFogColor.Init(shaderData->hShader, "uFogColor"); shaderData->muDynLightColor.Init(shaderData->hShader, "uDynLightColor"); shaderData->muObjectColor.Init(shaderData->hShader, "uObjectColor"); shaderData->muObjectColor2.Init(shaderData->hShader, "uObjectColor2"); shaderData->muGlowBottomColor.Init(shaderData->hShader, "uGlowBottomColor"); shaderData->muGlowTopColor.Init(shaderData->hShader, "uGlowTopColor"); shaderData->muGlowBottomPlane.Init(shaderData->hShader, "uGlowBottomPlane"); shaderData->muGlowTopPlane.Init(shaderData->hShader, "uGlowTopPlane"); shaderData->muGradientBottomPlane.Init(shaderData->hShader, "uGradientBottomPlane"); shaderData->muGradientTopPlane.Init(shaderData->hShader, "uGradientTopPlane"); shaderData->muSplitBottomPlane.Init(shaderData->hShader, "uSplitBottomPlane"); shaderData->muSplitTopPlane.Init(shaderData->hShader, "uSplitTopPlane"); shaderData->muDetailParms.Init(shaderData->hShader, "uDetailParms"); shaderData->muInterpolationFactor.Init(shaderData->hShader, "uInterpolationFactor"); shaderData->muAlphaThreshold.Init(shaderData->hShader, "uAlphaThreshold"); shaderData->muSpecularMaterial.Init(shaderData->hShader, "uSpecularMaterial"); shaderData->muAddColor.Init(shaderData->hShader, "uAddColor"); shaderData->muTextureAddColor.Init(shaderData->hShader, "uTextureAddColor"); shaderData->muTextureModulateColor.Init(shaderData->hShader, "uTextureModulateColor"); shaderData->muTextureBlendColor.Init(shaderData->hShader, "uTextureBlendColor"); shaderData->muTimer.Init(shaderData->hShader, "timer"); #ifdef NPOT_EMULATION shaderData->muNpotEmulation.Init(shaderData->hShader, "uNpotEmulation"); #endif shaderData->muFixedColormapStart.Init(shaderData->hShader, "uFixedColormapStart"); shaderData->muFixedColormapRange.Init(shaderData->hShader, "uFixedColormapRange"); shaderData->lights_index = glGetUniformLocation(shaderData->hShader, "lights"); shaderData->modelmatrix_index = glGetUniformLocation(shaderData->hShader, "ModelMatrix"); shaderData->texturematrix_index = glGetUniformLocation(shaderData->hShader, "TextureMatrix"); shaderData->normalmodelmatrix_index = glGetUniformLocation(shaderData->hShader, "NormalModelMatrix"); glUseProgram(shaderData->hShader); // set up other texture units (if needed by the shader) for (int i = 2; i<16; i++) { char stringbuf[20]; mysnprintf(stringbuf, 20, "texture%d", i); int tempindex = glGetUniformLocation(shaderData->hShader, stringbuf); if (tempindex >= 0) glUniform1i(tempindex, i - 1); } int shadowmapindex = glGetUniformLocation(shaderData->hShader, "ShadowMap"); if (shadowmapindex >= 0) glUniform1i(shadowmapindex, 16); glUseProgram(0); cur = shaderData; return linked; } //========================================================================== // // // //========================================================================== FShader::~FShader() { std::map::iterator it = variants.begin(); while (it != variants.end()) { glDeleteProgram(it->second->hShader); if (it->second->hVertProg != 0) glDeleteShader(it->second->hVertProg); if (it->second->hFragProg != 0) glDeleteShader(it->second->hFragProg); it++; } } //========================================================================== // // // //========================================================================== bool FShader::Bind(ShaderFlavourData& flavour) { uint32_t tag = CreateShaderTag(flavour); auto pos = variants.find(tag); if (pos == variants.end()) { FString variantConfig = "\n"; variantConfig.AppendFormat("#define MAXIMUM_LIGHT_VECTORS %d\n", gles.numlightvectors); variantConfig.AppendFormat("#define DEF_TEXTURE_MODE %d\n", flavour.textureMode); variantConfig.AppendFormat("#define DEF_TEXTURE_FLAGS %d\n", flavour.texFlags); variantConfig.AppendFormat("#define DEF_BLEND_FLAGS %d\n", flavour.blendFlags & 0x7); variantConfig.AppendFormat("#define DEF_FOG_2D %d\n", flavour.twoDFog); variantConfig.AppendFormat("#define DEF_FOG_ENABLED %d\n", flavour.fogEnabled); variantConfig.AppendFormat("#define DEF_FOG_RADIAL %d\n", flavour.fogEquationRadial); variantConfig.AppendFormat("#define DEF_FOG_COLOURED %d\n", flavour.colouredFog); variantConfig.AppendFormat("#define DEF_USE_U_LIGHT_LEVEL %d\n", flavour.useULightLevel); variantConfig.AppendFormat("#define DEF_DO_DESATURATE %d\n", flavour.doDesaturate); variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_MOD %d\n", flavour.dynLightsMod); variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_SUB %d\n", flavour.dynLightsSub); variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_ADD %d\n", flavour.dynLightsAdd); variantConfig.AppendFormat("#define DEF_USE_OBJECT_COLOR_2 %d\n", flavour.useObjectColor2); variantConfig.AppendFormat("#define DEF_USE_GLOW_TOP_COLOR %d\n", flavour.useGlowTopColor); variantConfig.AppendFormat("#define DEF_USE_GLOW_BOTTOM_COLOR %d\n", flavour.useGlowBottomColor); variantConfig.AppendFormat("#define DEF_USE_COLOR_MAP %d\n", flavour.useColorMap); variantConfig.AppendFormat("#define DEF_BUILD_LIGHTING %d\n", flavour.buildLighting); variantConfig.AppendFormat("#define DEF_BANDED_SW_LIGHTING %d\n", flavour.bandedSwLight); variantConfig.AppendFormat("#define USE_GLSL_V100 %d\n", gles.forceGLSLv100); #ifdef NPOT_EMULATION variantConfig.AppendFormat("#define DEF_NPOT_EMULATION %d\n", flavour.npotEmulation); #endif variantConfig.AppendFormat("#define DEF_HAS_SPOTLIGHT %d\n", flavour.hasSpotLight); variantConfig.AppendFormat("#define DEF_PALETTE_INTERPOLATE %d\n", flavour.paletteInterpolate); //Printf("Shader: %s, %08x %s", mFragProg2.GetChars(), tag, variantConfig.GetChars()); Load(mName.GetChars(), mVertProg, mFragProg, mFragProg2, mLightProg, mDefinesBase + variantConfig); variants.insert(std::make_pair(tag, cur)); } else { cur = pos->second; } GLRenderer->mShaderManager->SetActiveShader(this->cur); return true; } //========================================================================== // // Since all shaders are REQUIRED, any error here needs to be fatal // //========================================================================== FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType) { FString defines; defines += shaderdefines; // this can't be in the shader code due to ATI strangeness. if (!usediscard) defines += "#define NO_ALPHATEST\n"; FShader *shader = NULL; try { shader = new FShader(ShaderName); if (!shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars())) { I_FatalError("Unable to load shader %s\n", ShaderName); } } catch(CRecoverableError &err) { if (shader != NULL) delete shader; shader = NULL; I_FatalError("Unable to load shader %s:\n%s\n", ShaderName, err.GetMessage()); } return shader; } //========================================================================== // // // //========================================================================== FShaderManager::FShaderManager() { for (int passType = 0; passType < MAX_PASS_TYPES; passType++) mPassShaders.Push(new FShaderCollection((EPassType)passType)); } FShaderManager::~FShaderManager() { glUseProgram(0); mActiveShader = NULL; for (auto collection : mPassShaders) delete collection; } void FShaderManager::SetActiveShader(FShader::ShaderVariantData *sh) { if (mActiveShader != sh) { glUseProgram(sh!= NULL? sh->GetHandle() : 0); mActiveShader = sh; } } FShader *FShaderManager::BindEffect(int effect, EPassType passType, ShaderFlavourData &flavour) { if (passType < mPassShaders.Size()) return mPassShaders[passType]->BindEffect(effect, flavour); else return nullptr; } FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType) { if (passType < mPassShaders.Size()) return mPassShaders[passType]->Get(eff, alphateston); else return nullptr; } //========================================================================== // // // //========================================================================== FShaderCollection::FShaderCollection(EPassType passType) { CompileShaders(passType); } //========================================================================== // // // //========================================================================== FShaderCollection::~FShaderCollection() { Clean(); } //========================================================================== // // // //========================================================================== void FShaderCollection::CompileShaders(EPassType passType) { mMaterialShaders.Clear(); mMaterialShadersNAT.Clear(); for (int i = 0; i < MAX_EFFECTS; i++) { mEffectShaders[i] = NULL; } for(int i=0;defaultshaders[i].ShaderName != NULL;i++) { FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, passType); mMaterialShaders.Push(shc); if (i < SHADER_NoTexture) { shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, passType); mMaterialShadersNAT.Push(shc); } } #if 0 for(unsigned i = 0; i < usershaders.Size(); i++) { FString name = ExtractFileBase(usershaders[i].shader); FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines; FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, passType); mMaterialShaders.Push(shc); } #endif for(int i=0;iConfigure(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1, effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines)) { delete eff; } else mEffectShaders[i] = eff; } } //========================================================================== // // // //========================================================================== void FShaderCollection::Clean() { for (unsigned int i = 0; i < mMaterialShadersNAT.Size(); i++) { if (mMaterialShadersNAT[i] != NULL) delete mMaterialShadersNAT[i]; } for (unsigned int i = 0; i < mMaterialShaders.Size(); i++) { if (mMaterialShaders[i] != NULL) delete mMaterialShaders[i]; } for (int i = 0; i < MAX_EFFECTS; i++) { if (mEffectShaders[i] != NULL) delete mEffectShaders[i]; mEffectShaders[i] = NULL; } mMaterialShaders.Clear(); mMaterialShadersNAT.Clear(); } //========================================================================== // // // //========================================================================== int FShaderCollection::Find(const char * shn) { FName sfn = shn; for(unsigned int i=0;imName == sfn) { return i; } } return -1; } //========================================================================== // // // //========================================================================== FShader *FShaderCollection::BindEffect(int effect, ShaderFlavourData& flavour) { if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL) { mEffectShaders[effect]->Bind(flavour); return mEffectShaders[effect]; } return NULL; } //========================================================================== // // // //========================================================================== void gl_DestroyUserShaders() { // todo } }