1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-31 18:31:04 +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

@ -1678,6 +1678,12 @@ ITexture *CNullDriver::addRenderTargetTexture(const core::dimension2d<u32> &size
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,
const io::path &name, const ECOLOR_FORMAT format)
{

View file

@ -227,6 +227,10 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
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
ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen,
const io::path &name, const ECOLOR_FORMAT format) override;
@ -410,6 +414,8 @@ public:
//! Create render target.
IRenderTarget *addRenderTarget() override;
void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override {}
//! Remove render target.
void removeRenderTarget(IRenderTarget *renderTarget) override;

View file

@ -85,13 +85,19 @@ class COpenGLCoreCacheHandler
GL.BindTexture(prevTextureType, 0);
#if defined(IRR_COMPILE_GL_COMMON)
GL.Disable(prevTextureType);
GL.Enable(curTextureType);
// The "enable/disable texture" stuff is so legacy that
// 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
}
#if defined(IRR_COMPILE_GL_COMMON)
else if (!prevTexture)
GL.Enable(curTextureType);
if (curTextureType != GL_TEXTURE_2D_MULTISAMPLE)
GL.Enable(curTextureType);
#endif
GL.BindTexture(curTextureType, static_cast<const TOpenGLTexture *>(texture)->getOpenGLTextureName());
@ -110,7 +116,8 @@ class COpenGLCoreCacheHandler
GL.BindTexture(prevTextureType, 0);
#if defined(IRR_COMPILE_GL_COMMON)
GL.Disable(prevTextureType);
if (prevTextureType != GL_TEXTURE_2D_MULTISAMPLE)
GL.Disable(prevTextureType);
#endif
}

View file

@ -5,6 +5,7 @@
#pragma once
#include "IRenderTarget.h"
#include <stdexcept>
#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
#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;
if (currentTexture) {
if (currentTexture->getType() == ETT_2D) {
if (currentTexture->getType() == ETT_2D || currentTexture->getType() == ETT_2D_MS) {
GLuint textureID = currentTexture->getOpenGLTextureName();
const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN;
@ -172,7 +173,20 @@ public:
if (textureID != 0) {
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);
TEST_GL_ERROR(Driver);
} else if (AssignedTextures[i] != GL_NONE) {
@ -198,36 +212,50 @@ public:
// Set depth and stencil attachments.
if (RequestDepthStencilUpdate) {
const ECOLOR_FORMAT textureFormat = (DepthStencil) ? DepthStencil->getColorFormat() : ECF_UNKNOWN;
const ECOLOR_FORMAT textureFormat = DepthStencil ? DepthStencil->getColorFormat() : ECF_UNKNOWN;
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();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_ // The WEBGL_depth_texture extension does not allow attaching stencil+depth separate.
if (textureFormat == ECF_D24S8) {
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;
} 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;
}
#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) {
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, textarget, textureID, 0);
AssignedStencil = true;
} else {
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;
}
#endif
AssignedDepth = true;
} else {
// No (valid) depth/stencil texture.
if (AssignedDepth)
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) :
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)
{
_IRR_DEBUG_BREAK_IF(srcImages.empty())
DriverType = Driver->getDriverType();
_IRR_DEBUG_BREAK_IF(Type == ETT_2D_MS); // not supported by this constructor
TextureType = TextureTypeIrrToGL(Type);
HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
KeepImage = Driver->getTextureCreationFlag(ETCF_ALLOW_MEMORY_COPY);
@ -141,10 +142,10 @@ public:
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),
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)
{
DriverType = Driver->getDriverType();
@ -184,23 +185,47 @@ public:
const COpenGLCoreTexture *prevTexture = Driver->getCacheHandler()->getTextureCache().get(0);
Driver->getCacheHandler()->getTextureCache().set(0, this);
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);
// An INVALID_ENUM error is generated by TexParameter* if target is either
// TEXTURE_2D_MULTISAMPLE or TEXTURE_2D_MULTISAMPLE_ARRAY, and pname is any
// sampler state from table 23.18.
// ~ 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)
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
#endif
StatesCache.WrapU = ETC_CLAMP_TO_EDGE;
StatesCache.WrapV = ETC_CLAMP_TO_EDGE;
StatesCache.WrapW = ETC_CLAMP_TO_EDGE;
StatesCache.WrapU = ETC_CLAMP_TO_EDGE;
StatesCache.WrapV = ETC_CLAMP_TO_EDGE;
StatesCache.WrapW = ETC_CLAMP_TO_EDGE;
}
switch (Type) {
case ETT_2D:
GL.TexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0);
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:
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);
@ -595,6 +620,8 @@ protected:
switch (type) {
case ETT_2D:
return GL_TEXTURE_2D;
case ETT_2D_MS:
return GL_TEXTURE_2D_MULTISAMPLE;
case ETT_CUBEMAP:
return GL_TEXTURE_CUBE_MAP;
}
@ -610,6 +637,7 @@ protected:
GLint InternalFormat;
GLenum PixelFormat;
GLenum PixelType;
u8 MSAA;
void (*Converter)(const void *, s32, void *);
bool LockReadOnly;

View file

@ -651,6 +651,29 @@ IRenderTarget *COpenGLDriver::addRenderTarget()
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
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)
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);
#ifdef GL_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,
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))
return 0;
@ -2711,7 +2742,7 @@ ITexture *COpenGLDriver::addRenderTargetTexture(const core::dimension2d<u32> &si
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);
renderTargetTexture->drop();

View file

@ -108,6 +108,8 @@ public:
//! Create render target.
IRenderTarget *addRenderTarget() override;
void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override;
//! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount,
@ -270,6 +272,9 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
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
ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen,
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];
case EVDF_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:
return false;

View file

@ -675,6 +675,29 @@ IRenderTarget *COpenGL3DriverBase::addRenderTarget()
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
void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
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]));
// 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 (material.AntiAliasing & EAAM_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,
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
bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
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);
renderTargetTexture->drop();

View file

@ -74,6 +74,8 @@ public:
IRenderTarget *addRenderTarget() override;
void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override;
//! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount,
@ -209,6 +211,9 @@ public:
virtual ITexture *addRenderTargetTexture(const core::dimension2d<u32> &size,
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
ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen,
const io::path &name, const ECOLOR_FORMAT format) override;

View file

@ -74,6 +74,8 @@ public:
return false;
case EVDF_STENCIL_BUFFER:
return StencilBuffer;
case EVDF_TEXTURE_MULTISAMPLE:
return TextureMultisampleSupported;
default:
return false;
};
@ -161,6 +163,7 @@ public:
bool AnisotropicFilterSupported = 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");
BlendMinMaxSupported = true;
TextureMultisampleSupported = true;
// COGLESCoreExtensionHandler::Feature
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");
AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic");
BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax];
TextureMultisampleSupported = isVersionAtLeast(3, 1);
const bool TextureLODBiasSupported = queryExtension("GL_EXT_texture_lod_bias");
// COGLESCoreExtensionHandler::Feature