1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8#include "gl/SkGLContext.h" 9 10#include <X11/Xlib.h> 11#include <GL/glx.h> 12#include <GL/glu.h> 13 14namespace { 15 16/* Note: Skia requires glx 1.3 or newer */ 17 18/* This struct is taken from a mesa demo. Please update as required */ 19static const struct { int major, minor; } gl_versions[] = { 20 {1, 0}, 21 {1, 1}, 22 {1, 2}, 23 {1, 3}, 24 {1, 4}, 25 {1, 5}, 26 {2, 0}, 27 {2, 1}, 28 {3, 0}, 29 {3, 1}, 30 {3, 2}, 31 {3, 3}, 32 {4, 0}, 33 {4, 1}, 34 {4, 2}, 35 {4, 3}, 36 {4, 4}, 37 {0, 0} /* end of list */ 38}; 39#define NUM_GL_VERSIONS SK_ARRAY_COUNT(gl_versions) 40 41static bool ctxErrorOccurred = false; 42static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { 43 ctxErrorOccurred = true; 44 return 0; 45} 46 47class GLXGLContext : public SkGLContext { 48public: 49 GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareList); 50 ~GLXGLContext() override; 51 52private: 53 void destroyGLContext(); 54 55 void onPlatformMakeCurrent() const override; 56 void onPlatformSwapBuffers() const override; 57 GrGLFuncPtr onPlatformGetProcAddress(const char*) const override; 58 59 GLXContext fContext; 60 Display* fDisplay; 61 Pixmap fPixmap; 62 GLXPixmap fGlxPixmap; 63}; 64 65GLXGLContext::GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareContext) 66 : fContext(nullptr) 67 , fDisplay(nullptr) 68 , fPixmap(0) 69 , fGlxPixmap(0) { 70 fDisplay = XOpenDisplay(0); 71 72 GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr; 73 74 if (!fDisplay) { 75 SkDebugf("Failed to open X display.\n"); 76 this->destroyGLContext(); 77 return; 78 } 79 80 // Get a matching FB config 81 static int visual_attribs[] = { 82 GLX_X_RENDERABLE , True, 83 GLX_DRAWABLE_TYPE , GLX_PIXMAP_BIT, 84 None 85 }; 86 87 int glx_major, glx_minor; 88 89 // FBConfigs were added in GLX version 1.3. 90 if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) || 91 ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) { 92 SkDebugf("GLX version 1.3 or higher required.\n"); 93 this->destroyGLContext(); 94 return; 95 } 96 97 //SkDebugf("Getting matching framebuffer configs.\n"); 98 int fbcount; 99 GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), 100 visual_attribs, &fbcount); 101 if (!fbc) { 102 SkDebugf("Failed to retrieve a framebuffer config.\n"); 103 this->destroyGLContext(); 104 return; 105 } 106 //SkDebugf("Found %d matching FB configs.\n", fbcount); 107 108 // Pick the FB config/visual with the most samples per pixel 109 //SkDebugf("Getting XVisualInfos.\n"); 110 int best_fbc = -1, best_num_samp = -1; 111 112 int i; 113 for (i = 0; i < fbcount; ++i) { 114 XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]); 115 if (vi) { 116 int samp_buf, samples; 117 glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); 118 glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples); 119 120 //SkDebugf(" Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d," 121 // " SAMPLES = %d\n", 122 // i, (unsigned int)vi->visualid, samp_buf, samples); 123 124 if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) 125 best_fbc = i, best_num_samp = samples; 126 } 127 XFree(vi); 128 } 129 130 GLXFBConfig bestFbc = fbc[best_fbc]; 131 132 // Be sure to free the FBConfig list allocated by glXChooseFBConfig() 133 XFree(fbc); 134 135 // Get a visual 136 XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc); 137 //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid); 138 139 fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth); 140 141 if (!fPixmap) { 142 SkDebugf("Failed to create pixmap.\n"); 143 this->destroyGLContext(); 144 return; 145 } 146 147 fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap); 148 149 // Done with the visual info data 150 XFree(vi); 151 152 // Create the context 153 154 // Install an X error handler so the application won't exit if GL 3.0 155 // context allocation fails. 156 // 157 // Note this error handler is global. 158 // All display connections in all threads of a process use the same 159 // error handler, so be sure to guard against other threads issuing 160 // X commands while this code is running. 161 ctxErrorOccurred = false; 162 int (*oldHandler)(Display*, XErrorEvent*) = 163 XSetErrorHandler(&ctxErrorHandler); 164 165 // Get the default screen's GLX extension list 166 const char *glxExts = glXQueryExtensionsString( 167 fDisplay, DefaultScreen(fDisplay) 168 ); 169 170 171 // Check for the GLX_ARB_create_context extension string and the function. 172 // If either is not present, use GLX 1.3 context creation method. 173 if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"), 174 reinterpret_cast<const GLubyte*>(glxExts))) { 175 if (kGLES_GrGLStandard != forcedGpuAPI) { 176 fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True); 177 } 178 } else { 179 //SkDebugf("Creating context.\n"); 180 PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 181 (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB"); 182 183 if (kGLES_GrGLStandard == forcedGpuAPI) { 184 if (gluCheckExtension( 185 reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"), 186 reinterpret_cast<const GLubyte*>(glxExts))) { 187 static const int context_attribs_gles[] = { 188 GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 189 GLX_CONTEXT_MINOR_VERSION_ARB, 0, 190 GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, 191 None 192 }; 193 fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True, 194 context_attribs_gles); 195 } 196 } else { 197 // Well, unfortunately GLX will not just give us the highest context so instead we have 198 // to do this nastiness 199 for (i = NUM_GL_VERSIONS - 2; i > 0 ; i--) { 200 /* don't bother below GL 3.0 */ 201 if (gl_versions[i].major == 3 && gl_versions[i].minor == 0) { 202 break; 203 } 204 // On Nvidia GPUs, to use Nv Path rendering we need a compatibility profile for the 205 // time being. 206 // TODO when Nvidia implements NVPR on Core profiles, we should start requesting 207 // core here 208 static const int context_attribs_gl[] = { 209 GLX_CONTEXT_MAJOR_VERSION_ARB, gl_versions[i].major, 210 GLX_CONTEXT_MINOR_VERSION_ARB, gl_versions[i].minor, 211 GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 212 None 213 }; 214 fContext = 215 glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True, 216 context_attribs_gl); 217 218 // Sync to ensure any errors generated are processed. 219 XSync(fDisplay, False); 220 221 if (!ctxErrorOccurred && fContext) { 222 break; 223 } 224 // try again 225 ctxErrorOccurred = false; 226 } 227 228 // Couldn't create GL 3.0 context. 229 // Fall back to old-style 2.x context. 230 // When a context version below 3.0 is requested, 231 // implementations will return the newest context version 232 // compatible with OpenGL versions less than version 3.0. 233 if (ctxErrorOccurred || !fContext) { 234 static const int context_attribs_gl_fallback[] = { 235 GLX_CONTEXT_MAJOR_VERSION_ARB, 1, 236 GLX_CONTEXT_MINOR_VERSION_ARB, 0, 237 None 238 }; 239 240 ctxErrorOccurred = false; 241 242 fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True, 243 context_attribs_gl_fallback); 244 } 245 } 246 } 247 248 // Sync to ensure any errors generated are processed. 249 XSync(fDisplay, False); 250 251 // Restore the original error handler 252 XSetErrorHandler(oldHandler); 253 254 if (ctxErrorOccurred || !fContext) { 255 SkDebugf("Failed to create an OpenGL context.\n"); 256 this->destroyGLContext(); 257 return; 258 } 259 260 // Verify that context is a direct context 261 if (!glXIsDirect(fDisplay, fContext)) { 262 //SkDebugf("Indirect GLX rendering context obtained.\n"); 263 } else { 264 //SkDebugf("Direct GLX rendering context obtained.\n"); 265 } 266 267 //SkDebugf("Making context current.\n"); 268 if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { 269 SkDebugf("Could not set the context.\n"); 270 this->destroyGLContext(); 271 return; 272 } 273 274 SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface()); 275 if (nullptr == gl.get()) { 276 SkDebugf("Failed to create gl interface"); 277 this->destroyGLContext(); 278 return; 279 } 280 281 if (!gl->validate()) { 282 SkDebugf("Failed to validate gl interface"); 283 this->destroyGLContext(); 284 return; 285 } 286 287 this->init(gl.detach()); 288} 289 290 291GLXGLContext::~GLXGLContext() { 292 this->teardown(); 293 this->destroyGLContext(); 294} 295 296void GLXGLContext::destroyGLContext() { 297 if (fDisplay) { 298 glXMakeCurrent(fDisplay, 0, 0); 299 300 if (fContext) { 301 glXDestroyContext(fDisplay, fContext); 302 fContext = nullptr; 303 } 304 305 if (fGlxPixmap) { 306 glXDestroyGLXPixmap(fDisplay, fGlxPixmap); 307 fGlxPixmap = 0; 308 } 309 310 if (fPixmap) { 311 XFreePixmap(fDisplay, fPixmap); 312 fPixmap = 0; 313 } 314 315 XCloseDisplay(fDisplay); 316 fDisplay = nullptr; 317 } 318} 319 320void GLXGLContext::onPlatformMakeCurrent() const { 321 if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { 322 SkDebugf("Could not set the context.\n"); 323 } 324} 325 326void GLXGLContext::onPlatformSwapBuffers() const { 327 glXSwapBuffers(fDisplay, fGlxPixmap); 328} 329 330GrGLFuncPtr GLXGLContext::onPlatformGetProcAddress(const char* procName) const { 331 return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName)); 332} 333 334} // anonymous namespace 335 336SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) { 337 GLXGLContext* glxShareContext = reinterpret_cast<GLXGLContext*>(shareContext); 338 GLXGLContext *ctx = new GLXGLContext(forcedGpuAPI, glxShareContext); 339 if (!ctx->isValid()) { 340 delete ctx; 341 return nullptr; 342 } 343 return ctx; 344} 345