forked from dhewm/dhewm3
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathglimp.cpp
More file actions
1631 lines (1428 loc) · 57.3 KB
/
Copy pathglimp.cpp
File metadata and controls
1631 lines (1428 loc) · 57.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/sys_sdl.h"
#if SDL_VERSION_ATLEAST(3, 0, 0)
// backwards-compat with SDL2
#define SDL_WINDOW_ALLOW_HIGHDPI SDL_WINDOW_HIGH_PIXEL_DENSITY
#define SDL_GL_DeleteContext SDL_GL_DestroyContext
typedef SDL_WindowFlags My_SDL_WindowFlags;
#else // SDL1.2 or SDL2
// for compat with SDL3 - unfortunately SDL2 also has a SDL_WindowFlags type, but it's an enum
typedef Uint32 My_SDL_WindowFlags;
#endif
#include "sys/platform.h"
#include "framework/Licensee.h"
#include "renderer/tr_local.h"
#include "sys/sys_imgui.h"
#if defined(_WIN32) && defined(ID_ALLOW_TOOLS)
#include "sys/win32/win_local.h"
// SDL3 doesn't have SDL_syswm.h
#if ! SDL_VERSION_ATLEAST(3, 0, 0)
#include <SDL_syswm.h>
#endif
// from SDL_windowsopengl.h (internal SDL2 header)
#ifndef WGL_ARB_pixel_format
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_NEED_PALETTE_ARB 0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
#define WGL_TRANSPARENT_ARB 0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
#define WGL_SHARE_DEPTH_ARB 0x200C
#define WGL_SHARE_STENCIL_ARB 0x200D
#define WGL_SHARE_ACCUM_ARB 0x200E
#define WGL_SUPPORT_GDI_ARB 0x200F
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_STEREO_ARB 0x2012
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_RED_SHIFT_ARB 0x2016
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_GREEN_SHIFT_ARB 0x2018
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_BLUE_SHIFT_ARB 0x201A
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_ALPHA_SHIFT_ARB 0x201C
#define WGL_ACCUM_BITS_ARB 0x201D
#define WGL_ACCUM_RED_BITS_ARB 0x201E
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_AUX_BUFFERS_ARB 0x2024
#define WGL_NO_ACCELERATION_ARB 0x2025
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_SWAP_EXCHANGE_ARB 0x2028
#define WGL_SWAP_COPY_ARB 0x2029
#define WGL_SWAP_UNDEFINED_ARB 0x202A
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_TYPE_COLORINDEX_ARB 0x202C
#endif
#ifndef WGL_ARB_multisample
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
#endif
#endif // _WIN32 and ID_ALLOW_TOOLS
#if SDL_VERSION_ATLEAST(2, 0, 0)
static SDL_Window *window = NULL;
static SDL_GLContext context = NULL;
#else
static SDL_Surface *window = NULL;
#define SDL_WINDOW_OPENGL SDL_OPENGL
#define SDL_WINDOW_FULLSCREEN SDL_FULLSCREEN
#ifdef DHEWM3_VULKAN
// SDL1.2 can't do Vulkan windows; GLimp_VulkanProbe() already returns false
// there, so this define only keeps the (unsupported) combo compiling
#define SDL_WINDOW_VULKAN 0
#endif
#endif
#if SDL_VERSION_ATLEAST(3, 0, 0)
extern idCVar in_grabKeyboard;
static void SetSDLIcon()
{
#include "doom_icon.h" // contains the struct d3_icon
SDL_Surface* icon = SDL_CreateSurfaceFrom(d3_icon.width, d3_icon.height,
SDL_PIXELFORMAT_RGBA32, (void*)d3_icon.pixel_data,
d3_icon.bytes_per_pixel*d3_icon.width);
SDL_SetWindowIcon(window, icon);
SDL_DestroySurface(icon);
}
#else // SDL2 and SDL1.2
static void SetSDLIcon()
{
Uint32 rmask, gmask, bmask, amask;
// ok, the following is pretty stupid.. SDL_CreateRGBSurfaceFrom() pretends to use a void* for the data,
// but it's really treated as endian-specific Uint32* ...
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
#include "doom_icon.h" // contains the struct d3_icon
SDL_Surface* icon = SDL_CreateRGBSurfaceFrom((void*)d3_icon.pixel_data, d3_icon.width, d3_icon.height,
d3_icon.bytes_per_pixel*8, d3_icon.bytes_per_pixel*d3_icon.width,
rmask, gmask, bmask, amask);
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_SetWindowIcon(window, icon);
#else
SDL_WM_SetIcon(icon, NULL);
#endif
SDL_FreeSurface(icon);
}
#endif // SDL2 and SDL1.2
#if !SDL_VERSION_ATLEAST(3, 0, 0)
/*
===================
GLimp_ClosestFullscreenMode
SDL2: build a fully-specified SDL_DisplayMode for a real-fullscreen window, snapped to
the closest mode the display actually offers. Passing SDL_SetWindowDisplayMode() a mode
with only w/h set (refresh_rate == 0) makes SDL pick the driver's *default* mode for that
size — commonly 60 Hz even on a 144 Hz panel, which forces the whole engine to 60 Hz.
SDL_GetClosestDisplayMode() instead scans the real mode list: displayHz (r_displayRefresh)
is honored when non-zero, and 0 defaults to the desktop refresh, which is what a high-
refresh user expects. Falls back to the bare w/h request if no mode matches.
===================
*/
static SDL_DisplayMode GLimp_ClosestFullscreenMode( int w, int h, int displayHz, int displayIndex ) {
SDL_DisplayMode want = {};
want.w = w;
want.h = h;
want.refresh_rate = displayHz; // 0 -> SDL treats as the desktop refresh
SDL_DisplayMode closest = {};
if ( SDL_GetClosestDisplayMode( displayIndex, &want, &closest ) != NULL ) {
return closest;
}
return want; // no match: keep at least the resolution
}
#endif // SDL2
#ifdef DHEWM3_VULKAN
#if SDL_VERSION_ATLEAST(3, 0, 0)
#include <SDL3/SDL_vulkan.h>
#elif SDL_VERSION_ATLEAST(2, 0, 0)
#include <SDL_vulkan.h>
#endif
/*
===================
GLimp_VulkanProbe
Phase 4 M0 scaffolding (docs/vulkan-backend.md): prove SDL can create a
Vulkan-capable window (no SDL_WINDOW_OPENGL, no GL context, loads the Vulkan
library) and hand us the instance extension list, without touching the GL path.
At M1 this grows into the real window bring-up: the game window itself is
created with SDL_WINDOW_VULKAN and qgl loading is skipped.
===================
*/
bool GLimp_VulkanProbe( void ) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
assert( SDL_WasInit( SDL_INIT_VIDEO ) );
// creating the window is the real test: it loads the Vulkan library and
// verifies the video driver can do Vulkan surfaces at all
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_Window *probe = SDL_CreateWindow( "dude-vulkan-probe", 64, 64,
SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN );
#else
SDL_Window *probe = SDL_CreateWindow( "dude-vulkan-probe",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
64, 64, SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN );
#endif
if ( probe == NULL ) {
common->Warning( "Vulkan probe: SDL can't create a Vulkan-capable window: %s", SDL_GetError() );
return false;
}
bool ok = false;
#if SDL_VERSION_ATLEAST(3, 0, 0)
Uint32 extCount = 0;
const char * const * exts = SDL_Vulkan_GetInstanceExtensions( &extCount );
if ( exts != NULL ) {
ok = true;
common->Printf( "Vulkan probe: window OK, %u instance extensions:\n", (unsigned)extCount );
for ( Uint32 i = 0; i < extCount; i++ ) {
common->Printf( " %s\n", exts[i] );
}
}
#else
unsigned int extCount = 0;
if ( SDL_Vulkan_GetInstanceExtensions( probe, &extCount, NULL ) ) {
const char *exts[32] = {};
unsigned int n = extCount < 32 ? extCount : 32;
if ( SDL_Vulkan_GetInstanceExtensions( probe, &n, exts ) ) {
ok = true;
common->Printf( "Vulkan probe: window OK, %u instance extensions:\n", extCount );
for ( unsigned int i = 0; i < n; i++ ) {
common->Printf( " %s\n", exts[i] );
}
}
}
#endif
if ( !ok ) {
common->Warning( "Vulkan probe: SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError() );
}
SDL_DestroyWindow( probe );
return ok;
#else // SDL1.2
common->Warning( "Vulkan probe: the SDL 1.2 build has no Vulkan support" );
return false;
#endif
}
#endif // DHEWM3_VULKAN
/*
===================
GLimp_Init
===================
*/
// DUDE Phase 4: true while the live window is a Vulkan one (no GL context) —
// gates the GL-only paths below (swap, gamma ramp, swap interval, ImGui-on-GL)
static bool windowIsVulkan = false;
bool GLimp_Init(glimpParms_t parms) {
common->Printf("Initializing OpenGL subsystem\n");
assert(SDL_WasInit(SDL_INIT_VIDEO));
windowIsVulkan = false;
#ifdef DHEWM3_VULKAN
windowIsVulkan = parms.vulkan;
#endif
// DUDE Phase 4: Vulkan mode reuses the whole window machinery (display
// pick, fullscreen modes, HighDPI) but asks for a Vulkan-capable window
// and never creates a GL context — presentation is the backend's
// swapchain. The SDL_GL_SetAttribute calls further down are inert without
// the OPENGL flag, so they need no guards of their own.
My_SDL_WindowFlags flags = windowIsVulkan ? SDL_WINDOW_VULKAN : SDL_WINDOW_OPENGL;
if (parms.fullScreen == 1)
{
#if SDL_VERSION_ATLEAST(3, 0, 0)
// in SDL3 windows with SDL_WINDOW_FULLSCREEN set are fullscreen-desktop by default
// and for exclusive fullscreen SDL_SetWindowFullscreenMode() must be called
// after creating the window, so only set the flag if we want fullscreen-desktop mode
if(parms.fullScreen && parms.fullScreenDesktop) {
flags |= SDL_WINDOW_FULLSCREEN;
}
#elif SDL_VERSION_ATLEAST(2, 0, 0)
flags |= parms.fullScreenDesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
#else // SDL1.2
flags |= SDL_WINDOW_FULLSCREEN;
#endif
}
r_windowResizable.ClearModified();
#if SDL_VERSION_ATLEAST(2, 0, 0)
flags |= SDL_WINDOW_ALLOW_HIGHDPI;
if ( r_windowResizable.GetBool() ) {
flags |= SDL_WINDOW_RESIZABLE;
}
/* Doom3 has the nasty habit of modifying the default framebuffer's alpha channel and then
* relying on those modifications in blending operations (using GL_DST_(ONE_MINUS_)ALPHA).
* So far that hasn't been much of a problem, because Windows, macOS, X11 etc
* just ignore the alpha chan (unless maybe you explicitly tell a window it should be transparent).
* Unfortunately, Wayland by default *does* use the alpha channel, which often leads to
* rendering bugs (the window is partly transparent or very white in areas with low alpha).
* Mesa introduced an EGL extension that's supposed to fix that (EGL_EXT_present_opaque)
* and newer SDL2 versions use it by default (in the Wayland backend).
* Unfortunately, the implementation of that extension was broken (at least in Mesa before 24.1),
* seems like they just give you a visual without any alpha chan - which doesn't
* work for Doom3, as it needs a functioning alpha chan for blending operations, see above.
* See also: https://gitlab.freedesktop.org/mesa/mesa/-/issues/5886
*
* So to make sure DUDE (finally) works as expected on Wayland, we tell SDL2 to
* allow transparency and then fill the alpha-chan ourselves in RB_SwapBuffers()
* (unless the user disables that with r_fillWindowAlphaChan 0)
*
* NOTE: This bug is fixed in Mesa 24.1 and newer, and doesn't seem to occur with recent
* NVIDIA drivers either, so for SDL3 (which should be mostly used with current drivers/mesa)
* I don't enable this hack by default. If r_fillWindowAlphaChan == 1, it's enabled
* when creating the window, though.
*/
#ifdef SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY
SDL_SetHint(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, "1");
#elif SDL_MAJOR_VERSION == 2 // little hack so this works if the SDL2 version used for building is older than runtime version
SDL_SetHint("SDL_VIDEO_EGL_ALLOW_TRANSPARENCY", "1");
#endif
#endif
int colorbits = 24;
int depthbits = 24;
int stencilbits = 8;
for (int i = 0; i < 16; i++) {
int multisamples = parms.multiSamples;
// 0 - default
// 1 - minus colorbits
// 2 - minus depthbits
// 3 - minus stencil
if ((i % 4) == 0 && i) {
// one pass, reduce
switch (i / 4) {
case 2 :
if (colorbits == 24)
colorbits = 16;
break;
case 1 :
if (depthbits == 24)
depthbits = 16;
else if (depthbits == 16)
depthbits = 8;
case 3 :
if (stencilbits == 24)
stencilbits = 16;
else if (stencilbits == 16)
stencilbits = 8;
}
}
int tcolorbits = colorbits;
int tdepthbits = depthbits;
int tstencilbits = stencilbits;
if ((i % 4) == 3) {
// reduce colorbits
if (tcolorbits == 24)
tcolorbits = 16;
}
if ((i % 4) == 2) {
// reduce depthbits
if (tdepthbits == 24)
tdepthbits = 16;
else if (tdepthbits == 16)
tdepthbits = 8;
}
if ((i % 4) == 1) {
// reduce stencilbits
if (tstencilbits == 24)
tstencilbits = 16;
else if (tstencilbits == 16)
tstencilbits = 8;
else
tstencilbits = 0;
}
int channelcolorbits = 4;
if (tcolorbits == 24)
channelcolorbits = 8;
int talphabits = channelcolorbits;
try_again:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, channelcolorbits);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, channelcolorbits);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, channelcolorbits);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, tdepthbits);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, tstencilbits);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, talphabits);
SDL_GL_SetAttribute(SDL_GL_STEREO, parms.stereo ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (multisamples > 1) ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisamples);
// DUDE: GL 3.3 core context for the opengl3 backend; explicitly reset
// to legacy defaults otherwise so vid_restart can switch back
if ( parms.coreProfile ) {
common->Printf( "Requesting an OpenGL 3.3 Core context (r_graphicsAPI opengl3)\n" );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
} else {
// SDL's defaults: 2.1 compatibility
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 0 );
}
#if SDL_VERSION_ATLEAST(2, 0, 0) // SDL2 and SDL3 window creation
if ( r_glDebugContext.GetBool() ) {
common->Printf( "Requesting an OpenGL Debug Context (r_glDebugContext is enabled)\n" );
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
}
if ( parms.fullScreen && parms.fullScreenDesktop ) {
common->Printf( "Will create a pseudo-fullscreen window at the current desktop resolution\n" );
} else {
const char* windowMode = parms.fullScreen ? "fullscreen-" : "";
common->Printf("Will create a %swindow with resolution %dx%d (r_mode = %d)\n",
windowMode, parms.width, parms.height, r_mode.GetInteger());
}
Uint32 selectedDisplay = 0;
#if SDL_VERSION_ATLEAST(2, 0, 4)
// try to put the window on the display the mousecursor currently is on
{
#if SDL_VERSION_ATLEAST(3, 0, 0)
float x, y;
int numDisplays = 0;
SDL_DisplayID* displayIDs = SDL_GetDisplays(&numDisplays);
#else // SDL2
int numDisplays = SDL_GetNumVideoDisplays();
int x, y;
#endif
SDL_GetGlobalMouseState(&x, &y);
common->Printf("SDL detected %d displays: \n", numDisplays);
bool found = false;
for ( int j=0; j<numDisplays; ++j ) {
SDL_Rect rect;
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_DisplayID displayId_x = displayIDs[j];
int numModes = 0;
SDL_DisplayMode** modes = SDL_GetFullscreenDisplayModes(displayId_x, &numModes);
common->Printf( " Display %d (ID %u) has the following modes:\n", j, displayId_x );
for ( int dmIdx=0; dmIdx < numModes; ++dmIdx ) {
SDL_DisplayMode* mode = modes[dmIdx];
common->Printf( " - %d x %d @ %g Hz, density %g \n", mode->w, mode->h, mode->refresh_rate, mode->pixel_density );
}
SDL_free( modes );
if ( SDL_GetDisplayBounds(displayId_x, &rect) ) {
common->Printf(" Currently: %dx%d at (%d, %d) to (%d, %d)\n", rect.w, rect.h,
rect.x, rect.y, rect.x+rect.w, rect.y+rect.h);
#else // SDL2
int displayId_x = j;
if (SDL_GetDisplayBounds(displayId_x, &rect) == 0) {
// include the desktop refresh so mixed-rate multi-monitor users can tell
// which index is their high-refresh panel (see r_fullscreenDisplay)
SDL_DisplayMode ddm = {};
int ddHz = ( SDL_GetDesktopDisplayMode(j, &ddm) == 0 ) ? ddm.refresh_rate : 0;
common->Printf(" %d: %dx%d at (%d, %d) to (%d, %d) @ %d Hz\n", j, rect.w, rect.h,
rect.x, rect.y, rect.x+rect.w, rect.y+rect.h, ddHz);
#endif
if ( !found && x >= rect.x && x < rect.x + rect.w
&& y >= rect.y && y < rect.y + rect.h )
{
selectedDisplay = j;
found = true;
}
}
}
// explicit monitor override (r_fullscreenDisplay): pin the window to a chosen
// display index regardless of where the mouse is, so a mixed-refrsh multi-monitor
// user can force their high-refresh panel instead of a 60 Hz primary display.
const int wantDisplay = r_fullscreenDisplay.GetInteger();
if ( wantDisplay >= 0 ) {
if ( wantDisplay < numDisplays ) {
selectedDisplay = wantDisplay;
} else {
common->Warning("r_fullscreenDisplay %d is out of range (%d displays); using auto-selected %u\n",
wantDisplay, numDisplays, selectedDisplay);
}
}
#if SDL_VERSION_ATLEAST(3, 0, 0)
if(displayIDs != NULL) {
SDL_DisplayID displayID = displayIDs[selectedDisplay];
common->Printf("Will use display %u (%u)%s.\n", selectedDisplay, displayID,
wantDisplay >= 0 ? " (forced by r_fullscreenDisplay)" : " (mouse cursor's display)");
selectedDisplay = displayID;
SDL_free(displayIDs);
}
#else // SDL2
common->Printf("Will use display %u%s.\n", selectedDisplay,
wantDisplay >= 0 ? " (forced by r_fullscreenDisplay)" : " (mouse cursor's display)");
#endif
}
#endif // SDL_VERSION_ATLEAST(2, 0, 4)
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ENGINE_VERSION);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay));
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay));
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, parms.width);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, parms.height);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags);
// See above for the big comment about Wayland and alpha channels.
// When using SDL3 I assume that people usually won't be affected by this bug,
// because it's fixed in recent Mesa versions (and also works with the NVIDIA driver).
// However, with `r_fillWindowAlphaChan 1` its usage can still be enforced
// on Unix-like platforms (I don't think there's a point in this on Windows or Mac)
#if defined(__unix__) && !defined(__APPLE__)
if ( cvarSystem->GetCVarInteger( "r_fillWindowAlphaChan" ) == 1 ) {
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, true);
}
#endif
window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
if (window == NULL) {
common->Warning("Couldn't set GL mode %d/%d/%d with %dx MSAA: %s",
channelcolorbits, tdepthbits, tstencilbits, multisamples, SDL_GetError());
// before trying to reduce color channel size or whatever, first try reducing MSAA, if possible
if (multisamples > 1) {
multisamples = (multisamples <= 2) ? 0 : (multisamples/2);
// using goto because enhancing that logic which reduces attributes
// based on i (so it'd first try reducing MSAA) would be too painful
goto try_again;
}
continue;
} else {
// creating the window succeeded, so adjust r_multiSamples to the value that was actually used
parms.multiSamples = multisamples;
r_multiSamples.SetInteger(multisamples);
}
// handle exclusive fullscreen mode (windowed mode and fullscreen
// desktop were set when creating the window)
// TODO: just call GLimp_SetScreenParms() ?
if (parms.fullScreen && !parms.fullScreenDesktop) {
SDL_DisplayID displayID = SDL_GetDisplayForWindow( window );
SDL_DisplayMode mode = {};
if ( SDL_GetClosestFullscreenDisplayMode(displayID, parms.width, parms.height,
parms.displayHz, true, &mode) )
{
if ( ! SDL_SetWindowFullscreenMode(window, &mode) ) {
common->Warning("Can't set window fullscreen mode: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
if ( ! SDL_SetWindowFullscreen(window, true) ) {
common->Warning("Can't switch window to fullscreen mode: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
} else {
common->Warning("Can't get display mode: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
}
if ( ! SDL_SyncWindow(window) ) {
common->Warning("SDL_SyncWindow() failed: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
#else // SDL2
window = SDL_CreateWindow(ENGINE_VERSION,
SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay),
SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay),
parms.width, parms.height, flags);
if (!window) {
common->Warning("Couldn't set GL mode %d/%d/%d with %dx MSAA: %s",
channelcolorbits, tdepthbits, tstencilbits, multisamples, SDL_GetError());
// before trying to reduce color channel size or whatever, first try reducing MSAA, if possible
if(multisamples > 1) {
multisamples = (multisamples <= 2) ? 0 : (multisamples/2);
// using goto because enhancing that logic which reduces attributes
// based on i (so it'd first try reducing MSAA) would be too painful
goto try_again;
}
continue;
} else {
// creating the window succeeded, so adjust r_multiSamples to the value that was actually used
parms.multiSamples = multisamples;
r_multiSamples.SetInteger(multisamples);
}
// A real-fullscreen window is created at SDL's default mode for the resolution
// (often 60 Hz); pin it to the closest mode honoring the requested/desktop refresh
// so a 144 Hz panel isn't dropped to 60 Hz. Harmless if the closest mode is the
// same one SDL already chose.
if ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN)
{
SDL_DisplayMode fsmode = GLimp_ClosestFullscreenMode( parms.width, parms.height,
parms.displayHz, selectedDisplay );
SDL_SetWindowDisplayMode( window, &fsmode );
}
/* Check if we're really in the requested display mode. There is
(or was) an SDL bug were SDL switched into the wrong mode
without giving an error code. See the bug report for details:
https://bugzilla.libsdl.org/show_bug.cgi?id=4700 */
if ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN)
{
SDL_DisplayMode real_mode;
if (SDL_GetWindowDisplayMode(window, &real_mode) != 0)
{
SDL_DestroyWindow(window);
window = NULL;
common->Warning("Can't get display mode: %s\n", SDL_GetError());
return false; // trying other color depth etc is unlikely to help with this issue
}
if ((real_mode.w != parms.width) || (real_mode.h != parms.height))
{
common->Warning("Current display mode isn't requested display mode\n");
common->Warning("Likely SDL bug #4700, trying to work around it..\n");
int dIdx = SDL_GetWindowDisplayIndex(window);
if(dIdx != selectedDisplay) {
common->Warning("Window's display index is %d, but we wanted %d!\n", dIdx, selectedDisplay);
}
/* Mkay, try to hack around that. */
SDL_DisplayMode wanted_mode = GLimp_ClosestFullscreenMode( parms.width, parms.height,
parms.displayHz, selectedDisplay );
if (SDL_SetWindowDisplayMode(window, &wanted_mode) != 0)
{
SDL_DestroyWindow(window);
window = NULL;
common->Warning("Can't force resolution to %ix%i: %s\n", parms.width, parms.height, SDL_GetError());
return false; // trying other color depth etc is unlikely to help with this issue
}
/* The SDL doku says, that SDL_SetWindowSize() shouldn't be
used on fullscreen windows. But at least in my test with
SDL 2.0.9 the subsequent SDL_GetWindowDisplayMode() fails
if I don't call it. */
SDL_SetWindowSize(window, wanted_mode.w, wanted_mode.h);
if (SDL_GetWindowDisplayMode(window, &real_mode) != 0)
{
SDL_DestroyWindow(window);
window = NULL;
common->Warning("Can't get display mode: %s\n", SDL_GetError());
return false; // trying other color depth etc is unlikely to help with this issue
}
if ((real_mode.w != parms.width) || (real_mode.h != parms.height))
{
SDL_DestroyWindow(window);
window = NULL;
common->Warning("Still in wrong display mode: %ix%i instead of %ix%i\n",
real_mode.w, real_mode.h, parms.width, parms.height);
return false; // trying other color depth etc is unlikely to help with this issue
}
common->Warning("Now we have the requested resolution (%d x %d)\n", parms.width, parms.height);
}
}
#endif // SDL2
if ( windowIsVulkan ) {
// DUDE Phase 4: no GL context; the Vulkan backend owns presentation
// and reads r_swapInterval itself (present mode). ClearModified so
// the GL swap-interval poll doesn't fire on the dead path.
context = NULL;
r_swapInterval.ClearModified();
} else {
context = SDL_GL_CreateContext(window);
GLimp_SetSwapInterval( r_swapInterval.GetInteger() );
r_swapInterval.ClearModified();
}
// for HighDPI, window size and drawable size can differ
GLimp_UpdateWindowSize();
SetSDLIcon(); // for SDL2 this must be done after creating the window
// TODO: also check for fullscreen-desktop?
glConfig.isFullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
const char* fsStr = glConfig.isFullscreen ? "fullscreen " : "";
if ( (int)glConfig.winWidth != glConfig.vidWidth ) {
common->Printf( "Got a HighDPI %swindow with physical resolution %d x %d and virtual resolution %g x %g\n",
fsStr, glConfig.vidWidth, glConfig.vidHeight, glConfig.winWidth, glConfig.winHeight );
} else {
common->Printf( "Got a %swindow with resolution %g x %g\n", fsStr, glConfig.winWidth, glConfig.winHeight );
}
#else // SDL1.2 window creation
SDL_WM_SetCaption(ENGINE_VERSION, ENGINE_VERSION);
SetSDLIcon(); // for SDL1.2 this must be done before creating the window
if (SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, r_swapInterval.GetInteger()) < 0)
common->Warning("SDL_GL_SWAP_CONTROL not supported");
if ( r_glDebugContext.GetBool() ) {
common->Warning( "r_glDebugContext is set, but not supported by SDL1.2!\n" );
}
r_swapInterval.ClearModified();
window = SDL_SetVideoMode(parms.width, parms.height, colorbits, flags);
if (!window) {
common->DPrintf("Couldn't set GL mode %d/%d/%d: %s",
channelcolorbits, tdepthbits, tstencilbits, SDL_GetError());
// before trying to reduce color channel size or whatever, first try reducing MSAA, if possible
if(multisamples > 1) {
multisamples = (multisamples <= 2) ? 0 : (multisamples/2);
// using goto because enhancing that logic which reduces attributes
// based on i (so it'd first try reducing MSAA) would be too painful
goto try_again;
}
continue;
} else {
// creating the window succeeded, so adjust r_multiSamples to the value that was actually used
parms.multiSamples = multisamples;
r_multiSamples.SetInteger(multisamples);
}
glConfig.vidWidth = window->w;
glConfig.vidHeight = window->h;
glConfig.isFullscreen = (window->flags & SDL_FULLSCREEN) == SDL_FULLSCREEN;
#endif
#if defined(_WIN32) && defined(ID_ALLOW_TOOLS)
#if ! SDL_VERSION_ATLEAST(2, 0, 0)
#error "DUDE only supports the tools with SDL2, not SDL1!"
#endif
// The tools are Win32 specific. If building the tools
// then we know we are win32 and we have to include this
// config to get the editors to work.
#if SDL_VERSION_ATLEAST(3, 0, 0)
HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
HDC hdc = (HDC)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HDC_POINTER, NULL);
if ( hwnd && hdc ) {
win32.hWnd = hwnd;
win32.hDC = hdc;
#else // SDL2
// Get the HWND for later use.
SDL_SysWMinfo sdlinfo;
SDL_version sdlver;
SDL_VERSION(&sdlver);
sdlinfo.version = sdlver;
if (SDL_GetWindowWMInfo(window, &sdlinfo) && sdlinfo.subsystem == SDL_SYSWM_WINDOWS) {
win32.hWnd = sdlinfo.info.win.window;
win32.hDC = sdlinfo.info.win.hdc;
#endif
// NOTE: hInstance is set in main()
win32.hGLRC = qwglGetCurrentContext();
int pfIdx = GetPixelFormat(win32.hDC);
PIXELFORMATDESCRIPTOR src = {};
if (DescribePixelFormat(win32.hDC, pfIdx, sizeof(PIXELFORMATDESCRIPTOR), &win32.pfd) == 0)
{
common->Warning("DescribePixelFormat() failed: %d!\n", GetLastError());
PIXELFORMATDESCRIPTOR src =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
32, // 32-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
8, // 8 bit destination alpha
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
24, // 24-bit z-buffer
8, // 8-bit stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
memcpy(&win32.pfd, &src, sizeof(PIXELFORMATDESCRIPTOR));
}
win32.piAttribIList = NULL;
win32.wglGetPixelFormatAttribivARB = (BOOL(WINAPI*)(HDC,int,int,UINT,const int*,int*))SDL_GL_GetProcAddress("wglGetPixelFormatAttribivARB");
win32.wglChoosePixelFormatARB = (BOOL(WINAPI*)(HDC,const int*,const FLOAT*,UINT,int*piFormats,UINT*))SDL_GL_GetProcAddress("wglChoosePixelFormatARB");
if(win32.wglGetPixelFormatAttribivARB != NULL && win32.wglChoosePixelFormatARB != NULL) {
const int queryAttributes[] = {
// equivalents of all the SDL_GL_* attributes we set above (and ones set implicitly)
WGL_DRAW_TO_WINDOW_ARB,
WGL_RED_BITS_ARB,
WGL_GREEN_BITS_ARB,
WGL_BLUE_BITS_ARB,
WGL_ALPHA_BITS_ARB,
WGL_DOUBLE_BUFFER_ARB,
WGL_DEPTH_BITS_ARB,
WGL_STENCIL_BITS_ARB,
// WGL_ACCUM_*_BITS_ARB - not used
WGL_STEREO_ARB,
WGL_SAMPLE_BUFFERS_ARB,
WGL_SAMPLES_ARB,
// WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB - not used
WGL_ACCELERATION_ARB,
};
enum { NUM_ATTRIBUTES = sizeof(queryAttributes)/sizeof(queryAttributes[0]) };
int queryResults[NUM_ATTRIBUTES] = {};
win32.wglGetPixelFormatAttribivARB(win32.hDC, pfIdx, PFD_MAIN_PLANE, NUM_ATTRIBUTES, queryAttributes, queryResults);
static int attribIList[2*NUM_ATTRIBUTES+2] = {}; // +2 for terminating 0, 0 pair
for(int i=0; i<NUM_ATTRIBUTES; ++i) {
attribIList[i*2] = queryAttributes[i];
attribIList[i*2+1] = queryResults[i];
}
win32.piAttribIList = attribIList;
}
} else {
// TODO: can we just disable them?
common->Error("SDL_GetWindowWMInfo(), which is needed for Tools to work, failed!");
}
#endif // defined(_WIN32) && defined(ID_ALLOW_TOOLS)
idStr msaaStr;
if (parms.multiSamples > 0)
msaaStr = idStr::Format("%dx MSAA", parms.multiSamples);
else
msaaStr = "no MSAA";
common->Printf("Requested %d color bits per chan, %d alpha %d depth, %d stencil and %s\n",
channelcolorbits, talphabits, tdepthbits, tstencilbits, msaaStr.c_str());
if ( windowIsVulkan ) {
// no GL attributes to read back; describe the backend's scene
// image (RGBA8 + D24/32S8) so GfxInfo_f has sane numbers
glConfig.colorBits = 24;
glConfig.alphabits = 8;
glConfig.depthBits = 24;
glConfig.stencilBits = 8;
r_multiSamples.SetInteger( 0 );
} else {
int r, g, b, a, d, s, msaa;
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b);
SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a);
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &d);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &s);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &msaa);
if (msaa) {
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa);
}
if (msaa > 0)
msaaStr = idStr::Format("%dx MSAA", msaa);
else
msaaStr = "no MSAA";
common->Printf("Got %d stencil bits, %d depth bits, color bits: r%d g%d b%d a%d and %s\n", s, d, r, g, b, a, msaaStr.c_str());
glConfig.colorBits = r+g+b; // a bit imprecise, but seems to be used only in GfxInfo_f()
glConfig.alphabits = a;
glConfig.depthBits = d;
glConfig.stencilBits = s;
r_multiSamples.SetInteger(msaa);
}
glConfig.displayFrequency = 0;
// for r_fillWindowAlphaChan -1, see also the big comment above
glConfig.shouldFillWindowAlpha = false;
glConfig.isWayland = false;
#if SDL_VERSION_ATLEAST(2, 0, 0)
const char* videoDriver = SDL_GetCurrentVideoDriver();
if (idStr::Icmp(videoDriver, "wayland") == 0) {
#if SDL_MAJOR_VERSION == 2 // don't enable this hack by default with SDL3
glConfig.shouldFillWindowAlpha = true;
#endif
glConfig.isWayland = true;
}
#endif
glConfig.haveDebugContext = false;
#if SDL_VERSION_ATLEAST(2, 0, 0)
int cflags = 0;
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( SDL_GL_GetAttribute( SDL_GL_CONTEXT_FLAGS, &cflags ) ) {
#else // SDL2
if ( SDL_GL_GetAttribute( SDL_GL_CONTEXT_FLAGS, &cflags ) == 0 ) {
#endif
glConfig.haveDebugContext = (cflags & SDL_GL_CONTEXT_DEBUG_FLAG) != 0;
if ( glConfig.haveDebugContext )
common->Printf( "Got a debug context!\n" );
else if( r_glDebugContext.GetBool() ) {
common->Warning( "Requested a debug context, but didn't get one!\n" );
}
}
#endif
break;
}
if (!window) {
common->Warning("No usable GL mode found: %s", SDL_GetError());
return false;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
// SDL1.2 has no context, and is not supported by ImGui anyway.
// DUDE: works on both GL backends — sys_imgui picks the fixed-function GL2
// ImGui renderer for the legacy context and the shader-based GL3 one for the
// GL 3.3 core context (r_graphicsAPI opengl3). Under Vulkan the renderer
// side needs the live backend (instance/device/swapchain), so R_InitOpenGL
// runs ImGuiHooks::Init after rhi Init instead (Phase 4 M6).
if ( !windowIsVulkan ) {
D3::ImGuiHooks::Init(window, context);
}
#endif