[waffle] [PATCH 1/2] wflinfo: Make wflinfo understand OpenGL 3.1 profiles

Chad Versace chad.versace at linux.intel.com
Thu May 8 21:59:25 PDT 2014


Strictly speaking, an OpenGL 3.1 context has no profile. But if the user
calls wflinfo like below...
    wflinfo --api=gl --version=3.1
    wflinfo --api=gl --version=3.1 --profile=none
    wflinfo --api=gl --version=3.1 --profile=core
    wflinfo --api=gl --version=3.1 --profile=compat
then we know what they way. Make wflinfo do what the user wants.

Likewise, if the user provided a profile but no version, like this...
    wflinfo --api=gl --profile=core
    wflinfo --api=gl --profile=compat
then wflinfo iterated over OpenGL context versions from 4.4 to 3.2 in
search of the given profile. Because wflinfo now understands 3.1
profiles, it iterates down to 3.1.

Because OpenGL 3.1 contexts don't have true profiles, this patch adds
gobs of special case code to pretend they do.

Signed-off-by: Chad Versace <chad.versace at linux.intel.com>
---

v2: Prefix each line of the multiline warning message with "Wflinfo warn:" so
as to preserve the parseability of wflinfo's output. Requested by Jordan.

 src/utils/wflinfo.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 244 insertions(+), 14 deletions(-)

diff --git a/src/utils/wflinfo.c b/src/utils/wflinfo.c
index c4f5e80..e45c18f 100644
--- a/src/utils/wflinfo.c
+++ b/src/utils/wflinfo.c
@@ -117,6 +117,12 @@ static const struct option get_opts[] = {
     { 0 },
 };
 
