1 /*
2  * Copyright © 2010-2011 Linaro Limited
3  * Copyright © 2013 Canonical Ltd
4  *
5  * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
6  *
7  * glmark2 is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * glmark2.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  * Authors:
21  *  Alexandros Frantzis
22  */
23 #include "gl-state-glx.h"
24 #include "log.h"
25 #include "options.h"
26 
27 #include <climits>
28 
29 /******************
30  * Public methods *
31  ******************/
32 
33 bool
init_display(void * native_display,GLVisualConfig & visual_config)34 GLStateGLX::init_display(void* native_display, GLVisualConfig& visual_config)
35 {
36     xdpy_ = reinterpret_cast<Display*>(native_display);
37     requested_visual_config_ = visual_config;
38 
39     if (!lib_.open_from_alternatives({"libGL.so", "libGL.so.1"})) {
40         Log::error("Failed to load libGL\n");
41         return false;
42     }
43 
44     gladLoadGLXUserPtr(xdpy_, DefaultScreen(xdpy_), load_proc, this);
45     return (xdpy_ != 0);
46 }
47 
48 bool
init_surface(void * native_window)49 GLStateGLX::init_surface(void* native_window)
50 {
51     xwin_ = reinterpret_cast<Window>(native_window);
52 
53     return (xwin_ != 0);
54 }
55 
56 bool
init_gl_extensions()57 GLStateGLX::init_gl_extensions()
58 {
59     GLExtensions::MapBuffer = glMapBuffer;
60     GLExtensions::UnmapBuffer = glUnmapBuffer;
61 
62     GLExtensions::GenFramebuffers = glGenFramebuffersEXT;
63     GLExtensions::DeleteFramebuffers = glDeleteFramebuffersEXT;
64     GLExtensions::BindFramebuffer = glBindFramebufferEXT;
65     GLExtensions::FramebufferTexture2D = glFramebufferTexture2DEXT;
66     GLExtensions::FramebufferRenderbuffer = glFramebufferRenderbufferEXT;
67     GLExtensions::CheckFramebufferStatus = glCheckFramebufferStatusEXT;
68 
69     GLExtensions::GenRenderbuffers = glGenRenderbuffersEXT;
70     GLExtensions::DeleteRenderbuffers = glDeleteRenderbuffersEXT;
71     GLExtensions::BindRenderbuffer = glBindRenderbufferEXT;
72     GLExtensions::RenderbufferStorage = glRenderbufferStorageEXT;
73 
74     GLExtensions::GenerateMipmap = glGenerateMipmapEXT;
75 
76     return true;
77 }
78 
79 bool
valid()80 GLStateGLX::valid()
81 {
82     if (!ensure_glx_fbconfig())
83         return false;
84 
85     if (!ensure_glx_context())
86         return false;
87 
88     if (glx_context_ == glXGetCurrentContext())
89         return true;
90 
91     init_extensions();
92 
93     if (!glXMakeCurrent(xdpy_, xwin_, glx_context_)) {
94         Log::error("glXMakeCurrent failed\n");
95         return false;
96     }
97 
98     if (gladLoadGLUserPtr(load_proc, this) == 0) {
99         Log::error("Failed to load GL entry points\n");
100         return false;
101     }
102 
103     if (!init_gl_extensions())
104         return false;
105 
106     unsigned int desired_swap(0);
107     unsigned int actual_swap(-1);
108     if (glXSwapIntervalEXT) {
109         glXSwapIntervalEXT(xdpy_, xwin_, desired_swap);
110         glXQueryDrawable(xdpy_, xwin_, GLX_SWAP_INTERVAL_EXT, &actual_swap);
111         if (actual_swap == desired_swap)
112             return true;
113     }
114 
115     if (glXSwapIntervalMESA) {
116         glXSwapIntervalMESA(desired_swap);
117         actual_swap = glXGetSwapIntervalMESA();
118         if (actual_swap == desired_swap)
119             return true;
120     }
121 
122     Log::info("** Failed to set swap interval. Results may be bounded above by refresh rate.\n");
123 
124     return true;
125 }
126 
127 
128 bool
reset()129 GLStateGLX::reset()
130 {
131     if (glx_context_)
132     {
133         glXDestroyContext(xdpy_, glx_context_);
134         glx_context_ = 0;
135     }
136 
137     return true;
138 }
139 
140 void
swap()141 GLStateGLX::swap()
142 {
143     glXSwapBuffers(xdpy_, xwin_);
144 }
145 
146 bool
gotNativeConfig(intptr_t & vid)147 GLStateGLX::gotNativeConfig(intptr_t& vid)
148 {
149     if (!ensure_glx_fbconfig())
150         return false;
151 
152     int native_id;
153     if (glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_VISUAL_ID, &native_id) != Success)
154     {
155         Log::debug("Failed to get native visual id for GLXFBConfig 0x%x\n", glx_fbconfig_);
156         return false;
157     }
158 
159     vid = native_id;
160     return true;
161 }
162 
163 void
getVisualConfig(GLVisualConfig & vc)164 GLStateGLX::getVisualConfig(GLVisualConfig& vc)
165 {
166     if (!ensure_glx_fbconfig())
167         return;
168 
169     get_glvisualconfig_glx(glx_fbconfig_, vc);
170 }
171 
172 /*******************
173  * Private methods *
174  *******************/
175 
176 bool
check_glx_version()177 GLStateGLX::check_glx_version()
178 {
179     int glx_major, glx_minor;
180 
181     if (!glXQueryVersion(xdpy_, &glx_major, &glx_minor ) ||
182         (glx_major == 1  && glx_minor < 3) || glx_major < 1)
183     {
184         Log::error("GLX version >= 1.3 is required\n");
185         return false;
186     }
187 
188     return true;
189 }
190 
191 void
init_extensions()192 GLStateGLX::init_extensions()
193 {
194     /*
195      * Parse the extensions we care about from the extension string.
196      * Don't even bother to get function pointers until we know the
197      * extension is present.
198      */
199     std::string extString;
200     const char* exts = glXQueryExtensionsString(xdpy_, 0);
201     if (exts) {
202         extString = exts;
203     }
204 
205     /*
206      * GLX_EXT_swap_control or GL_MESA_swap_control. Note that
207      * GLX_SGI_swap_control is not enough because it doesn't allow 0 as a valid
208      * value (i.e. you can't turn off VSync).
209      */
210     if (!glXSwapIntervalEXT && !glXSwapIntervalMESA) {
211         Log::info("** GLX does not support GLX_EXT_swap_control or GLX_MESA_swap_control!\n");
212     }
213 }
214 
215 bool
ensure_glx_fbconfig()216 GLStateGLX::ensure_glx_fbconfig()
217 {
218     static int attribs[] = {
219         GLX_X_RENDERABLE, True,
220         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
221         GLX_RENDER_TYPE, GLX_RGBA_BIT,
222         GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
223         GLX_RED_SIZE, requested_visual_config_.red,
224         GLX_GREEN_SIZE, requested_visual_config_.green,
225         GLX_BLUE_SIZE, requested_visual_config_.blue,
226         GLX_ALPHA_SIZE, requested_visual_config_.alpha,
227         GLX_DEPTH_SIZE, requested_visual_config_.depth,
228         GLX_STENCIL_SIZE, requested_visual_config_.stencil,
229         GLX_BUFFER_SIZE, requested_visual_config_.buffer,
230         GLX_DOUBLEBUFFER, True,
231         None
232     };
233     int num_configs;
234 
235     if (glx_fbconfig_)
236         return true;
237 
238     if (!check_glx_version())
239         return false;
240 
241     GLXFBConfig *fbc = glXChooseFBConfig(xdpy_, DefaultScreen(xdpy_),
242                                          attribs, &num_configs);
243     if (!fbc) {
244         Log::error("glXChooseFBConfig() failed\n");
245         return false;
246     }
247 
248     std::vector<GLXFBConfig> configs(fbc, fbc + num_configs);
249 
250     Log::debug("Found %d matching FB configs.\n", num_configs);
251 
252     /* Select the best matching config */
253     glx_fbconfig_ = select_best_config(configs);
254 
255     XFree(fbc);
256 
257     if (Options::show_debug) {
258         int buf, red, green, blue, alpha, depth, stencil, id, native_id;
259         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_FBCONFIG_ID, &id);
260         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_VISUAL_ID, &native_id);
261         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_BUFFER_SIZE, &buf);
262         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_RED_SIZE, &red);
263         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_GREEN_SIZE, &green);
264         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_BLUE_SIZE, &blue);
265         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_ALPHA_SIZE, &alpha);
266         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_DEPTH_SIZE, &depth);
267         glXGetFBConfigAttrib(xdpy_, glx_fbconfig_, GLX_STENCIL_SIZE, &stencil);
268         Log::debug("GLX chosen config ID: 0x%x Native Visual ID: 0x%x\n"
269                    "  Buffer: %d bits\n"
270                    "     Red: %d bits\n"
271                    "   Green: %d bits\n"
272                    "    Blue: %d bits\n"
273                    "   Alpha: %d bits\n"
274                    "   Depth: %d bits\n"
275                    " Stencil: %d bits\n",
276                    id, native_id,
277                    buf, red, green, blue, alpha, depth, stencil);
278     }
279 
280 
281     return true;
282 }
283 
284 bool
ensure_glx_context()285 GLStateGLX::ensure_glx_context()
286 {
287     if (glx_context_)
288         return true;
289 
290     if (!ensure_glx_fbconfig())
291         return false;
292 
293     glx_context_ = glXCreateNewContext(xdpy_, glx_fbconfig_, GLX_RGBA_TYPE,
294                                        0, True);
295     if (!glx_context_) {
296         Log::error("glXCreateNewContext failed\n");
297         return false;
298     }
299 
300 
301     return true;
302 }
303 
304 void
get_glvisualconfig_glx(const GLXFBConfig config,GLVisualConfig & visual_config)305 GLStateGLX::get_glvisualconfig_glx(const GLXFBConfig config, GLVisualConfig &visual_config)
306 {
307     glXGetFBConfigAttrib(xdpy_, config, GLX_BUFFER_SIZE, &visual_config.buffer);
308     glXGetFBConfigAttrib(xdpy_, config, GLX_RED_SIZE, &visual_config.red);
309     glXGetFBConfigAttrib(xdpy_, config, GLX_GREEN_SIZE, &visual_config.green);
310     glXGetFBConfigAttrib(xdpy_, config, GLX_BLUE_SIZE, &visual_config.blue);
311     glXGetFBConfigAttrib(xdpy_, config, GLX_ALPHA_SIZE, &visual_config.alpha);
312     glXGetFBConfigAttrib(xdpy_, config, GLX_DEPTH_SIZE, &visual_config.depth);
313     glXGetFBConfigAttrib(xdpy_, config, GLX_STENCIL_SIZE, &visual_config.stencil);
314 }
315 
316 GLXFBConfig
select_best_config(std::vector<GLXFBConfig> configs)317 GLStateGLX::select_best_config(std::vector<GLXFBConfig> configs)
318 {
319     int best_score(INT_MIN);
320     GLXFBConfig best_config(0);
321 
322     /*
323      * Go through all the configs and choose the one with the best score,
324      * i.e., the one better matching the requested config.
325      */
326     for (std::vector<GLXFBConfig>::const_iterator iter = configs.begin();
327          iter != configs.end();
328          iter++)
329     {
330         const GLXFBConfig config(*iter);
331         GLVisualConfig vc;
332         int score;
333 
334         get_glvisualconfig_glx(config, vc);
335 
336         score = vc.match_score(requested_visual_config_);
337 
338         if (score > best_score) {
339             best_score = score;
340             best_config = config;
341         }
342     }
343 
344     return best_config;
345 }
346 
347 GLADapiproc
load_proc(void * userptr,const char * name)348 GLStateGLX::load_proc(void *userptr, const char* name)
349 {
350     if (glXGetProcAddress) {
351         const GLubyte* bytes = reinterpret_cast<const GLubyte*>(name);
352         GLADapiproc sym = glXGetProcAddress(bytes);
353         if (sym) {
354             return sym;
355         }
356     }
357 
358     GLStateGLX* state = reinterpret_cast<GLStateGLX*>(userptr);
359     return reinterpret_cast<GLADapiproc>(state->lib_.load(name));
360 }
361