• 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 onPlatformMakeCurrent() const override;
65     std::function<void()> onPlatformGetAutoContextRestore() const override;
66     void onPlatformSwapBuffers() 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.reset(new 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, 0, 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     auto gl = GrGLMakeNativeInterface();
243     if (!gl) {
244         SkDebugf("Failed to create gl interface");
245         this->destroyGLContext();
246         return;
247     }
248 
249     if (!gl->validate()) {
250         SkDebugf("Failed to validate gl interface");
251         this->destroyGLContext();
252         return;
253     }
254 
255     this->init(std::move(gl));
256 }
257 
258 
~GLXGLTestContext()259 GLXGLTestContext::~GLXGLTestContext() {
260     this->teardown();
261     this->destroyGLContext();
262 }
263 
destroyGLContext()264 void GLXGLTestContext::destroyGLContext() {
265     if (fDisplay) {
266         if (fContext) {
267             if (glXGetCurrentContext() == fContext) {
268                 // This will ensure that the context is immediately deleted.
269                 glXMakeContextCurrent(fDisplay, None, None, nullptr);
270             }
271             glXDestroyContext(fDisplay, fContext);
272             fContext = nullptr;
273         }
274 
275         if (fGlxPixmap) {
276             glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
277             fGlxPixmap = 0;
278         }
279 
280         if (fPixmap) {
281             XFreePixmap(fDisplay, fPixmap);
282             fPixmap = 0;
283         }
284 
285         fDisplay = nullptr;
286     }
287 }
288 
289 /* Create a context with the highest possible version.
290  *
291  * Disable Xlib errors for the duration of this function (by default they abort
292  * the program) and try to get a context starting from the highest version
293  * number - there is no way to just directly ask what the highest supported
294  * version is.
295  *
296  * Returns the correct context or NULL on failure.
297  */
CreateBestContext(bool isES,Display * display,GLXFBConfig bestFbc,GLXContext glxShareContext)298 GLXContext GLXGLTestContext::CreateBestContext(bool isES, Display* display, GLXFBConfig bestFbc,
299                                                GLXContext glxShareContext) {
300     auto glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
301         glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
302     if (!glXCreateContextAttribsARB) {
303         SkDebugf("Failed to get address of glXCreateContextAttribsARB");
304         return nullptr;
305     }
306     GLXContext context = nullptr;
307     // Install Xlib error handler that will set ctxErrorOccurred.
308     // WARNING: It is global for all threads.
309     ctxErrorOccurred = false;
310     int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);
311 
312     auto versions = isES ? gles_versions : gl_versions;
313     // Well, unfortunately GLX will not just give us the highest context so
314     // instead we have to do this nastiness
315     for (int i = versions.size() - 1; i >= 0 ; i--) {
316         // WARNING: Don't try to optimize this and make this array static. The
317         // glXCreateContextAttribsARB call writes to it upon failure and the
318         // next call would fail too.
319         std::vector<int> flags = {
320             GLX_CONTEXT_MAJOR_VERSION_ARB, versions[i].first,
321             GLX_CONTEXT_MINOR_VERSION_ARB, versions[i].second,
322         };
323         if (isES) {
324             flags.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
325             // the ES2 flag should work even for higher versions
326             flags.push_back(GLX_CONTEXT_ES2_PROFILE_BIT_EXT);
327         } else if (versions[i].first > 2) {
328             flags.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
329             // TODO When Nvidia implements NVPR on Core profiles, we should start
330             // requesting core here - currently Nv Path rendering on Nvidia
331             // requires a compatibility profile.
332             flags.push_back(GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
333         }
334         flags.push_back(0);
335         context = glXCreateContextAttribsARB(display, bestFbc, glxShareContext, true,
336                                              &flags[0]);
337         // Sync to ensure any errors generated are processed.
338         XSync(display, False);
339 
340         if (!ctxErrorOccurred && context) {
341             break;
342         }
343         // try again
344         ctxErrorOccurred = false;
345     }
346     // Restore the original error handler.
347     XSetErrorHandler(oldHandler);
348     return context;
349 }
350 
onPlatformMakeCurrent() const351 void GLXGLTestContext::onPlatformMakeCurrent() const {
352     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
353         SkDebugf("Could not set the context.\n");
354     }
355 }
356 
onPlatformGetAutoContextRestore() const357 std::function<void()> GLXGLTestContext::onPlatformGetAutoContextRestore() const {
358     if (glXGetCurrentContext() == fContext) {
359         return nullptr;
360     }
361     return context_restorer();
362 }
363 
onPlatformSwapBuffers() const364 void GLXGLTestContext::onPlatformSwapBuffers() const {
365     glXSwapBuffers(fDisplay, fGlxPixmap);
366 }
367 
onPlatformGetProcAddress(const char * procName) const368 GrGLFuncPtr GLXGLTestContext::onPlatformGetProcAddress(const char* procName) const {
369     return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
370 }
371 
372 }  // anonymous namespace
373 
374 namespace sk_gpu_test {
CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,GLTestContext * shareContext)375 GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
376                                            GLTestContext *shareContext) {
377     GLXGLTestContext *glxShareContext = reinterpret_cast<GLXGLTestContext *>(shareContext);
378     GLXGLTestContext *ctx = new GLXGLTestContext(forcedGpuAPI, glxShareContext);
379     if (!ctx->isValid()) {
380         delete ctx;
381         return nullptr;
382     }
383     return ctx;
384 }
385 }  // namespace sk_gpu_test
386