OpenGLRenderer: Implement texture blit/copy if glCopyImageSubData is not supported
This commit is contained in:
parent
4933a389a2
commit
7ab4d91900
|
|
@ -26,6 +26,8 @@ namespace Nz
|
||||||
|
|
||||||
namespace Nz::GL
|
namespace Nz::GL
|
||||||
{
|
{
|
||||||
|
class Texture;
|
||||||
|
|
||||||
enum class BufferTarget
|
enum class BufferTarget
|
||||||
{
|
{
|
||||||
Array,
|
Array,
|
||||||
|
|
@ -107,7 +109,9 @@ namespace Nz::GL
|
||||||
friend SymbolLoader;
|
friend SymbolLoader;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Context(const OpenGLDevice* device);
|
Context(const OpenGLDevice* device);
|
||||||
|
Context(const Context&) = delete;
|
||||||
|
Context(Context&&) = delete;
|
||||||
virtual ~Context();
|
virtual ~Context();
|
||||||
|
|
||||||
void BindBuffer(BufferTarget target, GLuint buffer, bool force = false) const;
|
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 BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const;
|
||||||
void BindVertexArray(GLuint vertexArray, bool force = false) 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 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;
|
virtual void EnableVerticalSync(bool enabled) = 0;
|
||||||
|
|
||||||
inline const OpenGLDevice* GetDevice() const;
|
inline const OpenGLDevice* GetDevice() const;
|
||||||
|
|
@ -160,6 +170,9 @@ namespace Nz::GL
|
||||||
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_FUNC)
|
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_FUNC)
|
||||||
#undef NAZARA_OPENGLRENDERER_FUNC
|
#undef NAZARA_OPENGLRENDERER_FUNC
|
||||||
|
|
||||||
|
Context& operator=(const Context&) = delete;
|
||||||
|
Context& operator=(Context&&) = delete;
|
||||||
|
|
||||||
static const Context* GetCurrentContext();
|
static const Context* GetCurrentContext();
|
||||||
static bool SetCurrentContext(const Context* context);
|
static bool SetCurrentContext(const Context* context);
|
||||||
|
|
||||||
|
|
@ -176,7 +189,8 @@ namespace Nz::GL
|
||||||
ContextParams m_params;
|
ContextParams m_params;
|
||||||
|
|
||||||
private:
|
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
|
enum class FunctionIndex
|
||||||
{
|
{
|
||||||
|
|
@ -187,6 +201,8 @@ namespace Nz::GL
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BlitFramebuffers;
|
||||||
|
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
struct Box
|
struct Box
|
||||||
|
|
@ -223,10 +239,13 @@ namespace Nz::GL
|
||||||
|
|
||||||
std::array<ExtensionStatus, UnderlyingCast(Extension::Max) + 1> m_extensionStatus;
|
std::array<ExtensionStatus, UnderlyingCast(Extension::Max) + 1> m_extensionStatus;
|
||||||
std::array<GLFunction, UnderlyingCast(FunctionIndex::Count)> m_originalFunctionPointer;
|
std::array<GLFunction, UnderlyingCast(FunctionIndex::Count)> m_originalFunctionPointer;
|
||||||
|
mutable std::unique_ptr<BlitFramebuffers> m_blitFramebuffers;
|
||||||
std::unordered_set<std::string> m_supportedExtensions;
|
std::unordered_set<std::string> m_supportedExtensions;
|
||||||
OpenGLVaoCache m_vaoCache;
|
OpenGLVaoCache m_vaoCache;
|
||||||
const OpenGLDevice* m_device;
|
const OpenGLDevice* m_device;
|
||||||
mutable State m_state;
|
mutable State m_state;
|
||||||
|
mutable bool m_didCollectErrors;
|
||||||
|
mutable bool m_hadAnyError;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@
|
||||||
|
|
||||||
namespace Nz::GL
|
namespace Nz::GL
|
||||||
{
|
{
|
||||||
inline Context::Context(const OpenGLDevice* device) :
|
inline bool Context::DidLastCallSucceed() const
|
||||||
m_vaoCache(*this),
|
|
||||||
m_device(device)
|
|
||||||
{
|
{
|
||||||
|
if (!m_didCollectErrors)
|
||||||
|
ProcessErrorStack();
|
||||||
|
|
||||||
|
return !m_hadAnyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const OpenGLDevice* Context::GetDevice() const
|
inline const OpenGLDevice* Context::GetDevice() const
|
||||||
|
|
|
||||||
|
|
@ -64,25 +64,7 @@ namespace Nz
|
||||||
const OpenGLTexture& glTexture = static_cast<const OpenGLTexture&>(source);
|
const OpenGLTexture& glTexture = static_cast<const OpenGLTexture&>(source);
|
||||||
|
|
||||||
const GL::Context& context = m_texture.EnsureDeviceContext();
|
const GL::Context& context = m_texture.EnsureDeviceContext();
|
||||||
|
return context.CopyTexture(glTexture.GetTexture(), m_texture, srcBox, dstPos);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat OpenGLTexture::GetFormat() const
|
PixelFormat OpenGLTexture::GetFormat() const
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,14 @@
|
||||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
||||||
|
#include <Nazara/Core/CallOnExit.hpp>
|
||||||
#include <Nazara/Core/Error.hpp>
|
#include <Nazara/Core/Error.hpp>
|
||||||
#include <Nazara/Core/Log.hpp>
|
#include <Nazara/Core/Log.hpp>
|
||||||
#include <Nazara/Core/StringExt.hpp>
|
#include <Nazara/Core/StringExt.hpp>
|
||||||
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
|
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
|
||||||
|
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
|
||||||
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
||||||
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
|
#include <Nazara/OpenGLRenderer/Wrapper/Framebuffer.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
@ -56,6 +58,12 @@ namespace Nz::GL
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Context::BlitFramebuffers
|
||||||
|
{
|
||||||
|
GL::Framebuffer drawFBO;
|
||||||
|
GL::Framebuffer readFBO;
|
||||||
|
};
|
||||||
|
|
||||||
struct Context::SymbolLoader
|
struct Context::SymbolLoader
|
||||||
{
|
{
|
||||||
SymbolLoader(Context& parent) :
|
SymbolLoader(Context& parent) :
|
||||||
|
|
@ -99,6 +107,15 @@ namespace Nz::GL
|
||||||
Context& context;
|
Context& context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Context::Context(const OpenGLDevice* device) :
|
||||||
|
m_vaoCache(*this),
|
||||||
|
m_device(device),
|
||||||
|
m_didCollectErrors(false),
|
||||||
|
m_hadAnyError(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Context::~Context()
|
Context::~Context()
|
||||||
{
|
{
|
||||||
if (m_device)
|
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
|
bool Context::ClearErrorStack() const
|
||||||
{
|
{
|
||||||
assert(GetCurrentContext() == this);
|
assert(GetCurrentContext() == this);
|
||||||
|
|
||||||
while (glGetError() != GL_NO_ERROR);
|
while (glGetError() != GL_NO_ERROR);
|
||||||
|
|
||||||
|
m_didCollectErrors = false;
|
||||||
|
m_hadAnyError = false;
|
||||||
|
|
||||||
return true;
|
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)
|
bool Context::Initialize(const ContextParams& params)
|
||||||
{
|
{
|
||||||
if (!SetCurrentContext(this))
|
if (!SetCurrentContext(this))
|
||||||
|
|
@ -397,8 +470,8 @@ namespace Nz::GL
|
||||||
glGetIntegerv(GL_VIEWPORT, res.data());
|
glGetIntegerv(GL_VIEWPORT, res.data());
|
||||||
m_state.viewport = { res[0], res[1], res[2], res[3] };
|
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.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);
|
EnableVerticalSync(false);
|
||||||
|
|
||||||
|
|
@ -419,6 +492,9 @@ namespace Nz::GL
|
||||||
NazaraError("OpenGL error: " + TranslateOpenGLError(lastError));
|
NazaraError("OpenGL error: " + TranslateOpenGLError(lastError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_didCollectErrors = true;
|
||||||
|
m_hadAnyError = hasAnyError;
|
||||||
|
|
||||||
return hasAnyError;
|
return hasAnyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -707,6 +783,7 @@ namespace Nz::GL
|
||||||
|
|
||||||
void Context::OnContextRelease()
|
void Context::OnContextRelease()
|
||||||
{
|
{
|
||||||
|
m_blitFramebuffers.reset();
|
||||||
m_vaoCache.Clear();
|
m_vaoCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -840,4 +917,22 @@ namespace Nz::GL
|
||||||
|
|
||||||
NazaraNotice(ss.str());
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue