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