- renamed 'common' to 'core'.

We'll need 'common' for something else.
This commit is contained in:
Christoph Oelckers 2020-04-11 23:38:30 +02:00
parent 736337979b
commit e2f5e8fe34
280 changed files with 187 additions and 187 deletions

View file

@ -1,235 +0,0 @@
/*
** Postprocessing framework
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
*/
#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"
#include "v_video.h"
extern bool vid_hdr_active;
CVAR(Int, gl_dither_bpc, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
namespace OpenGLRenderer
{
void FGLRenderer::RenderScreenQuad()
{
auto buffer = static_cast<GLVertexBuffer *>(screen->mVertexData->GetBufferObjects().first);
buffer->Bind(nullptr);
glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4);
}
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();
if (afterBloomDrawEndScene2D) 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
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);
}
}

View file

@ -1,134 +0,0 @@
/*
** Postprocessing framework
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
*/
#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, &currentProgram);
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);
}
}

View file

@ -1,41 +0,0 @@
#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

View file

@ -1,929 +0,0 @@
/*
** Postprocessing framework
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
*/
#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(int ms)
{
gl_multisample = ms;
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);
bool needsSceneTextures = (gl_ssao != 0);
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, needsSceneTextures);
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

View file

@ -1,206 +0,0 @@
#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
{
int gl_multisample = 0; // intentionally overload the global CVAR
public:
FGLRenderBuffers(int ms);
~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;
};
}

View file

@ -1,263 +0,0 @@
/*
** gl_renderer.cpp
** Renderer interface
**
**---------------------------------------------------------------------------
** Copyright 2005-2020 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#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 "m_png.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"
#include "../glbackend/gl_hwtexture.h"
#include "build.h"
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, cl_capfps)
extern bool NoInterpolateView;
namespace OpenGLRenderer
{
//===========================================================================
//
// Renderer interface
//
//===========================================================================
//-----------------------------------------------------------------------------
//
// Initialize
//
//-----------------------------------------------------------------------------
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
{
framebuffer = fb;
}
void FGLRenderer::Initialize(int width, int height)
{
mScreenBuffers = new FGLRenderBuffers(gl_multisample);
mSaveBuffers = new FGLRenderBuffers(0);
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(FTexture *mat)
{
auto pBaseLayer = mat->GetHardwareTexture(0);
auto BaseLayer = pBaseLayer ? *pBaseLayer : nullptr;
if (BaseLayer == nullptr)
{
// must create the hardware texture first
BaseLayer = new FHardwareTexture();
BaseLayer->CreateTexture(mat->GetWidth()*4, mat->GetHeight()*4, FHardwareTexture::TrueColor, false);
mat->SetHardwareTexture(0, BaseLayer);
}
BaseLayer->BindToFrameBuffer(mat->GetWidth()*4, mat->GetHeight()*4);
}
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void FGLRenderer::WriteSavePic ( 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;
mBuffers->BindSceneFB(false);
screen->SetViewportRects(&bounds);
int oldx = xdim;
int oldy = ydim;
auto oldwindowxy1 = windowxy1;
auto oldwindowxy2 = windowxy2;
xdim = width;
ydim = height;
videoSetViewableArea(0, 0, width - 1, height - 1);
renderSetAspect(65536, 65536);
bool didit = gi->GenerateSavePic();
xdim = oldx;
ydim = oldy;
videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y);
// The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first.
twodgen.Clear();
twodpsp.Clear();
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();
if (didit)
{
int numpixels = width * height;
uint8_t* scr = (uint8_t*)Xmalloc(numpixels * 3);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr);
M_CreatePNG(file, scr + ((height - 1) * width * 3), nullptr, SS_RGB, width, height, -width * 3, vid_gamma);
M_FinishPNG(file);
Xfree(scr);
}
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
mBuffers = mScreenBuffers;
bool useSSAO = (gl_ssao != 0);
mBuffers->BindSceneFB(useSSAO);
}
//===========================================================================
//
//
//
//===========================================================================
void FGLRenderer::BeginFrame()
{
mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
mSaveBuffers->Setup(240, 180, 240, 180);
}
}

View file

@ -1,111 +0,0 @@
#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);
void WriteSavePic(FileWriter *file, int width, int height);
void BeginFrame();
bool StartOffscreen();
void EndOffscreen();
void BindToFrameBuffer(FTexture* tex);
private:
void DrawScene(HWDrawInfo *di, int drawmode);
};
struct TexFilter_s
{
int minfilter;
int magfilter;
bool mipmapping;
} ;
extern FGLRenderer *GLRenderer;
}
#endif

View file

@ -1,349 +0,0 @@
/*
** Postprocessing framework
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
*/
#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, "engine/shaders/pp/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("engine/shaders/pp/present.fp", "engine/shaders/pp/present");
}
mShader->Bind();
}
/////////////////////////////////////////////////////////////////////////////
void FPresent3DCheckerShader::Bind()
{
if (!mShader)
{
Init("engine/shaders/pp/present_checker3d.fp", "engine/shaders/pp/presentChecker3d");
}
mShader->Bind();
}
void FPresent3DColumnShader::Bind()
{
if (!mShader)
{
Init("engine/shaders/pp/present_column3d.fp", "engine/shaders/pp/presentColumn3d");
}
mShader->Bind();
}
void FPresent3DRowShader::Bind()
{
if (!mShader)
{
Init("engine/shaders/pp/present_row3d.fp", "engine/shaders/pp/presentRow3d");
}
mShader->Bind();
}
}

