1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Implement support for FSAA in combination with post-processing (#15392)

- Actually it's MSAA I think, or perhaps the terms are equivalent
- I've made it fit into the existing Irrlicht architecture, but that has resulted in code duplication compared to my original "hacky" approach
- OpenGL 3.2+ and OpenGL ES 3.1+ are supported
- EDT_OPENGL3 is not required, EDT_OPENGL works too
- Helpful tutorial: https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing, section "Off-screen MSAA"
- This may be rough around the edges, but in general it works
This commit is contained in:
grorp 2024-11-18 14:06:48 +01:00 committed by GitHub
parent a8ea165042
commit 9b6a399011
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 290 additions and 42 deletions

View file

@ -410,10 +410,12 @@ anisotropic_filter (Anisotropic filtering) bool false
# * None - No antialiasing (default) # * None - No antialiasing (default)
# #
# * FSAA - Hardware-provided full-screen antialiasing # * FSAA - Hardware-provided full-screen antialiasing
# (incompatible with Post Processing and Undersampling)
# A.K.A multi-sample antialiasing (MSAA) # A.K.A multi-sample antialiasing (MSAA)
# Smoothens out block edges but does not affect the insides of textures. # Smoothens out block edges but does not affect the insides of textures.
# A restart is required to change this option. #
# If Post Processing is disabled, changing FSAA requires a restart.
# Also, if Post Processing is disabled, FSAA will not work together with
# undersampling or a non-default "3d_mode" setting.
# #
# * FXAA - Fast approximate antialiasing # * FXAA - Fast approximate antialiasing
# Applies a post-processing filter to detect and smoothen high-contrast edges. # Applies a post-processing filter to detect and smoothen high-contrast edges.

View file

@ -129,6 +129,9 @@ enum E_VIDEO_DRIVER_FEATURE
//! Support for clamping vertices beyond far-plane to depth instead of capping them. //! Support for clamping vertices beyond far-plane to depth instead of capping them.
EVDF_DEPTH_CLAMP, EVDF_DEPTH_CLAMP,
//! Support for multisample textures.
EVDF_TEXTURE_MULTISAMPLE,
//! Only used for counting the elements of this enum //! Only used for counting the elements of this enum
EVDF_COUNT EVDF_COUNT
}; };

View file

@ -26,6 +26,7 @@ enum E_CUBE_SURFACE
}; };
//! Interface of a Render Target. //! Interface of a Render Target.
/** This is a framebuffer object (FBO) in OpenGL. */
class IRenderTarget : public virtual IReferenceCounted class IRenderTarget : public virtual IReferenceCounted
{ {
public: public:

View file

@ -156,6 +156,9 @@ enum E_TEXTURE_TYPE
//! 2D texture. //! 2D texture.
ETT_2D, ETT_2D,
//! 2D texture with multisampling.
ETT_2D_MS,
//! Cubemap texture. //! Cubemap texture.
ETT_CUBEMAP ETT_CUBEMAP
}; };

View file

