Project
Loading...
Searching...
No Matches
GPUDisplayFrontendWayland.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15// Now the other headers
19#include "GPUDisplay.h"
20#include "GPULogging.h"
21#include <cstdio>
22#include <cstdlib>
23#include <cstring>
24#include <stdexcept>
25#include <chrono>
26#include <functional>
27
28#include <vulkan/vulkan.h>
29#include <vulkan/vulkan_wayland.h>
30
31#include <unistd.h>
32#include <fcntl.h>
33#include <sys/mman.h>
34#include <wayland-client.h>
35#include "xdg-shell-client-protocol.h"
36#include "xdg-decoration-client-protocol.h"
37#include <xkbcommon/xkbcommon.h>
38#include <linux/input-event-codes.h>
39
40using namespace o2::gpu;
41
42namespace o2::gpu::internal
43{
44template <class T, class... Args>
46 std::function<T(Args...)> func;
47 static T callback(void* context, Args... args)
48 {
49 const CCallWrapper* funcwrap = reinterpret_cast<const CCallWrapper*>(context);
50 return funcwrap->func(std::forward<Args>(args)...);
51 }
52};
53} // namespace o2::gpu::internal
54
60
61void GPUDisplayFrontendWayland::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton)
62{
63}
64
65int32_t GPUDisplayFrontendWayland::GetKey(uint32_t key, uint32_t state)
66{
67 int32_t retVal = 0;
68 if (mXKBkeymap) {
69 xkb_keysym_t sym = xkb_state_key_get_one_sym(mXKBstate, key + 8);
70 if (sym == 65453) {
71 return ('-');
72 } else if (sym == 65451) {
73 return ('+');
74 } else if (sym == XKB_KEY_Shift_L || sym == XKB_KEY_Shift_R) {
75 return (KEY_SHIFT);
76 } else if (sym == XKB_KEY_Alt_L) {
77 return (KEY_ALT);
78 } else if (sym == XKB_KEY_ISO_Level3_Shift || sym == XKB_KEY_Alt_R) {
79 return (KEY_RALT);
80 } else if (sym == XKB_KEY_Control_L) {
81 return (KEY_CTRL);
82 } else if (sym == XKB_KEY_Control_R) {
83 return (KEY_RCTRL);
84 } else if (sym == XKB_KEY_Up) {
85 return (KEY_UP);
86 } else if (sym == XKB_KEY_Down) {
87 return (KEY_DOWN);
88 } else if (sym == XKB_KEY_Left) {
89 return (KEY_LEFT);
90 } else if (sym == XKB_KEY_Right) {
91 return (KEY_RIGHT);
92 } else if (sym == XKB_KEY_Page_Up) {
93 return (KEY_PAGEUP);
94 } else if (sym == XKB_KEY_Page_Down) {
95 return (KEY_PAGEDOWN);
96 } else if (sym == XKB_KEY_Escape) {
97 return (KEY_ESCAPE);
98 } else if (sym == XKB_KEY_Return) {
99 return (KEY_ENTER);
100 } else if (sym == XKB_KEY_End) {
101 return (KEY_END);
102 } else if (sym == XKB_KEY_Home) {
103 return (KEY_HOME);
104 } else if (sym == XKB_KEY_Insert) {
105 return (KEY_INSERT);
106 } else if (sym == XKB_KEY_F1) {
107 return (KEY_F1);
108 } else if (sym == XKB_KEY_F2) {
109 return (KEY_F2);
110 } else if (sym == XKB_KEY_F3) {
111 return (KEY_F3);
112 } else if (sym == XKB_KEY_F4) {
113 return (KEY_F4);
114 } else if (sym == XKB_KEY_F5) {
115 return (KEY_F5);
116 } else if (sym == XKB_KEY_F6) {
117 return (KEY_F6);
118 } else if (sym == XKB_KEY_F7) {
119 return (KEY_F7);
120 } else if (sym == XKB_KEY_F8) {
121 return (KEY_F8);
122 } else if (sym == XKB_KEY_F9) {
123 return (KEY_F9);
124 } else if (sym == XKB_KEY_F10) {
125 return (KEY_F10);
126 } else if (sym == XKB_KEY_F11) {
127 return (KEY_F11);
128 } else if (sym == XKB_KEY_F12) {
129 return (KEY_F12);
130 } else if (sym == 32) {
131 return (KEY_SPACE);
132 } else if (sym > 255) {
133 return (0);
134 } else {
135 retVal = xkb_keysym_to_utf32(sym);
136 }
137 }
138 if (retVal > 255) {
139 return (0);
140 }
141 return retVal;
142}
143
144void GPUDisplayFrontendWayland::createBuffer(uint32_t width, uint32_t height)
145{
146 const uint32_t stride = width * 4;
147 const uint32_t size = stride * height;
148 if (ftruncate(mFd, size) < 0) {
149 throw std::runtime_error("Error setting waysland shm file size");
150 }
151 void* shm_data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, mFd, 0);
152 if (shm_data == MAP_FAILED) {
153 throw std::runtime_error("wayland mmap failed");
154 }
155 memset(shm_data, 0, size);
156 munmap(shm_data, size);
157
158 mPool = wl_shm_create_pool(mShm, mFd, size);
159 mBuffer = wl_shm_pool_create_buffer(mPool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888);
162 wl_surface_attach(mSurface, mBuffer, 0, 0);
163 wl_surface_commit(mSurface);
164}
165
166void GPUDisplayFrontendWayland::recreateBuffer(uint32_t width, uint32_t height)
167{
168 wl_surface_attach(mSurface, nullptr, 0, 0);
169 wl_surface_commit(mSurface);
170 wl_buffer_destroy(mBuffer);
171 wl_shm_pool_destroy(mPool);
172 createBuffer(width, height);
173}
174
175int32_t GPUDisplayFrontendWayland::FrontendMain()
176{
177 if (backend()->backendType() != GPUDisplayBackend::TYPE_VULKAN) {
178 fprintf(stderr, "Only Vulkan backend supported\n");
179 return 1;
180 }
181
182 mXKBcontext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
183 if (mXKBcontext == nullptr) {
184 throw std::runtime_error("Error initializing xkb context");
185 }
186
187 mWayland = wl_display_connect(nullptr);
188 if (mWayland == nullptr) {
189 throw std::runtime_error("Could not connect to wayland display");
190 }
191 mRegistry = wl_display_get_registry(mWayland);
192 if (mRegistry == nullptr) {
193 throw std::runtime_error("Could not create wayland registry");
194 }
195
196 mFd = memfd_create("/tmp/ca-gpu-display-wayland-memfile", 0);
197 if (mFd < 0) {
198 throw std::runtime_error("Error creating wayland shm segment file descriptor");
199 }
200
201 auto pointer_enter = [](void* data, wl_pointer* wl_pointer, uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
202 };
203 auto pointer_leave = [](void* data, wl_pointer* wl_pointer, uint32_t serial, wl_surface* wl_surface) {
204 };
205 auto pointer_motion = [](void* data, wl_pointer* wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
207 me->mMouseMvX = wl_fixed_to_double(surface_x);
208 me->mMouseMvY = wl_fixed_to_double(surface_y);
209 };
210 auto pointer_button = [](void* data, wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
212 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
213 if (button == BTN_RIGHT) {
214 me->mMouseDnR = true;
215 } else if (button == BTN_LEFT) {
216 me->mMouseDn = true;
217 }
218 me->mMouseDnX = me->mMouseMvX;
219 me->mMouseDnY = me->mMouseMvY;
220 } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
221 if (button == BTN_RIGHT) {
222 me->mMouseDnR = false;
223 } else if (button == BTN_LEFT) {
224 me->mMouseDn = false;
225 }
226 }
227 };
228 auto pointer_axis = [](void* data, wl_pointer* wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
229 if (axis == 0) {
230 ((GPUDisplayFrontendWayland*)data)->mMouseWheel += wl_fixed_to_double(value) * (-100.f / 15.f);
231 }
232 };
233#pragma GCC diagnostic push
234#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
235 const wl_pointer_listener pointer_listener = {.enter = pointer_enter, .leave = pointer_leave, .motion = pointer_motion, .button = pointer_button, .axis = pointer_axis, .frame = nullptr, .axis_source = nullptr, .axis_stop = nullptr, .axis_discrete = nullptr};
236#pragma GCC diagnostic pop
237
238 auto keyboard_keymap = [](void* data, wl_keyboard* wl_keyboard, uint32_t format, int32_t fd, uint32_t size) {
240 if (me->mXKBkeymap) {
241 xkb_state_unref(me->mXKBstate);
242 xkb_keymap_unref(me->mXKBkeymap);
243 }
244 char* keymap_string = (char*)mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
245 me->mXKBkeymap = xkb_keymap_new_from_string(me->mXKBcontext, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
246 me->mXKBstate = xkb_state_new(me->mXKBkeymap);
247 munmap(keymap_string, size);
248 close(fd);
249 };
250 auto keyboard_enter = [](void* data, wl_keyboard* wl_keyboard, uint32_t serial, wl_surface* surface, wl_array* keys) {};
251 auto keyboard_leave = [](void* data, wl_keyboard* wl_keyboard, uint32_t serial, wl_surface* surface) {};
252 auto keyboard_key = [](void* data, wl_keyboard* wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
254 int32_t symbol = me->GetKey(key, state);
255 int32_t keyPress = (symbol >= 'a' && symbol <= 'z') ? symbol + 'A' - 'a' : symbol;
256 if (state == XKB_KEY_DOWN) {
257 me->mKeys[keyPress] = true;
258 me->mKeysShift[keyPress] = me->mKeys[KEY_SHIFT];
259 me->HandleKey(symbol);
260 } else {
261 me->mKeys[keyPress] = false;
262 me->mKeysShift[keyPress] = false;
263 }
264 };
265 auto keyboard_modifiers = [](void* data, wl_keyboard* wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
267 xkb_state_update_mask(me->mXKBstate, mods_depressed, mods_latched, mods_locked, 0, 0, group);
268 };
269 auto keyboard_repat = [](void* data, wl_keyboard* wl_keyboard, int32_t rate, int32_t delay) {};
270 const wl_keyboard_listener keyboard_listener = {.keymap = keyboard_keymap, .enter = keyboard_enter, .leave = keyboard_leave, .key = keyboard_key, .modifiers = keyboard_modifiers, .repeat_info = keyboard_repat};
271
272 auto xdg_wm_base_ping = [](void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial) {
273 xdg_wm_base_pong(xdg_wm_base, serial);
274 };
275 const xdg_wm_base_listener xdg_wm_base_listener = {
276 .ping = xdg_wm_base_ping,
277 };
278
279 auto seat_capabilities = [&](struct wl_seat* seat, uint32_t capabilities) {
280 if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
281 mPointer = wl_seat_get_pointer(mSeat);
282 wl_pointer_add_listener(mPointer, &pointer_listener, this);
283 }
284 if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
285 mKeyboard = wl_seat_get_keyboard(mSeat);
286 wl_keyboard_add_listener(mKeyboard, &keyboard_listener, this);
287 }
288 };
289 auto seat_capabilities_c = internal::CCallWrapper<void, wl_seat*, uint32_t>{[seat_capabilities](wl_seat* seat, uint32_t capabilities) { seat_capabilities(seat, capabilities); }};
290
291 auto seat_name = [](void* data, struct wl_seat* seat, const char* name) {
292 if (((GPUDisplayFrontendWayland*)data)->mDisplay->param()->par.debugLevel >= 2) {
293 GPUInfo("Wayland seat: %s", name);
294 }
295 };
296 const wl_seat_listener seat_listener = {
297 .capabilities = seat_capabilities_c.callback,
298 .name = seat_name,
299 };
300
301 auto registry_global = [&](wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
302 if (mDisplay->param()->par.debugLevel >= 3) {
303 GPUInfo("Available interface %s", interface);
304 }
305 if (strcmp(interface, wl_output_interface.name) == 0) {
306 mOutput = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 1);
307 // wl_output_add_listener(mOutput, &output_listener, this);
308 } else if (strcmp(interface, wl_compositor_interface.name) == 0) {
309 mCompositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, 1);
310 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
311 mShm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1);
312 } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
313 mXdgBase = (xdg_wm_base*)wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
314 xdg_wm_base_add_listener(mXdgBase, &xdg_wm_base_listener, this);
315 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
316 mSeat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1);
317 wl_seat_add_listener(mSeat, &seat_listener, &seat_capabilities_c);
318 } else if (strcmp(interface, zxdg_toplevel_decoration_v1_interface.name) == 0) {
319 mDecManager = (zxdg_decoration_manager_v1*)wl_registry_bind(registry, name, &zxdg_toplevel_decoration_v1_interface, 1);
320 }
321 };
322
323 auto registry_global_c = internal::CCallWrapper<void, wl_registry*, uint32_t, const char*, uint32_t>{[registry_global](wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { registry_global(registry, name, interface, version); }};
324 auto registry_global_remove = [](void* a, wl_registry* b, uint32_t c) {};
325 const wl_registry_listener registry_listener = {.global = &registry_global_c.callback, .global_remove = registry_global_remove};
326
327 wl_registry_add_listener(mRegistry, &registry_listener, &registry_global_c);
328 wl_display_roundtrip(mWayland);
329
330 if (mCompositor == nullptr || mShm == nullptr || mXdgBase == nullptr || mSeat == nullptr || mOutput == nullptr) {
331 throw std::runtime_error("Error getting wayland objects");
332 }
333
334 mSurface = wl_compositor_create_surface(mCompositor);
335 if (mSurface == nullptr) {
336 throw std::runtime_error("Error creating wayland surface");
337 }
338 mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface);
339 if (mXdgSurface == nullptr) {
340 throw std::runtime_error("Error creating wayland xdg surface");
341 }
342 mXdgToplevel = xdg_surface_get_toplevel(mXdgSurface);
343
344 auto xdg_toplevel_handle_configure = [](void* data, xdg_toplevel* toplevel, int32_t width, int32_t height, wl_array* states) {
346 if (me->mDisplay->param()->par.debugLevel >= 3) {
347 GPUInfo("Wayland surface resized to %d %d", width, height);
348 }
349 me->mWidthRequested = width;
350 me->mHeightRequested = height;
351 };
352
353 auto xdg_surface_handle_configure = [](void* data, xdg_surface* surface, uint32_t serial) {
355 xdg_surface_ack_configure(me->mXdgSurface, serial);
356 if (me->mWidthRequested && me->mHeightRequested && (me->mWidthRequested != me->mDisplayWidth || me->mHeightRequested != me->mDisplayHeight)) {
357 me->recreateBuffer(me->mWidthRequested, me->mHeightRequested);
358 me->ResizeScene(me->mDisplayWidth, me->mDisplayHeight);
359 } else {
360 wl_surface_commit(((GPUDisplayFrontendWayland*)data)->mSurface);
361 }
362 me->mWidthRequested = me->mHeightRequested = 0;
363 };
364
365 auto xdg_toplevel_handle_close = [](void* data, xdg_toplevel* toplevel) {
366 ((GPUDisplayFrontendWayland*)data)->mDisplayControl = 2;
367 };
368
369#pragma GCC diagnostic push
370#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
371 const xdg_surface_listener xdg_surface_listener = {
372 .configure = xdg_surface_handle_configure,
373 };
374
375 const xdg_toplevel_listener xdg_toplevel_listener = {
376 .configure = xdg_toplevel_handle_configure,
377 .close = xdg_toplevel_handle_close,
378 .configure_bounds = nullptr};
379#pragma GCC diagnostic pop
380
381 xdg_surface_add_listener(mXdgSurface, &xdg_surface_listener, this);
382 xdg_toplevel_add_listener(mXdgToplevel, &xdg_toplevel_listener, this);
383
384 xdg_toplevel_set_title(mXdgToplevel, DISPLAY_WINDOW_NAME);
385
386 if (mDecManager) {
387 printf("Enabling decoration\n");
388 }
389
390 wl_surface_commit(mSurface);
391 wl_display_roundtrip(mWayland);
392
394
395 if (InitDisplay()) {
396 return (1);
397 }
398
399 while (wl_display_dispatch(mWayland) != -1 && mDisplayControl != 2) {
401 DrawGLScene();
402 wl_surface_damage(mSurface, 0, 0, mDisplayWidth, mDisplayHeight);
403 usleep(10000);
404 }
405
406 ExitDisplay();
407
408 if (mXKBkeymap) {
409 xkb_state_unref(mXKBstate);
410 xkb_keymap_unref(mXKBkeymap);
411 mXKBkeymap = nullptr;
412 }
413 xkb_context_unref(mXKBcontext);
414
415 pthread_mutex_lock(&mSemLockExit);
416 mDisplayRunning = true;
417 pthread_mutex_unlock(&mSemLockExit);
418
419 pthread_mutex_lock(&mSemLockExit);
420 mDisplayRunning = false;
421 pthread_mutex_unlock(&mSemLockExit);
422
423 if (mPointer) {
424 wl_pointer_release(mPointer);
425 mPointer = nullptr;
426 }
427 if (mKeyboard) {
428 wl_keyboard_release(mKeyboard);
429 mKeyboard = nullptr;
430 }
431 xdg_toplevel_destroy(mXdgToplevel);
432 xdg_surface_destroy(mXdgSurface);
433 wl_surface_destroy(mSurface);
434 wl_buffer_destroy(mBuffer);
435 wl_shm_pool_destroy(mPool);
436 wl_registry_destroy(mRegistry);
437 wl_display_disconnect(mWayland);
438 close(mFd);
439
440 return (0);
441}
442
444{
445 pthread_mutex_lock(&mSemLockExit);
446 if (mDisplayRunning) {
447 mDisplayControl = 2;
448 }
449 pthread_mutex_unlock(&mSemLockExit);
450 while (mDisplayRunning) {
451 usleep(10000);
452 }
453}
454
456{
457 if (set) {
458 xdg_toplevel_set_fullscreen(mXdgToplevel, mOutput);
459 } else {
460 xdg_toplevel_unset_fullscreen(mXdgToplevel);
461 }
462}
463
465{
466 if (set) {
467 xdg_toplevel_set_maximized(mXdgToplevel);
468 } else {
469 xdg_toplevel_unset_maximized(mXdgToplevel);
470 }
471}
472
476
478{
479 static pthread_t hThread;
480 if (pthread_create(&hThread, nullptr, FrontendThreadWrapper, this)) {
481 GPUError("Coult not Create frontend Thread...");
482 return (1);
483 }
484 return (0);
485}
486
492
493int32_t GPUDisplayFrontendWayland::getVulkanSurface(void* instance, void* surface)
494{
495 VkWaylandSurfaceCreateInfoKHR info{};
496 info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
497 info.flags = 0;
498 info.display = mWayland;
499 info.surface = mSurface;
500 return vkCreateWaylandSurfaceKHR(*(VkInstance*)instance, &info, nullptr, (VkSurfaceKHR*)surface) != VK_SUCCESS;
501}
502
504{
505 static const char* exts[] = {"VK_KHR_surface", "VK_KHR_wayland_surface"};
506 p = exts;
507 return 2;
508}
benchmark::State & state
int16_t time
Definition RawEventData.h:4
int32_t retVal
uint32_t c
Definition RawData.h:2
uint32_t version
Definition RawData.h:8
StringRef key
int32_t getVulkanSurface(void *instance, void *surface) override
void getSize(int32_t &width, int32_t &height) override
void OpenGLPrint(const char *s, float x, float y, float r, float g, float b, float a, bool fromBotton=true) override
uint32_t getReqVulkanExtensions(const char **&p) override
static constexpr int32_t KEY_F9
static constexpr int32_t KEY_F2
static void * FrontendThreadWrapper(void *)
static constexpr int32_t KEY_ALT
static constexpr int32_t KEY_F12
static constexpr int32_t KEY_F7
static constexpr int32_t KEY_END
static constexpr int32_t KEY_PAGEDOWN
static constexpr int32_t KEY_RALT
static constexpr int32_t KEY_F10
static constexpr int32_t KEY_F4
static constexpr int32_t KEY_LEFT
static constexpr int32_t KEY_ENTER
static constexpr int32_t KEY_F6
static constexpr int32_t KEY_CTRL
static constexpr int32_t KEY_F1
static constexpr const char * DISPLAY_WINDOW_NAME
static constexpr int32_t INIT_WIDTH
static constexpr int32_t KEY_SHIFT
static constexpr int32_t KEY_F3
static constexpr int32_t KEY_INSERT
static constexpr int32_t KEY_F11
static constexpr int32_t KEY_SPACE
static constexpr int32_t KEY_F5
static constexpr int32_t KEY_PAGEUP
static constexpr int32_t KEY_HOME
int32_t InitDisplay(bool initFailure=false)
static constexpr int32_t KEY_UP
static constexpr int32_t KEY_DOWN
static constexpr int32_t KEY_F8
static constexpr int32_t INIT_HEIGHT
static constexpr int32_t KEY_RIGHT
static constexpr int32_t KEY_RCTRL
static constexpr int32_t KEY_ESCAPE
const GPUParam * param()
Definition GPUDisplay.h:74
GLint GLenum GLint x
Definition glcorearb.h:403
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLenum * rate
Definition glcorearb.h:5735
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLsizei GLsizei height
Definition glcorearb.h:270
GLint GLsizei width
Definition glcorearb.h:270
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLboolean GLuint group
Definition glcorearb.h:3991
GLboolean GLboolean g
Definition glcorearb.h:1233
GLint GLenum GLboolean GLsizei stride
Definition glcorearb.h:867
GLboolean enable
Definition glcorearb.h:3991
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
GLuint * states
Definition glcorearb.h:4932
Polygon< T > close(Polygon< T > polygon)
Definition Polygon.h:126
constexpr value_T me
Definition TrackUtils.h:128
static T callback(void *context, Args... args)
std::vector< std::byte > createBuffer(gsl::span< std::string > data, uint32_t orbit=12345, uint16_t bc=678)