View file

@ -1,93 +0,0 @@
#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;
};
}

View file

@ -1,228 +0,0 @@
/*
** gl_buffers.cpp
** Low level vertex buffer class
**
**---------------------------------------------------------------------------
** Copyright 2018-2020 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <algorithm>
#include "gl_load.h"
//#include "glbackend.h"
#include "gl_buffers.h"
#include "v_video.h"
namespace OpenGLRenderer
{
//==========================================================================
//
// basic buffer implementation
//
//==========================================================================
GLBuffer::GLBuffer(int usetype)
: mUseType(usetype)
{
glGenBuffers(1, &mBufferId);
}
GLBuffer::~GLBuffer()
{
if (mBufferId != 0)
{
glBindBuffer(mUseType, mBufferId);
glUnmapBuffer(mUseType);
glBindBuffer(mUseType, 0);
glDeleteBuffers(1, &mBufferId);
}
}
void GLBuffer::Bind()
{
glBindBuffer(mUseType, mBufferId);
}
void GLBuffer::SetData(size_t size, const void *data, bool staticdata)
{
Bind();
if (data != nullptr)
{
glBufferData(mUseType, size, data, staticdata? GL_STATIC_DRAW : GL_STREAM_DRAW);
}
else
{
mPersistent = screen->BuffersArePersistent() && !staticdata;
if (mPersistent)
{
glBufferStorage(mUseType, size, nullptr, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
map = glMapBufferRange(mUseType, 0, size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mUseType, size, nullptr, staticdata ? GL_STATIC_DRAW : GL_STREAM_DRAW);
map = nullptr;
}
if (!staticdata) nomap = false;
}
buffersize = size;
}
void GLBuffer::SetSubData(size_t offset, size_t size, const void *data)
{
Bind();
glBufferSubData(mUseType, offset, size, data);
}
void GLBuffer::Map()
{
assert(nomap == false); // do not allow mapping of static buffers. Vulkan cannot do that so it should be blocked in OpenGL, too.
if (!mPersistent && !nomap)
{
Bind();
map = glMapBufferRange(mUseType, 0, buffersize, GL_MAP_WRITE_BIT|GL_MAP_UNSYNCHRONIZED_BIT);
}
}
void GLBuffer::Unmap()
{
assert(nomap == false);
if (!mPersistent && map != nullptr)
{
Bind();
glUnmapBuffer(mUseType);
map = nullptr;
}
}
void *GLBuffer::Lock(unsigned int size)
{
// This initializes this buffer as a static object with no data.
SetData(size, nullptr, true);
return glMapBufferRange(mUseType, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
}
void GLBuffer::Unlock()
{
Bind();
glUnmapBuffer(mUseType);
}
void GLBuffer::Resize(size_t newsize)
{
assert(!nomap); // only mappable buffers can be resized.
if (newsize > buffersize && !nomap)
{
// reallocate the buffer with twice the size
unsigned int oldbuffer = mBufferId;
// first unmap the old buffer
Bind();
glUnmapBuffer(mUseType);
glGenBuffers(1, &mBufferId);
SetData(newsize, nullptr, false);
glBindBuffer(GL_COPY_READ_BUFFER, oldbuffer);
// copy contents and delete the old buffer.
glCopyBufferSubData(GL_COPY_READ_BUFFER, mUseType, 0, 0, buffersize);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glDeleteBuffers(1, &oldbuffer);
buffersize = newsize;
}
}
//===========================================================================
//
// Vertex buffer implementation
//
//===========================================================================
void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT_2_10_10_10_REV };
static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4};
mStride = stride;
mNumBindingPoints = numBindingPoints;
for(int i = 0; i < numAttributes; i++)
{
if (attrs[i].location >= 0 && attrs[i].location < VATTR_MAX)
{
auto & attrinf = mAttributeInfo[attrs[i].location];
attrinf.format = VFmtToGLFmt[attrs[i].format];
attrinf.size = VFmtToSize[attrs[i].format];
attrinf.offset = attrs[i].offset;
attrinf.bindingpoint = attrs[i].binding;
}
}
}
void GLVertexBuffer::Bind(int *offsets)
{
int i = 0;
// This is what gets called from RenderState.Apply. It shouldn't be called anywhere else if the render state is in use
GLBuffer::Bind();
for(auto &attrinf : mAttributeInfo)
{
if (attrinf.size == 0)
{
glDisableVertexAttribArray(i);
}
else
{
glEnableVertexAttribArray(i);
size_t ofs = offsets == nullptr ? attrinf.offset : attrinf.offset + mStride * offsets[attrinf.bindingpoint];
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.format != GL_FLOAT, (GLsizei)mStride, (void*)(intptr_t)ofs);
}
i++;
}
}
void GLDataBuffer::BindRange(FRenderState *state, size_t start, size_t length)
{
glBindBufferRange(mUseType, mBindingPoint, mBufferId, start, length);
}
void GLDataBuffer::BindBase()
{
glBindBufferBase(mUseType, mBindingPoint, mBufferId);
}
GLVertexBuffer::GLVertexBuffer() : GLBuffer(GL_ARRAY_BUFFER) {}
GLIndexBuffer::GLIndexBuffer() : GLBuffer(GL_ELEMENT_ARRAY_BUFFER) {}
GLDataBuffer::GLDataBuffer(int bindingpoint, bool is_ssbo) : GLBuffer(is_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_UNIFORM_BUFFER), mBindingPoint(bindingpoint) {}
}

View file

@ -1,73 +0,0 @@
#pragma once
#include "buffers.h"
#ifdef _MSC_VER
// silence bogus warning C4250: 'GLVertexBuffer': inherits 'GLBuffer::GLBuffer::SetData' via dominance
// According to internet infos, the warning is erroneously emitted in this case.
#pragma warning(disable:4250)
#endif
namespace OpenGLRenderer
{
class GLBuffer : virtual public IBuffer
{
protected:
const int mUseType;
unsigned int mBufferId;
int mAllocationSize = 0;
bool mPersistent = false;
bool nomap = true;
GLBuffer(int usetype);
~GLBuffer();
void SetData(size_t size, const void *data, bool staticdata) override;
void SetSubData(size_t offset, size_t size, const void *data) override;
void Map() override;
void Unmap() override;
void Resize(size_t newsize) override;
void *Lock(unsigned int size) override;
void Unlock() override;
public:
void Bind();
};
class GLVertexBuffer : public IVertexBuffer, public GLBuffer
{
// If this could use the modern (since GL 4.3) binding system, things would be simpler... :(
struct GLVertexBufferAttribute
{
int bindingpoint;
int format;
int size;
int offset;
};
int mNumBindingPoints;
GLVertexBufferAttribute mAttributeInfo[VATTR_MAX] = {}; // Thanks to OpenGL's state system this needs to contain info about every attribute that may ever be in use throughout the entire renderer.
size_t mStride = 0;
public:
GLVertexBuffer();
void SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs) override;
void Bind(int *offsets);
};
class GLIndexBuffer : public IIndexBuffer, public GLBuffer
{
public:
GLIndexBuffer();
};
class GLDataBuffer : public IDataBuffer, public GLBuffer
{
int mBindingPoint;
public:
GLDataBuffer(int bindingpoint, bool is_ssbo);
void BindRange(FRenderState* state, size_t start, size_t length);
void BindBase();
};
}

View file

@ -1,365 +0,0 @@
/*
** OpenGL debugging support functions
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
*/
#include "templates.h"
#include "gl_load/gl_system.h"
#include "gl/system/gl_debug.h"
#include "stats.h"
#include "printf.h"
#include <set>
#include <string>
#include <vector>
CUSTOM_CVAR(Int, gl_debug_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (!OpenGLRenderer::FGLDebug::HasDebugApi())
{
Printf("No OpenGL debug support detected.\n");
}
}
CVAR(Bool, gl_debug_breakpoint, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
extern bool gpuStatActive;
extern bool keepGpuStatActive;
extern FString gpuStatOutput;
namespace OpenGLRenderer
{
namespace
{
std::vector<std::pair<FString, GLuint>> timeElapsedQueries;
}
//-----------------------------------------------------------------------------
//
// Updates OpenGL debugging state
//
//-----------------------------------------------------------------------------
void FGLDebug::Update()
{
gpuStatOutput = "";
for (auto &query : timeElapsedQueries)
{
GLuint timeElapsed = 0;
glGetQueryObjectuiv(query.second, GL_QUERY_RESULT, &timeElapsed);
glDeleteQueries(1, &query.second);
FString out;
out.Format("%s=%04.2f ms\n", query.first.GetChars(), timeElapsed / 1000000.0f);
gpuStatOutput += out;
}
timeElapsedQueries.clear();
gpuStatActive = keepGpuStatActive;
keepGpuStatActive = false;
if (!HasDebugApi())
return;
SetupBreakpointMode();
UpdateLoggingLevel();
OutputMessageLog();
}
//-----------------------------------------------------------------------------
//
// Label objects so they are referenced by name in debug messages and in
// OpenGL debuggers (renderdoc)
//
//-----------------------------------------------------------------------------
void FGLDebug::LabelObject(GLenum type, GLuint handle, const char *name)
{
if (HasDebugApi() && gl_debug_level != 0)
{
glObjectLabel(type, handle, -1, name);
}
}
void FGLDebug::LabelObjectPtr(void *ptr, const char *name)
{
if (HasDebugApi() && gl_debug_level != 0)
{
glObjectPtrLabel(ptr, -1, name);
}
}
//-----------------------------------------------------------------------------
//
// Marks which render pass/group is executing commands so that debuggers can
// display this information
//
//-----------------------------------------------------------------------------
void FGLDebug::PushGroup(const FString &name)
{
if (HasDebugApi() && gl_debug_level != 0)
{
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, (GLsizei)name.Len(), name.GetChars());
}
if (gpuStatActive)
{
GLuint queryHandle = 0;
glGenQueries(1, &queryHandle);
glBeginQuery(GL_TIME_ELAPSED, queryHandle);
timeElapsedQueries.push_back({ name, queryHandle });
}
}
void FGLDebug::PopGroup()
{
if (HasDebugApi() && gl_debug_level != 0)
{
glPopDebugGroup();
}
if (gpuStatActive)
{
glEndQuery(GL_TIME_ELAPSED);
}
}
//-----------------------------------------------------------------------------
//
// Turns on synchronous debugging on and off based on gl_debug_breakpoint
//
// Allows getting the debugger to break exactly at the OpenGL function emitting
// a message.
//
//-----------------------------------------------------------------------------
void FGLDebug::SetupBreakpointMode()
{
if (mBreakpointMode != gl_debug_breakpoint)
{
if (gl_debug_breakpoint)
{
glDebugMessageCallback(&FGLDebug::DebugCallback, this);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
else
{
glDebugMessageCallback(nullptr, nullptr);
glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
mBreakpointMode = gl_debug_breakpoint;
}
}
//-----------------------------------------------------------------------------
//
// Tells OpenGL which debug messages we are interested in
//
//-----------------------------------------------------------------------------
void FGLDebug::UpdateLoggingLevel()
{
const GLenum level = gl_debug_level;
if (level != mCurrentLevel)
{
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, level > 0);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, level > 1);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr, level > 2);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, level > 3);
mCurrentLevel = level;
}
}
//-----------------------------------------------------------------------------
//
// The log may already contain entries for a debug level we are no longer
// interested in..
//
//-----------------------------------------------------------------------------
bool FGLDebug::IsFilteredByDebugLevel(GLenum severity)
{
int severityLevel = 0;
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH: severityLevel = 1; break;
case GL_DEBUG_SEVERITY_MEDIUM: severityLevel = 2; break;
case GL_DEBUG_SEVERITY_LOW: severityLevel = 3; break;
case GL_DEBUG_SEVERITY_NOTIFICATION: severityLevel = 4; break;
}
return severityLevel > (int)gl_debug_level;
}
//-----------------------------------------------------------------------------
//
// Prints all logged messages to the console
//
//-----------------------------------------------------------------------------
void FGLDebug::OutputMessageLog()
{
if (mCurrentLevel <= 0)
return;
GLint maxDebugMessageLength = 0;
glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &maxDebugMessageLength);
const int maxMessages = 50;
const int messageLogSize = maxMessages * maxDebugMessageLength;
TArray<GLenum> sources, types, severities;
TArray<GLuint> ids;
TArray<GLsizei> lengths;
TArray<GLchar> messageLog;
sources.Resize(maxMessages);
types.Resize(maxMessages);
severities.Resize(maxMessages);
ids.Resize(maxMessages);
lengths.Resize(maxMessages);
messageLog.Resize(messageLogSize);
while (true)
{
GLuint numMessages = glGetDebugMessageLog(maxMessages, messageLogSize, &sources[0], &types[0], &ids[0], &severities[0], &lengths[0], &messageLog[0]);
if (numMessages <= 0) break;
GLsizei offset = 0;
for (GLuint i = 0; i < numMessages; i++)
{
if (!IsFilteredByDebugLevel(severities[i]))
PrintMessage(sources[i], types[i], ids[i], severities[i], lengths[i], &messageLog[offset]);
offset += lengths[i];
}
}
}
//-----------------------------------------------------------------------------
//
// Print a single message to the console
//
//-----------------------------------------------------------------------------
void FGLDebug::PrintMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message)
{
if (type == GL_DEBUG_TYPE_PUSH_GROUP || type == GL_DEBUG_TYPE_POP_GROUP)
return;
const int maxMessages = 50;
static int messagesPrinted = 0;
if (messagesPrinted > maxMessages)
return;
FString msg(message, length);
static std::set<std::string> seenMessages;
bool alreadySeen = !seenMessages.insert(msg.GetChars()).second;
if (alreadySeen)
return;
messagesPrinted++;
if (messagesPrinted == maxMessages)
{
Printf("Max OpenGL debug messages reached. Suppressing further output.\n");
}
else if (messagesPrinted < maxMessages)
{
FString sourceStr = SourceToString(source);
FString typeStr = TypeToString(type);
FString severityStr = SeverityToString(severity);
if (type != GL_DEBUG_TYPE_OTHER)
Printf("[%s] %s, %s: %s\n", sourceStr.GetChars(), severityStr.GetChars(), typeStr.GetChars(), msg.GetChars());
else
Printf("[%s] %s: %s\n", sourceStr.GetChars(), severityStr.GetChars(), msg.GetChars());
}
}
//-----------------------------------------------------------------------------
//
// OpenGL callback function used when synchronous debugging is enabled
//
//-----------------------------------------------------------------------------
void FGLDebug::DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
if (IsFilteredByDebugLevel(severity))
return;
PrintMessage(source, type, id, severity, length, message);
assert(severity == GL_DEBUG_SEVERITY_NOTIFICATION);
}
//-----------------------------------------------------------------------------
//
// Enum to string helpers
//
//-----------------------------------------------------------------------------
FString FGLDebug::SourceToString(GLenum source)
{
FString s;
switch (source)
{
case GL_DEBUG_SOURCE_API: s = "api"; break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: s = "window system"; break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: s = "shader compiler"; break;
case GL_DEBUG_SOURCE_THIRD_PARTY: s = "third party"; break;
case GL_DEBUG_SOURCE_APPLICATION: s = "application"; break;
case GL_DEBUG_SOURCE_OTHER: s = "other"; break;
default: s.Format("%d", (int)source);
}
return s;
}
FString FGLDebug::TypeToString(GLenum type)
{
FString s;
switch (type)
{
case GL_DEBUG_TYPE_ERROR: s = "error"; break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: s = "deprecated"; break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: s = "undefined"; break;
case GL_DEBUG_TYPE_PORTABILITY: s = "portability"; break;
case GL_DEBUG_TYPE_PERFORMANCE: s = "performance"; break;
case GL_DEBUG_TYPE_MARKER: s = "marker"; break;
case GL_DEBUG_TYPE_PUSH_GROUP: s = "push group"; break;
case GL_DEBUG_TYPE_POP_GROUP: s = "pop group"; break;
case GL_DEBUG_TYPE_OTHER: s = "other"; break;
default: s.Format("%d", (int)type);
}
return s;
}
FString FGLDebug::SeverityToString(GLenum severity)
{
FString s;
switch (severity)
{
case GL_DEBUG_SEVERITY_LOW: s = "low severity"; break;
case GL_DEBUG_SEVERITY_MEDIUM: s = "medium severity"; break;
case GL_DEBUG_SEVERITY_HIGH: s = "high severity"; break;
case GL_DEBUG_SEVERITY_NOTIFICATION: s = "notification"; break;
default: s.Format("%d", (int)severity);
}
return s;
}
}

