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