• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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