File extended_window.hpp
File List > illixr > extended_window.hpp
Go to the documentation of this file
#pragma once
#include "error_util.hpp"
#include "global_module_defs.hpp"
#include "phonebook.hpp"
#include
#include
#include
#ifdef __ANDROID__
# include
# include
# include
# include
# include
# include
# include
# include
# include
# include
# include
#elif defined(_WIN32) || defined(_WIN64)
# include
# include
# define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
# define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#else
# include
// GLX context magics
# define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
# define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
#endif
#ifdef __ANDROID__
# define DISPLAY_TYPE EGLDisplay
# define CONTEXT_TYPE EGLContext
# define WINDOW_TYPE ANativeWindow*
#elif defined(_WIN32) || defined(_WIN64)
typedef HGLRC(WINAPI* wglCreateContextAttribsARBProc)(HDC hDC, HGLRC hShareContext, const int* attribList);
# define CONTEXT_TYPE HGLRC
#else
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
# define DISPLAY_TYPE Display*
# define CONTEXT_TYPE GLXContext
# define WINDOW_TYPE Window
#endif
namespace ILLIXR {
class xlib_gl_extended_window : public phonebook::service {
public:
#ifdef __ANDROID__
xlib_gl_extended_window(int width, int height, EGLContext egl_context, ANativeWindow* window) {
window_ = window;
#elif defined(_WIN32) || defined(_WIN64)
xlib_gl_extended_window(int width, int height, HGLRC shared_gl_context) {
#else
xlib_gl_extended_window(int width, int height, GLXContext shared_gl_context) {
#endif
width_ = width;
height_ = height;
#ifndef NDEBUG
spdlog::get("illixr")->debug("[extended_window] Opening display");
#endif
// RAC_ERRNO_MSG("extended_window at start of xlib_gl_extended_window constructor");
#ifdef __ANDROID__
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint major_version, minor_version;
eglInitialize(display_, &major_version, &minor_version);
spdlog::get("illixr")->info("EGL Initialized with major version : {} minor version: {}", major_version, minor_version);
const EGLint attribs[] = {// EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8,
EGL_ALPHA_SIZE, 0,
// EGL_DEPTH_SIZE, 24,
EGL_NONE};
EGLint w, h, format;
EGLint numConfigs;
EGLConfig config = nullptr;
eglChooseConfig(display_, attribs, &config, 1, &numConfigs);
std::unique_ptr<EGLConfig[]> supportedConfigs(new EGLConfig[numConfigs]);
assert(supportedConfigs);
eglChooseConfig(display_, attribs, supportedConfigs.get(), numConfigs, &numConfigs);
assert(numConfigs);
auto i = 0;
for (; i < numConfigs; i++) {
auto& cfg = supportedConfigs[i];
EGLint r, g, b, d;
if (eglGetConfigAttrib(display_, cfg, EGL_RED_SIZE, &r) && eglGetConfigAttrib(display_, cfg, EGL_GREEN_SIZE, &g) &&
eglGetConfigAttrib(display_, cfg, EGL_BLUE_SIZE, &b) && eglGetConfigAttrib(display_, cfg, EGL_DEPTH_SIZE, &d) &&
r == 8 && g == 8 && b == 8 && d == 24) {
config = supportedConfigs[i];
break;
}
}
if (i == numConfigs) {
config = supportedConfigs[0];
}
if (config == nullptr) {
return;
}
eglGetConfigAttrib(display_, config, EGL_NATIVE_VISUAL_ID, &format);
if (window != nullptr) {
spdlog::get("illixr")->info("window is not nullptr");
} else {
spdlog::get("illixr")->info("window is nullptr");
}
try {
surface_ = eglCreateWindowSurface(display_, config, window_, nullptr);
} catch (const std::exception& e) {
return;
}
EGLint ctxattrb[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
context_ = eglCreateContext(display_, config, egl_context, ctxattrb);
eglQuerySurface(display_, surface_, EGL_WIDTH, &w);
eglQuerySurface(display_, surface_, EGL_HEIGHT, &h);
#elif defined(_WIN32) || defined(_WIN64)
HINSTANCE hInstance = GetModuleHandle(nullptr);
const TCHAR* class_name = TEXT("ILLIXR_Extended_Window_Class");
// Register a window class
WNDCLASS wc = {};
if (!GetClassInfo(hInstance, class_name, &wc)) {
wc.style = CS_OWNDC;
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = class_name;
if (!RegisterClass(&wc)) {
ILLIXR::abort("RegisterClass failed");
}
}
// Create the window
hwnd_ = CreateWindow(wc.lpszClassName, TEXT("ILLIXR Extended Window"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, width_, height_, nullptr, nullptr, wc.hInstance, nullptr);
if (!hwnd_) {
ILLIXR::abort("CreateWindow failed");
}
// Get device context
hdc_ = GetDC(hwnd_);
// Choose a pixel format
PIXELFORMATDESCRIPTOR pfd = {};
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 = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
#else
display_ = XOpenDisplay(nullptr);
if (display_ == nullptr) {
ILLIXR::abort("Cannot connect to X server");
} else {
// Apparently, XOpenDisplay's _true_ error indication is whether display_ is nullptr.
// https://cboard.cprogramming.com/linux-programming/119957-xlib-perversity.html
// if (errno != 0) {
// std::cerr << "XOpenDisplay succeeded, but errno = " << errno << "; This is benign, so I'm clearing it now.\n";
// }
errno = 0;
}
Window root = DefaultRootWindow(display_);
// 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_RED_SIZE,
8,
GLX_GREEN_SIZE,
8,
GLX_BLUE_SIZE,
8,
GLX_ALPHA_SIZE,
8,
GLX_DEPTH_SIZE,
24,
GLX_STENCIL_SIZE,
8,
GLX_DOUBLEBUFFER,
True,
None};
#endif
#ifndef __ANDROID__
# ifndef NDEBUG
spdlog::get("illixr")->debug("[extended_window] Getting matching framebuffer configs");
# endif
RAC_ERRNO_MSG("extended_window before glXChooseFBConfig");
# if defined(_WIN32) || defined(_WIN64)
int pixel_format = ChoosePixelFormat(hdc_, &pfd);
if (pixel_format == 0) {
ILLIXR::abort("ChoosePixelFormat failed");
}
SetPixelFormat(hdc_, pixel_format, &pfd);
// Create a dummy context so we can load extensions
HGLRC temp_context = wglCreateContext(hdc_);
wglMakeCurrent(hdc_, temp_context);
// Load extension
auto wglCreateContextAttribsARB = (wglCreateContextAttribsARBProc) wglGetProcAddress("wglCreateContextAttribsARB");
if (!wglCreateContextAttribsARB) {
ILLIXR::abort("wglCreateContextAttribsARB not available, using legacy context");
context_ = temp_context;
} else {
int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, 0};
context_ = wglCreateContextAttribsARB(hdc_, shared_gl_context, attribs);
// Replace dummy
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(temp_context);
wglMakeCurrent(hdc_, context_);
}
# else
int fb_count = 0;
int screen = DefaultScreen(display_);
GLXFBConfig* fb_config = glXChooseFBConfig(display_, screen, visual_attribs, &fb_count);
if (!fb_config) {
ILLIXR::abort("Failed to retrieve a framebuffer config");
}
# ifndef NDEBUG
spdlog::get("illixr")->debug("[extended_window] Found {} matching FB configs", fb_count);
// Pick the FB config/visual with the most samples per pixel
spdlog::get("illixr")->debug("[extended_window] Getting XVisualInfos");
# endif
int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
int i;
for (i = 0; i < fb_count; ++i) {
XVisualInfo* vis_info = glXGetVisualFromFBConfig(display_, fb_config[i]);
if (vis_info) {
int samp_buf, samples;
glXGetFBConfigAttrib(display_, fb_config[i], GLX_SAMPLE_BUFFERS, &samp_buf);
glXGetFBConfigAttrib(display_, fb_config[i], GLX_SAMPLES, &samples);
# ifndef NDEBUG
spdlog::get("illixr")->debug(
"[extended_window] Matching fb_config {}, visual ID {:x}: SAMPLE_BUFFERS = {}, SAMPLES = {}", i,
vis_info->visualid, samp_buf, samples);
# endif
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(vis_info);
}
assert(0 <= best_fbc && best_fbc < fb_count);
GLXFBConfig g_best_fbc = fb_config[best_fbc];
// Free the FBConfig list allocated by glXChooseFBConfig()
XFree(fb_config);
// Get a visual
XVisualInfo* vis_info = glXGetVisualFromFBConfig(display_, g_best_fbc);
# ifndef NDEBUG
spdlog::get("illixr")->debug("[extended_window] Chose visual ID = {:x}", vis_info->visualid);
spdlog::get("illixr")->debug("[extended_window] Creating colormap");
# endif
color_map_ = XCreateColormap(display_, root, vis_info->visual, AllocNone);
# ifndef NDEBUG
spdlog::get("illixr")->debug("[extended_window] Creating window");
# endif
XSetWindowAttributes attributes;
attributes.colormap = color_map_;
attributes.background_pixel = 0;
attributes.border_pixel = 0;
attributes.event_mask = ExposureMask | KeyPressMask;
window_ = XCreateWindow(display_, root, 0, 0, width_, height_, 0, vis_info->depth, InputOutput, vis_info->visual,
CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &attributes);
if (!window_) {
ILLIXR::abort("Failed to create window");
}
XStoreName(display_, window_, "ILLIXR Extended Window");
XMapWindow(display_, window_);
// Done with visual info
XFree(vis_info);
# ifndef NDEBUG
spdlog::get("illixr")->debug("[extended_window] Creating context");
# endif
auto glXCreateContextAttribsARB =
(glXCreateContextAttribsARBProc) glXGetProcAddressARB((const GLubyte*) "glXCreateContextAttribsARB");
int context_attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, None};
context_ = glXCreateContextAttribsARB(display_, g_best_fbc, shared_gl_context, True, context_attribs);
// Sync to process errors
RAC_ERRNO_MSG("extended_window before XSync");
XSync(display_, false);
RAC_ERRNO_MSG("extended_window after XSync");
# ifndef NDEBUG
// Doing glXMakeCurrent here makes a third thread, the runtime one, enter the mix, and
// then there are three GL threads: runtime, timewarp, and gldemo, and the switching of
// contexts without synchronization during the initialization phase causes a data race.
// This is why native.yaml sometimes succeeds and sometimes doesn't. Headless succeeds
// because this behavior is OpenGL implementation dependent, and apparently mesa
// differs from NVIDIA in this regard.
// The proper fix is #173. Comment the below back in once #173 is done. In any case,
// this is just for debugging and does not affect any functionality.
/*
const bool gl_result_0 = static_cast(glXMakeCurrent(display_, window_, context_));
int major = 0, minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
std::cout << "OpenGL context created" << std::endl
<< "Version " << major << "." << minor << std::endl
<< "Vendor " << glGetString(GL_VENDOR) << std::endl
<< "Renderer " << glGetString(GL_RENDERER) << std::endl;
const bool gl_result_1 = static_cast(glXMakeCurrent(display_, None, nullptr));
*/
# endif
# endif
#endif
}
#ifdef __ANDROID__
xlib_gl_extended_window(int _width, int _height, EGLContext egl_context) {
width_ = _width;
height_ = _height;
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint major_version, minor_version;
eglInitialize(display_, &major_version, &minor_version);
spdlog::get("illixr")->info("EGL Initialized with major version : {} minor version: {}", major_version, minor_version);
const EGLint attribs[] = {// EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8,
EGL_ALPHA_SIZE, 0,
// EGL_DEPTH_SIZE, 24,
EGL_NONE};
// EGLint w, h;
EGLint format;
EGLint numConfigs;
EGLConfig config = nullptr;
eglChooseConfig(display_, attribs, &config, 1, &numConfigs);
std::unique_ptr<EGLConfig[]> supportedConfigs(new EGLConfig[numConfigs]);
assert(supportedConfigs);
eglChooseConfig(display_, attribs, supportedConfigs.get(), numConfigs, &numConfigs);
assert(numConfigs);
auto i = 0;
for (; i < numConfigs; i++) {
auto& cfg = supportedConfigs[i];
EGLint r, g, b, d;
if (eglGetConfigAttrib(display_, cfg, EGL_RED_SIZE, &r) && eglGetConfigAttrib(display_, cfg, EGL_GREEN_SIZE, &g) &&
eglGetConfigAttrib(display_, cfg, EGL_BLUE_SIZE, &b) && eglGetConfigAttrib(display_, cfg, EGL_DEPTH_SIZE, &d) &&
r == 8 && g == 8 && b == 8 && d == 24) {
config = supportedConfigs[i];
break;
}
}
if (i == numConfigs) {
config = supportedConfigs[0];
}
if (config == nullptr) {
return;
}
eglGetConfigAttrib(display_, config, EGL_NATIVE_VISUAL_ID, &format);
surface_ = EGL_NO_SURFACE;
EGLint ctxattrb[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
context_ = eglCreateContext(display_, config, egl_context, ctxattrb);
}
#endif
~xlib_gl_extended_window() override {
#ifdef __ANDROID__
if (display_ != EGL_NO_DISPLAY) {
eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (context_ != EGL_NO_CONTEXT) {
eglDestroyContext(display_, context_);
}
if (surface_ != EGL_NO_SURFACE) {
eglDestroySurface(display_, surface_);
}
eglTerminate(display_);
}
display_ = EGL_NO_DISPLAY;
context_ = EGL_NO_CONTEXT;
surface_ = EGL_NO_SURFACE;
#elif defined(_WIN32) || defined(_WIN64)
wglMakeCurrent(nullptr, nullptr);
if (context_) {
wglDeleteContext(context_);
}
if (hdc_) {
ReleaseDC(hwnd_, hdc_);
}
if (hwnd_) {
DestroyWindow(hwnd_);
}
#else
[[maybe_unused]] const bool gl_result = static_cast<bool>(glXMakeCurrent(display_, None, nullptr));
assert(gl_result && "glXMakeCurrent should not fail");
glXDestroyContext(display_, context_);
XDestroyWindow(display_, window_);
Window root = DefaultRootWindow(display_);
XDestroyWindow(display_, root);
XFreeColormap(display_, color_map_);
XCloseDisplay(display_);
#endif
}
int width_;
int height_;
CONTEXT_TYPE context_;
#if defined(_WIN32) || defined(_WIN64)
HWND hwnd_;
HDC hdc_;
#else
DISPLAY_TYPE display_;
WINDOW_TYPE window_;
# ifdef __ANDROID__
EGLSurface surface_;
# else
private:
Colormap color_map_;
# endif
#endif
};
} // namespace ILLIXR