diff --git a/irr/include/EDriverFeatures.h b/irr/include/EDriverFeatures.h index 0a35161cf..c1fcd464d 100644 --- a/irr/include/EDriverFeatures.h +++ b/irr/include/EDriverFeatures.h @@ -132,6 +132,9 @@ enum E_VIDEO_DRIVER_FEATURE //! Support for multisample textures. EVDF_TEXTURE_MULTISAMPLE, + //! Support for 2D array textures. + EVDF_TEXTURE_2D_ARRAY, + //! Only used for counting the elements of this enum EVDF_COUNT }; diff --git a/irr/include/ITexture.h b/irr/include/ITexture.h index 1a65a34f1..bdbb72727 100644 --- a/irr/include/ITexture.h +++ b/irr/include/ITexture.h @@ -129,7 +129,10 @@ enum E_TEXTURE_TYPE ETT_2D_MS, //! Cubemap texture. - ETT_CUBEMAP + ETT_CUBEMAP, + + //! 2D array texture + ETT_2D_ARRAY }; //! Interface of a Video Driver dependent Texture. diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 29ce8678a..950723941 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -232,6 +232,15 @@ public: information. */ virtual ITexture *addTexture(const io::path &name, IImage *image) = 0; + /** + * Creates an array texture from IImages. + * @param name A name for the texture. + * @param images Pointer to array of images + * @param count Number of images (must be at least 1) + * @return Pointer to the newly created texture + */ + virtual ITexture *addArrayTexture(const io::path &name, IImage **images, u32 count) = 0; + //! Creates a cubemap texture from loaded IImages. /** \param name A name for the texture. Later calls of getTexture() with this name will return this texture. The name can _not_ be empty. diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index 59487c5b0..21346e6ed 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -294,25 +294,9 @@ u32 CNullDriver::getTextureCount() const ITexture *CNullDriver::addTexture(const core::dimension2d &size, const io::path &name, ECOLOR_FORMAT format) { - if (0 == name.size()) { - os::Printer::log("Could not create ITexture, texture needs to have a non-empty name.", ELL_WARNING); - return 0; - } - IImage *image = new CImage(format, size); - ITexture *t = 0; - - if (checkImage(image)) { - t = createDeviceDependentTexture(name, image); - } - + ITexture *t = addTexture(name, image); image->drop(); - - if (t) { - addTexture(t); - t->drop(); - } - return t; } @@ -329,7 +313,8 @@ ITexture *CNullDriver::addTexture(const io::path &name, IImage *image) ITexture *t = 0; if (checkImage(image)) { - t = createDeviceDependentTexture(name, image); + std::vector tmp { image }; + t = createDeviceDependentTexture(name, ETT_2D, tmp); } if (t) { @@ -340,6 +325,27 @@ ITexture *CNullDriver::addTexture(const io::path &name, IImage *image) return t; } +ITexture *CNullDriver::addArrayTexture(const io::path &name, IImage **images, u32 count) +{ + if (0 == name.size()) { + os::Printer::log("Could not create ITexture, texture needs to have a non-empty name.", ELL_WARNING); + return 0; + } + + // this is stupid but who cares + std::vector tmp(images, images + count); + + ITexture *t = nullptr; + if (checkImage(tmp)) { + t = createDeviceDependentTexture(name, ETT_2D_ARRAY, tmp); + } + if (t) { + addTexture(t); + t->drop(); + } + return t; +} + ITexture *CNullDriver::addTextureCubemap(const io::path &name, IImage *imagePosX, IImage *imageNegX, IImage *imagePosY, IImage *imageNegY, IImage *imagePosZ, IImage *imageNegZ) { @@ -357,7 +363,7 @@ ITexture *CNullDriver::addTextureCubemap(const io::path &name, IImage *imagePosX imageArray.push_back(imageNegZ); if (checkImage(imageArray)) { - t = createDeviceDependentTextureCubemap(name, imageArray); + t = createDeviceDependentTexture(name, ETT_CUBEMAP, imageArray); } if (t) { @@ -384,7 +390,7 @@ ITexture *CNullDriver::addTextureCubemap(const irr::u32 sideLen, const io::path ITexture *t = 0; if (checkImage(imageArray)) { - t = createDeviceDependentTextureCubemap(name, imageArray); + t = createDeviceDependentTexture(name, ETT_CUBEMAP, imageArray); if (t) { addTexture(t); @@ -479,7 +485,8 @@ video::ITexture *CNullDriver::loadTextureFromFile(io::IReadFile *file, const io: return nullptr; if (checkImage(image)) { - texture = createDeviceDependentTexture(hashName.size() ? hashName : file->getFileName(), image); + std::vector tmp { image }; + texture = createDeviceDependentTexture(hashName.size() ? hashName : file->getFileName(), ETT_2D, tmp); if (texture) os::Printer::log("Loaded texture", file->getFileName(), ELL_DEBUG); } @@ -519,18 +526,16 @@ video::ITexture *CNullDriver::findTexture(const io::path &filename) return 0; } -ITexture *CNullDriver::createDeviceDependentTexture(const io::path &name, IImage *image) +ITexture *CNullDriver::createDeviceDependentTexture(const io::path &name, E_TEXTURE_TYPE type, + const std::vector &images) { - SDummyTexture *dummy = new SDummyTexture(name, ETT_2D); - dummy->setSize(image->getDimension()); + if (type != ETT_2D && type != ETT_CUBEMAP) + return nullptr; + SDummyTexture *dummy = new SDummyTexture(name, type); + dummy->setSize(images[0]->getDimension()); return dummy; } -ITexture *CNullDriver::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) -{ - return new SDummyTexture(name, ETT_CUBEMAP); -} - bool CNullDriver::setRenderTargetEx(IRenderTarget *target, u16 clearFlag, SColor clearColor, f32 clearDepth, u8 clearStencil) { return false; @@ -883,6 +888,9 @@ bool CNullDriver::checkImage(const std::vector &image) const auto lastSize = image[0]->getDimension(); for (size_t i = 0; i < image.size(); ++i) { + if (!image[i]) + return false; + ECOLOR_FORMAT format = image[i]->getColorFormat(); auto size = image[i]->getDimension(); diff --git a/irr/src/CNullDriver.h b/irr/src/CNullDriver.h index 00ae0b79b..3ef8c642d 100644 --- a/irr/src/CNullDriver.h +++ b/irr/src/CNullDriver.h @@ -84,6 +84,8 @@ public: ITexture *addTexture(const io::path &name, IImage *image) override; + ITexture *addArrayTexture(const io::path &name, IImage **images, u32 count) override; + virtual ITexture *addTextureCubemap(const io::path &name, IImage *imagePosX, IImage *imageNegX, IImage *imagePosY, IImage *imageNegY, IImage *imagePosZ, IImage *imageNegZ) override; @@ -549,9 +551,8 @@ protected: //! adds a surface, not loaded or created by the Irrlicht Engine void addTexture(ITexture *surface); - virtual ITexture *createDeviceDependentTexture(const io::path &name, IImage *image); - - virtual ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image); + virtual ITexture *createDeviceDependentTexture(const io::path &name, E_TEXTURE_TYPE type, + const std::vector &images); //! checks triangle count and print warning if wrong bool checkPrimitiveCount(u32 prmcnt) const; diff --git a/irr/src/COGLESCoreExtensionHandler.h b/irr/src/COGLESCoreExtensionHandler.h index 80a7bb061..5c760e8ca 100644 --- a/irr/src/COGLESCoreExtensionHandler.h +++ b/irr/src/COGLESCoreExtensionHandler.h @@ -39,7 +39,8 @@ public: COGLESCoreExtensionHandler() : MaxAnisotropy(1), MaxIndices(0xffff), - MaxTextureSize(1), MaxTextureLODBias(0.f), StencilBuffer(false) + MaxTextureSize(1), MaxArrayTextureLayers(1), + MaxTextureLODBias(0.f), StencilBuffer(false) { for (u32 i = 0; i < IRR_OGLES_Feature_Count; ++i) FeatureAvailable[i] = false; @@ -87,6 +88,7 @@ protected: u8 MaxAnisotropy; u32 MaxIndices; u32 MaxTextureSize; + u32 MaxArrayTextureLayers; f32 MaxTextureLODBias; //! Minimal and maximal supported thickness for lines without smoothing float DimAliasedLine[2]; diff --git a/irr/src/COpenGLCoreTexture.h b/irr/src/COpenGLCoreTexture.h index 439f786c6..51b122075 100644 --- a/irr/src/COpenGLCoreTexture.h +++ b/irr/src/COpenGLCoreTexture.h @@ -110,7 +110,7 @@ public: } TEST_GL_ERROR(Driver); - initTexture(); + initTexture(tmpImages->size()); for (size_t i = 0; i < tmpImages->size(); ++i) uploadTexture(i, 0, (*tmpImages)[i]->getData()); @@ -142,6 +142,7 @@ public: MipLevelStored(0) { DriverType = Driver->getDriverType(); + _IRR_DEBUG_BREAK_IF(Type == ETT_2D_ARRAY) // not supported by this constructor TextureType = TextureTypeIrrToGL(Type); HasMipMaps = false; IsRenderTarget = true; @@ -208,7 +209,7 @@ public: StatesCache.WrapW = ETC_CLAMP_TO_EDGE; } - initTexture(); + initTexture(0); if (!name.empty()) Driver->irrGlObjectLabel(GL_TEXTURE, TextureName, name.c_str()); @@ -265,7 +266,19 @@ public: const bool use_gl_impl = Driver->Version.Spec != OpenGLSpec::ES; #endif - if (use_gl_impl) { + if (Type == ETT_2D_ARRAY) { + + // For OpenGL an array texture is basically just a 3D texture internally. + // So if we call glGetTexImage() we would download the entire array, + // except the caller only wants a single layer. + // To do this properly we could have to use glGetTextureSubImage() [4.5] + // or some trickery with glTextureView() [4.3]. + // Also neither of those will work on GLES. + + os::Printer::log("lock: read or read/write unimplemented for ETT_2D_ARRAY", ELL_WARNING); + passed = false; + + } else if (use_gl_impl) { IImage *tmpImage = LockImage; @@ -495,7 +508,7 @@ protected: Pitch = Size.Width * IImage::getBitsPerPixelFromFormat(ColorFormat) / 8; } - void initTexture() + void initTexture(u32 layers) { // Compressed textures cannot be pre-allocated and are initialized on upload if (IImage::isCompressedFormat(ColorFormat)) { @@ -554,6 +567,16 @@ protected: TEST_GL_ERROR(Driver); } break; + case ETT_2D_ARRAY: + if (Driver->getFeature().TexStorage) { + GL.TexStorage3D(TextureType, levels, InternalFormat, + Size.Width, Size.Height, layers); + } else { + GL.TexImage3D(TextureType, 0, InternalFormat, + Size.Width, Size.Height, layers, 0, PixelFormat, PixelType, 0); + } + TEST_GL_ERROR(Driver); + break; default: _IRR_DEBUG_BREAK_IF(1) break; @@ -591,12 +614,15 @@ protected: case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP: GL.TexSubImage2D(tmpTextureType, level, 0, 0, width, height, PixelFormat, PixelType, tmpData); - TEST_GL_ERROR(Driver); + break; + case GL_TEXTURE_2D_ARRAY: + GL.TexSubImage3D(tmpTextureType, level, 0, 0, layer, width, height, 1, PixelFormat, PixelType, tmpData); break; default: _IRR_DEBUG_BREAK_IF(1) break; } + TEST_GL_ERROR(Driver); delete tmpImage; } else { @@ -606,12 +632,12 @@ protected: case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP: Driver->irrGlCompressedTexImage2D(tmpTextureType, level, InternalFormat, width, height, 0, dataSize, data); - TEST_GL_ERROR(Driver); break; default: _IRR_DEBUG_BREAK_IF(1) break; } + TEST_GL_ERROR(Driver); } } @@ -624,6 +650,8 @@ protected: return GL_TEXTURE_2D_MULTISAMPLE; case ETT_CUBEMAP: return GL_TEXTURE_CUBE_MAP; + case ETT_2D_ARRAY: + return GL_TEXTURE_2D_ARRAY; } os::Printer::log("COpenGLCoreTexture::TextureTypeIrrToGL unknown texture type", ELL_WARNING); diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index e9cd10b49..50e097362 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -1609,20 +1609,9 @@ inline void COpenGLDriver::getGLTextureMatrix(GLfloat *o, const core::matrix4 &m o[15] = 1.f; } -ITexture *COpenGLDriver::createDeviceDependentTexture(const io::path &name, IImage *image) +ITexture *COpenGLDriver::createDeviceDependentTexture(const io::path &name, E_TEXTURE_TYPE type, const std::vector &images) { - std::vector tmp { image }; - - COpenGLTexture *texture = new COpenGLTexture(name, tmp, ETT_2D, this); - - return texture; -} - -ITexture *COpenGLDriver::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) -{ - COpenGLTexture *texture = new COpenGLTexture(name, image, ETT_CUBEMAP, this); - - return texture; + return new COpenGLTexture(name, images, ETT_2D, this); } void COpenGLDriver::disableFeature(E_VIDEO_DRIVER_FEATURE feature, bool flag) diff --git a/irr/src/COpenGLDriver.h b/irr/src/COpenGLDriver.h index 1b5c0c6d3..67c328f58 100644 --- a/irr/src/COpenGLDriver.h +++ b/irr/src/COpenGLDriver.h @@ -326,9 +326,7 @@ private: //! inits the parts of the open gl driver used on all platforms bool genericDriverInit(); - ITexture *createDeviceDependentTexture(const io::path &name, IImage *image) override; - - ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) override; + ITexture *createDeviceDependentTexture(const io::path &name, E_TEXTURE_TYPE type, const std::vector &images) override; //! creates a transposed matrix in supplied GLfloat array to pass to OpenGL inline void getGLMatrix(GLfloat gl_matrix[16], const core::matrix4 &m); diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index e83a5b3ec..0057611d4 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -267,6 +267,7 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d &screenS DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy); DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices); DriverAttributes->setAttribute("MaxTextureSize", (s32)MaxTextureSize); + DriverAttributes->setAttribute("MaxArrayTextureLayers", (s32)MaxArrayTextureLayers); DriverAttributes->setAttribute("MaxTextureLODBias", MaxTextureLODBias); DriverAttributes->setAttribute("Version", 100 * Version.Major + Version.Minor); DriverAttributes->setAttribute("AntiAlias", AntiAlias); @@ -1072,20 +1073,9 @@ void COpenGL3DriverBase::endDraw(const VertexType &vertexType) GL.DisableVertexAttribArray(attr.Index); } -ITexture *COpenGL3DriverBase::createDeviceDependentTexture(const io::path &name, IImage *image) +ITexture *COpenGL3DriverBase::createDeviceDependentTexture(const io::path &name, E_TEXTURE_TYPE type, const std::vector &images) { - std::vector tmp { image }; - - COpenGL3Texture *texture = new COpenGL3Texture(name, tmp, ETT_2D, this); - - return texture; -} - -ITexture *COpenGL3DriverBase::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) -{ - COpenGL3Texture *texture = new COpenGL3Texture(name, image, ETT_CUBEMAP, this); - - return texture; + return new COpenGL3Texture(name, images, type, this); } // Same as COpenGLDriver::TextureFlipMatrix diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h index 6154e3fa9..7b399cf93 100644 --- a/irr/src/OpenGL/Driver.h +++ b/irr/src/OpenGL/Driver.h @@ -267,9 +267,8 @@ protected: void chooseMaterial2D(); - ITexture *createDeviceDependentTexture(const io::path &name, IImage *image) override; - - ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) override; + ITexture *createDeviceDependentTexture(const io::path &name, E_TEXTURE_TYPE type, + const std::vector &images) override; //! Map Irrlicht wrap mode to OpenGL enum GLint getTextureWrapMode(u8 clamp) const; diff --git a/irr/src/OpenGL/ExtensionHandler.h b/irr/src/OpenGL/ExtensionHandler.h index 209fd415b..413ab7f23 100644 --- a/irr/src/OpenGL/ExtensionHandler.h +++ b/irr/src/OpenGL/ExtensionHandler.h @@ -76,6 +76,8 @@ public: return StencilBuffer; case EVDF_TEXTURE_MULTISAMPLE: return TextureMultisampleSupported; + case EVDF_TEXTURE_2D_ARRAY: + return Texture2DArraySupported; default: return false; }; @@ -176,6 +178,7 @@ public: bool AnisotropicFilterSupported = false; bool BlendMinMaxSupported = false; bool TextureMultisampleSupported = false; + bool Texture2DArraySupported = false; bool KHRDebugSupported = false; u32 MaxLabelLength = 0; }; diff --git a/irr/src/OpenGL3/Driver.cpp b/irr/src/OpenGL3/Driver.cpp index 8339068a6..09286b027 100644 --- a/irr/src/OpenGL3/Driver.cpp +++ b/irr/src/OpenGL3/Driver.cpp @@ -71,6 +71,7 @@ void COpenGL3Driver::initFeatures() LODBiasSupported = true; BlendMinMaxSupported = true; TextureMultisampleSupported = true; + Texture2DArraySupported = Version.Major >= 3 || queryExtension("GL_EXT_texture_array"); KHRDebugSupported = isVersionAtLeast(4, 6) || queryExtension("GL_KHR_debug"); if (KHRDebugSupported) MaxLabelLength = GetInteger(GL.MAX_LABEL_LENGTH); @@ -88,6 +89,8 @@ void COpenGL3Driver::initFeatures() MaxAnisotropy = GetInteger(GL.MAX_TEXTURE_MAX_ANISOTROPY); MaxIndices = GetInteger(GL_MAX_ELEMENTS_INDICES); MaxTextureSize = GetInteger(GL_MAX_TEXTURE_SIZE); + if (Texture2DArraySupported) + MaxArrayTextureLayers = GetInteger(GL_MAX_ARRAY_TEXTURE_LAYERS); GL.GetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &MaxTextureLODBias); GL.GetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, DimAliasedLine); DimAliasedPoint[0] = 1.0f; diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp index 6000059ed..2db021088 100644 --- a/irr/src/OpenGLES2/Driver.cpp +++ b/irr/src/OpenGLES2/Driver.cpp @@ -124,6 +124,7 @@ void COpenGLES2Driver::initFeatures() AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic"); BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax]; TextureMultisampleSupported = isVersionAtLeast(3, 1); + Texture2DArraySupported = Version.Major >= 3 || queryExtension("GL_EXT_texture_array"); KHRDebugSupported = queryExtension("GL_KHR_debug"); if (KHRDebugSupported) MaxLabelLength = GetInteger(GL.MAX_LABEL_LENGTH); @@ -145,6 +146,8 @@ void COpenGLES2Driver::initFeatures() if (Version.Major >= 3 || queryExtension("GL_EXT_draw_range_elements")) MaxIndices = GetInteger(GL_MAX_ELEMENTS_INDICES); MaxTextureSize = GetInteger(GL_MAX_TEXTURE_SIZE); + if (Texture2DArraySupported) + MaxArrayTextureLayers = GetInteger(GL_MAX_ARRAY_TEXTURE_LAYERS); if (LODBiasSupported) GL.GetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &MaxTextureLODBias); GL.GetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, DimAliasedLine);