Index: binaries/data/mods/public/gui/options/options.json =================================================================== --- binaries/data/mods/public/gui/options/options.json +++ binaries/data/mods/public/gui/options/options.json @@ -125,7 +125,11 @@ "config": "antialiasing", "list": [ { "value": "disabled", "label": "Disabled", "tooltip": "Do not use anti-aliasing." }, - { "value": "fxaa", "label": "FXAA", "tooltip": "Fast, but simple anti-aliasing." } + { "value": "fxaa", "label": "FXAA", "tooltip": "Fast, but simple anti-aliasing." }, + { "value": "msaa2", "label": "MSAA (x2)", "tooltip": "Slow, but high-quality anti-aliasing, uses 2 samples per pixel. Supported for GL3.3+." }, + { "value": "msaa4", "label": "MSAA (x4)", "tooltip": "Slow, but high-quality anti-aliasing, uses 4 samples per pixel. Supported for GL3.3+." }, + { "value": "msaa8", "label": "MSAA (x8)", "tooltip": "Slow, but high-quality anti-aliasing, uses 8 samples per pixel. Supported for GL3.3+." }, + { "value": "msaa16", "label": "MSAA (x16)", "tooltip": "Slow, but high-quality anti-aliasing, uses 16 samples per pixel. Supported for GL3.3+." } ], "function": "Renderer_UpdateAntiAliasingTechnique" }, Index: source/lib/external_libraries/glext_funcs.h =================================================================== --- source/lib/external_libraries/glext_funcs.h +++ source/lib/external_libraries/glext_funcs.h @@ -385,6 +385,9 @@ FUNC2(void*, glMapBufferRange, glMapBufferRange, "3.0", (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)) FUNC2(void, glFlushMappedBufferRange, glFlushMappedBufferRange, "3.0", (GLenum target, GLintptr offset, GLsizeiptr length)) +// GL_ARB_texture_multisample / GL3.3: +FUNC2(void, glTexImage2DMultisample, glTexImage2DMultisample, "3.3", (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) + // GL_GREMEDY_string_marker (from gDEBugger) FUNC(int, glStringMarkerGREMEDY, (GLsizei len, const GLvoid *string)) Index: source/renderer/PostprocManager.h =================================================================== --- source/renderer/PostprocManager.h +++ source/renderer/PostprocManager.h @@ -70,7 +70,15 @@ // @note CPostprocManager must be initialized first void ReleaseRenderOutput(); + // Returns true if we render main scene in the MSAA framebuffer. + bool IsMultisampleEnabled() const; + + // Resolves the MSAA framebuffer into the regular one. + void ResolveMultisampleFramebuffer(); + private: + void CreateMultisampleBuffer(); + void DestroyMultisampleBuffer(); // Two framebuffers, that we flip between at each shader pass. GLuint m_PingFbo, m_PongFbo; @@ -99,6 +107,11 @@ CStr m_AAName; CShaderTechniquePtr m_AATech; + bool m_UsingMultisampleBuffer; + GLuint m_MultisampleFBO; + GLuint m_MultisampleColorTex, m_MultisampleDepthTex; + GLsizei m_MultisampleCount; + std::vector m_AllowedSampleCounts; // The current screen dimensions in pixels. int m_Width, m_Height; Index: source/renderer/PostprocManager.cpp =================================================================== --- source/renderer/PostprocManager.cpp +++ source/renderer/PostprocManager.cpp @@ -32,13 +32,22 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" +#include "tools/atlas/GameInterface/GameLoop.h" #if !CONFIG2_GLES CPostprocManager::CPostprocManager() : m_IsInitialized(false), m_PingFbo(0), m_PongFbo(0), m_PostProcEffect(L"default"), m_ColorTex1(0), m_ColorTex2(0), m_DepthTex(0), m_BloomFbo(0), m_BlurTex2a(0), m_BlurTex2b(0), m_BlurTex4a(0), m_BlurTex4b(0), +<<<<<<< .mine + m_BlurTex8a(0), m_BlurTex8b(0), m_WhichBuffer(true), m_UsingMultisampleBuffer(false), + m_MultisampleFBO(0), m_MultisampleColorTex(0), m_MultisampleDepthTex(0), + m_MultisampleCount(0) +||||||| .r23484 + m_BlurTex8a(0), m_BlurTex8b(0), m_WhichBuffer(true) +======= m_BlurTex8a(0), m_BlurTex8b(0), m_WhichBuffer(true), m_Sharpness(0.3f) +>>>>>>> .r23989 { } @@ -76,6 +85,14 @@ if (m_IsInitialized) return; + GLint maxSamples = 0; + glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + const GLsizei possibleSampleCounts[] = {2, 4, 8, 16}; + std::copy_if( + std::begin(possibleSampleCounts), std::end(possibleSampleCounts), + std::back_inserter(m_AllowedSampleCounts), + [maxSamples](const GLsizei sampleCount) { return sampleCount <= maxSamples; } ); + // The screen size starts out correct and then must be updated with Resize() m_Width = g_Renderer.GetWidth(); m_Height = g_Renderer.GetHeight(); @@ -193,6 +210,12 @@ } */ + if (m_UsingMultisampleBuffer) + { + DestroyMultisampleBuffer(); + CreateMultisampleBuffer(); + } + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } @@ -371,6 +394,13 @@ pglDrawBuffers(1, buffers); m_WhichBuffer = true; + + if (m_UsingMultisampleBuffer) + { + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_MultisampleFBO); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + pglDrawBuffers(1, buffers); + } } @@ -576,13 +606,52 @@ m_AAName = newAAName; m_AATech.reset(); + if (m_UsingMultisampleBuffer) + { + m_UsingMultisampleBuffer = false; + DestroyMultisampleBuffer(); + } + // We have to hardcode names in the engine, because anti-aliasing // techinques strongly depend on the graphics pipeline. // We might use enums in future though. + const CStr msaaPrefix = "msaa"; if (m_AAName == "fxaa") { m_AATech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("fxaa")); } + else if (m_AAName.size() > msaaPrefix.size() && m_AAName.substr(0, msaaPrefix.size()) == msaaPrefix) + { +#if !CONFIG2_GLES + // We don't want to enable MSAA in Atlas, because it uses wxWidgets and its canvas. + if (g_AtlasGameLoop && g_AtlasGameLoop->running) + return; + const bool is_msaa_supported = + ogl_HaveVersion("3.3") && + ogl_HaveExtension("GL_ARB_multisample") && + ogl_HaveExtension("GL_ARB_texture_multisample") && + !m_AllowedSampleCounts.empty() && + g_RenderingOptions.GetPreferGLSL(); + if (!is_msaa_supported) + { + LOGWARNING("MSAA is unsupported."); + return; + } + std::stringstream ss(m_AAName.substr(msaaPrefix.size())); + ss >> m_MultisampleCount; + if (std::find(std::begin(m_AllowedSampleCounts), std::end(m_AllowedSampleCounts), m_MultisampleCount) == + std::end(m_AllowedSampleCounts)) + { + m_MultisampleCount = 4; + LOGWARNING("Wrong MSAA sample count: %s.", m_AAName.EscapeToPrintableASCII().c_str()); + } + m_UsingMultisampleBuffer = true; + CreateMultisampleBuffer(); +#else + #warning TODO: implement and test MSAA for GLES + LOGWARNING("MSAA is unsupported."); +#endif + } } void CPostprocManager::UpdateSharpeningTechnique() @@ -614,6 +683,77 @@ m_FarPlane = farPlane; } +void CPostprocManager::CreateMultisampleBuffer() +{ + glEnable(GL_MULTISAMPLE); + + glGenTextures(1, &m_MultisampleColorTex); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_MultisampleColorTex); + pglTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_MultisampleCount, GL_RGBA, m_Width, m_Height, GL_TRUE); + + // Allocate the Depth/Stencil texture. + glGenTextures(1, &m_MultisampleDepthTex); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_MultisampleDepthTex); + pglTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_MultisampleCount, GL_DEPTH24_STENCIL8_EXT, m_Width, m_Height, GL_TRUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); + + ogl_WarnIfError(); + + // Set up the framebuffers with some initial textures. + pglGenFramebuffersEXT(1, &m_MultisampleFBO); + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_MultisampleFBO); + + pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D_MULTISAMPLE, m_MultisampleColorTex, 0); + + pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D_MULTISAMPLE, m_MultisampleDepthTex, 0); + + GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + LOGWARNING("Multisample framebuffer object incomplete (A): 0x%04X", status); + m_UsingMultisampleBuffer = false; + DestroyMultisampleBuffer(); + } + + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void CPostprocManager::DestroyMultisampleBuffer() +{ + if (m_UsingMultisampleBuffer) + return; + if (m_MultisampleFBO) + pglDeleteFramebuffersEXT(1, &m_MultisampleFBO); + if (m_MultisampleColorTex) + glDeleteTextures(1, &m_MultisampleColorTex); + if (m_MultisampleDepthTex) + glDeleteTextures(1, &m_MultisampleDepthTex); + glDisable(GL_MULTISAMPLE); +} + +bool CPostprocManager::IsMultisampleEnabled() const +{ + return m_UsingMultisampleBuffer; +} + +void CPostprocManager::ResolveMultisampleFramebuffer() +{ + if (!m_UsingMultisampleBuffer) + return; + + pglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_PingFbo); + pglBlitFramebufferEXT(0, 0, m_Width, m_Height, 0, 0, m_Width, m_Height, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); + + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo); +} + #else #warning TODO: implement PostprocManager for GLES @@ -691,4 +831,21 @@ { } +void CPostprocManager::CreateMultisampleBuffer() +{ +} + +void CPostprocManager::DestroyMultisampleBuffer() +{ +} + +bool CPostprocManager::IsMultisampleEnabled() const +{ + return false; +} + +void CPostprocManager::ResolveMultisampleFramebuffer() +{ +} + #endif Index: source/renderer/Renderer.cpp =================================================================== --- source/renderer/Renderer.cpp +++ source/renderer/Renderer.cpp @@ -1364,6 +1364,12 @@ RenderModels(context, cullGroup); ogl_WarnIfError(); + // We need to resolve the MSAA framebuffer before the transparent pass to + // make it cheaper. Because most of transparent models have transparent + // or semi-transparent edges. + if (g_RenderingOptions.GetPostProc() && g_Renderer.GetPostprocManager().IsMultisampleEnabled()) + g_Renderer.GetPostprocManager().ResolveMultisampleFramebuffer(); + // render water if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0) {