diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index 16e0a8265..ce41757ca 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -17,6 +17,8 @@ #include #include +#define NAZARA_OPENGLRENDERER_DEBUG 1 + namespace Nz { class OpenGLDevice; @@ -93,6 +95,9 @@ namespace Nz::GL class NAZARA_OPENGLRENDERER_API Context { + struct SymbolLoader; + friend SymbolLoader; + public: inline Context(const OpenGLDevice* device); virtual ~Context(); @@ -107,6 +112,8 @@ namespace Nz::GL void BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const; void BindVertexArray(GLuint vertexArray, bool force = false) const; + bool ClearErrorStack() const; + virtual void EnableVerticalSync(bool enabled) = 0; inline const OpenGLDevice* GetDevice() const; @@ -125,6 +132,8 @@ namespace Nz::GL inline void NotifyTextureDestruction(GLuint texture) const; inline void NotifyVertexArrayDestruction(GLuint vao) const; + bool ProcessErrorStack() const; + void SetCurrentTextureUnit(UInt32 textureUnit) const; void SetScissorBox(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; +#if NAZARA_OPENGLRENDERER_DEBUG +#define NAZARA_OPENGLRENDERER_FUNC(name, sig) std::function> name; +#else #define NAZARA_OPENGLRENDERER_FUNC(name, sig) sig name = nullptr; +#endif NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_FUNC) #undef NAZARA_OPENGLRENDERER_FUNC diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 6ed671a76..5ef0360c7 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,88 @@ namespace Nz::GL { thread_local const Context* s_currentContext = nullptr; + namespace + { + template + struct GLWrapper; + + template + struct GLWrapper + { + template + 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) + { + funcPtr(std::forward(args)...); + + context->ProcessErrorStack(); + } + else + { + Ret r = funcPtr(std::forward(args)...); + + context->ProcessErrorStack(); + + return r; + } + }; + } + }; + } + + struct Context::SymbolLoader + { + SymbolLoader(Context& parent) : + context(parent) + { + } + + template + bool Load(Func& func, const char* funcName, bool mandatory, bool implementFallback = true) + { + FuncType funcPtr = LoadRaw(funcName); + if (funcPtr) + { +#if NAZARA_OPENGLRENDERER_DEBUG + if (std::strcmp(funcName, "glGetError") != 0) //< Prevent infinite recursion + func = GLWrapper>::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 + FuncType LoadRaw(const char* funcName) + { + const Loader& loader = context.GetLoader(); + return reinterpret_cast(loader.LoadFunction(funcName)); + } + + Context& context; + }; + Context::~Context() { 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) { if (!SetCurrentContext(this)) @@ -153,18 +245,11 @@ namespace Nz::GL return false; } - const Loader& loader = GetLoader(); - - auto LoadSymbol = [&](auto& func, const char* funcName, bool mandatory) - { - func = reinterpret_cast>(loader.LoadFunction(funcName)); - if (!func && !ImplementFallback(funcName) && !func && mandatory) //< Not a mistake - throw std::runtime_error("failed to load core function " + std::string(funcName)); - }; + SymbolLoader loader(*this); try { -#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(name, #name, true); +#define NAZARA_OPENGLRENDERER_FUNC(name, sig) loader.Load(name, #name, true); #define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) //< Do nothing NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC) #undef NAZARA_OPENGLRENDERER_EXT_FUNC @@ -214,7 +299,7 @@ namespace Nz::GL m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB; #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(name, #name, false); NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC) #undef NAZARA_OPENGLRENDERER_EXT_FUNC #undef NAZARA_OPENGLRENDERER_FUNC @@ -288,6 +373,58 @@ namespace Nz::GL 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 { if (m_state.currentTextureUnit != textureUnit) @@ -524,18 +661,12 @@ namespace Nz::GL bool Context::ImplementFallback(const std::string_view& function) { - const Loader& loader = GetLoader(); - - auto LoadSymbol = [&](auto& func, const char* funcName) -> bool - { - func = reinterpret_cast>(loader.LoadFunction(funcName)); - return func; - }; + SymbolLoader loader(*this); if (function == "glDebugMessageCallback") { - if (!LoadSymbol(glDebugMessageCallback, "glDebugMessageCallbackARB")) - return LoadSymbol(glDebugMessageCallback, "DebugMessageCallbackAMD"); + if (!loader.Load(glDebugMessageCallback, "glDebugMessageCallbackARB", false, false)) + return loader.Load(glDebugMessageCallback, "DebugMessageCallbackAMD", false, false); return true; }