/* ** 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 "gles_system.h" #include "m_png.h" #include "gles_buffers.h" #include "gles_framebuffer.h" #include "gles_renderbuffers.h" #include "gles_renderer.h" #include "gles_postprocessstate.h" #include "gles_shaderprogram.h" #include "hwrenderer/postprocessing/hw_postprocess.h" #include "hwrenderer/postprocessing/hw_postprocess_cvars.h" #include "flatvertices.h" #include "r_videoscale.h" #include "v_video.h" #include "templates.h" #include "hw_vrmodes.h" #include "v_draw.h" extern bool vid_hdr_active; namespace OpenGLESRenderer { int gl_dither_bpc = -1; void FGLRenderer::RenderScreenQuad() { auto buffer = static_cast(screen->mVertexData->GetBufferObjects().first); buffer->Bind(nullptr); glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 3); } void FGLRenderer::PostProcessScene(int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) { #ifndef NO_RENDER_BUFFER mBuffers->BindCurrentFB(); #endif if (afterBloomDrawEndScene2D) afterBloomDrawEndScene2D(); } //----------------------------------------------------------------------------- // // 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) { #ifdef NO_RENDER_BUFFER mBuffers->BindOutputFB(); #endif screen->Draw2D(); // draw all pending 2D stuff before copying the buffer twod->Clear(); FGLPostProcessState savedState; savedState.SaveTextureBindings(2); mBuffers->BindOutputFB(); IntRect box; if (bounds) { box = *bounds; } else { ClearBorders(); box = screen->mOutputLetterbox; } mBuffers->BindCurrentTexture(0); #ifndef NO_RENDER_BUFFER DrawPresentTexture(box, applyGamma); #endif } 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(vid_gamma, 0.1f, 4.f); mPresentShader->Uniforms->Contrast = clamp(vid_contrast, 0.1f, 3.f); mPresentShader->Uniforms->Brightness = clamp(vid_brightness, -0.8f, 0.8f); mPresentShader->Uniforms->Saturation = clamp(vid_saturation, -15.0f, 15.f); mPresentShader->Uniforms->GrayFormula = static_cast(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(); for (int n = 0; n < mPresentShader->Uniforms.mFields.size(); n++) { int index = -1; UniformFieldDesc desc = mPresentShader->Uniforms.mFields[n]; int loc = mPresentShader->Uniforms.UniformLocation[n]; switch (desc.Type) { case UniformType::Int: glUniform1i(loc, *((GLint*)(((char*)(&mPresentShader->Uniforms)) + desc.Offset))); break; case UniformType::Float: glUniform1f(loc, *((GLfloat*)(((char*)(&mPresentShader->Uniforms)) + desc.Offset))); break; case UniformType::Vec2: glUniform2fv(loc,1 , ((GLfloat*)(((char*)(&mPresentShader->Uniforms)) + desc.Offset))); break; } } 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); } }