mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +00:00
- added the needed parts of GZDoom's render backend to have the postprocessor working.
Not hooked up yet.
This commit is contained in:
parent
c4219f40be
commit
a021b96119
37 changed files with 4929 additions and 24 deletions
|
@ -613,6 +613,11 @@ file( GLOB HEADER_FILES
|
|||
common/rendering/*.h
|
||||
common/rendering/gl_load/*.h
|
||||
common/rendering/gl/system/*.h
|
||||
common/rendering/gl/renderer/*.h
|
||||
common/rendering/gl/shaders/*.h
|
||||
common/rendering/hwrenderer/data/*.h
|
||||
common/rendering/hwrenderer/postprocessing/*.h
|
||||
common/rendering/hwrenderer/utility/*.h
|
||||
|
||||
build/src/*.h
|
||||
platform/win32/*.h
|
||||
|
@ -660,7 +665,6 @@ set (PCH_SOURCES
|
|||
glbackend/glbackend.cpp
|
||||
glbackend/gl_palmanager.cpp
|
||||
glbackend/gl_texture.cpp
|
||||
glbackend/gl_buffers.cpp
|
||||
glbackend/hw_draw2d.cpp
|
||||
|
||||
thirdparty/src/base64.cpp
|
||||
|
@ -811,13 +815,21 @@ set (PCH_SOURCES
|
|||
#common/input/i_input.cpp
|
||||
common/input/m_joy.cpp
|
||||
|
||||
common/rendering/v_video.cpp
|
||||
common/rendering/v_framebuffer.cpp
|
||||
common/rendering/r_videoscale.cpp
|
||||
common/rendering/gl_load/gl_interface.cpp
|
||||
common/rendering/v_framebuffer.cpp
|
||||
common/rendering/v_video.cpp
|
||||
common/rendering/gl/renderer/gl_renderer.cpp
|
||||
common/rendering/gl/renderer/gl_postprocess.cpp
|
||||
common/rendering/gl/renderer/gl_postprocessstate.cpp
|
||||
common/rendering/gl/renderer/gl_renderbuffers.cpp
|
||||
common/rendering/gl/shaders/gl_shaderprogram.cpp
|
||||
common/rendering/gl/system/gl_buffers.cpp
|
||||
common/rendering/gl/system/gl_debug.cpp
|
||||
common/rendering/gl/system/gl_framebuffer.cpp
|
||||
|
||||
common/rendering/gl_load/gl_interface.cpp
|
||||
common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp
|
||||
common/rendering/hwrenderer/postprocessing/hw_postprocess_cvars.cpp
|
||||
common/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
|
||||
)
|
||||
|
||||
if( MSVC )
|
||||
|
@ -882,9 +894,13 @@ include_directories(
|
|||
common/dobject
|
||||
common/menu
|
||||
common/input
|
||||
common/rendering/gl/system
|
||||
common/rendering/gl_load
|
||||
common/rendering/gl
|
||||
common/rendering/gl/system
|
||||
common/rendering/gl/renderer
|
||||
common/rendering/gl/shaders
|
||||
common/rendering/hwrenderer/data
|
||||
common/rendering/hwrenderer/postprocessing
|
||||
common/rendering/hwrenderer/utility
|
||||
common/rendering
|
||||
platform
|
||||
|
||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -503,6 +503,7 @@ CUSTOM_CVARD(Float, vid_saturation, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adju
|
|||
}
|
||||
|
||||
CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
|
||||
CCMD (bumpgamma)
|
||||
{
|
||||
|
|
|
@ -82,7 +82,7 @@ EXTERN_CVAR(Int, r_upscalefactor)
|
|||
EXTERN_CVAR(Float, vid_gamma)
|
||||
EXTERN_CVAR(Float, vid_contrast)
|
||||
EXTERN_CVAR(Float, vid_brightness)
|
||||
|
||||
EXTERN_CVAR(Int, gl_multisample)
|
||||
|
||||
EXTERN_CVAR(Bool, in_joystick)
|
||||
EXTERN_CVAR(Int, in_mouse)
|
||||
|
|
255
source/common/rendering/gl/renderer/gl_postprocess.cpp
Normal file
255
source/common/rendering/gl/renderer/gl_postprocess.cpp
Normal file
|
@ -0,0 +1,255 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_postprocess.cpp
|
||||
** Post processing effects in the render pipeline
|
||||
**
|
||||
*/
|
||||
|
||||
#include "gl_load/gl_system.h"
|
||||
#include "m_png.h"
|
||||
#include "gl/system/gl_buffers.h"
|
||||
#include "gl/system/gl_framebuffer.h"
|
||||
#include "gamecvars.h"
|
||||
#include "gl/system/gl_debug.h"
|
||||
//#include "gl/renderer/gl_renderstate.h"
|
||||
#include "gl/renderer/gl_renderbuffers.h"
|
||||
#include "gl/renderer/gl_renderer.h"
|
||||
#include "gl/renderer/gl_postprocessstate.h"
|
||||
#include "gl/shaders/gl_shaderprogram.h"
|
||||
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
||||
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
|
||||
//#include "hwrenderer/data/flatvertices.h"
|
||||
#include "r_videoscale.h"
|
||||
|
||||
extern bool vid_hdr_active;
|
||||
|
||||
CVAR(Int, gl_dither_bpc, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
|
||||
void FGLRenderer::RenderScreenQuad()
|
||||
{
|
||||
// The buffer here needs to be enabled later again, of course.
|
||||
#if 0
|
||||
auto buffer = static_cast<GLVertexBuffer *>(screen->mVertexData->GetBufferObjects().first);
|
||||
buffer->Bind(nullptr);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4);
|
||||
#else
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(-1, -1);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(-1, 1);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(1, -1);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(1,1);
|
||||
glEnd();
|
||||
#endif
|
||||
}
|
||||
|
||||
void FGLRenderer::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
|
||||
{
|
||||
int sceneWidth = mBuffers->GetSceneWidth();
|
||||
int sceneHeight = mBuffers->GetSceneHeight();
|
||||
|
||||
GLPPRenderState renderstate(mBuffers);
|
||||
|
||||
hw_postprocess.Pass1(&renderstate, fixedcm, sceneWidth, sceneHeight);
|
||||
mBuffers->BindCurrentFB();
|
||||
afterBloomDrawEndScene2D();
|
||||
hw_postprocess.Pass2(&renderstate, fixedcm, sceneWidth, sceneHeight);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Adds ambient occlusion to the scene
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FGLRenderer::AmbientOccludeScene(float m5)
|
||||
{
|
||||
int sceneWidth = mBuffers->GetSceneWidth();
|
||||
int sceneHeight = mBuffers->GetSceneHeight();
|
||||
|
||||
GLPPRenderState renderstate(mBuffers);
|
||||
hw_postprocess.ssao.Render(&renderstate, m5, sceneWidth, sceneHeight);
|
||||
}
|
||||
|
||||
void FGLRenderer::BlurScene(float gameinfobluramount)
|
||||
{
|
||||
int sceneWidth = mBuffers->GetSceneWidth();
|
||||
int sceneHeight = mBuffers->GetSceneHeight();
|
||||
|
||||
GLPPRenderState renderstate(mBuffers);
|
||||
|
||||
hw_postprocess.bloom.RenderBlur(&renderstate, sceneWidth, sceneHeight, gameinfobluramount);
|
||||
}
|
||||
|
||||
void FGLRenderer::ClearTonemapPalette()
|
||||
{
|
||||
hw_postprocess.tonemap.ClearTonemapPalette();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copies the rendered screen to its final destination
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FGLRenderer::Flush()
|
||||
{
|
||||
CopyToBackbuffer(nullptr, true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Gamma correct while copying to frame buffer
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FGLRenderer::CopyToBackbuffer(const IntRect *bounds, bool applyGamma)
|
||||
{
|
||||
//screen->Draw2D(); // draw all pending 2D stuff before copying the buffer
|
||||
//screen->Clear2D();
|
||||
|
||||
GLPPRenderState renderstate(mBuffers);
|
||||
|
||||
FGLDebug::PushGroup("CopyToBackbuffer");
|
||||
FGLPostProcessState savedState;
|
||||
savedState.SaveTextureBindings(2);
|
||||
mBuffers->BindOutputFB();
|
||||
|
||||
IntRect box;
|
||||
if (bounds)
|
||||
{
|
||||
box = *bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearBorders();
|
||||
box = screen->mOutputLetterbox;
|
||||
}
|
||||
|
||||
mBuffers->BindCurrentTexture(0);
|
||||
DrawPresentTexture(box, applyGamma);
|
||||
FGLDebug::PopGroup();
|
||||
}
|
||||
|
||||
void FGLRenderer::DrawPresentTexture(const IntRect &box, bool applyGamma)
|
||||
{
|
||||
glViewport(box.left, box.top, box.width, box.height);
|
||||
|
||||
mBuffers->BindDitherTexture(1);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (ViewportLinearScale())
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
mPresentShader->Bind();
|
||||
if (!applyGamma || framebuffer->IsHWGammaActive())
|
||||
{
|
||||
mPresentShader->Uniforms->InvGamma = 1.0f;
|
||||
mPresentShader->Uniforms->Contrast = 1.0f;
|
||||
mPresentShader->Uniforms->Brightness = 0.0f;
|
||||
mPresentShader->Uniforms->Saturation = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
mPresentShader->Uniforms->InvGamma = 1.0f / clamp<float>(vid_gamma, 0.1f, 4.f);
|
||||
mPresentShader->Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
|
||||
mPresentShader->Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
|
||||
mPresentShader->Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.f);
|
||||
mPresentShader->Uniforms->GrayFormula = static_cast<int>(gl_satformula);
|
||||
}
|
||||
if (vid_hdr_active && framebuffer->IsFullscreen())
|
||||
{
|
||||
// Full screen exclusive mode treats a rgba16f frame buffer as linear.
|
||||
// It probably will eventually in desktop mode too, but the DWM doesn't seem to support that.
|
||||
mPresentShader->Uniforms->HdrMode = 1;
|
||||
mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 1023.0f : (float)((1 << gl_dither_bpc) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mPresentShader->Uniforms->HdrMode = 0;
|
||||
mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1);
|
||||
}
|
||||
mPresentShader->Uniforms->Scale = { screen->mScreenViewport.width / (float)mBuffers->GetWidth(), screen->mScreenViewport.height / (float)mBuffers->GetHeight() };
|
||||
mPresentShader->Uniforms->Offset = { 0.0f, 0.0f };
|
||||
mPresentShader->Uniforms.SetData();
|
||||
static_cast<GLDataBuffer*>(mPresentShader->Uniforms.GetBuffer())->BindBase();
|
||||
RenderScreenQuad();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Fills the black bars around the screen letterbox
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FGLRenderer::ClearBorders()
|
||||
{
|
||||
const auto &box = screen->mOutputLetterbox;
|
||||
|
||||
int clientWidth = framebuffer->GetClientWidth();
|
||||
int clientHeight = framebuffer->GetClientHeight();
|
||||
if (clientWidth == 0 || clientHeight == 0)
|
||||
return;
|
||||
|
||||
glViewport(0, 0, clientWidth, clientHeight);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
if (box.top > 0)
|
||||
{
|
||||
glScissor(0, 0, clientWidth, box.top);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
if (clientHeight - box.top - box.height > 0)
|
||||
{
|
||||
glScissor(0, box.top + box.height, clientWidth, clientHeight - box.top - box.height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
if (box.left > 0)
|
||||
{
|
||||
glScissor(0, box.top, box.left, box.height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
if (clientWidth - box.left - box.width > 0)
|
||||
{
|
||||
glScissor(box.left + box.width, box.top, clientWidth - box.left - box.width, box.height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
}
|
140
source/common/rendering/gl/renderer/gl_postprocessstate.cpp
Normal file
140
source/common/rendering/gl/renderer/gl_postprocessstate.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_postprocessstate.cpp
|
||||
** Render state maintenance
|
||||
**
|
||||
**/
|
||||
|
||||
#include "templates.h"
|
||||
#include "gl_load/gl_system.h"
|
||||
#include "gl_load/gl_interface.h"
|
||||
#include "gl/renderer/gl_postprocessstate.h"
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Saves state modified by post processing shaders
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FGLPostProcessState::FGLPostProcessState()
|
||||
{
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
SaveTextureBindings(1);
|
||||
|
||||
glGetBooleanv(GL_BLEND, &blendEnabled);
|
||||
glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
|
||||
glGetBooleanv(GL_DEPTH_TEST, &depthEnabled);
|
||||
glGetBooleanv(GL_MULTISAMPLE, &multisampleEnabled);
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
|
||||
glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRgb);
|
||||
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRgb);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRgb);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
|
||||
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void FGLPostProcessState::SaveTextureBindings(unsigned int numUnits)
|
||||
{
|
||||
while (textureBinding.Size() < numUnits)
|
||||
{
|
||||
unsigned int i = textureBinding.Size();
|
||||
|
||||
GLint texture;
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
textureBinding.Push(texture);
|
||||
|
||||
GLint sampler;
|
||||
glGetIntegerv(GL_SAMPLER_BINDING, &sampler);
|
||||
glBindSampler(i, 0);
|
||||
samplerBinding.Push(sampler);
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Restores state at the end of post processing
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FGLPostProcessState::~FGLPostProcessState()
|
||||
{
|
||||
if (blendEnabled)
|
||||
glEnable(GL_BLEND);
|
||||
else
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (scissorEnabled)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
else
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
if (depthEnabled)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
else
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
if (multisampleEnabled)
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
else
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
|
||||
glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha);
|
||||
glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha);
|
||||
|
||||
glUseProgram(currentProgram);
|
||||
|
||||
// Fully unbind to avoid incomplete texture warnings from Nvidia's driver when gl_debug_level 4 is active
|
||||
for (unsigned int i = 0; i < textureBinding.Size(); i++)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < samplerBinding.Size(); i++)
|
||||
{
|
||||
glBindSampler(i, samplerBinding[i]);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < textureBinding.Size(); i++)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_2D, textureBinding[i]);
|
||||
}
|
||||
|
||||
glActiveTexture(activeTex);
|
||||
}
|
||||
|
||||
}
|
41
source/common/rendering/gl/renderer/gl_postprocessstate.h
Normal file
41
source/common/rendering/gl/renderer/gl_postprocessstate.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef __GL_POSTPROCESSSTATE_H
|
||||
#define __GL_POSTPROCESSSTATE_H
|
||||
|
||||
#include <string.h>
|
||||
#include "gl_load/gl_interface.h"
|
||||
#include "matrix.h"
|
||||
#include "c_cvars.h"
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
class FGLPostProcessState
|
||||
{
|
||||
public:
|
||||
FGLPostProcessState();
|
||||
~FGLPostProcessState();
|
||||
|
||||
void SaveTextureBindings(unsigned int numUnits);
|
||||
|
||||
private:
|
||||
FGLPostProcessState(const FGLPostProcessState &) = delete;
|
||||
FGLPostProcessState &operator=(const FGLPostProcessState &) = delete;
|
||||
|
||||
GLint activeTex;
|
||||
TArray<GLint> textureBinding;
|
||||
TArray<GLint> samplerBinding;
|
||||
GLboolean blendEnabled;
|
||||
GLboolean scissorEnabled;
|
||||
GLboolean depthEnabled;
|
||||
GLboolean multisampleEnabled;
|
||||
GLint currentProgram;
|
||||
GLint blendEquationRgb;
|
||||
GLint blendEquationAlpha;
|
||||
GLint blendSrcRgb;
|
||||
GLint blendSrcAlpha;
|
||||
GLint blendDestRgb;
|
||||
GLint blendDestAlpha;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
933
source/common/rendering/gl/renderer/gl_renderbuffers.cpp
Normal file
933
source/common/rendering/gl/renderer/gl_renderbuffers.cpp
Normal file
|
@ -0,0 +1,933 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_renderbuffers.cpp
|
||||
** Render buffers used during rendering
|
||||
**
|
||||
*/
|
||||
|
||||
#include "gl_load/gl_system.h"
|
||||
#include "v_video.h"
|
||||
#include "printf.h"
|
||||
#include "gl_load/gl_interface.h"
|
||||
#include "gamecvars.h"
|
||||
#include "gl/system/gl_debug.h"
|
||||
#include "gl/renderer/gl_renderer.h"
|
||||
#include "gl/renderer/gl_renderbuffers.h"
|
||||
#include "gl/renderer/gl_postprocessstate.h"
|
||||
#include "gl/shaders/gl_shaderprogram.h"
|
||||
#include "gl/system/gl_buffers.h"
|
||||
#include <random>
|
||||
|
||||
EXTERN_CVAR(Int, gl_debug_level)
|
||||
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Initialize render buffers and textures used in rendering passes
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FGLRenderBuffers::FGLRenderBuffers()
|
||||
{
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Free render buffer resources
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FGLRenderBuffers::~FGLRenderBuffers()
|
||||
{
|
||||
ClearScene();
|
||||
ClearPipeline();
|
||||
ClearEyeBuffers();
|
||||
DeleteTexture(mDitherTexture);
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::ClearScene()
|
||||
{
|
||||
DeleteFrameBuffer(mSceneFB);
|
||||
DeleteFrameBuffer(mSceneDataFB);
|
||||
DeleteRenderBuffer(mSceneMultisampleBuf);
|
||||
DeleteRenderBuffer(mSceneFogBuf);
|
||||
DeleteRenderBuffer(mSceneNormalBuf);
|
||||
DeleteRenderBuffer(mSceneDepthStencilBuf);
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::ClearPipeline()
|
||||
{
|
||||
for (int i = 0; i < NumPipelineTextures; i++)
|
||||
{
|
||||
DeleteFrameBuffer(mPipelineFB[i]);
|
||||
DeleteTexture(mPipelineTexture[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::ClearEyeBuffers()
|
||||
{
|
||||
for (auto handle : mEyeFBs)
|
||||
DeleteFrameBuffer(handle);
|
||||
|
||||
for (auto handle : mEyeTextures)
|
||||
DeleteTexture(handle);
|
||||
|
||||
mEyeTextures.Clear();
|
||||
mEyeFBs.Clear();
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::DeleteTexture(PPGLTexture &tex)
|
||||
{
|
||||
if (tex.handle != 0)
|
||||
glDeleteTextures(1, &tex.handle);
|
||||
tex.handle = 0;
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::DeleteRenderBuffer(PPGLRenderBuffer &buf)
|
||||
{
|
||||
if (buf.handle != 0)
|
||||
glDeleteRenderbuffers(1, &buf.handle);
|
||||
buf.handle = 0;
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::DeleteFrameBuffer(PPGLFrameBuffer &fb)
|
||||
{
|
||||
if (fb.handle != 0)
|
||||
glDeleteFramebuffers(1, &fb.handle);
|
||||
fb.handle = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Makes sure all render buffers have sizes suitable for rending at the
|
||||
// specified resolution
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHeight)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
I_FatalError("Requested invalid render buffer sizes: screen = %dx%d", width, height);
|
||||
|
||||
int samples = clamp((int)gl_multisample, 0, mMaxSamples);
|
||||
|
||||
GLint activeTex;
|
||||
GLint textureBinding;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
||||
|
||||
if (width != mWidth || height != mHeight)
|
||||
CreatePipeline(width, height);
|
||||
|
||||
if (width != mWidth || height != mHeight || mSamples != samples)
|
||||
CreateScene(width, height, samples, false);
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mSamples = samples;
|
||||
mSceneWidth = sceneWidth;
|
||||
mSceneHeight = sceneHeight;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureBinding);
|
||||
glActiveTexture(activeTex);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (FailedCreate)
|
||||
{
|
||||
ClearScene();
|
||||
ClearPipeline();
|
||||
ClearEyeBuffers();
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mSamples = 0;
|
||||
mSceneWidth = 0;
|
||||
mSceneHeight = 0;
|
||||
I_FatalError("Unable to create render buffers.");
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates the scene buffers
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::CreateScene(int width, int height, int samples, bool needsSceneTextures)
|
||||
{
|
||||
ClearScene();
|
||||
|
||||
if (samples > 1)
|
||||
{
|
||||
if (needsSceneTextures)
|
||||
{
|
||||
mSceneMultisampleTex = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false);
|
||||
mSceneDepthStencilTex = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false);
|
||||
mSceneFogTex = Create2DMultisampleTexture("SceneFog", GL_RGBA8, width, height, samples, false);
|
||||
mSceneNormalTex = Create2DMultisampleTexture("SceneNormal", GL_RGB10_A2, width, height, samples, false);
|
||||
mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisampleTex, {}, {}, mSceneDepthStencilTex, true);
|
||||
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisampleTex, mSceneFogTex, mSceneNormalTex, mSceneDepthStencilTex, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSceneMultisampleBuf = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, width, height, samples);
|
||||
mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples);
|
||||
mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisampleBuf, mSceneDepthStencilBuf);
|
||||
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisampleBuf, mSceneDepthStencilBuf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (needsSceneTextures)
|
||||
{
|
||||
mSceneDepthStencilTex = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
|
||||
mSceneFogTex = Create2DTexture("SceneFog", GL_RGBA8, width, height);
|
||||
mSceneNormalTex = Create2DTexture("SceneNormal", GL_RGB10_A2, width, height);
|
||||
mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], {}, {}, mSceneDepthStencilTex, false);
|
||||
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneFogTex, mSceneNormalTex, mSceneDepthStencilTex, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
|
||||
mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mSceneDepthStencilBuf);
|
||||
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneDepthStencilBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates the buffers needed for post processing steps
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::CreatePipeline(int width, int height)
|
||||
{
|
||||
ClearPipeline();
|
||||
ClearEyeBuffers();
|
||||
|
||||
for (int i = 0; i < NumPipelineTextures; i++)
|
||||
{
|
||||
mPipelineTexture[i] = Create2DTexture("PipelineTexture", GL_RGBA16F, width, height);
|
||||
mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates eye buffers if needed
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::CreateEyeBuffers(int eye)
|
||||
{
|
||||
if (mEyeFBs.Size() > unsigned(eye))
|
||||
return;
|
||||
|
||||
GLint activeTex, textureBinding, frameBufferBinding;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);
|
||||
|
||||
while (mEyeFBs.Size() <= unsigned(eye))
|
||||
{
|
||||
PPGLTexture texture = Create2DTexture("EyeTexture", GL_RGBA16F, mWidth, mHeight);
|
||||
mEyeTextures.Push(texture);
|
||||
mEyeFBs.Push(CreateFrameBuffer("EyeFB", texture));
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureBinding);
|
||||
glActiveTexture(activeTex);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates a 2D texture defaulting to linear filtering and clamp to edge
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PPGLTexture FGLRenderBuffers::Create2DTexture(const char *name, GLuint format, int width, int height, const void *data)
|
||||
{
|
||||
PPGLTexture tex;
|
||||
tex.Width = width;
|
||||
tex.Height = height;
|
||||
glGenTextures(1, &tex.handle);
|
||||
glBindTexture(GL_TEXTURE_2D, tex.handle);
|
||||
FGLDebug::LabelObject(GL_TEXTURE, tex.handle, name);
|
||||
|
||||
GLenum dataformat = 0, datatype = 0;
|
||||
switch (format)
|
||||
{
|
||||
case GL_RGBA8: dataformat = GL_RGBA; datatype = GL_UNSIGNED_BYTE; break;
|
||||
case GL_RGBA16: dataformat = GL_RGBA; datatype = GL_UNSIGNED_SHORT; break;
|
||||
case GL_RGBA16F: dataformat = GL_RGBA; datatype = GL_FLOAT; break;
|
||||
case GL_RGBA32F: dataformat = GL_RGBA; datatype = GL_FLOAT; break;
|
||||
case GL_RGBA16_SNORM: dataformat = GL_RGBA; datatype = GL_SHORT; break;
|
||||
case GL_R32F: dataformat = GL_RED; datatype = GL_FLOAT; break;
|
||||
case GL_R16F: dataformat = GL_RED; datatype = GL_FLOAT; break;
|
||||
case GL_RG32F: dataformat = GL_RG; datatype = GL_FLOAT; break;
|
||||
case GL_RG16F: dataformat = GL_RG; datatype = GL_FLOAT; break;
|
||||
case GL_RGB10_A2: dataformat = GL_RGBA; datatype = GL_UNSIGNED_INT_10_10_10_2; break;
|
||||
case GL_DEPTH_COMPONENT24: dataformat = GL_DEPTH_COMPONENT; datatype = GL_FLOAT; break;
|
||||
case GL_STENCIL_INDEX8: dataformat = GL_STENCIL_INDEX; datatype = GL_INT; break;
|
||||
case GL_DEPTH24_STENCIL8: dataformat = GL_DEPTH_STENCIL; datatype = GL_UNSIGNED_INT_24_8; break;
|
||||
default: I_FatalError("Unknown format passed to FGLRenderBuffers.Create2DTexture");
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, dataformat, datatype, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return tex;
|
||||
}
|
||||
|
||||
PPGLTexture FGLRenderBuffers::Create2DMultisampleTexture(const char *name, GLuint format, int width, int height, int samples, bool fixedSampleLocations)
|
||||
{
|
||||
PPGLTexture tex;
|
||||
tex.Width = width;
|
||||
tex.Height = height;
|
||||
glGenTextures(1, &tex.handle);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex.handle);
|
||||
FGLDebug::LabelObject(GL_TEXTURE, tex.handle, name);
|
||||
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, fixedSampleLocations);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
|
||||
return tex;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates a render buffer
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PPGLRenderBuffer FGLRenderBuffers::CreateRenderBuffer(const char *name, GLuint format, int width, int height)
|
||||
{
|
||||
PPGLRenderBuffer buf;
|
||||
glGenRenderbuffers(1, &buf.handle);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, buf.handle);
|
||||
FGLDebug::LabelObject(GL_RENDERBUFFER, buf.handle, name);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
|
||||
return buf;
|
||||
}
|
||||
|
||||
PPGLRenderBuffer FGLRenderBuffers::CreateRenderBuffer(const char *name, GLuint format, int width, int height, int samples)
|
||||
{
|
||||
if (samples <= 1)
|
||||
return CreateRenderBuffer(name, format, width, height);
|
||||
|
||||
PPGLRenderBuffer buf;
|
||||
glGenRenderbuffers(1, &buf.handle);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, buf.handle);
|
||||
FGLDebug::LabelObject(GL_RENDERBUFFER, buf.handle, name);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, format, width, height);
|
||||
return buf;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates a frame buffer
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLTexture colorbuffer)
|
||||
{
|
||||
PPGLFrameBuffer fb;
|
||||
glGenFramebuffers(1, &fb.handle);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer.handle, 0);
|
||||
if (CheckFrameBufferCompleteness())
|
||||
ClearFrameBuffer(false, false);
|
||||
return fb;
|
||||
}
|
||||
|
||||
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLTexture colorbuffer, PPGLRenderBuffer depthstencil)
|
||||
{
|
||||
PPGLFrameBuffer fb;
|
||||
glGenFramebuffers(1, &fb.handle);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer.handle, 0);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
|
||||
if (CheckFrameBufferCompleteness())
|
||||
ClearFrameBuffer(true, true);
|
||||
return fb;
|
||||
}
|
||||
|
||||
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLRenderBuffer colorbuffer, PPGLRenderBuffer depthstencil)
|
||||
{
|
||||
PPGLFrameBuffer fb;
|
||||
glGenFramebuffers(1, &fb.handle);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer.handle);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
|
||||
if (CheckFrameBufferCompleteness())
|
||||
ClearFrameBuffer(true, true);
|
||||
return fb;
|
||||
}
|
||||
|
||||
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLTexture colorbuffer0, PPGLTexture colorbuffer1, PPGLTexture colorbuffer2, PPGLTexture depthstencil, bool multisample)
|
||||
{
|
||||
PPGLFrameBuffer fb;
|
||||
glGenFramebuffers(1, &fb.handle);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||
if (multisample)
|
||||
{
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer0.handle, 0);
|
||||
if (colorbuffer1.handle != 0)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer1.handle, 0);
|
||||
if (colorbuffer2.handle != 0)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer2.handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, depthstencil.handle, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer0.handle, 0);
|
||||
if (colorbuffer1.handle != 0)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorbuffer1.handle, 0);
|
||||
if (colorbuffer2.handle != 0)
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorbuffer2.handle, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil.handle, 0);
|
||||
}
|
||||
if (CheckFrameBufferCompleteness())
|
||||
ClearFrameBuffer(true, true);
|
||||
return fb;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Verifies that the frame buffer setup is valid
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FGLRenderBuffers::CheckFrameBufferCompleteness()
|
||||
{
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (result == GL_FRAMEBUFFER_COMPLETE)
|
||||
return true;
|
||||
|
||||
bool FailedCreate = true;
|
||||
|
||||
if (gl_debug_level > 0)
|
||||
{
|
||||
FString error = "glCheckFramebufferStatus failed: ";
|
||||
switch (result)
|
||||
{
|
||||
default: error.AppendFormat("error code %d", (int)result); break;
|
||||
case GL_FRAMEBUFFER_UNDEFINED: error << "GL_FRAMEBUFFER_UNDEFINED"; break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break;
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: error << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: error << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break;
|
||||
}
|
||||
Printf("%s\n", error.GetChars());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Clear frame buffer to make sure it never contains uninitialized data
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::ClearFrameBuffer(bool stencil, bool depth)
|
||||
{
|
||||
GLboolean scissorEnabled;
|
||||
GLint stencilValue;
|
||||
GLdouble depthValue;
|
||||
glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
|
||||
glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilValue);
|
||||
glGetDoublev(GL_DEPTH_CLEAR_VALUE, &depthValue);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClearDepth(0.0);
|
||||
glClearStencil(0);
|
||||
GLenum flags = GL_COLOR_BUFFER_BIT;
|
||||
if (stencil)
|
||||
flags |= GL_STENCIL_BUFFER_BIT;
|
||||
if (depth)
|
||||
flags |= GL_DEPTH_BUFFER_BIT;
|
||||
glClear(flags);
|
||||
glClearStencil(stencilValue);
|
||||
glClearDepth(depthValue);
|
||||
if (scissorEnabled)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Resolves the multisample frame buffer by copying it to the first pipeline texture
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BlitSceneToTexture()
|
||||
{
|
||||
mCurrentPipelineTexture = 0;
|
||||
|
||||
if (mSamples <= 1)
|
||||
return;
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mSceneFB.handle);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture].handle);
|
||||
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0)
|
||||
{
|
||||
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Eye textures and their frame buffers
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BlitToEyeTexture(int eye, bool allowInvalidate)
|
||||
{
|
||||
CreateEyeBuffers(eye);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture].handle);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mEyeFBs[eye].handle);
|
||||
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0 && allowInvalidate)
|
||||
{
|
||||
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::BlitFromEyeTexture(int eye)
|
||||
{
|
||||
if (mEyeFBs.Size() <= unsigned(eye)) return;
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture].handle);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, mEyeFBs[eye].handle);
|
||||
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0)
|
||||
{
|
||||
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
|
||||
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::BindEyeTexture(int eye, int texunit)
|
||||
{
|
||||
CreateEyeBuffers(eye);
|
||||
glActiveTexture(GL_TEXTURE0 + texunit);
|
||||
glBindTexture(GL_TEXTURE_2D, mEyeTextures[eye].handle);
|
||||
}
|
||||
|
||||
void FGLRenderBuffers::BindDitherTexture(int texunit)
|
||||
{
|
||||
if (!mDitherTexture)
|
||||
{
|
||||
static const float data[64] =
|
||||
{
|
||||
.0078125, .2578125, .1328125, .3828125, .0234375, .2734375, .1484375, .3984375,
|
||||
.7578125, .5078125, .8828125, .6328125, .7734375, .5234375, .8984375, .6484375,
|
||||
.0703125, .3203125, .1953125, .4453125, .0859375, .3359375, .2109375, .4609375,
|
||||
.8203125, .5703125, .9453125, .6953125, .8359375, .5859375, .9609375, .7109375,
|
||||
.0390625, .2890625, .1640625, .4140625, .0546875, .3046875, .1796875, .4296875,
|
||||
.7890625, .5390625, .9140625, .6640625, .8046875, .5546875, .9296875, .6796875,
|
||||
.1015625, .3515625, .2265625, .4765625, .1171875, .3671875, .2421875, .4921875,
|
||||
.8515625, .6015625, .9765625, .7265625, .8671875, .6171875, .9921875, .7421875,
|
||||
};
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + texunit);
|
||||
mDitherTexture = Create2DTexture("DitherTexture", GL_R32F, 8, 8, data);
|
||||
}
|
||||
mDitherTexture.Bind(1, GL_NEAREST, GL_REPEAT);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Makes the scene frame buffer active (multisample, depth, stecil, etc.)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindSceneFB(bool sceneData)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, sceneData ? mSceneDataFB.handle : mSceneFB.handle);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Binds the scene color texture to the specified texture unit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindSceneColorTexture(int index)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
if (mSamples > 1)
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneMultisampleTex.handle);
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, mPipelineTexture[0].handle);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Binds the scene fog data texture to the specified texture unit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindSceneFogTexture(int index)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
if (mSamples > 1)
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneFogTex.handle);
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, mSceneFogTex.handle);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Binds the scene normal data texture to the specified texture unit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindSceneNormalTexture(int index)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
if (mSamples > 1)
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneNormalTex.handle);
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, mSceneNormalTex.handle);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Binds the depth texture to the specified texture unit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindSceneDepthTexture(int index)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
if (mSamples > 1)
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneDepthStencilTex.handle);
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, mSceneDepthStencilTex.handle);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Binds the current scene/effect/hud texture to the specified texture unit
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindCurrentTexture(int index, int filter, int wrap)
|
||||
{
|
||||
mPipelineTexture[mCurrentPipelineTexture].Bind(index, filter, wrap);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Makes the frame buffer for the current texture active
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindCurrentFB()
|
||||
{
|
||||
mPipelineFB[mCurrentPipelineTexture].Bind();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Makes the frame buffer for the next texture active
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindNextFB()
|
||||
{
|
||||
int out = (mCurrentPipelineTexture + 1) % NumPipelineTextures;
|
||||
mPipelineFB[out].Bind();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Next pipeline texture now contains the output
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::NextTexture()
|
||||
{
|
||||
mCurrentPipelineTexture = (mCurrentPipelineTexture + 1) % NumPipelineTextures;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Makes the screen frame buffer active
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FGLRenderBuffers::BindOutputFB()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Returns true if render buffers are supported and should be used
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FGLRenderBuffers::FailedCreate = false;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates or updates textures used by postprocess effects
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PPGLTextureBackend *GLPPRenderState::GetGLTexture(PPTexture *texture)
|
||||
{
|
||||
if (!texture->Backend)
|
||||
{
|
||||
FGLPostProcessState savedState;
|
||||
|
||||
auto backend = std::make_unique<PPGLTextureBackend>();
|
||||
|
||||
int glformat;
|
||||
switch (texture->Format)
|
||||
{
|
||||
default:
|
||||
case PixelFormat::Rgba8: glformat = GL_RGBA8; break;
|
||||
case PixelFormat::Rgba16f: glformat = GL_RGBA16F; break;
|
||||
case PixelFormat::R32f: glformat = GL_R32F; break;
|
||||
case PixelFormat::Rg16f: glformat = GL_RG16F; break;
|
||||
case PixelFormat::Rgba16_snorm: glformat = GL_RGBA16_SNORM; break;
|
||||
}
|
||||
|
||||
if (texture->Data)
|
||||
backend->Tex = buffers->Create2DTexture("PPTexture", glformat, texture->Width, texture->Height, texture->Data.get());
|
||||
else
|
||||
backend->Tex = buffers->Create2DTexture("PPTexture", glformat, texture->Width, texture->Height);
|
||||
|
||||
texture->Backend = std::move(backend);
|
||||
}
|
||||
return static_cast<PPGLTextureBackend*>(texture->Backend.get());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Compile the shaders declared by post process effects
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FShaderProgram *GLPPRenderState::GetGLShader(PPShader *shader)
|
||||
{
|
||||
if (!shader->Backend)
|
||||
{
|
||||
auto glshader = std::make_unique<FShaderProgram>();
|
||||
|
||||
FString prolog;
|
||||
if (!shader->Uniforms.empty())
|
||||
prolog = UniformBlockDecl::Create("Uniforms", shader->Uniforms, POSTPROCESS_BINDINGPOINT);
|
||||
prolog += shader->Defines;
|
||||
|
||||
glshader->Compile(FShaderProgram::Vertex, shader->VertexShader, "", shader->Version);
|
||||
glshader->Compile(FShaderProgram::Fragment, shader->FragmentShader, prolog, shader->Version);
|
||||
glshader->Link(shader->FragmentShader.GetChars());
|
||||
if (!shader->Uniforms.empty())
|
||||
glshader->SetUniformBufferLocation(POSTPROCESS_BINDINGPOINT, "Uniforms");
|
||||
|
||||
shader->Backend = std::move(glshader);
|
||||
}
|
||||
return static_cast<FShaderProgram*>(shader->Backend.get());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Renders one post process effect
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLPPRenderState::Draw()
|
||||
{
|
||||
FGLPostProcessState savedState;
|
||||
|
||||
// Bind input textures
|
||||
for (unsigned int index = 0; index < Textures.Size(); index++)
|
||||
{
|
||||
savedState.SaveTextureBindings(index + 1);
|
||||
|
||||
const PPTextureInput &input = Textures[index];
|
||||
int filter = (input.Filter == PPFilterMode::Nearest) ? GL_NEAREST : GL_LINEAR;
|
||||
int wrap = (input.Wrap == PPWrapMode::Clamp) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
|
||||
|
||||
switch (input.Type)
|
||||
{
|
||||
default:
|
||||
case PPTextureType::CurrentPipelineTexture:
|
||||
buffers->BindCurrentTexture(index, filter, wrap);
|
||||
break;
|
||||
|
||||
case PPTextureType::NextPipelineTexture:
|
||||
I_FatalError("PPTextureType::NextPipelineTexture not allowed as input\n");
|
||||
break;
|
||||
|
||||
case PPTextureType::PPTexture:
|
||||
GetGLTexture(input.Texture)->Tex.Bind(index, filter, wrap);
|
||||
break;
|
||||
|
||||
case PPTextureType::SceneColor:
|
||||
buffers->BindSceneColorTexture(index);
|
||||
break;
|
||||
|
||||
case PPTextureType::SceneFog:
|
||||
buffers->BindSceneFogTexture(index);
|
||||
break;
|
||||
|
||||
case PPTextureType::SceneNormal:
|
||||
buffers->BindSceneNormalTexture(index);
|
||||
break;
|
||||
|
||||
case PPTextureType::SceneDepth:
|
||||
buffers->BindSceneDepthTexture(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set render target
|
||||
switch (Output.Type)
|
||||
{
|
||||
default:
|
||||
I_FatalError("Unsupported postprocess output type\n");
|
||||
break;
|
||||
|
||||
case PPTextureType::CurrentPipelineTexture:
|
||||
buffers->BindCurrentFB();
|
||||
break;
|
||||
|
||||
case PPTextureType::NextPipelineTexture:
|
||||
buffers->BindNextFB();
|
||||
break;
|
||||
|
||||
case PPTextureType::PPTexture:
|
||||
if (GetGLTexture(Output.Texture)->FB)
|
||||
GetGLTexture(Output.Texture)->FB.Bind();
|
||||
else
|
||||
GetGLTexture(Output.Texture)->FB = buffers->CreateFrameBuffer("PPTextureFB"/*Output.Texture.GetChars()*/, GetGLTexture(Output.Texture)->Tex);
|
||||
break;
|
||||
|
||||
case PPTextureType::SceneColor:
|
||||
buffers->BindSceneFB(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set blend mode
|
||||
if (BlendMode.BlendOp == STYLEOP_Add && BlendMode.SrcAlpha == STYLEALPHA_One && BlendMode.DestAlpha == STYLEALPHA_Zero && BlendMode.Flags == 0)
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
else
|
||||
{
|
||||
// To do: support all the modes
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
if (BlendMode.SrcAlpha == STYLEALPHA_One && BlendMode.DestAlpha == STYLEALPHA_One)
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
else
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
// Setup viewport
|
||||
glViewport(Viewport.left, Viewport.top, Viewport.width, Viewport.height);
|
||||
|
||||
auto shader = GetGLShader(Shader);
|
||||
|
||||
// Set uniforms
|
||||
if (Uniforms.Data.Size() > 0)
|
||||
{
|
||||
if (!shader->Uniforms)
|
||||
shader->Uniforms.reset(screen->CreateDataBuffer(POSTPROCESS_BINDINGPOINT, false, false));
|
||||
shader->Uniforms->SetData(Uniforms.Data.Size(), Uniforms.Data.Data());
|
||||
static_cast<GLDataBuffer*>(shader->Uniforms.get())->BindBase();
|
||||
}
|
||||
|
||||
// Set shader
|
||||
shader->Bind();
|
||||
|
||||
// Draw the screen quad
|
||||
GLRenderer->RenderScreenQuad();
|
||||
|
||||
// Advance to next PP texture if our output was sent there
|
||||
if (Output.Type == PPTextureType::NextPipelineTexture)
|
||||
buffers->NextTexture();
|
||||
|
||||
glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
|
||||
}
|
||||
|
||||
void GLPPRenderState::PushGroup(const FString &name)
|
||||
{
|
||||
FGLDebug::PushGroup(name.GetChars());
|
||||
}
|
||||
|
||||
void GLPPRenderState::PopGroup()
|
||||
{
|
||||
FGLDebug::PopGroup();
|
||||
}
|
||||
|
||||
|
||||
// Store the current stereo 3D eye buffer, and Load the next one
|
||||
|
||||
int FGLRenderBuffers::NextEye(int eyeCount)
|
||||
{
|
||||
int nextEye = (mCurrentEye + 1) % eyeCount;
|
||||
if (nextEye == mCurrentEye) return mCurrentEye;
|
||||
BlitToEyeTexture(mCurrentEye);
|
||||
mCurrentEye = nextEye;
|
||||
BlitFromEyeTexture(mCurrentEye);
|
||||
return mCurrentEye;
|
||||
}
|
||||
|
||||
} // namespace OpenGLRenderer
|
205
source/common/rendering/gl/renderer/gl_renderbuffers.h
Normal file
205
source/common/rendering/gl/renderer/gl_renderbuffers.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
class FGLRenderBuffers;
|
||||
|
||||
class PPGLTexture
|
||||
{
|
||||
public:
|
||||
void Bind(int index, int filter = GL_NEAREST, int wrap = GL_CLAMP_TO_EDGE)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
glBindTexture(GL_TEXTURE_2D, handle);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
|
||||
}
|
||||
|
||||
int Width = -1;
|
||||
int Height = -1;
|
||||
|
||||
explicit operator bool() const { return handle != 0; }
|
||||
|
||||
private:
|
||||
GLuint handle = 0;
|
||||
|
||||
friend class FGLRenderBuffers;
|
||||
friend class PPGLTextureBackend;
|
||||
};
|
||||
|
||||
class PPGLFrameBuffer
|
||||
{
|
||||
public:
|
||||
void Bind()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, handle);
|
||||
}
|
||||
|
||||
explicit operator bool() const { return handle != 0; }
|
||||
|
||||
private:
|
||||
GLuint handle = 0;
|
||||
|
||||
friend class FGLRenderBuffers;
|
||||
friend class PPGLTextureBackend;
|
||||
};
|
||||
|
||||
class PPGLRenderBuffer
|
||||
{
|
||||
private:
|
||||
GLuint handle = 0;
|
||||
|
||||
explicit operator bool() const { return handle != 0; }
|
||||
|
||||
friend class FGLRenderBuffers;
|
||||
};
|
||||
|
||||
class PPGLTextureBackend : public PPTextureBackend
|
||||
{
|
||||
public:
|
||||
~PPGLTextureBackend()
|
||||
{
|
||||
if (Tex.handle != 0)
|
||||
{
|
||||
glDeleteTextures(1, &Tex.handle);
|
||||
Tex.handle = 0;
|
||||
}
|
||||
if (FB.handle != 0)
|
||||
{
|
||||
glDeleteFramebuffers(1, &FB.handle);
|
||||
FB.handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PPGLTexture Tex;
|
||||
PPGLFrameBuffer FB;
|
||||
};
|
||||
|
||||
class FShaderProgram;
|
||||
|
||||
class GLPPRenderState : public PPRenderState
|
||||
{
|
||||
public:
|
||||
GLPPRenderState(FGLRenderBuffers *buffers) : buffers(buffers) { }
|
||||
|
||||
void PushGroup(const FString &name) override;
|
||||
void PopGroup() override;
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
PPGLTextureBackend *GetGLTexture(PPTexture *texture);
|
||||
FShaderProgram *GetGLShader(PPShader *shader);
|
||||
|
||||
FGLRenderBuffers *buffers;
|
||||
};
|
||||
|
||||
class FGLRenderBuffers
|
||||
{
|
||||
public:
|
||||
FGLRenderBuffers();
|
||||
~FGLRenderBuffers();
|
||||
|
||||
void Setup(int width, int height, int sceneWidth, int sceneHeight);
|
||||
|
||||
void BindSceneFB(bool sceneData);
|
||||
void BindSceneColorTexture(int index);
|
||||
void BindSceneFogTexture(int index);
|
||||
void BindSceneNormalTexture(int index);
|
||||
void BindSceneDepthTexture(int index);
|
||||
void BlitSceneToTexture();
|
||||
|
||||
void BindCurrentTexture(int index, int filter = GL_NEAREST, int wrap = GL_CLAMP_TO_EDGE);
|
||||
void BindCurrentFB();
|
||||
void BindNextFB();
|
||||
void NextTexture();
|
||||
|
||||
PPGLFrameBuffer GetCurrentFB() const { return mPipelineFB[mCurrentPipelineTexture]; }
|
||||
|
||||
void BindOutputFB();
|
||||
|
||||
void BlitToEyeTexture(int eye, bool allowInvalidate=true);
|
||||
void BlitFromEyeTexture(int eye);
|
||||
void BindEyeTexture(int eye, int texunit);
|
||||
int NextEye(int eyeCount);
|
||||
int & CurrentEye() { return mCurrentEye; }
|
||||
|
||||
void BindDitherTexture(int texunit);
|
||||
|
||||
int GetWidth() const { return mWidth; }
|
||||
int GetHeight() const { return mHeight; }
|
||||
|
||||
int GetSceneWidth() const { return mSceneWidth; }
|
||||
int GetSceneHeight() const { return mSceneHeight; }
|
||||
|
||||
private:
|
||||
void ClearScene();
|
||||
void ClearPipeline();
|
||||
void ClearEyeBuffers();
|
||||
void CreateScene(int width, int height, int samples, bool needsSceneTextures);
|
||||
void CreatePipeline(int width, int height);
|
||||
void CreateEyeBuffers(int eye);
|
||||
|
||||
PPGLTexture Create2DTexture(const char *name, GLuint format, int width, int height, const void *data = nullptr);
|
||||
PPGLTexture Create2DMultisampleTexture(const char *name, GLuint format, int width, int height, int samples, bool fixedSampleLocations);
|
||||
PPGLRenderBuffer CreateRenderBuffer(const char *name, GLuint format, int width, int height);
|
||||
PPGLRenderBuffer CreateRenderBuffer(const char *name, GLuint format, int width, int height, int samples);
|
||||
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer);
|
||||
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer, PPGLRenderBuffer depthstencil);
|
||||
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLRenderBuffer colorbuffer, PPGLRenderBuffer depthstencil);
|
||||
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer0, PPGLTexture colorbuffer1, PPGLTexture colorbuffer2, PPGLTexture depthstencil, bool multisample);
|
||||
bool CheckFrameBufferCompleteness();
|
||||
void ClearFrameBuffer(bool stencil, bool depth);
|
||||
void DeleteTexture(PPGLTexture &handle);
|
||||
void DeleteRenderBuffer(PPGLRenderBuffer &handle);
|
||||
void DeleteFrameBuffer(PPGLFrameBuffer &handle);
|
||||
|
||||
int mWidth = 0;
|
||||
int mHeight = 0;
|
||||
int mSamples = 0;
|
||||
int mMaxSamples = 0;
|
||||
int mSceneWidth = 0;
|
||||
int mSceneHeight = 0;
|
||||
|
||||
static const int NumPipelineTextures = 2;
|
||||
int mCurrentPipelineTexture = 0;
|
||||
|
||||
// Buffers for the scene
|
||||
PPGLTexture mSceneMultisampleTex;
|
||||
PPGLTexture mSceneDepthStencilTex;
|
||||
PPGLTexture mSceneFogTex;
|
||||
PPGLTexture mSceneNormalTex;
|
||||
PPGLRenderBuffer mSceneMultisampleBuf;
|
||||
PPGLRenderBuffer mSceneDepthStencilBuf;
|
||||
PPGLRenderBuffer mSceneFogBuf;
|
||||
PPGLRenderBuffer mSceneNormalBuf;
|
||||
PPGLFrameBuffer mSceneFB;
|
||||
PPGLFrameBuffer mSceneDataFB;
|
||||
|
||||
// Effect/HUD buffers
|
||||
PPGLTexture mPipelineTexture[NumPipelineTextures];
|
||||
PPGLFrameBuffer mPipelineFB[NumPipelineTextures];
|
||||
|
||||
// Eye buffers
|
||||
TArray<PPGLTexture> mEyeTextures;
|
||||
TArray<PPGLFrameBuffer> mEyeFBs;
|
||||
int mCurrentEye = 0;
|
||||
|
||||
// Shadow map texture
|
||||
PPGLTexture mShadowMapTexture;
|
||||
PPGLFrameBuffer mShadowMapFB;
|
||||
int mCurrentShadowMapSize = 0;
|
||||
|
||||
PPGLTexture mDitherTexture;
|
||||
|
||||
static bool FailedCreate;
|
||||
|
||||
friend class GLPPRenderState;
|
||||
};
|
||||
|
||||
}
|
243
source/common/rendering/gl/renderer/gl_renderer.cpp
Normal file
243
source/common/rendering/gl/renderer/gl_renderer.cpp
Normal file
|
@ -0,0 +1,243 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl1_renderer.cpp
|
||||
** Renderer interface
|
||||
**
|
||||
*/
|
||||
|
||||
#include "gl_load/gl_system.h"
|
||||
#include "files.h"
|
||||
#include "v_video.h"
|
||||
#include "m_png.h"
|
||||
#include "filesystem.h"
|
||||
#include "i_time.h"
|
||||
#include "cmdlib.h"
|
||||
//#include "swrenderer/r_swscene.h"
|
||||
//#include "hwrenderer/utility/hw_clock.h"
|
||||
|
||||
#include "gl_load/gl_interface.h"
|
||||
#include "gl/system/gl_framebuffer.h"
|
||||
#include "gamecvars.h"
|
||||
#include "gl/system/gl_debug.h"
|
||||
#include "gl/renderer/gl_renderer.h"
|
||||
//#include "gl/renderer/gl_renderstate.h"
|
||||
#include "gl/renderer/gl_renderbuffers.h"
|
||||
#include "gl/shaders/gl_shaderprogram.h"
|
||||
//#include "hwrenderer/data/flatvertices.h"
|
||||
//#include "gl/textures/gl_samplers.h"
|
||||
//#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
||||
//#include "hwrenderer/data/hw_viewpointbuffer.h"
|
||||
#include "r_videoscale.h"
|
||||
//#include "r_data/models/models.h"
|
||||
#include "gl/renderer/gl_postprocessstate.h"
|
||||
#include "gl/system/gl_buffers.h"
|
||||
|
||||
EXTERN_CVAR(Int, screenblocks)
|
||||
EXTERN_CVAR(Bool, cl_capfps)
|
||||
|
||||
extern bool NoInterpolateView;
|
||||
|
||||
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
FGLRenderer* GLRenderer;
|
||||
//===========================================================================
|
||||
//
|
||||
// Renderer interface
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Initialize
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
|
||||
{
|
||||
framebuffer = fb;
|
||||
}
|
||||
|
||||
void FGLRenderer::Initialize(int width, int height)
|
||||
{
|
||||
mScreenBuffers = new FGLRenderBuffers();
|
||||
mSaveBuffers = new FGLRenderBuffers();
|
||||
mBuffers = mScreenBuffers;
|
||||
mPresentShader = new FPresentShader();
|
||||
mPresent3dCheckerShader = new FPresent3DCheckerShader();
|
||||
mPresent3dColumnShader = new FPresent3DColumnShader();
|
||||
mPresent3dRowShader = new FPresent3DRowShader();
|
||||
|
||||
//glGenQueries(1, &PortalQueryObject);
|
||||
|
||||
// needed for the core profile, because someone decided it was a good idea to remove the default VAO.
|
||||
glGenVertexArrays(1, &mVAOID);
|
||||
glBindVertexArray(mVAOID);
|
||||
FGLDebug::LabelObject(GL_VERTEX_ARRAY, mVAOID, "FGLRenderer.mVAOID");
|
||||
|
||||
mFBID = 0;
|
||||
mOldFBID = 0;
|
||||
|
||||
//mShaderManager = new FShaderManager;
|
||||
//mSamplerManager = new FSamplerManager;
|
||||
}
|
||||
|
||||
FGLRenderer::~FGLRenderer()
|
||||
{
|
||||
//FlushModels();
|
||||
//TexMan.FlushAll();
|
||||
//if (mShaderManager != nullptr) delete mShaderManager;
|
||||
//if (mSamplerManager != nullptr) delete mSamplerManager;
|
||||
if (mFBID != 0) glDeleteFramebuffers(1, &mFBID);
|
||||
if (mVAOID != 0)
|
||||
{
|
||||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(1, &mVAOID);
|
||||
}
|
||||
//if (PortalQueryObject != 0) glDeleteQueries(1, &PortalQueryObject);
|
||||
|
||||
//if (swdrawer) delete swdrawer;
|
||||
if (mBuffers) delete mBuffers;
|
||||
if (mSaveBuffers) delete mSaveBuffers;
|
||||
if (mPresentShader) delete mPresentShader;
|
||||
if (mPresent3dCheckerShader) delete mPresent3dCheckerShader;
|
||||
if (mPresent3dColumnShader) delete mPresent3dColumnShader;
|
||||
if (mPresent3dRowShader) delete mPresent3dRowShader;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FGLRenderer::StartOffscreen()
|
||||
{
|
||||
bool firstBind = (mFBID == 0);
|
||||
if (mFBID == 0)
|
||||
glGenFramebuffers(1, &mFBID);
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mOldFBID);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFBID);
|
||||
if (firstBind)
|
||||
FGLDebug::LabelObject(GL_FRAMEBUFFER, mFBID, "OffscreenFB");
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FGLRenderer::EndOffscreen()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mOldFBID);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FGLRenderer::BindToFrameBuffer(FMaterial *mat)
|
||||
{
|
||||
#if 0
|
||||
auto BaseLayer = static_cast<FHardwareTexture*>(mat->GetLayer(0, 0));
|
||||
|
||||
if (BaseLayer == nullptr)
|
||||
{
|
||||
// must create the hardware texture first
|
||||
BaseLayer->BindOrCreate(mat->sourcetex, 0, 0, 0, 0);
|
||||
FHardwareTexture::Unbind(0);
|
||||
gl_RenderState.ClearLastMaterial();
|
||||
}
|
||||
BaseLayer->BindToFrameBuffer(mat->GetWidth(), mat->GetHeight());
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Render the view to a savegame picture
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
#if 0
|
||||
void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
|
||||
{
|
||||
IntRect bounds;
|
||||
bounds.left = 0;
|
||||
bounds.top = 0;
|
||||
bounds.width = width;
|
||||
bounds.height = height;
|
||||
|
||||
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
|
||||
glFinish();
|
||||
|
||||
// Switch to render buffers dimensioned for the savepic
|
||||
mBuffers = mSaveBuffers;
|
||||
|
||||
hw_ClearFakeFlat();
|
||||
gl_RenderState.SetVertexBuffer(screen->mVertexData);
|
||||
screen->mVertexData->Reset();
|
||||
screen->mLights->Clear();
|
||||
screen->mViewpoints->Clear();
|
||||
|
||||
// This shouldn't overwrite the global viewpoint even for a short time.
|
||||
FRenderViewpoint savevp;
|
||||
sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
gl_RenderState.SetNoSoftLightLevel();
|
||||
CopyToBackbuffer(&bounds, false);
|
||||
|
||||
// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
|
||||
glFinish();
|
||||
|
||||
int numpixels = width * height;
|
||||
uint8_t * scr = (uint8_t *)M_Malloc(numpixels * 3);
|
||||
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
|
||||
|
||||
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, true);
|
||||
M_Free(scr);
|
||||
|
||||
// Switch back the screen render buffers
|
||||
screen->SetViewportRects(nullptr);
|
||||
mBuffers = mScreenBuffers;
|
||||
}
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FGLRenderer::BeginFrame()
|
||||
{
|
||||
mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
|
||||
//mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
|
||||
}
|
||||
|
||||
}
|
121
source/common/rendering/gl/renderer/gl_renderer.h
Normal file
121
source/common/rendering/gl/renderer/gl_renderer.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
#ifndef __GL_RENDERER_H
|
||||
#define __GL_RENDERER_H
|
||||
|
||||
#include "v_video.h"
|
||||
#include "vectors.h"
|
||||
#include "matrix.h"
|
||||
#include "gl/renderer/gl_renderbuffers.h"
|
||||
#include <functional>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244)
|
||||
#endif
|
||||
|
||||
struct particle_t;
|
||||
class FCanvasTexture;
|
||||
class FFlatVertexBuffer;
|
||||
class FSkyVertexBuffer;
|
||||
class FShaderManager;
|
||||
class HWPortal;
|
||||
class FLightBuffer;
|
||||
class DPSprite;
|
||||
class FGLRenderBuffers;
|
||||
class FGL2DDrawer;
|
||||
class FHardwareTexture;
|
||||
class SWSceneDrawer;
|
||||
class HWViewpointBuffer;
|
||||
struct FRenderViewpoint;
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
class FSamplerManager;
|
||||
class OpenGLFrameBuffer;
|
||||
class FPresentShaderBase;
|
||||
class FPresentShader;
|
||||
class FPresent3DCheckerShader;
|
||||
class FPresent3DColumnShader;
|
||||
class FPresent3DRowShader;
|
||||
|
||||
class FGLRenderer
|
||||
{
|
||||
public:
|
||||
|
||||
OpenGLFrameBuffer *framebuffer;
|
||||
int mMirrorCount = 0;
|
||||
int mPlaneMirrorCount = 0;
|
||||
//FShaderManager *mShaderManager = nullptr;
|
||||
//FSamplerManager *mSamplerManager = nullptr;
|
||||
unsigned int mFBID;
|
||||
unsigned int mVAOID;
|
||||
//unsigned int PortalQueryObject;
|
||||
unsigned int mStencilValue = 0;
|
||||
|
||||
int mOldFBID;
|
||||
|
||||
FGLRenderBuffers *mBuffers = nullptr;
|
||||
FGLRenderBuffers *mScreenBuffers = nullptr;
|
||||
FGLRenderBuffers *mSaveBuffers = nullptr;
|
||||
FPresentShader *mPresentShader = nullptr;
|
||||
FPresent3DCheckerShader *mPresent3dCheckerShader = nullptr;
|
||||
FPresent3DColumnShader *mPresent3dColumnShader = nullptr;
|
||||
FPresent3DRowShader *mPresent3dRowShader = nullptr;
|
||||
|
||||
//FRotator mAngles;
|
||||
|
||||
//SWSceneDrawer *swdrawer = nullptr;
|
||||
|
||||
FGLRenderer(OpenGLFrameBuffer *fb);
|
||||
~FGLRenderer() ;
|
||||
|
||||
void Initialize(int width, int height);
|
||||
|
||||
void ClearBorders();
|
||||
|
||||
void PresentStereo();
|
||||
void RenderScreenQuad();
|
||||
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D);
|
||||
void AmbientOccludeScene(float m5);
|
||||
void ClearTonemapPalette();
|
||||
void BlurScene(float gameinfobluramount);
|
||||
void CopyToBackbuffer(const IntRect *bounds, bool applyGamma);
|
||||
void DrawPresentTexture(const IntRect &box, bool applyGamma);
|
||||
void Flush();
|
||||
//void Draw2D(F2DDrawer *data);
|
||||
#if 0
|
||||
void WriteSavePic(player_t *player, FileWriter *file, int width, int height);
|
||||
#endif
|
||||
void BeginFrame();
|
||||
|
||||
|
||||
bool StartOffscreen();
|
||||
void EndOffscreen();
|
||||
|
||||
void BindToFrameBuffer(FMaterial *mat);
|
||||
|
||||
private:
|
||||
|
||||
void DrawScene(HWDrawInfo *di, int drawmode);
|
||||
bool QuadStereoCheckInitialRenderContextState();
|
||||
void PresentAnaglyph(bool r, bool g, bool b);
|
||||
void PresentSideBySide();
|
||||
void PresentTopBottom();
|
||||
void prepareInterleavedPresent(FPresentShaderBase& shader);
|
||||
void PresentColumnInterleaved();
|
||||
void PresentRowInterleaved();
|
||||
void PresentCheckerInterleaved();
|
||||
void PresentQuadStereo();
|
||||
|
||||
};
|
||||
|
||||
struct TexFilter_s
|
||||
{
|
||||
int minfilter;
|
||||
int magfilter;
|
||||
bool mipmapping;
|
||||
} ;
|
||||
|
||||
|
||||
extern FGLRenderer *GLRenderer;
|
||||
|
||||
}
|
||||
#endif
|
355
source/common/rendering/gl/shaders/gl_shaderprogram.cpp
Normal file
355
source/common/rendering/gl/shaders/gl_shaderprogram.cpp
Normal file
|
@ -0,0 +1,355 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_shaderprogram.cpp
|
||||
** GLSL shader program compile and link
|
||||
**
|
||||
*/
|
||||
|
||||
#include "gl_load/gl_system.h"
|
||||
#include "v_video.h"
|
||||
#include "gl_load/gl_interface.h"
|
||||
#include "gamecvars.h"
|
||||
#include "gl/system/gl_debug.h"
|
||||
#include "gl/shaders/gl_shaderprogram.h"
|
||||
#include "hwrenderer/utility/hw_shaderpatcher.h"
|
||||
#include "filesystem.h"
|
||||
#include "printf.h"
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
bool IsShaderCacheActive();
|
||||
TArray<uint8_t> LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat);
|
||||
void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray<uint8_t> &binary, uint32_t binaryFormat);
|
||||
|
||||
FShaderProgram::FShaderProgram()
|
||||
{
|
||||
for (int i = 0; i < NumShaderTypes; i++)
|
||||
mShaders[i] = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Free shader program resources
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FShaderProgram::~FShaderProgram()
|
||||
{
|
||||
if (mProgram != 0)
|
||||
glDeleteProgram(mProgram);
|
||||
|
||||
for (int i = 0; i < NumShaderTypes; i++)
|
||||
{
|
||||
if (mShaders[i] != 0)
|
||||
glDeleteShader(mShaders[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates an OpenGL shader object for the specified type of shader
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FShaderProgram::CreateShader(ShaderType type)
|
||||
{
|
||||
GLenum gltype = 0;
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case Vertex: gltype = GL_VERTEX_SHADER; break;
|
||||
case Fragment: gltype = GL_FRAGMENT_SHADER; break;
|
||||
}
|
||||
mShaders[type] = glCreateShader(gltype);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Compiles a shader and attaches it the program object
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion)
|
||||
{
|
||||
int lump = fileSystem.FindFile(lumpName);
|
||||
if (lump == -1) I_FatalError("Unable to load '%s'", lumpName);
|
||||
FString code = fileSystem.ReadFile(lump).GetString().GetChars();
|
||||
Compile(type, lumpName, code, defines, maxGlslVersion);
|
||||
}
|
||||
|
||||
void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion)
|
||||
{
|
||||
mShaderNames[type] = name;
|
||||
mShaderSources[type] = PatchShader(type, code, defines, maxGlslVersion);
|
||||
}
|
||||
|
||||
void FShaderProgram::CompileShader(ShaderType type)
|
||||
{
|
||||
CreateShader(type);
|
||||
|
||||
const auto &handle = mShaders[type];
|
||||
|
||||
FGLDebug::LabelObject(GL_SHADER, handle, mShaderNames[type]);
|
||||
|
||||
const FString &patchedCode = mShaderSources[type];
|
||||
int lengths[1] = { (int)patchedCode.Len() };
|
||||
const char *sources[1] = { patchedCode.GetChars() };
|
||||
glShaderSource(handle, 1, sources, lengths);
|
||||
|
||||
glCompileShader(handle);
|
||||
|
||||
GLint status = 0;
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||
if (status == GL_FALSE)
|
||||
{
|
||||
I_FatalError("Compile Shader '%s':\n%s\n", mShaderNames[type].GetChars(), GetShaderInfoLog(handle).GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mProgram == 0)
|
||||
mProgram = glCreateProgram();
|
||||
glAttachShader(mProgram, handle);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Links a program with the compiled shaders
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FShaderProgram::Link(const char *name)
|
||||
{
|
||||
FGLDebug::LabelObject(GL_PROGRAM, mProgram, name);
|
||||
|
||||
uint32_t binaryFormat = 0;
|
||||
TArray<uint8_t> binary;
|
||||
#if 0
|
||||
if (IsShaderCacheActive())
|
||||
binary = LoadCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binaryFormat);
|
||||
#endif
|
||||
bool loadedFromBinary = false;
|
||||
if (binary.Size() > 0 && glProgramBinary)
|
||||
{
|
||||
if (mProgram == 0)
|
||||
mProgram = glCreateProgram();
|
||||
glProgramBinary(mProgram, binaryFormat, binary.Data(), binary.Size());
|
||||
GLint status = 0;
|
||||
glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
|
||||
loadedFromBinary = (status == GL_TRUE);
|
||||
}
|
||||
|
||||
if (!loadedFromBinary)
|
||||
{
|
||||
CompileShader(Vertex);
|
||||
CompileShader(Fragment);
|
||||
|
||||
glLinkProgram(mProgram);
|
||||
|
||||
GLint status = 0;
|
||||
glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
|
||||
if (status == GL_FALSE)
|
||||
{
|
||||
I_FatalError("Link Shader '%s':\n%s\n", name, GetProgramInfoLog(mProgram).GetChars());
|
||||
}
|
||||
#if 0
|
||||
else if (glProgramBinary && IsShaderCacheActive())
|
||||
{
|
||||
int binaryLength = 0;
|
||||
glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
||||
binary.Resize(binaryLength);
|
||||
glGetProgramBinary(mProgram, binary.Size(), &binaryLength, &binaryFormat, binary.Data());
|
||||
binary.Resize(binaryLength);
|
||||
SaveCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binary, binaryFormat);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is only for old OpenGL which didn't allow to set the binding from within the shader.
|
||||
if (screen->glslversion < 4.20)
|
||||
{
|
||||
glUseProgram(mProgram);
|
||||
for (auto &uni : samplerstobind)
|
||||
{
|
||||
auto index = glGetUniformLocation(mProgram, uni.first);
|
||||
if (index >= 0)
|
||||
{
|
||||
glUniform1i(index, uni.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
samplerstobind.Clear();
|
||||
samplerstobind.ShrinkToFit();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Set uniform buffer location (only useful for GL 3.3)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FShaderProgram::SetUniformBufferLocation(int index, const char *name)
|
||||
{
|
||||
if (screen->glslversion < 4.20)
|
||||
{
|
||||
GLuint uniformBlockIndex = glGetUniformBlockIndex(mProgram, name);
|
||||
if (uniformBlockIndex != GL_INVALID_INDEX)
|
||||
glUniformBlockBinding(mProgram, uniformBlockIndex, index);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Makes the shader the active program
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FShaderProgram::Bind()
|
||||
{
|
||||
glUseProgram(mProgram);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Returns the shader info log (warnings and compile errors)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString FShaderProgram::GetShaderInfoLog(GLuint handle)
|
||||
{
|
||||
static char buffer[10000];
|
||||
GLsizei length = 0;
|
||||
buffer[0] = 0;
|
||||
glGetShaderInfoLog(handle, 10000, &length, buffer);
|
||||
return FString(buffer);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Returns the program info log (warnings and compile errors)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString FShaderProgram::GetProgramInfoLog(GLuint handle)
|
||||
{
|
||||
static char buffer[10000];
|
||||
GLsizei length = 0;
|
||||
buffer[0] = 0;
|
||||
glGetProgramInfoLog(handle, 10000, &length, buffer);
|
||||
return FString(buffer);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Patches a shader to be compatible with the version of OpenGL in use
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion)
|
||||
{
|
||||
FString patchedCode;
|
||||
|
||||
// If we have 4.2, always use it because it adds important new syntax.
|
||||
if (maxGlslVersion < 420 && gl.glslversion >= 4.2f) maxGlslVersion = 420;
|
||||
int shaderVersion = std::min((int)round(gl.glslversion * 10) * 10, maxGlslVersion);
|
||||
patchedCode.AppendFormat("#version %d\n", shaderVersion);
|
||||
|
||||
// TODO: Find some way to add extension requirements to the patching
|
||||
//
|
||||
// #extension GL_ARB_uniform_buffer_object : require
|
||||
// #extension GL_ARB_shader_storage_buffer_object : require
|
||||
|
||||
if (defines)
|
||||
patchedCode << defines;
|
||||
|
||||
// these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here.
|
||||
patchedCode << "precision highp int;\n";
|
||||
patchedCode << "precision highp float;\n";
|
||||
|
||||
patchedCode << "#line 1\n";
|
||||
patchedCode << RemoveLayoutLocationDecl(code, type == Vertex ? "out" : "in");
|
||||
|
||||
if (maxGlslVersion < 420)
|
||||
{
|
||||
// Here we must strip out all layout(binding) declarations for sampler uniforms and store them in 'samplerstobind' which can then be processed by the link function.
|
||||
patchedCode = RemoveSamplerBindings(patchedCode, samplerstobind);
|
||||
}
|
||||
|
||||
return patchedCode;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FPresentShaderBase::Init(const char * vtx_shader_name, const char * program_name)
|
||||
{
|
||||
FString prolog = Uniforms.CreateDeclaration("Uniforms", PresentUniforms::Desc());
|
||||
|
||||
mShader.reset(new FShaderProgram());
|
||||
mShader->Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", prolog, 330);
|
||||
mShader->Compile(FShaderProgram::Fragment, vtx_shader_name, prolog, 330);
|
||||
mShader->Link(program_name);
|
||||
mShader->SetUniformBufferLocation(Uniforms.BindingPoint(), "Uniforms");
|
||||
Uniforms.Init();
|
||||
}
|
||||
|
||||
void FPresentShader::Bind()
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present.fp", "shaders/glsl/present");
|
||||
}
|
||||
mShader->Bind();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FPresent3DCheckerShader::Bind()
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present_checker3d.fp", "shaders/glsl/presentChecker3d");
|
||||
}
|
||||
mShader->Bind();
|
||||
}
|
||||
|
||||
void FPresent3DColumnShader::Bind()
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present_column3d.fp", "shaders/glsl/presentColumn3d");
|
||||
}
|
||||
mShader->Bind();
|
||||
}
|
||||
|
||||
void FPresent3DRowShader::Bind()
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present_row3d.fp", "shaders/glsl/presentRow3d");
|
||||
}
|
||||
mShader->Bind();
|
||||
}
|
||||
|
||||
|
||||
}
|
93
source/common/rendering/gl/shaders/gl_shaderprogram.h
Normal file
93
source/common/rendering/gl/shaders/gl_shaderprogram.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "gl_load/gl_system.h"
|
||||
//#include "gl_shader.h"
|
||||
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
class FShaderProgram : public PPShaderBackend
|
||||
{
|
||||
public:
|
||||
FShaderProgram();
|
||||
~FShaderProgram();
|
||||
|
||||
enum ShaderType
|
||||
{
|
||||
Vertex,
|
||||
Fragment,
|
||||
NumShaderTypes
|
||||
};
|
||||
|
||||
void Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion);
|
||||
void Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion);
|
||||
void Link(const char *name);
|
||||
void SetUniformBufferLocation(int index, const char *name);
|
||||
|
||||
void Bind();
|
||||
|
||||
GLuint Handle() { return mProgram; }
|
||||
//explicit operator bool() const { return mProgram != 0; }
|
||||
|
||||
std::unique_ptr<IDataBuffer> Uniforms;
|
||||
|
||||
private:
|
||||
FShaderProgram(const FShaderProgram &) = delete;
|
||||
FShaderProgram &operator=(const FShaderProgram &) = delete;
|
||||
|
||||
void CompileShader(ShaderType type);
|
||||
FString PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion);
|
||||
|
||||
void CreateShader(ShaderType type);
|
||||
FString GetShaderInfoLog(GLuint handle);
|
||||
FString GetProgramInfoLog(GLuint handle);
|
||||
|
||||
GLuint mProgram = 0;
|
||||
GLuint mShaders[NumShaderTypes];
|
||||
FString mShaderSources[NumShaderTypes];
|
||||
FString mShaderNames[NumShaderTypes];
|
||||
TArray<std::pair<FString, int>> samplerstobind;
|
||||
};
|
||||
|
||||
class FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
virtual ~FPresentShaderBase() {}
|
||||
virtual void Bind() = 0;
|
||||
|
||||
ShaderUniforms<PresentUniforms, POSTPROCESS_BINDINGPOINT> Uniforms;
|
||||
|
||||
protected:
|
||||
virtual void Init(const char * vtx_shader_name, const char * program_name);
|
||||
std::unique_ptr<FShaderProgram> mShader;
|
||||
};
|
||||
|
||||
class FPresentShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind() override;
|
||||
|
||||
};
|
||||
|
||||
class FPresent3DCheckerShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind() override;
|
||||
};
|
||||
|
||||
class FPresent3DColumnShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind() override;
|
||||
};
|
||||
|
||||
class FPresent3DRowShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind() override;
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include "gl_load.h"
|
||||
#include "glbackend.h"
|
||||
//#include "glbackend.h"
|
||||
#include "gl_buffers.h"
|
||||
|
||||
namespace OpenGLRenderer
|
|
@ -44,9 +44,9 @@ CUSTOM_CVAR(Int, gl_debug_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOIN
|
|||
|
||||
CVAR(Bool, gl_debug_breakpoint, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
|
||||
bool gpuStatActive;
|
||||
bool keepGpuStatActive;
|
||||
FString gpuStatOutput;
|
||||
extern bool gpuStatActive;
|
||||
extern bool keepGpuStatActive;
|
||||
extern FString gpuStatOutput;
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include "v_video.h"
|
||||
#include "printf.h"
|
||||
#include "gl_load/gl_interface.h"
|
||||
//#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "gamecvars.h"
|
||||
|
||||
static TArray<FString> m_Extensions;
|
||||
RenderContext gl;
|
||||
|
|
33
source/common/rendering/hwrenderer/data/renderqueue.h
Normal file
33
source/common/rendering/hwrenderer/data/renderqueue.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tflags.h"
|
||||
|
||||
// A render queue is what contains all render commands.
|
||||
// On Vulkan there can be several of them so this interface is needed to allow for the needed parallelism.
|
||||
// On OpenGL the render state is global so all this will do is to translate the system independent calls into OpenGL API calls.
|
||||
|
||||
enum class ColormaskBits
|
||||
{
|
||||
RED = 1,
|
||||
GREEN = 2,
|
||||
BLUE = 4,
|
||||
ALPHA = 8
|
||||
};
|
||||
|
||||
typedef TFlags<ColormaskBits, uint8_t> Colormask;
|
||||
|
||||
class IRenderQueue
|
||||
{
|
||||
Colormask mColorMask;
|
||||
|
||||
|
||||
Colormask GetColorMask() const
|
||||
{
|
||||
return mColorMask;
|
||||
}
|
||||
|
||||
virtual void SetColorMask(Colormask mask) = 0;
|
||||
|
||||
|
||||
};
|
154
source/common/rendering/hwrenderer/data/shaderuniforms.h
Normal file
154
source/common/rendering/hwrenderer/data/shaderuniforms.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "v_video.h"
|
||||
|
||||
enum
|
||||
{
|
||||
LIGHTBUF_BINDINGPOINT = 1,
|
||||
POSTPROCESS_BINDINGPOINT = 2,
|
||||
VIEWPOINT_BINDINGPOINT = 3,
|
||||
LIGHTNODES_BINDINGPOINT = 4,
|
||||
LIGHTLINES_BINDINGPOINT = 5,
|
||||
LIGHTLIST_BINDINGPOINT = 6
|
||||
};
|
||||
|
||||
enum class UniformType
|
||||
{
|
||||
Int,
|
||||
UInt,
|
||||
Float,
|
||||
Vec2,
|
||||
Vec3,
|
||||
Vec4,
|
||||
IVec2,
|
||||
IVec3,
|
||||
IVec4,
|
||||
UVec2,
|
||||
UVec3,
|
||||
UVec4,
|
||||
Mat4
|
||||
};
|
||||
|
||||
class UniformFieldDesc
|
||||
{
|
||||
public:
|
||||
UniformFieldDesc() { }
|
||||
UniformFieldDesc(const char *name, UniformType type, std::size_t offset) : Name(name), Type(type), Offset(offset) { }
|
||||
|
||||
const char *Name;
|
||||
UniformType Type;
|
||||
std::size_t Offset;
|
||||
};
|
||||
|
||||
class UniformBlockDecl
|
||||
{
|
||||
public:
|
||||
static FString Create(const char *name, const std::vector<UniformFieldDesc> &fields, int bindingpoint)
|
||||
{
|
||||
FString decl;
|
||||
FString layout;
|
||||
if (bindingpoint == -1)
|
||||
{
|
||||
layout = "push_constant";
|
||||
}
|
||||
else if (screen->glslversion < 4.20)
|
||||
{
|
||||
layout = "std140";
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.Format("std140, binding = %d", bindingpoint);
|
||||
}
|
||||
decl.Format("layout(%s) uniform %s\n{\n", layout.GetChars(), name);
|
||||
for (size_t i = 0; i < fields.size(); i++)
|
||||
{
|
||||
decl.AppendFormat("\t%s %s;\n", GetTypeStr(fields[i].Type), fields[i].Name);
|
||||
}
|
||||
decl += "};\n";
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
private:
|
||||
static const char *GetTypeStr(UniformType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case UniformType::Int: return "int";
|
||||
case UniformType::UInt: return "uint";
|
||||
case UniformType::Float: return "float";
|
||||
case UniformType::Vec2: return "vec2";
|
||||
case UniformType::Vec3: return "vec3";
|
||||
case UniformType::Vec4: return "vec4";
|
||||
case UniformType::IVec2: return "ivec2";
|
||||
case UniformType::IVec3: return "ivec3";
|
||||
case UniformType::IVec4: return "ivec4";
|
||||
case UniformType::UVec2: return "uvec2";
|
||||
case UniformType::UVec3: return "uvec3";
|
||||
case UniformType::UVec4: return "uvec4";
|
||||
case UniformType::Mat4: return "mat4";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int bindingpoint>
|
||||
class ShaderUniforms
|
||||
{
|
||||
public:
|
||||
ShaderUniforms()
|
||||
{
|
||||
memset(&Values, 0, sizeof(Values));
|
||||
}
|
||||
|
||||
~ShaderUniforms()
|
||||
{
|
||||
if (mBuffer != nullptr)
|
||||
delete mBuffer;
|
||||
}
|
||||
|
||||
int BindingPoint() const
|
||||
{
|
||||
return bindingpoint;
|
||||
}
|
||||
|
||||
FString CreateDeclaration(const char *name, const std::vector<UniformFieldDesc> &fields)
|
||||
{
|
||||
mFields = fields;
|
||||
return UniformBlockDecl::Create(name, fields, bindingpoint);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
if (mBuffer == nullptr)
|
||||
mBuffer = screen->CreateDataBuffer(bindingpoint, false, false);
|
||||
}
|
||||
|
||||
void SetData()
|
||||
{
|
||||
if (mBuffer != nullptr)
|
||||
mBuffer->SetData(sizeof(T), &Values);
|
||||
}
|
||||
|
||||
IDataBuffer* GetBuffer() const
|
||||
{
|
||||
// OpenGL needs to mess around with this in ways that should not be part of the interface.
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
T *operator->() { return &Values; }
|
||||
const T *operator->() const { return &Values; }
|
||||
|
||||
T Values;
|
||||
|
||||
private:
|
||||
ShaderUniforms(const ShaderUniforms &) = delete;
|
||||
ShaderUniforms &operator=(const ShaderUniforms &) = delete;
|
||||
|
||||
IDataBuffer *mBuffer = nullptr;
|
||||
std::vector<UniformFieldDesc> mFields;
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,826 @@
|
|||
|
||||
#include "v_video.h"
|
||||
#include "hw_postprocess.h"
|
||||
#include "gamecvars.h"
|
||||
#include "stats.h"
|
||||
#include "imagehelpers.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
|
||||
#include <random>
|
||||
|
||||
Postprocess hw_postprocess;
|
||||
|
||||
PPResource *PPResource::First = nullptr;
|
||||
|
||||
bool gpuStatActive = false;
|
||||
bool keepGpuStatActive = false;
|
||||
FString gpuStatOutput;
|
||||
|
||||
ADD_STAT(gpu)
|
||||
{
|
||||
keepGpuStatActive = true;
|
||||
return gpuStatOutput;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPBloom::UpdateTextures(int width, int height)
|
||||
{
|
||||
if (width == lastWidth && height == lastHeight)
|
||||
return;
|
||||
|
||||
int bloomWidth = (width + 1) / 2;
|
||||
int bloomHeight = (height + 1) / 2;
|
||||
|
||||
for (int i = 0; i < NumBloomLevels; i++)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
blevel.Viewport.left = 0;
|
||||
blevel.Viewport.top = 0;
|
||||
blevel.Viewport.width = (bloomWidth + 1) / 2;
|
||||
blevel.Viewport.height = (bloomHeight + 1) / 2;
|
||||
blevel.VTexture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::Rgba16f };
|
||||
blevel.HTexture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::Rgba16f };
|
||||
|
||||
bloomWidth = blevel.Viewport.width;
|
||||
bloomHeight = blevel.Viewport.height;
|
||||
}
|
||||
|
||||
lastWidth = width;
|
||||
lastHeight = height;
|
||||
}
|
||||
|
||||
void PPBloom::RenderBloom(PPRenderState *renderstate, int sceneWidth, int sceneHeight, int fixedcm)
|
||||
{
|
||||
// Only bloom things if enabled and no special fixed light mode is active
|
||||
if (!gl_bloom || sceneWidth <= 0 || sceneHeight <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderstate->PushGroup("bloom");
|
||||
|
||||
UpdateTextures(sceneWidth, sceneHeight);
|
||||
|
||||
ExtractUniforms extractUniforms;
|
||||
extractUniforms.Scale = screen->SceneScale();
|
||||
extractUniforms.Offset = screen->SceneOffset();
|
||||
|
||||
auto &level0 = levels[0];
|
||||
|
||||
// Extract blooming pixels from scene texture:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomExtract;
|
||||
renderstate->Uniforms.Set(extractUniforms);
|
||||
renderstate->Viewport = level0.Viewport;
|
||||
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||
renderstate->SetInputTexture(1, &hw_postprocess.exposure.CameraTexture);
|
||||
renderstate->SetOutputTexture(&level0.VTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
const float blurAmount = gl_bloom_amount;
|
||||
BlurUniforms blurUniforms;
|
||||
ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights);
|
||||
|
||||
// Blur and downscale:
|
||||
for (int i = 0; i < NumBloomLevels - 1; i++)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i + 1];
|
||||
|
||||
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||
|
||||
// Linear downscale:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = next.Viewport;
|
||||
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&next.VTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
// Blur and upscale:
|
||||
for (int i = NumBloomLevels - 1; i > 0; i--)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i - 1];
|
||||
|
||||
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||
|
||||
// Linear upscale:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = next.Viewport;
|
||||
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&next.VTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
BlurStep(renderstate, blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false);
|
||||
BlurStep(renderstate, blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true);
|
||||
|
||||
// Add bloom back to scene texture:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = screen->mSceneViewport;
|
||||
renderstate->SetInputTexture(0, &level0.VTexture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputCurrent();
|
||||
renderstate->SetAdditiveBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
}
|
||||
|
||||
void PPBloom::RenderBlur(PPRenderState *renderstate, int sceneWidth, int sceneHeight, float gameinfobluramount)
|
||||
{
|
||||
// No scene, no blur!
|
||||
if (sceneWidth <= 0 || sceneHeight <= 0)
|
||||
return;
|
||||
|
||||
UpdateTextures(sceneWidth, sceneHeight);
|
||||
|
||||
// first, respect the CVar
|
||||
float blurAmount = gl_menu_blur;
|
||||
|
||||
// if CVar is negative, use the gameinfo entry
|
||||
if (gl_menu_blur < 0)
|
||||
blurAmount = gameinfobluramount;
|
||||
|
||||
// if blurAmount == 0 or somehow still returns negative, exit to prevent a crash, clearly we don't want this
|
||||
if (blurAmount <= 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderstate->PushGroup("blur");
|
||||
|
||||
int numLevels = 3;
|
||||
assert(numLevels <= NumBloomLevels);
|
||||
|
||||
auto &level0 = levels[0];
|
||||
|
||||
// Grab the area we want to bloom:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = level0.Viewport;
|
||||
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&level0.VTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
BlurUniforms blurUniforms;
|
||||
ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights);
|
||||
|
||||
// Blur and downscale:
|
||||
for (int i = 0; i < numLevels - 1; i++)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i + 1];
|
||||
|
||||
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||
|
||||
// Linear downscale:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = next.Viewport;
|
||||
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&next.VTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
// Blur and upscale:
|
||||
for (int i = numLevels - 1; i > 0; i--)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i - 1];
|
||||
|
||||
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||
|
||||
// Linear upscale:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = next.Viewport;
|
||||
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&next.VTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
BlurStep(renderstate, blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false);
|
||||
BlurStep(renderstate, blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true);
|
||||
|
||||
// Copy blur back to scene texture:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BloomCombine;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = screen->mScreenViewport;
|
||||
renderstate->SetInputTexture(0, &level0.VTexture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputCurrent();
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
}
|
||||
|
||||
void PPBloom::BlurStep(PPRenderState *renderstate, const BlurUniforms &blurUniforms, PPTexture &input, PPTexture &output, PPViewport viewport, bool vertical)
|
||||
{
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = vertical ? &BlurVertical : &BlurHorizontal;
|
||||
renderstate->Uniforms.Set(blurUniforms);
|
||||
renderstate->Viewport = viewport;
|
||||
renderstate->SetInputTexture(0, &input);
|
||||
renderstate->SetOutputTexture(&output);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
float PPBloom::ComputeBlurGaussian(float n, float theta) // theta = Blur Amount
|
||||
{
|
||||
return (float)((1.0f / sqrtf(2 * (float)M_PI * theta)) * expf(-(n * n) / (2.0f * theta * theta)));
|
||||
}
|
||||
|
||||
void PPBloom::ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights)
|
||||
{
|
||||
sampleWeights[0] = ComputeBlurGaussian(0, blurAmount);
|
||||
|
||||
float totalWeights = sampleWeights[0];
|
||||
|
||||
for (int i = 0; i < sampleCount / 2; i++)
|
||||
{
|
||||
float weight = ComputeBlurGaussian(i + 1.0f, blurAmount);
|
||||
|
||||
sampleWeights[i * 2 + 1] = weight;
|
||||
sampleWeights[i * 2 + 2] = weight;
|
||||
|
||||
totalWeights += weight * 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
sampleWeights[i] /= totalWeights;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPLensDistort::Render(PPRenderState *renderstate)
|
||||
{
|
||||
if (gl_lens == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float k[4] =
|
||||
{
|
||||
gl_lens_k,
|
||||
gl_lens_k * gl_lens_chromatic,
|
||||
gl_lens_k * gl_lens_chromatic * gl_lens_chromatic,
|
||||
0.0f
|
||||
};
|
||||
float kcube[4] =
|
||||
{
|
||||
gl_lens_kcube,
|
||||
gl_lens_kcube * gl_lens_chromatic,
|
||||
gl_lens_kcube * gl_lens_chromatic * gl_lens_chromatic,
|
||||
0.0f
|
||||
};
|
||||
|
||||
float aspect = screen->mSceneViewport.width / (float)screen->mSceneViewport.height;
|
||||
|
||||
// Scale factor to keep sampling within the input texture
|
||||
float r2 = aspect * aspect * 0.25f + 0.25f;
|
||||
float sqrt_r2 = sqrt(r2);
|
||||
float f0 = 1.0f + std::max(r2 * (k[0] + kcube[0] * sqrt_r2), 0.0f);
|
||||
float f2 = 1.0f + std::max(r2 * (k[2] + kcube[2] * sqrt_r2), 0.0f);
|
||||
float f = std::max(f0, f2);
|
||||
float scale = 1.0f / f;
|
||||
|
||||
LensUniforms uniforms;
|
||||
uniforms.AspectRatio = aspect;
|
||||
uniforms.Scale = scale;
|
||||
uniforms.LensDistortionCoefficient = k;
|
||||
uniforms.CubicDistortionValue = kcube;
|
||||
|
||||
renderstate->PushGroup("lens");
|
||||
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &Lens;
|
||||
renderstate->Uniforms.Set(uniforms);
|
||||
renderstate->Viewport = screen->mScreenViewport;
|
||||
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||
renderstate->SetOutputNext();
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPFXAA::Render(PPRenderState *renderstate)
|
||||
{
|
||||
if (0 == gl_fxaa)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateShaders();
|
||||
|
||||
FXAAUniforms uniforms;
|
||||
uniforms.ReciprocalResolution = { 1.0f / screen->mScreenViewport.width, 1.0f / screen->mScreenViewport.height };
|
||||
|
||||
renderstate->PushGroup("fxaa");
|
||||
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &FXAALuma;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = screen->mScreenViewport;
|
||||
renderstate->SetInputCurrent(0, PPFilterMode::Nearest);
|
||||
renderstate->SetOutputNext();
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->Shader = &FXAA;
|
||||
renderstate->Uniforms.Set(uniforms);
|
||||
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
}
|
||||
|
||||
int PPFXAA::GetMaxVersion()
|
||||
{
|
||||
return screen->glslversion >= 4.f ? 400 : 330;
|
||||
}
|
||||
|
||||
void PPFXAA::CreateShaders()
|
||||
{
|
||||
if (LastQuality == gl_fxaa)
|
||||
return;
|
||||
|
||||
FXAALuma = { "shaders/glsl/fxaa.fp", "#define FXAA_LUMA_PASS\n", {} };
|
||||
FXAA = { "shaders/glsl/fxaa.fp", GetDefines(), FXAAUniforms::Desc(), GetMaxVersion() };
|
||||
LastQuality = gl_fxaa;
|
||||
}
|
||||
|
||||
FString PPFXAA::GetDefines()
|
||||
{
|
||||
int quality;
|
||||
|
||||
switch (gl_fxaa)
|
||||
{
|
||||
default:
|
||||
case IFXAAShader::Low: quality = 10; break;
|
||||
case IFXAAShader::Medium: quality = 12; break;
|
||||
case IFXAAShader::High: quality = 29; break;
|
||||
case IFXAAShader::Extreme: quality = 39; break;
|
||||
}
|
||||
|
||||
const int gatherAlpha = GetMaxVersion() >= 400 ? 1 : 0;
|
||||
|
||||
// TODO: enable FXAA_GATHER4_ALPHA on OpenGL earlier than 4.0
|
||||
// when GL_ARB_gpu_shader5/GL_NV_gpu_shader5 extensions are supported
|
||||
|
||||
FString result;
|
||||
result.Format(
|
||||
"#define FXAA_QUALITY__PRESET %i\n"
|
||||
"#define FXAA_GATHER4_ALPHA %i\n",
|
||||
quality, gatherAlpha);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPCameraExposure::Render(PPRenderState *renderstate, int sceneWidth, int sceneHeight)
|
||||
{
|
||||
if (!gl_bloom)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderstate->PushGroup("exposure");
|
||||
|
||||
UpdateTextures(sceneWidth, sceneHeight);
|
||||
|
||||
ExposureExtractUniforms extractUniforms;
|
||||
extractUniforms.Scale = screen->SceneScale();
|
||||
extractUniforms.Offset = screen->SceneOffset();
|
||||
|
||||
ExposureCombineUniforms combineUniforms;
|
||||
combineUniforms.ExposureBase = gl_exposure_base;
|
||||
combineUniforms.ExposureMin = gl_exposure_min;
|
||||
combineUniforms.ExposureScale = gl_exposure_scale;
|
||||
combineUniforms.ExposureSpeed = gl_exposure_speed;
|
||||
|
||||
auto &level0 = ExposureLevels[0];
|
||||
|
||||
// Extract light blevel from scene texture:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &ExposureExtract;
|
||||
renderstate->Uniforms.Set(extractUniforms);
|
||||
renderstate->Viewport = level0.Viewport;
|
||||
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&level0.Texture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
// Find the average value:
|
||||
for (size_t i = 0; i + 1 < ExposureLevels.size(); i++)
|
||||
{
|
||||
auto &blevel = ExposureLevels[i];
|
||||
auto &next = ExposureLevels[i + 1];
|
||||
|
||||
renderstate->Shader = &ExposureAverage;
|
||||
renderstate->Uniforms.Clear();
|
||||
renderstate->Viewport = next.Viewport;
|
||||
renderstate->SetInputTexture(0, &blevel.Texture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&next.Texture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
// Combine average value with current camera exposure:
|
||||
renderstate->Shader = &ExposureCombine;
|
||||
renderstate->Uniforms.Set(combineUniforms);
|
||||
renderstate->Viewport.left = 0;
|
||||
renderstate->Viewport.top = 0;
|
||||
renderstate->Viewport.width = 1;
|
||||
renderstate->Viewport.height = 1;
|
||||
renderstate->SetInputTexture(0, &ExposureLevels.back().Texture, PPFilterMode::Linear);
|
||||
renderstate->SetOutputTexture(&CameraTexture);
|
||||
if (!FirstExposureFrame)
|
||||
renderstate->SetAlphaBlend();
|
||||
else
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
|
||||
FirstExposureFrame = false;
|
||||
}
|
||||
|
||||
void PPCameraExposure::UpdateTextures(int width, int height)
|
||||
{
|
||||
int firstwidth = std::max(width / 2, 1);
|
||||
int firstheight = std::max(height / 2, 1);
|
||||
|
||||
if (ExposureLevels.size() > 0 && ExposureLevels[0].Viewport.width == firstwidth && ExposureLevels[0].Viewport.height == firstheight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ExposureLevels.clear();
|
||||
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
width = std::max(width / 2, 1);
|
||||
height = std::max(height / 2, 1);
|
||||
|
||||
PPExposureLevel blevel;
|
||||
blevel.Viewport.left = 0;
|
||||
blevel.Viewport.top = 0;
|
||||
blevel.Viewport.width = width;
|
||||
blevel.Viewport.height = height;
|
||||
blevel.Texture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::R32f };
|
||||
ExposureLevels.push_back(std::move(blevel));
|
||||
|
||||
i++;
|
||||
|
||||
} while (width > 1 || height > 1);
|
||||
|
||||
FirstExposureFrame = true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPTonemap::UpdateTextures()
|
||||
{
|
||||
if (gl_tonemap == Palette && !PaletteTexture.Data)
|
||||
{
|
||||
std::shared_ptr<void> data(new uint32_t[512 * 512], [](void *p) { delete[](uint32_t*)p; });
|
||||
|
||||
uint8_t *lut = (uint8_t *)data.get();
|
||||
for (int r = 0; r < 64; r++)
|
||||
{
|
||||
for (int g = 0; g < 64; g++)
|
||||
{
|
||||
for (int b = 0; b < 64; b++)
|
||||
{
|
||||
PalEntry color = ImageHelpers::BaseColors[(uint8_t)ImageHelpers::PTM_BestColor((r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4),
|
||||
gl_paltonemap_reverselookup, gl_paltonemap_powtable, 0, 256)];
|
||||
int index = ((r * 64 + g) * 64 + b) * 4;
|
||||
lut[index] = color.r;
|
||||
lut[index + 1] = color.g;
|
||||
lut[index + 2] = color.b;
|
||||
lut[index + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PaletteTexture = { 512, 512, PixelFormat::Rgba8, data };
|
||||
}
|
||||
}
|
||||
|
||||
void PPTonemap::Render(PPRenderState *renderstate)
|
||||
{
|
||||
if (gl_tonemap == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateTextures();
|
||||
|
||||
PPShader *shader = nullptr;
|
||||
switch (gl_tonemap)
|
||||
{
|
||||
default:
|
||||
case Linear: shader = &LinearShader; break;
|
||||
case Reinhard: shader = &ReinhardShader; break;
|
||||
case HejlDawson: shader = &HejlDawsonShader; break;
|
||||
case Uncharted2: shader = &Uncharted2Shader; break;
|
||||
case Palette: shader = &PaletteShader; break;
|
||||
}
|
||||
|
||||
renderstate->PushGroup("tonemap");
|
||||
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = shader;
|
||||
renderstate->Viewport = screen->mScreenViewport;
|
||||
renderstate->SetInputCurrent(0);
|
||||
if (gl_tonemap == Palette)
|
||||
renderstate->SetInputTexture(1, &PaletteTexture);
|
||||
renderstate->SetOutputNext();
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PPAmbientOcclusion::PPAmbientOcclusion()
|
||||
{
|
||||
// Must match quality enum in PPAmbientOcclusion::DeclareShaders
|
||||
double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 };
|
||||
|
||||
std::mt19937 generator(1337);
|
||||
std::uniform_real_distribution<double> distribution(0.0, 1.0);
|
||||
for (int quality = 0; quality < NumAmbientRandomTextures; quality++)
|
||||
{
|
||||
std::shared_ptr<void> data(new int16_t[16 * 4], [](void *p) { delete[](int16_t*)p; });
|
||||
int16_t *randomValues = (int16_t *)data.get();
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality];
|
||||
double x = cos(angle);
|
||||
double y = sin(angle);
|
||||
double z = distribution(generator);
|
||||
double w = distribution(generator);
|
||||
|
||||
randomValues[i * 4 + 0] = (int16_t)clamp(x * 32767.0, -32768.0, 32767.0);
|
||||
randomValues[i * 4 + 1] = (int16_t)clamp(y * 32767.0, -32768.0, 32767.0);
|
||||
randomValues[i * 4 + 2] = (int16_t)clamp(z * 32767.0, -32768.0, 32767.0);
|
||||
randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0);
|
||||
}
|
||||
|
||||
AmbientRandomTexture[quality] = { 4, 4, PixelFormat::Rgba16_snorm, data };
|
||||
}
|
||||
}
|
||||
|
||||
void PPAmbientOcclusion::CreateShaders()
|
||||
{
|
||||
if (gl_ssao == LastQuality)
|
||||
return;
|
||||
|
||||
// Must match quality values in PPAmbientOcclusion::UpdateTextures
|
||||
int numDirections, numSteps;
|
||||
switch (gl_ssao)
|
||||
{
|
||||
default:
|
||||
case LowQuality: numDirections = 2; numSteps = 4; break;
|
||||
case MediumQuality: numDirections = 4; numSteps = 4; break;
|
||||
case HighQuality: numDirections = 8; numSteps = 4; break;
|
||||
}
|
||||
|
||||
FString defines;
|
||||
defines.Format(R"(
|
||||
#define USE_RANDOM_TEXTURE
|
||||
#define RANDOM_TEXTURE_WIDTH 4.0
|
||||
#define NUM_DIRECTIONS %d.0
|
||||
#define NUM_STEPS %d.0
|
||||
)", numDirections, numSteps);
|
||||
|
||||
LinearDepth = { "shaders/glsl/lineardepth.fp", "", LinearDepthUniforms::Desc() };
|
||||
LinearDepthMS = { "shaders/glsl/lineardepth.fp", "#define MULTISAMPLE\n", LinearDepthUniforms::Desc() };
|
||||
AmbientOcclude = { "shaders/glsl/ssao.fp", defines, SSAOUniforms::Desc() };
|
||||
AmbientOccludeMS = { "shaders/glsl/ssao.fp", defines + "\n#define MULTISAMPLE\n", SSAOUniforms::Desc() };
|
||||
BlurVertical = { "shaders/glsl/depthblur.fp", "#define BLUR_VERTICAL\n", DepthBlurUniforms::Desc() };
|
||||
BlurHorizontal = { "shaders/glsl/depthblur.fp", "#define BLUR_HORIZONTAL\n", DepthBlurUniforms::Desc() };
|
||||
Combine = { "shaders/glsl/ssaocombine.fp", "", AmbientCombineUniforms::Desc() };
|
||||
CombineMS = { "shaders/glsl/ssaocombine.fp", "#define MULTISAMPLE\n", AmbientCombineUniforms::Desc() };
|
||||
|
||||
LastQuality = gl_ssao;
|
||||
}
|
||||
|
||||
void PPAmbientOcclusion::UpdateTextures(int width, int height)
|
||||
{
|
||||
if ((width <= 0 || height <= 0) || (width == LastWidth && height == LastHeight))
|
||||
return;
|
||||
|
||||
AmbientWidth = (width + 1) / 2;
|
||||
AmbientHeight = (height + 1) / 2;
|
||||
|
||||
LinearDepthTexture = { AmbientWidth, AmbientHeight, PixelFormat::R32f };
|
||||
Ambient0 = { AmbientWidth, AmbientHeight, PixelFormat::Rg16f };
|
||||
Ambient1 = { AmbientWidth, AmbientHeight, PixelFormat::Rg16f };
|
||||
|
||||
LastWidth = width;
|
||||
LastHeight = height;
|
||||
}
|
||||
|
||||
void PPAmbientOcclusion::Render(PPRenderState *renderstate, float m5, int sceneWidth, int sceneHeight)
|
||||
{
|
||||
if (gl_ssao == 0 || sceneWidth == 0 || sceneHeight == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateShaders();
|
||||
UpdateTextures(sceneWidth, sceneHeight);
|
||||
|
||||
float bias = gl_ssao_bias;
|
||||
float aoRadius = gl_ssao_radius;
|
||||
const float blurAmount = gl_ssao_blur;
|
||||
float aoStrength = gl_ssao_strength;
|
||||
|
||||
//float tanHalfFovy = tan(fovy * (M_PI / 360.0f));
|
||||
float tanHalfFovy = 1.0f / m5;
|
||||
float invFocalLenX = tanHalfFovy * (sceneWidth / (float)sceneHeight);
|
||||
float invFocalLenY = tanHalfFovy;
|
||||
float nDotVBias = clamp(bias, 0.0f, 1.0f);
|
||||
float r2 = aoRadius * aoRadius;
|
||||
|
||||
float blurSharpness = 1.0f / blurAmount;
|
||||
|
||||
auto sceneScale = screen->SceneScale();
|
||||
auto sceneOffset = screen->SceneOffset();
|
||||
|
||||
int randomTexture = clamp(gl_ssao - 1, 0, NumAmbientRandomTextures - 1);
|
||||
|
||||
LinearDepthUniforms linearUniforms;
|
||||
linearUniforms.SampleIndex = 0;
|
||||
linearUniforms.LinearizeDepthA = 1.0f / screen->GetZFar() - 1.0f / screen->GetZNear();
|
||||
linearUniforms.LinearizeDepthB = std::max(1.0f / screen->GetZNear(), 1.e-8f);
|
||||
linearUniforms.InverseDepthRangeA = 1.0f;
|
||||
linearUniforms.InverseDepthRangeB = 0.0f;
|
||||
linearUniforms.Scale = sceneScale;
|
||||
linearUniforms.Offset = sceneOffset;
|
||||
|
||||
SSAOUniforms ssaoUniforms;
|
||||
ssaoUniforms.SampleIndex = 0;
|
||||
ssaoUniforms.UVToViewA = { 2.0f * invFocalLenX, 2.0f * invFocalLenY };
|
||||
ssaoUniforms.UVToViewB = { -invFocalLenX, -invFocalLenY };
|
||||
ssaoUniforms.InvFullResolution = { 1.0f / AmbientWidth, 1.0f / AmbientHeight };
|
||||
ssaoUniforms.NDotVBias = nDotVBias;
|
||||
ssaoUniforms.NegInvR2 = -1.0f / r2;
|
||||
ssaoUniforms.RadiusToScreen = aoRadius * 0.5f / tanHalfFovy * AmbientHeight;
|
||||
ssaoUniforms.AOMultiplier = 1.0f / (1.0f - nDotVBias);
|
||||
ssaoUniforms.AOStrength = aoStrength;
|
||||
ssaoUniforms.Scale = sceneScale;
|
||||
ssaoUniforms.Offset = sceneOffset;
|
||||
|
||||
DepthBlurUniforms blurUniforms;
|
||||
blurUniforms.BlurSharpness = blurSharpness;
|
||||
blurUniforms.PowExponent = gl_ssao_exponent;
|
||||
|
||||
AmbientCombineUniforms combineUniforms;
|
||||
combineUniforms.SampleCount = gl_multisample;
|
||||
combineUniforms.Scale = screen->SceneScale();
|
||||
combineUniforms.Offset = screen->SceneOffset();
|
||||
combineUniforms.DebugMode = gl_ssao_debug;
|
||||
|
||||
IntRect ambientViewport;
|
||||
ambientViewport.left = 0;
|
||||
ambientViewport.top = 0;
|
||||
ambientViewport.width = AmbientWidth;
|
||||
ambientViewport.height = AmbientHeight;
|
||||
|
||||
renderstate->PushGroup("ssao");
|
||||
|
||||
// Calculate linear depth values
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = gl_multisample > 1 ? &LinearDepthMS : &LinearDepth;
|
||||
renderstate->Uniforms.Set(linearUniforms);
|
||||
renderstate->Viewport = ambientViewport;
|
||||
renderstate->SetInputSceneDepth(0);
|
||||
renderstate->SetInputSceneColor(1);
|
||||
renderstate->SetOutputTexture(&LinearDepthTexture);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
// Apply ambient occlusion
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = gl_multisample > 1 ? &AmbientOccludeMS : &AmbientOcclude;
|
||||
renderstate->Uniforms.Set(ssaoUniforms);
|
||||
renderstate->Viewport = ambientViewport;
|
||||
renderstate->SetInputTexture(0, &LinearDepthTexture);
|
||||
renderstate->SetInputSceneNormal(1);
|
||||
renderstate->SetInputTexture(2, &AmbientRandomTexture[randomTexture], PPFilterMode::Nearest, PPWrapMode::Repeat);
|
||||
renderstate->SetOutputTexture(&Ambient0);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
// Blur SSAO texture
|
||||
if (gl_ssao_debug < 2)
|
||||
{
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BlurHorizontal;
|
||||
renderstate->Uniforms.Set(blurUniforms);
|
||||
renderstate->Viewport = ambientViewport;
|
||||
renderstate->SetInputTexture(0, &Ambient0);
|
||||
renderstate->SetOutputTexture(&Ambient1);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = &BlurVertical;
|
||||
renderstate->Uniforms.Set(blurUniforms);
|
||||
renderstate->Viewport = ambientViewport;
|
||||
renderstate->SetInputTexture(0, &Ambient1);
|
||||
renderstate->SetOutputTexture(&Ambient0);
|
||||
renderstate->SetNoBlend();
|
||||
renderstate->Draw();
|
||||
}
|
||||
|
||||
// Add SSAO back to scene texture:
|
||||
renderstate->Clear();
|
||||
renderstate->Shader = gl_multisample > 1 ? &CombineMS : &Combine;
|
||||
renderstate->Uniforms.Set(combineUniforms);
|
||||
renderstate->Viewport = screen->mSceneViewport;
|
||||
if (gl_ssao_debug < 4)
|
||||
renderstate->SetInputTexture(0, &Ambient0, PPFilterMode::Linear);
|
||||
else
|
||||
renderstate->SetInputSceneNormal(0, PPFilterMode::Linear);
|
||||
renderstate->SetInputSceneFog(1);
|
||||
renderstate->SetOutputSceneColor();
|
||||
if (gl_ssao_debug != 0)
|
||||
renderstate->SetNoBlend();
|
||||
else
|
||||
renderstate->SetAlphaBlend();
|
||||
renderstate->Draw();
|
||||
|
||||
renderstate->PopGroup();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PPPresent::PPPresent()
|
||||
{
|
||||
static const float data[64] =
|
||||
{
|
||||
.0078125, .2578125, .1328125, .3828125, .0234375, .2734375, .1484375, .3984375,
|
||||
.7578125, .5078125, .8828125, .6328125, .7734375, .5234375, .8984375, .6484375,
|
||||
.0703125, .3203125, .1953125, .4453125, .0859375, .3359375, .2109375, .4609375,
|
||||
.8203125, .5703125, .9453125, .6953125, .8359375, .5859375, .9609375, .7109375,
|
||||
.0390625, .2890625, .1640625, .4140625, .0546875, .3046875, .1796875, .4296875,
|
||||
.7890625, .5390625, .9140625, .6640625, .8046875, .5546875, .9296875, .6796875,
|
||||
.1015625, .3515625, .2265625, .4765625, .1171875, .3671875, .2421875, .4921875,
|
||||
.8515625, .6015625, .9765625, .7265625, .8671875, .6171875, .9921875, .7421875,
|
||||
};
|
||||
|
||||
std::shared_ptr<void> pixels(new float[64], [](void *p) { delete[](float*)p; });
|
||||
memcpy(pixels.get(), data, 64 * sizeof(float));
|
||||
Dither = { 8, 8, PixelFormat::R32f, pixels };
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Postprocess::Pass1(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight)
|
||||
{
|
||||
exposure.Render(state, sceneWidth, sceneHeight);
|
||||
bloom.RenderBloom(state, sceneWidth, sceneHeight, fixedcm);
|
||||
}
|
||||
|
||||
void Postprocess::Pass2(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight)
|
||||
{
|
||||
tonemap.Render(state);
|
||||
lens.Render(state);
|
||||
fxaa.Render(state);
|
||||
}
|
|
@ -0,0 +1,771 @@
|
|||
#pragma once
|
||||
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
struct PostProcessShader;
|
||||
|
||||
typedef FRenderStyle PPBlendMode;
|
||||
typedef IntRect PPViewport;
|
||||
|
||||
class PPTexture;
|
||||
class PPShader;
|
||||
|
||||
enum class PPFilterMode { Nearest, Linear };
|
||||
enum class PPWrapMode { Clamp, Repeat };
|
||||
enum class PPTextureType { CurrentPipelineTexture, NextPipelineTexture, PPTexture, SceneColor, SceneFog, SceneNormal, SceneDepth, SwapChain, ShadowMap };
|
||||
|
||||
class PPTextureInput
|
||||
{
|
||||
public:
|
||||
PPFilterMode Filter = PPFilterMode::Nearest;
|
||||
PPWrapMode Wrap = PPWrapMode::Clamp;
|
||||
PPTextureType Type = PPTextureType::CurrentPipelineTexture;
|
||||
PPTexture *Texture = nullptr;
|
||||
};
|
||||
|
||||
class PPOutput
|
||||
{
|
||||
public:
|
||||
PPTextureType Type = PPTextureType::NextPipelineTexture;
|
||||
PPTexture *Texture = nullptr;
|
||||
};
|
||||
|
||||
class PPUniforms
|
||||
{
|
||||
public:
|
||||
PPUniforms()
|
||||
{
|
||||
}
|
||||
|
||||
PPUniforms(const PPUniforms &src)
|
||||
{
|
||||
Data = src.Data;
|
||||
}
|
||||
|
||||
~PPUniforms()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
PPUniforms &operator=(const PPUniforms &src)
|
||||
{
|
||||
Data = src.Data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Data.Clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Set(const T &v)
|
||||
{
|
||||
if (Data.Size() != (int)sizeof(T))
|
||||
{
|
||||
Data.Resize(sizeof(T));
|
||||
memcpy(Data.Data(), &v, Data.Size());
|
||||
}
|
||||
}
|
||||
|
||||
TArray<uint8_t> Data;
|
||||
};
|
||||
|
||||
class PPRenderState
|
||||
{
|
||||
public:
|
||||
virtual ~PPRenderState() = default;
|
||||
|
||||
virtual void PushGroup(const FString &name) = 0;
|
||||
virtual void PopGroup() = 0;
|
||||
|
||||
virtual void Draw() = 0;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Shader = nullptr;
|
||||
Textures = TArray<PPTextureInput>();
|
||||
Uniforms = PPUniforms();
|
||||
Viewport = PPViewport();
|
||||
BlendMode = PPBlendMode();
|
||||
Output = PPOutput();
|
||||
ShadowMapBuffers = false;
|
||||
}
|
||||
|
||||
void SetInputTexture(int index, PPTexture *texture, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
if ((int)Textures.Size() < index + 1)
|
||||
Textures.Resize(index + 1);
|
||||
auto &tex = Textures[index];
|
||||
tex.Filter = filter;
|
||||
tex.Wrap = wrap;
|
||||
tex.Type = PPTextureType::PPTexture;
|
||||
tex.Texture = texture;
|
||||
}
|
||||
|
||||
void SetInputCurrent(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::CurrentPipelineTexture, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneColor(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneColor, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneFog(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneFog, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneNormal(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneNormal, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneDepth(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneDepth, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSpecialType(int index, PPTextureType type, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
if ((int)Textures.Size() < index + 1)
|
||||
Textures.Resize(index + 1);
|
||||
auto &tex = Textures[index];
|
||||
tex.Filter = filter;
|
||||
tex.Wrap = wrap;
|
||||
tex.Type = type;
|
||||
tex.Texture = nullptr;
|
||||
}
|
||||
|
||||
void SetShadowMapBuffers(bool enable)
|
||||
{
|
||||
ShadowMapBuffers = enable;
|
||||
}
|
||||
|
||||
void SetOutputTexture(PPTexture *texture)
|
||||
{
|
||||
Output.Type = PPTextureType::PPTexture;
|
||||
Output.Texture = texture;
|
||||
}
|
||||
|
||||
void SetOutputCurrent()
|
||||
{
|
||||
Output.Type = PPTextureType::CurrentPipelineTexture;
|
||||
Output.Texture = nullptr;
|
||||
}
|
||||
|
||||
void SetOutputNext()
|
||||
{
|
||||
Output.Type = PPTextureType::NextPipelineTexture;
|
||||
Output.Texture = nullptr;
|
||||
}
|
||||
|
||||
void SetOutputSceneColor()
|
||||
{
|
||||
Output.Type = PPTextureType::SceneColor;
|
||||
Output.Texture = nullptr;
|
||||
}
|
||||
|
||||
void SetOutputSwapChain()
|
||||
{
|
||||
Output.Type = PPTextureType::SwapChain;
|
||||
Output.Texture = nullptr;
|
||||
}
|
||||
|
||||
void SetOutputShadowMap()
|
||||
{
|
||||
Output.Type = PPTextureType::ShadowMap;
|
||||
Output.Texture = nullptr;
|
||||
}
|
||||
|
||||
void SetNoBlend()
|
||||
{
|
||||
BlendMode.BlendOp = STYLEOP_Add;
|
||||
BlendMode.SrcAlpha = STYLEALPHA_One;
|
||||
BlendMode.DestAlpha = STYLEALPHA_Zero;
|
||||
BlendMode.Flags = 0;
|
||||
}
|
||||
|
||||
void SetAdditiveBlend()
|
||||
{
|
||||
BlendMode.BlendOp = STYLEOP_Add;
|
||||
BlendMode.SrcAlpha = STYLEALPHA_One;
|
||||
BlendMode.DestAlpha = STYLEALPHA_One;
|
||||
BlendMode.Flags = 0;
|
||||
}
|
||||
|
||||
void SetAlphaBlend()
|
||||
{
|
||||
BlendMode.BlendOp = STYLEOP_Add;
|
||||
BlendMode.SrcAlpha = STYLEALPHA_Src;
|
||||
BlendMode.DestAlpha = STYLEALPHA_InvSrc;
|
||||
BlendMode.Flags = 0;
|
||||
}
|
||||
|
||||
PPShader *Shader;
|
||||
TArray<PPTextureInput> Textures;
|
||||
PPUniforms Uniforms;
|
||||
PPViewport Viewport;
|
||||
PPBlendMode BlendMode;
|
||||
PPOutput Output;
|
||||
bool ShadowMapBuffers = false;
|
||||
};
|
||||
|
||||
enum class PixelFormat
|
||||
{
|
||||
Rgba8,
|
||||
Rgba16f,
|
||||
R32f,
|
||||
Rg16f,
|
||||
Rgba16_snorm
|
||||
};
|
||||
|
||||
class PPResource
|
||||
{
|
||||
public:
|
||||
PPResource()
|
||||
{
|
||||
Next = First;
|
||||
First = this;
|
||||
if (Next) Next->Prev = this;
|
||||
}
|
||||
|
||||
PPResource(const PPResource &)
|
||||
{
|
||||
Next = First;
|
||||
First = this;
|
||||
if (Next) Next->Prev = this;
|
||||
}
|
||||
|
||||
virtual ~PPResource()
|
||||
{
|
||||
if (Next) Next->Prev = Prev;
|
||||
if (Prev) Prev->Next = Next;
|
||||
else First = Next;
|
||||
}
|
||||
|
||||
PPResource &operator=(const PPResource &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
static void ResetAll()
|
||||
{
|
||||
for (PPResource *cur = First; cur; cur = cur->Next)
|
||||
cur->ResetBackend();
|
||||
}
|
||||
|
||||
virtual void ResetBackend() = 0;
|
||||
|
||||
private:
|
||||
static PPResource *First;
|
||||
PPResource *Prev = nullptr;
|
||||
PPResource *Next = nullptr;
|
||||
};
|
||||
|
||||
class PPTextureBackend
|
||||
{
|
||||
public:
|
||||
virtual ~PPTextureBackend() = default;
|
||||
};
|
||||
|
||||
class PPTexture : public PPResource
|
||||
{
|
||||
public:
|
||||
PPTexture() = default;
|
||||
PPTexture(int width, int height, PixelFormat format, std::shared_ptr<void> data = {}) : Width(width), Height(height), Format(format), Data(data) { }
|
||||
|
||||
void ResetBackend() override { Backend.reset(); }
|
||||
|
||||
int Width;
|
||||
int Height;
|
||||
PixelFormat Format;
|
||||
std::shared_ptr<void> Data;
|
||||
|
||||
std::unique_ptr<PPTextureBackend> Backend;
|
||||
};
|
||||
|
||||
class PPShaderBackend
|
||||
{
|
||||
public:
|
||||
virtual ~PPShaderBackend() = default;
|
||||
};
|
||||
|
||||
class PPShader : public PPResource
|
||||
{
|
||||
public:
|
||||
PPShader() = default;
|
||||
PPShader(const FString &fragment, const FString &defines, const std::vector<UniformFieldDesc> &uniforms, int version = 330) : FragmentShader(fragment), Defines(defines), Uniforms(uniforms), Version(version) { }
|
||||
|
||||
void ResetBackend() override { Backend.reset(); }
|
||||
|
||||
FString VertexShader = "shaders/glsl/screenquad.vp";
|
||||
FString FragmentShader;
|
||||
FString Defines;
|
||||
std::vector<UniformFieldDesc> Uniforms;
|
||||
int Version = 330;
|
||||
|
||||
std::unique_ptr<PPShaderBackend> Backend;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ExtractUniforms
|
||||
{
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "Scale", UniformType::Vec2, offsetof(ExtractUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(ExtractUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct BlurUniforms
|
||||
{
|
||||
float SampleWeights[8];
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "SampleWeights0", UniformType::Float, offsetof(BlurUniforms, SampleWeights[0]) },
|
||||
{ "SampleWeights1", UniformType::Float, offsetof(BlurUniforms, SampleWeights[1]) },
|
||||
{ "SampleWeights2", UniformType::Float, offsetof(BlurUniforms, SampleWeights[2]) },
|
||||
{ "SampleWeights3", UniformType::Float, offsetof(BlurUniforms, SampleWeights[3]) },
|
||||
{ "SampleWeights4", UniformType::Float, offsetof(BlurUniforms, SampleWeights[4]) },
|
||||
{ "SampleWeights5", UniformType::Float, offsetof(BlurUniforms, SampleWeights[5]) },
|
||||
{ "SampleWeights6", UniformType::Float, offsetof(BlurUniforms, SampleWeights[6]) },
|
||||
{ "SampleWeights7", UniformType::Float, offsetof(BlurUniforms, SampleWeights[7]) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
enum { NumBloomLevels = 4 };
|
||||
|
||||
class PPBlurLevel
|
||||
{
|
||||
public:
|
||||
PPViewport Viewport;
|
||||
PPTexture VTexture;
|
||||
PPTexture HTexture;
|
||||
};
|
||||
|
||||
class PPBloom
|
||||
{
|
||||
public:
|
||||
void RenderBloom(PPRenderState *renderstate, int sceneWidth, int sceneHeight, int fixedcm);
|
||||
void RenderBlur(PPRenderState *renderstate, int sceneWidth, int sceneHeight, float gameinfobluramount);
|
||||
|
||||
private:
|
||||
void BlurStep(PPRenderState *renderstate, const BlurUniforms &blurUniforms, PPTexture &input, PPTexture &output, PPViewport viewport, bool vertical);
|
||||
void UpdateTextures(int width, int height);
|
||||
|
||||
static float ComputeBlurGaussian(float n, float theta);
|
||||
static void ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights);
|
||||
|
||||
PPBlurLevel levels[NumBloomLevels];
|
||||
int lastWidth = 0;
|
||||
int lastHeight = 0;
|
||||
|
||||
PPShader BloomCombine = { "shaders/glsl/bloomcombine.fp", "", {} };
|
||||
PPShader BloomExtract = { "shaders/glsl/bloomextract.fp", "", ExtractUniforms::Desc() };
|
||||
PPShader BlurVertical = { "shaders/glsl/blur.fp", "#define BLUR_VERTICAL\n", BlurUniforms::Desc() };
|
||||
PPShader BlurHorizontal = { "shaders/glsl/blur.fp", "#define BLUR_HORIZONTAL\n", BlurUniforms::Desc() };
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct LensUniforms
|
||||
{
|
||||
float AspectRatio;
|
||||
float Scale;
|
||||
float Padding0, Padding1;
|
||||
FVector4 LensDistortionCoefficient;
|
||||
FVector4 CubicDistortionValue;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "Aspect", UniformType::Float, offsetof(LensUniforms, AspectRatio) },
|
||||
{ "Scale", UniformType::Float, offsetof(LensUniforms, Scale) },
|
||||
{ "Padding0", UniformType::Float, offsetof(LensUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(LensUniforms, Padding1) },
|
||||
{ "k", UniformType::Vec4, offsetof(LensUniforms, LensDistortionCoefficient) },
|
||||
{ "kcube", UniformType::Vec4, offsetof(LensUniforms, CubicDistortionValue) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPLensDistort
|
||||
{
|
||||
public:
|
||||
void Render(PPRenderState *renderstate);
|
||||
|
||||
private:
|
||||
PPShader Lens = { "shaders/glsl/lensdistortion.fp", "", LensUniforms::Desc() };
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FXAAUniforms
|
||||
{
|
||||
FVector2 ReciprocalResolution;
|
||||
float Padding0, Padding1;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "ReciprocalResolution", UniformType::Vec2, offsetof(FXAAUniforms, ReciprocalResolution) },
|
||||
{ "Padding0", UniformType::Float, offsetof(FXAAUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(FXAAUniforms, Padding1) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPFXAA
|
||||
{
|
||||
public:
|
||||
void Render(PPRenderState *renderstate);
|
||||
|
||||
private:
|
||||
void CreateShaders();
|
||||
int GetMaxVersion();
|
||||
FString GetDefines();
|
||||
|
||||
PPShader FXAALuma;
|
||||
PPShader FXAA;
|
||||
int LastQuality = -1;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ExposureExtractUniforms
|
||||
{
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "Scale", UniformType::Vec2, offsetof(ExposureExtractUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(ExposureExtractUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct ExposureCombineUniforms
|
||||
{
|
||||
float ExposureBase;
|
||||
float ExposureMin;
|
||||
float ExposureScale;
|
||||
float ExposureSpeed;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "ExposureBase", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureBase) },
|
||||
{ "ExposureMin", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureMin) },
|
||||
{ "ExposureScale", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureScale) },
|
||||
{ "ExposureSpeed", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureSpeed) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPExposureLevel
|
||||
{
|
||||
public:
|
||||
PPViewport Viewport;
|
||||
PPTexture Texture;
|
||||
};
|
||||
|
||||
class PPCameraExposure
|
||||
{
|
||||
public:
|
||||
void Render(PPRenderState *renderstate, int sceneWidth, int sceneHeight);
|
||||
|
||||
PPTexture CameraTexture = { 1, 1, PixelFormat::R32f };
|
||||
|
||||
private:
|
||||
void UpdateTextures(int width, int height);
|
||||
|
||||
std::vector<PPExposureLevel> ExposureLevels;
|
||||
bool FirstExposureFrame = true;
|
||||
|
||||
PPShader ExposureExtract = { "shaders/glsl/exposureextract.fp", "", ExposureExtractUniforms::Desc() };
|
||||
PPShader ExposureAverage = { "shaders/glsl/exposureaverage.fp", "", {}, 400 };
|
||||
PPShader ExposureCombine = { "shaders/glsl/exposurecombine.fp", "", ExposureCombineUniforms::Desc() };
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ColormapUniforms
|
||||
{
|
||||
FVector4 MapStart;
|
||||
FVector4 MapRange;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "uFixedColormapStart", UniformType::Vec4, offsetof(ColormapUniforms, MapStart) },
|
||||
{ "uFixedColormapRange", UniformType::Vec4, offsetof(ColormapUniforms, MapRange) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PPTonemap
|
||||
{
|
||||
public:
|
||||
void Render(PPRenderState *renderstate);
|
||||
void ClearTonemapPalette() { PaletteTexture = {}; }
|
||||
|
||||
private:
|
||||
void UpdateTextures();
|
||||
|
||||
PPTexture PaletteTexture;
|
||||
|
||||
PPShader LinearShader = { "shaders/glsl/tonemap.fp", "#define LINEAR\n", {} };
|
||||
PPShader ReinhardShader = { "shaders/glsl/tonemap.fp", "#define REINHARD\n", {} };
|
||||
PPShader HejlDawsonShader = { "shaders/glsl/tonemap.fp", "#define HEJLDAWSON\n", {} };
|
||||
PPShader Uncharted2Shader = { "shaders/glsl/tonemap.fp", "#define UNCHARTED2\n", {} };
|
||||
PPShader PaletteShader = { "shaders/glsl/tonemap.fp", "#define PALETTE\n", {} };
|
||||
|
||||
enum TonemapMode
|
||||
{
|
||||
None,
|
||||
Uncharted2,
|
||||
HejlDawson,
|
||||
Reinhard,
|
||||
Linear,
|
||||
Palette,
|
||||
NumTonemapModes
|
||||
};
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct LinearDepthUniforms
|
||||
{
|
||||
int SampleIndex;
|
||||
float LinearizeDepthA;
|
||||
float LinearizeDepthB;
|
||||
float InverseDepthRangeA;
|
||||
float InverseDepthRangeB;
|
||||
float Padding0, Padding1, Padding2;
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "SampleIndex", UniformType::Int, offsetof(LinearDepthUniforms, SampleIndex) },
|
||||
{ "LinearizeDepthA", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthA) },
|
||||
{ "LinearizeDepthB", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthB) },
|
||||
{ "InverseDepthRangeA", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeA) },
|
||||
{ "InverseDepthRangeB", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeB) },
|
||||
{ "Padding0", UniformType::Float, offsetof(LinearDepthUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(LinearDepthUniforms, Padding1) },
|
||||
{ "Padding2", UniformType::Float, offsetof(LinearDepthUniforms, Padding2) },
|
||||
{ "Scale", UniformType::Vec2, offsetof(LinearDepthUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(LinearDepthUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct SSAOUniforms
|
||||
{
|
||||
FVector2 UVToViewA;
|
||||
FVector2 UVToViewB;
|
||||
FVector2 InvFullResolution;
|
||||
float NDotVBias;
|
||||
float NegInvR2;
|
||||
float RadiusToScreen;
|
||||
float AOMultiplier;
|
||||
float AOStrength;
|
||||
int SampleIndex;
|
||||
float Padding0, Padding1;
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "UVToViewA", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewA) },
|
||||
{ "UVToViewB", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewB) },
|
||||
{ "InvFullResolution", UniformType::Vec2, offsetof(SSAOUniforms, InvFullResolution) },
|
||||
{ "NDotVBias", UniformType::Float, offsetof(SSAOUniforms, NDotVBias) },
|
||||
{ "NegInvR2", UniformType::Float, offsetof(SSAOUniforms, NegInvR2) },
|
||||
{ "RadiusToScreen", UniformType::Float, offsetof(SSAOUniforms, RadiusToScreen) },
|
||||
{ "AOMultiplier", UniformType::Float, offsetof(SSAOUniforms, AOMultiplier) },
|
||||
{ "AOStrength", UniformType::Float, offsetof(SSAOUniforms, AOStrength) },
|
||||
{ "SampleIndex", UniformType::Int, offsetof(SSAOUniforms, SampleIndex) },
|
||||
{ "Padding0", UniformType::Float, offsetof(SSAOUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(SSAOUniforms, Padding1) },
|
||||
{ "Scale", UniformType::Vec2, offsetof(SSAOUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(SSAOUniforms, Offset) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthBlurUniforms
|
||||
{
|
||||
float BlurSharpness;
|
||||
float PowExponent;
|
||||
float Padding0, Padding1;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "BlurSharpness", UniformType::Float, offsetof(DepthBlurUniforms, BlurSharpness) },
|
||||
{ "PowExponent", UniformType::Float, offsetof(DepthBlurUniforms, PowExponent) },
|
||||
{ "Padding0", UniformType::Float, offsetof(DepthBlurUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(DepthBlurUniforms, Padding1) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct AmbientCombineUniforms
|
||||
{
|
||||
int SampleCount;
|
||||
int DebugMode, Padding1, Padding2;
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "SampleCount", UniformType::Int, offsetof(AmbientCombineUniforms, SampleCount) },
|
||||
{ "DebugMode", UniformType::Int, offsetof(AmbientCombineUniforms, DebugMode) },
|
||||
{ "Padding1", UniformType::Int, offsetof(AmbientCombineUniforms, Padding1) },
|
||||
{ "Padding2", UniformType::Int, offsetof(AmbientCombineUniforms, Padding2) },
|
||||
{ "Scale", UniformType::Vec2, offsetof(AmbientCombineUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(AmbientCombineUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPAmbientOcclusion
|
||||
{
|
||||
public:
|
||||
PPAmbientOcclusion();
|
||||
void Render(PPRenderState *renderstate, float m5, int sceneWidth, int sceneHeight);
|
||||
|
||||
private:
|
||||
void CreateShaders();
|
||||
void UpdateTextures(int width, int height);
|
||||
|
||||
enum Quality
|
||||
{
|
||||
Off,
|
||||
LowQuality,
|
||||
MediumQuality,
|
||||
HighQuality,
|
||||
NumQualityModes
|
||||
};
|
||||
|
||||
int AmbientWidth = 0;
|
||||
int AmbientHeight = 0;
|
||||
|
||||
int LastQuality = -1;
|
||||
int LastWidth = 0;
|
||||
int LastHeight = 0;
|
||||
|
||||
PPShader LinearDepth;
|
||||
PPShader LinearDepthMS;
|
||||
PPShader AmbientOcclude;
|
||||
PPShader AmbientOccludeMS;
|
||||
PPShader BlurVertical;
|
||||
PPShader BlurHorizontal;
|
||||
PPShader Combine;
|
||||
PPShader CombineMS;
|
||||
|
||||
PPTexture LinearDepthTexture;
|
||||
PPTexture Ambient0;
|
||||
PPTexture Ambient1;
|
||||
|
||||
enum { NumAmbientRandomTextures = 3 };
|
||||
PPTexture AmbientRandomTexture[NumAmbientRandomTextures];
|
||||
};
|
||||
|
||||
struct PresentUniforms
|
||||
{
|
||||
float InvGamma;
|
||||
float Contrast;
|
||||
float Brightness;
|
||||
float Saturation;
|
||||
int GrayFormula;
|
||||
int WindowPositionParity; // top-of-window might not be top-of-screen
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
float ColorScale;
|
||||
int HdrMode;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "InvGamma", UniformType::Float, offsetof(PresentUniforms, InvGamma) },
|
||||
{ "Contrast", UniformType::Float, offsetof(PresentUniforms, Contrast) },
|
||||
{ "Brightness", UniformType::Float, offsetof(PresentUniforms, Brightness) },
|
||||
{ "Saturation", UniformType::Float, offsetof(PresentUniforms, Saturation) },
|
||||
{ "GrayFormula", UniformType::Int, offsetof(PresentUniforms, GrayFormula) },
|
||||
{ "WindowPositionParity", UniformType::Int, offsetof(PresentUniforms, WindowPositionParity) },
|
||||
{ "UVScale", UniformType::Vec2, offsetof(PresentUniforms, Scale) },
|
||||
{ "UVOffset", UniformType::Vec2, offsetof(PresentUniforms, Offset) },
|
||||
{ "ColorScale", UniformType::Float, offsetof(PresentUniforms, ColorScale) },
|
||||
{ "HdrMode", UniformType::Int, offsetof(PresentUniforms, HdrMode) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPPresent
|
||||
{
|
||||
public:
|
||||
PPPresent();
|
||||
|
||||
PPTexture Dither;
|
||||
|
||||
PPShader Present = { "shaders/glsl/present.fp", "", PresentUniforms::Desc() };
|
||||
PPShader Checker3D = { "shaders/glsl/present_checker3d.fp", "", PresentUniforms::Desc() };
|
||||
PPShader Column3D = { "shaders/glsl/present_column3d.fp", "", PresentUniforms::Desc() };
|
||||
PPShader Row3D = { "shaders/glsl/present_row3d.fp", "", PresentUniforms::Desc() };
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Postprocess
|
||||
{
|
||||
public:
|
||||
PPBloom bloom;
|
||||
PPLensDistort lens;
|
||||
PPFXAA fxaa;
|
||||
PPCameraExposure exposure;
|
||||
PPTonemap tonemap;
|
||||
PPAmbientOcclusion ssao;
|
||||
PPPresent present;
|
||||
|
||||
|
||||
void Pass1(PPRenderState *state, int fixedcm, int sceneWidth, int sceneHeight);
|
||||
void Pass2(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight);
|
||||
};
|
||||
|
||||
extern Postprocess hw_postprocess;
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_postprocess.cpp
|
||||
** Post processing effects in the render pipeline
|
||||
**
|
||||
*/
|
||||
|
||||
#include "hw_postprocess_cvars.h"
|
||||
#include "v_video.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE);
|
||||
CUSTOM_CVAR(Float, gl_bloom_amount, 1.4f, CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0.1f) self = 0.1f;
|
||||
}
|
||||
|
||||
CVAR(Float, gl_exposure_scale, 1.3f, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_exposure_min, 0.35f, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_exposure_base, 0.35f, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_exposure_speed, 0.05f, CVAR_ARCHIVE)
|
||||
|
||||
CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0 || self > 5)
|
||||
self = 0;
|
||||
}
|
||||
|
||||
CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CVAR(Float, gl_lens_k, -0.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Float, gl_lens_kcube, 0.1f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Float, gl_lens_chromatic, 1.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CUSTOM_CVAR(Int, gl_fxaa, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0 || self >= IFXAAShader::Count)
|
||||
{
|
||||
self = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0 || self > 3)
|
||||
self = 0;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_ssao_portals, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0)
|
||||
self = 0;
|
||||
}
|
||||
|
||||
CVAR(Float, gl_ssao_strength, 0.7f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, gl_ssao_debug, 0, 0)
|
||||
CVAR(Float, gl_ssao_bias, 0.2f, 0)
|
||||
CVAR(Float, gl_ssao_radius, 80.0f, 0)
|
||||
CUSTOM_CVAR(Float, gl_ssao_blur, 16.0f, 0)
|
||||
{
|
||||
if (self < 0.1f) self = 0.1f;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, gl_ssao_exponent, 1.8f, 0)
|
||||
{
|
||||
if (self < 0.1f) self = 0.1f;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, gl_paltonemap_powtable, 2.0f, CVAR_ARCHIVE | CVAR_NOINITCALL)
|
||||
{
|
||||
screen->UpdatePalette();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, gl_paltonemap_reverselookup, true, CVAR_ARCHIVE | CVAR_NOINITCALL)
|
||||
{
|
||||
screen->UpdatePalette();
|
||||
}
|
||||
|
||||
CVAR(Float, gl_menu_blur, -1.0f, CVAR_ARCHIVE)
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "c_cvars.h"
|
||||
|
||||
class IFXAAShader
|
||||
{
|
||||
public:
|
||||
enum Quality
|
||||
{
|
||||
None,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Extreme,
|
||||
Count
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
EXTERN_CVAR(Bool, gl_bloom)
|
||||
EXTERN_CVAR(Float, gl_bloom_amount)
|
||||
EXTERN_CVAR(Float, gl_exposure_scale)
|
||||
EXTERN_CVAR(Float, gl_exposure_min)
|
||||
EXTERN_CVAR(Float, gl_exposure_base)
|
||||
EXTERN_CVAR(Float, gl_exposure_speed)
|
||||
EXTERN_CVAR(Int, gl_tonemap)
|
||||
EXTERN_CVAR(Int, gl_bloom_kernel_size)
|
||||
EXTERN_CVAR(Bool, gl_lens)
|
||||
EXTERN_CVAR(Float, gl_lens_k)
|
||||
EXTERN_CVAR(Float, gl_lens_kcube)
|
||||
EXTERN_CVAR(Float, gl_lens_chromatic)
|
||||
EXTERN_CVAR(Int, gl_fxaa)
|
||||
EXTERN_CVAR(Int, gl_ssao)
|
||||
EXTERN_CVAR(Int, gl_ssao_portals)
|
||||
EXTERN_CVAR(Float, gl_ssao_strength)
|
||||
EXTERN_CVAR(Int, gl_ssao_debug)
|
||||
EXTERN_CVAR(Float, gl_ssao_bias)
|
||||
EXTERN_CVAR(Float, gl_ssao_radius)
|
||||
EXTERN_CVAR(Float, gl_ssao_blur)
|
||||
EXTERN_CVAR(Float, gl_ssao_exponent)
|
||||
EXTERN_CVAR(Float, gl_paltonemap_powtable)
|
||||
EXTERN_CVAR(Bool, gl_paltonemap_reverselookup)
|
||||
EXTERN_CVAR(Float, gl_menu_blur)
|
||||
EXTERN_CVAR(Float, vid_brightness)
|
||||
EXTERN_CVAR(Float, vid_contrast)
|
||||
EXTERN_CVAR(Float, vid_saturation)
|
||||
EXTERN_CVAR(Int, gl_satformula)
|
||||
|
86
source/common/rendering/hwrenderer/utility/hw_cvars.cpp
Normal file
86
source/common/rendering/hwrenderer/utility/hw_cvars.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "v_video.h"
|
||||
#include "hw_cvars.h"
|
||||
#include "menu/menu.h"
|
||||
|
||||
|
||||
|
||||
// OpenGL stuff moved here
|
||||
// GL related CVARs
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Texture CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
#if 0
|
||||
CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
|
||||
{
|
||||
screen->TextureFilterChanged();
|
||||
}
|
||||
|
||||
CCMD(gl_flush)
|
||||
{
|
||||
//TexMan.FlushAll();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
|
||||
{
|
||||
if (self < 0 || self > 6) self=4;
|
||||
screen->TextureFilterChanged();
|
||||
}
|
||||
CVAR(Bool, gl_precache, false, CVAR_ARCHIVE)
|
||||
#endif
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sprite CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
#if 0
|
||||
CVAR(Bool, gl_usecolorblending, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, gl_sprite_blend, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
CVAR(Int, gl_spriteclip, 1, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_sclipthreshold, 10.0, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_sclipfactor, 1.8f, CVAR_ARCHIVE)
|
||||
CVAR(Int, gl_particles_style, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // 0 = square, 1 = round, 2 = smooth
|
||||
CVAR(Int, gl_billboard_mode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, gl_billboard_faces_camera, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, gl_billboard_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, gl_enhanced_nv_stealth, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0 || self > 8) self = 0;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0 || self > 8) self = 1;
|
||||
}
|
||||
#endif
|
71
source/common/rendering/hwrenderer/utility/hw_cvars.h
Normal file
71
source/common/rendering/hwrenderer/utility/hw_cvars.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "c_cvars.h"
|
||||
|
||||
EXTERN_CVAR(Bool,gl_enhanced_nightvision)
|
||||
EXTERN_CVAR(Int, screenblocks);
|
||||
EXTERN_CVAR(Bool, gl_texture)
|
||||
EXTERN_CVAR(Int, gl_texture_filter)
|
||||
EXTERN_CVAR(Float, gl_texture_filter_anisotropic)
|
||||
EXTERN_CVAR(Int, gl_texture_format)
|
||||
EXTERN_CVAR(Bool, gl_texture_usehires)
|
||||
EXTERN_CVAR(Bool, gl_usefb)
|
||||
|
||||
EXTERN_CVAR(Int, gl_weaponlight)
|
||||
|
||||
EXTERN_CVAR (Bool, gl_light_sprites);
|
||||
EXTERN_CVAR (Bool, gl_light_particles);
|
||||
EXTERN_CVAR (Bool, gl_light_shadowmap);
|
||||
EXTERN_CVAR (Int, gl_shadowmap_quality);
|
||||
|
||||
EXTERN_CVAR(Int, gl_fogmode)
|
||||
EXTERN_CVAR(Int, gl_lightmode)
|
||||
EXTERN_CVAR(Bool,gl_mirror_envmap)
|
||||
|
||||
EXTERN_CVAR(Bool,gl_mirrors)
|
||||
EXTERN_CVAR(Bool,gl_mirror_envmap)
|
||||
|
||||
EXTERN_CVAR(Float, gl_mask_threshold)
|
||||
EXTERN_CVAR(Float, gl_mask_sprite_threshold)
|
||||
|
||||
EXTERN_CVAR(Int, gl_multisample)
|
||||
|
||||
EXTERN_CVAR(Bool, gl_bloom)
|
||||
EXTERN_CVAR(Float, gl_bloom_amount)
|
||||
EXTERN_CVAR(Int, gl_bloom_kernel_size)
|
||||
EXTERN_CVAR(Int, gl_tonemap)
|
||||
EXTERN_CVAR(Float, gl_exposure)
|
||||
EXTERN_CVAR(Bool, gl_lens)
|
||||
EXTERN_CVAR(Float, gl_lens_k)
|
||||
EXTERN_CVAR(Float, gl_lens_kcube)
|
||||
EXTERN_CVAR(Float, gl_lens_chromatic)
|
||||
EXTERN_CVAR(Int, gl_ssao)
|
||||
EXTERN_CVAR(Int, gl_ssao_portals)
|
||||
EXTERN_CVAR(Float, gl_ssao_strength)
|
||||
EXTERN_CVAR(Int, gl_ssao_debug)
|
||||
EXTERN_CVAR(Float, gl_ssao_bias)
|
||||
EXTERN_CVAR(Float, gl_ssao_radius)
|
||||
EXTERN_CVAR(Float, gl_ssao_blur_amount)
|
||||
|
||||
EXTERN_CVAR(Int, gl_debug_level)
|
||||
EXTERN_CVAR(Bool, gl_debug_breakpoint)
|
||||
|
||||
|
||||
EXTERN_CVAR(Bool, gl_usecolorblending)
|
||||
EXTERN_CVAR(Bool, gl_sprite_blend)
|
||||
EXTERN_CVAR(Int, gl_spriteclip)
|
||||
EXTERN_CVAR(Float, gl_sclipthreshold)
|
||||
EXTERN_CVAR(Float, gl_sclipfactor)
|
||||
EXTERN_CVAR(Int, gl_particles_style)
|
||||
EXTERN_CVAR(Int, gl_billboard_mode)
|
||||
EXTERN_CVAR(Bool, gl_billboard_faces_camera)
|
||||
EXTERN_CVAR(Bool, gl_billboard_particles)
|
||||
EXTERN_CVAR(Int, gl_enhanced_nv_stealth)
|
||||
EXTERN_CVAR(Int, gl_fuzztype)
|
||||
|
||||
EXTERN_CVAR(Int, gl_shadowmap_filter)
|
||||
|
||||
EXTERN_CVAR(Bool, gl_brightfog)
|
||||
EXTERN_CVAR(Bool, gl_lightadditivesurfaces)
|
||||
EXTERN_CVAR(Bool, gl_notexturefill)
|
295
source/common/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
Normal file
295
source/common/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
Normal file
|
@ -0,0 +1,295 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2004-2018 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** hw_shaderpatcher.cpp
|
||||
**
|
||||
** Modifies shader source to account for different syntax versions or engine changes.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "hw_shaderpatcher.h"
|
||||
|
||||
|
||||
static bool IsGlslWhitespace(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
case '\f':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FString NextGlslToken(const char *chars, long len, long &pos)
|
||||
{
|
||||
// Eat whitespace
|
||||
long tokenStart = pos;
|
||||
while (tokenStart != len && IsGlslWhitespace(chars[tokenStart]))
|
||||
tokenStart++;
|
||||
|
||||
// Find token end
|
||||
long tokenEnd = tokenStart;
|
||||
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.
|
||||
|
||||
long len = (long)code.Len();
|
||||
char *chars = code.LockBuffer();
|
||||
|
||||
long startIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
long matchIndex = code.IndexOf("uniform", startIndex);
|
||||
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)
|
||||
{
|
||||
long pos = matchIndex + 7;
|
||||
FString type = NextGlslToken(chars, len, pos);
|
||||
FString identifier = NextGlslToken(chars, len, pos);
|
||||
|
||||
isLegacyUniformName = type.Compare("float") == 0 && identifier.Compare("timer") == 0;
|
||||
}
|
||||
|
||||
if (isLegacyUniformName)
|
||||
{
|
||||
long statementEndIndex = code.IndexOf(';', matchIndex + 7);
|
||||
if (statementEndIndex == -1)
|
||||
statementEndIndex = len;
|
||||
for (long i = matchIndex; i <= statementEndIndex; i++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
long matchIndex = code.IndexOf("texture2d", startIndex);
|
||||
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)
|
||||
{
|
||||
long len = (long)code.Len();
|
||||
char *chars = code.LockBuffer();
|
||||
|
||||
long startIndex = 0;
|
||||
long startpos, endpos;
|
||||
while (true)
|
||||
{
|
||||
long matchIndex = code.IndexOf("layout(binding", startIndex);
|
||||
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)
|
||||
{
|
||||
long pos = matchIndex + 14;
|
||||
startpos = matchIndex;
|
||||
while (IsGlslWhitespace(chars[pos])) pos++;
|
||||
if (chars[pos] == '=')
|
||||
{
|
||||
char *p;
|
||||
pos++;
|
||||
auto val = strtol(&chars[pos], &p, 0);
|
||||
if (p != &chars[pos])
|
||||
{
|
||||
pos = long(p - chars);
|
||||
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);
|
||||
|
||||
isSamplerUniformName = uniform.Compare("uniform") == 0 && isShaderType(type);
|
||||
if (isSamplerUniformName)
|
||||
{
|
||||
samplerstobind.Push(std::make_pair(identifier, val));
|
||||
for (auto pos = startpos; pos < endpos; pos++)
|
||||
{
|
||||
if (!IsGlslWhitespace(chars[pos]))
|
||||
chars[pos] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSamplerUniformName)
|
||||
{
|
||||
startIndex = endpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex = matchIndex + 7;
|
||||
}
|
||||
}
|
||||
|
||||
code.UnlockBuffer();
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword)
|
||||
{
|
||||
long len = (long)code.Len();
|
||||
char *chars = code.LockBuffer();
|
||||
|
||||
long startIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
long matchIndex = code.IndexOf("layout(location", startIndex);
|
||||
if (matchIndex == -1)
|
||||
break;
|
||||
|
||||
long endIndex = matchIndex;
|
||||
|
||||
// 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;
|
||||
long i;
|
||||
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
|
||||
for (long i = matchIndex; i < endIndex; i++)
|
||||
chars[i] = ' ';
|
||||
}
|
||||
|
||||
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[] =
|
||||
{
|
||||
{"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", ""},
|
||||
{"Brightmap","shaders/glsl/func_brightmap.fp", "shaders/glsl/material_normal.fp", "#define BRIGHTMAP\n"},
|
||||
{"Specular", "shaders/glsl/func_spec.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"},
|
||||
{"SpecularBrightmap", "shaders/glsl/func_spec.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n#define BRIGHTMAP\n"},
|
||||
{"PBR","shaders/glsl/func_pbr.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"},
|
||||
{"PBRBrightmap","shaders/glsl/func_pbr.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n#define BRIGHTMAP\n"},
|
||||
{"Paletted", "shaders/glsl/func_paletted.fp", "shaders/glsl/material_nolight.fp", ""},
|
||||
{"No Texture", "shaders/glsl/func_notexture.fp", "shaders/glsl/material_normal.fp", ""},
|
||||
{"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", ""},
|
||||
{nullptr,nullptr,nullptr,nullptr}
|
||||
};
|
||||
|
||||
const FEffectShader effectshaders[] =
|
||||
{
|
||||
{ "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" },
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "zstring.h"
|
||||
#include "utility"
|
||||
|
||||
FString RemoveLegacyUserUniforms(FString code);
|
||||
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind); // For GL 3.3 compatibility which cannot declare sampler bindings in the sampler source.
|
||||
FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword);
|
||||
|
||||
struct FDefaultShader
|
||||
{
|
||||
const char * ShaderName;
|
||||
const char * gettexelfunc;
|
||||
const char * lightfunc;
|
||||
const char * Defines;
|
||||
};
|
||||
|
||||
struct FEffectShader
|
||||
{
|
||||
const char *ShaderName;
|
||||
const char *vp;
|
||||
const char *fp1;
|
||||
const char *fp2;
|
||||
const char *fp3;
|
||||
const char *defines;
|
||||
};
|
||||
|
||||
extern const FDefaultShader defaultshaders[];
|
||||
extern const FEffectShader effectshaders[];
|
|
@ -292,6 +292,9 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
|||
mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
|
||||
mSceneViewport.width = viewwidth;
|
||||
mSceneViewport.height = height;
|
||||
#else
|
||||
// For now use the full screen. This needs to be done better.
|
||||
mSceneViewport = mScreenViewport;
|
||||
#endif
|
||||
|
||||
// Scale viewports to fit letterbox
|
||||
|
@ -302,12 +305,12 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
|||
{
|
||||
mScreenViewport.width = mOutputLetterbox.width;
|
||||
mScreenViewport.height = mOutputLetterbox.height;
|
||||
#if 0
|
||||
|
||||
mSceneViewport.left = (int)round(mSceneViewport.left * scaleX);
|
||||
mSceneViewport.top = (int)round(mSceneViewport.top * scaleY);
|
||||
mSceneViewport.width = (int)round(mSceneViewport.width * scaleX);
|
||||
mSceneViewport.height = (int)round(mSceneViewport.height * scaleY);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -236,7 +236,7 @@ public:
|
|||
//IShadowMap mShadowMap;
|
||||
|
||||
IntRect mScreenViewport;
|
||||
//IntRect mSceneViewport;
|
||||
IntRect mSceneViewport;
|
||||
IntRect mOutputLetterbox;
|
||||
float mSceneClearColor[4];
|
||||
|
||||
|
@ -259,7 +259,7 @@ public:
|
|||
inline int GetWidth() const { return Width; }
|
||||
inline int GetHeight() const { return Height; }
|
||||
|
||||
#if 0
|
||||
// Currently there is no concept of a scene viewport in Build, but let's keep this stuff here to implement it later.
|
||||
FVector2 SceneScale() const
|
||||
{
|
||||
return { mSceneViewport.width / (float)mScreenViewport.width, mSceneViewport.height / (float)mScreenViewport.height };
|
||||
|
@ -269,7 +269,6 @@ public:
|
|||
{
|
||||
return { mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height };
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make the surface visible.
|
||||
virtual void Update ();
|
||||
|
@ -395,8 +394,6 @@ extern DFrameBuffer *screen;
|
|||
#define SCREENHEIGHT (screen->GetHeight ())
|
||||
#define SCREENPITCH (screen->GetPitch ())
|
||||
|
||||
EXTERN_CVAR (Float, Gamma)
|
||||
|
||||
|
||||
// Allocates buffer screens, call before R_Init.
|
||||
void V_InitScreenSize();
|
||||
|
|
|
@ -71,6 +71,43 @@ namespace ImageHelpers
|
|||
return bestcolor;
|
||||
}
|
||||
|
||||
// [SP] Re-implemented BestColor for more precision rather than speed. This function is only ever called once until the game palette is changed.
|
||||
|
||||
int PTM_BestColor(int r, int g, int b, bool reverselookup, float powtable_val, int first, int num)
|
||||
{
|
||||
const PalEntry* pal = BaseColors;
|
||||
static double powtable[256];
|
||||
static bool firstTime = true;
|
||||
static float trackpowtable = 0.;
|
||||
|
||||
double fbestdist = DBL_MAX, fdist;
|
||||
int bestcolor = 0;
|
||||
|
||||
if (firstTime || trackpowtable != powtable_val)
|
||||
{
|
||||
auto pt = powtable_val;
|
||||
trackpowtable = pt;
|
||||
firstTime = false;
|
||||
for (int x = 0; x < 256; x++) powtable[x] = pow((double)x / 255, (double)pt);
|
||||
}
|
||||
|
||||
for (int color = first; color < num; color++)
|
||||
{
|
||||
double x = powtable[abs(r - pal[color].r)];
|
||||
double y = powtable[abs(g - pal[color].g)];
|
||||
double z = powtable[abs(b - pal[color].b)];
|
||||
fdist = x + y + z;
|
||||
if (color == first || (reverselookup ? (fdist <= fbestdist) : (fdist < fbestdist)))
|
||||
{
|
||||
if (fdist == 0 && !reverselookup)
|
||||
return color;
|
||||
|
||||
fbestdist = fdist;
|
||||
bestcolor = color;
|
||||
}
|
||||
}
|
||||
return bestcolor;
|
||||
}
|
||||
|
||||
void SetPalette(const PalEntry* colors)
|
||||
{
|
||||
|
|
|
@ -59,6 +59,7 @@ namespace ImageHelpers
|
|||
|
||||
// Todo: This should not pick fullbright colors.
|
||||
int BestColor(int r, int g, int b, int first = 0, int num = 255);
|
||||
int PTM_BestColor(int r, int g, int b, bool reverselookup, float powtable_val, int first, int num);
|
||||
void SetPalette(const PalEntry* colors);
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,16 @@ include_directories(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../common/2d
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform )
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
disttable[i] = 255 - eax >> 8;
|
||||
disttable[i] = 255 - (eax >> 8);
|
||||
|
||||
eax = (eax * eax) >> 8;
|
||||
}
|
||||
|
@ -257,9 +257,8 @@ int LoadSound(const char* name)
|
|||
else if (!ISDEMOVER) // demo tries to load sound files it doesn't have
|
||||
{
|
||||
Printf("Unable to open sound '%s'!\n", filename.GetChars());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue