diff --git a/src/common/rendering/gl_load/gl_interface.cpp b/src/common/rendering/gl_load/gl_interface.cpp index 431c4a029e2..fd6542b736e 100644 --- a/src/common/rendering/gl_load/gl_interface.cpp +++ b/src/common/rendering/gl_load/gl_interface.cpp @@ -141,7 +141,6 @@ void gl_LoadExtensions() #ifdef __MOBILE__ gl_version = 3.31; gl.flags |= RFL_NO_CLIP_PLANES; - gl.flags |= RFL_INVALIDATE_BUFFER; gl.flags |= RFL_SHADER_STORAGE_BUFFER; #endif // Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!) @@ -157,6 +156,8 @@ void gl_LoadExtensions() gl.vendorstring = (char*)glGetString(GL_VENDOR); gl.modelstring = (char*)glGetString(GL_RENDERER); + bool isGLES = strstr(glversion, "OpenGL ES") != NULL; + // first test for optional features if (CheckExtension("GL_ARB_texture_compression")) gl.flags |= RFL_TEXTURE_COMPRESSION; if (CheckExtension("GL_EXT_texture_compression_s3tc")) gl.flags |= RFL_TEXTURE_COMPRESSION_S3TC; @@ -194,8 +195,10 @@ void gl_LoadExtensions() if (gl_no_clip_planes) gl.flags |= RFL_NO_CLIP_PLANES; - if (gl_version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; - if (gl_version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; + if (((isGLES && gl_version >= 3.0f) || (!isGLES && gl_version >= 4.3f)) || CheckExtension("GL_ARB_invalidate_subdata")) + gl.flags |= RFL_INVALIDATE_BUFFER; + if (((isGLES && gl_version >= 3.2f) || (!isGLES && gl_version >= 4.3f)) || CheckExtension("GL_KHR_debug")) + gl.flags |= RFL_DEBUG; glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &v); gl.maxuniforms = v; diff --git a/src/common/rendering/gles/gles_buffers.cpp b/src/common/rendering/gles/gles_buffers.cpp index 7f56f737f6b..f17078aa212 100644 --- a/src/common/rendering/gles/gles_buffers.cpp +++ b/src/common/rendering/gles/gles_buffers.cpp @@ -60,6 +60,22 @@ GLBuffer::GLBuffer(int usetype) { glGenBuffers(1, &mBufferId); isData = false; + hasGLBuffer = true; + } + else if ((usetype == GL_UNIFORM_BUFFER) || (usetype == GL_SHADER_STORAGE_BUFFER)) + { + // GLDataBuffer stays a CPU-side buffer for viewpoint/lights/bones, + // which read it back via a raw pointer into `memory` and never touch + // the GPU object (gles_renderstate.cpp's ApplyShader()/ApplyViewport()), + // so isData stays true and that CPU shadow copy is kept exactly as + // before. But real postprocess/custom shaders declare a genuine + // `layout(std140) uniform Uniforms{}` block and can only read it via + // an actually-uploaded, actually-bound GL buffer, so also create and + // track a real one here - mirroring gl_buffers.cpp, whose GLBuffer + // always has a real buffer object regardless of type. + glGenBuffers(1, &mBufferId); + hasGLBuffer = true; + isData = true; } else { @@ -71,7 +87,11 @@ GLBuffer::~GLBuffer() { if (mBufferId != 0) { - if (gles.useMappedBuffers) + // Only real vertex/index buffers are ever glMapBufferRange'd (Map()/ + // Lock() below are still gated on !isData); data buffers (isData == + // true, including the uniform/storage ones above) are only ever + // written via SetData()/glBufferData, so must not be glUnmapBuffer'd. + if (!isData && gles.useMappedBuffers) { glBindBuffer(mUseType, mBufferId); glUnmapBuffer(mUseType); @@ -86,13 +106,12 @@ GLBuffer::~GLBuffer() void GLBuffer::Bind() { - if (!isData) + if (hasGLBuffer) { glBindBuffer(mUseType, mBufferId); } } - void GLBuffer::SetData(size_t size, const void* data, BufferUsageType usage) { bool staticdata = (usage == BufferUsageType::Static || usage == BufferUsageType::Mappable); @@ -107,7 +126,7 @@ void GLBuffer::SetData(size_t size, const void* data, BufferUsageType usage) memcpy(memory, data, size); } - if (!isData) + if (hasGLBuffer) { Bind(); glBufferData(mUseType, size, data, staticdata ? GL_STATIC_DRAW : GL_STREAM_DRAW); @@ -132,7 +151,7 @@ void GLBuffer::SetSubData(size_t offset, size_t size, const void *data) memcpy(memory + offset, data, size); - if (!isData) + if (hasGLBuffer) { glBufferSubData(mUseType, offset, size, data); } @@ -314,7 +333,7 @@ void GLVertexBuffer::Bind(int *offsets) glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.normalize, (GLsizei)mStride, (void*)(intptr_t)ofs); else { - if (gles.glesMode >= GLES_MODE_OGL3) + if (strcmp(gles.shaderVersionString, "100") != 0) glVertexAttribIPointer(i, attrinf.size, attrinf.format, (GLsizei)mStride, (void*)(intptr_t)ofs); } } @@ -332,12 +351,12 @@ void GLDataBuffer::BindRange(FRenderState *state, size_t start, size_t 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(0), mBindingPoint(bindingpoint) {} +GLDataBuffer::GLDataBuffer(int bindingpoint, bool is_ssbo) : GLBuffer(is_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_UNIFORM_BUFFER), mBindingPoint(bindingpoint) {} } \ No newline at end of file diff --git a/src/common/rendering/gles/gles_buffers.h b/src/common/rendering/gles/gles_buffers.h index 50156ef7ee5..abd4f1edf47 100644 --- a/src/common/rendering/gles/gles_buffers.h +++ b/src/common/rendering/gles/gles_buffers.h @@ -22,6 +22,7 @@ class GLBuffer : virtual public IBuffer GLsync mGLSync = 0; bool isData = false; + bool hasGLBuffer = false; // true once a real glGenBuffers() object backs mBufferId char *memory = nullptr; GLBuffer(int usetype); diff --git a/src/common/rendering/gles/gles_framebuffer.cpp b/src/common/rendering/gles/gles_framebuffer.cpp index 2ec5ffc3336..2a29573466f 100644 --- a/src/common/rendering/gles/gles_framebuffer.cpp +++ b/src/common/rendering/gles/gles_framebuffer.cpp @@ -356,6 +356,18 @@ IDataBuffer *OpenGLFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bo return new GLDataBuffer(bindingpoint, ssbo); } +void OpenGLFrameBuffer::BlurScene(float amount) +{ + if (GLRenderer != nullptr) + GLRenderer->BlurScene(amount); +} + +void OpenGLFrameBuffer::UpdatePalette() +{ + if (GLRenderer != nullptr) + GLRenderer->ClearTonemapPalette(); +} + void OpenGLFrameBuffer::SetViewportRects(IntRect *bounds) { @@ -516,7 +528,7 @@ void OpenGLFrameBuffer::Draw2D(bool outside2D) void OpenGLFrameBuffer::PostProcessScene(bool swscene, int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) { - //if (!swscene) GLRenderer->mBuffers->BlitSceneToTexture(); // Copy the resulting scene to the current post process texture + if (!swscene) GLRenderer->mBuffers->BlitSceneToTexture(); // Copy the resulting scene to the current post process texture GLRenderer->PostProcessScene(fixedcm, flash, afterBloomDrawEndScene2D); } diff --git a/src/common/rendering/gles/gles_framebuffer.h b/src/common/rendering/gles/gles_framebuffer.h index 7452b6127d9..67bd6fb68ed 100644 --- a/src/common/rendering/gles/gles_framebuffer.h +++ b/src/common/rendering/gles/gles_framebuffer.h @@ -59,6 +59,8 @@ class OpenGLFrameBuffer : public SystemGLFrameBuffer void Draw2D(bool outside2D = false) override; void PostProcessScene(bool swscene, int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) override; + void BlurScene(float amount) override; + void UpdatePalette() override; bool HWGammaActive = false; // Are we using hardware or software gamma? std::unique_ptr mDebug; // Debug API diff --git a/src/common/rendering/gles/gles_postprocess.cpp b/src/common/rendering/gles/gles_postprocess.cpp index 144315d718e..46d99427bad 100644 --- a/src/common/rendering/gles/gles_postprocess.cpp +++ b/src/common/rendering/gles/gles_postprocess.cpp @@ -56,10 +56,49 @@ void FGLRenderer::RenderScreenQuad() void FGLRenderer::PostProcessScene(int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) { + int sceneWidth = mBuffers->GetSceneWidth(); + int sceneHeight = mBuffers->GetSceneHeight(); + + GLPPRenderState renderstate(mBuffers); + + if (strcmp(gles.shaderVersionString, "100") != 0) + { + hw_postprocess.Pass1(&renderstate, fixedcm, sceneWidth, sceneHeight); + } #ifndef NO_RENDER_BUFFER mBuffers->BindCurrentFB(); #endif if (afterBloomDrawEndScene2D) afterBloomDrawEndScene2D(); + if (strcmp(gles.shaderVersionString, "100") != 0) + { + hw_postprocess.Pass2(&renderstate, fixedcm, flash, sceneWidth, sceneHeight); + } +} + +void FGLRenderer::BlurScene(float gameinfobluramount) +{ + int sceneWidth = mBuffers->GetSceneWidth(); + int sceneHeight = mBuffers->GetSceneHeight(); + + if (strcmp(gles.shaderVersionString, "100") == 0) + { + return; + } + + GLPPRenderState renderstate(mBuffers); + + auto vrmode = VRMode::GetVRMode(true); + int eyeCount = vrmode->mEyeCount; + for (int i = 0; i < eyeCount; ++i) + { + hw_postprocess.bloom.RenderBlur(&renderstate, sceneWidth, sceneHeight, gameinfobluramount); + mBuffers->NextEye(eyeCount); + } +} + +void FGLRenderer::ClearTonemapPalette() +{ + hw_postprocess.tonemap.ClearTonemapPalette(); } diff --git a/src/common/rendering/gles/gles_renderbuffers.cpp b/src/common/rendering/gles/gles_renderbuffers.cpp index 253619ca7d3..f66651cec4c 100644 --- a/src/common/rendering/gles/gles_renderbuffers.cpp +++ b/src/common/rendering/gles/gles_renderbuffers.cpp @@ -23,6 +23,7 @@ #include "v_video.h" #include "printf.h" #include "hw_cvars.h" +#include "gles_debug.h" #include "gles_renderer.h" #include "gles_renderbuffers.h" #include "gles_postprocessstate.h" @@ -56,6 +57,7 @@ namespace OpenGLESRenderer FGLRenderBuffers::~FGLRenderBuffers() { ClearScene(); + ClearPipeline(); ClearEyeBuffers(); DeleteTexture(mDitherTexture); @@ -68,6 +70,17 @@ namespace OpenGLESRenderer DeleteRenderBuffer(mSceneStencilBuf); } + void FGLRenderBuffers::ClearPipeline() + { + for (int i = 0; i < NumPipelineTextures; i++) + { + DeleteFrameBuffer(mPipelineFB[i]); + DeleteTexture(mPipelineTexture[i]); + } + DeleteRenderBuffer(mPipelineDepthStencilBuf); + DeleteRenderBuffer(mPipelineStencilBuf); + } + void FGLRenderBuffers::ClearEyeBuffers() { for (auto handle : mEyeFBs) @@ -162,7 +175,9 @@ namespace OpenGLESRenderer mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH_COMPONENT16, width, height); mSceneStencilBuf = CreateRenderBuffer("SceneStencil", GL_STENCIL_INDEX8, width, height); } - mSceneFB= CreateFrameBuffer("SceneFB", mSceneTex, mSceneDepthStencilBuf, mSceneStencilBuf); + // The scene is rendered directly into pipeline texture 0, which then doubles as + // the first postprocess ping-pong buffer (no separate scene texture/blit needed). + mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mSceneDepthStencilBuf, mSceneStencilBuf); } //========================================================================== @@ -173,9 +188,24 @@ namespace OpenGLESRenderer void FGLRenderBuffers::CreatePipeline(int width, int height) { + ClearPipeline(); ClearEyeBuffers(); - mSceneTex = Create2DTexture("PipelineTexture", GL_RGBA, width, height); + for (int i = 0; i < NumPipelineTextures; i++) + mPipelineTexture[i] = Create2DTexture("PipelineTexture", GL_RGBA, width, height); + + // Pipeline FBOs get their own depth/stencil buffer, distinct from the scene's, + // since they're used for 2D-only postprocess quads (depth test always disabled there). + if (gles.depthStencilAvailable) + mPipelineDepthStencilBuf = CreateRenderBuffer("PipelineDepthStencil", GL_DEPTH24_STENCIL8, width, height); + else + { + mPipelineDepthStencilBuf = CreateRenderBuffer("PipelineDepthStencil", GL_DEPTH_COMPONENT16, width, height); + mPipelineStencilBuf = CreateRenderBuffer("PipelineStencil", GL_STENCIL_INDEX8, width, height); + } + + for (int i = 0; i < NumPipelineTextures; i++) + mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i], mPipelineDepthStencilBuf, mPipelineStencilBuf); } //========================================================================== @@ -401,7 +431,7 @@ namespace OpenGLESRenderer { CreateEyeBuffers(eye); - glBindFramebuffer(GL_READ_FRAMEBUFFER, mSceneFB.handle); + glBindFramebuffer(GL_READ_FRAMEBUFFER, GetCurrentFB().handle); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mEyeFBs[eye].handle); glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); @@ -413,7 +443,7 @@ namespace OpenGLESRenderer { if (mEyeFBs.Size() <= unsigned(eye)) return; - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mSceneFB.handle); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GetCurrentFB().handle); glBindFramebuffer(GL_READ_FRAMEBUFFER, mEyeFBs[eye].handle); glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); @@ -481,7 +511,18 @@ namespace OpenGLESRenderer void FGLRenderBuffers::BindCurrentTexture(int index, int filter, int wrap) { - mSceneTex.Bind(index, filter, wrap); + mPipelineTexture[mCurrentPipelineTexture].Bind(index, filter, wrap); + } + + //========================================================================== + // + // Binds the current scene color texture to the specified texture unit + // + //========================================================================== + + void FGLRenderBuffers::BindSceneColorTexture(int index) + { + mPipelineTexture[0].Bind(index, GL_NEAREST, GL_CLAMP_TO_EDGE); } //========================================================================== @@ -493,10 +534,46 @@ namespace OpenGLESRenderer void FGLRenderBuffers::BindCurrentFB() { #ifndef NO_RENDER_BUFFER - mSceneFB.Bind(); + mPipelineFB[mCurrentPipelineTexture].Bind(); #endif } + //========================================================================== + // + // Makes the frame buffer for the next texture active + // + //========================================================================== + + void FGLRenderBuffers::BindNextFB() + { + int nextPipelineTexture = (mCurrentPipelineTexture + 1) % NumPipelineTextures; + mPipelineFB[nextPipelineTexture].Bind(); + } + + //========================================================================== + // + // Advances to the next pipeline texture (after having rendered into it) + // + //========================================================================== + + void FGLRenderBuffers::NextTexture() + { + mCurrentPipelineTexture = (mCurrentPipelineTexture + 1) % NumPipelineTextures; + } + + //========================================================================== + // + // Resets the postprocess ping-pong chain to point back at the rendered scene + // (pipeline texture 0). GLES has no multisampled scene buffer, so the scene + // is already rendered directly into pipeline texture 0 - no actual blit needed. + // + //========================================================================== + + void FGLRenderBuffers::BlitSceneToTexture() + { + mCurrentPipelineTexture = 0; + } + //========================================================================== // // Makes the screen frame buffer active @@ -516,7 +593,190 @@ namespace OpenGLESRenderer 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(); + + // GLES only has an 8-bit RGBA pipeline - downgrade any HDR/higher precision + // pixel format requests to something the GLES driver actually supports. + GLuint glformat = GL_RGBA; + 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(texture->Backend.get()); + } + + //========================================================================== + // + // Compile the shaders declared by post process effects + // + //========================================================================== + + FShaderProgram *GLPPRenderState::GetGLShader(PPShader *shader) + { + if (!shader->Backend) + { + auto glshader = std::make_unique(); + + FString prolog; + if (!shader->Uniforms.empty()) + prolog = UniformBlockDecl::Create("Uniforms", shader->Uniforms, POSTPROCESS_BINDINGPOINT); + prolog += shader->Defines; + + FString vertexShaderPath = shader->VertexShader; + vertexShaderPath.Substitute("shaders/", "shaders_gles/"); + + glshader->Compile(FShaderProgram::Vertex, vertexShaderPath.GetChars(), "", shader->Version); + glshader->Compile(FShaderProgram::Fragment, shader->FragmentShader.GetChars(), prolog.GetChars(), shader->Version); + glshader->Link(shader->FragmentShader.GetChars()); + if (!shader->Uniforms.empty()) + glshader->SetUniformBufferLocation(POSTPROCESS_BINDINGPOINT, "Uniforms"); + + shader->Backend = std::move(glshader); + } + return static_cast(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: + case PPTextureType::SceneNormal: + case PPTextureType::SceneDepth: + // GLES has no separate G-buffer - these are not used by any effect enabled on this backend. + buffers->BindSceneColorTexture(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(), BufferUsageType::Static); + static_cast(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(); + } } // namespace OpenGLESRenderer \ No newline at end of file diff --git a/src/common/rendering/gles/gles_renderbuffers.h b/src/common/rendering/gles/gles_renderbuffers.h index fc018f5f3c7..124f2f215c1 100644 --- a/src/common/rendering/gles/gles_renderbuffers.h +++ b/src/common/rendering/gles/gles_renderbuffers.h @@ -83,6 +83,21 @@ class PPGLTextureBackend : public PPTextureBackend 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 { @@ -93,13 +108,15 @@ class FGLRenderBuffers void Setup(int width, int height, int sceneWidth, int sceneHeight); void BindSceneFB(bool sceneData); - + void BindSceneColorTexture(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(); @@ -119,6 +136,7 @@ class FGLRenderBuffers private: void ClearScene(); + void ClearPipeline(); void ClearEyeBuffers(); void CreateScene(int width, int height); @@ -144,12 +162,19 @@ class FGLRenderBuffers // Buffers for the scene PPGLTexture mSceneDepthStencilTex; - PPGLTexture mSceneTex; PPGLRenderBuffer mSceneDepthStencilBuf; PPGLRenderBuffer mSceneStencilBuf; // This is only use when combined depth-stencil is not avaliable PPGLFrameBuffer mSceneFB; bool mSceneUsesTextures = false; + // Effect/postprocess ping-pong buffers (also holds the rendered scene in slot 0) + static const int NumPipelineTextures = 2; + int mCurrentPipelineTexture = 0; + PPGLTexture mPipelineTexture[NumPipelineTextures]; + PPGLFrameBuffer mPipelineFB[NumPipelineTextures]; + PPGLRenderBuffer mPipelineDepthStencilBuf; + PPGLRenderBuffer mPipelineStencilBuf; // This is only used when combined depth-stencil is not available + // Eye buffers TArray mEyeTextures; TArray mEyeFBs; diff --git a/src/common/rendering/gles/gles_renderer.h b/src/common/rendering/gles/gles_renderer.h index 9b06271a1a3..7f8e315c2b9 100644 --- a/src/common/rendering/gles/gles_renderer.h +++ b/src/common/rendering/gles/gles_renderer.h @@ -65,6 +65,8 @@ class FGLRenderer void PresentStereo(); void RenderScreenQuad(); void PostProcessScene(int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D); + void BlurScene(float gameinfobluramount); + void ClearTonemapPalette(); void CopyToBackbuffer(const IntRect *bounds, bool applyGamma); void DrawPresentTexture(const IntRect &box, bool applyGamma); diff --git a/src/common/rendering/gles/gles_renderstate.cpp b/src/common/rendering/gles/gles_renderstate.cpp index 5995fd3b638..65ae1f92fd5 100644 --- a/src/common/rendering/gles/gles_renderstate.cpp +++ b/src/common/rendering/gles/gles_renderstate.cpp @@ -346,7 +346,7 @@ bool FGLRenderState::ApplyShader() activeShader->cur->muLightRange.Set(range); } - if (gles.glesMode >= GLES_MODE_OGL3) + if (strcmp(gles.shaderVersionString, "100") != 0) { // Upload bone data // NOTE, this is pretty inefficient, it will be reloading the same data over and over in a single frame @@ -354,11 +354,8 @@ bool FGLRenderState::ApplyShader() if ((mBoneIndexBase >= 0)) { float* bonesPtr = ((float*)screen->mBones->GetBuffer()->Memory()); - int number = screen->mBones->GetCurrentIndex(); - glUniformMatrix4fv(activeShader->cur->bones_index, number, false, bonesPtr); - activeShader->cur->muBoneIndexBase.Set(mBoneIndexBase); } } diff --git a/src/common/rendering/gles/gles_shader.cpp b/src/common/rendering/gles/gles_shader.cpp index dba9a233a69..4880f96c90e 100644 --- a/src/common/rendering/gles/gles_shader.cpp +++ b/src/common/rendering/gles/gles_shader.cpp @@ -47,6 +47,8 @@ #include #include +EXTERN_CVAR(Bool, gl_customshader) + namespace OpenGLESRenderer { @@ -220,13 +222,14 @@ void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, con SaveShaders(); } -bool FShader::Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines) +bool FShader::Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines, bool userShaderPath) { mVertProg = vert_prog_lump; mFragProg = fragprog; mFragProg2 = fragprog2; mLightProg = light_fragprog; mDefinesBase = defines; + mUserShaderPath = userShaderPath; return true; } @@ -248,7 +251,10 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * vert_prog_lump.Substitute("shaders/", "shaders_gles/"); frag_prog_lump.Substitute("shaders/", "shaders_gles/"); - proc_prog_lump.Substitute("shaders/", "shaders_gles/"); + // A user/mod-supplied custom shader lives at its own literal path and has no + // counterpart under shaders_gles/, so it must not be rewritten. + if (!mUserShaderPath) + proc_prog_lump.Substitute("shaders/", "shaders_gles/"); light_fragprog.Substitute("shaders/", "shaders_gles/"); //light_fragprog.Substitute("material_pbr", "material_normal"); @@ -351,6 +357,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * uniform sampler2D texture9; uniform sampler2D texture10; uniform sampler2D texture11; + uniform sampler2D texture12; // timer data uniform float timer; @@ -737,7 +744,7 @@ bool FShader::Bind(ShaderFlavourData& flavour) // //========================================================================== -FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType) +FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType, bool userShaderPath) { FString defines; defines += shaderdefines; @@ -745,7 +752,7 @@ FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderP if (!usediscard) defines += "#define NO_ALPHATEST\n"; FShader *shader = new FShader(ShaderName); - shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars()); + shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars(), userShaderPath); return shader; } @@ -843,15 +850,16 @@ void FShaderCollection::CompileShaders(EPassType passType) } } -#if 0 - for(unsigned i = 0; i < usershaders.Size(); i++) + if (gl_customshader) { - FString name = ExtractFileBase(usershaders[i].shader); - FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines; - FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, passType); - mMaterialShaders.Push(shc); + for(unsigned i = 0; i < usershaders.Size(); i++) + { + FString name = ExtractFileBase(usershaders[i].shader.GetChars()); + FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines; + FShader *shc = Compile(name.GetChars(), usershaders[i].shader.GetChars(), defaultshaders[usershaders[i].shaderType].lightfunc, defines.GetChars(), true, passType, true); + mMaterialShaders.Push(shc); + } } -#endif for(int i=0;iglslversion < 4.20) + { + GLuint uniformBlockIndex = glGetUniformBlockIndex(mProgram, name); + if (uniformBlockIndex != GL_INVALID_INDEX) + glUniformBlockBinding(mProgram, uniformBlockIndex, index); + } } //========================================================================== diff --git a/src/common/rendering/gles/gles_system.cpp b/src/common/rendering/gles/gles_system.cpp index d9bedafdd70..58210b1b6a5 100644 --- a/src/common/rendering/gles/gles_system.cpp +++ b/src/common/rendering/gles/gles_system.cpp @@ -8,7 +8,7 @@ CVAR(Bool, gles_use_mapped_buffer, false, 0); CVAR(Bool, gles_force_glsl_v100, false, 0); CVAR(Int, gles_max_lights_per_surface, 32, 0); -EXTERN_CVAR(Bool, gl_customshader); + void setGlVersion(double glv); @@ -165,8 +165,17 @@ namespace OpenGLESRenderer Printf("GL Version parsed = %f\n", glVersion); + bool isGLES = !strncmp(glVersionStr, "OpenGL ES", strlen("OpenGL ES")); + gles.flags = RFL_NO_CLIP_PLANES; + // KHR_debug is core (unsuffixed) in GLES 3.2 and desktop GL 4.3, + // but the extension may also be present on older drivers. + if ((isGLES && glVersion >= 3.2) || (!isGLES && glVersion >= 4.3) || CheckExtension("GL_KHR_debug")) + { + gles.flags |= RFL_DEBUG; + } + gles.useMappedBuffers = gles_use_mapped_buffer; gles.forceGLSLv100 = gles_force_glsl_v100; gles.maxlights = gles_max_lights_per_surface; @@ -175,9 +184,6 @@ namespace OpenGLESRenderer gles.modelstring = (char*)glGetString(GL_RENDERER); gles.vendorstring = (char*)glGetString(GL_VENDOR); - - gl_customshader = false; // Disable user shaders for GLES renderer - GLint maxTextureSize[1]; glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize); gles.max_texturesize = maxTextureSize[0]; @@ -203,7 +209,7 @@ namespace OpenGLESRenderer { Printf("GLES choosing mode: GLES_MODE_GLES\n"); - gles.shaderVersionString = "100"; + gles.shaderVersionString = "300 es"; gles.depthStencilAvailable = CheckExtension("GL_OES_packed_depth_stencil"); gles.npotAvailable = CheckExtension("GL_OES_texture_npot"); gles.depthClampAvailable = CheckExtension("GL_EXT_depth_clamp"); @@ -231,6 +237,12 @@ namespace OpenGLESRenderer gles.depthClampAvailable = true; gles.anistropicFilterAvailable = true; } + + if (gles.forceGLSLv100) + { + Printf("GLES forcing GLSL version 100\n"); + gles.shaderVersionString = "100"; + } setGlVersion(glVersion); } diff --git a/wadsrc/static/shaders_gles/glsl/burn.fp b/wadsrc/static/shaders_gles/glsl/burn.fp index 040311711d4..e6aed3e1905 100644 --- a/wadsrc/static/shaders_gles/glsl/burn.fp +++ b/wadsrc/static/shaders_gles/glsl/burn.fp @@ -1,4 +1,10 @@ +#if __VERSION__ >= 300 +#define varying in +out vec4 FragColor; +#define gl_FragColor FragColor +#endif + varying vec4 vTexCoord; varying vec4 vColor; diff --git a/wadsrc/static/shaders_gles/glsl/fogboundary.fp b/wadsrc/static/shaders_gles/glsl/fogboundary.fp index 9a6448bee34..571a39a629e 100644 --- a/wadsrc/static/shaders_gles/glsl/fogboundary.fp +++ b/wadsrc/static/shaders_gles/glsl/fogboundary.fp @@ -1,3 +1,10 @@ + +#if __VERSION__ >= 300 +#define varying in +out vec4 FragColor; +#define gl_FragColor FragColor +#endif + varying vec4 pixelpos; //=========================================================================== diff --git a/wadsrc/static/shaders_gles/glsl/main.fp b/wadsrc/static/shaders_gles/glsl/main.fp index 99e64ca76e0..afed829b017 100644 --- a/wadsrc/static/shaders_gles/glsl/main.fp +++ b/wadsrc/static/shaders_gles/glsl/main.fp @@ -1,4 +1,11 @@ +#if __VERSION__ >= 300 +#define varying in +out vec4 FragColor; +#define gl_FragColor FragColor +#define texture2D texture +#endif + varying vec4 vTexCoord; varying vec4 vColor; varying vec4 pixelpos; diff --git a/wadsrc/static/shaders_gles/glsl/main.vp b/wadsrc/static/shaders_gles/glsl/main.vp index efa1ff00871..804c306c507 100644 --- a/wadsrc/static/shaders_gles/glsl/main.vp +++ b/wadsrc/static/shaders_gles/glsl/main.vp @@ -1,3 +1,7 @@ +#if __VERSION__ >= 300 +#define attribute in +#define varying out +#endif attribute vec4 aPosition; attribute vec2 aTexCoord; @@ -216,4 +220,4 @@ BonesResult ApplyBones() return result; } -#endif +#endif \ No newline at end of file diff --git a/wadsrc/static/shaders_gles/pp/present.fp b/wadsrc/static/shaders_gles/pp/present.fp index b3654a2e9e6..531fafd10a3 100644 --- a/wadsrc/static/shaders_gles/pp/present.fp +++ b/wadsrc/static/shaders_gles/pp/present.fp @@ -1,4 +1,11 @@ +#if __VERSION__ >= 300 +#define varying in +out vec4 FragColor; +#define gl_FragColor FragColor +#define texture2D texture +#endif + varying vec2 TexCoord; uniform sampler2D InputTexture; diff --git a/wadsrc/static/shaders_gles/pp/screenquad.vp b/wadsrc/static/shaders_gles/pp/screenquad.vp index 87378f6e959..f94478f3ac9 100644 --- a/wadsrc/static/shaders_gles/pp/screenquad.vp +++ b/wadsrc/static/shaders_gles/pp/screenquad.vp @@ -1,3 +1,7 @@ +#if __VERSION__ >= 300 +#define attribute in +#define varying out +#endif attribute vec4 PositionInProjection; attribute vec2 UV; @@ -8,4 +12,4 @@ void main() { gl_Position = PositionInProjection; TexCoord = UV; -} +} \ No newline at end of file