647 lines
18 KiB
C++
647 lines
18 KiB
C++
// Copyright (C) 2020 Jérôme Leclercq
|
|
// This file is part of the "Nazara Engine - OpenGL Renderer"
|
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
|
|
|
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
|
#include <Nazara/Core/Error.hpp>
|
|
#include <Nazara/Core/Log.hpp>
|
|
#include <Nazara/Core/StringExt.hpp>
|
|
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
|
|
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
|
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
|
|
|
namespace Nz::GL
|
|
{
|
|
thread_local const Context* s_currentContext = nullptr;
|
|
|
|
Context::~Context()
|
|
{
|
|
if (m_device)
|
|
m_device->NotifyContextDestruction(*this);
|
|
}
|
|
|
|
void Context::BindBuffer(BufferTarget target, GLuint buffer, bool force) const
|
|
{
|
|
if (m_state.bufferTargets[UnderlyingCast(target)] != buffer || force)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glBindBuffer(ToOpenGL(target), buffer);
|
|
|
|
m_state.bufferTargets[UnderlyingCast(target)] = buffer;
|
|
}
|
|
}
|
|
|
|
void Context::BindFramebuffer(GLuint fbo) const
|
|
{
|
|
if (m_state.boundDrawFBO != fbo || m_state.boundReadFBO != fbo)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
m_state.boundDrawFBO = fbo;
|
|
m_state.boundReadFBO = fbo;
|
|
}
|
|
}
|
|
|
|
void Context::BindFramebuffer(FramebufferTarget target, GLuint fbo) const
|
|
{
|
|
auto& currentFbo = (target == FramebufferTarget::Draw) ? m_state.boundDrawFBO : m_state.boundReadFBO;
|
|
if (currentFbo != fbo)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glBindFramebuffer((target == FramebufferTarget::Draw) ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, fbo);
|
|
currentFbo = fbo;
|
|
}
|
|
}
|
|
|
|
void Context::BindProgram(GLuint program) const
|
|
{
|
|
if (m_state.boundProgram != program)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glUseProgram(program);
|
|
m_state.boundProgram = program;
|
|
}
|
|
}
|
|
|
|
void Context::BindSampler(UInt32 textureUnit, GLuint sampler) const
|
|
{
|
|
if (textureUnit >= m_state.textureUnits.size())
|
|
throw std::runtime_error("unsupported texture unit #" + std::to_string(textureUnit));
|
|
|
|
auto& unit = m_state.textureUnits[textureUnit];
|
|
if (unit.sampler != sampler)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glBindSampler(textureUnit, sampler);
|
|
unit.sampler = sampler;
|
|
}
|
|
}
|
|
|
|
void Context::BindTexture(TextureTarget target, GLuint texture) const
|
|
{
|
|
BindTexture(m_state.currentTextureUnit, target, texture);
|
|
}
|
|
|
|
void Context::BindTexture(UInt32 textureUnit, TextureTarget target, GLuint texture) const
|
|
{
|
|
if (textureUnit >= m_state.textureUnits.size())
|
|
throw std::runtime_error("unsupported texture unit #" + std::to_string(textureUnit));
|
|
|
|
auto& unit = m_state.textureUnits[textureUnit];
|
|
if (unit.textureTargets[UnderlyingCast(target)] != texture)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
SetCurrentTextureUnit(textureUnit);
|
|
|
|
glBindTexture(ToOpenGL(target), texture);
|
|
|
|
unit.textureTargets[UnderlyingCast(target)] = texture;
|
|
}
|
|
}
|
|
|
|
void Context::BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const
|
|
{
|
|
if (uboUnit >= m_state.uboUnits.size())
|
|
throw std::runtime_error("unsupported uniform buffer unit #" + std::to_string(uboUnit));
|
|
|
|
auto& unit = m_state.uboUnits[uboUnit];
|
|
if (unit.buffer != buffer || unit.offset != offset || unit.size != size)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glBindBufferRange(GL_UNIFORM_BUFFER, uboUnit, buffer, offset, size);
|
|
|
|
unit.buffer = buffer;
|
|
unit.offset = offset;
|
|
unit.size = size;
|
|
}
|
|
}
|
|
|
|
void Context::BindVertexArray(GLuint vertexArray, bool force) const
|
|
{
|
|
if (m_state.boundVertexArray != vertexArray || force)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glBindVertexArray(vertexArray);
|
|
m_state.boundVertexArray = vertexArray;
|
|
}
|
|
}
|
|
|
|
bool Context::Initialize(const ContextParams& params)
|
|
{
|
|
if (!Activate())
|
|
{
|
|
NazaraError("failed to activate context");
|
|
return false;
|
|
}
|
|
|
|
const Loader& loader = GetLoader();
|
|
|
|
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
|
|
{
|
|
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(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
|
|
#undef NAZARA_OPENGLRENDERER_FUNC
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
NazaraError(e.what());
|
|
return false;
|
|
}
|
|
|
|
// Retrieve OpenGL version
|
|
auto DecodeDigit = [](char c) -> int
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
return c - '0';
|
|
else
|
|
return -1;
|
|
};
|
|
|
|
GLint majorVersion = 0;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
|
|
|
|
GLint minorVersion = 0;
|
|
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
|
|
|
|
m_params.glMajorVersion = majorVersion;
|
|
m_params.glMinorVersion = minorVersion;
|
|
|
|
// Load extensions
|
|
GLint extensionCount = 0;
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
|
|
|
|
for (GLint i = 0; i < extensionCount; ++i)
|
|
m_supportedExtensions.emplace(reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)));
|
|
|
|
m_extensionStatus.fill(ExtensionStatus::NotSupported);
|
|
|
|
// TextureFilterAnisotropic
|
|
if (m_supportedExtensions.count("GL_ARB_texture_filter_anisotropic"))
|
|
m_extensionStatus[UnderlyingCast(Extension::TextureFilterAnisotropic)] = ExtensionStatus::ARB;
|
|
else if (m_supportedExtensions.count("GL_EXT_texture_filter_anisotropic"))
|
|
m_extensionStatus[UnderlyingCast(Extension::TextureFilterAnisotropic)] = ExtensionStatus::EXT;
|
|
|
|
// SpirV
|
|
if (m_supportedExtensions.count("GL_ARB_gl_spirv"))
|
|
m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB;
|
|
|
|
#define NAZARA_OPENGLRENDERER_FUNC(name, sig)
|
|
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) LoadSymbol(name, #name, false);
|
|
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_FUNC)
|
|
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
|
|
#undef NAZARA_OPENGLRENDERER_FUNC
|
|
|
|
// If we requested an OpenGL ES context but cannot create one, check for some compatibility extensions
|
|
if (params.type == ContextType::OpenGL_ES && m_params.type != params.type)
|
|
{
|
|
if (m_supportedExtensions.count("GL_ARB_ES3_2_compatibility"))
|
|
{
|
|
m_params.type = ContextType::OpenGL_ES;
|
|
m_params.glMajorVersion = 3;
|
|
m_params.glMinorVersion = 2;
|
|
}
|
|
else if (m_supportedExtensions.count("GL_ARB_ES3_1_compatibility"))
|
|
{
|
|
m_params.type = ContextType::OpenGL_ES;
|
|
m_params.glMajorVersion = 3;
|
|
m_params.glMinorVersion = 1;
|
|
}
|
|
else if (m_supportedExtensions.count("GL_ARB_ES3_compatibility"))
|
|
{
|
|
m_params.type = ContextType::OpenGL_ES;
|
|
m_params.glMajorVersion = 3;
|
|
m_params.glMinorVersion = 0;
|
|
}
|
|
else
|
|
NazaraWarning("desktop support for OpenGL ES is missing, falling back to OpenGL...");
|
|
}
|
|
|
|
// Set debug callback (if supported)
|
|
if (glDebugMessageCallback)
|
|
{
|
|
glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
|
|
{
|
|
const Context* context = static_cast<const Context*>(userParam);
|
|
context->HandleDebugMessage(source, type, id, severity, length, message);
|
|
}, this);
|
|
}
|
|
|
|
GLint maxTextureUnits = -1;
|
|
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
|
|
if (maxTextureUnits < 32) //< OpenGL ES 3.0 requires at least 32 textures units
|
|
NazaraWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is " + std::to_string(maxTextureUnits) + ", >= 32 expected");
|
|
|
|
assert(maxTextureUnits > 0);
|
|
m_state.textureUnits.resize(maxTextureUnits);
|
|
|
|
GLint maxUniformBufferUnits = -1;
|
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferUnits);
|
|
if (maxUniformBufferUnits < 24) //< OpenGL ES 3.0 requires at least 24 uniform buffers units
|
|
NazaraWarning("GL_MAX_UNIFORM_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", >= 24 expected");
|
|
|
|
assert(maxUniformBufferUnits > 0);
|
|
m_state.uboUnits.resize(maxUniformBufferUnits);
|
|
|
|
std::array<GLint, 4> res;
|
|
|
|
glGetIntegerv(GL_SCISSOR_BOX, res.data());
|
|
m_state.scissorBox = { res[0], res[1], res[2], res[3] };
|
|
|
|
glGetIntegerv(GL_VIEWPORT, res.data());
|
|
m_state.viewport = { res[0], res[1], res[2], res[3] };
|
|
|
|
EnableVerticalSync(false);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Context::SetCurrentTextureUnit(UInt32 textureUnit) const
|
|
{
|
|
if (m_state.currentTextureUnit != textureUnit)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
|
m_state.currentTextureUnit = textureUnit;
|
|
}
|
|
}
|
|
|
|
void Context::SetScissorBox(GLint x, GLint y, GLsizei width, GLsizei height) const
|
|
{
|
|
if (m_state.scissorBox.x != x ||
|
|
m_state.scissorBox.y != y ||
|
|
m_state.scissorBox.width != width ||
|
|
m_state.scissorBox.height != height)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glScissor(x, y, width, height);
|
|
|
|
m_state.scissorBox.x = x;
|
|
m_state.scissorBox.y = y;
|
|
m_state.scissorBox.width = width;
|
|
m_state.scissorBox.height = height;
|
|
}
|
|
}
|
|
|
|
void Context::SetViewport(GLint x, GLint y, GLsizei width, GLsizei height) const
|
|
{
|
|
if (m_state.viewport.x != x ||
|
|
m_state.viewport.y != y ||
|
|
m_state.viewport.width != width ||
|
|
m_state.viewport.height != height)
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
glViewport(x, y, width, height);
|
|
|
|
m_state.viewport.x = x;
|
|
m_state.viewport.y = y;
|
|
m_state.viewport.width = width;
|
|
m_state.viewport.height = height;
|
|
}
|
|
}
|
|
|
|
void Context::UpdateStates(const RenderStates& renderStates) const
|
|
{
|
|
if (!SetCurrentContext(this))
|
|
throw std::runtime_error("failed to activate context");
|
|
|
|
if (renderStates.blending)
|
|
{
|
|
if (m_state.renderStates.dstBlend != renderStates.dstBlend ||
|
|
m_state.renderStates.srcBlend != renderStates.srcBlend)
|
|
{
|
|
glBlendFunc(ToOpenGL(renderStates.srcBlend), ToOpenGL(renderStates.dstBlend));
|
|
m_state.renderStates.dstBlend = renderStates.dstBlend;
|
|
m_state.renderStates.srcBlend = renderStates.srcBlend;
|
|
}
|
|
}
|
|
|
|
if (renderStates.depthBuffer)
|
|
{
|
|
if (m_state.renderStates.depthCompare != renderStates.depthCompare)
|
|
{
|
|
glDepthFunc(ToOpenGL(m_state.renderStates.depthCompare));
|
|
m_state.renderStates.depthCompare = renderStates.depthCompare;
|
|
}
|
|
|
|
if (m_state.renderStates.depthWrite != renderStates.depthWrite)
|
|
{
|
|
glDepthMask((renderStates.depthWrite) ? GL_TRUE : GL_FALSE);
|
|
m_state.renderStates.depthWrite = renderStates.depthWrite;
|
|
}
|
|
}
|
|
|
|
if (renderStates.faceCulling)
|
|
{
|
|
if (m_state.renderStates.cullingSide != renderStates.cullingSide)
|
|
{
|
|
glCullFace(ToOpenGL(m_state.renderStates.cullingSide));
|
|
m_state.renderStates.cullingSide = renderStates.cullingSide;
|
|
}
|
|
}
|
|
|
|
/*
|
|
TODO: Use glPolyonMode if available (OpenGL)
|
|
if (m_state.renderStates.faceFilling != renderStates.faceFilling)
|
|
{
|
|
glPolygonMode(GL_FRONT_AND_BACK, FaceFilling[states.faceFilling]);
|
|
m_state.renderStates.faceFilling = renderStates.faceFilling;
|
|
}*/
|
|
|
|
if (renderStates.stencilTest)
|
|
{
|
|
auto ApplyStencilStates = [&](bool front)
|
|
{
|
|
auto& currentStencilData = (front) ? m_state.renderStates.stencilFront : m_state.renderStates.stencilBack;
|
|
auto& newStencilData = (front) ? renderStates.stencilFront : renderStates.stencilBack;
|
|
|
|
if (currentStencilData.compare != newStencilData.compare ||
|
|
currentStencilData.reference != newStencilData.reference ||
|
|
currentStencilData.writeMask != newStencilData.writeMask)
|
|
{
|
|
glStencilFuncSeparate((front) ? GL_FRONT : GL_BACK, ToOpenGL(newStencilData.compare), newStencilData.reference, newStencilData.writeMask);
|
|
currentStencilData.compare = newStencilData.compare;
|
|
currentStencilData.reference = newStencilData.reference;
|
|
currentStencilData.writeMask = newStencilData.writeMask;
|
|
}
|
|
|
|
if (currentStencilData.depthFail != newStencilData.depthFail ||
|
|
currentStencilData.fail != newStencilData.fail ||
|
|
currentStencilData.pass != newStencilData.pass)
|
|
{
|
|
glStencilOpSeparate((front) ? GL_FRONT : GL_BACK, ToOpenGL(newStencilData.fail), ToOpenGL(newStencilData.depthFail), ToOpenGL(newStencilData.pass));
|
|
currentStencilData.depthFail = newStencilData.depthFail;
|
|
currentStencilData.fail = newStencilData.fail;
|
|
currentStencilData.pass = newStencilData.pass;
|
|
}
|
|
};
|
|
|
|
ApplyStencilStates(true);
|
|
ApplyStencilStates(false);
|
|
}
|
|
|
|
if (!NumberEquals(m_state.renderStates.lineWidth, renderStates.lineWidth, 0.001f))
|
|
{
|
|
glLineWidth(renderStates.lineWidth);
|
|
m_state.renderStates.lineWidth = renderStates.lineWidth;
|
|
}
|
|
|
|
/*if (!NumberEquals(m_state.renderStates.pointSize, renderStates.pointSize, 0.001f))
|
|
{
|
|
glPointSize(renderStates.pointSize);
|
|
m_state.renderStates.pointSize = renderStates.pointSize;
|
|
}*/
|
|
|
|
if (m_state.renderStates.blending != renderStates.blending)
|
|
{
|
|
if (renderStates.blending)
|
|
glEnable(GL_BLEND);
|
|
else
|
|
glDisable(GL_BLEND);
|
|
|
|
m_state.renderStates.blending = renderStates.blending;
|
|
}
|
|
|
|
if (m_state.renderStates.colorWrite != renderStates.colorWrite)
|
|
{
|
|
GLboolean param = (renderStates.colorWrite) ? GL_TRUE : GL_FALSE;
|
|
glColorMask(param, param, param, param);
|
|
|
|
m_state.renderStates.colorWrite = renderStates.colorWrite;
|
|
}
|
|
|
|
if (m_state.renderStates.depthBuffer != renderStates.depthBuffer)
|
|
{
|
|
if (renderStates.depthBuffer)
|
|
glEnable(GL_DEPTH_TEST);
|
|
else
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
m_state.renderStates.depthBuffer = renderStates.depthBuffer;
|
|
}
|
|
|
|
if (m_state.renderStates.faceCulling != renderStates.faceCulling)
|
|
{
|
|
if (renderStates.faceCulling)
|
|
glEnable(GL_CULL_FACE);
|
|
else
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
m_state.renderStates.faceCulling = renderStates.faceCulling;
|
|
}
|
|
|
|
if (m_state.renderStates.scissorTest != renderStates.scissorTest)
|
|
{
|
|
if (renderStates.scissorTest)
|
|
glEnable(GL_SCISSOR_TEST);
|
|
else
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
m_state.renderStates.scissorTest = renderStates.scissorTest;
|
|
}
|
|
|
|
if (m_state.renderStates.stencilTest != renderStates.stencilTest)
|
|
{
|
|
if (renderStates.stencilTest)
|
|
glEnable(GL_STENCIL_TEST);
|
|
else
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
m_state.renderStates.stencilTest = renderStates.stencilTest;
|
|
}
|
|
}
|
|
|
|
const Context* Context::GetCurrentContext()
|
|
{
|
|
return s_currentContext;
|
|
}
|
|
|
|
bool Context::SetCurrentContext(const Context* context)
|
|
{
|
|
const Context*& currentContext = s_currentContext; //< Pay TLS cost only once
|
|
if (currentContext == context)
|
|
return true;
|
|
|
|
if (currentContext)
|
|
{
|
|
currentContext->Desactivate();
|
|
currentContext = nullptr;
|
|
}
|
|
|
|
if (context)
|
|
{
|
|
if (!context->Activate())
|
|
return false;
|
|
|
|
currentContext = context;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Context::OnContextRelease()
|
|
{
|
|
m_vaoCache.Clear();
|
|
}
|
|
|
|
bool Context::ImplementFallback(const std::string_view& function)
|
|
{
|
|
const Loader& loader = GetLoader();
|
|
|
|
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 (!LoadSymbol(glDebugMessageCallback, "glDebugMessageCallbackARB"))
|
|
return LoadSymbol(glDebugMessageCallback, "DebugMessageCallbackAMD");
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Context::NotifyContextDestruction(Context* context)
|
|
{
|
|
const Context*& currentContext = s_currentContext; //< Pay TLS cost only once
|
|
if (currentContext == context)
|
|
currentContext = nullptr;
|
|
}
|
|
|
|
void Context::HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) const
|
|
{
|
|
std::stringstream ss;
|
|
ss << "OpenGL debug message (ID: 0x" << std::to_string(id) << "):\n";
|
|
ss << "Sent by context: " << this;
|
|
ss << "\n-Source: ";
|
|
switch (source)
|
|
{
|
|
case GL_DEBUG_SOURCE_API:
|
|
ss << "OpenGL API";
|
|
break;
|
|
|
|
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
|
ss << "Operating system";
|
|
break;
|
|
|
|
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
|
ss << "Shader compiler";
|
|
break;
|
|
|
|
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
|
ss << "Third party";
|
|
break;
|
|
|
|
case GL_DEBUG_SOURCE_APPLICATION:
|
|
ss << "Application";
|
|
break;
|
|
|
|
case GL_DEBUG_SOURCE_OTHER:
|
|
ss << "Other";
|
|
break;
|
|
|
|
default:
|
|
// Peut être rajouté par une extension
|
|
ss << "Unknown";
|
|
break;
|
|
}
|
|
ss << '\n';
|
|
|
|
ss << "-Type: ";
|
|
switch (type)
|
|
{
|
|
case GL_DEBUG_TYPE_ERROR:
|
|
ss << "Error";
|
|
break;
|
|
|
|
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
|
ss << "Deprecated behavior";
|
|
break;
|
|
|
|
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
|
ss << "Undefined behavior";
|
|
break;
|
|
|
|
case GL_DEBUG_TYPE_PORTABILITY:
|
|
ss << "Portability";
|
|
break;
|
|
|
|
case GL_DEBUG_TYPE_PERFORMANCE:
|
|
ss << "Performance";
|
|
break;
|
|
|
|
case GL_DEBUG_TYPE_OTHER:
|
|
ss << "Other";
|
|
break;
|
|
|
|
default:
|
|
// Peut être rajouté par une extension
|
|
ss << "Unknown";
|
|
break;
|
|
}
|
|
ss << '\n';
|
|
|
|
ss << "-Severity: ";
|
|
switch (severity)
|
|
{
|
|
case GL_DEBUG_SEVERITY_HIGH:
|
|
ss << "High";
|
|
break;
|
|
|
|
case GL_DEBUG_SEVERITY_MEDIUM:
|
|
ss << "Medium";
|
|
break;
|
|
|
|
case GL_DEBUG_SEVERITY_LOW:
|
|
ss << "Low";
|
|
break;
|
|
|
|
default:
|
|
ss << "Unknown";
|
|
break;
|
|
}
|
|
ss << '\n';
|
|
|
|
ss << "Message: " << std::string_view(message, length) << '\n';
|
|
|
|
NazaraNotice(ss.str());
|
|
}
|
|
}
|