xref: /aosp_15_r20/external/skia/tools/window/unix/GaneshGLWindowContext_unix.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 
2 /*
3  * Copyright 2016 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 "tools/window/unix/GaneshGLWindowContext_unix.h"
9 
10 #include "include/gpu/ganesh/gl/glx/GrGLMakeGLXInterface.h"
11 #include "include/gpu/ganesh/gl/GrGLInterface.h"
12 #include "tools/window/GLWindowContext.h"
13 #include "tools/window/unix/XlibWindowInfo.h"
14 
15 #include <GL/gl.h>
16 #include <GL/glx.h>
17 #include <X11/Xlib.h>
18 
19 using skwindow::DisplayParams;
20 using skwindow::XlibWindowInfo;
21 using skwindow::internal::GLWindowContext;
22 
23 namespace {
24 
25 static bool gCtxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * ev)26 static int ctxErrorHandler(Display* dpy, XErrorEvent* ev) {
27     gCtxErrorOccurred = true;
28     return 0;
29 }
30 
31 class GLWindowContext_xlib : public GLWindowContext {
32 public:
33     GLWindowContext_xlib(const XlibWindowInfo&, std::unique_ptr<const DisplayParams>);
34     ~GLWindowContext_xlib() override;
35 
36     void onDestroyContext() override;
37 
38 protected:
39     sk_sp<const GrGLInterface> onInitializeContext() override;
40     void onSwapBuffers() override;
41 
42 private:
43     GLWindowContext_xlib(void*, std::unique_ptr<const DisplayParams>);
44 
45     Display* fDisplay;
46     XWindow fWindow;
47     GLXFBConfig* fFBConfig;
48     XVisualInfo* fVisualInfo;
49     GLXContext fGLContext;
50 };
51 
GLWindowContext_xlib(const XlibWindowInfo & winInfo,std::unique_ptr<const DisplayParams> params)52 GLWindowContext_xlib::GLWindowContext_xlib(const XlibWindowInfo& winInfo,
53                                            std::unique_ptr<const DisplayParams> params)
54         : GLWindowContext(std::move(params))
55         , fDisplay(winInfo.fDisplay)
56         , fWindow(winInfo.fWindow)
57         , fFBConfig(winInfo.fFBConfig)
58         , fVisualInfo(winInfo.fVisualInfo)
59         , fGLContext() {
60     fWidth = winInfo.fWidth;
61     fHeight = winInfo.fHeight;
62     this->initializeContext();
63 }
64 
65 using CreateContextAttribsFn = GLXContext(Display*, GLXFBConfig, GLXContext, Bool, const int*);
66 
make_interface()67 static sk_sp<const GrGLInterface> make_interface() { return GrGLInterfaces::MakeGLX(); }
68 
onInitializeContext()69 sk_sp<const GrGLInterface> GLWindowContext_xlib::onInitializeContext() {
70     SkASSERT(fDisplay);
71     SkASSERT(!fGLContext);
72     sk_sp<const GrGLInterface> interface;
73     bool current = false;
74 
75     // We attempt to use glXCreateContextAttribsARB as RenderDoc requires that the context be
76     // created with this rather than glXCreateContext.
77     CreateContextAttribsFn* createContextAttribs = (CreateContextAttribsFn*)glXGetProcAddressARB(
78             (const GLubyte*)"glXCreateContextAttribsARB");
79     if (createContextAttribs && fFBConfig) {
80         // Install Xlib error handler that will set gCtxErrorOccurred
81         int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);
82 
83         // Specifying 3.2 allows an arbitrarily high context version (so long as no 3.2 features
84         // have been removed).
85         for (int minor = 2; minor >= 0 && !fGLContext; --minor) {
86             // Ganesh prefers a core profile which incidentally allows RenderDoc to work correctly.
87             for (int profile :
88                  {GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB}) {
89                 gCtxErrorOccurred = false;
90                 int attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB,
91                                  3,
92                                  GLX_CONTEXT_MINOR_VERSION_ARB,
93                                  minor,
94                                  GLX_CONTEXT_PROFILE_MASK_ARB,
95                                  profile,
96                                  0};
97                 fGLContext = createContextAttribs(fDisplay, *fFBConfig, nullptr, True, attribs);
98 
99                 // Sync to ensure any errors generated are processed.
100                 XSync(fDisplay, False);
101                 if (gCtxErrorOccurred) {
102                     continue;
103                 }
104 
105                 if (fGLContext && profile == GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB &&
106                     glXMakeCurrent(fDisplay, fWindow, fGLContext)) {
107                     current = true;
108                     // Look to see if RenderDoc is attached. If so, re-create the context with a
109                     // core profile.
110                     interface = make_interface();
111                     if (interface && interface->fExtensions.has("GL_EXT_debug_tool")) {
112                         interface.reset();
113                         glXMakeCurrent(fDisplay, None, nullptr);
114                         glXDestroyContext(fDisplay, fGLContext);
115                         current = false;
116                         fGLContext = nullptr;
117                     }
118                 }
119                 if (fGLContext) {
120                     break;
121                 }
122             }
123         }
124         // Restore the original error handler
125         XSetErrorHandler(oldHandler);
126     }
127     if (!fGLContext) {
128         fGLContext = glXCreateContext(fDisplay, fVisualInfo, nullptr, GL_TRUE);
129     }
130     if (!fGLContext) {
131         return nullptr;
132     }
133 
134     if (!current && !glXMakeCurrent(fDisplay, fWindow, fGLContext)) {
135         return nullptr;
136     }
137 
138     const char* glxExtensions = glXQueryExtensionsString(fDisplay, DefaultScreen(fDisplay));
139     if (glxExtensions) {
140         if (strstr(glxExtensions, "GLX_EXT_swap_control")) {
141             PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
142                     (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB(
143                             (const GLubyte*)"glXSwapIntervalEXT");
144             glXSwapIntervalEXT(fDisplay, fWindow, fDisplayParams->disableVsync() ? 0 : 1);
145         }
146     }
147 
148     glClearStencil(0);
149     glClearColor(0, 0, 0, 0);
150     glStencilMask(0xffffffff);
151     glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
152 
153     glXGetConfig(fDisplay, fVisualInfo, GLX_STENCIL_SIZE, &fStencilBits);
154     glXGetConfig(fDisplay, fVisualInfo, GLX_SAMPLES_ARB, &fSampleCount);
155     fSampleCount = std::max(fSampleCount, 1);
156 
157     XWindow root;
158     int x, y;
159     unsigned int border_width, depth;
160     XGetGeometry(fDisplay,
161                  fWindow,
162                  &root,
163                  &x,
164                  &y,
165                  (unsigned int*)&fWidth,
166                  (unsigned int*)&fHeight,
167                  &border_width,
168                  &depth);
169     glViewport(0, 0, fWidth, fHeight);
170 
171     return interface ? interface : make_interface();
172 }
173 
~GLWindowContext_xlib()174 GLWindowContext_xlib::~GLWindowContext_xlib() { this->destroyContext(); }
175 
onDestroyContext()176 void GLWindowContext_xlib::onDestroyContext() {
177     if (!fDisplay || !fGLContext) {
178         return;
179     }
180     glXMakeCurrent(fDisplay, None, nullptr);
181     glXDestroyContext(fDisplay, fGLContext);
182     fGLContext = nullptr;
183 }
184 
onSwapBuffers()185 void GLWindowContext_xlib::onSwapBuffers() {
186     if (fDisplay && fGLContext) {
187         glXSwapBuffers(fDisplay, fWindow);
188     }
189 }
190 
191 }  // anonymous namespace
192 
193 namespace skwindow {
194 
MakeGaneshGLForXlib(const XlibWindowInfo & winInfo,std::unique_ptr<const DisplayParams> params)195 std::unique_ptr<WindowContext> MakeGaneshGLForXlib(const XlibWindowInfo& winInfo,
196                                                    std::unique_ptr<const DisplayParams> params) {
197     std::unique_ptr<WindowContext> ctx(new GLWindowContext_xlib(winInfo, std::move(params)));
198     if (!ctx->isValid()) {
199         return nullptr;
200     }
201     return ctx;
202 }
203 
204 }  // namespace skwindow
205