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 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; 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 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 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 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 259 GLXGLTestContext::~GLXGLTestContext() { 260 this->teardown(); 261 this->destroyGLContext(); 262 } 263 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 */ 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 351 void GLXGLTestContext::onPlatformMakeCurrent() const { 352 if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { 353 SkDebugf("Could not set the context.\n"); 354 } 355 } 356 357 std::function<void()> GLXGLTestContext::onPlatformGetAutoContextRestore() const { 358 if (glXGetCurrentContext() == fContext) { 359 return nullptr; 360 } 361 return context_restorer(); 362 } 363 364 void GLXGLTestContext::onPlatformSwapBuffers() const { 365 glXSwapBuffers(fDisplay, fGlxPixmap); 366 } 367 368 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 { 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