• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::Error(EGL_BAD_NATIVE_WINDOW,
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::Error(EGL_BAD_NATIVE_WINDOW,
101                               "Failed to get the parent window's dimensions.");
102         }
103 
104         // The depth, colormap and visual must match otherwise we get a X error
105         // so we specify the colormap attribute. Also we do not want the window
106         // to be taken into account for input so we specify the event and
107         // do-not-propagate masks to 0 (the defaults). Finally we specify the
108         // border pixel attribute so that we can use a different visual depth
109         // than our parent (seems like X uses that as a condition to render
110         // the subwindow in a different buffer)
111         XSetWindowAttributes attributes;
112         unsigned long attributeMask = CWColormap | CWBorderPixel;
113 
114         colormap = XCreateColormap(mDisplay, mParent, visual, AllocNone);
115         if (!colormap)
116         {
117             XFree(visualInfo);
118             return egl::Error(EGL_BAD_NATIVE_WINDOW,
119                               "Failed to create the Colormap for the child window.");
120         }
121         attributes.colormap     = colormap;
122         attributes.border_pixel = 0;
123 
124         // TODO(cwallez) set up our own error handler to see if the call failed
125         mWindow = XCreateWindow(mDisplay, mParent, 0, 0, mParentWidth, mParentHeight, 0,
126                                 visualInfo->depth, InputOutput, visual, attributeMask, &attributes);
127     }
128 
129     mGLXWindow = mGLX.createWindow(mFBConfig, (mUseChildWindow ? mWindow : mParent), nullptr);
130 
131     if (mUseChildWindow)
132     {
133         XMapWindow(mDisplay, mWindow);
134     }
135 
136     XFlush(mDisplay);
137 
138     if (mUseChildWindow)
139     {
140         XFree(visualInfo);
141         XFreeColormap(mDisplay, colormap);
142     }
143 
144     mGLXDisplay->syncXCommands(true);
145 
146     return egl::NoError();
147 }
148 
makeCurrent(const gl::Context * context)149 egl::Error WindowSurfaceGLX::makeCurrent(const gl::Context *context)
150 {
151     return egl::NoError();
152 }
153 
swap(const gl::Context * context,SurfaceSwapFeedback * feedback)154 egl::Error WindowSurfaceGLX::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
155 {
156     // We need to swap before resizing as some drivers clobber the back buffer
157     // when the window is resized.
158     mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl);
159     mGLX.swapBuffers(mGLXWindow);
160 
161     if (mUseChildWindow)
162     {
163         egl::Error error = checkForResize();
164         if (error.isError())
165         {
166             return error;
167         }
168     }
169 
170     return egl::NoError();
171 }
172 
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)173 egl::Error WindowSurfaceGLX::postSubBuffer(const gl::Context *context,
174                                            EGLint x,
175                                            EGLint y,
176                                            EGLint width,
177                                            EGLint height)
178 {
179     UNIMPLEMENTED();
180     return egl::NoError();
181 }
182 
querySurfacePointerANGLE(EGLint attribute,void ** value)183 egl::Error WindowSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void **value)
184 {
185     UNIMPLEMENTED();
186     return egl::NoError();
187 }
188 
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)189 egl::Error WindowSurfaceGLX::bindTexImage(const gl::Context *context,
190                                           gl::Texture *texture,
191                                           EGLint buffer)
192 {
193     UNIMPLEMENTED();
194     return egl::NoError();
195 }
196 
releaseTexImage(const gl::Context * context,EGLint buffer)197 egl::Error WindowSurfaceGLX::releaseTexImage(const gl::Context *context, EGLint buffer)
198 {
199     UNIMPLEMENTED();
200     return egl::NoError();
201 }
202 
setSwapInterval(const egl::Display * display,EGLint interval)203 void WindowSurfaceGLX::setSwapInterval(const egl::Display *display, EGLint interval)
204 {
205     mSwapControl.targetSwapInterval = interval;
206 }
207 
getWidth() const208 EGLint WindowSurfaceGLX::getWidth() const
209 {
210     if (mUseChildWindow)
211     {
212         // If there's a child window, the size of the window is always the same as the cached
213         // size of its parent.
214         return mParentWidth;
215     }
216     else
217     {
218         unsigned int parentWidth, parentHeight;
219         if (!getWindowDimensions(mParent, &parentWidth, &parentHeight))
220         {
221             return mParentWidth;
222         }
223         return parentWidth;
224     }
225 }
226 
getHeight() const227 EGLint WindowSurfaceGLX::getHeight() const
228 {
229     if (mUseChildWindow)
230     {
231         // If there's a child window, the size of the window is always the same as the cached
232         // size of its parent.
233         return mParentHeight;
234     }
235     else
236     {
237         unsigned int parentWidth, parentHeight;
238         if (!getWindowDimensions(mParent, &parentWidth, &parentHeight))
239         {
240             return mParentHeight;
241         }
242         return parentHeight;
243     }
244 }
245 
isPostSubBufferSupported() const246 EGLint WindowSurfaceGLX::isPostSubBufferSupported() const
247 {
248     UNIMPLEMENTED();
249     return EGL_FALSE;
250 }
251 
getSwapBehavior() const252 EGLint WindowSurfaceGLX::getSwapBehavior() const
253 {
254     return EGL_BUFFER_DESTROYED;
255 }
256 
checkForResize()257 egl::Error WindowSurfaceGLX::checkForResize()
258 {
259     // TODO(cwallez) set up our own error handler to see if the call failed
260     unsigned int newParentWidth, newParentHeight;
261     if (!getWindowDimensions(mParent, &newParentWidth, &newParentHeight))
262     {
263         return egl::Error(EGL_BAD_CURRENT_SURFACE,
264                           "Failed to retrieve the size of the parent window.");
265     }
266 
267     if (mParentWidth != newParentWidth || mParentHeight != newParentHeight)
268     {
269         mParentWidth  = newParentWidth;
270         mParentHeight = newParentHeight;
271 
272         mGLX.waitGL();
273         XResizeWindow(mDisplay, mWindow, mParentWidth, mParentHeight);
274         mGLX.waitX();
275         XSync(mDisplay, False);
276     }
277 
278     return egl::NoError();
279 }
280 
getDrawable() const281 glx::Drawable WindowSurfaceGLX::getDrawable() const
282 {
283     return mGLXWindow;
284 }
285 
getWindowDimensions(Window window,unsigned int * width,unsigned int * height) const286 bool WindowSurfaceGLX::getWindowDimensions(Window window,
287                                            unsigned int *width,
288                                            unsigned int *height) const
289 {
290     Window root;
291     int x, y;
292     unsigned int border, depth;
293     return XGetGeometry(mDisplay, window, &root, &x, &y, width, height, &border, &depth) != 0;
294 }
295 
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)296 egl::Error WindowSurfaceGLX::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
297 {
298     if (!mGLX.getSyncValuesOML(mGLXWindow, reinterpret_cast<int64_t *>(ust),
299                                reinterpret_cast<int64_t *>(msc), reinterpret_cast<int64_t *>(sbc)))
300     {
301         return egl::Error(EGL_BAD_SURFACE, "glXGetSyncValuesOML failed.");
302     }
303     return egl::NoError();
304 }
305 
getMscRate(EGLint * numerator,EGLint * denominator)306 egl::Error WindowSurfaceGLX::getMscRate(EGLint *numerator, EGLint *denominator)
307 {
308     if (!mGLX.getMscRateOML(mGLXWindow, reinterpret_cast<int32_t *>(numerator),
309                             reinterpret_cast<int32_t *>(denominator)))
310     {
311         return egl::Error(EGL_BAD_SURFACE, "glXGetMscRateOML failed.");
312     }
313     if (mGLXDisplay->getRenderer()->getFeatures().clampMscRate.enabled)
314     {
315         // Clamp any refresh rate under 2Hz to 30Hz
316         if (*numerator < *denominator * 2)
317         {
318             *numerator   = 30;
319             *denominator = 1;
320         }
321     }
322     return egl::NoError();
323 }
324 
325 }  // namespace rx
326