xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // WindowSurfaceGLX.cpp: GLX implementation of egl::Surface for windows
8 
9 #include "common/debug.h"
10 
11 #include "libANGLE/renderer/gl/glx/DisplayGLX.h"
12 
13 #include "libANGLE/renderer/gl/glx/FunctionsGLX.h"
14 #include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h"
15 
16 namespace rx
17 {
18 
IgnoreX11Errors(Display *,XErrorEvent *)19 static int IgnoreX11Errors(Display *, XErrorEvent *)
20 {
21     return 0;
22 }
23 
WindowSurfaceGLX(const egl::SurfaceState & state,const FunctionsGLX & glx,DisplayGLX * glxDisplay,Window window,Display * display,glx::FBConfig fbConfig)24 WindowSurfaceGLX::WindowSurfaceGLX(const egl::SurfaceState &state,
25                                    const FunctionsGLX &glx,
26                                    DisplayGLX *glxDisplay,
27                                    Window window,
28                                    Display *display,
29                                    glx::FBConfig fbConfig)
30     : SurfaceGLX(state),
31       mParent(window),
32       mWindow(0),
33       mDisplay(display),
34       mUseChildWindow(false),
35       mParentWidth(0),
36       mParentHeight(0),
37       mGLX(glx),
38       mGLXDisplay(glxDisplay),
39       mFBConfig(fbConfig),
40       mGLXWindow(0)
41 {}
42 
~WindowSurfaceGLX()43 WindowSurfaceGLX::~WindowSurfaceGLX()
44 {
45     if (mGLXWindow)
46     {
47         mGLX.destroyWindow(mGLXWindow);
48     }
49 
50     if (mUseChildWindow && mWindow)
51     {
52         // When destroying the window, it may happen that the window has already been
53         // destroyed by the application (this happens in Chromium). There is no way to
54         // atomically check that a window exists and to destroy it so instead we call
55         // XDestroyWindow, ignoring any errors.
56         auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors);
57         XDestroyWindow(mDisplay, mWindow);
58         XSync(mDisplay, False);
59         XSetErrorHandler(oldErrorHandler);
60     }
61 
62     mGLXDisplay->syncXCommands(true);
63 }
64 
initialize(const egl::Display * display)65 egl::Error WindowSurfaceGLX::initialize(const egl::Display *display)
66 {
67     mUseChildWindow = !mGLXDisplay->isWindowVisualIdSpecified();
68 
69     XVisualInfo *visualInfo = nullptr;
70     Colormap colormap       = 0;
71     if (!mUseChildWindow)
72     {
73         XWindowAttributes windowAttributes;
74         XGetWindowAttributes(mDisplay, mParent, &windowAttributes);
75         unsigned long visualId = windowAttributes.visual->visualid;
76         // If the window's visual ID is different from the one provided by
77         // ANGLE_X11_VISUAL_ID, fallback to using a child window.
78         if (!mGLXDisplay->isMatchingWindowVisualId(visualId))
79         {
80             mUseChildWindow = true;
81         }
82     }
83     if (mUseChildWindow)
84     {
85         // The visual of the X window, GLX window and GLX context must match,
86         // however we received a user-created window that can have any visual
87         // and wouldn't work with our GLX context. To work in all cases, we
88         // create a child window with the right visual that covers all of its
89         // parent.
90         visualInfo = mGLX.getVisualFromFBConfig(mFBConfig);
91         if (!visualInfo)
92         {
93             return egl::EglBadNativeWindow()
94                    << "Failed to get the XVisualInfo for the child window.";
95         }
96         Visual *visual = visualInfo->visual;
97 
98         if (!getWindowDimensions(mParent, &mParentWidth, &mParentHeight))
99         {
100             return egl::EglBadNativeWindow() << "Failed to get the parent window's dimensions.";
101         }
102 
103         // The depth, colormap and visual must match otherwise we get a X error
104         // so we specify the colormap attribute. Also we do not want the window
105         // to be taken into account for input so we specify the event and
106         // do-not-propagate masks to 0 (the defaults). Finally we specify the
107         // border pixel attribute so that we can use a different visual depth
108         // than our parent (seems like X uses that as a condition to render
109         // the subwindow in a different buffer)
110         XSetWindowAttributes attributes;
111         unsigned long attributeMask = CWColormap | CWBorderPixel;
112 
113         colormap = XCreateColormap(mDisplay, mParent, visual, AllocNone);
114         if (!colormap)
115         {
116             XFree(visualInfo);
117             return egl::EglBadNativeWindow()
118                    << "Failed to create the Colormap for the child window.";
119         }
120         attributes.colormap     = colormap;
121         attributes.border_pixel = 0;
122 
123         // TODO(cwallez) set up our own error handler to see if the call failed
124         mWindow = XCreateWindow(mDisplay, mParent, 0, 0, mParentWidth, mParentHeight, 0,
125                                 visualInfo->depth, InputOutput, visual, attributeMask, &attributes);
126     }
127 
128     mGLXWindow = mGLX.createWindow(mFBConfig, (mUseChildWindow ? mWindow : mParent), nullptr);
129 
130     if (mUseChildWindow)
131     {
132         XMapWindow(mDisplay, mWindow);
133     }
134 
135     XFlush(mDisplay);
136 
137     if (mUseChildWindow)
138     {
139         XFree(visualInfo);
140         XFreeColormap(mDisplay, colormap);
141     }
142 
143     mGLXDisplay->syncXCommands(true);
144 
145     return egl::NoError();
146 }
147 
makeCurrent(const gl::Context * context)148 egl::Error WindowSurfaceGLX::makeCurrent(const gl::Context *context)
149 {
150     return egl::NoError();
151 }
152 
swap(const gl::Context * context)153 egl::Error WindowSurfaceGLX::swap(const gl::Context *context)
154 {
155     // We need to swap before resizing as some drivers clobber the back buffer
156     // when the window is resized.
157     mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl);
158     mGLX.swapBuffers(mGLXWindow);
159 
160     if (mUseChildWindow)
161     {
162         egl::Error error = checkForResize();
163         if (error.isError())
164         {
165             return error;
166         }
167     }
168 
169     return egl::NoError();
170 }
171 
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)172 egl::Error WindowSurfaceGLX::postSubBuffer(const gl::Context *context,
173                                            EGLint x,
174                                            EGLint y,
175                                            EGLint width,
176                                            EGLint height)
177 {
178     UNIMPLEMENTED();
179     return egl::NoError();
180 }
181 
querySurfacePointerANGLE(EGLint attribute,void ** value)182 egl::Error WindowSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void **value)
183 {
184     UNIMPLEMENTED();
185     return egl::NoError();
186 }
187 
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)188 egl::Error WindowSurfaceGLX::bindTexImage(const gl::Context *context,
189                                           gl::Texture *texture,
190                                           EGLint buffer)
191 {
192     UNIMPLEMENTED();
193     return egl::NoError();
194 }
195 
releaseTexImage(const gl::Context * context,EGLint buffer)196 egl::Error WindowSurfaceGLX::releaseTexImage(const gl::Context *context, EGLint buffer)
197 {
198     UNIMPLEMENTED();
199     return egl::NoError();
200 }
201 
setSwapInterval(const egl::Display * display,EGLint interval)202 void WindowSurfaceGLX::setSwapInterval(const egl::Display *display, EGLint interval)
203 {
204     mSwapControl.targetSwapInterval = interval;
205 }
206 
getWidth() const207 EGLint WindowSurfaceGLX::getWidth() const
208 {
209     if (mUseChildWindow)
210     {
211         // If there's a child window, the size of the window is always the same as the cached
212         // size of its parent.
213         return mParentWidth;
214     }
215     else
216     {
217         unsigned int parentWidth, parentHeight;
218         if (!getWindowDimensions(mParent, &parentWidth, &parentHeight))
219         {
220             return mParentWidth;
221         }
222         return parentWidth;
223     }
224 }
225 
getHeight() const226 EGLint WindowSurfaceGLX::getHeight() const
227 {
228     if (mUseChildWindow)
229     {
230         // If there's a child window, the size of the window is always the same as the cached
231         // size of its parent.
232         return mParentHeight;
233     }
234     else
235     {
236         unsigned int parentWidth, parentHeight;
237         if (!getWindowDimensions(mParent, &parentWidth, &parentHeight))
238         {
239             return mParentHeight;
240         }
241         return parentHeight;
242     }
243 }
244 
isPostSubBufferSupported() const245 EGLint WindowSurfaceGLX::isPostSubBufferSupported() const
246 {
247     UNIMPLEMENTED();
248     return EGL_FALSE;
249 }
250 
getSwapBehavior() const251 EGLint WindowSurfaceGLX::getSwapBehavior() const
252 {
253     return EGL_BUFFER_DESTROYED;
254 }
255 
checkForResize()256 egl::Error WindowSurfaceGLX::checkForResize()
257 {
258     // TODO(cwallez) set up our own error handler to see if the call failed
259     unsigned int newParentWidth, newParentHeight;
260     if (!getWindowDimensions(mParent, &newParentWidth, &newParentHeight))
261     {
262         return egl::EglBadCurrentSurface() << "Failed to retrieve the size of the parent window.";
263     }
264 
265     if (mParentWidth != newParentWidth || mParentHeight != newParentHeight)
266     {
267         mParentWidth  = newParentWidth;
268         mParentHeight = newParentHeight;
269 
270         mGLX.waitGL();
271         XResizeWindow(mDisplay, mWindow, mParentWidth, mParentHeight);
272         mGLX.waitX();
273         XSync(mDisplay, False);
274     }
275 
276     return egl::NoError();
277 }
278 
getDrawable() const279 glx::Drawable WindowSurfaceGLX::getDrawable() const
280 {
281     return mGLXWindow;
282 }
283 
getWindowDimensions(Window window,unsigned int * width,unsigned int * height) const284 bool WindowSurfaceGLX::getWindowDimensions(Window window,
285                                            unsigned int *width,
286                                            unsigned int *height) const
287 {
288     Window root;
289     int x, y;
290     unsigned int border, depth;
291     return XGetGeometry(mDisplay, window, &root, &x, &y, width, height, &border, &depth) != 0;
292 }
293 
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)294 egl::Error WindowSurfaceGLX::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
295 {
296     if (!mGLX.getSyncValuesOML(mGLXWindow, reinterpret_cast<int64_t *>(ust),
297                                reinterpret_cast<int64_t *>(msc), reinterpret_cast<int64_t *>(sbc)))
298     {
299         return egl::EglBadSurface() << "glXGetSyncValuesOML failed.";
300     }
301     return egl::NoError();
302 }
303 
getMscRate(EGLint * numerator,EGLint * denominator)304 egl::Error WindowSurfaceGLX::getMscRate(EGLint *numerator, EGLint *denominator)
305 {
306     if (!mGLX.getMscRateOML(mGLXWindow, reinterpret_cast<int32_t *>(numerator),
307                             reinterpret_cast<int32_t *>(denominator)))
308     {
309         return egl::EglBadSurface() << "glXGetMscRateOML failed.";
310     }
311     if (mGLXDisplay->getRenderer()->getFeatures().clampMscRate.enabled)
312     {
313         // Clamp any refresh rate under 2Hz to 30Hz
314         if (*numerator < *denominator * 2)
315         {
316             *numerator   = 30;
317             *denominator = 1;
318         }
319     }
320     return egl::NoError();
321 }
322 
323 }  // namespace rx
324