diff --git a/include/Nazara/Renderer/Context.hpp b/include/Nazara/Renderer/Context.hpp index a7c0f5043..74a7cff1e 100644 --- a/include/Nazara/Renderer/Context.hpp +++ b/include/Nazara/Renderer/Context.hpp @@ -37,14 +37,20 @@ class NAZARA_API NzContext : public NzRefCounted ~NzContext(); bool Create(const NzContextParameters& parameters = NzContextParameters()); + void Destroy(); + void EnableVerticalSync(bool enabled); + const NzContextParameters& GetParameters() const; + bool IsActive() const; + bool SetActive(bool active) const; void SwapBuffers(); static bool EnsureContext(); + static const NzContext* GetCurrent(); static const NzContext* GetReference(); static const NzContext* GetThreadContext(); diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index 08079582a..ca0498f6d 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -22,6 +22,10 @@ #if defined(NAZARA_PLATFORM_WINDOWS) #include #elif defined(NAZARA_PLATFORM_LINUX) + namespace GLX + { + #include + } #include #endif @@ -331,8 +335,10 @@ NAZARA_API extern PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; NAZARA_API extern PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; NAZARA_API extern PFNWGLSWAPINTERVALEXTPROC wglSwapInterval; #elif defined(NAZARA_PLATFORM_LINUX) -NAZARA_API extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs; -NAZARA_API extern PFNGLXSWAPINTERVALSGIPROC glXSwapInterval; +NAZARA_API extern GLX::PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs; +NAZARA_API extern GLX::PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; +NAZARA_API extern GLX::PFNGLXSWAPINTERVALMESAPROC NzglXSwapIntervalMESA; +NAZARA_API extern GLX::PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; #endif #endif // NAZARA_RENDERER_OPENGL diff --git a/src/Nazara/Renderer/Context.cpp b/src/Nazara/Renderer/Context.cpp index 3066cd269..3149e2de9 100644 --- a/src/Nazara/Renderer/Context.cpp +++ b/src/Nazara/Renderer/Context.cpp @@ -15,7 +15,8 @@ #if defined(NAZARA_PLATFORM_WINDOWS) #include #elif defined(NAZARA_PLATFORM_LINUX) - #include + #include + #define CALLBACK #else #error Lack of implementation: Context #endif @@ -194,6 +195,19 @@ void NzContext::Destroy() } } +void NzContext::EnableVerticalSync(bool enabled) +{ + #ifdef NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("No context has been created"); + return; + } + #endif + + m_impl->EnableVerticalSync(enabled); +} + const NzContextParameters& NzContext::GetParameters() const { #ifdef NAZARA_RENDERER_SAFE diff --git a/src/Nazara/Renderer/GLX/ContextImpl.cpp b/src/Nazara/Renderer/GLX/ContextImpl.cpp new file mode 100644 index 000000000..967305d7d --- /dev/null +++ b/src/Nazara/Renderer/GLX/ContextImpl.cpp @@ -0,0 +1,297 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// Code inspiré de NeHe (Lesson1) et de la SFML par Laurent Gomila + +#include +#include +#include +#include +#include + +using namespace GLX; + +namespace +{ + Display* m_display; + int m_sharedDisplay = 0; + + bool ctxErrorOccurred = false; + int ctxErrorHandler( Display* /*dpy*/, XErrorEvent* /*ev*/ ) + { + ctxErrorOccurred = true; + return 0; + } +} + +NzContextImpl::NzContextImpl() : +m_colormap(0), +m_context(0), +m_window(0), +m_ownsWindow(false) +{ + if (m_sharedDisplay == 0) + m_display = XOpenDisplay(nullptr); + + ++m_sharedDisplay; +} + +NzContextImpl::~NzContextImpl() +{ + Destroy(); + + if (--m_sharedDisplay == 0) + { + XCloseDisplay(m_display); + m_display = nullptr; + } +} + +bool NzContextImpl::Activate() +{ + return glXMakeCurrent(m_display, m_window, m_context) == true; +} + +bool NzContextImpl::Create(NzContextParameters& parameters) +{ + // En cas d'exception, la ressource sera quand même libérée + NzCallOnExit onExit([this] () + { + Destroy(); + }); + + // Get a matching FB config + static int visual_attribs[] = + { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_BUFFER_SIZE, parameters.bitsPerPixel, + GLX_ALPHA_SIZE, (parameters.bitsPerPixel == 32) ? 8 : 0, + GLX_DEPTH_SIZE, parameters.depthBits, + GLX_STENCIL_SIZE, parameters.stencilBits, + GLX_DOUBLEBUFFER, True, + GLX_SAMPLE_BUFFERS, (parameters.antialiasingLevel > 0) ? True : False, + GLX_SAMPLES, parameters.antialiasingLevel, + None + }; + + int glx_major = 0; + int glx_minor = 0; + // FBConfigs were added in GLX version 1.3. + if (!glXQueryVersion(m_display, &glx_major, &glx_minor) || ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) + { + NazaraError("Invalid GLX version, version > 1.3 is required."); + return false; + } + + int fbcount; + GLXFBConfig* fbc = glXChooseFBConfig(m_display, XDefaultScreen(m_display), visual_attribs, &fbcount); + if (!fbc) + { + NazaraError("Failed to retrieve a framebuffer config"); + return false; + } + + // Pick the FB config/visual with the most samples per pixel + int best_fbc = -1; + int worst_fbc = -1; + int best_num_samp = -1; + int worst_num_samp = 999; + + for (int i = 0; i < fbcount; ++i) + { + XVisualInfo* vi = glXGetVisualFromFBConfig(m_display, fbc[i]); + + if (vi) + { + int samp_buf = 0, samples = 0; + glXGetFBConfigAttrib(m_display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(m_display, fbc[i], GLX_SAMPLES , &samples ); + + if ((best_fbc < 0) || (samp_buf && (samples > best_num_samp))) + { + best_fbc = i; + best_num_samp = samples; + } + if ((worst_fbc < 0) || !samp_buf || (samples < worst_num_samp)) + { + worst_fbc = i; + worst_num_samp = samples; + } + } + XFree(vi); + } + + GLXFBConfig bestFbc = fbc[best_fbc]; + + // Be sure to free the FBConfig list allocated by glXChooseFBConfig() + XFree(fbc); + + // Get a visual + XVisualInfo* vi = glXGetVisualFromFBConfig(m_display, bestFbc); + if (!vi) + { + NazaraError("Failed to get best VisualInfo"); + return false; + } + + // If context is shared by multiple windows + if (parameters.window) + { + m_window = parameters.window; + m_ownsWindow = false; + } + else + { + XSetWindowAttributes swa; + swa.colormap = m_colormap = XCreateColormap( + m_display, + XRootWindow( + m_display, + vi->screen), + vi->visual, + AllocNone + ); + + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = StructureNotifyMask; + + if (!m_colormap) + { + NazaraError("Failed to create colormap for context"); + return false; + } + + m_window = XCreateWindow( + m_display, + XRootWindow( + m_display, + vi->screen), + 0, 0, // X, Y + 1, 1, // W H + 0, + vi->depth, + InputOutput, + vi->visual, + CWBorderPixel | CWColormap | CWEventMask, + &swa + ); + + m_ownsWindow = true; + } + + if (!m_window) + { + NazaraError("Failed to create window"); + return false; + } + + // Done with the visual info data + XFree(vi); + + // Install an X error handler so the application won't exit if GL 3.0 + // context allocation fails. + // + // Note this error handler is global. All display connections in all threads + // of a process use the same error handler, so be sure to guard against other + // threads issuing X commands while this code is running. + ctxErrorOccurred = false; + int (*oldHandler)(Display*, XErrorEvent*) = + XSetErrorHandler(&ctxErrorHandler); + + // Check for the GLX_ARB_create_context extension string and the function. + // If either is not present, use GLX 1.3 context creation method. + if (!glXCreateContextAttribs) + { + NazaraWarning("glXCreateContextAttribs() not found. Using old-style GLX context"); + m_context = glXCreateNewContext(m_display, bestFbc, GLX_RGBA_TYPE, parameters.shared ? parameters.shareContext->m_impl->m_context : 0, True); + } + // If it does, try to get a GL 3.0 context! + else + { + int profile = parameters.compatibilityProfile ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + int debug = parameters.debugMode ? GLX_CONTEXT_DEBUG_BIT_ARB : 0; + + int major = 3;//parameters.majorVersion; + int minor = 3;//parameters.minorVersion; + + int context_attribs[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, major, + GLX_CONTEXT_MINOR_VERSION_ARB, minor, + GLX_CONTEXT_PROFILE_MASK_ARB, profile, + GLX_CONTEXT_FLAGS_ARB, debug, + None, None + }; + + m_context = glXCreateContextAttribs( + m_display, + bestFbc, + parameters.shared ? parameters.shareContext->m_impl->m_context : 0, + True, + context_attribs + ); + } + + // Sync to ensure any errors generated are processed. + XSync(m_display, False); + XSetErrorHandler(oldHandler); + if (ctxErrorOccurred || !m_context) + { + NazaraError("Failed to create context, check the version"); + return false; + } + + onExit.Reset(); + + return true; +} + +void NzContextImpl::Destroy() +{ + // Destroy the context + if (m_context) + { + if (glXGetCurrentContext() == m_context) + glXMakeCurrent(m_display, None, nullptr); + glXDestroyContext(m_display, m_context); + m_context = nullptr; + } + + // Destroy the window if we own it + if (m_ownsWindow && m_window) + { + XFreeColormap(m_display, m_colormap); + XDestroyWindow(m_display, m_window); + m_ownsWindow = false; + m_window = 0; + XFlush(m_display); + } +} + +void NzContextImpl::EnableVerticalSync(bool enabled) +{ + if (glXSwapIntervalEXT) + glXSwapIntervalEXT(m_display, glXGetCurrentDrawable(), enabled ? 1 : 0); + else if (NzglXSwapIntervalMESA) + NzglXSwapIntervalMESA(enabled ? 1 : 0); + else if (glXSwapIntervalSGI) + glXSwapIntervalSGI(enabled ? 1 : 0); + else + NazaraError("Vertical sync not supported"); +} + +void NzContextImpl::SwapBuffers() +{ + if (m_window) + glXSwapBuffers(m_display, m_window); +} + +bool NzContextImpl::Desactivate() +{ + return glXMakeCurrent(m_display, None, nullptr) == true; +} diff --git a/src/Nazara/Renderer/GLX/ContextImpl.hpp b/src/Nazara/Renderer/GLX/ContextImpl.hpp new file mode 100644 index 000000000..0a3a8b8dc --- /dev/null +++ b/src/Nazara/Renderer/GLX/ContextImpl.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine". +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CONTEXTIMPL_HPP +#define NAZARA_CONTEXTIMPL_HPP + +#include + +class NzContextParameters; + +class NzContextImpl +{ + public: + NzContextImpl(); + ~NzContextImpl(); + + bool Activate(); + + bool Create(NzContextParameters& parameters); + + void Destroy(); + + void EnableVerticalSync(bool enabled); + + void SwapBuffers(); + + static bool Desactivate(); + + private: + GLX::Colormap m_colormap; + GLX::GLXContext m_context; + GLX::Window m_window; + bool m_ownsWindow; +}; + +#endif // NAZARA_CONTEXTIMPL_HPP diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 01f60a39f..f72714592 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -29,7 +29,7 @@ namespace if (!entry) // wglGetProcAddress ne fonctionne pas sur les fonctions OpenGL <= 1.1 entry = reinterpret_cast(GetProcAddress(openGLlibrary, name)); #elif defined(NAZARA_PLATFORM_LINUX) - NzOpenGLFunc entry = reinterpret_cast(glXGetProcAddress(name)); + NzOpenGLFunc entry = reinterpret_cast(GLX::glXGetProcAddress(reinterpret_cast(name))); #else #error OS not handled #endif @@ -741,6 +741,10 @@ bool NzOpenGL::Initialize() /****************************Chargement OpenGL****************************/ + #if defined(NAZARA_PLATFORM_LINUX) + glXCreateContextAttribs = reinterpret_cast(LoadEntry("glXCreateContextAttribsARB", false)); + #endif + NzContext loadContext; if (!loadContext.Create(parameters)) { @@ -753,8 +757,6 @@ bool NzOpenGL::Initialize() wglChoosePixelFormat = reinterpret_cast(LoadEntry("wglChoosePixelFormatARB", false)); if (!wglChoosePixelFormat) wglChoosePixelFormat = reinterpret_cast(LoadEntry("wglChoosePixelFormatEXT", false)); - #elif defined(NAZARA_PLATFORM_LINUX) - glXCreateContextAttribs = reinterpret_cast(LoadEntry("glXCreateContextAttribsARB", false)); #endif // Récupération de la version d'OpenGL et du GLSL @@ -975,7 +977,9 @@ bool NzOpenGL::Initialize() wglGetExtensionsStringEXT = reinterpret_cast(LoadEntry("wglGetExtensionsStringEXT", false)); wglSwapInterval = reinterpret_cast(LoadEntry("wglSwapIntervalEXT", false)); #elif defined(NAZARA_PLATFORM_LINUX) - glXSwapInterval = reinterpret_cast(LoadEntry("glXSwapIntervalSGI", false)); + glXSwapIntervalEXT = reinterpret_cast(LoadEntry("glXSwapIntervalEXT", false)); + NzglXSwapIntervalMESA = reinterpret_cast(LoadEntry("glXSwapIntervalMESA", false)); + glXSwapIntervalSGI = reinterpret_cast(LoadEntry("glXSwapIntervalSGI", false)); #endif if (!glGetStringi || !LoadExtensions3()) @@ -2377,6 +2381,8 @@ PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = nullptr; PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = nullptr; PFNWGLSWAPINTERVALEXTPROC wglSwapInterval = nullptr; #elif defined(NAZARA_PLATFORM_LINUX) -PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = nullptr; -PFNGLXSWAPINTERVALSGIPROC glXSwapInterval = nullptr; +GLX::PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = nullptr; +GLX::PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr; +GLX::PFNGLXSWAPINTERVALMESAPROC NzglXSwapIntervalMESA = nullptr; +GLX::PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr; #endif diff --git a/src/Nazara/Renderer/RenderWindow.cpp b/src/Nazara/Renderer/RenderWindow.cpp index 479fdb2ff..9de0bae05 100644 --- a/src/Nazara/Renderer/RenderWindow.cpp +++ b/src/Nazara/Renderer/RenderWindow.cpp @@ -13,13 +13,15 @@ #include #include -NzRenderWindow::NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) +NzRenderWindow::NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) : +NzRenderTarget(), NzWindow() { NzErrorFlags flags(nzErrorFlag_ThrowException, true); Create(mode, title, style, parameters); } -NzRenderWindow::NzRenderWindow(NzWindowHandle handle, const NzContextParameters& parameters) +NzRenderWindow::NzRenderWindow(NzWindowHandle handle, const NzContextParameters& parameters) : +NzRenderTarget(), NzWindow() { NzErrorFlags flags(nzErrorFlag_ThrowException, true); Create(handle, parameters); @@ -149,30 +151,13 @@ void NzRenderWindow::EnableVerticalSync(bool enabled) { if (m_context) { - #if defined(NAZARA_PLATFORM_WINDOWS) if (!m_context->SetActive(true)) { NazaraError("Failed to activate context"); return; } - if (wglSwapInterval) - wglSwapInterval(enabled ? 1 : 0); - else - #elif defined(NAZARA_PLATFORM_LINUX) - if (!m_context->SetActive(true)) - { - NazaraError("Failed to activate context"); - return; - } - - if (glXSwapInterval) - glXSwapInterval(enabled ? 1 : 0); - else - #else - #error Vertical Sync is not supported on this platform - #endif - NazaraError("Vertical Sync is not supported on this platform"); + m_context->EnableVerticalSync(enabled); } else NazaraError("No context"); diff --git a/src/Nazara/Renderer/Win32/ContextImpl.cpp b/src/Nazara/Renderer/Win32/ContextImpl.cpp index 5fbe85539..7ee7a4497 100644 --- a/src/Nazara/Renderer/Win32/ContextImpl.cpp +++ b/src/Nazara/Renderer/Win32/ContextImpl.cpp @@ -226,6 +226,14 @@ void NzContextImpl::Destroy() } } +void NzContextImpl::EnableVerticalSync(bool enabled) +{ + if (wglSwapInterval) + wglSwapInterval(enabled ? 1 : 0); + else + NazaraError("Vertical sync not supported"); +} + void NzContextImpl::SwapBuffers() { ::SwapBuffers(m_deviceContext); diff --git a/src/Nazara/Renderer/Win32/ContextImpl.hpp b/src/Nazara/Renderer/Win32/ContextImpl.hpp index 07d88d44a..87e3e5ae1 100644 --- a/src/Nazara/Renderer/Win32/ContextImpl.hpp +++ b/src/Nazara/Renderer/Win32/ContextImpl.hpp @@ -16,8 +16,13 @@ class NzContextImpl NzContextImpl(); bool Activate(); + bool Create(NzContextParameters& parameters); + void Destroy(); + + void EnableVerticalSync(bool enabled); + void SwapBuffers(); static bool Desactivate();