1 /**************************************************************************
2  *
3  * Copyright 2014 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25 
26 
27 #include "glfeatures.hpp"
28 
29 #include <assert.h>
30 
31 #include <sstream>
32 
33 #include "os.hpp"
34 #include "glproc.hpp"
35 
36 
37 namespace glfeatures {
38 
39 
40 bool
matches(const Profile expected) const41 Profile::matches(const Profile expected) const
42 {
43     if (api != expected.api) {
44         return false;
45     }
46 
47     if (!versionGreaterOrEqual(expected.major, expected.minor)) {
48         return false;
49     }
50 
51     /*
52      * GLX_ARB_create_context/WGL_ARB_create_context specs state that
53      *
54      *   "If version 3.1 is requested, the context returned may implement
55      *   any of the following versions:
56      *
57      *     * Version 3.1. The GL_ARB_compatibility extension may or may not
58      *       be implemented, as determined by the implementation.
59      *
60      *     * The core profile of version 3.2 or greater."
61      */
62     if (core != expected.core &&
63         (expected.major != 3 || expected.minor != 1)) {
64         return false;
65     }
66 
67     /*
68      * Only check forward-compatible flag prior to 3.2 contexts.
69      *
70      * Note that on MacOSX all 3.2+ context must be forward-compatible.
71      */
72 #ifndef __APPLE__
73     if (forwardCompatible > expected.forwardCompatible) {
74         return false;
75     }
76 #endif
77 
78     return true;
79 }
80 
81 
82 std::string
str(void) const83 Profile::str(void) const
84 {
85     std::stringstream ss;
86     ss << *this;
87     return ss.str();
88 }
89 
90 
91 std::ostream &
operator <<(std::ostream & os,const Profile & profile)92 operator << (std::ostream &os, const Profile & profile) {
93     os << "OpenGL";
94     if (profile.api == API_GLES) {
95         os << " ES";
96     }
97     os << " " << profile.major << "." << profile.minor;
98     if (profile.api == API_GL) {
99         if (profile.versionGreaterOrEqual(3, 2)) {
100             os << " " << (profile.core ? "core" : "compat");
101         }
102         if (profile.forwardCompatible) {
103             os << " forward-compatible";
104         }
105     }
106     return os;
107 }
108 
109 
110 static inline bool
isDigit(char c)111 isDigit(char c) {
112     return c >= '0' && c <= '9';
113 }
114 
115 
116 static unsigned
parseNumber(const char * & p)117 parseNumber(const char * & p)
118 {
119     unsigned n = 0;
120     char c = *p;
121     while (isDigit(c)) {
122         unsigned digit = c - '0';
123         n *= 10;
124         n += digit;
125         ++p;
126         c = *p;
127     }
128     return n;
129 }
130 
131 
132 /*
133  * Parse API and version numbers from a GL_VERSION string.
134  *
135  * OpenGL 2.1 specification states that GL_VERSION string is laid out as
136  *
137  *     <version number><space><vendor-specific information>
138  *
139  *   Where <version number> is either of the form <major number>.<minor number>
140  *   or <major number>.<minor number>.<release number>, where the numbers all
141  *   have one or more digits.  The release number and vendor specific
142  *   information are optional.
143  *
144  * OpenGL ES 1.x specification states that GL_VERSION is laid out as
145  *
146  *   "OpenGL ES-XX 1.x" XX={CM,CL}
147  *
148  * OpenGL ES 2 and 3 specifications state that GL_VERSION is laid out as
149  *
150  *   "OpenGL ES N.M vendor-specific information"
151  */
152 static Profile
parseVersion(const char * version)153 parseVersion(const char *version)
154 {
155     Profile profile(API_GL, 0, 0, false);
156 
157     const char *p = version;
158 
159     if (p[0] == 'O' &&
160         p[1] == 'p' &&
161         p[2] == 'e' &&
162         p[3] == 'n' &&
163         p[4] == 'G' &&
164         p[5] == 'L' &&
165         p[6] == ' ' &&
166         p[7] == 'E' &&
167         p[8] == 'S') {
168         p += 9;
169 
170         profile.api = API_GLES;
171 
172         // skip `-{CM,CL}`
173         if (*p == '-') {
174             ++p;
175             while (*p != ' ') {
176                 if (*p == '\0') {
177                     goto malformed;
178                 }
179                 ++p;
180             }
181         }
182 
183         // skip white-space
184         while (*p == ' ') {
185             if (*p == '\0') {
186                 goto malformed;
187             }
188             ++p;
189         }
190     }
191 
192     if (!isDigit(*p)) {
193         goto malformed;
194     }
195 
196     profile.major = parseNumber(p);
197     if (*p++ == '.' &&
198         isDigit(*p)) {
199         profile.minor = parseNumber(p);
200     } else {
201         goto malformed;
202     }
203 
204     return profile;
205 
206 malformed:
207     os::log("warning: malformed GL_VERSION (\"%s\")\n", version);
208     return profile;
209 }
210 
211 
212 /*
213  * Get the profile of the current context.
214  */
215 Profile
getCurrentContextProfile(void)216 getCurrentContextProfile(void)
217 {
218     Profile profile(API_GL, 0, 0, false);
219 
220     assert(parseVersion("3.0 Mesa 10.3.2") == Profile(API_GL, 3, 0));
221     assert(parseVersion("3.3 (Core Profile) Mesa 10.3.2") == Profile(API_GL, 3, 3));
222     assert(parseVersion("4.4.0 NVIDIA 331.89") == Profile(API_GL, 4, 4));
223     assert(parseVersion("OpenGL ES 3.0 Mesa 10.3.2") == Profile(API_GLES, 3, 0));
224 
225     const char *version = (const char *)_glGetString(GL_VERSION);
226     if (!version) {
227         os::log("apitrace: warning: got null GL_VERSION\n");
228         return profile;
229     }
230 
231     // Parse the version string.
232     profile = parseVersion(version);
233 
234     if (profile.major >= 3) {
235         /*
236          * From OpenGL and OpenGL ES version 3 onwards, the GL version may also
237          * be queried via GL_MAJOR VERSION and GL_MINOR_VERSION, which should
238          * match the major number and minor number in GL_VERSION string, so use
239          * it to check we parsed the versions correcly.
240          */
241 
242         GLint majorVersion = 0;
243         _glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
244         GLint minorVersion = 0;
245         _glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
246 
247         if (majorVersion != profile.major ||
248             minorVersion != profile.minor) {
249             os::log("apitrace: warning: OpenGL context version mismatch (GL_VERSION=\"%s\", but GL_MAJOR/MINOR_VERSION=%u.%u)\n",
250                     version, majorVersion, minorVersion);
251         }
252     }
253 
254     if (profile.api == API_GL) {
255         if (profile.versionGreaterOrEqual(3, 0)) {
256             GLint contextFlags = 0;
257             _glGetIntegerv(GL_CONTEXT_FLAGS, &contextFlags);
258             if (contextFlags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
259                 profile.forwardCompatible = true;
260             }
261         }
262 
263         if (profile.versionGreaterOrEqual(3, 2)) {
264             GLint profileMask = 0;
265             _glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask);
266             if (profileMask & GL_CONTEXT_CORE_PROFILE_BIT) {
267                 profile.core = true;
268             }
269         }
270     }
271 
272     return profile;
273 
274 }
275 
276 
277 void
getCurrentContextExtensions(const Profile & profile)278 Extensions::getCurrentContextExtensions(const Profile & profile)
279 {
280     assert(strings.empty());
281     if (profile.major >= 3) {
282         // Use glGetStringi
283         GLint num_strings = 0;
284         _glGetIntegerv(GL_NUM_EXTENSIONS, &num_strings);
285         assert(num_strings);
286         for (int i = 0; i < num_strings; ++i) {
287             const char *extension = reinterpret_cast<const char *>(_glGetStringi(GL_EXTENSIONS, i));
288             assert(extension);
289             if (extension) {
290                 strings.insert(extension);
291             }
292         }
293     } else {
294         // Use glGetString
295         const char *begin = reinterpret_cast<const char *>(_glGetString(GL_EXTENSIONS));
296         assert(begin);
297         if (begin) {
298             do {
299                 const char *end = begin;
300                 char c = *end;
301                 while (c != '\0' && c != ' ') {
302                     ++end;
303                     c = *end;
304                 }
305                 if (end != begin) {
306                     strings.insert(std::string(begin, end));
307                 }
308                 if (c == '\0') {
309                     break;
310                 }
311                 begin = end + 1;
312             } while(true);
313         }
314     }
315 }
316 
317 
318 bool
has(const char * string) const319 Extensions::has(const char *string) const
320 {
321     return strings.find(string) != strings.end();
322 }
323 
324 
Features(void)325 Features::Features(void)
326 {
327     memset(this, 0, sizeof *this);
328 }
329 
330 
331 void
load(const Profile & profile,const Extensions & ext)332 Features::load(const Profile & profile, const Extensions & ext)
333 {
334     ES = profile.es();
335     core = profile.core;
336 
337     ARB_draw_buffers = !ES;
338 
339     // Check extensions we use.
340     ARB_sampler_objects = ext.has("GL_ARB_sampler_objects");
341     ARB_get_program_binary = ext.has("GL_ARB_get_program_binary");
342     KHR_debug = !ES && ext.has("GL_KHR_debug");
343     EXT_debug_label = ext.has("GL_EXT_debug_label");
344     ARB_direct_state_access = ext.has("GL_ARB_direct_state_access");
345     ARB_shader_image_load_store = ext.has("GL_ARB_shader_image_load_store");
346     ARB_shader_storage_buffer_object = ext.has("GL_ARB_shader_storage_buffer_object");
347     ARB_program_interface_query = ext.has("GL_ARB_program_interface_query");
348 
349     NV_read_depth_stencil = ES && ext.has("GL_NV_read_depth_stencil");
350 
351     if (profile.desktop()) {
352         ARB_color_buffer_float = profile.versionGreaterOrEqual(3, 0) ||
353                                  ext.has("GL_ARB_color_buffer_float");
354 
355         // GL_EXT_texture3D uses different entrypoints
356         texture_3d = profile.versionGreaterOrEqual(1, 2);
357 
358         pixel_buffer_object = profile.versionGreaterOrEqual(2, 1) ||
359                               ext.has("GL_ARB_pixel_buffer_object") ||
360                               ext.has("GL_EXT_pixel_buffer_object");
361 
362         read_buffer = 1;
363 
364         // GL_EXT_framebuffer_object requires different entry points
365         framebuffer_object = profile.versionGreaterOrEqual(3, 0) ||
366                              ext.has("GL_ARB_framebuffer_object");
367 
368         read_framebuffer_object = framebuffer_object;
369 
370         query_buffer_object = profile.versionGreaterOrEqual(4, 4) ||
371                               ext.has("GL_ARB_query_buffer_object") ||
372                               ext.has("GL_AMD_query_buffer_object");
373 
374         primitive_restart = profile.versionGreaterOrEqual(3, 1) ||
375                             ext.has("GL_NV_primitive_restart");
376 
377         unpack_subimage = 1;
378     } else {
379         texture_3d = 1;
380 
381         pixel_buffer_object = profile.versionGreaterOrEqual(3, 0) ||
382                               ext.has("GL_NV_pixel_buffer_object");
383 
384         // GL_EXT_multiview_draw_buffers requires different entry points
385         // GL_NV_read_buffer requires different entry points
386         read_buffer = 0;
387 
388         // GL_OES_framebuffer_object requires different entry points
389         framebuffer_object = profile.versionGreaterOrEqual(2, 0);
390 
391         // GL_ANGLE_framebuffer_blit requires different entry points
392         // GL_APPLE_framebuffer_multisample requires different entry points
393         // GL_NV_framebuffer_blit requires different entry points
394         read_framebuffer_object = 0;
395 
396         query_buffer_object = 0;
397 
398         primitive_restart = 0;
399 
400         unpack_subimage = ext.has("GL_EXT_unpack_subimage");
401     }
402 }
403 
404 
405 void
load(void)406 Features::load(void)
407 {
408     glfeatures::Profile profile = glfeatures::getCurrentContextProfile();
409     glfeatures::Extensions exts;
410     exts.getCurrentContextExtensions(profile);
411     load(profile, exts);
412 }
413 
414 
415 } /* namespace glfeatures */
416