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