Add OpenGLRenderer (WIP)

This commit is contained in:
Lynix
2020-04-15 19:38:11 +02:00
parent ebb271a089
commit 68760209c1
118 changed files with 19236 additions and 414 deletions

View File

@@ -0,0 +1,10 @@
// 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/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
}

View File

@@ -0,0 +1,44 @@
// 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/GLContext.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
GLContext::~GLContext() = default;
bool GLContext::LoadCoreFunctions(Loader& loader)
{
if (!Activate())
{
NazaraError("failed to activate context");
return false;
}
auto LoadSymbol = [&](auto& func, const char* funcName)
{
func = reinterpret_cast<std::decay_t<decltype(func)>>(loader.LoadFunction(funcName));
if (!func)
throw std::runtime_error("failed to load core function " + std::string(funcName));
};
try
{
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(name, #name);
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC)
#undef NAZARA_OPENGLRENDERER_FUNC
}
catch (const std::exception& e)
{
NazaraError(e.what());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,11 @@
// 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/Loader.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
Loader::~Loader() = default;
}

View File

@@ -0,0 +1,278 @@
// 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/Win32/WGLContext.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLLoader.hpp>
#include <array>
#include <cassert>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
thread_local WGLContext* s_currentContext = nullptr;
GL::WGLContext::WGLContext(WGLLoader& loader) :
m_loader(loader)
{
}
WGLContext::~WGLContext()
{
Destroy();
}
bool WGLContext::Activate()
{
WGLContext*& currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext)
{
if (currentContext == this)
return true;
// Only one context can be activated per thread
currentContext->Desactivate();
}
bool succeeded = m_loader.wglMakeCurrent(m_deviceContext, m_handle);
if (!succeeded)
{
NazaraError("failed to activate context: " + Error::GetLastSystemError());
return false;
}
currentContext = this;
return true;
}
bool WGLContext::Create(const ContextParams& params)
{
Destroy();
// Creating a context requires a Window
m_window.reset(::CreateWindowA("STATIC", nullptr, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, nullptr, nullptr, GetModuleHandle(nullptr), nullptr));
if (!m_window)
{
NazaraError("failed to create dummy window: " + Error::GetLastSystemError());
return false;
}
::ShowWindow(m_window.get(), FALSE);
m_deviceContext = ::GetDC(m_window.get());
if (!m_deviceContext)
{
NazaraError("failed to retrieve dummy window device context: " + Error::GetLastSystemError());
return false;
}
if (!SetPixelFormat(params))
return false;
WGLContext* currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext && currentContext->wglCreateContextAttribsARB)
{
struct OpenGLVersion
{
int major;
int minor;
};
std::array<OpenGLVersion, 8> supportedVersions = {
{
{ 4, 6 },
{ 4, 5 },
{ 4, 4 },
{ 4, 3 },
{ 4, 2 },
{ 4, 1 },
{ 4, 0 },
{ 3, 3 }
}
};
for (const OpenGLVersion& version : supportedVersions)
{
std::array<int, 3 * 2 + 1> attributes = {
WGL_CONTEXT_MAJOR_VERSION_ARB, version.major,
WGL_CONTEXT_MINOR_VERSION_ARB, version.minor,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB
};
m_handle = currentContext->wglCreateContextAttribsARB(m_deviceContext, nullptr, attributes.data());
if (m_handle)
break;
}
if (!m_handle)
{
NazaraError("failed to create WGL context: " + Error::GetLastSystemError());
return false;
}
}
else
{
m_handle = m_loader.wglCreateContext(m_deviceContext);
if (!m_handle)
{
NazaraError("failed to create WGL context: " + Error::GetLastSystemError());
return false;
}
}
LoadWGLExt();
return true;
}
void WGLContext::Destroy()
{
if (m_handle)
{
WGLContext*& currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext == this)
currentContext = nullptr;
m_loader.wglDeleteContext(m_handle);
m_handle = nullptr;
}
}
void WGLContext::EnableVerticalSync(bool enabled)
{
}
void WGLContext::SwapBuffers()
{
m_loader.SwapBuffers(m_deviceContext);
}
void WGLContext::Desactivate()
{
WGLContext*& currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext == this)
{
m_loader.wglMakeCurrent(nullptr, nullptr);
currentContext = nullptr;
}
}
bool WGLContext::LoadWGLExt()
{
if (!Activate())
return false;
#define NAZARA_OPENGLRENDERER_FUNC(name, sig)
#define NAZARA_OPENGLRENDERER_EXT_BEGIN(ext)
#define NAZARA_OPENGLRENDERER_EXT_END()
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) name = reinterpret_cast<sig>(m_loader.wglGetProcAddress(#name));
NAZARA_OPENGLRENDERER_FOREACH_WGL_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_BEGIN, NAZARA_OPENGLRENDERER_EXT_END, NAZARA_OPENGLRENDERER_EXT_FUNC)
#undef NAZARA_OPENGLRENDERER_EXT_BEGIN
#undef NAZARA_OPENGLRENDERER_EXT_END
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
#undef NAZARA_OPENGLRENDERER_FUNC
const char* extensionString = nullptr;
if (wglGetExtensionsStringARB)
extensionString = wglGetExtensionsStringARB(m_deviceContext);
else if (wglGetExtensionsStringEXT)
extensionString = wglGetExtensionsStringEXT();
if (extensionString)
{
SplitString(extensionString, " ", [&](std::string_view extension)
{
m_supportedExtensions.emplace(extension);
return true;
});
}
return true;
}
bool WGLContext::SetPixelFormat(const ContextParams& params)
{
PIXELFORMATDESCRIPTOR descriptor = {};
descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR);
descriptor.nVersion = 1;
int pixelFormat = 0;
if (params.sampleCount > 1)
{
WGLContext* currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext)
{
// WGL_ARB_pixel_format and WGL_EXT_pixel_format are the same, except for the symbol
auto wglChoosePixelFormat = (currentContext->wglChoosePixelFormatARB) ? currentContext->wglChoosePixelFormatARB : currentContext->wglChoosePixelFormatEXT;
if (wglChoosePixelFormat)
{
std::array<int, 10 * 2 + 1> attributes = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB, (params.bitsPerPixel == 32) ? 24 : params.bitsPerPixel,
WGL_ALPHA_BITS_ARB, (params.bitsPerPixel == 32) ? 8 : 0,
WGL_DEPTH_BITS_ARB, params.depthBits,
WGL_STENCIL_BITS_ARB, params.stencilBits,
WGL_DOUBLE_BUFFER_ARB, (params.doubleBuffering) ? GL_TRUE : GL_FALSE,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, params.sampleCount
};
int& sampleCount = attributes[attributes.size() - 3];
do
{
UINT formatCount;
if (currentContext->wglChoosePixelFormatARB(m_deviceContext, attributes.data(), nullptr, 1, &pixelFormat, &formatCount))
{
if (formatCount > 0)
break;
}
sampleCount--;
}
while (sampleCount > 1);
if (params.sampleCount != sampleCount)
NazaraWarning("couldn't find a pixel format matching " + std::to_string(params.sampleCount) + " sample count, using " + std::to_string(sampleCount) + " sample(s) instead");
}
}
}
if (pixelFormat == 0)
{
descriptor.cColorBits = BYTE((params.bitsPerPixel == 32) ? 24 : params.bitsPerPixel);
descriptor.cDepthBits = BYTE(params.depthBits);
descriptor.cStencilBits = BYTE(params.stencilBits);
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
descriptor.iPixelType = PFD_TYPE_RGBA;
if (params.bitsPerPixel == 32)
descriptor.cAlphaBits = 8;
if (params.doubleBuffering)
descriptor.dwFlags |= PFD_DOUBLEBUFFER;
pixelFormat = m_loader.ChoosePixelFormat(m_deviceContext, &descriptor);
if (pixelFormat == 0)
{
NazaraError("Failed to choose pixel format: " + Error::GetLastSystemError());
return false;
}
}
if (!m_loader.SetPixelFormat(m_deviceContext, pixelFormat, &descriptor))
{
NazaraError("Failed to choose pixel format: " + Error::GetLastSystemError());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,70 @@
// 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/Win32/WGLLoader.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
WGLLoader::WGLLoader(DynLib& openglLib) :
m_opengl32Lib(openglLib)
{
if (!m_gdi32Lib.Load("gdi32.dll"))
throw std::runtime_error("failed to load gdi32.dll: " + m_gdi32Lib.GetLastError());
auto LoadSymbol = [](DynLib& lib, auto& func, const char* funcName)
{
func = reinterpret_cast<std::decay_t<decltype(func)>>(lib.GetSymbol(funcName));
if (!func)
throw std::runtime_error("failed to load core function " + std::string(funcName));
};
// Load gdi32 functions
#define NAZARA_OPENGLRENDERER_EXT_BEGIN(ext)
#define NAZARA_OPENGLRENDERER_EXT_END()
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) //< Ignore extensions
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(m_gdi32Lib, name, #name);
NAZARA_OPENGLRENDERER_FOREACH_GDI32_FUNC(NAZARA_OPENGLRENDERER_FUNC)
#undef NAZARA_OPENGLRENDERER_FUNC
// Load base WGL functions
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(m_opengl32Lib, name, #name);
NAZARA_OPENGLRENDERER_FOREACH_WGL_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_BEGIN, NAZARA_OPENGLRENDERER_EXT_END, NAZARA_OPENGLRENDERER_EXT_FUNC)
#undef NAZARA_OPENGLRENDERER_FUNC
// In order to load OpenGL functions, we have to create a context first
WGLContext dummyContext(*this);
if (!dummyContext.Create({}))
throw std::runtime_error("failed to create load context");
WGLContext loadContext(*this);
if (!loadContext.Create({}))
throw std::runtime_error("failed to create load context");
if (!loadContext.LoadCoreFunctions(*this))
throw std::runtime_error("failed to load OpenGL functions");
#undef NAZARA_OPENGLRENDERER_EXT_BEGIN
#undef NAZARA_OPENGLRENDERER_EXT_END
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
}
std::unique_ptr<GLContext> WGLLoader::CreateContext()
{
return {}; //< TODO
}
GLFunction WGLLoader::LoadFunction(const char* name)
{
GLFunction func = reinterpret_cast<GLFunction>(wglGetProcAddress(name));
if (!func) //< wglGetProcAddress doesn't work for OpenGL 1.1 functions
func = reinterpret_cast<GLFunction>(m_opengl32Lib.GetSymbol(name));
return func;
}
}