[waffle] [PATCH 05/12] waffle: add waffle_display_info_json()

Frank Henigman fjhenigman at google.com
Wed Jan 6 11:56:34 PST 2016


Duplicate wflinfo functionality in the API, with the difference that the
information is returned in JSON form.
The function has a parameter for including platform-specific information,
but it is ignored for now.

Signed-off-by: Frank Henigman <fjhenigman at google.com>
---
 include/waffle/waffle.h         |   5 +
 man/waffle_display.3.xml        |  19 +++
 src/waffle/api/waffle_display.c | 284 +++++++++++++++++++++++++++++++++++++++-
 src/waffle/waffle.def.in        |   1 +
 4 files changed, 308 insertions(+), 1 deletion(-)

diff --git a/include/waffle/waffle.h b/include/waffle/waffle.h
index df0218e..1800399 100644
--- a/include/waffle/waffle.h
+++ b/include/waffle/waffle.h
@@ -214,6 +214,11 @@ bool
 waffle_display_supports_context_api(struct waffle_display *self,
                                     int32_t context_api);
 
+#if WAFFLE_API_VERSION >= 0x0106
+char*
+waffle_display_info_json(struct waffle_display *self, bool platform_too);
+#endif
+
 union waffle_native_display*
 waffle_display_get_native(struct waffle_display *self);
 
diff --git a/man/waffle_display.3.xml b/man/waffle_display.3.xml
index 9896247..5358472 100644
--- a/man/waffle_display.3.xml
+++ b/man/waffle_display.3.xml
@@ -24,6 +24,7 @@
     <refname>waffle_display</refname>
     <refname>waffle_display_connect</refname>
     <refname>waffle_display_disconnect</refname>
+    <refname>waffle_display_info_json</refname>
     <refname>waffle_display_supports_context_api</refname>
     <refname>waffle_display_get_native</refname>
     <refpurpose>class <classname>waffle_display</classname></refpurpose>
@@ -58,6 +59,12 @@ struct waffle_display;
       </funcprototype>
 
       <funcprototype>
+        <funcdef>char* <function>waffle_display_info_json</function></funcdef>
+        <paramdef>struct waffle_display *<parameter>self</parameter></paramdef>
+        <paramdef>bool <parameter>platform_info</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
         <funcdef>bool <function>waffle_display_supports_context_api</function></funcdef>
         <paramdef>struct waffle_display *<parameter>self</parameter></paramdef>
         <paramdef>int32_t <parameter>context_api</parameter></paramdef>
@@ -129,6 +136,18 @@ struct waffle_display;
       </varlistentry>
 
       <varlistentry>
+        <term><function>waffle_display_info_json()</function></term>
+        <listitem>
+          <para>
+            Return a JSON string containing information about the current context on the given display, including Waffle platform and API, GL version/vendor/renderer and extensions.
+            If <parameter>platform_info</parameter> is true, platform-specific information (such as GLX or EGL versions and extensions) will be included as available.
+            Returns <constant>NULL</constant> on error.
+            The string should be deallocated with <citerefentry><refentrytitle><function>free</function></refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><function>waffle_display_supports_context_api()</function></term>
         <listitem>
           <para>
diff --git a/src/waffle/api/waffle_display.c b/src/waffle/api/waffle_display.c
index fa19462..7abe2ef 100644
--- a/src/waffle/api/waffle_display.c
+++ b/src/waffle/api/waffle_display.c
@@ -23,13 +23,61 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#include <ctype.h>
+#include <stdio.h>
+
 #include "api_priv.h"
 
-#include "wcore_error.h"
+#include "json.h"
+
+#include "wcore_context.h"
 #include "wcore_display.h"
+#include "wcore_error.h"
 #include "wcore_platform.h"
 #include "wcore_util.h"
 
+typedef unsigned int GLint;
+typedef unsigned int GLenum;
+typedef unsigned char GLubyte;
+
+enum {
+    // Copied from <GL/gl*.h>.
+    GL_NO_ERROR = 0,
+
+    GL_CONTEXT_FLAGS = 0x821e,
+    GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = 0x00000001,
+    GL_CONTEXT_FLAG_DEBUG_BIT              = 0x00000002,
+    GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB  = 0x00000004,
+
+    GL_VENDOR                              = 0x1F00,
+    GL_RENDERER                            = 0x1F01,
+    GL_VERSION                             = 0x1F02,
+    GL_EXTENSIONS                          = 0x1F03,
+    GL_NUM_EXTENSIONS                      = 0x821D,
+    GL_SHADING_LANGUAGE_VERSION            = 0x8B8C,
+};
+
+#ifndef _WIN32
+#define APIENTRY
+#else
+#ifndef APIENTRY
+#define APIENTRY __stdcall
+#endif
+#endif
+
+static GLenum (APIENTRY *glGetError)(void);
+static void (APIENTRY *glGetIntegerv)(GLenum pname, GLint *params);
+static const GLubyte * (APIENTRY *glGetString)(GLenum name);
+static const GLubyte * (APIENTRY *glGetStringi)(GLenum name, GLint i);
+
+#if defined(__GNUC__)
+#define NORETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define NORETURN __declspec(noreturn)
+#else
+#define NORETURN
+#endif
+
 WAFFLE_API struct waffle_display*
 waffle_display_connect(const char *name)
 {
@@ -90,6 +138,240 @@ waffle_display_supports_context_api(
                                                             context_api);
 }
 
