1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // DisplayGLX.cpp: GLX implementation of egl::Display
8
9 #include <algorithm>
10 #include <cstring>
11 #include <fstream>
12
13 #include "common/debug.h"
14 #include "common/system_utils.h"
15 #include "libANGLE/Config.h"
16 #include "libANGLE/Context.h"
17 #include "libANGLE/Display.h"
18 #include "libANGLE/Surface.h"
19 #include "libANGLE/renderer/gl/ContextGL.h"
20 #include "libANGLE/renderer/gl/RendererGL.h"
21 #include "libANGLE/renderer/gl/renderergl_utils.h"
22
23 #include "libANGLE/renderer/gl/glx/DisplayGLX.h"
24
25 #include <EGL/eglext.h>
26
27 #include "libANGLE/renderer/gl/glx/DisplayGLX_api.h"
28 #include "libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h"
29 #include "libANGLE/renderer/gl/glx/PixmapSurfaceGLX.h"
30 #include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h"
31 #include "libANGLE/renderer/gl/glx/glx_utils.h"
32
33 namespace
34 {
35
GetRobustnessVideoMemoryPurge(const egl::AttributeMap & attribs)36 rx::RobustnessVideoMemoryPurgeStatus GetRobustnessVideoMemoryPurge(const egl::AttributeMap &attribs)
37 {
38 return static_cast<rx::RobustnessVideoMemoryPurgeStatus>(
39 attribs.get(GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV, GL_FALSE));
40 }
41
42 } // anonymous namespace
43
44 namespace rx
45 {
46
IgnoreX11Errors(Display *,XErrorEvent *)47 static int IgnoreX11Errors(Display *, XErrorEvent *)
48 {
49 return 0;
50 }
51
52 class FunctionsGLGLX : public FunctionsGL
53 {
54 public:
FunctionsGLGLX(PFNGETPROCPROC getProc)55 FunctionsGLGLX(PFNGETPROCPROC getProc) : mGetProc(getProc) {}
56
~FunctionsGLGLX()57 ~FunctionsGLGLX() override {}
58
59 private:
loadProcAddress(const std::string & function) const60 void *loadProcAddress(const std::string &function) const override
61 {
62 return reinterpret_cast<void *>(mGetProc(function.c_str()));
63 }
64
65 PFNGETPROCPROC mGetProc;
66 };
67
DisplayGLX(const egl::DisplayState & state)68 DisplayGLX::DisplayGLX(const egl::DisplayState &state)
69 : DisplayGL(state),
70 mRequestedVisual(-1),
71 mContextConfig(nullptr),
72 mContext(nullptr),
73 mCurrentNativeContexts(),
74 mInitPbuffer(0),
75 mUsesNewXDisplay(false),
76 mIsMesa(false),
77 mHasMultisample(false),
78 mHasARBCreateContext(false),
79 mHasARBCreateContextProfile(false),
80 mHasARBCreateContextRobustness(false),
81 mHasEXTCreateContextES2Profile(false),
82 mHasNVRobustnessVideoMemoryPurge(false),
83 mSwapControl(SwapControl::Absent),
84 mMinSwapInterval(0),
85 mMaxSwapInterval(0),
86 mCurrentSwapInterval(-1),
87 mCurrentDrawable(0),
88 mXDisplay(nullptr),
89 mEGLDisplay(nullptr)
90 {}
91
~DisplayGLX()92 DisplayGLX::~DisplayGLX() {}
93
initialize(egl::Display * display)94 egl::Error DisplayGLX::initialize(egl::Display *display)
95 {
96 mEGLDisplay = display;
97 mXDisplay = reinterpret_cast<Display *>(display->getNativeDisplayId());
98 const auto &attribMap = display->getAttributeMap();
99
100 // ANGLE_platform_angle allows the creation of a default display
101 // using EGL_DEFAULT_DISPLAY (= nullptr). In this case just open
102 // the display specified by the DISPLAY environment variable.
103 if (mXDisplay == reinterpret_cast<Display *>(EGL_DEFAULT_DISPLAY))
104 {
105 mUsesNewXDisplay = true;
106 mXDisplay = XOpenDisplay(nullptr);
107 if (!mXDisplay)
108 {
109 return egl::Error(EGL_NOT_INITIALIZED, "Could not open the default X display.");
110 }
111 }
112
113 std::string glxInitError;
114 if (!mGLX.initialize(mXDisplay, DefaultScreen(mXDisplay), &glxInitError))
115 {
116 return egl::Error(EGL_NOT_INITIALIZED, std::move(glxInitError));
117 }
118
119 mHasMultisample = mGLX.minorVersion > 3 || mGLX.hasExtension("GLX_ARB_multisample");
120 mHasARBCreateContext = mGLX.hasExtension("GLX_ARB_create_context");
121 mHasARBCreateContextProfile = mGLX.hasExtension("GLX_ARB_create_context_profile");
122 mHasARBCreateContextRobustness = mGLX.hasExtension("GLX_ARB_create_context_robustness");
123 mHasEXTCreateContextES2Profile = mGLX.hasExtension("GLX_EXT_create_context_es2_profile");
124 mHasNVRobustnessVideoMemoryPurge = mGLX.hasExtension("GLX_NV_robustness_video_memory_purge");
125
126 std::string clientVendor = mGLX.getClientString(GLX_VENDOR);
127 mIsMesa = clientVendor.find("Mesa") != std::string::npos;
128
129 // Choose the swap_control extension to use, if any.
130 // The EXT version is better as it allows glXSwapInterval to be called per
131 // window, while we'll potentially need to change the swap interval on each
132 // swap buffers when using the SGI or MESA versions.
133 if (mGLX.hasExtension("GLX_EXT_swap_control"))
134 {
135 mSwapControl = SwapControl::EXT;
136
137 // In GLX_EXT_swap_control querying these is done on a GLXWindow so we just
138 // set default values.
139 mMinSwapInterval = 0;
140 mMaxSwapInterval = 4;
141 }
142 else if (mGLX.hasExtension("GLX_MESA_swap_control"))
143 {
144 // If we have the Mesa or SGI extension, assume that you can at least set
145 // a swap interval of 0 or 1.
146 mSwapControl = SwapControl::Mesa;
147 mMinSwapInterval = 0;
148 mMinSwapInterval = 1;
149 }
150 else if (mGLX.hasExtension("GLX_SGI_swap_control"))
151 {
152 mSwapControl = SwapControl::SGI;
153 mMinSwapInterval = 0;
154 mMinSwapInterval = 1;
155 }
156 else
157 {
158 mSwapControl = SwapControl::Absent;
159 mMinSwapInterval = 1;
160 mMinSwapInterval = 1;
161 }
162
163 if (attribMap.contains(EGL_X11_VISUAL_ID_ANGLE))
164 {
165 mRequestedVisual = static_cast<EGLint>(attribMap.get(EGL_X11_VISUAL_ID_ANGLE, -1));
166 // There is no direct way to get the GLXFBConfig matching an X11 visual ID
167 // so we have to iterate over all the GLXFBConfigs to find the right one.
168 int nConfigs;
169 int attribList[] = {
170 None,
171 };
172 glx::FBConfig *allConfigs = mGLX.chooseFBConfig(attribList, &nConfigs);
173
174 for (int i = 0; i < nConfigs; ++i)
175 {
176 if (getGLXFBConfigAttrib(allConfigs[i], GLX_VISUAL_ID) == mRequestedVisual)
177 {
178 mContextConfig = allConfigs[i];
179 break;
180 }
181 }
182 XFree(allConfigs);
183
184 if (mContextConfig == nullptr)
185 {
186 return egl::Error(EGL_NOT_INITIALIZED, "Invalid visual ID requested.");
187 }
188 }
189 else
190 {
191 // When glXMakeCurrent is called, the context and the surface must be
192 // compatible which in glX-speak means that their config have the same
193 // color buffer type, are both RGBA or ColorIndex, and their buffers have
194 // the same depth, if they exist.
195 // Since our whole EGL implementation is backed by only one GL context, this
196 // context must be compatible with all the GLXFBConfig corresponding to the
197 // EGLconfigs that we will be exposing.
198 int nConfigs;
199 int attribList[] = {// We want RGBA8 and DEPTH24_STENCIL8
200 GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8,
201 GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8,
202 // We want RGBA rendering (vs COLOR_INDEX) and doublebuffer
203 GLX_RENDER_TYPE, GLX_RGBA_BIT,
204 // Double buffer is not strictly required as a non-doublebuffer
205 // context can work with a doublebuffered surface, but it still
206 // flickers and all applications want doublebuffer anyway.
207 GLX_DOUBLEBUFFER, True,
208 // All of these must be supported for full EGL support
209 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
210 // This makes sure the config have an associated visual Id
211 GLX_X_RENDERABLE, True, GLX_CONFIG_CAVEAT, GLX_NONE, None};
212 glx::FBConfig *candidates = mGLX.chooseFBConfig(attribList, &nConfigs);
213 if (nConfigs == 0)
214 {
215 XFree(candidates);
216 return egl::Error(EGL_NOT_INITIALIZED,
217 "Could not find a decent GLX FBConfig to create the context.");
218 }
219 mContextConfig = candidates[0];
220 XFree(candidates);
221 }
222
223 const auto &eglAttributes = display->getAttributeMap();
224 if (mHasARBCreateContext)
225 {
226 egl::Error error = initializeContext(mContextConfig, eglAttributes, &mContext);
227 if (error.isError())
228 {
229 return error;
230 }
231 }
232 else
233 {
234 if (eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE,
235 EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) ==
236 EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
237 {
238 return egl::Error(EGL_NOT_INITIALIZED,
239 "Cannot create an OpenGL ES platform on GLX without "
240 "the GLX_ARB_create_context extension.");
241 }
242
243 XVisualInfo visualTemplate;
244 visualTemplate.visualid = getGLXFBConfigAttrib(mContextConfig, GLX_VISUAL_ID);
245
246 int numVisuals = 0;
247 XVisualInfo *visuals =
248 XGetVisualInfo(mXDisplay, VisualIDMask, &visualTemplate, &numVisuals);
249 if (numVisuals <= 0)
250 {
251 return egl::Error(EGL_NOT_INITIALIZED,
252 "Could not get the visual info from the fb config");
253 }
254 ASSERT(numVisuals == 1);
255
256 mContext = mGLX.createContext(&visuals[0], nullptr, true);
257 XFree(visuals);
258
259 if (!mContext)
260 {
261 return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context.");
262 }
263 }
264 ASSERT(mContext);
265
266 mCurrentNativeContexts[angle::GetCurrentThreadUniqueId()] = mContext;
267
268 // FunctionsGL and DisplayGL need to make a few GL calls, for example to
269 // query the version of the context so we need to make the context current.
270 // glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer
271 // (of size 1, 1) for the duration of these calls.
272 // Ideally we would want to unset the current context and destroy the pbuffer
273 // before going back to the application but this is TODO
274 // We could use a pbuffer of size (0, 0) but it fails on the Intel Mesa driver
275 // as commented on https://bugs.freedesktop.org/show_bug.cgi?id=38869 so we
276 // use (1, 1) instead.
277
278 int initPbufferAttribs[] = {
279 GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None,
280 };
281 mInitPbuffer = mGLX.createPbuffer(mContextConfig, initPbufferAttribs);
282 if (!mInitPbuffer)
283 {
284 return egl::Error(EGL_NOT_INITIALIZED, "Could not create the initialization pbuffer.");
285 }
286
287 if (!mGLX.makeCurrent(mInitPbuffer, mContext))
288 {
289 return egl::Error(EGL_NOT_INITIALIZED,
290 "Could not make the initialization pbuffer current.");
291 }
292
293 std::unique_ptr<FunctionsGL> functionsGL(new FunctionsGLGLX(mGLX.getProc));
294 functionsGL->initialize(eglAttributes);
295 if (mHasNVRobustnessVideoMemoryPurge)
296 {
297 GLenum status = functionsGL->getGraphicsResetStatus();
298 if (status != GL_NO_ERROR && status != GL_PURGED_CONTEXT_RESET_NV)
299 {
300 return egl::Error(EGL_NOT_INITIALIZED, "Context lost for unknown reason.");
301 }
302 }
303 // TODO(cwallez, angleproject:1303) Disable the OpenGL ES backend on Linux NVIDIA and Intel as
304 // it has problems on our automated testing. An OpenGL ES backend might not trigger this test if
305 // there is no Desktop OpenGL support, but that's not the case in our automated testing.
306 VendorID vendor = GetVendorID(functionsGL.get());
307 bool isOpenGLES =
308 eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) ==
309 EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
310 if (isOpenGLES && (IsIntel(vendor) || IsNvidia(vendor)))
311 {
312 return egl::Error(EGL_NOT_INITIALIZED,
313 "Intel or NVIDIA OpenGL ES drivers are not supported.");
314 }
315
316 syncXCommands(false);
317
318 mRenderer.reset(new RendererGL(std::move(functionsGL), eglAttributes, this));
319 const gl::Version &maxVersion = mRenderer->getMaxSupportedESVersion();
320 if (maxVersion < gl::Version(2, 0))
321 {
322 return egl::Error(EGL_NOT_INITIALIZED, "OpenGL ES 2.0 is not supportable.");
323 }
324
325 return DisplayGL::initialize(display);
326 }
327
terminate()328 void DisplayGLX::terminate()
329 {
330 DisplayGL::terminate();
331
332 if (mInitPbuffer)
333 {
334 mGLX.destroyPbuffer(mInitPbuffer);
335 mInitPbuffer = 0;
336 }
337
338 mCurrentNativeContexts.clear();
339
340 if (mContext)
341 {
342 mGLX.destroyContext(mContext);
343 mContext = nullptr;
344 }
345
346 mGLX.terminate();
347
348 mRenderer.reset();
349
350 if (mUsesNewXDisplay)
351 {
352 XCloseDisplay(mXDisplay);
353 }
354 }
355
makeCurrent(egl::Display * display,egl::Surface * drawSurface,egl::Surface * readSurface,gl::Context * context)356 egl::Error DisplayGLX::makeCurrent(egl::Display *display,
357 egl::Surface *drawSurface,
358 egl::Surface *readSurface,
359 gl::Context *context)
360 {
361 glx::Drawable newDrawable =
362 (drawSurface ? GetImplAs<SurfaceGLX>(drawSurface)->getDrawable() : mInitPbuffer);
363 glx::Context newContext = mContext;
364 // If the thread calling makeCurrent does not have the correct context current (either mContext
365 // or 0), we need to set it current.
366 if (!context)
367 {
368 newDrawable = 0;
369 newContext = 0;
370 }
371 if (newDrawable != mCurrentDrawable ||
372 newContext != mCurrentNativeContexts[angle::GetCurrentThreadUniqueId()])
373 {
374 if (mGLX.makeCurrent(newDrawable, newContext) != True)
375 {
376 return egl::Error(EGL_CONTEXT_LOST, "Failed to make the GLX context current");
377 }
378 mCurrentNativeContexts[angle::GetCurrentThreadUniqueId()] = newContext;
379 mCurrentDrawable = newDrawable;
380 }
381
382 return DisplayGL::makeCurrent(display, drawSurface, readSurface, context);
383 }
384
createWindowSurface(const egl::SurfaceState & state,EGLNativeWindowType window,const egl::AttributeMap & attribs)385 SurfaceImpl *DisplayGLX::createWindowSurface(const egl::SurfaceState &state,
386 EGLNativeWindowType window,
387 const egl::AttributeMap &attribs)
388 {
389 ASSERT(configIdToGLXConfig.count(state.config->configID) > 0);
390 glx::FBConfig fbConfig = configIdToGLXConfig[state.config->configID];
391
392 return new WindowSurfaceGLX(state, mGLX, this, window, mGLX.getDisplay(), fbConfig);
393 }
394
createPbufferSurface(const egl::SurfaceState & state,const egl::AttributeMap & attribs)395 SurfaceImpl *DisplayGLX::createPbufferSurface(const egl::SurfaceState &state,
396 const egl::AttributeMap &attribs)
397 {
398 ASSERT(configIdToGLXConfig.count(state.config->configID) > 0);
399 glx::FBConfig fbConfig = configIdToGLXConfig[state.config->configID];
400
401 EGLint width = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0));
402 EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0));
403 bool largest = (attribs.get(EGL_LARGEST_PBUFFER, EGL_FALSE) == EGL_TRUE);
404
405 return new PbufferSurfaceGLX(state, width, height, largest, mGLX, fbConfig);
406 }
407
createPbufferFromClientBuffer(const egl::SurfaceState & state,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)408 SurfaceImpl *DisplayGLX::createPbufferFromClientBuffer(const egl::SurfaceState &state,
409 EGLenum buftype,
410 EGLClientBuffer clientBuffer,
411 const egl::AttributeMap &attribs)
412 {
413 UNIMPLEMENTED();
414 return nullptr;
415 }
416
createPixmapSurface(const egl::SurfaceState & state,NativePixmapType nativePixmap,const egl::AttributeMap & attribs)417 SurfaceImpl *DisplayGLX::createPixmapSurface(const egl::SurfaceState &state,
418 NativePixmapType nativePixmap,
419 const egl::AttributeMap &attribs)
420 {
421 ASSERT(configIdToGLXConfig.count(state.config->configID) > 0);
422 glx::FBConfig fbConfig = configIdToGLXConfig[state.config->configID];
423 return new PixmapSurfaceGLX(state, nativePixmap, mGLX.getDisplay(), mGLX, fbConfig);
424 }
425
validatePixmap(const egl::Config * config,EGLNativePixmapType pixmap,const egl::AttributeMap & attributes) const426 egl::Error DisplayGLX::validatePixmap(const egl::Config *config,
427 EGLNativePixmapType pixmap,
428 const egl::AttributeMap &attributes) const
429 {
430 Window rootWindow;
431 int x = 0;
432 int y = 0;
433 unsigned int width = 0;
434 unsigned int height = 0;
435 unsigned int borderWidth = 0;
436 unsigned int depth = 0;
437 int status = XGetGeometry(mGLX.getDisplay(), pixmap, &rootWindow, &x, &y, &width, &height,
438 &borderWidth, &depth);
439 if (!status)
440 {
441 return egl::Error(EGL_BAD_NATIVE_PIXMAP, "Invalid native pixmap, XGetGeometry failed: " +
442 x11::XErrorToString(mXDisplay, status));
443 }
444
445 return egl::NoError();
446 }
447
createContext(const gl::State & state,gl::ErrorSet * errorSet,const egl::Config * configuration,const gl::Context * shareContext,const egl::AttributeMap & attribs)448 ContextImpl *DisplayGLX::createContext(const gl::State &state,
449 gl::ErrorSet *errorSet,
450 const egl::Config *configuration,
451 const gl::Context *shareContext,
452 const egl::AttributeMap &attribs)
453 {
454 RobustnessVideoMemoryPurgeStatus robustnessVideoMemoryPurgeStatus =
455 GetRobustnessVideoMemoryPurge(attribs);
456 return new ContextGL(state, errorSet, mRenderer, robustnessVideoMemoryPurgeStatus);
457 }
458
initializeContext(glx::FBConfig config,const egl::AttributeMap & eglAttributes,glx::Context * context)459 egl::Error DisplayGLX::initializeContext(glx::FBConfig config,
460 const egl::AttributeMap &eglAttributes,
461 glx::Context *context)
462 {
463 int profileMask = 0;
464
465 EGLint requestedDisplayType = static_cast<EGLint>(
466 eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE));
467 if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)
468 {
469 if (!mHasEXTCreateContextES2Profile)
470 {
471 return egl::Error(EGL_NOT_INITIALIZED,
472 "Cannot create an OpenGL ES platform on GLX without "
473 "the GLX_EXT_create_context_es_profile extension.");
474 }
475
476 ASSERT(mHasARBCreateContextProfile);
477 profileMask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
478 }
479
480 // Create a context of the requested version, if any.
481 gl::Version requestedVersion(static_cast<EGLint>(eglAttributes.get(
482 EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE)),
483 static_cast<EGLint>(eglAttributes.get(
484 EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE)));
485 if (static_cast<EGLint>(requestedVersion.major) != EGL_DONT_CARE &&
486 static_cast<EGLint>(requestedVersion.minor) != EGL_DONT_CARE)
487 {
488 if (!(profileMask & GLX_CONTEXT_ES2_PROFILE_BIT_EXT) &&
489 requestedVersion >= gl::Version(3, 2))
490 {
491 profileMask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
492 }
493 return createContextAttribs(config, requestedVersion, profileMask, context);
494 }
495
496 // The only way to get a core profile context of the highest version using
497 // glXCreateContextAttrib is to try creationg contexts in decreasing version
498 // numbers. It might look that asking for a core context of version (0, 0)
499 // works on some driver but it create a _compatibility_ context of the highest
500 // version instead. The cost of failing a context creation is small (< 0.1 ms)
501 // on Mesa but is unfortunately a bit expensive on the Nvidia driver (~3ms).
502 // Also try to get any Desktop GL context, but if that fails fallback to
503 // asking for OpenGL ES contexts.
504
505 // NOTE: below we return as soon as we're able to create a context so the
506 // "error" variable is EGL_SUCCESS when returned contrary to the common idiom
507 // of returning "error" when there is an actual error.
508 for (const auto &info : GenerateContextCreationToTry(requestedDisplayType, mIsMesa))
509 {
510 int profileFlag = 0;
511 if (info.type == ContextCreationTry::Type::DESKTOP_CORE)
512 {
513 profileFlag |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
514 }
515 else if (info.type == ContextCreationTry::Type::ES)
516 {
517 profileFlag |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
518 }
519
520 egl::Error error = createContextAttribs(config, info.version, profileFlag, context);
521 if (!error.isError())
522 {
523 return error;
524 }
525 }
526
527 return egl::Error(EGL_NOT_INITIALIZED, "Could not create a backing OpenGL context.");
528 }
529
generateConfigs()530 egl::ConfigSet DisplayGLX::generateConfigs()
531 {
532 egl::ConfigSet configs;
533 configIdToGLXConfig.clear();
534
535 const gl::Version &maxVersion = getMaxSupportedESVersion();
536 ASSERT(maxVersion >= gl::Version(2, 0));
537 bool supportsES3 = maxVersion >= gl::Version(3, 0);
538
539 int contextRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_RED_SIZE);
540 int contextGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_GREEN_SIZE);
541 int contextBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_BLUE_SIZE);
542 int contextAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ALPHA_SIZE);
543
544 int contextDepthSize = getGLXFBConfigAttrib(mContextConfig, GLX_DEPTH_SIZE);
545 int contextStencilSize = getGLXFBConfigAttrib(mContextConfig, GLX_STENCIL_SIZE);
546
547 int contextSamples = mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLES) : 0;
548 int contextSampleBuffers =
549 mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLE_BUFFERS) : 0;
550
551 int contextAccumRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_RED_SIZE);
552 int contextAccumGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_GREEN_SIZE);
553 int contextAccumBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_BLUE_SIZE);
554 int contextAccumAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_ALPHA_SIZE);
555
556 int attribList[] = {
557 GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_RENDERABLE, True, GLX_DOUBLEBUFFER, True, None,
558 };
559
560 int glxConfigCount;
561 glx::FBConfig *glxConfigs = mGLX.chooseFBConfig(attribList, &glxConfigCount);
562
563 for (int i = 0; i < glxConfigCount; i++)
564 {
565 glx::FBConfig glxConfig = glxConfigs[i];
566 egl::Config config;
567
568 // Native stuff
569 config.nativeVisualID = getGLXFBConfigAttrib(glxConfig, GLX_VISUAL_ID);
570 config.nativeVisualType = getGLXFBConfigAttrib(glxConfig, GLX_X_VISUAL_TYPE);
571 config.nativeRenderable = EGL_TRUE;
572
573 // When a visual ID has been specified with EGL_ANGLE_x11_visual we should
574 // only return configs with this visual: it will maximize performance by avoid
575 // blits in the driver when showing the window on the screen.
576 if (mRequestedVisual != -1 && config.nativeVisualID != mRequestedVisual)
577 {
578 continue;
579 }
580
581 // Buffer sizes
582 config.redSize = getGLXFBConfigAttrib(glxConfig, GLX_RED_SIZE);
583 config.greenSize = getGLXFBConfigAttrib(glxConfig, GLX_GREEN_SIZE);
584 config.blueSize = getGLXFBConfigAttrib(glxConfig, GLX_BLUE_SIZE);
585 config.alphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ALPHA_SIZE);
586 config.depthSize = getGLXFBConfigAttrib(glxConfig, GLX_DEPTH_SIZE);
587 config.stencilSize = getGLXFBConfigAttrib(glxConfig, GLX_STENCIL_SIZE);
588
589 // We require RGBA8 and the D24S8 (or no DS buffer)
590 if (config.redSize != contextRedSize || config.greenSize != contextGreenSize ||
591 config.blueSize != contextBlueSize || config.alphaSize != contextAlphaSize)
592 {
593 continue;
594 }
595 // The GLX spec says that it is ok for a whole buffer to not be present
596 // however the Mesa Intel driver (and probably on other Mesa drivers)
597 // fails to make current when the Depth stencil doesn't exactly match the
598 // configuration.
599 bool hasSameDepthStencil =
600 config.depthSize == contextDepthSize && config.stencilSize == contextStencilSize;
601 bool hasNoDepthStencil = config.depthSize == 0 && config.stencilSize == 0;
602 if (!hasSameDepthStencil && (mIsMesa || !hasNoDepthStencil))
603 {
604 continue;
605 }
606
607 config.colorBufferType = EGL_RGB_BUFFER;
608 config.luminanceSize = 0;
609 config.alphaMaskSize = 0;
610
611 config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
612
613 // Multisample and accumulation buffers
614 int samples = mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLES) : 0;
615 int sampleBuffers =
616 mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLE_BUFFERS) : 0;
617
618 int accumRedSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_RED_SIZE);
619 int accumGreenSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_GREEN_SIZE);
620 int accumBlueSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_BLUE_SIZE);
621 int accumAlphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_ALPHA_SIZE);
622
623 if (samples != contextSamples || sampleBuffers != contextSampleBuffers ||
624 accumRedSize != contextAccumRedSize || accumGreenSize != contextAccumGreenSize ||
625 accumBlueSize != contextAccumBlueSize || accumAlphaSize != contextAccumAlphaSize)
626 {
627 continue;
628 }
629
630 config.samples = samples;
631 config.sampleBuffers = sampleBuffers;
632
633 // Transparency
634 if (getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_TYPE) == GLX_TRANSPARENT_RGB)
635 {
636 config.transparentType = EGL_TRANSPARENT_RGB;
637 config.transparentRedValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_RED_VALUE);
638 config.transparentGreenValue =
639 getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_GREEN_VALUE);
640 config.transparentBlueValue =
641 getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_BLUE_VALUE);
642 }
643 else
644 {
645 config.transparentType = EGL_NONE;
646 }
647
648 // Pbuffer
649 config.maxPBufferWidth = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_WIDTH);
650 config.maxPBufferHeight = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_HEIGHT);
651 config.maxPBufferPixels = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_PIXELS);
652
653 // Caveat
654 config.configCaveat = EGL_NONE;
655
656 int caveat = getGLXFBConfigAttrib(glxConfig, GLX_CONFIG_CAVEAT);
657 if (caveat == GLX_SLOW_CONFIG)
658 {
659 config.configCaveat = EGL_SLOW_CONFIG;
660 }
661 else if (caveat == GLX_NON_CONFORMANT_CONFIG)
662 {
663 continue;
664 }
665
666 // Misc
667 config.level = getGLXFBConfigAttrib(glxConfig, GLX_LEVEL);
668
669 config.minSwapInterval = mMinSwapInterval;
670 config.maxSwapInterval = mMaxSwapInterval;
671
672 // TODO(cwallez) wildly guessing these formats, another TODO says they should be removed
673 // anyway
674 config.renderTargetFormat = GL_RGBA8;
675 config.depthStencilFormat = GL_DEPTH24_STENCIL8;
676
677 config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0);
678 config.renderableType = config.conformant;
679
680 // TODO(cwallez) I have no idea what this is
681 config.matchNativePixmap = EGL_NONE;
682
683 config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
684
685 // GLX doesn't support binding pbuffers to textures and there is no way to differentiate in
686 // EGL that pixmaps can be bound but pbuffers cannot. If both pixmaps and pbuffers are
687 // supported, generate extra configs with either pbuffer or pixmap support.
688 int glxDrawable = getGLXFBConfigAttrib(glxConfig, GLX_DRAWABLE_TYPE);
689 bool pbufferSupport = (glxDrawable & EGL_PBUFFER_BIT) != 0;
690 bool pixmapSupport = (glxDrawable & GLX_PIXMAP_BIT) != 0;
691 bool pixmapBindToTextureSupport =
692 pixmapSupport && mGLX.hasExtension("GLX_EXT_texture_from_pixmap");
693
694 if (pbufferSupport && pixmapBindToTextureSupport)
695 {
696 // Generate the pixmap-only config
697 config.surfaceType = (glxDrawable & GLX_WINDOW_BIT ? EGL_WINDOW_BIT : 0) |
698 (pixmapSupport ? EGL_PIXMAP_BIT : 0);
699
700 config.bindToTextureRGB = getGLXFBConfigAttrib(glxConfig, GLX_BIND_TO_TEXTURE_RGB_EXT);
701 config.bindToTextureRGBA =
702 getGLXFBConfigAttrib(glxConfig, GLX_BIND_TO_TEXTURE_RGBA_EXT);
703 config.yInverted = getGLXFBConfigAttrib(glxConfig, GLX_Y_INVERTED_EXT);
704
705 int id = configs.add(config);
706 configIdToGLXConfig[id] = glxConfig;
707 }
708
709 // Generate the pbuffer config. It can support pixmaps but not bind-to-texture.
710 config.surfaceType = (glxDrawable & GLX_WINDOW_BIT ? EGL_WINDOW_BIT : 0) |
711 (pbufferSupport ? EGL_PBUFFER_BIT : 0) |
712 (pixmapSupport ? EGL_PIXMAP_BIT : 0);
713
714 config.bindToTextureRGB = false;
715 config.bindToTextureRGBA = false;
716 config.yInverted = false;
717
718 int id = configs.add(config);
719 configIdToGLXConfig[id] = glxConfig;
720 }
721
722 XFree(glxConfigs);
723
724 return configs;
725 }
726
testDeviceLost()727 bool DisplayGLX::testDeviceLost()
728 {
729 return false;
730 }
731
restoreLostDevice(const egl::Display * display)732 egl::Error DisplayGLX::restoreLostDevice(const egl::Display *display)
733 {
734 return egl::Error(EGL_BAD_DISPLAY);
735 }
736
isValidNativeWindow(EGLNativeWindowType window) const737 bool DisplayGLX::isValidNativeWindow(EGLNativeWindowType window) const
738 {
739
740 // Check the validity of the window by calling a getter function on the window that
741 // returns a status code. If the window is bad the call return a status of zero. We
742 // need to set a temporary X11 error handler while doing this because the default
743 // X11 error handler exits the program on any error.
744 auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors);
745 XWindowAttributes attributes;
746 int status = XGetWindowAttributes(mXDisplay, window, &attributes);
747 XSetErrorHandler(oldErrorHandler);
748
749 return status != 0;
750 }
751
waitClient(const gl::Context * context)752 egl::Error DisplayGLX::waitClient(const gl::Context *context)
753 {
754 mGLX.waitGL();
755 return egl::NoError();
756 }
757
waitNative(const gl::Context * context,EGLint engine)758 egl::Error DisplayGLX::waitNative(const gl::Context *context, EGLint engine)
759 {
760 // eglWaitNative is used to notify the driver of changes in X11 for the current surface, such as
761 // changes of the window size. We use this event to update the child window of WindowSurfaceGLX
762 // to match its parent window's size.
763 // Handling eglWaitNative this way helps the application control when resize happens. This is
764 // important because drivers have a tendency to clobber the back buffer when the windows are
765 // resized. See http://crbug.com/326995
766 egl::Surface *drawSurface = context->getCurrentDrawSurface();
767 egl::Surface *readSurface = context->getCurrentReadSurface();
768 if (drawSurface != nullptr)
769 {
770 SurfaceGLX *glxDrawSurface = GetImplAs<SurfaceGLX>(drawSurface);
771 ANGLE_TRY(glxDrawSurface->checkForResize());
772 }
773
774 if (readSurface != drawSurface && readSurface != nullptr)
775 {
776 SurfaceGLX *glxReadSurface = GetImplAs<SurfaceGLX>(readSurface);
777 ANGLE_TRY(glxReadSurface->checkForResize());
778 }
779
780 // We still need to forward the resizing of the child window to the driver.
781 mGLX.waitX();
782 return egl::NoError();
783 }
784
getMaxSupportedESVersion() const785 gl::Version DisplayGLX::getMaxSupportedESVersion() const
786 {
787 return mRenderer->getMaxSupportedESVersion();
788 }
789
syncXCommands(bool alwaysSync) const790 void DisplayGLX::syncXCommands(bool alwaysSync) const
791 {
792 if (mUsesNewXDisplay || alwaysSync)
793 {
794 XSync(mGLX.getDisplay(), False);
795 }
796 }
797
setSwapInterval(glx::Drawable drawable,SwapControlData * data)798 void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data)
799 {
800 ASSERT(data != nullptr);
801
802 // TODO(cwallez) error checking?
803 if (mSwapControl == SwapControl::EXT)
804 {
805 // Prefer the EXT extension, it gives per-drawable swap intervals, which will
806 // minimize the number of driver calls.
807 if (data->maxSwapInterval < 0)
808 {
809 unsigned int maxSwapInterval = 0;
810 mGLX.queryDrawable(drawable, GLX_MAX_SWAP_INTERVAL_EXT, &maxSwapInterval);
811 data->maxSwapInterval = static_cast<int>(maxSwapInterval);
812 }
813
814 // When the egl configs were generated we had to guess what the max swap interval
815 // was because we didn't have a window to query it one (and that this max could
816 // depend on the monitor). This means that the target interval might be higher
817 // than the max interval and needs to be clamped.
818 const int realInterval = std::min(data->targetSwapInterval, data->maxSwapInterval);
819 if (data->currentSwapInterval != realInterval)
820 {
821 mGLX.swapIntervalEXT(drawable, realInterval);
822 data->currentSwapInterval = realInterval;
823 }
824 }
825 else if (mCurrentSwapInterval != data->targetSwapInterval)
826 {
827 // With the Mesa or SGI extensions we can still do per-drawable swap control
828 // manually but it is more expensive in number of driver calls.
829 if (mSwapControl == SwapControl::Mesa)
830 {
831 mGLX.swapIntervalMESA(data->targetSwapInterval);
832 }
833 else if (mSwapControl == SwapControl::SGI)
834 {
835 mGLX.swapIntervalSGI(data->targetSwapInterval);
836 }
837 mCurrentSwapInterval = data->targetSwapInterval;
838 }
839 }
840
isWindowVisualIdSpecified() const841 bool DisplayGLX::isWindowVisualIdSpecified() const
842 {
843 return mRequestedVisual != -1;
844 }
845
isMatchingWindowVisualId(unsigned long visualId) const846 bool DisplayGLX::isMatchingWindowVisualId(unsigned long visualId) const
847 {
848 return isWindowVisualIdSpecified() && static_cast<unsigned long>(mRequestedVisual) == visualId;
849 }
850
generateExtensions(egl::DisplayExtensions * outExtensions) const851 void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const
852 {
853 outExtensions->createContextRobustness = mHasARBCreateContextRobustness;
854
855 // Contexts are virtualized so textures ans semaphores can be shared globally
856 outExtensions->displayTextureShareGroup = true;
857 outExtensions->displaySemaphoreShareGroup = true;
858
859 outExtensions->surfacelessContext = true;
860
861 if (!mRenderer->getFeatures().disableSyncControlSupport.enabled)
862 {
863 const bool hasSyncControlOML = mGLX.hasExtension("GLX_OML_sync_control");
864 outExtensions->syncControlCHROMIUM = hasSyncControlOML;
865 outExtensions->syncControlRateANGLE = hasSyncControlOML;
866 }
867
868 outExtensions->textureFromPixmapNOK = mGLX.hasExtension("GLX_EXT_texture_from_pixmap");
869
870 outExtensions->robustnessVideoMemoryPurgeNV = mHasNVRobustnessVideoMemoryPurge;
871
872 DisplayGL::generateExtensions(outExtensions);
873 }
874
generateCaps(egl::Caps * outCaps) const875 void DisplayGLX::generateCaps(egl::Caps *outCaps) const
876 {
877 outCaps->textureNPOT = true;
878 }
879
makeCurrentSurfaceless(gl::Context * context)880 egl::Error DisplayGLX::makeCurrentSurfaceless(gl::Context *context)
881 {
882 // Nothing to do because GLX always uses the same context and the previous surface can be left
883 // current.
884 return egl::NoError();
885 }
886
getGLXFBConfigAttrib(glx::FBConfig config,int attrib) const887 int DisplayGLX::getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const
888 {
889 int result;
890 mGLX.getFBConfigAttrib(config, attrib, &result);
891 return result;
892 }
893
createContextAttribs(glx::FBConfig,const Optional<gl::Version> & version,int profileMask,glx::Context * context) const894 egl::Error DisplayGLX::createContextAttribs(glx::FBConfig,
895 const Optional<gl::Version> &version,
896 int profileMask,
897 glx::Context *context) const
898 {
899 std::vector<int> attribs;
900
901 if (mHasARBCreateContextRobustness)
902 {
903 attribs.push_back(GLX_CONTEXT_FLAGS_ARB);
904 attribs.push_back(GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB);
905 attribs.push_back(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
906 attribs.push_back(GLX_LOSE_CONTEXT_ON_RESET_ARB);
907 if (mHasNVRobustnessVideoMemoryPurge)
908 {
909 attribs.push_back(GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV);
910 attribs.push_back(GL_TRUE);
911 }
912 }
913
914 if (version.valid())
915 {
916 attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB);
917 attribs.push_back(version.value().major);
918
919 attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB);
920 attribs.push_back(version.value().minor);
921 }
922
923 if (profileMask != 0 && mHasARBCreateContextProfile)
924 {
925 attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
926 attribs.push_back(profileMask);
927 }
928
929 attribs.push_back(None);
930
931 // When creating a context with glXCreateContextAttribsARB, a variety of X11 errors can
932 // be generated. To prevent these errors from crashing our process, we simply ignore
933 // them and only look if GLXContext was created.
934 // Process all events before setting the error handler to avoid desynchronizing XCB instances
935 // (the error handler is NOT per-display).
936 XSync(mXDisplay, False);
937 auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors);
938 *context = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, attribs.data());
939 XSetErrorHandler(oldErrorHandler);
940
941 if (!*context)
942 {
943 return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context.");
944 }
945
946 return egl::NoError();
947 }
948
initializeFrontendFeatures(angle::FrontendFeatures * features) const949 void DisplayGLX::initializeFrontendFeatures(angle::FrontendFeatures *features) const
950 {
951 mRenderer->initializeFrontendFeatures(features);
952 }
953
populateFeatureList(angle::FeatureList * features)954 void DisplayGLX::populateFeatureList(angle::FeatureList *features)
955 {
956 mRenderer->getFeatures().populateFeatureList(features);
957 }
958
getRenderer() const959 RendererGL *DisplayGLX::getRenderer() const
960 {
961 return mRenderer.get();
962 }
963
getWindowSystem() const964 angle::NativeWindowSystem DisplayGLX::getWindowSystem() const
965 {
966 return angle::NativeWindowSystem::X11;
967 }
968
CreateGLXDisplay(const egl::DisplayState & state)969 DisplayImpl *CreateGLXDisplay(const egl::DisplayState &state)
970 {
971 return new DisplayGLX(state);
972 }
973
974 } // namespace rx
975