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