diff --git a/src/gl/shaders/gl_postprocessshader.cpp b/src/gl/shaders/gl_postprocessshader.cpp index 1bd7bc394d..0baf065312 100644 --- a/src/gl/shaders/gl_postprocessshader.cpp +++ b/src/gl/shaders/gl_postprocessshader.cpp @@ -69,72 +69,29 @@ void FCustomPostProcessShaders::Run(FString target) void PostProcessShaderInstance::Run() { - if (!Program) - { - const char *lumpName = Desc->ShaderLumpName.GetChars(); - int lump = Wads.CheckNumForFullName(lumpName); - if (lump == -1) I_FatalError("Unable to load '%s'", lumpName); - FString code = Wads.ReadLump(lump).GetString().GetChars(); + if (!IsShaderSupported()) + return; - Program.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); // Hmm, should this use shader.shaderversion? - Program.Compile(FShaderProgram::Fragment, lumpName, code, "", Desc->ShaderVersion); - Program.SetFragDataLocation(0, "FragColor"); - Program.Link(Desc->ShaderLumpName.GetChars()); - Program.SetAttribLocation(0, "PositionInProjection"); - InputTexture.Init(Program, "InputTexture"); - CustomTexture.Init(Program, "CustomTexture"); + CompileShader(); - if (Desc->Texture) - { - HWTexture = new FHardwareTexture(Desc->Texture->GetWidth(), Desc->Texture->GetHeight(), false); - HWTexture->CreateTexture((unsigned char*)Desc->Texture->GetPixelsBgra(), Desc->Texture->GetWidth(), Desc->Texture->GetHeight(), 0, false, 0, "CustomTexture"); - } - } + if (!Desc->Enabled) + return; FGLDebug::PushGroup(Desc->ShaderLumpName.GetChars()); FGLPostProcessState savedState; - savedState.SaveTextureBindings(2); GLRenderer->mBuffers->BindNextFB(); GLRenderer->mBuffers->BindCurrentTexture(0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - Program.Bind(); + mProgram.Bind(); - TMap::Iterator it1f(Desc->Uniform1f); - TMap::Pair *pair1f; - while (it1f.NextPair(pair1f)) - { - int location = glGetUniformLocation(Program, pair1f->Key.GetChars()); - if (location != -1) - glUniform1f(location, pair1f->Value); - } + UpdateUniforms(); - TMap::Iterator it1i(Desc->Uniform1i); - TMap::Pair *pair1i; - while (it1i.NextPair(pair1i)) - { - int location = glGetUniformLocation(Program, pair1i->Key.GetChars()); - if (location != -1) - glUniform1i(location, pair1i->Value); - } + mInputTexture.Set(0); - InputTexture.Set(0); - - if (HWTexture) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, HWTexture->GetTextureHandle(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glActiveTexture(GL_TEXTURE0); - - CustomTexture.Set(1); - } GLRenderer->RenderScreenQuad(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -143,3 +100,95 @@ void PostProcessShaderInstance::Run() FGLDebug::PopGroup(); } + +bool PostProcessShaderInstance::IsShaderSupported() +{ + int activeShaderVersion = (int)round(gl.glslversion * 10) * 10; + return activeShaderVersion >= Desc->ShaderVersion; +} + +void PostProcessShaderInstance::CompileShader() +{ + if (mProgram) + return; + + // Get the custom shader + const char *lumpName = Desc->ShaderLumpName.GetChars(); + int lump = Wads.CheckNumForFullName(lumpName); + if (lump == -1) I_FatalError("Unable to load '%s'", lumpName); + FString code = Wads.ReadLump(lump).GetString().GetChars(); + + // Build an uniform block to use be used as input + // (this is technically not an uniform block, but it could be changed into that for Vulkan GLSL support) + FString uniformBlock; + TMap::Iterator it(Desc->Uniforms); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + FString type; + FString name = pair->Key; + + switch (pair->Value.Type) + { + case PostProcessUniformType::Float: type = "float"; break; + case PostProcessUniformType::Int: type = "int"; break; + case PostProcessUniformType::Vec2: type = "vec2"; break; + case PostProcessUniformType::Vec3: type = "vec3"; break; + default: break; + } + + if (!type.IsEmpty()) + uniformBlock.AppendFormat("uniform %s %s;\n", type.GetChars(), name.GetChars()); + } + + // Build the input textures + FString uniformTextures; + uniformTextures += "uniform sampler2D InputTexture;\n"; + + // Setup pipeline + FString pipelineInOut; + pipelineInOut += "in vec2 TexCoord;\n"; + pipelineInOut += "out vec4 FragColor;\n"; + + FString prolog; + prolog += uniformBlock; + prolog += uniformTextures; + prolog += pipelineInOut; + + mProgram.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", Desc->ShaderVersion); + mProgram.Compile(FShaderProgram::Fragment, lumpName, code, prolog.GetChars(), Desc->ShaderVersion); + mProgram.SetFragDataLocation(0, "FragColor"); + mProgram.Link(Desc->ShaderLumpName.GetChars()); + mProgram.SetAttribLocation(0, "PositionInProjection"); + mInputTexture.Init(mProgram, "InputTexture"); +} + +void PostProcessShaderInstance::UpdateUniforms() +{ + TMap::Iterator it(Desc->Uniforms); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + int location = glGetUniformLocation(mProgram, pair->Key.GetChars()); + if (location != -1) + { + switch (pair->Value.Type) + { + case PostProcessUniformType::Float: + glUniform1f(location, (float)pair->Value.Values[0]); + break; + case PostProcessUniformType::Int: + glUniform1i(location, (int)pair->Value.Values[0]); + break; + case PostProcessUniformType::Vec2: + glUniform2f(location, (float)pair->Value.Values[0], (float)pair->Value.Values[1]); + break; + case PostProcessUniformType::Vec3: + glUniform3f(location, (float)pair->Value.Values[0], (float)pair->Value.Values[1], (float)pair->Value.Values[2]); + break; + default: + break; + } + } + } +} diff --git a/src/gl/shaders/gl_postprocessshader.h b/src/gl/shaders/gl_postprocessshader.h index 171b439458..876cb9f56c 100644 --- a/src/gl/shaders/gl_postprocessshader.h +++ b/src/gl/shaders/gl_postprocessshader.h @@ -4,16 +4,31 @@ class PostProcessShaderInstance; +enum class PostProcessUniformType +{ + Undefined, + Int, + Float, + Vec2, + Vec3 +}; + +struct PostProcessUniformValue +{ + PostProcessUniformType Type = PostProcessUniformType::Undefined; + double Values[4] = { 0.0, 0.0, 0.0, 0.0 }; +}; + struct PostProcessShader { FString Target; FString ShaderLumpName; int ShaderVersion = 0; - FTexture *Texture = nullptr; FString Name; - TMap Uniform1i; - TMap Uniform1f; + bool Enabled = false; + + TMap Uniforms; }; extern TArray PostProcessShaders; @@ -26,10 +41,14 @@ public: void Run(); PostProcessShader *Desc; - FShaderProgram Program; - FBufferedUniformSampler InputTexture; - FBufferedUniformSampler CustomTexture; - FHardwareTexture *HWTexture = nullptr; + +private: + bool IsShaderSupported(); + void CompileShader(); + void UpdateUniforms(); + + FShaderProgram mProgram; + FBufferedUniformSampler mInputTexture; }; class FCustomPostProcessShaders diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index e153dc6536..758f854358 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -37,6 +37,7 @@ #include "sc_man.h" #include "cmdlib.h" #include "vm.h" +#include "d_player.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_debug.h" @@ -698,11 +699,28 @@ void gl_ParseHardwareShader(FScanner &sc, int deflump) sc.MustGetString(); shaderdesc.Name = sc.String; } - else if (sc.Compare("texture")) + else if (sc.Compare("uniform")) { sc.MustGetString(); - FTextureID no = TexMan.CheckForTexture(sc.String, FTexture::TEX_Wall); - shaderdesc.Texture = TexMan[no]; + FString uniformType = sc.String; + uniformType.ToLower(); + + sc.MustGetString(); + FString uniformName = sc.String; + + PostProcessUniformType parsedType = PostProcessUniformType::Undefined; + + if (uniformType.Compare("int") == 0) + parsedType = PostProcessUniformType::Int; + else if (uniformType.Compare("float") == 0) + parsedType = PostProcessUniformType::Float; + else if (uniformType.Compare("vec2") == 0) + parsedType = PostProcessUniformType::Vec2; + else if (uniformType.Compare("vec3") == 0) + parsedType = PostProcessUniformType::Vec3; + + if (parsedType != PostProcessUniformType::Undefined) + shaderdesc.Uniforms[uniformName].Type = parsedType; } } @@ -769,18 +787,110 @@ void gl_ParseHardwareShader(FScanner &sc, int deflump) } } +static bool IsConsolePlayer(player_t *player) +{ + AActor *activator = player ? player->mo : nullptr; + if (activator == nullptr || activator->player == nullptr) + return false; + return int(activator->player - players) == consoleplayer; +} + +DEFINE_ACTION_FUNCTION(_Shader, SetEnabled) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_BOOL_DEF(value); + + if (IsConsolePlayer(player)) + { + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + shader.Enabled = value; + } + } + return 0; +} + DEFINE_ACTION_FUNCTION(_Shader, SetUniform1f) { PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); PARAM_STRING(shaderName); PARAM_STRING(uniformName); PARAM_FLOAT_DEF(value); - for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + if (IsConsolePlayer(player)) { - PostProcessShader &shader = PostProcessShaders[i]; - if (shader.Name == shaderName) - shader.Uniform1f[uniformName] = value; + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = value; + vec4[1] = 0.0; + vec4[2] = 0.0; + vec4[3] = 1.0; + } + } + } + return 0; +} + +DEFINE_ACTION_FUNCTION(_Shader, SetUniform2f) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_STRING(uniformName); + PARAM_FLOAT_DEF(x); + PARAM_FLOAT_DEF(y); + + if (IsConsolePlayer(player)) + { + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = x; + vec4[1] = y; + vec4[2] = 0.0; + vec4[3] = 1.0; + } + } + } + return 0; +} + +DEFINE_ACTION_FUNCTION(_Shader, SetUniform3f) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_STRING(uniformName); + PARAM_FLOAT_DEF(x); + PARAM_FLOAT_DEF(y); + PARAM_FLOAT_DEF(z); + + if (IsConsolePlayer(player)) + { + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = x; + vec4[1] = y; + vec4[2] = z; + vec4[3] = 1.0; + } + } } return 0; } @@ -788,15 +898,25 @@ DEFINE_ACTION_FUNCTION(_Shader, SetUniform1f) DEFINE_ACTION_FUNCTION(_Shader, SetUniform1i) { PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); PARAM_STRING(shaderName); PARAM_STRING(uniformName); PARAM_INT_DEF(value); - for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + if (IsConsolePlayer(player)) { - PostProcessShader &shader = PostProcessShaders[i]; - if (shader.Name == shaderName) - shader.Uniform1i[uniformName] = value; + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = (double)value; + vec4[1] = 0.0; + vec4[2] = 0.0; + vec4[3] = 1.0; + } + } } return 0; } diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index c215768d14..f35e39993c 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -777,6 +777,9 @@ class Lighting : SectorEffect native struct Shader native { - native static void SetUniform1f(string shaderName, string uniformName, float value); - native static void SetUniform1i(string shaderName, string uniformName, int value); + native clearscope static void SetEnabled(PlayerInfo player, string shaderName, bool enable); + native clearscope static void SetUniform1f(PlayerInfo player, string shaderName, string uniformName, float value); + native clearscope static void SetUniform2f(PlayerInfo player, string shaderName, string uniformName, vector2 value); + native clearscope static void SetUniform3f(PlayerInfo player, string shaderName, string uniformName, vector3 value); + native clearscope static void SetUniform1i(PlayerInfo player, string shaderName, string uniformName, int value); }