blob: 8004add4d71b9708b5105612abf3f8e32fab65e2 [file] [log] [blame]
José Fonseca0396fa02015-01-05 16:20:08 +00001/**************************************************************************
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 "glprofile.hpp"
28
José Fonsecaa44df7a2015-01-05 19:59:59 +000029#include <assert.h>
30
31#include "os.hpp"
32#include "glproc.hpp"
33
José Fonseca0396fa02015-01-05 16:20:08 +000034
35namespace glprofile {
36
37
Jose Fonseca0e1d41b2015-03-13 17:17:47 +000038bool
39Profile::matches(const Profile expected) const
40{
41 if (api != expected.api) {
42 return false;
43 }
44
45 if (!versionGreaterOrEqual(expected.major, expected.minor)) {
46 return false;
47 }
48
49 /*
50 * GLX_ARB_create_context/WGL_ARB_create_context specs state that
51 *
52 * "If version 3.1 is requested, the context returned may implement
53 * any of the following versions:
54 *
55 * * Version 3.1. The GL_ARB_compatibility extension may or may not
56 * be implemented, as determined by the implementation.
57 *
58 * * The core profile of version 3.2 or greater."
59 */
60 if (core != expected.core &&
61 (expected.major != 3 || expected.minor != 1)) {
62 return false;
63 }
64
65 /*
66 * Only check forward-compatible flag prior to 3.2 contexts.
67 *
68 * Note that on MacOSX all 3.2+ context must be forward-compatible.
69 */
70#ifndef __APPLE__
71 if (forwardCompatible > expected.forwardCompatible) {
72 return false;
73 }
74#endif
75
76 return true;
77}
78
79
José Fonseca0396fa02015-01-05 16:20:08 +000080std::ostream &
81operator << (std::ostream &os, const Profile & profile) {
82 os << "OpenGL";
83 if (profile.api == API_GLES) {
84 os << " ES";
85 }
86 os << " " << profile.major << "." << profile.minor;
Jose Fonseca56c419c2015-03-13 16:07:33 +000087 if (profile.api == API_GL) {
88 if (profile.versionGreaterOrEqual(3, 2)) {
89 os << " " << (profile.core ? "core" : "compat");
90 }
91 if (profile.forwardCompatible) {
92 os << " forward-compatible";
93 }
José Fonseca0396fa02015-01-05 16:20:08 +000094 }
95 return os;
96}
97
98
José Fonsecaa44df7a2015-01-05 19:59:59 +000099static inline bool
100isDigit(char c) {
101 return c >= '0' && c <= '9';
102}
103
104
105static unsigned
106parseNumber(const char * & p)
107{
108 unsigned n = 0;
109 char c = *p;
110 while (isDigit(c)) {
111 unsigned digit = c - '0';
112 n *= 10;
113 n += digit;
114 ++p;
115 c = *p;
116 }
117 return n;
118}
119
120
121/*
122 * Parse API and version numbers from a GL_VERSION string.
123 *
124 * OpenGL 2.1 specification states that GL_VERSION string is laid out as
125 *
126 * <version number><space><vendor-specific information>
127 *
128 * Where <version number> is either of the form <major number>.<minor number>
129 * or <major number>.<minor number>.<release number>, where the numbers all
130 * have one or more digits. The release number and vendor specific
131 * information are optional.
132 *
133 * OpenGL ES 1.x specification states that GL_VERSION is laid out as
134 *
135 * "OpenGL ES-XX 1.x" XX={CM,CL}
136 *
137 * OpenGL ES 2 and 3 specifications state that GL_VERSION is laid out as
138 *
139 * "OpenGL ES N.M vendor-specific information"
140 */
141static Profile
142parseVersion(const char *version)
143{
144 Profile profile(API_GL, 0, 0, false);
145
146 const char *p = version;
147
148 if (p[0] == 'O' &&
149 p[1] == 'p' &&
150 p[2] == 'e' &&
151 p[3] == 'n' &&
152 p[4] == 'G' &&
153 p[5] == 'L' &&
154 p[6] == ' ' &&
155 p[7] == 'E' &&
156 p[8] == 'S') {
157 p += 9;
158
159 profile.api = API_GLES;
160
161 // skip `-{CM,CL}`
162 if (*p == '-') {
163 ++p;
164 while (*p != ' ') {
165 if (*p == '\0') {
166 goto malformed;
167 }
168 ++p;
169 }
170 }
171
172 // skip white-space
173 while (*p == ' ') {
174 if (*p == '\0') {
175 goto malformed;
176 }
177 ++p;
178 }
179 }
180
181 if (!isDigit(*p)) {
182 goto malformed;
183 }
184
185 profile.major = parseNumber(p);
186 if (*p++ == '.' &&
187 isDigit(*p)) {
188 profile.minor = parseNumber(p);
189 } else {
190 goto malformed;
191 }
192
193 return profile;
194
195malformed:
196 os::log("warning: malformed GL_VERSION (\"%s\")\n", version);
197 return profile;
198}
199
200
201/*
202 * Get the profile of the current context.
203 */
204Profile
205getCurrentContextProfile(void)
206{
207 Profile profile(API_GL, 0, 0, false);
208
209 assert(parseVersion("3.0 Mesa 10.3.2") == Profile(API_GL, 3, 0));
210 assert(parseVersion("3.3 (Core Profile) Mesa 10.3.2") == Profile(API_GL, 3, 3));
211 assert(parseVersion("4.4.0 NVIDIA 331.89") == Profile(API_GL, 4, 4));
212 assert(parseVersion("OpenGL ES 3.0 Mesa 10.3.2") == Profile(API_GLES, 3, 0));
213
214 const char *version = (const char *)_glGetString(GL_VERSION);
215 if (!version) {
216 os::log("warning: null GL_VERSION\n");
217 return profile;
218 }
219
220 // Parse the version string.
221 profile = parseVersion(version);
222
223 if (profile.major >= 3) {
224 /*
225 * From OpenGL and OpenGL ES version 3 onwards, the GL version may also
226 * be queried via GL_MAJOR VERSION and GL_MINOR_VERSION, which should
227 * match the major number and minor number in GL_VERSION string, so use
228 * it to check we parsed the versions correcly.
229 */
230
231 GLint majorVersion = 0;
232 _glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
233 GLint minorVersion = 0;
234 _glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
235
236 if (majorVersion != profile.major ||
237 minorVersion != profile.minor) {
238 os::log("apitrace: warning: OpenGL context version mismatch (GL_VERSION=\"%s\", but GL_MAJOR/MINOR_VERSION=%u.%u)\n",
239 version, majorVersion, minorVersion);
240 }
241 }
242
Jose Fonseca56c419c2015-03-13 16:07:33 +0000243 if (profile.api == API_GL) {
244 if (profile.versionGreaterOrEqual(3, 0)) {
245 GLint contextFlags = 0;
246 _glGetIntegerv(GL_CONTEXT_FLAGS, &contextFlags);
247 if (contextFlags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
248 profile.forwardCompatible = true;
249 }
250 }
251
252 if (profile.versionGreaterOrEqual(3, 2)) {
253 GLint profileMask = 0;
254 _glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask);
255 if (profileMask & GL_CONTEXT_CORE_PROFILE_BIT) {
256 profile.core = true;
257 }
José Fonsecaa44df7a2015-01-05 19:59:59 +0000258 }
259 }
260
261 return profile;
262
263}
264
265
José Fonseca440d8aa2015-01-23 15:32:23 +0000266void
267Extensions::getCurrentContextExtensions(const Profile & profile)
268{
269 assert(strings.empty());
270 if (profile.major >= 3) {
271 // Use glGetStringi
272 GLint num_strings = 0;
273 _glGetIntegerv(GL_NUM_EXTENSIONS, &num_strings);
274 assert(num_strings);
275 for (int i = 0; i < num_strings; ++i) {
276 const char *extension = reinterpret_cast<const char *>(_glGetStringi(GL_EXTENSIONS, i));
277 assert(extension);
278 if (extension) {
279 strings.insert(extension);
280 }
281 }
282 } else {
283 // Use glGetString
284 const char *begin = reinterpret_cast<const char *>(_glGetString(GL_EXTENSIONS));
285 assert(begin);
286 if (begin) {
287 do {
288 const char *end = begin;
289 char c = *end;
290 while (c != '\0' && c != ' ') {
291 ++end;
292 c = *end;
293 }
294 if (end != begin) {
295 strings.insert(std::string(begin, end));
296 }
297 if (c == '\0') {
298 break;
299 }
300 begin = end + 1;
301 } while(true);
302 }
303 }
304}
305
306
307bool
308Extensions::has(const char *string) const
309{
310 return strings.find(string) != strings.end();
311}
312
313
José Fonseca0396fa02015-01-05 16:20:08 +0000314} /* namespace glprofile */