OpenGLRenderer: Implement texture blit/copy if glCopyImageSubData is not supported

This commit is contained in:
Jérôme Leclercq 2021-09-21 17:39:24 +02:00
parent 4933a389a2
commit 7ab4d91900
4 changed files with 124 additions and 26 deletions

View File

@ -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<ExtensionStatus, UnderlyingCast(Extension::Max) + 1> m_extensionStatus;
std::array<GLFunction, UnderlyingCast(FunctionIndex::Count)> m_originalFunctionPointer;
mutable std::unique_ptr<BlitFramebuffers> m_blitFramebuffers;
std::unordered_set<std::string> m_supportedExtensions;
OpenGLVaoCache m_vaoCache;
const OpenGLDevice* m_device;
mutable State m_state;
mutable bool m_didCollectErrors;
mutable bool m_hadAnyError;
};
}

View File

@ -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

View File

@ -64,25 +64,7 @@ namespace Nz
const OpenGLTexture& glTexture = static_cast<const OpenGLTexture&>(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

View File

@ -3,12 +3,14 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Framebuffer.hpp>
#include <cstring>
#include <sstream>
#include <stdexcept>
@ -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<BlitFramebuffers>();
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;
}
}