From da8b2ce5b9db383f4bb177008b3599c858a241ef Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Sat, 9 Sep 2023 22:47:31 -0400 Subject: [PATCH] Add new, more capable, WGL offscreen support --- CMakeLists.txt | 4 +- src/glview/OffscreenContextFactory.cc | 7 +- src/glview/OffscreenContextWGL.cc | 132 ++++++++++++++++++++++++++ src/glview/OffscreenContextWGL.h | 10 ++ 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/glview/OffscreenContextWGL.cc create mode 100644 src/glview/OffscreenContextWGL.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f4d308db3e..020fcb2139 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -656,7 +656,9 @@ elseif(WIN32) if(NOT NULLGL) set(OFFSCREEN_METHOD "Windows WGL") message(STATUS "Offscreen OpenGL Context - using Microsoft WGL") - set(PLATFORM_SOURCES ${PLATFORM_SOURCES} src/glview/offscreen-old/OffscreenContextWGL.cc) + set(PLATFORM_SOURCES ${PLATFORM_SOURCES} + src/glview/offscreen-old/OffscreenContextWGL.cc + src/glview/OffscreenContextWGL.cc) endif() endif() diff --git a/src/glview/OffscreenContextFactory.cc b/src/glview/OffscreenContextFactory.cc index 0fe9c5ef96..9355047b0b 100644 --- a/src/glview/OffscreenContextFactory.cc +++ b/src/glview/OffscreenContextFactory.cc @@ -8,6 +8,7 @@ #endif #ifdef _WIN32 #include "offscreen-old/OffscreenContextWGL.h" +#include "OffscreenContextWGL.h" #endif #ifdef ENABLE_EGL #include "offscreen-old/OffscreenContextEGL.h" @@ -37,7 +38,7 @@ const char *defaultProvider() { return "glx"; #endif #ifdef _WIN32 - return "wgl-old"; + return "wgl"; #endif #endif // NULLGL } @@ -96,6 +97,10 @@ std::shared_ptr create(const std::string& provider, const Offscre return offscreen_old::CreateOffscreenContextWGL(attrib.width, attrib.height, attrib.majorGLVersion, attrib.minorGLVersion, attrib.compatibilityProfile); + } else if (provider == "wgl") { + return CreateOffscreenContextWGL(attrib.width, attrib.height, + attrib.majorGLVersion, attrib.minorGLVersion, + attrib.compatibilityProfile); } else #endif diff --git a/src/glview/OffscreenContextWGL.cc b/src/glview/OffscreenContextWGL.cc new file mode 100644 index 0000000000..c90054efd4 --- /dev/null +++ b/src/glview/OffscreenContextWGL.cc @@ -0,0 +1,132 @@ +#include "OffscreenContextWGL.h" + +#include +#include + +#undef NOGDI // To access ChoosePixelFormat, PIXELFORMATDESCRIPTOR, etc. +#include +#include +#define GLAD_WGL +#define GLAD_WGL_IMPLEMENTATION +#include + +#include "printutils.h" +#include "scope_guard.hpp" + +class OffscreenContextWGL : public OffscreenContext { + +public: + HWND window = nullptr; + HDC deviceContext = nullptr; + HGLRC renderContext = nullptr; + + OffscreenContextWGL(int width, int height) : OffscreenContext(width, height) {} + ~OffscreenContextWGL() { + wglMakeCurrent(nullptr, nullptr); + if (this->renderContext) wglDeleteContext(this->renderContext); + if (this->deviceContext) ReleaseDC(this->window, this->deviceContext); + if (this->window) DestroyWindow(this->window); + } + + std::string getInfo() const override { + std::stringstream result; + // should probably get some info from WGL context here? + result << "GL context creator: WGL (new)\n" + << "PNG generator: lodepng\n"; + + return result.str(); + } + + bool makeCurrent() const override { + return wglMakeCurrent(this->deviceContext, this->renderContext); + } +}; + +std::shared_ptr CreateOffscreenContextWGL(size_t width, size_t height, + size_t majorGLVersion, size_t minorGLVersion, bool compatibilityProfile) +{ + auto ctx = std::make_shared(width, height); + + WNDCLASSEX wndClass = { + .cbSize = sizeof(WNDCLASSEX), + .style = CS_OWNDC, + .lpfnWndProc = &DefWindowProc, + .lpszClassName = L"OpenSCAD" + }; + static ATOM atom = RegisterClassEx(&wndClass); + + // Create the window. Position and size it. + // Style the window and remove the caption bar (WS_POPUP) + ctx->window = CreateWindowEx(0, MAKEINTATOM(atom), L"openscad", WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, + CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, 0, 0); + ctx->deviceContext = GetDC(ctx->window); + if (ctx->deviceContext == nullptr) { + std::cerr << "GetDC() failed: " << std::system_category().message(GetLastError()) << std::endl; + return nullptr; + } + + PIXELFORMATDESCRIPTOR pixelFormatDesc = { + .nSize = sizeof(PIXELFORMATDESCRIPTOR), + .nVersion = 1, + .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL, + .iPixelType = PFD_TYPE_RGBA, + + .cColorBits = 32, + .cDepthBits = 24, + .cStencilBits = 8 + }; + int pixelFormat = ChoosePixelFormat(ctx->deviceContext, &pixelFormatDesc); + if (!pixelFormat) { + std::cerr << "ChoosePixelFormat() failed: " << std::system_category().message(GetLastError()) << std::endl; + return nullptr; + } + + if (!SetPixelFormat(ctx->deviceContext, pixelFormat, &pixelFormatDesc)) { + std::cerr << "SetPixelFormat() failed: " << std::system_category().message(GetLastError()) << std::endl; + return nullptr; + } + + const auto tmpRenderContext = wglCreateContext(ctx->deviceContext); + if (tmpRenderContext == nullptr) { + std::cerr << "wglCreateContext() failed: " << std::system_category().message(GetLastError()) << std::endl; + return nullptr; + } + auto guard = sg::make_scope_guard([tmpRenderContext]() { + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(tmpRenderContext); + }); + + if (!wglMakeCurrent(ctx->deviceContext, tmpRenderContext)) { + std::cerr << "wglMakeCurrent() failed: " << std::system_category().message(GetLastError()) << std::endl; + return nullptr; + } + + auto wglVersion = gladLoaderLoadWGL(ctx->deviceContext); + if (wglVersion == 0) { + std::cerr << "GLAD: Unable to load WGL" << std::endl; + return nullptr; + } + PRINTDB("GLAD: Loaded WGL %d.%d", GLAD_VERSION_MAJOR(wglVersion) % GLAD_VERSION_MINOR(wglVersion)); + + if (!wglCreateContextAttribsARB) { + std::cerr << "wglCreateContextAttribsARB() not available" << std::endl; + return nullptr; + } + + // Note: If we want to use wglChoosePixelFormatARB() to request a special pixel format, we could do that here. + + int attributes[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, static_cast(majorGLVersion), + WGL_CONTEXT_MINOR_VERSION_ARB, static_cast(minorGLVersion), + WGL_CONTEXT_PROFILE_MASK_ARB, + compatibilityProfile ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0 + }; + ctx->renderContext = wglCreateContextAttribsARB(ctx->deviceContext, nullptr, attributes); + if (ctx->renderContext == nullptr) { + std::cerr << "wglCreateContextAttribsARB() failed: " << std::system_category().message(GetLastError()) << std::endl; + return nullptr; + } + + return ctx; +} diff --git a/src/glview/OffscreenContextWGL.h b/src/glview/OffscreenContextWGL.h new file mode 100644 index 0000000000..c4a430507c --- /dev/null +++ b/src/glview/OffscreenContextWGL.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +#include "OffscreenContext.h" + +std::shared_ptr CreateOffscreenContextWGL( + size_t width, size_t height, size_t majorGLVersion, + size_t minorGLVersion, bool compatibilityProfile);