View file

@ -1,44 +0,0 @@
#ifndef __GL_DEBUG_H
#define __GL_DEBUG_H
#include <string.h>
#include "gl_load/gl_interface.h"
#include "c_cvars.h"
#include "v_video.h"
namespace OpenGLRenderer
{
class FGLDebug
{
public:
void Update();
static void LabelObject(GLenum type, GLuint handle, const char *name);
static void LabelObjectPtr(void *ptr, const char *name);
static void PushGroup(const FString &name);
static void PopGroup();
static bool HasDebugApi() { return (gl.flags & RFL_DEBUG) != 0; }
private:
void SetupBreakpointMode();
void UpdateLoggingLevel();
void OutputMessageLog();
static bool IsFilteredByDebugLevel(GLenum severity);
static void PrintMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message);
static void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);
static FString SourceToString(GLenum source);
static FString TypeToString(GLenum type);
static FString SeverityToString(GLenum severity);
GLenum mCurrentLevel = 0;
bool mBreakpointMode = false;
};
}
#endif

View file

@ -1,486 +0,0 @@
/*
** gl_framebuffer.cpp
** Implementation of the non-hardware specific parts of the
** OpenGL frame buffer
**
**---------------------------------------------------------------------------
** Copyright 2010-2020 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "gl_load/gl_system.h"
#include "v_video.h"
#include "m_png.h"
#include "printf.h"
#include "templates.h"
#include "palette.h"
#include "glbackend/glbackend.h"
#include "gl_load/gl_interface.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderbuffers.h"
#include "hwrenderer/data/flatvertices.h"
/*
#include "gl/textures/gl_samplers.h"
#include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "hwrenderer/models/hw_models.h"
#include "hwrenderer/scene/hw_skydome.h"
#include "hwrenderer/data/hw_viewpointbuffer.h"
#include "hwrenderer/dynlights/hw_lightbuffer.h"
#include "gl/shaders/gl_shaderprogram.h"
*/
#include "gl_debug.h"
#include "r_videoscale.h"
//#include "gl_buffers.h"
//#include "hwrenderer/data/flatvertices.h"
EXTERN_CVAR (Bool, vid_vsync)
EXTERN_CVAR(Bool, r_drawvoxels)
EXTERN_CVAR(Int, gl_tonemap)
EXTERN_CVAR(Bool, gl_texture_usehires)
EXTERN_CVAR(Int, gl_ssao)
void gl_LoadExtensions();
void gl_PrintStartupLog();
//void Draw2D(F2DDrawer *drawer, FRenderState &state);
extern bool vid_hdr_active;
void DrawFullscreenBlends();
namespace OpenGLRenderer
{
FGLRenderer *GLRenderer;
//==========================================================================
//
//
//
//==========================================================================
OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, bool fullscreen) :
Super(hMonitor, fullscreen)
{
// SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver.
// If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed!
Super::SetVSync(vid_vsync);
#ifdef IMPLEMENT_IT
// Make sure all global variables tracking OpenGL context state are reset..
FHardwareTexture::InitGlobalState();
gl_RenderState.Reset();
#endif
GLRenderer = nullptr;
}
OpenGLFrameBuffer::~OpenGLFrameBuffer()
{
PPResource::ResetAll();
if (mVertexData != nullptr) delete mVertexData;
#ifdef IMPLEMENT_IT
if (mSkyData != nullptr) delete mSkyData;
if (mViewpoints != nullptr) delete mViewpoints;
if (mLights != nullptr) delete mLights;
mShadowMap.Reset();
#endif
if (GLRenderer)
{
delete GLRenderer;
GLRenderer = nullptr;
}
}
//==========================================================================
//
// Initializes the GL renderer
//
//==========================================================================
void OpenGLFrameBuffer::InitializeState()
{
static bool first=true;
if (first)
{
if (ogl_LoadFunctions() == ogl_LOAD_FAILED)
{
I_FatalError("Failed to load OpenGL functions.");
}
}
gl_LoadExtensions();
// Move some state to the framebuffer object for easier access.
hwcaps = gl.flags;
glslversion = gl.glslversion;
uniformblockalignment = gl.uniformblockalignment;
maxuniformblock = gl.maxuniformblock;
vendorstring = gl.vendorstring;
if (first)
{
first=false;
gl_PrintStartupLog();
}
glDepthFunc(GL_LESS);
glEnable(GL_DITHER);
glDisable(GL_CULL_FACE);
glDisable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_CLAMP);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LINE_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
SetViewportRects(nullptr);
mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight());
#ifdef IMPLEMENT_IT
mSkyData = new FSkyVertexBuffer;
mViewpoints = new HWViewpointBuffer;
mLights = new FLightBuffer();
#endif
GLRenderer = new FGLRenderer(this);
GLRenderer->Initialize(GetWidth(), GetHeight());
#ifdef IMPLEMENT_IT
static_cast<GLDataBuffer*>(mLights->GetBuffer())->BindBase();
#endif
mDebug = std::make_shared<FGLDebug>();
mDebug->Update();
}
//==========================================================================
//
// Updates the screen
//
//==========================================================================
void OpenGLFrameBuffer::Update()
{
#if 0
twoD.Reset();
Flush3D.Reset();
Flush3D.Clock();
#endif
GLRenderer->Flush();
// Flush3D.Unclock();
Swap();
Super::Update();
screen->mVertexData->Reset();
}
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void OpenGLFrameBuffer::WriteSavePic(FileWriter *file, int width, int height)
{
GLRenderer->WriteSavePic(file, width, height);
}
const char* OpenGLFrameBuffer::DeviceName() const
{
return gl.modelstring;
}
//==========================================================================
//
// Swap the buffers
//
//==========================================================================
CVAR(Bool, gl_finishbeforeswap, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
void OpenGLFrameBuffer::Swap()
{
bool swapbefore = gl_finishbeforeswap && camtexcount == 0;
//Finish.Reset();
//Finish.Clock();
if (swapbefore) glFinish();
FPSLimit();
SwapBuffers();
if (!swapbefore) glFinish();
//Finish.Unclock();
camtexcount = 0;
//FHardwareTexture::UnbindAll();
mDebug->Update();
}
//==========================================================================
//
// Enable/disable vertical sync
//
//==========================================================================
void OpenGLFrameBuffer::SetVSync(bool vsync)
{
// Switch to the default frame buffer because some drivers associate the vsync state with the bound FB object.
GLint oldDrawFramebufferBinding = 0, oldReadFramebufferBinding = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDrawFramebufferBinding);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldReadFramebufferBinding);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
Super::SetVSync(vsync);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDrawFramebufferBinding);
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldReadFramebufferBinding);
}
//===========================================================================
//
//
//===========================================================================
void OpenGLFrameBuffer::CleanForRestart()
{
}
#ifdef IMPLEMENT_IT
void OpenGLFrameBuffer::SetTextureFilterMode()
{
//if (GLRenderer != nullptr && GLRenderer->mSamplerManager != nullptr) GLRenderer->mSamplerManager->SetTextureFilterMode();
}
IHardwareTexture *OpenGLFrameBuffer::CreateHardwareTexture()
{
return nullptr;// new FHardwareTexture(true/*tex->bNoCompress*/);
}
void OpenGLFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation)
{
auto tex = mat->tex;
if (tex->isSWCanvas()) return;
// Textures that are already scaled in the texture lump will not get replaced by hires textures.
int flags = mat->isExpanded() ? CTF_Expand : (gl_texture_usehires && !tex->isScaled()) ? CTF_CheckHires : 0;
int numLayers = mat->GetLayers();
auto base = static_cast<FHardwareTexture*>(mat->GetLayer(0, translation));
if (base->BindOrCreate(tex, 0, CLAMP_NONE, translation, flags))
{
for (int i = 1; i < numLayers; i++)
{
FTexture *layer;
auto systex = static_cast<FHardwareTexture*>(mat->GetLayer(i, 0, &layer));
systex->BindOrCreate(layer, i, CLAMP_NONE, 0, mat->isExpanded() ? CTF_Expand : 0);
}
}
// unbind everything.
FHardwareTexture::UnbindAll();
}
FModelRenderer *OpenGLFrameBuffer::CreateModelRenderer(int mli)
{
return new FHWModelRenderer(nullptr, gl_RenderState, mli);
}
#endif
IVertexBuffer *OpenGLFrameBuffer::CreateVertexBuffer()
{
return new GLVertexBuffer;
}
IIndexBuffer *OpenGLFrameBuffer::CreateIndexBuffer()
{
return new GLIndexBuffer;
}
IDataBuffer *OpenGLFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize)
{
return new GLDataBuffer(bindingpoint, ssbo);
}
#ifdef IMPLEMENT_IT
void OpenGLFrameBuffer::TextureFilterChanged()
{
if (GLRenderer != NULL && GLRenderer->mSamplerManager != NULL) GLRenderer->mSamplerManager->SetTextureFilterMode();
}
#endif
void OpenGLFrameBuffer::BlurScene(float amount)
{
GLRenderer->BlurScene(amount);
}
#if 0
void OpenGLFrameBuffer::UpdatePalette()
{
if (GLRenderer)
GLRenderer->ClearTonemapPalette();
}
#endif
//===========================================================================
//
//
//
//===========================================================================
void OpenGLFrameBuffer::BeginFrame()
{
SetViewportRects(nullptr);
if (GLRenderer != nullptr)
GLRenderer->BeginFrame();
}
//===========================================================================
//
// Takes a screenshot
//
//===========================================================================
TArray<uint8_t> OpenGLFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma)
{
const auto &viewport = mOutputLetterbox;
// Grab what is in the back buffer.
// We cannot rely on SCREENWIDTH/HEIGHT here because the output may have been scaled.
TArray<uint8_t> pixels;
pixels.Resize(viewport.width * viewport.height * 3);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(viewport.left, viewport.top, viewport.width, viewport.height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
// Copy to screenshot buffer:
int w = SCREENWIDTH;
int h = SCREENHEIGHT;
TArray<uint8_t> ScreenshotBuffer(w * h * 3, true);
float rcpWidth = 1.0f / w;
float rcpHeight = 1.0f / h;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
float u = (x + 0.5f) * rcpWidth;
float v = (y + 0.5f) * rcpHeight;
int sx = u * viewport.width;
int sy = v * viewport.height;
int sindex = (sx + sy * viewport.width) * 3;
int dindex = (x + (h - y - 1) * w) * 3;
ScreenshotBuffer[dindex] = pixels[sindex];
ScreenshotBuffer[dindex + 1] = pixels[sindex + 1];
ScreenshotBuffer[dindex + 2] = pixels[sindex + 2];
}
}
pitch = w * 3;
color_type = SS_RGB;
// Screenshot should not use gamma correction if it was already applied to rendered image
gamma = 1;
return ScreenshotBuffer;
}
//===========================================================================
//
// 2D drawing
//
//===========================================================================
void OpenGLFrameBuffer::Draw2D()
{
if (GLRenderer != nullptr)
{
GLRenderer->mBuffers->BindCurrentFB();
::DrawFullscreenBlends();
DrawRateStuff();
auto savepal = curbasepal;
if (!(curpaletteflags & (Pal_Fullscreen|Pal_2D))) curbasepal = 0;
GLInterface.Draw2D(&twodgen);
curbasepal = savepal;
}
}
void OpenGLFrameBuffer::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
{
GLRenderer->PostProcessScene(fixedcm, afterBloomDrawEndScene2D);
}
}
void videoShowFrame(int32_t w)
{
static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
if (gl_ssao)
{
glDrawBuffers(1, buffers);
OpenGLRenderer::GLRenderer->AmbientOccludeScene(GLInterface.GetProjectionM5());
glViewport(screen->mSceneViewport.left, screen->mSceneViewport.top, screen->mSceneViewport.width, screen->mSceneViewport.height);
OpenGLRenderer::GLRenderer->mBuffers->BindSceneFB(true);
glDrawBuffers(3, buffers);
// To do: the translucent part of the scene should be drawn here
glDrawBuffers(1, buffers);
}
OpenGLRenderer::GLRenderer->mBuffers->BlitSceneToTexture(); // Copy the resulting scene to the current post process texture
screen->PostProcessScene(0, []() {
GLInterface.Draw2D(&twodpsp); // draws the weapon sprites
});
screen->Update();
// After finishing the frame, reset everything for the next frame. This needs to be done better.
screen->BeginFrame();
if (gl_ssao)
{
OpenGLRenderer::GLRenderer->mBuffers->BindSceneFB(true);
glDrawBuffers(3, buffers);
}
else
{
OpenGLRenderer::GLRenderer->mBuffers->BindSceneFB(false);
}
twodpsp.Clear();
twodgen.Clear();
GLInterface.ResetFrame();
}

