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
|
//
// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayGLX.cpp: GLX implementation of egl::Display
#include "libANGLE/renderer/gl/glx/DisplayGLX.h"
#include <EGL/eglext.h>
#include <algorithm>
#include <cstring>
#include <fstream>
#include "common/debug.h"
#include "libANGLE/Config.h"
#include "libANGLE/Display.h"
#include "libANGLE/Surface.h"
#include "libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h"
#include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h"
#include "libANGLE/renderer/gl/RendererGL.h"
#include "libANGLE/renderer/gl/renderergl_utils.h"
#include "third_party/libXNVCtrl/NVCtrl.h"
#include "third_party/libXNVCtrl/NVCtrlLib.h"
namespace
{
// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
// Return empty string on failing.
egl::Error GetAMDDriverVersion(std::string *version)
{
*version = "";
const char kAMDDriverInfoFileName[] = "/etc/ati/amdpcsdb.default";
std::ifstream file(kAMDDriverInfoFileName);
if (!file)
{
return egl::Error(EGL_SUCCESS);
}
std::string line;
while (std::getline(file, line))
{
static const char kReleaseVersion[] = "ReleaseVersion=";
if (line.compare(0, std::strlen(kReleaseVersion), kReleaseVersion) != 0)
{
continue;
}
const size_t begin = line.find_first_of("0123456789");
if (begin == std::string::npos)
{
continue;
}
const size_t end = line.find_first_not_of("0123456789.", begin);
if (end == std::string::npos)
{
*version = line.substr(begin);
}
else
{
*version = line.substr(begin, end - begin);
}
return egl::Error(EGL_SUCCESS);
}
return egl::Error(EGL_SUCCESS);
}
} // anonymous namespace
namespace rx
{
static int IgnoreX11Errors(Display *, XErrorEvent *)
{
return 0;
}
SwapControlData::SwapControlData()
: targetSwapInterval(0),
maxSwapInterval(-1),
currentSwapInterval(-1)
{
}
class FunctionsGLGLX : public FunctionsGL
{
public:
FunctionsGLGLX(PFNGETPROCPROC getProc)
: mGetProc(getProc)
{
}
~FunctionsGLGLX() override {}
private:
void *loadProcAddress(const std::string &function) override
{
return reinterpret_cast<void*>(mGetProc(function.c_str()));
}
PFNGETPROCPROC mGetProc;
};
DisplayGLX::DisplayGLX()
: DisplayGL(),
mFunctionsGL(nullptr),
mRequestedVisual(-1),
mContextConfig(nullptr),
mContext(nullptr),
mDummyPbuffer(0),
mUsesNewXDisplay(false),
mIsMesa(false),
mHasMultisample(false),
mHasARBCreateContext(false),
mHasARBCreateContextProfile(false),
mHasARBCreateContextRobustness(false),
mHasEXTCreateContextES2Profile(false),
mSwapControl(SwapControl::Absent),
mMinSwapInterval(0),
mMaxSwapInterval(0),
mCurrentSwapInterval(-1),
mXDisplay(nullptr),
mEGLDisplay(nullptr)
{
}
DisplayGLX::~DisplayGLX()
{
}
egl::Error DisplayGLX::initialize(egl::Display *display)
{
mEGLDisplay = display;
mXDisplay = display->getNativeDisplayId();
const auto &attribMap = display->getAttributeMap();
// ANGLE_platform_angle allows the creation of a default display
// using EGL_DEFAULT_DISPLAY (= nullptr). In this case just open
// the display specified by the DISPLAY environment variable.
if (mXDisplay == EGL_DEFAULT_DISPLAY)
{
mUsesNewXDisplay = true;
mXDisplay = XOpenDisplay(NULL);
if (!mXDisplay)
{
return egl::Error(EGL_NOT_INITIALIZED, "Could not open the default X display.");
}
}
std::string glxInitError;
if (!mGLX.initialize(mXDisplay, DefaultScreen(mXDisplay), &glxInitError))
{
return egl::Error(EGL_NOT_INITIALIZED, glxInitError.c_str());
}
mHasMultisample = mGLX.minorVersion > 3 || mGLX.hasExtension("GLX_ARB_multisample");
mHasARBCreateContext = mGLX.hasExtension("GLX_ARB_create_context");
mHasARBCreateContextProfile = mGLX.hasExtension("GLX_ARB_create_context_profile");
mHasARBCreateContextRobustness = mGLX.hasExtension("GLX_ARB_create_context_robustness");
mHasEXTCreateContextES2Profile = mGLX.hasExtension("GLX_EXT_create_context_es2_profile");
std::string clientVendor = mGLX.getClientString(GLX_VENDOR);
mIsMesa = clientVendor.find("Mesa") != std::string::npos;
// Choose the swap_control extension to use, if any.
// The EXT version is better as it allows glXSwapInterval to be called per
// window, while we'll potentially need to change the swap interval on each
// swap buffers when using the SGI or MESA versions.
if (mGLX.hasExtension("GLX_EXT_swap_control"))
{
mSwapControl = SwapControl::EXT;
// In GLX_EXT_swap_control querying these is done on a GLXWindow so we just
// set default values.
mMinSwapInterval = 0;
mMaxSwapInterval = 4;
}
else if (mGLX.hasExtension("GLX_MESA_swap_control"))
{
// If we have the Mesa or SGI extension, assume that you can at least set
// a swap interval of 0 or 1.
mSwapControl = SwapControl::Mesa;
mMinSwapInterval = 0;
mMinSwapInterval = 1;
}
else if (mGLX.hasExtension("GLX_SGI_swap_control"))
{
mSwapControl = SwapControl::SGI;
mMinSwapInterval = 0;
mMinSwapInterval = 1;
}
else
{
mSwapControl = SwapControl::Absent;
mMinSwapInterval = 1;
mMinSwapInterval = 1;
}
if (attribMap.contains(EGL_X11_VISUAL_ID_ANGLE))
{
mRequestedVisual = static_cast<EGLint>(attribMap.get(EGL_X11_VISUAL_ID_ANGLE, -1));
// There is no direct way to get the GLXFBConfig matching an X11 visual ID
// so we have to iterate over all the GLXFBConfigs to find the right one.
int nConfigs;
int attribList[] = {
None,
};
glx::FBConfig *allConfigs = mGLX.chooseFBConfig(attribList, &nConfigs);
for (int i = 0; i < nConfigs; ++i)
{
if (getGLXFBConfigAttrib(allConfigs[i], GLX_VISUAL_ID) == mRequestedVisual)
{
mContextConfig = allConfigs[i];
break;
}
}
XFree(allConfigs);
if (mContextConfig == nullptr)
{
return egl::Error(EGL_NOT_INITIALIZED, "Invalid visual ID requested.");
}
}
else
{
// When glXMakeCurrent is called, the context and the surface must be
// compatible which in glX-speak means that their config have the same
// color buffer type, are both RGBA or ColorIndex, and their buffers have
// the same depth, if they exist.
// Since our whole EGL implementation is backed by only one GL context, this
// context must be compatible with all the GLXFBConfig corresponding to the
// EGLconfigs that we will be exposing.
int nConfigs;
int attribList[] =
{
// We want RGBA8 and DEPTH24_STENCIL8
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 8,
// We want RGBA rendering (vs COLOR_INDEX) and doublebuffer
GLX_RENDER_TYPE, GLX_RGBA_BIT,
// Double buffer is not strictly required as a non-doublebuffer
// context can work with a doublebuffered surface, but it still
// flickers and all applications want doublebuffer anyway.
GLX_DOUBLEBUFFER, True,
// All of these must be supported for full EGL support
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
// This makes sure the config have an associated visual Id
GLX_X_RENDERABLE, True,
GLX_CONFIG_CAVEAT, GLX_NONE,
None
};
glx::FBConfig *candidates = mGLX.chooseFBConfig(attribList, &nConfigs);
if (nConfigs == 0)
{
XFree(candidates);
return egl::Error(EGL_NOT_INITIALIZED, "Could not find a decent GLX FBConfig to create the context.");
}
mContextConfig = candidates[0];
XFree(candidates);
}
const auto &eglAttributes = display->getAttributeMap();
if (mHasARBCreateContext)
{
egl::Error error = initializeContext(mContextConfig, eglAttributes, &mContext);
if (error.isError())
{
return error;
}
}
else
{
if (eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) ==
EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
{
return egl::Error(EGL_NOT_INITIALIZED,
"Cannot create an OpenGL ES platform on GLX without the "
"GLX_ARB_create_context extension.");
}
XVisualInfo visualTemplate;
visualTemplate.visualid = getGLXFBConfigAttrib(mContextConfig, GLX_VISUAL_ID);
int numVisuals = 0;
XVisualInfo *visuals =
XGetVisualInfo(mXDisplay, VisualIDMask, &visualTemplate, &numVisuals);
if (numVisuals <= 0)
{
return egl::Error(EGL_NOT_INITIALIZED,
"Could not get the visual info from the fb config");
}
ASSERT(numVisuals == 1);
mContext = mGLX.createContext(&visuals[0], nullptr, true);
XFree(visuals);
if (!mContext)
{
return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context.");
}
}
ASSERT(mContext);
// FunctionsGL and DisplayGL need to make a few GL calls, for example to
// query the version of the context so we need to make the context current.
// glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer
// (of size 1, 1) for the duration of these calls.
// Ideally we would want to unset the current context and destroy the pbuffer
// before going back to the application but this is TODO
// We could use a pbuffer of size (0, 0) but it fails on the Intel Mesa driver
// as commented on https://bugs.freedesktop.org/show_bug.cgi?id=38869 so we
// use (1, 1) instead.
int dummyPbufferAttribs[] =
{
GLX_PBUFFER_WIDTH, 1,
GLX_PBUFFER_HEIGHT, 1,
None,
};
mDummyPbuffer = mGLX.createPbuffer(mContextConfig, dummyPbufferAttribs);
if (!mDummyPbuffer)
{
return egl::Error(EGL_NOT_INITIALIZED, "Could not create the dummy pbuffer.");
}
if (!mGLX.makeCurrent(mDummyPbuffer, mContext))
{
return egl::Error(EGL_NOT_INITIALIZED, "Could not make the dummy pbuffer current.");
}
mFunctionsGL = new FunctionsGLGLX(mGLX.getProc);
mFunctionsGL->initialize();
// TODO(cwallez, angleproject:1303) Disable the OpenGL ES backend on Linux NVIDIA and Intel as
// it has problems on our automated testing. An OpenGL ES backend might not trigger this test if
// there is no Desktop OpenGL support, but that's not the case in our automated testing.
VendorID vendor = GetVendorID(mFunctionsGL);
bool isOpenGLES =
eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) ==
EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
if (isOpenGLES && (IsIntel(vendor) || IsNvidia(vendor)))
{
return egl::Error(EGL_NOT_INITIALIZED, "Intel or NVIDIA OpenGL ES drivers are not supported.");
}
syncXCommands();
return DisplayGL::initialize(display);
}
void DisplayGLX::terminate()
{
DisplayGL::terminate();
if (mDummyPbuffer)
{
mGLX.destroyPbuffer(mDummyPbuffer);
mDummyPbuffer = 0;
}
if (mContext)
{
mGLX.destroyContext(mContext);
mContext = nullptr;
}
mGLX.terminate();
SafeDelete(mFunctionsGL);
}
SurfaceImpl *DisplayGLX::createWindowSurface(const egl::SurfaceState &state,
const egl::Config *configuration,
EGLNativeWindowType window,
const egl::AttributeMap &attribs)
{
ASSERT(configIdToGLXConfig.count(configuration->configID) > 0);
glx::FBConfig fbConfig = configIdToGLXConfig[configuration->configID];
return new WindowSurfaceGLX(state, mGLX, this, getRenderer(), window, mGLX.getDisplay(),
mContext, fbConfig);
}
SurfaceImpl *DisplayGLX::createPbufferSurface(const egl::SurfaceState &state,
const egl::Config *configuration,
const egl::AttributeMap &attribs)
{
ASSERT(configIdToGLXConfig.count(configuration->configID) > 0);
glx::FBConfig fbConfig = configIdToGLXConfig[configuration->configID];
EGLint width = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0));
EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0));
bool largest = (attribs.get(EGL_LARGEST_PBUFFER, EGL_FALSE) == EGL_TRUE);
return new PbufferSurfaceGLX(state, getRenderer(), width, height, largest, mGLX, mContext,
fbConfig);
}
SurfaceImpl *DisplayGLX::createPbufferFromClientBuffer(const egl::SurfaceState &state,
const egl::Config *configuration,
EGLenum buftype,
EGLClientBuffer clientBuffer,
const egl::AttributeMap &attribs)
{
UNIMPLEMENTED();
return nullptr;
}
SurfaceImpl *DisplayGLX::createPixmapSurface(const egl::SurfaceState &state,
const egl::Config *configuration,
NativePixmapType nativePixmap,
const egl::AttributeMap &attribs)
{
UNIMPLEMENTED();
return nullptr;
}
egl::Error DisplayGLX::getDevice(DeviceImpl **device)
{
UNIMPLEMENTED();
return egl::Error(EGL_BAD_DISPLAY);
}
egl::Error DisplayGLX::initializeContext(glx::FBConfig config,
const egl::AttributeMap &eglAttributes,
glx::Context *context)
{
int profileMask = 0;
EGLint requestedDisplayType = static_cast<EGLint>(
eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE));
if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
{
if (!mHasEXTCreateContextES2Profile)
{
return egl::Error(EGL_NOT_INITIALIZED,
"Cannot create an OpenGL ES platform on GLX without the "
"GLX_EXT_create_context_es_profile extension.");
}
ASSERT(mHasARBCreateContextProfile);
profileMask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
}
// Create a context of the requested version, if any.
gl::Version requestedVersion(static_cast<EGLint>(eglAttributes.get(
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE)),
static_cast<EGLint>(eglAttributes.get(
EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE)));
if (static_cast<EGLint>(requestedVersion.major) != EGL_DONT_CARE &&
static_cast<EGLint>(requestedVersion.minor) != EGL_DONT_CARE)
{
if (!(profileMask & GLX_CONTEXT_ES2_PROFILE_BIT_EXT) &&
requestedVersion >= gl::Version(3, 2))
{
profileMask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
}
return createContextAttribs(config, requestedVersion, profileMask, context);
}
// It is commonly assumed that glXCreateContextAttrib will create a context
// of the highest version possible but it is not specified in the spec and
// is not true on the Mesa drivers. On Mesa, Instead we try to create a
// context per GL version until we succeed, starting from newer version.
// On both Mesa and other drivers we try to create a desktop context and fall
// back to ES context.
// The code could be simpler if the Mesa code path was used for all drivers,
// however the cost of failing a context creation can be high (3 milliseconds
// for the NVIDIA driver). The good thing is that failed context creation only
// takes 0.1 milliseconds on Mesa.
struct ContextCreationInfo
{
EGLint displayType;
int profileFlag;
Optional<gl::Version> version;
};
// clang-format off
// For regular drivers we try to create a core, compatibility, then ES context.
// Without requiring any specific version (the Optional version is undefined).
const ContextCreationInfo contextsToTry[] = {
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, {} },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, {} },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, {} },
};
// On Mesa we try to create a core context, except for versions below 3.2
// where it is not applicable. (and fallback to ES as well)
const ContextCreationInfo mesaContextsToTry[] = {
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 5) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 4) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 3) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 2) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 1) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 0) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(3, 3) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(3, 2) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(3, 1) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(3, 0) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(2, 0) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 5) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 4) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 3) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 2) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 1) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 0) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(3, 2) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(3, 1) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(3, 0) } },
{ EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(2, 0) } },
};
// clang-format on
const ContextCreationInfo *toTry = contextsToTry;
size_t toTryLength = ArraySize(contextsToTry);
if (mIsMesa)
{
toTry = mesaContextsToTry;
toTryLength = ArraySize(mesaContextsToTry);
}
// NOTE: below we return as soon as we're able to create a context so the
// "error" variable is EGL_SUCCESS when returned contrary to the common idiom
// of returning "error" when there is an actual error.
for (size_t i = 0; i < toTryLength; ++i)
{
const ContextCreationInfo &info = toTry[i];
if (requestedDisplayType != EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE &&
requestedDisplayType != info.displayType)
{
continue;
}
egl::Error error = createContextAttribs(config, info.version, info.profileFlag, context);
if (!error.isError())
{
return error;
}
}
return egl::Error(EGL_NOT_INITIALIZED, "Could not create a backing OpenGL context.");
}
egl::ConfigSet DisplayGLX::generateConfigs()
{
egl::ConfigSet configs;
configIdToGLXConfig.clear();
const gl::Version &maxVersion = getMaxSupportedESVersion();
ASSERT(maxVersion >= gl::Version(2, 0));
bool supportsES3 = maxVersion >= gl::Version(3, 0);
int contextRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_RED_SIZE);
int contextGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_GREEN_SIZE);
int contextBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_BLUE_SIZE);
int contextAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ALPHA_SIZE);
int contextDepthSize = getGLXFBConfigAttrib(mContextConfig, GLX_DEPTH_SIZE);
int contextStencilSize = getGLXFBConfigAttrib(mContextConfig, GLX_STENCIL_SIZE);
int contextSamples = mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLES) : 0;
int contextSampleBuffers =
mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLE_BUFFERS) : 0;
int contextAccumRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_RED_SIZE);
int contextAccumGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_GREEN_SIZE);
int contextAccumBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_BLUE_SIZE);
int contextAccumAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_ALPHA_SIZE);
int attribList[] =
{
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_X_RENDERABLE, True,
GLX_DOUBLEBUFFER, True,
None,
};
int glxConfigCount;
glx::FBConfig *glxConfigs = mGLX.chooseFBConfig(attribList, &glxConfigCount);
for (int i = 0; i < glxConfigCount; i++)
{
glx::FBConfig glxConfig = glxConfigs[i];
egl::Config config;
// Native stuff
config.nativeVisualID = getGLXFBConfigAttrib(glxConfig, GLX_VISUAL_ID);
config.nativeVisualType = getGLXFBConfigAttrib(glxConfig, GLX_X_VISUAL_TYPE);
config.nativeRenderable = EGL_TRUE;
// When a visual ID has been specified with EGL_ANGLE_x11_visual we should
// only return configs with this visual: it will maximize performance by avoid
// blits in the driver when showing the window on the screen.
if (mRequestedVisual != -1 && config.nativeVisualID != mRequestedVisual)
{
continue;
}
// Buffer sizes
config.redSize = getGLXFBConfigAttrib(glxConfig, GLX_RED_SIZE);
config.greenSize = getGLXFBConfigAttrib(glxConfig, GLX_GREEN_SIZE);
config.blueSize = getGLXFBConfigAttrib(glxConfig, GLX_BLUE_SIZE);
config.alphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ALPHA_SIZE);
config.depthSize = getGLXFBConfigAttrib(glxConfig, GLX_DEPTH_SIZE);
config.stencilSize = getGLXFBConfigAttrib(glxConfig, GLX_STENCIL_SIZE);
// We require RGBA8 and the D24S8 (or no DS buffer)
if (config.redSize != contextRedSize || config.greenSize != contextGreenSize ||
config.blueSize != contextBlueSize || config.alphaSize != contextAlphaSize)
{
continue;
}
// The GLX spec says that it is ok for a whole buffer to not be present
// however the Mesa Intel driver (and probably on other Mesa drivers)
// fails to make current when the Depth stencil doesn't exactly match the
// configuration.
bool hasSameDepthStencil =
config.depthSize == contextDepthSize && config.stencilSize == contextStencilSize;
bool hasNoDepthStencil = config.depthSize == 0 && config.stencilSize == 0;
if (!hasSameDepthStencil && (mIsMesa || !hasNoDepthStencil))
{
continue;
}
config.colorBufferType = EGL_RGB_BUFFER;
config.luminanceSize = 0;
config.alphaMaskSize = 0;
config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
// Multisample and accumulation buffers
int samples = mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLES) : 0;
int sampleBuffers =
mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLE_BUFFERS) : 0;
int accumRedSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_RED_SIZE);
int accumGreenSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_GREEN_SIZE);
int accumBlueSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_BLUE_SIZE);
int accumAlphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_ALPHA_SIZE);
if (samples != contextSamples ||
sampleBuffers != contextSampleBuffers ||
accumRedSize != contextAccumRedSize ||
accumGreenSize != contextAccumGreenSize ||
accumBlueSize != contextAccumBlueSize ||
accumAlphaSize != contextAccumAlphaSize)
{
continue;
}
config.samples = samples;
config.sampleBuffers = sampleBuffers;
// Transparency
if (getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_TYPE) == GLX_TRANSPARENT_RGB)
{
config.transparentType = EGL_TRANSPARENT_RGB;
config.transparentRedValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_RED_VALUE);
config.transparentGreenValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_GREEN_VALUE);
config.transparentBlueValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_BLUE_VALUE);
}
else
{
config.transparentType = EGL_NONE;
}
// Pbuffer
config.maxPBufferWidth = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_WIDTH);
config.maxPBufferHeight = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_HEIGHT);
config.maxPBufferPixels = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_PIXELS);
// Caveat
config.configCaveat = EGL_NONE;
int caveat = getGLXFBConfigAttrib(glxConfig, GLX_CONFIG_CAVEAT);
if (caveat == GLX_SLOW_CONFIG)
{
config.configCaveat = EGL_SLOW_CONFIG;
}
else if (caveat == GLX_NON_CONFORMANT_CONFIG)
{
continue;
}
// Misc
config.level = getGLXFBConfigAttrib(glxConfig, GLX_LEVEL);
config.bindToTextureRGB = EGL_FALSE;
config.bindToTextureRGBA = EGL_FALSE;
int glxDrawable = getGLXFBConfigAttrib(glxConfig, GLX_DRAWABLE_TYPE);
config.surfaceType = 0 |
(glxDrawable & GLX_WINDOW_BIT ? EGL_WINDOW_BIT : 0) |
(glxDrawable & GLX_PBUFFER_BIT ? EGL_PBUFFER_BIT : 0) |
(glxDrawable & GLX_PIXMAP_BIT ? EGL_PIXMAP_BIT : 0);
config.minSwapInterval = mMinSwapInterval;
config.maxSwapInterval = mMaxSwapInterval;
// TODO(cwallez) wildly guessing these formats, another TODO says they should be removed anyway
config.renderTargetFormat = GL_RGBA8;
config.depthStencilFormat = GL_DEPTH24_STENCIL8;
config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0);
config.renderableType = config.conformant;
// TODO(cwallez) I have no idea what this is
config.matchNativePixmap = EGL_NONE;
int id = configs.add(config);
configIdToGLXConfig[id] = glxConfig;
}
XFree(glxConfigs);
return configs;
}
bool DisplayGLX::testDeviceLost()
{
if (mHasARBCreateContextRobustness)
{
return getRenderer()->getResetStatus() != GL_NO_ERROR;
}
return false;
}
egl::Error DisplayGLX::restoreLostDevice()
{
return egl::Error(EGL_BAD_DISPLAY);
}
bool DisplayGLX::isValidNativeWindow(EGLNativeWindowType window) const
{
// There is no function in Xlib to check the validity of a Window directly.
// However a small number of functions used to obtain window information
// return a status code (0 meaning failure) and guarantee that they will
// fail if the window doesn't exist (the rational is that these function
// are used by window managers). Out of these function we use XQueryTree
// as it seems to be the simplest; a drawback is that it will allocate
// memory for the list of children, because we use a child window for
// WindowSurface.
Window root;
Window parent;
Window *children = nullptr;
unsigned nChildren;
int status = XQueryTree(mGLX.getDisplay(), window, &root, &parent, &children, &nChildren);
if (children)
{
XFree(children);
}
return status != 0;
}
std::string DisplayGLX::getVendorString() const
{
// UNIMPLEMENTED();
return "";
}
egl::Error DisplayGLX::waitClient() const
{
mGLX.waitGL();
return egl::Error(EGL_SUCCESS);
}
egl::Error DisplayGLX::getDriverVersion(std::string *version) const
{
VendorID vendor = GetVendorID(mFunctionsGL);
switch (vendor)
{
case VENDOR_ID_NVIDIA:
return getNVIDIADriverVersion(version);
case VENDOR_ID_AMD:
return GetAMDDriverVersion(version);
default:
*version = "";
return egl::Error(EGL_SUCCESS);
}
}
egl::Error DisplayGLX::waitNative(EGLint engine,
egl::Surface *drawSurface,
egl::Surface *readSurface) const
{
// eglWaitNative is used to notice the driver of changes in X11 for the current surface, such as
// changes of the window size. We use this event to update the child window of WindowSurfaceGLX
// to match its parent window's size.
// Handling eglWaitNative this way helps the application control when resize happens. This is
// important because drivers have a tendency to clobber the back buffer when the windows are
// resized. See http://crbug.com/326995
if (drawSurface != nullptr)
{
SurfaceGLX *glxDrawSurface = GetImplAs<SurfaceGLX>(drawSurface);
egl::Error error = glxDrawSurface->checkForResize();
if (error.isError())
{
return error;
}
}
if (readSurface != drawSurface && readSurface != nullptr)
{
SurfaceGLX *glxReadSurface = GetImplAs<SurfaceGLX>(readSurface);
egl::Error error = glxReadSurface->checkForResize();
if (error.isError())
{
return error;
}
}
// We still need to forward the resizing of the child window to the driver.
mGLX.waitX();
return egl::Error(EGL_SUCCESS);
}
void DisplayGLX::syncXCommands() const
{
if (mUsesNewXDisplay)
{
XSync(mGLX.getDisplay(), False);
}
}
void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data)
{
ASSERT(data != nullptr);
// TODO(cwallez) error checking?
if (mSwapControl == SwapControl::EXT)
{
// Prefer the EXT extension, it gives per-drawable swap intervals, which will
// minimize the number of driver calls.
if (data->maxSwapInterval < 0)
{
unsigned int maxSwapInterval = 0;
mGLX.queryDrawable(drawable, GLX_MAX_SWAP_INTERVAL_EXT, &maxSwapInterval);
data->maxSwapInterval = static_cast<int>(maxSwapInterval);
}
// When the egl configs were generated we had to guess what the max swap interval
// was because we didn't have a window to query it one (and that this max could
// depend on the monitor). This means that the target interval might be higher
// than the max interval and needs to be clamped.
const int realInterval = std::min(data->targetSwapInterval, data->maxSwapInterval);
if (data->currentSwapInterval != realInterval)
{
mGLX.swapIntervalEXT(drawable, realInterval);
data->currentSwapInterval = realInterval;
}
}
else if (mCurrentSwapInterval != data->targetSwapInterval)
{
// With the Mesa or SGI extensions we can still do per-drawable swap control
// manually but it is more expensive in number of driver calls.
if (mSwapControl == SwapControl::Mesa)
{
mGLX.swapIntervalMESA(data->targetSwapInterval);
}
else if (mSwapControl == SwapControl::SGI)
{
mGLX.swapIntervalSGI(data->targetSwapInterval);
}
mCurrentSwapInterval = data->targetSwapInterval;
}
}
bool DisplayGLX::isValidWindowVisualId(unsigned long visualId) const
{
return mRequestedVisual == -1 || static_cast<unsigned long>(mRequestedVisual) == visualId;
}
const FunctionsGL *DisplayGLX::getFunctionsGL() const
{
return mFunctionsGL;
}
void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
outExtensions->createContextRobustness = mHasARBCreateContextRobustness;
}
void DisplayGLX::generateCaps(egl::Caps *outCaps) const
{
outCaps->textureNPOT = true;
}
int DisplayGLX::getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const
{
int result;
mGLX.getFBConfigAttrib(config, attrib, &result);
return result;
}
egl::Error DisplayGLX::createContextAttribs(glx::FBConfig,
const Optional<gl::Version> &version,
int profileMask,
glx::Context *context) const
{
std::vector<int> attribs;
if (mHasARBCreateContextRobustness)
{
attribs.push_back(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
attribs.push_back(GLX_LOSE_CONTEXT_ON_RESET_ARB);
}
if (version.valid())
{
attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB);
attribs.push_back(version.value().major);
attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB);
attribs.push_back(version.value().minor);
}
if (profileMask != 0 && mHasARBCreateContextProfile)
{
attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
attribs.push_back(profileMask);
}
attribs.push_back(None);
// When creating a context with glXCreateContextAttribsARB, a variety of X11 errors can
// be generated. To prevent these errors from crashing our process, we simply ignore
// them and only look if GLXContext was created.
// Process all events before setting the error handler to avoid desynchronizing XCB instances
// (the error handler is NOT per-display).
XSync(mXDisplay, False);
auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors);
*context = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, attribs.data());
XSetErrorHandler(oldErrorHandler);
if (!*context)
{
return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context.");
}
return egl::Error(EGL_SUCCESS);
}
egl::Error DisplayGLX::getNVIDIADriverVersion(std::string *version) const
{
*version = "";
int eventBase = 0;
int errorBase = 0;
if (XNVCTRLQueryExtension(mXDisplay, &eventBase, &errorBase))
{
int screenCount = ScreenCount(mXDisplay);
for (int screen = 0; screen < screenCount; ++screen)
{
char *buffer = nullptr;
if (XNVCTRLIsNvScreen(mXDisplay, screen) &&
XNVCTRLQueryStringAttribute(mXDisplay, screen, 0,
NV_CTRL_STRING_NVIDIA_DRIVER_VERSION, &buffer))
{
*version = buffer;
XFree(buffer);
}
}
}
return egl::Error(EGL_SUCCESS);
}
}
|