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