diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index 2434549b2..6d30b56be 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -40,6 +40,7 @@ namespace Nz std::unique_ptr InstantiateTexture(const TextureInfo& params) override; std::unique_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; + inline void NotifySamplerDestruction(GLuint texture) const; inline void NotifyTextureDestruction(GLuint texture) const; OpenGLDevice& operator=(const OpenGLDevice&) = delete; diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.inl b/include/Nazara/OpenGLRenderer/OpenGLDevice.inl index 0c77347a6..98208b40e 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.inl @@ -12,6 +12,12 @@ namespace Nz return *m_referenceContext; } + inline void OpenGLDevice::NotifySamplerDestruction(GLuint texture) const + { + for (const GL::Context* context : m_contexts) + context->NotifySamplerDestruction(texture); + } + inline void OpenGLDevice::NotifyTextureDestruction(GLuint texture) const { for (const GL::Context* context : m_contexts) diff --git a/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp b/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp index 4ba930384..efe9b37bf 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace Nz @@ -16,18 +17,18 @@ namespace Nz class NAZARA_OPENGLRENDERER_API OpenGLTextureSampler : public TextureSampler { public: - OpenGLTextureSampler(Vk::Device& device, TextureSamplerInfo samplerInfo); + OpenGLTextureSampler(OpenGLDevice& device, TextureSamplerInfo samplerInfo); OpenGLTextureSampler(const OpenGLTextureSampler&) = default; OpenGLTextureSampler(OpenGLTextureSampler&&) noexcept = default; ~OpenGLTextureSampler() = default; - inline VkSampler GetSampler() const; + inline const GL::Sampler& GetSampler() const; OpenGLTextureSampler& operator=(const OpenGLTextureSampler&) = delete; OpenGLTextureSampler& operator=(OpenGLTextureSampler&&) = delete; private: - Vk::Sampler m_sampler; + GL::Sampler m_sampler; }; } diff --git a/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.inl b/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.inl index 1935928c3..710e835fb 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLTextureSampler.inl @@ -2,12 +2,12 @@ // This file is part of the "Nazara Engine - OpenGL Renderer" // For conditions of distribution and use, see copyright notice in Config.hpp -#include +#include #include namespace Nz { - inline VkSampler OpenGLTextureSampler::GetSampler() const + inline const GL::Sampler& OpenGLTextureSampler::GetSampler() const { return m_sampler; } diff --git a/include/Nazara/OpenGLRenderer/Utils.hpp b/include/Nazara/OpenGLRenderer/Utils.hpp index ad330277d..b7f4c4200 100644 --- a/include/Nazara/OpenGLRenderer/Utils.hpp +++ b/include/Nazara/OpenGLRenderer/Utils.hpp @@ -16,6 +16,9 @@ namespace Nz { + inline GLenum ToOpenGL(SamplerFilter filter); + inline GLenum ToOpenGL(SamplerFilter minFilter, SamplerMipmapMode mipmapFilter); + inline GLenum ToOpenGL(SamplerWrap wrapMode); inline GLenum ToOpenGL(ShaderStageType stageType); //NAZARA_OPENGLRENDERER_API std::string TranslateOpenGLError(GLenum code); diff --git a/include/Nazara/OpenGLRenderer/Utils.inl b/include/Nazara/OpenGLRenderer/Utils.inl index 4e6be66cc..279648237 100644 --- a/include/Nazara/OpenGLRenderer/Utils.inl +++ b/include/Nazara/OpenGLRenderer/Utils.inl @@ -10,6 +10,64 @@ namespace Nz { + GLenum ToOpenGL(SamplerFilter filter) + { + switch (filter) + { + case SamplerFilter::SamplerFilter_Linear: return GL_LINEAR; + case SamplerFilter::SamplerFilter_Nearest: return GL_NEAREST; + } + + NazaraError("Unhandled SamplerFilter 0x" + String::Number(UnderlyingCast(filter), 16)); + return {}; + } + + GLenum ToOpenGL(SamplerFilter minFilter, SamplerMipmapMode mipmapFilter) + { + switch (minFilter) + { + case SamplerFilter::SamplerFilter_Linear: + { + switch (mipmapFilter) + { + case SamplerMipmapMode_Linear: return GL_LINEAR_MIPMAP_LINEAR; + case SamplerMipmapMode_Nearest: return GL_LINEAR_MIPMAP_NEAREST; + } + + NazaraError("Unhandled SamplerFilter 0x" + String::Number(UnderlyingCast(mipmapFilter), 16)); + return {}; + } + + case SamplerFilter::SamplerFilter_Nearest: + { + switch (mipmapFilter) + { + case SamplerMipmapMode_Linear: return GL_NEAREST_MIPMAP_LINEAR; + case SamplerMipmapMode_Nearest: return GL_NEAREST_MIPMAP_NEAREST; + } + + NazaraError("Unhandled SamplerFilter 0x" + String::Number(UnderlyingCast(mipmapFilter), 16)); + return {}; + } + } + + NazaraError("Unhandled SamplerFilter 0x" + String::Number(UnderlyingCast(minFilter), 16)); + return {}; + } + + GLenum ToOpenGL(SamplerWrap wrapMode) + { + switch (wrapMode) + { + case SamplerWrap::SamplerWrap_Clamp: return GL_CLAMP_TO_EDGE; + case SamplerWrap::SamplerWrap_MirroredRepeat: return GL_MIRRORED_REPEAT; + case SamplerWrap::SamplerWrap_Repeat: return GL_REPEAT; + } + + NazaraError("Unhandled SamplerWrap 0x" + String::Number(UnderlyingCast(wrapMode), 16)); + return {}; + } + GLenum ToOpenGL(ShaderStageType stageType) { switch (stageType) diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index b0d731bac..d24d56650 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -30,8 +30,9 @@ namespace Nz::GL enum class Extension { SpirV, + TextureFilterAnisotropic, - Max = SpirV + Max = TextureFilterAnisotropic }; enum class ExtensionStatus @@ -73,7 +74,9 @@ namespace Nz::GL inline Context(const OpenGLDevice* device); virtual ~Context(); + void BindSampler(UInt32 textureUnit, GLuint sampler) const; void BindTexture(TextureTarget target, GLuint texture) const; + void BindTexture(UInt32 textureUnit, TextureTarget target, GLuint texture) const; virtual void EnableVerticalSync(bool enabled) = 0; @@ -86,8 +89,11 @@ namespace Nz::GL bool Initialize(const ContextParams& params); + inline void NotifySamplerDestruction(GLuint sampler) const; inline void NotifyTextureDestruction(GLuint texture) const; + inline void SetCurrentTextureUnit(UInt32 textureUnit) const; + virtual void SwapBuffers() = 0; #define NAZARA_OPENGLRENDERER_FUNC(name, sig) sig name = nullptr; @@ -113,7 +119,14 @@ namespace Nz::GL struct State { - std::array boundTextures; + struct TextureUnit + { + GLuint sampler = 0; + std::array textureTargets = { 0 }; + }; + + std::vector textureUnits; + UInt32 currentTextureUnit = 0; }; std::array m_extensionStatus; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.inl b/include/Nazara/OpenGLRenderer/Wrapper/Context.inl index d72897342..88429dd1d 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.inl @@ -37,12 +37,33 @@ namespace Nz::GL return m_supportedExtensions.find(extension) != m_supportedExtensions.end(); } + inline void Context::NotifySamplerDestruction(GLuint sampler) const + { + for (auto& unit : m_state.textureUnits) + { + if (unit.sampler == sampler) + unit.sampler = 0; + } + } + inline void Context::NotifyTextureDestruction(GLuint texture) const { - for (GLuint& boundTexture : m_state.boundTextures) + for (auto& unit : m_state.textureUnits) { - if (boundTexture == texture) - boundTexture = 0; + for (GLuint& boundTexture : unit.textureTargets) + { + if (boundTexture == texture) + boundTexture = 0; + } + } + } + + inline void Context::SetCurrentTextureUnit(UInt32 textureUnit) const + { + if (m_state.currentTextureUnit != textureUnit) + { + glActiveTexture(GL_TEXTURE0 + textureUnit); + m_state.currentTextureUnit = textureUnit; } } } diff --git a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp index ac90702c9..21e4f707a 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp @@ -110,7 +110,9 @@ typedef void (GL_APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const G cb(glReadPixels, PFNGLREADPIXELSPROC) \ cb(glRenderbufferStorage, PFNGLRENDERBUFFERSTORAGEPROC) \ cb(glSamplerParameterf, PFNGLSAMPLERPARAMETERFPROC) \ + cb(glSamplerParameterfv, PFNGLSAMPLERPARAMETERFVPROC) \ cb(glSamplerParameteri, PFNGLSAMPLERPARAMETERIPROC) \ + cb(glSamplerParameteriv, PFNGLSAMPLERPARAMETERIVPROC) \ cb(glScissor, PFNGLSCISSORPROC) \ cb(glShaderBinary, PFNGLSHADERBINARYPROC) \ cb(glShaderSource, PFNGLSHADERSOURCEPROC) \ diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Sampler.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Sampler.hpp index 368511917..38fd5b542 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Sampler.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Sampler.hpp @@ -4,36 +4,40 @@ #pragma once -#ifndef NAZARA_OPENGLRENDERER_VKSAMPLER_HPP -#define NAZARA_OPENGLRENDERER_VKSAMPLER_HPP +#ifndef NAZARA_OPENGLRENDERER_GLSAMPLER_HPP +#define NAZARA_OPENGLRENDERER_GLSAMPLER_HPP #include +#include +#include #include -namespace Nz +namespace Nz::GL { - namespace Vk + class Sampler : public DeviceObject { - class Sampler : public DeviceObject - { - friend DeviceObject; + friend DeviceObject; - public: - Sampler() = default; - Sampler(const Sampler&) = delete; - Sampler(Sampler&&) = default; - ~Sampler() = default; + public: + Sampler() = default; + Sampler(const Sampler&) = delete; + Sampler(Sampler&&) noexcept = default; + ~Sampler() = default; - Sampler& operator=(const Sampler&) = delete; - Sampler& operator=(Sampler&&) = delete; + inline void SetParameterf(GLenum pname, GLfloat param); + inline void SetParameteri(GLenum pname, GLint param); + inline void SetParameterfv(GLenum pname, const GLfloat* param); + inline void SetParameteriv(GLenum pname, const GLint* param); - private: - static inline VkResult CreateHelper(Device& device, const VkSamplerCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkSampler* handle); - static inline void DestroyHelper(Device& device, VkSampler handle, const VkAllocationCallbacks* allocator); - }; - } + Sampler& operator=(const Sampler&) = delete; + Sampler& operator=(Sampler&&) noexcept = default; + + private: + static inline GLuint CreateHelper(OpenGLDevice& device, const Context& context); + static inline void DestroyHelper(OpenGLDevice& device, const Context& context, GLuint objectId); + }; } #include -#endif // NAZARA_OPENGLRENDERER_VKSAMPLER_HPP +#endif diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Sampler.inl b/include/Nazara/OpenGLRenderer/Wrapper/Sampler.inl index ab1407c55..b48ab4c03 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Sampler.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/Sampler.inl @@ -5,19 +5,53 @@ #include #include -namespace Nz +namespace Nz::GL { - namespace Vk + inline void Sampler::SetParameterf(GLenum pname, GLfloat param) { - inline VkResult Sampler::CreateHelper(Device& device, const VkSamplerCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkSampler* handle) - { - return device.vkCreateSampler(device, createInfo, allocator, handle); - } + assert(m_objectId); - inline void Sampler::DestroyHelper(Device& device, VkSampler handle, const VkAllocationCallbacks* allocator) - { - return device.vkDestroySampler(device, handle, allocator); - } + const Context& context = EnsureDeviceContext(); + context.glSamplerParameterf(m_objectId, pname, param); + } + + inline void Sampler::SetParameteri(GLenum pname, GLint param) + { + assert(m_objectId); + + const Context& context = EnsureDeviceContext(); + context.glSamplerParameteri(m_objectId, pname, param); + } + + inline void Sampler::SetParameterfv(GLenum pname, const GLfloat* param) + { + assert(m_objectId); + + const Context& context = EnsureDeviceContext(); + context.glSamplerParameterfv(m_objectId, pname, param); + } + + inline void Sampler::SetParameteriv(GLenum pname, const GLint* param) + { + assert(m_objectId); + + const Context& context = EnsureDeviceContext(); + context.glSamplerParameteriv(m_objectId, pname, param); + } + + inline GLuint Sampler::CreateHelper(OpenGLDevice& device, const Context& context) + { + GLuint sampler = 0; + context.glGenSamplers(1U, &sampler); + + return sampler; + } + + inline void Sampler::DestroyHelper(OpenGLDevice& device, const Context& context, GLuint objectId) + { + context.glDeleteSamplers(1U, &objectId); + + device.NotifySamplerDestruction(objectId); } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp b/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp index 6a8dd75eb..72dac5f12 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp @@ -2,34 +2,28 @@ // This file is part of the "Nazara Engine - OpenGL Renderer" // For conditions of distribution and use, see copyright notice in Config.hpp -#if 0 - #include +#include #include #include namespace Nz { - OpenGLTextureSampler::OpenGLTextureSampler(Vk::Device& device, TextureSamplerInfo samplerInfo) + OpenGLTextureSampler::OpenGLTextureSampler(OpenGLDevice& device, TextureSamplerInfo samplerInfo) { - VkSamplerCreateInfo createInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - createInfo.magFilter = ToOpenGL(samplerInfo.magFilter); - createInfo.minFilter = ToOpenGL(samplerInfo.minFilter); - createInfo.addressModeU = ToOpenGL(samplerInfo.wrapModeU); - createInfo.addressModeV = ToOpenGL(samplerInfo.wrapModeV); - createInfo.addressModeW = ToOpenGL(samplerInfo.wrapModeW); - createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - createInfo.mipmapMode = ToOpenGL(samplerInfo.mipmapMode); + if (!m_sampler.Create(device)) + throw std::runtime_error("failed to create sampler object"); - if (samplerInfo.anisotropyLevel > 0.f) - { - createInfo.anisotropyEnable = VK_TRUE; - createInfo.maxAnisotropy = samplerInfo.anisotropyLevel; - } + // In OpenGL, min and mipmap sampler are part of the same enum - if (!m_sampler.Create(device, createInfo)) - throw std::runtime_error("Failed to create sampler: " + TranslateOpenGLError(m_sampler.GetLastErrorCode())); + m_sampler.SetParameteri(GL_TEXTURE_MIN_FILTER, ToOpenGL(samplerInfo.minFilter, samplerInfo.mipmapMode)); + m_sampler.SetParameteri(GL_TEXTURE_MAG_FILTER, ToOpenGL(samplerInfo.magFilter)); + + m_sampler.SetParameteri(GL_TEXTURE_WRAP_S, ToOpenGL(samplerInfo.wrapModeU)); + m_sampler.SetParameteri(GL_TEXTURE_WRAP_T, ToOpenGL(samplerInfo.wrapModeV)); + m_sampler.SetParameteri(GL_TEXTURE_WRAP_R, ToOpenGL(samplerInfo.wrapModeW)); + + if (samplerInfo.anisotropyLevel > 1.f) + m_sampler.SetParameterf(GL_TEXTURE_MAX_ANISOTROPY_EXT, samplerInfo.anisotropyLevel); } } - -#endif diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 156e21af3..c4333e90f 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -22,13 +22,40 @@ namespace Nz::GL m_device->NotifyContextDestruction(*this); } + void Context::BindSampler(UInt32 textureUnit, GLuint sampler) const + { + if (textureUnit >= m_state.textureUnits.size()) + throw std::runtime_error("unsupported texture unit #" + std::to_string(textureUnit)); + + auto& unit = m_state.textureUnits[textureUnit]; + if (unit.sampler != sampler) + { + if (!SetCurrentContext(this)) + throw std::runtime_error("failed to activate context"); + + glBindSampler(textureUnit, sampler); + unit.sampler = sampler; + } + } + void Context::BindTexture(TextureTarget target, GLuint texture) const { - if (!SetCurrentContext(this)) - throw std::runtime_error("failed to activate context"); + BindTexture(m_state.currentTextureUnit, target, texture); + } - if (m_state.boundTextures[UnderlyingCast(target)] != texture) + void Context::BindTexture(UInt32 textureUnit, TextureTarget target, GLuint texture) const + { + if (textureUnit >= m_state.textureUnits.size()) + throw std::runtime_error("unsupported texture unit #" + std::to_string(textureUnit)); + + auto& unit = m_state.textureUnits[textureUnit]; + if (unit.textureTargets[UnderlyingCast(target)] != texture) { + if (!SetCurrentContext(this)) + throw std::runtime_error("failed to activate context"); + + SetCurrentTextureUnit(textureUnit); + GLenum glTarget; switch (target) { @@ -54,7 +81,7 @@ namespace Nz::GL glBindTexture(glTarget, texture); - m_state.boundTextures[UnderlyingCast(target)] = texture; + unit.textureTargets[UnderlyingCast(target)] = texture; } } @@ -66,8 +93,6 @@ namespace Nz::GL return false; } - m_state.boundTextures.fill(0); - const Loader& loader = GetLoader(); auto LoadSymbol = [&](auto& func, const char* funcName, bool mandatory) @@ -118,6 +143,14 @@ namespace Nz::GL }); m_extensionStatus.fill(ExtensionStatus::NotSupported); + + // TextureFilterAnisotropic + if (m_supportedExtensions.count("GL_ARB_texture_filter_anisotropic")) + m_extensionStatus[UnderlyingCast(Extension::TextureFilterAnisotropic)] = ExtensionStatus::ARB; + else if (m_supportedExtensions.count("GL_EXT_texture_filter_anisotropic")) + m_extensionStatus[UnderlyingCast(Extension::TextureFilterAnisotropic)] = ExtensionStatus::EXT; + + // SpirV if (m_supportedExtensions.count("GL_ARB_gl_spirv")) m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB; @@ -162,6 +195,14 @@ namespace Nz::GL }, this); } + GLint maxTextureUnits = -1; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + if (maxTextureUnits < 32) //< OpenGL ES 3.0 requires at least 32 textures units + NazaraWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is " + std::to_string(maxTextureUnits) + ", >= 32 expected"); + + assert(maxTextureUnits > 0); + m_state.textureUnits.resize(maxTextureUnits); + return true; }