+static bool
+strneq(const char *a, const char *b, size_t n)
+{
+    return strncmp(a, b, n) == 0;
+}
+
 static void __attribute__((noreturn))
 error_printf(const char *module, const char *fmt, ...)
 {
@@ -200,6 +206,12 @@ enum {
 #define WINDOW_WIDTH  320
 #define WINDOW_HEIGHT 240
 
+#define GL_MAJOR_VERSION                  0x821B
+#define GL_MINOR_VERSION                  0x821C
+#define GL_CONTEXT_PROFILE_MASK           0x9126
+#define GL_CONTEXT_CORE_PROFILE_BIT       0x00000001
+#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
+
 static GLenum (*glGetError)(void);
 static void (*glGetIntegerv)(GLenum pname, GLint *params);
 static const GLubyte * (*glGetString)(GLenum name);
@@ -705,14 +717,202 @@ fail:
     return false;
 }
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+/// @brief Return 10 * version of the current OpenGL context.
+static int
+gl_get_version(void)
+{
+    GLint major_version = 0;
+    GLint minor_version = 0;
+
+    glGetIntegerv(GL_MAJOR_VERSION, &major_version);
+    if (glGetError()) {
+        error_printf("Wflinfo", "glGetIntegerv(GL_MAJOR_VERSION) failed");
+    }
+
+    glGetIntegerv(GL_MINOR_VERSION, &minor_version);
+    if (glGetError()) {
+        error_printf("Wflinfo", "glGetIntegerv(GL_MINOR_VERSION) failed");
+    }
+    return 10 * major_version + minor_version;
+}
+
+/// @brief Check if current context has an extension using glGetString().
+static bool
+gl_has_extension_GetString(const char *name)
+{
+    const size_t buf_len = 4096;
+    char exts[buf_len];
+
+    const uint8_t *exts_orig = glGetString(GL_EXTENSIONS);
+    if (glGetError()) {
+        error_printf("Wflinfo", "glGetInteger(GL_EXTENSIONS) failed");
+    }
+
+    memcpy(exts, exts_orig, buf_len);
+    exts[buf_len - 1] = 0;
+
+    char *ext = strtok(exts, " ");
+    do {
+        if (strneq(ext, name, buf_len)) {
+            return true;
+        }
+        ext = strtok(NULL, " ");
+    } while (ext);
+
+    return false;
+}
+
+/// @brief Check if current context has an extension using glGetStringi().
+static bool
+gl_has_extension_GetStringi(const char *name)
+{
+    const size_t max_ext_len = 128;
+    uint32_t num_exts = 0;
+
+    glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts);
+    if (glGetError()) {
+        error_printf("Wflinfo", "glGetIntegerv(GL_NUM_EXTENSIONS) failed");
+    }
+
+    for (int i = 0; i < num_exts; i++) {
+        const uint8_t *ext = glGetStringi(GL_EXTENSIONS, i);
+        if (!ext || glGetError()) {
+            error_printf("Wflinfo", "glGetStringi(GL_EXTENSIONS) failed");
+        } else if (strneq((const char*) ext, name, max_ext_len)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/// @brief Check if current context has an extension.
+static bool
+gl_has_extension(const char *name)
+{
+    if (gl_get_version() >= 30) {
+        return gl_has_extension_GetStringi(name);
+    } else {
+        return gl_has_extension_GetString(name);
+    }
+}
+
+/// @brief Get the profile of a desktop OpenGL context.
+///
+/// Return one of WAFFLE_CONTEXT_CORE_PROFILE,
+/// WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, or WAFFLE_NONE.
+///
+/// Even though an OpenGL 3.1 context strictly has no profile, according to
+/// this function a 3.1 context belongs to the core profile if and only if it
+/// lacks the GL_ARB_compatibility extension.
+///
+/// According to this function, a context has no profile if and only if its
+/// version is 3.0 or lower.
+static enum waffle_enum
+gl_get_profile(void)
+{
+    int version = gl_get_version();
+
+    if (version >= 32) {
+        uint32_t profile_mask = 0;
+        glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile_mask);
+        if (glGetError()) {
+            error_printf("Wflinfo", "glGetIntegerv(GL_CONTEXT_PROFILE_MASK) "
+                        "failed");
+        } else if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT) {
+            return WAFFLE_CONTEXT_CORE_PROFILE;
+        } else if (profile_mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
+            return WAFFLE_CONTEXT_COMPATIBILITY_PROFILE;
+        } else {
+            error_printf("Wflinfo", "glGetIntegerv(GL_CONTEXT_PROFILE_MASK) "
+                         "return a mask with no profile bit: 0x%x",
+                         profile_mask);
+        }
+    } else if (version == 31) {
+        if (gl_has_extension("GL_ARB_compatibility")) {
+            return WAFFLE_CONTEXT_CORE_PROFILE;
+        } else {
+            return WAFFLE_CONTEXT_COMPATIBILITY_PROFILE;
+        }
+    } else {
+        return WAFFLE_NONE;
+    }
+}
 
+/// @brief Create an OpenGL >= 3.1 context.
+///
+/// If the requested profile is WAFFLE_NONE or WAFFLE_DONT_CARE and context
+/// creation succeeds, then return true.
+///
+/// If a specific profile of OpenGL 3.1 is requested, then this function tries
+/// to honor the intent of that request even though, strictly speaking, an
+/// OpenGL 3.1 context has no profile.  (See gl_get_profile() for a description
+/// of how wflinfo determines the profile of a context). If context creation
+/// succeeds but its profile is incorrect, then return false.
+///
+/// On failure, @a out_ctx and @out_config remain unmodified.
+///
 static bool
+wflinfo_try_create_context_gl31(struct waffle_display *dpy,
+                                struct wflinfo_config_attrs attrs,
+                                struct waffle_context **out_ctx,
+                                struct waffle_config **out_config,
+                                bool exit_if_ctx_creation_fails)
+{
+    struct waffle_config *config = NULL;
+    struct waffle_context *ctx = NULL;
+    bool ok;
+
+    // It's illegal to request a waffle_config with WAFFLE_CONTEXT_PROFILE
+    // != WAFFLE_NONE. Therefore, request an OpenGL 3.1 config without
+    // a profile and later verify that the desired and actual profile
+    // agree.
+    const enum waffle_enum desired_profile = attrs.profile;
+    attrs.version = 31;
+    attrs.profile = WAFFLE_NONE;
+    wflinfo_try_create_context(dpy, attrs, &ctx, &config,
+                               exit_if_ctx_creation_fails);
+
+    if (desired_profile == WAFFLE_NONE ||
+        desired_profile == WAFFLE_DONT_CARE) {
+        goto success;
+    }
+
+    // The user cares about the profile. We must bind the context to inspect
+    // its profile.
+    //
+    // Skip window creation. No window is needed when binding an OpenGL >= 3.0
+    // context.
+    ok = waffle_make_current(dpy, NULL, ctx);
+    if (!ok) {
+        error_waffle();
+    }
+
+    const enum waffle_enum actual_profile = gl_get_profile();
+    waffle_make_current(dpy, NULL, NULL);
+    if (actual_profile == desired_profile) {
+        goto success;
+    }
+
+    return false;
+
+success:
+    *out_ctx = ctx;
+    *out_config = config;
+    return true;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+/// Exit on failure.
+static void
 wflinfo_create_context(struct waffle_display *dpy,
                        struct wflinfo_config_attrs attrs,
                        struct waffle_context **out_ctx,
                        struct waffle_config **out_config)
 {
+    bool ok = false;
+
     if (attrs.api == WAFFLE_CONTEXT_OPENGL &&
         attrs.profile != WAFFLE_NONE &&
         attrs.version == WAFFLE_DONT_CARE) {
@@ -724,21 +924,54 @@ wflinfo_create_context(struct waffle_display *dpy,
         static int known_gl_profile_versions[] =
             { 32, 33, 40, 41, 42, 43, 44 };
 
-        bool ok;
-
         for (int i = ARRAY_SIZE(known_gl_profile_versions) - 1; i >= 0; i--) {
             attrs.version = known_gl_profile_versions[i];
-            ok = wflinfo_try_create_context(dpy, attrs,
-                                            out_ctx, out_config, false);
+            ok = wflinfo_try_create_context(dpy, attrs, out_ctx, out_config, false);
             if (ok) {
-                return true;
+                return;
             }
         }
 
-        return false;
+        // Handle OpenGL 3.1 separately because profiles are weird in 3.1.
+        ok = wflinfo_try_create_context_gl31(
+                dpy, attrs, out_ctx, out_config,
+                /*exit_if_ctx_creation_fails*/ false);
+        if (ok) {
+            return;
+        }
+
+        error_printf("Wflinfo", "Failed to create context; Try choosing a "
+                     "specific context version with --version");
+    } else if (attrs.api == WAFFLE_CONTEXT_OPENGL &&
+               attrs.version == 31) {
+        // The user requested a specific profile of an OpenGL 3.1 context.
+        // Strictly speaking, an OpenGL 3.1 context has no profile, but let's
+        // do what the user wants.
+        ok = wflinfo_try_create_context_gl31(
+                dpy, attrs, out_ctx, out_config,
+                /*exit_if_ctx_creation_fails*/ true);
+        if (ok) {
+            return;
+        }
+
+        printf("Wflinfo warn: Succesfully requested an OpenGL 3.1 context, but returned\n"
+               "Wflinfo warn: context had the wrong profile.  Fallback to requesting an\n"
+               "Wflinfo warn: OpenGL 3.2 context, which is guaranteed to have the correct\n"
+               "Wflinfo warn: profile if context creation succeeds.\n");
+        attrs.version = 32;
+        assert(attrs.profile == WAFFLE_CONTEXT_CORE_PROFILE ||
+               attrs.profile == WAFFLE_CONTEXT_COMPATIBILITY_PROFILE);
+        ok = wflinfo_try_create_context(dpy, attrs, out_ctx, out_config,
+                                        /*exit_on_fail*/ false);
+        if (ok) {
+            return;
+        }
+
+        error_printf("Wflinfo", "Failed to create an OpenGL 3.1 or later "
+                     "context with requested profile");
     } else {
-        return wflinfo_try_create_context(dpy, attrs,
-                                          out_ctx, out_config, true);
+        wflinfo_try_create_context(dpy, attrs, out_ctx, out_config,
+                                  /*exit_on_fail*/ true);
     }
 }
 
@@ -804,11 +1037,8 @@ main(int argc, char **argv)
         .forward_compat = opts.context_forward_compatible,
         .debug = opts.context_debug,
     };
-    ok = wflinfo_create_context(dpy, config_attrs, &ctx, &config);
-    if (!ok) {
-        error_printf("Wflinfo", "Failed to create context; Try choosing a "
-                     "specific context with --version and/or --profile");
-    }
+
+    wflinfo_create_context(dpy, config_attrs, &ctx, &config);
 
     window = waffle_window_create(config, WINDOW_WIDTH, WINDOW_HEIGHT);
     if (!window)
-- 
1.9.0



More information about the waffle mailing list