diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index 67e305f4a..0f1eda6f9 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -26,6 +26,8 @@ namespace Nz namespace Nz::GL { + class Texture; + enum class BufferTarget { Array, @@ -107,7 +109,9 @@ namespace Nz::GL friend SymbolLoader; public: - inline Context(const OpenGLDevice* device); + Context(const OpenGLDevice* device); + Context(const Context&) = delete; + Context(Context&&) = delete; virtual ~Context(); void BindBuffer(BufferTarget target, GLuint buffer, bool force = false) const; @@ -120,8 +124,14 @@ namespace Nz::GL void BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const; void BindVertexArray(GLuint vertexArray, bool force = false) const; + bool BlitTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Vector3ui& dstPos, SamplerFilter filter) const; + bool ClearErrorStack() const; + bool CopyTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Vector3ui& dstPos) const; + + inline bool DidLastCallSucceed() const; + virtual void EnableVerticalSync(bool enabled) = 0; inline const OpenGLDevice* GetDevice() const; @@ -160,6 +170,9 @@ namespace Nz::GL NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_FUNC) #undef NAZARA_OPENGLRENDERER_FUNC + Context& operator=(const Context&) = delete; + Context& operator=(Context&&) = delete; + static const Context* GetCurrentContext(); static bool SetCurrentContext(const Context* context); @@ -176,7 +189,8 @@ namespace Nz::GL ContextParams m_params; private: - void GL_APIENTRY HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) const; + void HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) const; + bool InitializeBlitFramebuffers() const; enum class FunctionIndex { @@ -187,6 +201,8 @@ namespace Nz::GL Count }; + struct BlitFramebuffers; + struct State { struct Box @@ -223,10 +239,13 @@ namespace Nz::GL std::array m_extensionStatus; std::array m_originalFunctionPointer; + mutable std::unique_ptr m_blitFramebuffers; std::unordered_set m_supportedExtensions; OpenGLVaoCache m_vaoCache; const OpenGLDevice* m_device; mutable State m_state; + mutable bool m_didCollectErrors; + mutable bool m_hadAnyError; }; } diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.inl b/include/Nazara/OpenGLRenderer/Wrapper/Context.inl index 6b27ce8f3..cb86106c8 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.inl @@ -7,10 +7,12 @@ namespace Nz::GL { - inline Context::Context(const OpenGLDevice* device) : - m_vaoCache(*this), - m_device(device) + inline bool Context::DidLastCallSucceed() const { + if (!m_didCollectErrors) + ProcessErrorStack(); + + return !m_hadAnyError; } inline const OpenGLDevice* Context::GetDevice() const diff --git a/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp b/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp index 98436cbee..435780de0 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp @@ -64,25 +64,7 @@ namespace Nz const OpenGLTexture& glTexture = static_cast(source); const GL::Context& context = m_texture.EnsureDeviceContext(); - - // Use glCopyImageSubData if available - if (context.glCopyImageSubData) - { - GLuint srcImage = glTexture.GetTexture().GetObjectId(); - GLenum srcTarget = ToOpenGL(ToTextureTarget(glTexture.GetType())); - - GLuint dstImage = m_texture.GetObjectId(); - GLenum dstTarget = ToOpenGL(ToTextureTarget(m_params.type)); - - context.glCopyImageSubData(srcImage, srcTarget, 0, GLint(srcBox.x), GLint(srcBox.y), GLint(srcBox.z), dstImage, dstTarget, 0, GLint(dstPos.x), GLint(dstPos.y), GLint(dstPos.z), GLsizei(srcBox.width), GLsizei(srcBox.height), GLsizei(srcBox.depth)); - return true; - } - else - { - //TODO: Blit using framebuffers - } - - return false; + return context.CopyTexture(glTexture.GetTexture(), m_texture, srcBox, dstPos); } PixelFormat OpenGLTexture::GetFormat() const diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index adcde76e7..4cffc5ee3 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -3,12 +3,14 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include #include +#include #include -#include +#include #include #include #include @@ -56,6 +58,12 @@ namespace Nz::GL }; } + struct Context::BlitFramebuffers + { + GL::Framebuffer drawFBO; + GL::Framebuffer readFBO; + }; + struct Context::SymbolLoader { SymbolLoader(Context& parent) : @@ -99,6 +107,15 @@ namespace Nz::GL Context& context; }; + + Context::Context(const OpenGLDevice* device) : + m_vaoCache(*this), + m_device(device), + m_didCollectErrors(false), + m_hadAnyError(false) + { + } + Context::~Context() { if (m_device) @@ -235,15 +252,71 @@ namespace Nz::GL } } + bool Context::BlitTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Vector3ui& dstPos, SamplerFilter filter) const + { + if (!m_blitFramebuffers && !InitializeBlitFramebuffers()) + return false; + + //TODO: handle other textures types + assert(source.GetTarget() == TextureTarget::Target2D); + assert(destination.GetTarget() == TextureTarget::Target2D); + + // Bind framebuffers before configuring them (so they won't override each other) + BindFramebuffer(FramebufferTarget::Draw, m_blitFramebuffers->drawFBO.GetObjectId()); + BindFramebuffer(FramebufferTarget::Read, m_blitFramebuffers->readFBO.GetObjectId()); + + // Attach textures to color attachment + m_blitFramebuffers->drawFBO.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destination.GetObjectId()); + if (GLenum checkResult = m_blitFramebuffers->drawFBO.Check(); checkResult != GL_FRAMEBUFFER_COMPLETE) + { + NazaraError("Blit draw FBO is incomplete: " + TranslateOpenGLError(checkResult)); + return false; + } + + m_blitFramebuffers->readFBO.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, source.GetObjectId()); + if (GLenum checkResult = m_blitFramebuffers->readFBO.Check(); checkResult != GL_FRAMEBUFFER_COMPLETE) + { + NazaraError("Blit read FBO is incomplete: " + TranslateOpenGLError(checkResult)); + return false; + } + + glBlitFramebuffer(srcBox.x, srcBox.y, srcBox.x + srcBox.width, srcBox.y + srcBox.height, dstPos.x, dstPos.y, dstPos.x + srcBox.width, dstPos.y + srcBox.height, GL_COLOR_BUFFER_BIT, ToOpenGL(filter)); + return true; + } + bool Context::ClearErrorStack() const { assert(GetCurrentContext() == this); while (glGetError() != GL_NO_ERROR); + m_didCollectErrors = false; + m_hadAnyError = false; + return true; } + bool Context::CopyTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Vector3ui& dstPos) const + { + // Use glCopyImageSubData if available + if (glCopyImageSubData && false) + { + GLuint srcImage = source.GetObjectId(); + GLenum srcTarget = ToOpenGL(source.GetTarget()); + + GLuint dstImage = destination.GetObjectId(); + GLenum dstTarget = ToOpenGL(destination.GetTarget()); + + glCopyImageSubData(srcImage, srcTarget, 0, GLint(srcBox.x), GLint(srcBox.y), GLint(srcBox.z), dstImage, dstTarget, 0, GLint(dstPos.x), GLint(dstPos.y), GLint(dstPos.z), GLsizei(srcBox.width), GLsizei(srcBox.height), GLsizei(srcBox.depth)); + return true; + } + else + { + // If glCopyImageSubData is not available, fallback to framebuffer blit + return BlitTexture(source, destination, srcBox, dstPos, SamplerFilter::Nearest); + } + } + bool Context::Initialize(const ContextParams& params) { if (!SetCurrentContext(this)) @@ -397,8 +470,8 @@ namespace Nz::GL glGetIntegerv(GL_VIEWPORT, res.data()); m_state.viewport = { res[0], res[1], res[2], res[3] }; - m_state.renderStates.frontFace = FrontFace::CounterClockwise; //< OpenGL default front face is counter-clockwise m_state.renderStates.depthCompare = RendererComparison::Less; //< OpenGL default depth mode is GL_LESS + m_state.renderStates.frontFace = FrontFace::CounterClockwise; //< OpenGL default front face is counter-clockwise EnableVerticalSync(false); @@ -419,6 +492,9 @@ namespace Nz::GL NazaraError("OpenGL error: " + TranslateOpenGLError(lastError)); } + m_didCollectErrors = true; + m_hadAnyError = hasAnyError; + return hasAnyError; } @@ -707,6 +783,7 @@ namespace Nz::GL void Context::OnContextRelease() { + m_blitFramebuffers.reset(); m_vaoCache.Clear(); } @@ -840,4 +917,22 @@ namespace Nz::GL NazaraNotice(ss.str()); } + + bool Context::InitializeBlitFramebuffers() const + { + m_blitFramebuffers = std::make_unique(); + if (!m_blitFramebuffers->drawFBO.Create(*this)) + { + NazaraError("failed to initialize draw FBO"); + return false; + } + + if (!m_blitFramebuffers->readFBO.Create(*this)) + { + NazaraError("failed to initialize read FBO"); + return false; + } + + return true; + } }