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