View file

@ -1,65 +0,0 @@
#ifndef __GL_FRAMEBUFFER
#define __GL_FRAMEBUFFER
#include "gl_sysfb.h"
#include <memory>
namespace OpenGLRenderer
{
class FGLDebug;
class OpenGLFrameBuffer : public SystemGLFrameBuffer
{
typedef SystemGLFrameBuffer Super;
public:
explicit OpenGLFrameBuffer() {}
OpenGLFrameBuffer(void *hMonitor, bool fullscreen) ;
~OpenGLFrameBuffer();
void InitializeState() override;
void Update() override;
void Draw2D() override;
void CleanForRestart() override;
const char* DeviceName() const override;
void WriteSavePic(FileWriter* file, int width, int height) override;
#ifdef IMPLEMENT_IT
void SetTextureFilterMode() override;
IHardwareTexture *CreateHardwareTexture() override;
void PrecacheMaterial(FMaterial *mat, int translation) override;
FModelRenderer *CreateModelRenderer(int mli) override;
void TextureFilterChanged() override;
#endif
void BeginFrame() override;
//void SetViewportRects(IntRect *bounds) override;
void BlurScene(float amount) override;
IVertexBuffer *CreateVertexBuffer() override;
IIndexBuffer *CreateIndexBuffer() override;
IDataBuffer *CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize) override;
// Retrieves a buffer containing image data for a screenshot.
// Hint: Pitch can be negative for upside-down images, in which case buffer
// points to the last row in the buffer, which will be the first row output.
virtual TArray<uint8_t> GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) override;
void Swap();
bool IsHWGammaActive() const { return HWGammaActive; }
void SetVSync(bool vsync) override;
//void Draw2D() override;
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) override;
bool HWGammaActive = false; // Are we using hardware or software gamma?
std::shared_ptr<FGLDebug> mDebug; // Debug API
int camtexcount = 0;
};
}
#endif //__GL_FRAMEBUFFER