• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/private/SkOnce.h"
9 #include "tools/gpu/gl/GLTestContext.h"
10 
11 #include <X11/Xlib.h>
12 #include <GL/glx.h>
13 #include <GL/glu.h>
14 
15 #include <vector>
16 #include <utility>
17 
18 namespace {
19 
20 /* Note: Skia requires glx 1.3 or newer */
21 
22 /* This struct is taken from a mesa demo.  Please update as required */
23 static const std::vector<std::pair<int, int>> gl_versions = {
24    {1, 0},
25    {1, 1},
26    {1, 2},
27    {1, 3},
28    {1, 4},
29    {1, 5},
30    {2, 0},
31    {2, 1},
32    {3, 0},
33    {3, 1},
34    {3, 2},
35    {3, 3},
36    {4, 0},
37    {4, 1},
38    {4, 2},
39    {4, 3},
40    {4, 4},
41 };
42 
43 static const std::vector<std::pair<int, int>> gles_versions = {
44     {2, 0},
45     {3, 0},
46 };
47 
48 static bool ctxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * ev)49 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
50     ctxErrorOccurred = true;
51     return 0;
52 }
53 
54 class GLXGLTestContext : public sk_gpu_test::GLTestContext {
55 public:
56     GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* shareList);
57     ~GLXGLTestContext() override;
58 
59 private:
60     void destroyGLContext();
61     static GLXContext CreateBestContext(bool isES, Display* display, GLXFBConfig bestFbc,
62                                         GLXContext glxSharedContext);
63 
64     void onPlatformMakeNotCurrent() const override;
65     void onPlatformMakeCurrent() const override;
66     std::function<void()> onPlatformGetAutoContextRestore() const override;
67     GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
68 
69     GLXContext fContext;
70     Display* fDisplay;
71     Pixmap fPixmap;
72     GLXPixmap fGlxPixmap;
73 };
74 
get_display()75 static Display* get_display() {
76     class AutoDisplay {
77     public:
78         AutoDisplay() { fDisplay = XOpenDisplay(nullptr); }
79         ~AutoDisplay() {
80             if (fDisplay) {
81                 XCloseDisplay(fDisplay);
82             }
83         }
84         Display* display() const { return fDisplay; }
85     private:
86         Display* fDisplay;
87     };
88     static std::unique_ptr<AutoDisplay> ad;
89     static SkOnce once;
90     once([] { ad = std::make_unique<AutoDisplay>(); });
91     return ad->display();
92 }
93 
context_restorer()94 std::function<void()> context_restorer() {
95     auto display = glXGetCurrentDisplay();
96     auto drawable = glXGetCurrentDrawable();
97     auto context = glXGetCurrentContext();
98     // On some systems calling glXMakeCurrent with a null display crashes.
99     if (!display) {
100         display = get_display();
101     }
102     return [display, drawable, context] { glXMakeCurrent(display, drawable, context); };
103 }
104 
GLXGLTestContext(GrGLStandard forcedGpuAPI,GLXGLTestContext * shareContext)105 GLXGLTestContext::GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* shareContext)
106     : fContext(nullptr)
107     , fDisplay(nullptr)
108     , fPixmap(0)
109     , fGlxPixmap(0) {
110     // We cross our fingers that this is the first X call in the program and that if the application
111     // is actually threaded that this succeeds.
112     static SkOnce gOnce;
113     gOnce([] { XInitThreads(); });
114 
115     fDisplay = get_display();
116 
117     GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr;
118 
119     if (!fDisplay) {
120         SkDebugf("Failed to open X display.\n");
121         this->destroyGLContext();
122         return;
123     }
124 
125     // Get a matching FB config
126     static int visual_attribs[] = {
127         GLX_X_RENDERABLE    , True,
128         GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
129         None
130     };
131 
132     int glx_major, glx_minor;
133 
134     // FBConfigs were added in GLX version 1.3.
135     if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) ||
136             ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) {
137         SkDebugf("GLX version 1.3 or higher required.\n");
138         this->destroyGLContext();
139         return;
140     }
141 
142     //SkDebugf("Getting matching framebuffer configs.\n");
143     int fbcount;
144     GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
145                                           visual_attribs, &fbcount);
146     if (!fbc) {
147         SkDebugf("Failed to retrieve a framebuffer config.\n");
148         this->destroyGLContext();
149         return;
150     }
151     //SkDebugf("Found %d matching FB configs.\n", fbcount);
152 
153     // Pick the FB config/visual with the most samples per pixel
154     //SkDebugf("Getting XVisualInfos.\n");
155     int best_fbc = -1, best_num_samp = -1;
156 
157     int i;
158     for (i = 0; i < fbcount; ++i) {
159         XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
160         if (vi) {
161             int samp_buf, samples;
162             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
163             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
164 
165             //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
166             //       " SAMPLES = %d\n",
167             //        i, (unsigned int)vi->visualid, samp_buf, samples);
168 
169             if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) {
170                 best_fbc = i;
171                 best_num_samp = samples;
172             }
173         }
174         XFree(vi);
175     }
176 
177     GLXFBConfig bestFbc = fbc[best_fbc];
178 
179     // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
180     XFree(fbc);
181 
182     // Get a visual
183     XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
184     //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
185 
186     fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
187 
188     if (!fPixmap) {
189         SkDebugf("Failed to create pixmap.\n");
190         this->destroyGLContext();
191         return;
192     }
193 
194     fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
195 
196     // Done with the visual info data
197     XFree(vi);
198 
199     // Get the default screen's GLX extension list
200     const char *glxExts = glXQueryExtensionsString(
201         fDisplay, DefaultScreen(fDisplay)
202     );
203     // Check for the GLX_ARB_create_context extension string and the function.
204     // If either is not present, use GLX 1.3 context creation method.
205     if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"),
206                            reinterpret_cast<const GLubyte*>(glxExts))) {
207         if (kGLES_GrGLStandard != forcedGpuAPI) {
208             fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, nullptr, True);
209         }
210     } else {
211         if (kGLES_GrGLStandard == forcedGpuAPI) {
212             if (gluCheckExtension(
213                     reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"),
214                     reinterpret_cast<const GLubyte*>(glxExts))) {
215                 fContext = CreateBestContext(true, fDisplay, bestFbc, glxShareContext);
216             }
217         } else {
218             fContext = CreateBestContext(false, fDisplay, bestFbc, glxShareContext);
219         }
220     }
221     if (!fContext) {
222         SkDebugf("Failed to create an OpenGL context.\n");
223         this->destroyGLContext();
224         return;
225     }
226 
227     // Verify that context is a direct context
228     if (!glXIsDirect(fDisplay, fContext)) {
229         //SkDebugf("Indirect GLX rendering context obtained.\n");
230     } else {
231         //SkDebugf("Direct GLX rendering context obtained.\n");
232     }
233 
234     SkScopeExit restorer(context_restorer());
235     //SkDebugf("Making context current.\n");
236     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
237       SkDebugf("Could not set the context.\n");
238         this->destroyGLContext();
239         return;
240     }
241 
242 #ifdef SK_GL
243     auto gl = GrGLMakeNativeInterface();
244     if (!gl) {
245         SkDebugf("Failed to create gl interface");
246         this->destroyGLContext();
247         return;
248     }
249 
250     if (!gl->validate()) {
251         SkDebugf("Failed to validate gl interface");
252         this->destroyGLContext();
253         return;
254     }
255 
256     this->init(std::move(gl));
257 #else
258     // Allow the GLTestContext creation to succeed without a GrGLInterface to support
259     // GrContextFactory's persistent GL context workaround for Vulkan. We won't need the
260     // GrGLInterface since we're not running the GL backend.
261     this->init(nullptr);
262 #endif
263 }
264 
265 
~GLXGLTestContext()266 GLXGLTestContext::~GLXGLTestContext() {
267     this->teardown();
268     this->destroyGLContext();
269 }
270 
destroyGLContext()271 void GLXGLTestContext::destroyGLContext() {
272     if (fDisplay) {
273         if (fContext) {
274             if (glXGetCurrentContext() == fContext) {
275                 // This will ensure that the context is immediately deleted.
276                 glXMakeContextCurrent(fDisplay, None, None, nullptr);
277             }
278             glXDestroyContext(fDisplay, fContext);
279             fContext = nullptr;
280         }
281 
282         if (fGlxPixmap) {
283             glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
284             fGlxPixmap = 0;
285         }
286 
287         if (fPixmap) {
288             XFreePixmap(fDisplay, fPixmap);
289             fPixmap = 0;
290         }
291 
292         fDisplay = nullptr;
293     }
294 }
295 
296 /* Create a context with the highest possible version.
297  *
298  * Disable Xlib errors for the duration of this function (by default they abort
299  * the program) and try to get a context starting from the highest version
300  * number - there is no way to just directly ask what the highest supported
301  * version is.
302  *
303  * Returns the correct context or NULL on failure.
304  */
CreateBestContext(bool isES,Display * display,GLXFBConfig bestFbc,GLXContext glxShareContext)305 GLXContext GLXGLTestContext::CreateBestContext(bool isES, Display* display, GLXFBConfig bestFbc,
306                                                GLXContext glxShareContext) {
307     auto glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
308         glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
309     if (!glXCreateContextAttribsARB) {
310         SkDebugf("Failed to get address of glXCreateContextAttribsARB");
311         return nullptr;
312     }
313     GLXContext context = nullptr;
314     // Install Xlib error handler that will set ctxErrorOccurred.
315     // WARNING: It is global for all threads.
316     ctxErrorOccurred = false;
317     int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);
318 
319     auto versions = isES ? gles_versions : gl_versions;
320     // Well, unfortunately GLX will not just give us the highest context so
321     // instead we have to do this nastiness
322     for (int i = versions.size() - 1; i >= 0 ; i--) {
323         // WARNING: Don't try to optimize this and make this array static. The
324         // glXCreateContextAttribsARB call writes to it upon failure and the
325         // next call would fail too.
326         std::vector<int> flags = {
327             GLX_CONTEXT_MAJOR_VERSION_ARB, versions[i].first,
328             GLX_CONTEXT_MINOR_VERSION_ARB, versions[i].second,
329         };
330         if (isES) {
331             flags.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
332             // the ES2 flag should work even for higher versions
333             flags.push_back(GLX_CONTEXT_ES2_PROFILE_BIT_EXT);
334         } else if (versions[i].first > 2) {
335             flags.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
336             flags.push_back(GLX_CONTEXT_CORE_PROFILE_BIT_ARB);
337         }
338         flags.push_back(0);
339         context = glXCreateContextAttribsARB(display, bestFbc, glxShareContext, true,
340                                              &flags[0]);
341         // Sync to ensure any errors generated are processed.
342         XSync(display, False);
343 
344         if (!ctxErrorOccurred && context) {
345             break;
346         }
347         // try again
348         ctxErrorOccurred = false;
349     }
350     // Restore the original error handler.
351     XSetErrorHandler(oldHandler);
352     return context;
353 }
354 
onPlatformMakeNotCurrent() const355 void GLXGLTestContext::onPlatformMakeNotCurrent() const {
356     if (!glXMakeCurrent(fDisplay, None , nullptr)) {
357         SkDebugf("Could not reset the context.\n");
358     }
359 }
360 
onPlatformMakeCurrent() const361 void GLXGLTestContext::onPlatformMakeCurrent() const {
362     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
363         SkDebugf("Could not set the context.\n");
364     }
365 }
366 
onPlatformGetAutoContextRestore() const367 std::function<void()> GLXGLTestContext::onPlatformGetAutoContextRestore() const {
368     if (glXGetCurrentContext() == fContext) {
369         return nullptr;
370     }
371     return context_restorer();
372 }
373 
onPlatformGetProcAddress(const char * procName) const374 GrGLFuncPtr GLXGLTestContext::onPlatformGetProcAddress(const char* procName) const {
375     return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
376 }
377 
378 }  // anonymous namespace
379 
380 namespace sk_gpu_test {
CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,GLTestContext * shareContext)381 GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
382                                            GLTestContext *shareContext) {
383     GLXGLTestContext *glxShareContext = reinterpret_cast<GLXGLTestContext *>(shareContext);
384     GLXGLTestContext *ctx = new GLXGLTestContext(forcedGpuAPI, glxShareContext);
385     if (!ctx->isValid()) {
386         delete ctx;
387         return nullptr;
388     }
389     return ctx;
390 }
391 }  // namespace sk_gpu_test
392