qzdoom/src/gl/shaders/gl_postprocessshader.cpp
Christoph Oelckers 401c9ab8ca - moved gl_interface.cpp/h to gl_load folder.
These files are not part of the actual renderer but part of the system code.
This means, for separated modern and legacy GL renderers, there still will only be one set of this, unlike everything else.
2018-05-16 23:21:21 +02:00

250 lines
7.2 KiB
C++

//
//---------------------------------------------------------------------------
//
// Copyright(C) 2017 Magnus Norddahl
// 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/
//
//--------------------------------------------------------------------------
//
#include "gl/system/gl_system.h"
#include "v_video.h"
#include "w_wad.h"
#include "gl_load/gl_interface.h"
#include "gl/system/gl_debug.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_postprocessstate.h"
#include "gl/renderer/gl_renderbuffers.h"
#include "gl/shaders/gl_postprocessshader.h"
#include "gl/shaders/gl_postprocessshaderinstance.h"
#include "textures/bitmap.h"
CVAR(Bool, gl_custompost, true, 0)
TArray<PostProcessShader> PostProcessShaders;
FCustomPostProcessShaders::FCustomPostProcessShaders()
{
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
{
mShaders.push_back(std::unique_ptr<PostProcessShaderInstance>(new PostProcessShaderInstance(&PostProcessShaders[i])));
}
}
FCustomPostProcessShaders::~FCustomPostProcessShaders()
{
}
void FCustomPostProcessShaders::Run(FString target)
{
if (!gl_custompost)
return;
for (auto &shader : mShaders)
{
if (shader->Desc->Target == target)
{
shader->Run();
}
}
}
/////////////////////////////////////////////////////////////////////////////
PostProcessShaderInstance::~PostProcessShaderInstance()
{
for (const auto &it : mTextureHandles)
glDeleteTextures(1, (GLuint*)&it.second);
}
void PostProcessShaderInstance::Run()
{
if (!IsShaderSupported())
return;
CompileShader();
if (!Desc->Enabled)
return;
FGLDebug::PushGroup(Desc->ShaderLumpName.GetChars());
FGLPostProcessState savedState;
savedState.SaveTextureBindings(1 + Desc->Textures.CountUsed());
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);
mProgram.Bind();
UpdateUniforms();
BindTextures();
mInputTexture.Set(0);
GLRenderer->RenderScreenQuad();
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLRenderer->mBuffers->NextTexture();
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<FString, PostProcessUniformValue>::Iterator it(Desc->Uniforms);
TMap<FString, PostProcessUniformValue>::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";
TMap<FString, FString>::Iterator itTextures(Desc->Textures);
TMap<FString, FString>::Pair *pairTextures;
while (itTextures.NextPair(pairTextures))
{
uniformTextures.AppendFormat("uniform sampler2D %s;\n", pairTextures->Key.GetChars());
}
// 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<FString, PostProcessUniformValue>::Iterator it(Desc->Uniforms);
TMap<FString, PostProcessUniformValue>::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;
}
}
}
}
void PostProcessShaderInstance::BindTextures()
{
int textureUnit = 1;
TMap<FString, FString>::Iterator it(Desc->Textures);
TMap<FString, FString>::Pair *pair;
while (it.NextPair(pair))
{
int location = glGetUniformLocation(mProgram, pair->Key.GetChars());
if (location == -1)
continue;
FString name = pair->Value;
FTexture *tex = TexMan(TexMan.CheckForTexture(name, ETextureType::Any));
if (tex && tex->UseType != ETextureType::Null)
{
glUniform1i(location, textureUnit);
glActiveTexture(GL_TEXTURE0 + 1);
auto it = mTextureHandles.find(tex);
if (it == mTextureHandles.end())
{
FBitmap bitmap;
bitmap.Create(tex->GetWidth(), tex->GetHeight());
tex->CopyTrueColorPixels(&bitmap, 0, 0);
GLuint handle = 0;
glGenTextures(1, &handle);
glBindTexture(GL_TEXTURE_2D, handle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex->GetWidth(), tex->GetHeight(), 0, GL_BGRA, GL_UNSIGNED_BYTE, bitmap.GetPixels());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
mTextureHandles[tex] = handle;
}
else
{
glBindTexture(GL_TEXTURE_2D, it->second);
}
textureUnit++;
}
}
}