bluenoise-raytracer/raytracer/nvpro_core/nvgl/contextwindow_gl.cpp
2024-05-25 11:53:25 +02:00

682 lines
19 KiB
C++

/*
* Copyright (c) 2013-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2013-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
//--------------------------------------------------------------------
#include <nvgl/contextwindow_gl.hpp>
#include <nvgl/error_gl.hpp>
#include <nvgl/extensions_gl.hpp>
#ifdef WIN32
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
#include <GL/wgl.h>
#include "resources.h"
#include <windows.h>
#include <windowsx.h>
#elif defined LINUX
#define GLFW_EXPOSE_NATIVE_GLX
#define GLFW_EXPOSE_NATIVE_X11
#include <GL/glx.h>
#include <GLFW/glfw3native.h>
#endif
#include <fileformats/bmp.hpp>
#include <nvh/nvprint.hpp>
#include <algorithm>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
namespace nvgl {
//------------------------------------------------------------------------------
// OGL callback
//------------------------------------------------------------------------------
#ifndef NDEBUG
static void APIENTRY myOpenGLCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const GLvoid* userParam)
{
ContextWindow* window = (ContextWindow*)userParam;
GLenum filter = window->m_debugFilter;
GLenum severitycmp = severity;
// minor fixup for filtering so notification becomes lowest priority
if(GL_DEBUG_SEVERITY_NOTIFICATION == filter)
{
filter = GL_DEBUG_SEVERITY_LOW_ARB + 1;
}
if(GL_DEBUG_SEVERITY_NOTIFICATION == severitycmp)
{
severitycmp = GL_DEBUG_SEVERITY_LOW_ARB + 1;
}
if(!filter || severitycmp <= filter)
{
//static std::map<GLuint, bool> ignoreMap;
//if(ignoreMap[id] == true)
// return;
const char* strSource = "0";
const char* strType = strSource;
switch(source)
{
case GL_DEBUG_SOURCE_API_ARB:
strSource = "API";
break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
strSource = "WINDOWS";
break;
case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
strSource = "SHADER COMP.";
break;
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
strSource = "3RD PARTY";
break;
case GL_DEBUG_SOURCE_APPLICATION_ARB:
strSource = "APP";
break;
case GL_DEBUG_SOURCE_OTHER_ARB:
strSource = "OTHER";
break;
}
switch(type)
{
case GL_DEBUG_TYPE_ERROR_ARB:
strType = "ERROR";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
strType = "Deprecated";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
strType = "Undefined";
break;
case GL_DEBUG_TYPE_PORTABILITY_ARB:
strType = "Portability";
break;
case GL_DEBUG_TYPE_PERFORMANCE_ARB:
strType = "Performance";
break;
case GL_DEBUG_TYPE_OTHER_ARB:
strType = "Other";
break;
}
switch(severity)
{
case GL_DEBUG_SEVERITY_HIGH_ARB:
LOGE("ARB_debug : %s High - %s - %s : %s\n", window->m_debugTitle.c_str(), strSource, strType, message);
break;
case GL_DEBUG_SEVERITY_MEDIUM_ARB:
LOGW("ARB_debug : %s Medium - %s - %s : %s\n", window->m_debugTitle.c_str(), strSource, strType, message);
break;
case GL_DEBUG_SEVERITY_LOW_ARB:
LOGI("ARB_debug : %s Low - %s - %s : %s\n", window->m_debugTitle.c_str(), strSource, strType, message);
break;
default:
//LOGI("ARB_debug : comment - %s - %s : %s\n", strSource, strType, message);
break;
}
}
}
#endif
// from GLFW 3.0
static int stringInExtensionString(const char* string, const char* exts)
{
const GLubyte* extensions = (const GLubyte*)exts;
const GLubyte* start;
GLubyte* where;
GLubyte* terminator;
// It takes a bit of care to be fool-proof about parsing the
// OpenGL extensions string. Don't be fooled by sub-strings,
// etc.
start = extensions;
for(;;)
{
where = (GLubyte*)strstr((const char*)start, string);
if(!where)
return GL_FALSE;
terminator = where + strlen(string);
if(where == start || *(where - 1) == ' ')
{
if(*terminator == ' ' || *terminator == '\0')
break;
}
start = terminator;
}
return GL_TRUE;
}
#ifdef WIN32
//////////////////////////////////////////////////////////////////////////
// WIN 32
static HMODULE s_module = NULL;
struct ContextWindowInternalGL
{
HDC m_hDCaffinity = NULL;
HDC m_hDC = NULL;
HGLRC m_hRC = NULL;
PFNWGLSWAPINTERVALEXTPROC m_wglSwapIntervalEXT = NULL;
PFNWGLGETEXTENSIONSSTRINGARBPROC m_wglGetExtensionsStringARB = NULL;
PFNWGLDELETEDCNVPROC m_wglDeleteDCNV = NULL;
bool init(const ContextWindowCreateInfo& settings, GLFWwindow* sourcewindow, ContextWindow* ctxwindow)
{
GLuint PixelFormat;
HWND hWnd = glfwGetWin32Window(sourcewindow);
HINSTANCE hInstance = GetModuleHandle(NULL);
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = settings.depth;
pfd.cStencilBits = settings.stencil;
if(settings.stereo)
{
pfd.dwFlags |= PFD_STEREO;
}
if(settings.MSAA > 1)
{
HWND hWndDummy = CreateWindowEx(NULL, "Static", "Dummy", WS_OVERLAPPEDWINDOW, 0, 0, 10, 10, NULL, NULL, hInstance, NULL);
m_hDC = GetDC(hWndDummy);
PixelFormat = ChoosePixelFormat(m_hDC, &pfd);
SetPixelFormat(m_hDC, PixelFormat, &pfd);
m_hRC = wglCreateContext(m_hDC);
wglMakeCurrent(m_hDC, m_hRC);
PFNWGLCHOOSEPIXELFORMATARBPROC fn_wglChoosePixelFormatARB =
(PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
ReleaseDC(hWndDummy, m_hDC);
m_hDC = GetDC(hWnd);
int attri[] = {WGL_DRAW_TO_WINDOW_ARB,
true,
WGL_PIXEL_TYPE_ARB,
WGL_TYPE_RGBA_ARB,
WGL_SUPPORT_OPENGL_ARB,
true,
WGL_ACCELERATION_ARB,
WGL_FULL_ACCELERATION_ARB,
WGL_DOUBLE_BUFFER_ARB,
true,
WGL_DEPTH_BITS_ARB,
settings.depth,
WGL_STENCIL_BITS_ARB,
settings.stencil,
WGL_SAMPLE_BUFFERS_ARB,
1,
WGL_SAMPLES_ARB,
settings.MSAA,
0,
0};
GLuint nfmts;
int fmt;
if(!fn_wglChoosePixelFormatARB(m_hDC, attri, NULL, 1, &fmt, &nfmts))
{
wglDeleteContext(m_hRC);
return false;
}
wglDeleteContext(m_hRC);
DestroyWindow(hWndDummy);
if(!SetPixelFormat(m_hDC, fmt, &pfd))
return false;
}
else
{
m_hDC = GetDC(hWnd);
PixelFormat = ChoosePixelFormat(m_hDC, &pfd);
SetPixelFormat(m_hDC, PixelFormat, &pfd);
}
m_hRC = wglCreateContext(m_hDC);
wglMakeCurrent(m_hDC, m_hRC);
HDC hdcContext = m_hDC;
if(settings.device)
{
PFNWGLENUMGPUSNVPROC fn_wglEnumGpusNV = (PFNWGLENUMGPUSNVPROC)wglGetProcAddress("wglEnumGpusNV");
PFNWGLENUMGPUDEVICESNVPROC fn_wglEnumGpuDevicesNV =
(PFNWGLENUMGPUDEVICESNVPROC)wglGetProcAddress("wglEnumGpuDevicesNV");
PFNWGLCREATEAFFINITYDCNVPROC fn_wglCreateAffinityDCNV =
(PFNWGLCREATEAFFINITYDCNVPROC)wglGetProcAddress("wglCreateAffinityDCNV");
m_wglDeleteDCNV = (PFNWGLDELETEDCNVPROC)wglGetProcAddress("wglDeleteDCNV");
if(fn_wglEnumGpusNV && fn_wglCreateAffinityDCNV && m_wglDeleteDCNV)
{
const uint32_t MAX_GPU = 4;
HGPUNV hGPU[MAX_GPU];
_GPU_DEVICE devices[MAX_GPU];
HGPUNV GpuMask[2] = {0};
uint32_t gpuIndex = 0;
// Get a list of the first MAX_GPU GPUs in the system
while((gpuIndex < MAX_GPU) && fn_wglEnumGpusNV(gpuIndex, &hGPU[gpuIndex]))
{
fn_wglEnumGpuDevicesNV(hGPU[gpuIndex], 0, &devices[gpuIndex]);
gpuIndex++;
}
GpuMask[0] = hGPU[settings.device];
// delete old
wglMakeCurrent(NULL, NULL);
wglDeleteContext(m_hRC);
m_hDCaffinity = fn_wglCreateAffinityDCNV(GpuMask);
hdcContext = m_hDCaffinity;
PixelFormat = ChoosePixelFormat(m_hDCaffinity, &pfd);
SetPixelFormat(m_hDCaffinity, PixelFormat, &pfd);
m_hRC = wglCreateContext(m_hDCaffinity);
wglMakeCurrent(m_hDC, m_hRC);
}
}
PFNWGLCREATECONTEXTATTRIBSARBPROC fn_wglCreateContextAttribsARB =
(PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
if(fn_wglCreateContextAttribsARB)
{
HGLRC hRC = NULL;
std::vector<int> attribList;
#define ADDATTRIB(a, b) \
{ \
attribList.push_back(a); \
attribList.push_back(b); \
}
int maj = settings.major;
int min = settings.minor;
ADDATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, maj)
ADDATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, min)
if(settings.core)
ADDATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB)
else
ADDATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB)
int ctxtflags = 0;
if(settings.debug)
ctxtflags |= WGL_CONTEXT_DEBUG_BIT_ARB;
if(settings.robust)
ctxtflags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
if(settings.forward) // use it if you want errors when compat options still used
ctxtflags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
ADDATTRIB(WGL_CONTEXT_FLAGS_ARB, ctxtflags);
ADDATTRIB(0, 0)
int* p = &(attribList[0]);
if(!(hRC = fn_wglCreateContextAttribsARB(hdcContext, 0, p)))
{
GLint MajorVersionContext = 0;
GLint MinorVersionContext = 0;
glGetIntegerv(GL_MAJOR_VERSION, &MajorVersionContext);
glGetIntegerv(GL_MINOR_VERSION, &MinorVersionContext);
if((MajorVersionContext * 100 + MinorVersionContext * 10) < (maj * 100 + min * 10))
{
LOGE("OpenGL version %d.%d not available. Only %d.%d found\n", maj, min, MajorVersionContext, MinorVersionContext);
}
LOGE("wglCreateContextAttribsARB() failed for OpenGL context.\n");
return false;
}
if(!wglMakeCurrent(m_hDC, hRC))
{
LOGE("wglMakeCurrent() failed for OpenGL context.\n");
}
else
{
wglDeleteContext(m_hRC);
m_hRC = hRC;
#ifndef NDEBUG
PFNGLDEBUGMESSAGECALLBACKARBPROC fn_glDebugMessageCallbackARB =
(PFNGLDEBUGMESSAGECALLBACKARBPROC)wglGetProcAddress("glDebugMessageCallbackARB");
PFNGLDEBUGMESSAGECONTROLARBPROC fn_glDebugMessageControlARB =
(PFNGLDEBUGMESSAGECONTROLARBPROC)wglGetProcAddress("glDebugMessageControlARB");
if(fn_glDebugMessageCallbackARB)
{
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
fn_glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
fn_glDebugMessageCallbackARB(myOpenGLCallback, ctxwindow);
}
#endif
}
}
m_wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
m_wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
if(!s_module)
{
s_module = LoadLibraryA("opengl32.dll");
}
return true;
}
void deinit()
{
wglDeleteContext(m_hRC);
if(m_hDCaffinity)
{
m_wglDeleteDCNV(m_hDCaffinity);
}
}
};
void ContextWindow::makeContextCurrent()
{
wglMakeCurrent(m_internal->m_hDC, m_internal->m_hRC);
}
void ContextWindow::makeContextNonCurrent()
{
wglMakeCurrent(0, 0);
}
int ContextWindow::extensionSupported(const char* name)
{
// we are not using the glew query, as glew will only report
// those extension it knows about, not what the actual driver may support
int i;
GLint count;
// Check if extension is in the modern OpenGL extensions string list
// This should be safe to use since GL 3.0 is around for a long time :)
glGetIntegerv(GL_NUM_EXTENSIONS, &count);
for(i = 0; i < count; i++)
{
const char* en = (const char*)glGetStringi(GL_EXTENSIONS, i);
if(!en)
{
return GL_FALSE;
}
if(strcmp(en, name) == 0)
return GL_TRUE;
}
// Check platform specifc gets
const char* exts = NULL;
if(m_internal->m_wglGetExtensionsStringARB)
{
exts = m_internal->m_wglGetExtensionsStringARB(m_internal->m_hDC);
}
if(!exts)
{
return FALSE;
}
return stringInExtensionString(name, exts);
}
void* ContextWindow::sysGetProcAddress(const char* name)
{
void* p = (void*)wglGetProcAddress(name);
if(p == 0 || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1))
{
p = (void*)GetProcAddress(s_module, name);
}
return p;
}
void ContextWindow::swapInterval(int i)
{
m_internal->m_wglSwapIntervalEXT(i);
}
void ContextWindow::swapBuffers()
{
SwapBuffers(m_internal->m_hDC);
}
#else
struct ContextWindowInternalGL
{
GLFWwindow* m_glfwwindow = nullptr;
bool init(const ContextWindowCreateInfo& settings, GLFWwindow* sourcewindow, ContextWindow* ctxwindow)
{
m_glfwwindow = sourcewindow;
glfwMakeContextCurrent(m_glfwwindow);
#ifndef NDEBUG
PFNGLDEBUGMESSAGECALLBACKARBPROC fn_glDebugMessageCallbackARB =
(PFNGLDEBUGMESSAGECALLBACKARBPROC)glfwGetProcAddress("glDebugMessageCallbackARB");
PFNGLDEBUGMESSAGECONTROLARBPROC fn_glDebugMessageControlARB =
(PFNGLDEBUGMESSAGECONTROLARBPROC)glfwGetProcAddress("glDebugMessageControlARB");
if(fn_glDebugMessageCallbackARB)
{
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
fn_glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
fn_glDebugMessageCallbackARB(myOpenGLCallback, ctxwindow);
}
#endif
return true;
}
void deinit() {}
};
void ContextWindow::makeContextCurrent()
{
glfwMakeContextCurrent(m_internal->m_glfwwindow);
}
void ContextWindow::makeContextNonCurrent()
{
glfwMakeContextCurrent(NULL);
}
int ContextWindow::extensionSupported(const char* name)
{
// we are not using the glew query, as glew will only report
// those extension it knows about, not what the actual driver may support
int i;
GLint count;
// Check if extension is in the modern OpenGL extensions string list
// This should be safe to use since GL 3.0 is around for a long time :)
glGetIntegerv(GL_NUM_EXTENSIONS, &count);
for(i = 0; i < count; i++)
{
const char* en = (const char*)glGetStringi(GL_EXTENSIONS, i);
if(!en)
{
return GL_FALSE;
}
if(strcmp(en, name) == 0)
return GL_TRUE;
}
// Check platform specifc gets
const char* exts = glXQueryExtensionsString(glfwGetX11Display(), 0);
if(!exts)
{
return GL_FALSE;
}
return stringInExtensionString(name, exts);
}
void* ContextWindow::sysGetProcAddress(const char* name)
{
void* p = (void*)glfwGetProcAddress(name);
return p;
}
void ContextWindow::swapInterval(int i)
{
glfwSwapInterval(i);
}
void ContextWindow::swapBuffers()
{
glfwSwapBuffers(m_internal->m_glfwwindow);
}
#endif
//////////////////////////////////////////////////////////////////////////
ContextWindow::ContextWindow()
{
if(m_internal)
{
delete m_internal;
}
}
//------------------------------------------------------------------------------
bool ContextWindow::init(const ContextWindowCreateInfo* cflags, GLFWwindow* sourcewindow, const char* dbgTitle)
{
if(!m_internal)
{
m_internal = new ContextWindowInternalGL;
}
ContextWindowCreateInfo settings;
if(cflags)
{
settings = *cflags;
}
m_debugFilter = GL_DEBUG_SEVERITY_HIGH_ARB;
m_debugTitle = dbgTitle;
if(m_internal->init(settings, sourcewindow, this))
{
load_GL(ContextWindow::sysGetProcAddress);
const char* renderer = (const char*)glGetString(GL_RENDERER);
GLint major = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
GLint minor = 0;
glGetIntegerv(GL_MINOR_VERSION, &minor);
if(major < settings.major || (major == settings.major && minor < settings.minor))
{
LOGE(" OpenGL context version too low: required %d.%d, found %d.%d\n", settings.major, settings.minor, major, minor);
return false;
}
int version = major * 10 + minor;
int success = 1;
#define HAS_VERSION(MA, MI) \
if(version >= (MA * 10 + MI)) \
success = success && has_GL_VERSION_##MA##_##MI;
HAS_VERSION(1, 1);
HAS_VERSION(1, 2);
HAS_VERSION(1, 3);
HAS_VERSION(1, 4);
HAS_VERSION(1, 5);
HAS_VERSION(2, 0);
HAS_VERSION(2, 1);
HAS_VERSION(3, 0);
HAS_VERSION(3, 1);
HAS_VERSION(3, 2);
HAS_VERSION(3, 3);
HAS_VERSION(4, 0);
HAS_VERSION(4, 1);
HAS_VERSION(4, 2);
HAS_VERSION(4, 3);
HAS_VERSION(4, 4);
HAS_VERSION(4, 5);
HAS_VERSION(4, 6);
#undef HAS_VERSION
if(!success)
{
LOGE(" OpenGL context version incomplete symbols\n");
return false;
}
m_deviceName = std::string(renderer);
return true;
}
return false;
}
void* ContextWindow::getProcAddress(const char* name)
{
return sysGetProcAddress(name);
}
void ContextWindow::screenshot(const char* filename, int x, int y, int width, int height, unsigned char* data)
{
glFinish();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_BYTE, data);
if(filename)
{
saveBMP(filename, width, height, data);
}
}
void ContextWindow::deinit()
{
makeContextNonCurrent();
m_internal->deinit();
}
} // namespace nvgl