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