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