raze/source/common/rendering/gles/gles_shader.cpp

956 lines
29 KiB
C++
Raw Normal View History

//
//---------------------------------------------------------------------------
//
// 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 <map>
#include <memory>
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<uint8_t> data;
};
static const char *ShaderMagic = "ZDSC";
static std::map<FString, std::unique_ptr<ProgramBinary>> 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<ProgramBinary> 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<FileWriter> 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<uint8_t> 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<uint8_t> &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);
bool lightbuffertype = screen->mLights->GetBufferType();
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<uint8_t> 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<uint32_t, ShaderVariantData*>::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);
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);
//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)
{
FShader *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;i<MAX_EFFECTS;i++)
{
FShader *eff = new FShader(effectshaders[i].ShaderName);
if (!eff->Configure(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;i<mMaterialShaders.Size();i++)
{
if (mMaterialShaders[i]->mName == 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
}
}