+static int
+parse_version(const char *version)
+{
+    int count, major, minor;
+
+    if (version == NULL)
+        return 0;
+
+    while (*version != '\0' && !isdigit(*version))
+        version++;
+
+    count = sscanf(version, "%d.%d", &major, &minor);
+    if (count != 2)
+        return 0;
+
+    if (minor > 9)
+        return 0;
+
+    return (major * 10) + minor;
+}
+
+static void
+add_context_flags(struct json *jj)
+{
+    static struct {
+        GLint flag;
+        char *str;
+    } flags[] = {
+        { GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, "FORWARD_COMPATIBLE" },
+        { GL_CONTEXT_FLAG_DEBUG_BIT, "DEBUG" },
+        { GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, "ROBUST_ACCESS" },
+    };
+    int flag_count = sizeof(flags) / sizeof(flags[0]);
+    GLint context_flags = 0;
+
+    glGetIntegerv(GL_CONTEXT_FLAGS, &context_flags);
+    if (glGetError() != GL_NO_ERROR)
+        return json_append(jj, json_str("WFLINFO_GL_ERROR"));
+
+    if (context_flags == 0)
+        return json_append(jj, json_num(0));
+
+    for (int i = 0; i < flag_count; i++) {
+        if ((flags[i].flag & context_flags) != 0) {
+            json_append(jj, json_str(flags[i].str));
+            context_flags = context_flags & ~flags[i].flag;
+        }
+    }
+    for (int i = 0; context_flags != 0; context_flags >>= 1, i++) {
+        if ((context_flags & 1) != 0) {
+            json_append(jj, json_num(1 << i));
+        }
+    }
+}
+
+static void
+add_extensions(struct json *jj, bool use_stringi)
+{
+    GLint count = 0, i;
+    const char *ext;
+
+    if (use_stringi) {
+        glGetIntegerv(GL_NUM_EXTENSIONS, &count);
+        if (glGetError() != GL_NO_ERROR) {
+            json_append(jj, json_str("WFLINFO_GL_ERROR"));
+        } else {
+            for (i = 0; i < count; i++) {
+                ext = (const char *) glGetStringi(GL_EXTENSIONS, i);
+                if (glGetError() != GL_NO_ERROR)
+                    ext = "WFLINFO_GL_ERROR";
+                json_append(jj, json_str(ext));
+            }
+        }
+    } else {
+        const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
+        if (glGetError() != GL_NO_ERROR)
+            json_append(jj, json_str("WFLINFO_GL_ERROR"));
+        else
+            json_append(jj, json_split(extensions, " "));
+    }
+}
+
+static void
+add_generic_info(struct json *jj, struct wcore_context *ctx)
+{
+    int32_t dl;
+    //XXX this pattern seems to occur repeatedly - do we need two sets of enums?
+    switch (ctx->context_api) {
+        case WAFFLE_CONTEXT_OPENGL:     dl = WAFFLE_DL_OPENGL;      break;
+        case WAFFLE_CONTEXT_OPENGL_ES1: dl = WAFFLE_DL_OPENGL_ES1;  break;
+        case WAFFLE_CONTEXT_OPENGL_ES2: dl = WAFFLE_DL_OPENGL_ES2;  break;
+        case WAFFLE_CONTEXT_OPENGL_ES3: dl = WAFFLE_DL_OPENGL_ES3;  break;
+        default:
+            abort();
+            break;
+    }
+
+    glGetError = waffle_dl_sym(dl, "glGetError");
+    if (!glGetError)
+        return json_append(jj, NULL);
+
+    glGetIntegerv = waffle_dl_sym(dl, "glGetIntegerv");
+    if (!glGetIntegerv)
+        return json_append(jj, NULL);
+
+    glGetString = waffle_dl_sym(dl, "glGetString");
+    if (!glGetString)
+        return json_append(jj, NULL);
+
+    // Retrieving GL functions is tricky. When glGetStringi is supported, here
+    // are some boggling variations as of 2014-11-19:
+    //   - Mali drivers on EGL 1.4 expose glGetStringi statically from
+    //     libGLESv2 but not dynamically from eglGetProcAddress. The EGL 1.4 spec
+    //     permits this behavior.
+    //   - EGL 1.5 requires that eglGetStringi be exposed dynamically through
+    //     eglGetProcAddress. Exposing statically with dlsym is optional.
+    //   - Windows requires that glGetStringi be exposed dynamically from
+    //     wglGetProcAddress. Exposing statically from GetProcAddress (Window's
+    //     dlsym equivalent) is optional.
+    //   - Mesa drivers expose glGetStringi statically from libGL and libGLESv2
+    //     and dynamically from eglGetProcAddress and glxGetProcAddress.
+    //   - Mac exposes glGetStringi only statically.
+    //
+    // Try waffle_dl_sym before waffle_get_proc_address because
+    // (1) egl/glXProcAddress can return invalid non-null pointers for
+    // unsupported functions and (2) dlsym returns non-null if and only if the
+    // library exposes the symbol.
+    glGetStringi = waffle_dl_sym(dl, "glGetStringi");
+    if (!glGetStringi) {
+        glGetStringi = waffle_get_proc_address("glGetStringi");
+    }
+
+    while(glGetError() != GL_NO_ERROR) {
+        /* Clear all errors */
+    }
+
+    const char *vendor = (const char *) glGetString(GL_VENDOR);
+    if (glGetError() != GL_NO_ERROR || vendor == NULL) {
+        vendor = "WFLINFO_GL_ERROR";
+    }
+
+    const char *renderer = (const char *) glGetString(GL_RENDERER);
+    if (glGetError() != GL_NO_ERROR || renderer == NULL) {
+        renderer = "WFLINFO_GL_ERROR";
+    }
+
+    const char *version_str = (const char *) glGetString(GL_VERSION);
+    if (glGetError() != GL_NO_ERROR || version_str == NULL) {
+        version_str = "WFLINFO_GL_ERROR";
+    }
+
+    assert(ctx->display->platform->waffle_platform);
+    const char *platform =
+            wcore_enum_to_string(ctx->display->platform->waffle_platform);
+    assert(platform != NULL);
+
+    assert(ctx->context_api);
+    const char *api = wcore_enum_to_string(ctx->context_api);
+    assert(api != NULL);
+
+    json_appendv(jj,
+        "waffle", "{",
+            "platform", json_str(platform),
+            "api",      json_str(api),
+         "}",
+        "opengl", "{",
+            "vendor",   json_str(vendor),
+            "renderer", json_str(renderer),
+            "version",  json_str(version_str),
+        "}", "");
+
+    int version = parse_version(version_str);
+
+    if (ctx->context_api == WAFFLE_CONTEXT_OPENGL && version >= 31) {
+        json_appendv(jj, "context_flags", "[", "");
+        add_context_flags(jj);
+        json_append(jj, "]");
+    }
+
+    // OpenGL and OpenGL ES >= 3.0 support glGetStringi(GL_EXTENSION, i).
+    const bool use_getstringi = version >= 30;
+
+    if (!glGetStringi && use_getstringi)
+        return json_append(jj, NULL);
+
+    // There are two exceptional cases where wflinfo may not get a
+    // version (or a valid version): one is in gles1 and the other
+    // is GL < 2.0. In these cases do not return WFLINFO_GL_ERROR,
+    // return None. This is preferable to returning WFLINFO_GL_ERROR
+    // because it creates a consistant interface for parsers
+    const char *language_str = "None";
+    if ((ctx->context_api == WAFFLE_CONTEXT_OPENGL && version >= 20) ||
+            ctx->context_api == WAFFLE_CONTEXT_OPENGL_ES2 ||
+            ctx->context_api == WAFFLE_CONTEXT_OPENGL_ES3) {
+        language_str = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
+        if (glGetError() != GL_NO_ERROR || language_str == NULL) {
+            language_str = "WFLINFO_GL_ERROR";
+        }
+    }
+
+    json_appendv(jj, "shading_language_version", json_str(language_str), "");
+    json_appendv(jj, "extensions", "[", "");
+    add_extensions(jj, use_getstringi);
+    json_append(jj, "]");
+}
+
+WAFFLE_API char*
+waffle_display_info_json(struct waffle_display *self, bool platform_too)
+{
+    struct wcore_display *wc_self = wcore_display(self);
+
+    const struct api_object *obj_list[] = {
+        wc_self ? &wc_self->api : NULL,
+    };
+
+    if (!api_check_entry(obj_list, 1))
+        return NULL;
+
+    if (!wc_self->current_context) {
+        wcore_errorf(WAFFLE_ERROR_UNKNOWN, "no current context");
+        return NULL;
+    }
+
+    struct json *jj = json_init();
+    if (!jj)
+        return NULL;
+
+    json_appendv(jj, "{", "generic", "{", "");
+    add_generic_info(jj, wc_self->current_context);
+    json_appendv(jj, "}", "}", "");
+
+    return json_destroy(jj);
+}
+
 WAFFLE_API union waffle_native_display*
 waffle_display_get_native(struct waffle_display *self)
 {
diff --git a/src/waffle/waffle.def.in b/src/waffle/waffle.def.in
index 75a56c0..99e0d56 100644
--- a/src/waffle/waffle.def.in
+++ b/src/waffle/waffle.def.in
@@ -12,6 +12,7 @@ EXPORTS
     waffle_is_extension_in_string
     waffle_display_connect
     waffle_display_disconnect
+    waffle_display_info_json
     waffle_display_supports_context_api
     waffle_display_get_native
     waffle_config_choose
-- 
2.6.0.rc2.230.g3dd15c0



More information about the waffle mailing list