2019-12-28 17:20:47 +00:00
|
|
|
/*
|
|
|
|
** hw_shaderpatcher.cpp
|
|
|
|
** Modifies shader source to account for different syntax versions or engine changes.
|
|
|
|
**
|
2020-01-03 07:03:29 +00:00
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright(C) 2004-2018 Christoph Oelckers
|
|
|
|
** Copyright(C) 2016-2018 Magnus Norddahl
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
2019-12-28 17:20:47 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2022-06-06 09:45:02 +00:00
|
|
|
#include "cmdlib.h"
|
2019-12-28 17:20:47 +00:00
|
|
|
#include "hw_shaderpatcher.h"
|
2022-06-06 09:45:02 +00:00
|
|
|
#include "textures.h"
|
|
|
|
#include "hw_renderstate.h"
|
|
|
|
#include "v_video.h"
|
2019-12-28 17:20:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
static bool IsGlslWhitespace(char c)
|
|
|
|
{
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case ' ':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\t':
|
|
|
|
case '\f':
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 08:04:45 +00:00
|
|
|
static FString NextGlslToken(const char *chars, ptrdiff_t len, ptrdiff_t &pos)
|
2019-12-28 17:20:47 +00:00
|
|
|
{
|
|
|
|
// Eat whitespace
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t tokenStart = pos;
|
2019-12-28 17:20:47 +00:00
|
|
|
while (tokenStart != len && IsGlslWhitespace(chars[tokenStart]))
|
|
|
|
tokenStart++;
|
|
|
|
|
|
|
|
// Find token end
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t tokenEnd = tokenStart;
|
2019-12-28 17:20:47 +00:00
|
|
|
while (tokenEnd != len && !IsGlslWhitespace(chars[tokenEnd]) && chars[tokenEnd] != ';')
|
|
|
|
tokenEnd++;
|
|
|
|
|
|
|
|
pos = tokenEnd;
|
|
|
|
return FString(chars + tokenStart, tokenEnd - tokenStart);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isShaderType(const char *name)
|
|
|
|
{
|
|
|
|
return !strcmp(name, "sampler1D") || !strcmp(name, "sampler2D") || !strcmp(name, "sampler3D") || !strcmp(name, "samplerCube") || !strcmp(name, "sampler2DMS");
|
|
|
|
}
|
|
|
|
|
|
|
|
FString RemoveLegacyUserUniforms(FString code)
|
|
|
|
{
|
|
|
|
// User shaders must declare their uniforms via the GLDEFS file.
|
|
|
|
|
|
|
|
code.Substitute("uniform sampler2D tex;", " ");
|
|
|
|
code.Substitute("uniform float timer;", " ");
|
|
|
|
|
|
|
|
// The following code searches for legacy uniform declarations in the shader itself and replaces them with whitespace.
|
|
|
|
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t len = code.Len();
|
2019-12-28 17:20:47 +00:00
|
|
|
char *chars = code.LockBuffer();
|
|
|
|
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t startIndex = 0;
|
2019-12-28 17:20:47 +00:00
|
|
|
while (true)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t matchIndex = code.IndexOf("uniform", startIndex);
|
2019-12-28 17:20:47 +00:00
|
|
|
if (matchIndex == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bool isLegacyUniformName = false;
|
|
|
|
|
|
|
|
bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]);
|
|
|
|
bool isKeywordEnd = matchIndex + 7 == len || IsGlslWhitespace(chars[matchIndex + 7]);
|
|
|
|
if (isKeywordStart && isKeywordEnd)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t pos = matchIndex + 7;
|
2019-12-28 17:20:47 +00:00
|
|
|
FString type = NextGlslToken(chars, len, pos);
|
|
|
|
FString identifier = NextGlslToken(chars, len, pos);
|
|
|
|
|
|
|
|
isLegacyUniformName = type.Compare("float") == 0 && identifier.Compare("timer") == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isLegacyUniformName)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t statementEndIndex = code.IndexOf(';', matchIndex + 7);
|
2019-12-28 17:20:47 +00:00
|
|
|
if (statementEndIndex == -1)
|
|
|
|
statementEndIndex = len;
|
2021-08-14 08:04:45 +00:00
|
|
|
for (ptrdiff_t i = matchIndex; i <= statementEndIndex; i++)
|
2019-12-28 17:20:47 +00:00
|
|
|
{
|
|
|
|
if (!IsGlslWhitespace(chars[i]))
|
|
|
|
chars[i] = ' ';
|
|
|
|
}
|
|
|
|
startIndex = statementEndIndex;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
startIndex = matchIndex + 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also remove all occurences of the token 'texture2d'. Some shaders may still use this deprecated function to access a sampler.
|
|
|
|
// Modern GLSL only allows use of 'texture'.
|
|
|
|
while (true)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t matchIndex = code.IndexOf("texture2d", startIndex);
|
2019-12-28 17:20:47 +00:00
|
|
|
if (matchIndex == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Check if this is a real token.
|
|
|
|
bool isKeywordStart = matchIndex == 0 || !isalnum(chars[matchIndex - 1] & 255);
|
|
|
|
bool isKeywordEnd = matchIndex + 9 == len || !isalnum(chars[matchIndex + 9] & 255);
|
|
|
|
if (isKeywordStart && isKeywordEnd)
|
|
|
|
{
|
|
|
|
chars[matchIndex + 7] = chars[matchIndex + 8] = ' ';
|
|
|
|
}
|
|
|
|
startIndex = matchIndex + 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
code.UnlockBuffer();
|
|
|
|
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t len = code.Len();
|
2019-12-28 17:20:47 +00:00
|
|
|
char *chars = code.LockBuffer();
|
|
|
|
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t startIndex = 0;
|
2021-11-14 14:03:50 +00:00
|
|
|
ptrdiff_t startpos, endpos = 0;
|
2019-12-28 17:20:47 +00:00
|
|
|
while (true)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t matchIndex = code.IndexOf("layout(binding", startIndex);
|
2019-12-28 17:20:47 +00:00
|
|
|
if (matchIndex == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bool isSamplerUniformName = false;
|
|
|
|
|
|
|
|
bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]);
|
|
|
|
bool isKeywordEnd = matchIndex + 14 == len || IsGlslWhitespace(chars[matchIndex + 14]) || chars[matchIndex + 14] == '=';
|
|
|
|
if (isKeywordStart && isKeywordEnd)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t pos = matchIndex + 14;
|
2019-12-28 17:20:47 +00:00
|
|
|
startpos = matchIndex;
|
|
|
|
while (IsGlslWhitespace(chars[pos])) pos++;
|
|
|
|
if (chars[pos] == '=')
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
pos++;
|
|
|
|
auto val = strtol(&chars[pos], &p, 0);
|
|
|
|
if (p != &chars[pos])
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
pos = (p - chars);
|
2019-12-28 17:20:47 +00:00
|
|
|
while (IsGlslWhitespace(chars[pos])) pos++;
|
|
|
|
if (chars[pos] == ')')
|
|
|
|
{
|
|
|
|
endpos = ++pos;
|
|
|
|
FString uniform = NextGlslToken(chars, len, pos);
|
|
|
|
FString type = NextGlslToken(chars, len, pos);
|
|
|
|
FString identifier = NextGlslToken(chars, len, pos);
|
|
|
|
|
2023-10-07 21:48:50 +00:00
|
|
|
isSamplerUniformName = uniform.Compare("uniform") == 0 && isShaderType(type.GetChars());
|
2019-12-28 17:20:47 +00:00
|
|
|
if (isSamplerUniformName)
|
|
|
|
{
|
|
|
|
samplerstobind.Push(std::make_pair(identifier, val));
|
2021-12-24 08:56:02 +00:00
|
|
|
for (auto posi = startpos; posi < endpos; posi++)
|
2019-12-28 17:20:47 +00:00
|
|
|
{
|
2021-12-24 08:56:02 +00:00
|
|
|
if (!IsGlslWhitespace(chars[posi]))
|
|
|
|
chars[posi] = ' ';
|
2019-12-28 17:20:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isSamplerUniformName)
|
|
|
|
{
|
|
|
|
startIndex = endpos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
startIndex = matchIndex + 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code.UnlockBuffer();
|
|
|
|
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword)
|
|
|
|
{
|
|
|
|
char *chars = code.LockBuffer();
|
|
|
|
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t startIndex = 0;
|
2019-12-28 17:20:47 +00:00
|
|
|
while (true)
|
|
|
|
{
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t matchIndex = code.IndexOf("layout(location", startIndex);
|
2019-12-28 17:20:47 +00:00
|
|
|
if (matchIndex == -1)
|
|
|
|
break;
|
|
|
|
|
2021-08-14 08:04:45 +00:00
|
|
|
ptrdiff_t endIndex = matchIndex;
|
2019-12-28 17:20:47 +00:00
|
|
|
|
|
|
|
// Find end of layout declaration
|
|
|
|
while (chars[endIndex] != ')' && chars[endIndex] != 0)
|
|
|
|
endIndex++;
|
|
|
|
|
|
|
|
if (chars[endIndex] == ')')
|
|
|
|
endIndex++;
|
|
|
|
else if (chars[endIndex] == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Skip whitespace
|
|
|
|
while (IsGlslWhitespace(chars[endIndex]))
|
|
|
|
endIndex++;
|
|
|
|
|
|
|
|
// keyword following the declaration?
|
|
|
|
bool keywordFound = true;
|
2023-10-07 16:44:31 +00:00
|
|
|
ptrdiff_t i;
|
2019-12-28 17:20:47 +00:00
|
|
|
for (i = 0; inoutkeyword[i] != 0; i++)
|
|
|
|
{
|
|
|
|
if (chars[endIndex + i] != inoutkeyword[i])
|
|
|
|
{
|
|
|
|
keywordFound = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (keywordFound && IsGlslWhitespace(chars[endIndex + i]))
|
|
|
|
{
|
|
|
|
// yes - replace declaration with spaces
|
2021-12-24 08:56:02 +00:00
|
|
|
for (auto ii = matchIndex; ii < endIndex; ii++)
|
|
|
|
chars[ii] = ' ';
|
2019-12-28 17:20:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
startIndex = endIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
code.UnlockBuffer();
|
|
|
|
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Note: the MaterialShaderIndex enum in gl_shader.h needs to be updated whenever this array is modified.
|
|
|
|
const FDefaultShader defaultshaders[] =
|
|
|
|
{
|
2020-04-26 21:17:54 +00:00
|
|
|
{"Default", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Warp 1", "shaders/glsl/func_warp1.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Warp 2", "shaders/glsl/func_warp2.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Specular", "shaders/glsl/func_spec.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"},
|
|
|
|
{"PBR","shaders/glsl/func_pbr.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"},
|
2020-09-26 06:50:40 +00:00
|
|
|
{"Paletted", "shaders/glsl/func_paletted.fp", "shaders/glsl/material_nolight.fp", "#define PALETTE_EMULATION\n"},
|
2020-04-26 21:17:54 +00:00
|
|
|
{"No Texture", "shaders/glsl/func_notexture.fp", "shaders/glsl/material_normal.fp", "#define NO_LAYERS\n"},
|
|
|
|
{"Basic Fuzz", "shaders/glsl/fuzz_standard.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Smooth Fuzz", "shaders/glsl/fuzz_smooth.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Swirly Fuzz", "shaders/glsl/fuzz_swirly.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Translucent Fuzz", "shaders/glsl/fuzz_smoothtranslucent.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Noise Fuzz", "shaders/glsl/fuzz_noise.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp", "shaders/glsl/material_normal.fp", ""},
|
|
|
|
{"Software Fuzz", "shaders/glsl/fuzz_software.fp", "shaders/glsl/material_normal.fp", ""},
|
2019-12-28 17:20:47 +00:00
|
|
|
{nullptr,nullptr,nullptr,nullptr}
|
|
|
|
};
|
|
|
|
|
|
|
|
const FEffectShader effectshaders[] =
|
|
|
|
{
|
2020-04-26 21:17:54 +00:00
|
|
|
{ "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", nullptr, nullptr, "#define NO_ALPHATEST\n" },
|
|
|
|
{ "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" },
|
|
|
|
{ "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
|
|
|
|
{ "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
|
2019-12-28 17:20:47 +00:00
|
|
|
};
|
2022-06-06 09:45:02 +00:00
|
|
|
|
|
|
|
int DFrameBuffer::GetShaderCount()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; defaultshaders[i].ShaderName != nullptr; i++);
|
|
|
|
|
|
|
|
return MAX_PASS_TYPES * (countof(defaultshaders) - 1 + usershaders.Size() + MAX_EFFECTS + SHADER_NoTexture);
|
|
|
|
}
|
|
|
|
|