@ -273,6 +273,14 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size, virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) = 0; const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) = 0;
//! Adds a multisampled render target texture to the texture cache.
/** \param msaa The number of samples to use, values that make sense are > 1.
Only works if the driver supports the EVDF_TEXTURE_MULTISAMPLE feature,
check via queryFeature.
\see addRenderTargetTexture */
virtual ITexture *addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) = 0;
//! Adds a new render target texture with 6 sides for a cubemap map to the texture cache. //! Adds a new render target texture with 6 sides for a cubemap map to the texture cache.
/** \param sideLen Length of one cubemap side. /** \param sideLen Length of one cubemap side.
\param name A name for the texture. Later calls of getTexture() with this name will return this texture. \param name A name for the texture. Later calls of getTexture() with this name will return this texture.
@ -358,6 +366,10 @@ public:
//! Remove all render targets. //! Remove all render targets.
virtual void removeAllRenderTargets() = 0; virtual void removeAllRenderTargets() = 0;
//! Blit contents of one render target to another one.
/** This is glBlitFramebuffer in OpenGL. */
virtual void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) = 0;
//! Sets a boolean alpha channel on the texture based on a color key. //! Sets a boolean alpha channel on the texture based on a color key.
/** This makes the texture fully transparent at the texels where /** This makes the texture fully transparent at the texels where
this color key can be found when using for example draw2DImage this color key can be found when using for example draw2DImage

View file

@ -1678,6 +1678,12 @@ ITexture *CNullDriver::addRenderTargetTexture(const core::dimension2d<u32> &size
return 0; return 0;
} }
ITexture *CNullDriver::addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name, const ECOLOR_FORMAT format)
{
return 0;
}
ITexture *CNullDriver::addRenderTargetTextureCubemap(const irr::u32 sideLen, ITexture *CNullDriver::addRenderTargetTextureCubemap(const irr::u32 sideLen,
const io::path &name, const ECOLOR_FORMAT format) const io::path &name, const ECOLOR_FORMAT format)
{ {

View file

@ -227,6 +227,10 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size, virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override;
//! Creates a multisampled render target texture.
virtual ITexture *addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override;
//! Creates a render target texture for a cubemap //! Creates a render target texture for a cubemap
ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen, ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen,
const io::path &name, const ECOLOR_FORMAT format) override; const io::path &name, const ECOLOR_FORMAT format) override;
@ -410,6 +414,8 @@ public:
//! Create render target. //! Create render target.
IRenderTarget *addRenderTarget() override; IRenderTarget *addRenderTarget() override;
void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override {}
//! Remove render target. //! Remove render target.
void removeRenderTarget(IRenderTarget *renderTarget) override; void removeRenderTarget(IRenderTarget *renderTarget) override;

View file

@ -85,13 +85,19 @@ class COpenGLCoreCacheHandler
GL.BindTexture(prevTextureType, 0); GL.BindTexture(prevTextureType, 0);
#if defined(IRR_COMPILE_GL_COMMON) #if defined(IRR_COMPILE_GL_COMMON)
GL.Disable(prevTextureType); // The "enable/disable texture" stuff is so legacy that
GL.Enable(curTextureType); // it's not even allowed for multisample textures.
// (IRR_COMPILE_GL_COMMON is for the legacy driver.)
if (prevTextureType != GL_TEXTURE_2D_MULTISAMPLE)
GL.Disable(prevTextureType);
if (curTextureType != GL_TEXTURE_2D_MULTISAMPLE)
GL.Enable(curTextureType);
#endif #endif
} }
#if defined(IRR_COMPILE_GL_COMMON) #if defined(IRR_COMPILE_GL_COMMON)
else if (!prevTexture) else if (!prevTexture)
GL.Enable(curTextureType); if (curTextureType != GL_TEXTURE_2D_MULTISAMPLE)
GL.Enable(curTextureType);
#endif #endif
GL.BindTexture(curTextureType, static_cast<const TOpenGLTexture *>(texture)->getOpenGLTextureName()); GL.BindTexture(curTextureType, static_cast<const TOpenGLTexture *>(texture)->getOpenGLTextureName());
@ -110,7 +116,8 @@ class COpenGLCoreCacheHandler
GL.BindTexture(prevTextureType, 0); GL.BindTexture(prevTextureType, 0);
#if defined(IRR_COMPILE_GL_COMMON) #if defined(IRR_COMPILE_GL_COMMON)
GL.Disable(prevTextureType); if (prevTextureType != GL_TEXTURE_2D_MULTISAMPLE)
GL.Disable(prevTextureType);
#endif #endif
} }

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "IRenderTarget.h" #include "IRenderTarget.h"
#include <stdexcept>
#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS #ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT
@ -122,7 +123,7 @@ public:
TOpenGLTexture *currentTexture = (depthStencil && depthStencil->getDriverType() == DriverType) ? static_cast<TOpenGLTexture *>(depthStencil) : 0; TOpenGLTexture *currentTexture = (depthStencil && depthStencil->getDriverType() == DriverType) ? static_cast<TOpenGLTexture *>(depthStencil) : 0;
if (currentTexture) { if (currentTexture) {
if (currentTexture->getType() == ETT_2D) { if (currentTexture->getType() == ETT_2D || currentTexture->getType() == ETT_2D_MS) {
GLuint textureID = currentTexture->getOpenGLTextureName(); GLuint textureID = currentTexture->getOpenGLTextureName();
const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN; const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN;
@ -172,7 +173,20 @@ public:
if (textureID != 0) { if (textureID != 0) {
AssignedTextures[i] = GL_COLOR_ATTACHMENT0 + i; AssignedTextures[i] = GL_COLOR_ATTACHMENT0 + i;
GLenum textarget = currentTexture->getType() == ETT_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i]; GLenum textarget;
switch (currentTexture->getType()) {
case ETT_2D:
textarget = GL_TEXTURE_2D;
break;
case ETT_2D_MS:
textarget = GL_TEXTURE_2D_MULTISAMPLE;
break;
case ETT_CUBEMAP:
textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i];
break;
default:
throw std::logic_error("not reachable");
}
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], textarget, textureID, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], textarget, textureID, 0);
TEST_GL_ERROR(Driver); TEST_GL_ERROR(Driver);
} else if (AssignedTextures[i] != GL_NONE) { } else if (AssignedTextures[i] != GL_NONE) {
@ -198,36 +212,50 @@ public:
// Set depth and stencil attachments. // Set depth and stencil attachments.
if (RequestDepthStencilUpdate) { if (RequestDepthStencilUpdate) {
const ECOLOR_FORMAT textureFormat = (DepthStencil) ? DepthStencil->getColorFormat() : ECF_UNKNOWN; const ECOLOR_FORMAT textureFormat = DepthStencil ? DepthStencil->getColorFormat() : ECF_UNKNOWN;
if (IImage::isDepthFormat(textureFormat)) { if (IImage::isDepthFormat(textureFormat)) {
GLenum textarget;
switch (DepthStencil->getType()) {
case ETT_2D:
textarget = GL_TEXTURE_2D;
break;
case ETT_2D_MS:
textarget = GL_TEXTURE_2D_MULTISAMPLE;
break;
default:
// ETT_CUBEMAP is rejected for depth/stencil by setTextures
throw std::logic_error("not reachable");
}
GLuint textureID = static_cast<TOpenGLTexture *>(DepthStencil)->getOpenGLTextureName(); GLuint textureID = static_cast<TOpenGLTexture *>(DepthStencil)->getOpenGLTextureName();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_ // The WEBGL_depth_texture extension does not allow attaching stencil+depth separate. #ifdef _IRR_EMSCRIPTEN_PLATFORM_ // The WEBGL_depth_texture extension does not allow attaching stencil+depth separate.
if (textureFormat == ECF_D24S8) { if (textureFormat == ECF_D24S8) {
GLenum attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT GLenum attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, textureID, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textarget, textureID, 0);
AssignedStencil = true; AssignedStencil = true;
} else { } else {
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textarget, textureID, 0);
AssignedStencil = false; AssignedStencil = false;
} }
#else #else
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textarget, textureID, 0);
if (textureFormat == ECF_D24S8) { if (textureFormat == ECF_D24S8) {
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureID, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, textarget, textureID, 0);
AssignedStencil = true; AssignedStencil = true;
} else { } else {
if (AssignedStencil) if (AssignedStencil)
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, textarget, 0, 0);
AssignedStencil = false; AssignedStencil = false;
} }
#endif #endif
AssignedDepth = true; AssignedDepth = true;
} else { } else {
// No (valid) depth/stencil texture.
if (AssignedDepth) if (AssignedDepth)
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);

View file

@ -45,12 +45,13 @@ public:
COpenGLCoreTexture(const io::path &name, const std::vector<IImage *> &srcImages, E_TEXTURE_TYPE type, TOpenGLDriver *driver) : COpenGLCoreTexture(const io::path &name, const std::vector<IImage *> &srcImages, E_TEXTURE_TYPE type, TOpenGLDriver *driver) :
ITexture(name, type), Driver(driver), TextureType(GL_TEXTURE_2D), ITexture(name, type), Driver(driver), TextureType(GL_TEXTURE_2D),
TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), MSAA(0), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0),
KeepImage(false), MipLevelStored(0), LegacyAutoGenerateMipMaps(false) KeepImage(false), MipLevelStored(0), LegacyAutoGenerateMipMaps(false)
{ {
_IRR_DEBUG_BREAK_IF(srcImages.empty()) _IRR_DEBUG_BREAK_IF(srcImages.empty())
DriverType = Driver->getDriverType(); DriverType = Driver->getDriverType();
_IRR_DEBUG_BREAK_IF(Type == ETT_2D_MS); // not supported by this constructor
TextureType = TextureTypeIrrToGL(Type); TextureType = TextureTypeIrrToGL(Type);
HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
KeepImage = Driver->getTextureCreationFlag(ETCF_ALLOW_MEMORY_COPY); KeepImage = Driver->getTextureCreationFlag(ETCF_ALLOW_MEMORY_COPY);
@ -141,10 +142,10 @@ public:
TEST_GL_ERROR(Driver); TEST_GL_ERROR(Driver);
} }
COpenGLCoreTexture(const io::path &name, const core::dimension2d<u32> &size, E_TEXTURE_TYPE type, ECOLOR_FORMAT format, TOpenGLDriver *driver) : COpenGLCoreTexture(const io::path &name, const core::dimension2d<u32> &size, E_TEXTURE_TYPE type, ECOLOR_FORMAT format, TOpenGLDriver *driver, u8 msaa = 0) :
ITexture(name, type), ITexture(name, type),
Driver(driver), TextureType(GL_TEXTURE_2D), Driver(driver), TextureType(GL_TEXTURE_2D),
TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), KeepImage(false), TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), MSAA(msaa), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), KeepImage(false),
MipLevelStored(0), LegacyAutoGenerateMipMaps(false) MipLevelStored(0), LegacyAutoGenerateMipMaps(false)
{ {
DriverType = Driver->getDriverType(); DriverType = Driver->getDriverType();
@ -184,23 +185,47 @@ public:
const COpenGLCoreTexture *prevTexture = Driver->getCacheHandler()->getTextureCache().get(0); const COpenGLCoreTexture *prevTexture = Driver->getCacheHandler()->getTextureCache().get(0);
Driver->getCacheHandler()->getTextureCache().set(0, this); Driver->getCacheHandler()->getTextureCache().set(0, this);
GL.TexParameteri(TextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // An INVALID_ENUM error is generated by TexParameter* if target is either
GL.TexParameteri(TextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // TEXTURE_2D_MULTISAMPLE or TEXTURE_2D_MULTISAMPLE_ARRAY, and pname is any
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // sampler state from table 23.18.
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // ~ https://registry.khronos.org/OpenGL/specs/gl/glspec46.core.pdf
if (Type != ETT_2D_MS) {
GL.TexParameteri(TextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GL.TexParameteri(TextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#if defined(GL_VERSION_1_2) #if defined(GL_VERSION_1_2)
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
#endif #endif
StatesCache.WrapU = ETC_CLAMP_TO_EDGE; StatesCache.WrapU = ETC_CLAMP_TO_EDGE;
StatesCache.WrapV = ETC_CLAMP_TO_EDGE; StatesCache.WrapV = ETC_CLAMP_TO_EDGE;
StatesCache.WrapW = ETC_CLAMP_TO_EDGE; StatesCache.WrapW = ETC_CLAMP_TO_EDGE;
}
switch (Type) { switch (Type) {
case ETT_2D: case ETT_2D:
GL.TexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0); GL.TexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0);
break; break;
case ETT_2D_MS: {
// glTexImage2DMultisample is supported by OpenGL 3.2+
// glTexStorage2DMultisample is supported by OpenGL 4.3+ and OpenGL ES 3.1+
#ifdef IRR_COMPILE_GL_COMMON // legacy driver
constexpr bool use_gl_impl = true;
#else
const bool use_gl_impl = Driver->Version.Spec != OpenGLSpec::ES;
#endif
GLint max_samples = 0;
GL.GetIntegerv(GL_MAX_SAMPLES, &max_samples);
MSAA = std::min(MSAA, (u8)max_samples);
if (use_gl_impl)
GL.TexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA, InternalFormat, Size.Width, Size.Height, GL_TRUE);
else
GL.TexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA, InternalFormat, Size.Width, Size.Height, GL_TRUE);
break;
}
case ETT_CUBEMAP: case ETT_CUBEMAP:
GL.TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0); GL.TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0);
GL.TexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0); GL.TexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0);
@ -595,6 +620,8 @@ protected:
switch (type) { switch (type) {
case ETT_2D: case ETT_2D:
return GL_TEXTURE_2D; return GL_TEXTURE_2D;
case ETT_2D_MS:
return GL_TEXTURE_2D_MULTISAMPLE;
case ETT_CUBEMAP: case ETT_CUBEMAP:
return GL_TEXTURE_CUBE_MAP; return GL_TEXTURE_CUBE_MAP;
} }
@ -610,6 +637,7 @@ protected:
GLint InternalFormat; GLint InternalFormat;
GLenum PixelFormat; GLenum PixelFormat;
GLenum PixelType; GLenum PixelType;
u8 MSAA;
void (*Converter)(const void *, s32, void *); void (*Converter)(const void *, s32, void *);
bool LockReadOnly; bool LockReadOnly;

View file

@ -651,6 +651,29 @@ IRenderTarget *COpenGLDriver::addRenderTarget()
return renderTarget; return renderTarget;
} }
void COpenGLDriver::blitRenderTarget(IRenderTarget *from, IRenderTarget *to)
{
if (Version < 300) {
os::Printer::log("glBlitFramebuffer not supported by OpenGL < 3.0", ELL_ERROR);
return;
}
GLuint prev_fbo_id;
CacheHandler->getFBO(prev_fbo_id);
COpenGLRenderTarget *src = static_cast<COpenGLRenderTarget *>(from);
COpenGLRenderTarget *dst = static_cast<COpenGLRenderTarget *>(to);
GL.BindFramebuffer(GL.READ_FRAMEBUFFER, src->getBufferID());
GL.BindFramebuffer(GL.DRAW_FRAMEBUFFER, dst->getBufferID());
GL.BlitFramebuffer(
0, 0, src->getSize().Width, src->getSize().Height,
0, 0, dst->getSize().Width, dst->getSize().Height,
GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT | GL.STENCIL_BUFFER_BIT, GL.NEAREST);
// This resets both read and draw framebuffer. Note that we bypass CacheHandler here.
GL.BindFramebuffer(GL.FRAMEBUFFER, prev_fbo_id);
}
// small helper function to create vertex buffer object address offsets // small helper function to create vertex buffer object address offsets
static inline const GLvoid *buffer_offset(const size_t offset) static inline const GLvoid *buffer_offset(const size_t offset)
{ {
@ -2091,7 +2114,9 @@ void COpenGLDriver::setBasicRenderStates(const SMaterial &material, const SMater
else if (lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE) else if (lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE)
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
if ((AntiAlias >= 2) && (material.AntiAliasing & (EAAM_SIMPLE | EAAM_QUALITY))) { // Enable MSAA even if it's not enabled in the OpenGL context, we might
// be rendering to an FBO with multisampling.
if (material.AntiAliasing & (EAAM_SIMPLE | EAAM_QUALITY)) {
glEnable(GL_MULTISAMPLE_ARB); glEnable(GL_MULTISAMPLE_ARB);
#ifdef GL_NV_multisample_filter_hint #ifdef GL_NV_multisample_filter_hint
if (FeatureAvailable[IRR_NV_multisample_filter_hint]) { if (FeatureAvailable[IRR_NV_multisample_filter_hint]) {
@ -2694,6 +2719,12 @@ IVideoDriver *COpenGLDriver::getVideoDriver()
ITexture *COpenGLDriver::addRenderTargetTexture(const core::dimension2d<u32> &size, ITexture *COpenGLDriver::addRenderTargetTexture(const core::dimension2d<u32> &size,
const io::path &name, const ECOLOR_FORMAT format) const io::path &name, const ECOLOR_FORMAT format)
{
return addRenderTargetTextureMs(size, 0, name, format);
}
ITexture *COpenGLDriver::addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name, const ECOLOR_FORMAT format)
{ {
if (IImage::isCompressedFormat(format)) if (IImage::isCompressedFormat(format))
return 0; return 0;
@ -2711,7 +2742,7 @@ ITexture *COpenGLDriver::addRenderTargetTexture(const core::dimension2d<u32> &si
destSize = destSize.getOptimalSize((size == size.getOptimalSize()), false, false); destSize = destSize.getOptimalSize((size == size.getOptimalSize()), false, false);
} }
COpenGLTexture *renderTargetTexture = new COpenGLTexture(name, destSize, ETT_2D, format, this); COpenGLTexture *renderTargetTexture = new COpenGLTexture(name, destSize, msaa > 0 ? ETT_2D_MS : ETT_2D, format, this, msaa);
addTexture(renderTargetTexture); addTexture(renderTargetTexture);
renderTargetTexture->drop(); renderTargetTexture->drop();

View file

@ -108,6 +108,8 @@ public:
//! Create render target. //! Create render target.
IRenderTarget *addRenderTarget() override; IRenderTarget *addRenderTarget() override;
void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override;
//! draws a vertex primitive list //! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount, virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount, const void *indexList, u32 primitiveCount,
@ -270,6 +272,9 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size, virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override;
virtual ITexture *addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) override;
//! Creates a render target texture for a cubemap //! Creates a render target texture for a cubemap
ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen, ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen,
const io::path &name, const ECOLOR_FORMAT format) override; const io::path &name, const ECOLOR_FORMAT format) override;

View file

@ -612,6 +612,8 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
return FeatureAvailable[IRR_ARB_seamless_cube_map]; return FeatureAvailable[IRR_ARB_seamless_cube_map];
case EVDF_DEPTH_CLAMP: case EVDF_DEPTH_CLAMP:
return FeatureAvailable[IRR_NV_depth_clamp] || FeatureAvailable[IRR_ARB_depth_clamp]; return FeatureAvailable[IRR_NV_depth_clamp] || FeatureAvailable[IRR_ARB_depth_clamp];
case EVDF_TEXTURE_MULTISAMPLE:
return (Version >= 302) || FeatureAvailable[IRR_ARB_texture_multisample];
default: default:
return false; return false;

View file

@ -675,6 +675,29 @@ IRenderTarget *COpenGL3DriverBase::addRenderTarget()
return renderTarget; return renderTarget;
} }
void COpenGL3DriverBase::blitRenderTarget(IRenderTarget *from, IRenderTarget *to)
{
if (Version.Spec == OpenGLSpec::ES && Version.Major < 3) {
os::Printer::log("glBlitFramebuffer not supported by OpenGL ES < 3.0", ELL_ERROR);
return;
}
GLuint prev_fbo_id;
CacheHandler->getFBO(prev_fbo_id);
COpenGL3RenderTarget *src = static_cast<COpenGL3RenderTarget *>(from);
COpenGL3RenderTarget *dst = static_cast<COpenGL3RenderTarget *>(to);
GL.BindFramebuffer(GL.READ_FRAMEBUFFER, src->getBufferID());
GL.BindFramebuffer(GL.DRAW_FRAMEBUFFER, dst->getBufferID());
GL.BlitFramebuffer(
0, 0, src->getSize().Width, src->getSize().Height,
0, 0, dst->getSize().Width, dst->getSize().Height,
GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT | GL.STENCIL_BUFFER_BIT, GL.NEAREST);
// This resets both read and draw framebuffer. Note that we bypass CacheHandler here.
GL.BindFramebuffer(GL.FRAMEBUFFER, prev_fbo_id);
}
//! draws a vertex primitive list //! draws a vertex primitive list
void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 vertexCount, void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount, const void *indexList, u32 primitiveCount,
@ -1317,6 +1340,8 @@ void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const S
GL.LineWidth(core::clamp(static_cast<GLfloat>(material.Thickness), DimAliasedLine[0], DimAliasedLine[1])); GL.LineWidth(core::clamp(static_cast<GLfloat>(material.Thickness), DimAliasedLine[0], DimAliasedLine[1]));
// Anti aliasing // Anti aliasing
// Deal with MSAA even if it's not enabled in the OpenGL context, we might be
// rendering to an FBO with multisampling.
if (resetAllRenderStates || lastmaterial.AntiAliasing != material.AntiAliasing) { if (resetAllRenderStates || lastmaterial.AntiAliasing != material.AntiAliasing) {
if (material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE) if (material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE)
GL.Enable(GL_SAMPLE_ALPHA_TO_COVERAGE); GL.Enable(GL_SAMPLE_ALPHA_TO_COVERAGE);
@ -1631,12 +1656,18 @@ IGPUProgrammingServices *COpenGL3DriverBase::getGPUProgrammingServices()
ITexture *COpenGL3DriverBase::addRenderTargetTexture(const core::dimension2d<u32> &size, ITexture *COpenGL3DriverBase::addRenderTargetTexture(const core::dimension2d<u32> &size,
const io::path &name, const ECOLOR_FORMAT format) const io::path &name, const ECOLOR_FORMAT format)
{
return addRenderTargetTextureMs(size, 0, name, format);
}
ITexture *COpenGL3DriverBase::addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name, const ECOLOR_FORMAT format)
{ {
// disable mip-mapping // disable mip-mapping
bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false); setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
COpenGL3Texture *renderTargetTexture = new COpenGL3Texture(name, size, ETT_2D, format, this); COpenGL3Texture *renderTargetTexture = new COpenGL3Texture(name, size, msaa > 0 ? ETT_2D_MS : ETT_2D, format, this, msaa);
addTexture(renderTargetTexture); addTexture(renderTargetTexture);
renderTargetTexture->drop(); renderTargetTexture->drop();

View file

@ -74,6 +74,8 @@ public:
IRenderTarget *addRenderTarget() override; IRenderTarget *addRenderTarget() override;
void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override;
//! draws a vertex primitive list //! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount, virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount, const void *indexList, u32 primitiveCount,
@ -209,6 +211,9 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size, virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override;
virtual ITexture *addRenderTargetTextureMs(const core::dimension2d<u32> &size, u8 msaa,
const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override;
//! Creates a render target texture for a cubemap //! Creates a render target texture for a cubemap
ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen, ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen,
const io::path &name, const ECOLOR_FORMAT format) override; const io::path &name, const ECOLOR_FORMAT format) override;

View file

@ -74,6 +74,8 @@ public:
return false; return false;
case EVDF_STENCIL_BUFFER: case EVDF_STENCIL_BUFFER:
return StencilBuffer; return StencilBuffer;
case EVDF_TEXTURE_MULTISAMPLE:
return TextureMultisampleSupported;
default: default:
return false; return false;
}; };
@ -161,6 +163,7 @@ public:
bool AnisotropicFilterSupported = false; bool AnisotropicFilterSupported = false;
bool BlendMinMaxSupported = false; bool BlendMinMaxSupported = false;
bool TextureMultisampleSupported = false;
}; };
} }

View file

@ -69,6 +69,7 @@ void COpenGL3Driver::initFeatures()
AnisotropicFilterSupported = isVersionAtLeast(4, 6) || queryExtension("GL_ARB_texture_filter_anisotropic") || queryExtension("GL_EXT_texture_filter_anisotropic"); AnisotropicFilterSupported = isVersionAtLeast(4, 6) || queryExtension("GL_ARB_texture_filter_anisotropic") || queryExtension("GL_EXT_texture_filter_anisotropic");
BlendMinMaxSupported = true; BlendMinMaxSupported = true;
TextureMultisampleSupported = true;
// COGLESCoreExtensionHandler::Feature // COGLESCoreExtensionHandler::Feature
static_assert(MATERIAL_MAX_TEXTURES <= 16, "Only up to 16 textures are guaranteed"); static_assert(MATERIAL_MAX_TEXTURES <= 16, "Only up to 16 textures are guaranteed");

View file

@ -124,6 +124,7 @@ void COpenGLES2Driver::initFeatures()
const bool MRTSupported = Version.Major >= 3 || queryExtension("GL_EXT_draw_buffers"); const bool MRTSupported = Version.Major >= 3 || queryExtension("GL_EXT_draw_buffers");
AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic"); AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic");
BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax]; BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax];
TextureMultisampleSupported = isVersionAtLeast(3, 1);
const bool TextureLODBiasSupported = queryExtension("GL_EXT_texture_lod_bias"); const bool TextureLODBiasSupported = queryExtension("GL_EXT_texture_lod_bias");
// COGLESCoreExtensionHandler::Feature // COGLESCoreExtensionHandler::Feature

View file

@ -26,7 +26,7 @@ video::ITexture *TextureBuffer::getTexture(u8 index)
} }
void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format, bool clear) void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format, bool clear, u8 msaa)
{ {
assert(index != NO_DEPTH_TEXTURE); assert(index != NO_DEPTH_TEXTURE);
@ -41,9 +41,10 @@ void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::str
definition.name = name; definition.name = name;
definition.format = format; definition.format = format;
definition.clear = clear; definition.clear = clear;
definition.msaa = msaa;
} }
void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format, bool clear) void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format, bool clear, u8 msaa)
{ {
assert(index != NO_DEPTH_TEXTURE); assert(index != NO_DEPTH_TEXTURE);
@ -58,6 +59,7 @@ void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &na
definition.name = name; definition.name = name;
definition.format = format; definition.format = format;
definition.clear = clear; definition.clear = clear;
definition.msaa = msaa;
} }
void TextureBuffer::reset(PipelineContext &context) void TextureBuffer::reset(PipelineContext &context)
@ -125,13 +127,19 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini
if (definition.valid) { if (definition.valid) {
if (definition.clear) { if (definition.clear) {
// We're not able to clear a render target texture
// We're not able to create a normal texture with MSAA
// (could be solved by more refactoring in Irrlicht, but not needed for now)
sanity_check(definition.msaa < 1);
video::IImage *image = m_driver->createImage(definition.format, size); video::IImage *image = m_driver->createImage(definition.format, size);
// Cannot use image->fill because it's not implemented for all formats. // Cannot use image->fill because it's not implemented for all formats.
std::memset(image->getData(), 0, image->getDataSizeFromFormat(definition.format, size.Width, size.Height)); std::memset(image->getData(), 0, image->getDataSizeFromFormat(definition.format, size.Width, size.Height));
*texture = m_driver->addTexture(definition.name.c_str(), image); *texture = m_driver->addTexture(definition.name.c_str(), image);
image->drop(); image->drop();
} } else if (definition.msaa > 0) {
else { *texture = m_driver->addRenderTargetTextureMs(size, definition.msaa, definition.name.c_str(), definition.format);
} else {
*texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format); *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format);
} }
} }
@ -189,6 +197,12 @@ void TextureBufferOutput::activate(PipelineContext &context)
RenderTarget::activate(context); RenderTarget::activate(context);
} }
video::IRenderTarget *TextureBufferOutput::getIrrRenderTarget(PipelineContext &context)
{
activate(context); // Needed to make sure that render_target is set up.
return render_target;
}
u8 DynamicSource::getTextureCount() u8 DynamicSource::getTextureCount()
{ {
assert(isConfigured()); assert(isConfigured());

View file

@ -117,7 +117,7 @@ public:
* @param name unique name of the texture * @param name unique name of the texture
* @param format color format * @param format color format
*/ */
void setTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false); void setTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false, u8 msaa = 0);
/** /**
* Configure relative-size texture for the specific index * Configure relative-size texture for the specific index
@ -127,7 +127,7 @@ public:
* @param name unique name of the texture * @param name unique name of the texture
* @param format color format * @param format color format
*/ */
void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false); void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false, u8 msaa = 0);
virtual u8 getTextureCount() override { return m_textures.size(); } virtual u8 getTextureCount() override { return m_textures.size(); }
virtual video::ITexture *getTexture(u8 index) override; virtual video::ITexture *getTexture(u8 index) override;
@ -146,6 +146,7 @@ private:
core::dimension2du size; core::dimension2du size;
std::string name; std::string name;
video::ECOLOR_FORMAT format; video::ECOLOR_FORMAT format;
u8 msaa;
}; };
/** /**
@ -174,6 +175,9 @@ public:
TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map, u8 depth_stencil); TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map, u8 depth_stencil);
virtual ~TextureBufferOutput() override; virtual ~TextureBufferOutput() override;
void activate(PipelineContext &context) override; void activate(PipelineContext &context) override;
video::IRenderTarget *getIrrRenderTarget(PipelineContext &context);
private: private:
static const u8 NO_DEPTH_TEXTURE = 255; static const u8 NO_DEPTH_TEXTURE = 255;

View file

@ -9,6 +9,7 @@
#include "client/shader.h" #include "client/shader.h"
#include "client/tile.h" #include "client/tile.h"
#include "settings.h" #include "settings.h"
#include "mt_opengl.h"
PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector<u8> &_texture_map) : PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector<u8> &_texture_map) :
shader_id(_shader_id), texture_map(_texture_map) shader_id(_shader_id), texture_map(_texture_map)
@ -102,21 +103,45 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
static const u8 TEXTURE_EXPOSURE_2 = 4; static const u8 TEXTURE_EXPOSURE_2 = 4;
static const u8 TEXTURE_FXAA = 5; static const u8 TEXTURE_FXAA = 5;
static const u8 TEXTURE_VOLUME = 6; static const u8 TEXTURE_VOLUME = 6;
static const u8 TEXTURE_MSAA_COLOR = 7;
static const u8 TEXTURE_MSAA_DEPTH = 8;
static const u8 TEXTURE_SCALE_DOWN = 10; static const u8 TEXTURE_SCALE_DOWN = 10;
static const u8 TEXTURE_SCALE_UP = 20; static const u8 TEXTURE_SCALE_UP = 20;
// Super-sampling is simply rendering into a larger texture.
// Downscaling is done by the final step when rendering to the screen.
const std::string antialiasing = g_settings->get("antialiasing");
const bool enable_bloom = g_settings->getBool("enable_bloom"); const bool enable_bloom = g_settings->getBool("enable_bloom");
const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom;
const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure"); const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure");
const std::string antialiasing = g_settings->get("antialiasing");
const u16 antialiasing_scale = MYMAX(2, g_settings->getU16("fsaa"));
// This code only deals with MSAA in combination with post-processing. MSAA without
// post-processing works via a flag at OpenGL context creation instead.
// To make MSAA work with post-processing, we need multisample texture support,
// which has higher OpenGL (ES) version requirements.
// Note: This is not about renderbuffer objects, but about textures,
// since that's what we use and what Irrlicht allows us to use.
const bool msaa_available = driver->queryFeature(video::EVDF_TEXTURE_MULTISAMPLE);
const bool enable_msaa = antialiasing == "fsaa" && msaa_available;
if (antialiasing == "fsaa" && !msaa_available)
warningstream << "Ignoring configured FSAA. FSAA is not supported in "
<< "combination with post-processing by the current video driver." << std::endl;
const bool enable_ssaa = antialiasing == "ssaa"; const bool enable_ssaa = antialiasing == "ssaa";
const bool enable_fxaa = antialiasing == "fxaa"; const bool enable_fxaa = antialiasing == "fxaa";
const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom;
// Super-sampling is simply rendering into a larger texture.
// Downscaling is done by the final step when rendering to the screen.
if (enable_ssaa) { if (enable_ssaa) {
u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa")); scale *= antialiasing_scale;
scale *= ssaa_scale; }
if (enable_msaa) {
buffer->setTexture(TEXTURE_MSAA_COLOR, scale, "3d_render_msaa", color_format, false, antialiasing_scale);
buffer->setTexture(TEXTURE_MSAA_DEPTH, scale, "3d_depthmap_msaa", depth_format, false, antialiasing_scale);
} }
buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format); buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
@ -125,7 +150,14 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format); buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
// attach buffer to the previous step // attach buffer to the previous step
previousStep->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH)); if (enable_msaa) {
TextureBufferOutput *msaa = pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_MSAA_COLOR }, TEXTURE_MSAA_DEPTH);
previousStep->setRenderTarget(msaa);
TextureBufferOutput *normal = pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH);
pipeline->addStep<ResolveMSAAStep>(msaa, normal);
} else {
previousStep->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH));
}
// shared variables // shared variables
u32 shader_id; u32 shader_id;
@ -234,3 +266,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
return effect; return effect;
} }
void ResolveMSAAStep::run(PipelineContext &context)
{
context.device->getVideoDriver()->blitRenderTarget(msaa_fbo->getIrrRenderTarget(context),
target_fbo->getIrrRenderTarget(context));
}

View file

@ -44,4 +44,18 @@ private:
void configureMaterial(); void configureMaterial();
}; };
class ResolveMSAAStep : public TrivialRenderStep
{
public:
ResolveMSAAStep(TextureBufferOutput *_msaa_fbo, TextureBufferOutput *_target_fbo) :
msaa_fbo(_msaa_fbo), target_fbo(_target_fbo) {};
void run(PipelineContext &context) override;
private:
TextureBufferOutput *msaa_fbo;
TextureBufferOutput *target_fbo;
};
RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client); RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client);

View file

@ -179,7 +179,10 @@ RenderingEngine::RenderingEngine(MyEventReceiver *receiver)
// bpp, fsaa, vsync // bpp, fsaa, vsync
bool vsync = g_settings->getBool("vsync"); bool vsync = g_settings->getBool("vsync");
bool enable_fsaa = g_settings->get("antialiasing") == "fsaa"; // Don't enable MSAA in OpenGL context creation if post-processing is enabled,
// the post-processing pipeline handles it.
bool enable_fsaa = g_settings->get("antialiasing") == "fsaa" &&
!g_settings->getBool("enable_post_processing");
u16 fsaa = enable_fsaa ? MYMAX(2, g_settings->getU16("fsaa")) : 0; u16 fsaa = enable_fsaa ? MYMAX(2, g_settings->getU16("fsaa")) : 0;
// Determine driver // Determine driver