Flutter Engine
The Flutter Engine
CreatePlatformGLTestContext_glx.cpp
Go to the documentation of this file.
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
11
12#include <X11/Xlib.h>
13#include <GL/glx.h>
14#include <GL/glu.h>
15
16#include <vector>
17#include <utility>
18
19namespace {
20
21/* Note: Skia requires glx 1.3 or newer */
22
23/* This struct is taken from a mesa demo. Please update as required */
24static 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
44static const std::vector<std::pair<int, int>> gles_versions = {
45 {2, 0},
46 {3, 0},
47};
48
49static bool ctxErrorOccurred = false;
50static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
51 ctxErrorOccurred = true;
52 return 0;
53}
54
55class GLXGLTestContext : public sk_gpu_test::GLTestContext {
56public:
57 GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* shareList);
58 ~GLXGLTestContext() override;
59
60private:
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
76static 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
95std::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
106GLXGLTestContext::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)
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
267GLXGLTestContext::~GLXGLTestContext() {
268 this->teardown();
269 this->destroyGLContext();
270}
271
272void 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 */
306GLXContext 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
356void GLXGLTestContext::onPlatformMakeNotCurrent() const {
357 if (!glXMakeCurrent(fDisplay, None , nullptr)) {
358 SkDebugf("Could not reset the context.\n");
359 }
360}
361
362void GLXGLTestContext::onPlatformMakeCurrent() const {
363 if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
364 SkDebugf("Could not set the context.\n");
365 }
366}
367
368std::function<void()> GLXGLTestContext::onPlatformGetAutoContextRestore() const {
369 if (glXGetCurrentContext() == fContext) {
370 return nullptr;
371 }
372 return context_restorer();
373}
374
375GrGLFuncPtr GLXGLTestContext::onPlatformGetProcAddress(const char* procName) const {
376 return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
377}
378
379} // anonymous namespace
380
381namespace sk_gpu_test {
382GLTestContext *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
void(* GrGLFuncPtr)()
Definition: GrGLInterface.h:17
GrGLStandard
Definition: GrGLTypes.h:19
@ kGLES_GrGLStandard
Definition: GrGLTypes.h:22
unsigned char GrGLubyte
Definition: GrGLTypes.h:111
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
const Context & fContext
Definition: SkOnce.h:22
FlutterSemanticsFlag flags
Dart_NativeFunction function
Definition: fuchsia.cc:51
SK_API sk_sp< const GrGLInterface > MakeGLX()
static bool init()
gl
Definition: malisc.py:41
GLTestContext * CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI, GLTestContext *shareContext)