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." }, + { "value": "msaa4", "label": "MSAA (x4)", "tooltip": "Slow, but high-quality anti-aliasing, uses 4 samples per pixel." }, + { "value": "msaa8", "label": "MSAA (x8)", "tooltip": "Slow, but high-quality anti-aliasing, uses 8 samples per pixel." }, + { "value": "msaa16", "label": "MSAA (x16)", "tooltip": "Slow, but high-quality anti-aliasing, uses 16 samples per pixel." } ], "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 @@ -69,6 +69,8 @@ void ReleaseRenderOutput(); private: + void CreateMultisampleBuffer(); + void DestroyMultisampleBuffer(); // Two framebuffers, that we flip between at each shader pass. GLuint m_PingFbo, m_PongFbo; @@ -93,6 +95,10 @@ CStr m_AAName; CShaderTechniquePtr m_AATech; + bool m_UsingMultisampleBuffer; + GLuint m_MultisampleFBO; + GLuint m_MultisampleColorTex, m_MultisampleDepthTex; + GLsizei m_MultisampleCount; // 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,6 +32,7 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" +#include "tools/atlas/GameInterface/GameLoop.h" #if !CONFIG2_GLES @@ -191,6 +192,12 @@ } */ + if (m_UsingMultisampleBuffer) + { + DestroyMultisampleBuffer(); + CreateMultisampleBuffer(); + } + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } @@ -369,6 +376,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); + } } @@ -474,6 +488,14 @@ if (m_PostProcEffect == L"default") return; + if (m_UsingMultisampleBuffer) + { + 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_READ_FRAMEBUFFER_EXT, 0); + } + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo); pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, 0, 0); @@ -561,13 +583,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") && + g_RenderingOptions.GetPreferGLSL(); + if (!is_msaa_supported) + { + LOGWARNING("MSAA is unsupported."); + return; + } + std::stringstream ss(m_AAName.substr(msaaPrefix.size())); + ss >> m_MultisampleCount; + const GLsizei allowedSampleCounts[] = {2, 4, 8, 16}; + if (std::find(std::begin(allowedSampleCounts), std::end(allowedSampleCounts), m_MultisampleCount) == + std::end(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::SetDepthBufferClipPlanes(float nearPlane, float farPlane) @@ -576,6 +637,60 @@ 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); +} + #else #warning TODO: implement PostprocManager for GLES @@ -645,4 +760,12 @@ { } +void CPostprocManager::CreateMultisampleBuffer() +{ +} + +void CPostprocManager::DestroyMultisampleBuffer() +{ +} + #endif