OpenGLRenderer: Add debug wrapper (which handle OpenGL errors)
This commit is contained in:
parent
df33262ab4
commit
6848ff8b34
|
|
@ -17,6 +17,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#define NAZARA_OPENGLRENDERER_DEBUG 1
|
||||||
|
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
class OpenGLDevice;
|
class OpenGLDevice;
|
||||||
|
|
@ -93,6 +95,9 @@ namespace Nz::GL
|
||||||
|
|
||||||
class NAZARA_OPENGLRENDERER_API Context
|
class NAZARA_OPENGLRENDERER_API Context
|
||||||
{
|
{
|
||||||
|
struct SymbolLoader;
|
||||||
|
friend SymbolLoader;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Context(const OpenGLDevice* device);
|
inline Context(const OpenGLDevice* device);
|
||||||
virtual ~Context();
|
virtual ~Context();
|
||||||
|
|
@ -107,6 +112,8 @@ 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 ClearErrorStack() const;
|
||||||
|
|
||||||
virtual void EnableVerticalSync(bool enabled) = 0;
|
virtual void EnableVerticalSync(bool enabled) = 0;
|
||||||
|
|
||||||
inline const OpenGLDevice* GetDevice() const;
|
inline const OpenGLDevice* GetDevice() const;
|
||||||
|
|
@ -125,6 +132,8 @@ namespace Nz::GL
|
||||||
inline void NotifyTextureDestruction(GLuint texture) const;
|
inline void NotifyTextureDestruction(GLuint texture) const;
|
||||||
inline void NotifyVertexArrayDestruction(GLuint vao) const;
|
inline void NotifyVertexArrayDestruction(GLuint vao) const;
|
||||||
|
|
||||||
|
bool ProcessErrorStack() const;
|
||||||
|
|
||||||
void SetCurrentTextureUnit(UInt32 textureUnit) const;
|
void SetCurrentTextureUnit(UInt32 textureUnit) const;
|
||||||
void SetScissorBox(GLint x, GLint y, GLsizei width, GLsizei height) const;
|
void SetScissorBox(GLint x, GLint y, GLsizei width, GLsizei height) const;
|
||||||
void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height) const;
|
void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height) const;
|
||||||
|
|
@ -133,7 +142,11 @@ namespace Nz::GL
|
||||||
|
|
||||||
void UpdateStates(const RenderStates& renderStates) const;
|
void UpdateStates(const RenderStates& renderStates) const;
|
||||||
|
|
||||||
|
#if NAZARA_OPENGLRENDERER_DEBUG
|
||||||
|
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) std::function<std::remove_pointer_t<sig>> name;
|
||||||
|
#else
|
||||||
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) sig name = nullptr;
|
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) sig name = nullptr;
|
||||||
|
#endif
|
||||||
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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
|
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
|
||||||
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
||||||
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
|
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
|
||||||
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
||||||
|
|
@ -17,6 +18,88 @@ namespace Nz::GL
|
||||||
{
|
{
|
||||||
thread_local const Context* s_currentContext = nullptr;
|
thread_local const Context* s_currentContext = nullptr;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template<typename>
|
||||||
|
struct GLWrapper;
|
||||||
|
|
||||||
|
template<typename Ret, typename... Args>
|
||||||
|
struct GLWrapper<Ret(Args...)>
|
||||||
|
{
|
||||||
|
template<typename FuncType>
|
||||||
|
static auto WrapErrorHandling(FuncType funcPtr)
|
||||||
|
{
|
||||||
|
return [funcPtr](Args&&... args) -> Ret
|
||||||
|
{
|
||||||
|
const Context* context = s_currentContext; //< pay TLS cost once
|
||||||
|
assert(context);
|
||||||
|
|
||||||
|
context->ClearErrorStack();
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<Ret, void>)
|
||||||
|
{
|
||||||
|
funcPtr(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
context->ProcessErrorStack();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Ret r = funcPtr(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
context->ProcessErrorStack();
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context::SymbolLoader
|
||||||
|
{
|
||||||
|
SymbolLoader(Context& parent) :
|
||||||
|
context(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FuncType, typename Func>
|
||||||
|
bool Load(Func& func, const char* funcName, bool mandatory, bool implementFallback = true)
|
||||||
|
{
|
||||||
|
FuncType funcPtr = LoadRaw<FuncType>(funcName);
|
||||||
|
if (funcPtr)
|
||||||
|
{
|
||||||
|
#if NAZARA_OPENGLRENDERER_DEBUG
|
||||||
|
if (std::strcmp(funcName, "glGetError") != 0) //< Prevent infinite recursion
|
||||||
|
func = GLWrapper<std::remove_pointer_t<FuncType>>::template WrapErrorHandling(funcPtr);
|
||||||
|
else
|
||||||
|
func = funcPtr;
|
||||||
|
#else
|
||||||
|
func = funcPtr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!func)
|
||||||
|
{
|
||||||
|
if (!implementFallback || (!context.ImplementFallback(funcName) && !func)) //< double-check
|
||||||
|
{
|
||||||
|
if (mandatory)
|
||||||
|
throw std::runtime_error("failed to load core function " + std::string(funcName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FuncType>
|
||||||
|
FuncType LoadRaw(const char* funcName)
|
||||||
|
{
|
||||||
|
const Loader& loader = context.GetLoader();
|
||||||
|
return reinterpret_cast<FuncType>(loader.LoadFunction(funcName));
|
||||||
|
}
|
||||||
|
|
||||||
|
Context& context;
|
||||||
|
};
|
||||||
|
|
||||||
Context::~Context()
|
Context::~Context()
|
||||||
{
|
{
|
||||||
if (m_device)
|
if (m_device)
|
||||||
|
|
@ -145,6 +228,15 @@ namespace Nz::GL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Context::ClearErrorStack() const
|
||||||
|
{
|
||||||
|
assert(GetCurrentContext() == this);
|
||||||
|
|
||||||
|
while (glGetError() != GL_NO_ERROR);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Context::Initialize(const ContextParams& params)
|
bool Context::Initialize(const ContextParams& params)
|
||||||
{
|
{
|
||||||
if (!SetCurrentContext(this))
|
if (!SetCurrentContext(this))
|
||||||
|
|
@ -153,18 +245,11 @@ namespace Nz::GL
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Loader& loader = GetLoader();
|
SymbolLoader loader(*this);
|
||||||
|
|
||||||
auto LoadSymbol = [&](auto& func, const char* funcName, bool mandatory)
|
|
||||||
{
|
|
||||||
func = reinterpret_cast<std::decay_t<decltype(func)>>(loader.LoadFunction(funcName));
|
|
||||||
if (!func && !ImplementFallback(funcName) && !func && mandatory) //< Not a mistake
|
|
||||||
throw std::runtime_error("failed to load core function " + std::string(funcName));
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(name, #name, true);
|
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) loader.Load<sig>(name, #name, true);
|
||||||
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) //< Do nothing
|
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) //< Do nothing
|
||||||
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC)
|
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC)
|
||||||
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
|
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
|
||||||
|
|
@ -214,7 +299,7 @@ namespace Nz::GL
|
||||||
m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB;
|
m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB;
|
||||||
|
|
||||||
#define NAZARA_OPENGLRENDERER_FUNC(name, sig)
|
#define NAZARA_OPENGLRENDERER_FUNC(name, sig)
|
||||||
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) LoadSymbol(name, #name, false);
|
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) loader.Load<sig>(name, #name, false);
|
||||||
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC)
|
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC)
|
||||||
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
|
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
|
||||||
#undef NAZARA_OPENGLRENDERER_FUNC
|
#undef NAZARA_OPENGLRENDERER_FUNC
|
||||||
|
|
@ -288,6 +373,58 @@ namespace Nz::GL
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Context::ProcessErrorStack() const
|
||||||
|
{
|
||||||
|
assert(GetCurrentContext() == this);
|
||||||
|
|
||||||
|
bool hasAnyError = false;
|
||||||
|
|
||||||
|
GLuint lastError;
|
||||||
|
while ((lastError = glGetError()) != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
hasAnyError = true;
|
||||||
|
|
||||||
|
switch (lastError)
|
||||||
|
{
|
||||||
|
// OpenGL/OpenGL ES error codes
|
||||||
|
case GL_INVALID_ENUM:
|
||||||
|
NazaraError("OpenGL error: an unacceptable value is specified for an enumerated argument");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_INVALID_VALUE:
|
||||||
|
NazaraError("OpenGL error: a numeric argument is out of range");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_INVALID_OPERATION:
|
||||||
|
NazaraError("OpenGL error: the specified operation is not allowed in the current state");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||||
|
NazaraError("OpenGL error: the framebuffer object is not complete");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_OUT_OF_MEMORY:
|
||||||
|
NazaraError("OpenGL error: there is not enough memory left to execute the command");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// OpenGL error codes
|
||||||
|
case GL_STACK_UNDERFLOW:
|
||||||
|
NazaraError("OpenGL error: an attempt has been made to perform an operation that would cause an internal stack to underflow.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_STACK_OVERFLOW:
|
||||||
|
NazaraError("OpenGL error: an attempt has been made to perform an operation that would cause an internal stack to overflow.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
NazaraError("OpenGL error: an unknown error was reported (code: " + std::to_string(lastError) + ")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasAnyError;
|
||||||
|
}
|
||||||
|
|
||||||
void Context::SetCurrentTextureUnit(UInt32 textureUnit) const
|
void Context::SetCurrentTextureUnit(UInt32 textureUnit) const
|
||||||
{
|
{
|
||||||
if (m_state.currentTextureUnit != textureUnit)
|
if (m_state.currentTextureUnit != textureUnit)
|
||||||
|
|
@ -524,18 +661,12 @@ namespace Nz::GL
|
||||||
|
|
||||||
bool Context::ImplementFallback(const std::string_view& function)
|
bool Context::ImplementFallback(const std::string_view& function)
|
||||||
{
|
{
|
||||||
const Loader& loader = GetLoader();
|
SymbolLoader loader(*this);
|
||||||
|
|
||||||
auto LoadSymbol = [&](auto& func, const char* funcName) -> bool
|
|
||||||
{
|
|
||||||
func = reinterpret_cast<std::decay_t<decltype(func)>>(loader.LoadFunction(funcName));
|
|
||||||
return func;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (function == "glDebugMessageCallback")
|
if (function == "glDebugMessageCallback")
|
||||||
{
|
{
|
||||||
if (!LoadSymbol(glDebugMessageCallback, "glDebugMessageCallbackARB"))
|
if (!loader.Load<PFNGLDEBUGMESSAGECALLBACKPROC>(glDebugMessageCallback, "glDebugMessageCallbackARB", false, false))
|
||||||
return LoadSymbol(glDebugMessageCallback, "DebugMessageCallbackAMD");
|
return loader.Load<PFNGLDEBUGMESSAGECALLBACKPROC>(glDebugMessageCallback, "DebugMessageCallbackAMD", false, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue