[PATCH v4 19/19] egl: EGL image trace support

Imre Deak imre.deak at intel.com
Thu Jun 14 08:55:27 PDT 2012


Getting the dimension / format of EGL images is not straitghforward.
One way to get these would be through glGetTexLevelParameter, but it's
not supported by GLES. Another way is to attach the EGL image to a
renderbuffer and call glGetRenderbufferParameter on it, but at least
mesa doesn't support attaching images in RGBA_REV format. And that
format happens to be an important one in applications we care about
(like the Android browser). We could also check the parameters of the
underlying native buffer, but these are not exposed through any API (for
either Xpixmaps or Android native buffers)

So the only way I found is to try a non-destructive glCopyTexSubImage2D
with sizes starting from GL_MAX_TEXTURE_SIZE down to 1 and determine the
correct size based on whether this returns an error or not.

Signed-off-by: Imre Deak <imre.deak at intel.com>
---
 helpers/eglsize.cpp       |  231 +++++++++++++++++++++++++++++++++++++++++++++
 helpers/eglsize.hpp       |   37 +++++++
 helpers/glsize.hpp        |    1 -
 retrace/glretrace_egl.cpp |   20 ++++
 specs/eglapi.py           |    4 +-
 specs/stdapi.py           |    4 +-
 wrappers/CMakeLists.txt   |    1 +
 wrappers/egltrace.py      |    9 ++
 wrappers/trace.py         |    9 ++-
 9 files changed, 311 insertions(+), 5 deletions(-)
 create mode 100644 helpers/eglsize.cpp
 create mode 100644 helpers/eglsize.hpp

diff --git a/helpers/eglsize.cpp b/helpers/eglsize.cpp
new file mode 100644
index 0000000..2c3e13a
--- /dev/null
+++ b/helpers/eglsize.cpp
@@ -0,0 +1,231 @@
+/*
+ * Auxiliary functions to compute the size of array/blob arguments.
+ */
+
+#include <string.h>
+#include <map>
+
+#include "os_thread.hpp"
+#include "glimports.hpp"
+#include "glproc.hpp"
+#include "eglsize.hpp"
+#include "assert.h"
+
+
+static os::recursive_mutex image_map_mutex;
+static std::map<EGLImageKHR, struct image_info *>image_map;
+
+static int
+bisect_val(int min, int max, bool is_valid_val(int val))
+{
+    bool valid;
+
+    while (1) {
+        int try_val = min + (max - min + 1) / 2;
+
+        valid = is_valid_val(try_val);
+        if (min == max)
+            break;
+
+        if (valid)
+            min = try_val;
+        else
+            max = try_val - 1;
+    }
+
+    return valid ? min : -1;
+}
+
+static bool
+is_valid_width(int val)
+{
+    _glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, val, 1);
+    return _glGetError() == GL_NO_ERROR;
+}
+
+static bool
+is_valid_height(int val)
+{
+    _glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, val);
+    return _glGetError() == GL_NO_ERROR;
+}
+
+static int
+detect_size(int *width_ret, int *height_ret)
+{
+    static GLint max_tex_size;
+    int width;
+    int height;
+
+    if (!max_tex_size)
+        _glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
+
+    width = bisect_val(1, max_tex_size, is_valid_width);
+    if (width < 0)
+        return -1;
+
+    height = bisect_val(1, max_tex_size, is_valid_height);
+    if (height < 0)
+        return -1;
+
+    *width_ret = width;
+    *height_ret = height;
+
+    return 0;
+}
+
+void
+_eglCreateImageKHR_get_image_info(EGLImageKHR image, struct image_info *info)
+{
+    GLuint fbo = 0;
+    GLuint orig_fbo = 0;
+    GLuint texture = 0;
+    GLuint orig_texture;
+    GLenum status;
+
+    _glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&orig_fbo);
+    _glGenFramebuffers(1, &fbo);
+    _glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    _glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *)&orig_texture);
+    _glGenTextures(1, &texture);
+    _glBindTexture(GL_TEXTURE_2D, texture);
+
+    _glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+    memset(info, sizeof *info, 0);
+
+    _glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                            GL_TEXTURE_2D, texture, 0);
+    status = _glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status == GL_FRAMEBUFFER_COMPLETE) {
+        if (detect_size(&info->width, &info->height) != 0)
+            os::log("%s: can't detect image size\n", __func__);
+    } else {
+        os::log("%s: error: %x\n", __func__, status);
+    }
+
+    /* Don't leak errors to the traced application. */
+    (void)_glGetError();
+
+    _glBindTexture(GL_TEXTURE_2D, orig_texture);
+    _glDeleteTextures(1, &texture);
+
+    _glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo);
+    _glDeleteFramebuffers(1, &fbo);
+
+    return;
+}
+
+struct image_info *
+get_image_info(EGLImageKHR image)
+{
+    struct image_info *info;
+
+    image_map_mutex.lock();
+    info = image_map[image];
+    image_map.erase(info);
+    image_map_mutex.unlock();
+
+    return info;
+}
+
+void
+_eglCreateImageKHR_prolog(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                            EGLClientBuffer buffer, const EGLint *attrib_list,
+                            EGLImageKHR image)
+{
+    struct image_info *info;
+
+    info = (struct image_info *)malloc(sizeof *info);
+    _eglCreateImageKHR_get_image_info(image, info);
+
+    image_map_mutex.lock();
+    assert(image_map.find(image) == image_map.end());
+    image_map[image] = info;
+    image_map_mutex.unlock();
+}
+
+void
+_eglDestroyImageKHR_prolog(EGLImageKHR image)
+{
+    image_map_mutex.lock();
+    image_map.erase(image);
+    image_map_mutex.unlock();
+
+    free(get_image_info(image));
+}
+
+void
+get_texture_2d_image(struct image_blob *blob)
+{
+    GLuint fbo = 0;
+    GLint prev_fbo = 0;
+    GLint texture;
+    GLenum status;
+
+    _glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
+    if (!texture)
+        return;
+
+    _glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
+    _glGenFramebuffers(1, &fbo);
+    _glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    _glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                            texture, 0);
+    status = _glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE)
+        os::log("%s: error: %d\n", __func__, status);
+    _glReadPixels(0, 0, blob->info.width, blob->info.height, GL_RGBA,
+                  GL_UNSIGNED_BYTE, blob->data);
+    /* Don't leak errors to the traced application. */
+    (void)_glGetError();
+
+    _glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
+    _glDeleteFramebuffers(1, &fbo);
+}
+
+size_t
+_glEGLImageTargetTexture2DOES_size(GLint target, EGLImageKHR image)
+{
+    struct image_info *info;
+    size_t size;
+
+    info = get_image_info(image);
+    size = sizeof(struct image_blob) - 1;
+    /* We always read out the pixels in RGBA format */
+    size += info->width * info->height * 4;
+
+    return size;
+}
+
+void *
+_glEGLImageTargetTexture2DOES_get_ptr(GLenum target, EGLImageKHR image)
+{
+    GLuint tex;
+    GLuint bound_tex;
+    size_t image_blob_size = _glEGLImageTargetTexture2DOES_size(target, image);
+    struct image_blob *blob;
+    struct image_info *info;
+
+    _glGenTextures(1, &tex);
+    _glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *)&bound_tex);
+    _glBindTexture(GL_TEXTURE_2D, tex);
+    _glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+    blob = (struct image_blob *)malloc(image_blob_size);
+    info = get_image_info(image);
+    blob->info = *info;
+    get_texture_2d_image(blob);
+    _glBindTexture(GL_TEXTURE_2D, bound_tex);
+    _glDeleteBuffers(1, &tex);
+
+    return (void *)blob;
+}
+
+void
+_glEGLImageTargetTexture2DOES_put_ptr(const void *buffer)
+{
+    free((void *)buffer);
+}
+
diff --git a/helpers/eglsize.hpp b/helpers/eglsize.hpp
new file mode 100644
index 0000000..a6fd377
--- /dev/null
+++ b/helpers/eglsize.hpp
@@ -0,0 +1,37 @@
+/*
+ * Auxiliary functions to compute the size of array/blob arguments.
+ */
+
+#ifndef _EGLSIZE_HPP_
+#define _EGLSIZE_HPP_
+
+struct image_info
+{
+    int width;
+    int height;
+};
+
+struct image_blob
+{
+    struct image_info info;
+    char data[1];
+};
+
+void
+_eglDestroyImageKHR_prolog(EGLImageKHR image);
+
+void
+_eglCreateImageKHR_prolog(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                            EGLClientBuffer buffer, const EGLint *attrib_list,
+                            EGLImageKHR image);
+
+size_t
+_glEGLImageTargetTexture2DOES_size(GLint target, EGLImageKHR image);
+
+void *
+_glEGLImageTargetTexture2DOES_get_ptr(GLenum target, EGLImageKHR image);
+
+void
+_glEGLImageTargetTexture2DOES_put_ptr(const void *buffer);
+
+#endif
diff --git a/helpers/glsize.hpp b/helpers/glsize.hpp
index 0103ecb..39f1dc9 100644
--- a/helpers/glsize.hpp
+++ b/helpers/glsize.hpp
@@ -813,5 +813,4 @@ _AttribPairList_size(const T *pAttribList, const T terminator = static_cast<T>(0
     return size;
 }
 
-
 #endif /* _GL_SIZE_HPP_ */
diff --git a/retrace/glretrace_egl.cpp b/retrace/glretrace_egl.cpp
index 4603b9f..048c061 100644
--- a/retrace/glretrace_egl.cpp
+++ b/retrace/glretrace_egl.cpp
@@ -33,6 +33,7 @@
 #include "glretrace.hpp"
 #include "os.hpp"
 #include "os_thread.hpp"
+#include "eglsize.hpp"
 
 #ifndef EGL_OPENGL_ES_API
 #define EGL_OPENGL_ES_API		0x30A0
@@ -266,6 +267,23 @@ static void retrace_eglSwapBuffers(trace::Call &call) {
     }
 }
 
+static void retrace_glEGLImageTargetTexture2DOES(trace::Call &call)
+{
+    EGLenum target = call.arg(0).toUInt();
+    struct image_blob *iblob;
+    struct image_info *info;
+
+    if (target != GL_TEXTURE_2D) {
+        os::log("%s: target %d not supported\n", __func__, target);
+        return;
+    }
+    iblob = static_cast<struct image_blob *>((call.arg(1)).toPointer());
+    info = &iblob->info;
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info->width, info->height, 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, iblob->data);
+}
+
 const retrace::Entry egl_callbacks[] = {
     {"eglGetError", &retrace::ignore},
     {"eglGetDisplay", &retrace::ignore},
@@ -301,6 +319,8 @@ const retrace::Entry egl_callbacks[] = {
     {"eglSwapBuffers", &retrace_eglSwapBuffers},
     //{"eglCopyBuffers", &retrace::ignore},
     {"eglGetProcAddress", &retrace::ignore},
+    {"eglCreateImageKHR", &retrace::ignore},
+    {"glEGLImageTargetTexture2DOES", &retrace_glEGLImageTargetTexture2DOES},
     {NULL, NULL},
 };
 
diff --git a/specs/eglapi.py b/specs/eglapi.py
index d1ba647..6da9990 100644
--- a/specs/eglapi.py
+++ b/specs/eglapi.py
@@ -356,7 +356,7 @@ eglapi.addFunctions([
     Function(EGLBoolean, "eglUnlockSurfaceKHR", [(EGLDisplay, "display"), (EGLSurface, "surface")]),
 
     # EGL_KHR_image_base
-    Function(EGLImageKHR, "eglCreateImageKHR", [(EGLDisplay, "dpy"), (EGLContext, "ctx"), (EGLenum, "target"), (EGLClientBuffer, "buffer"), (EGLAttribList, "attrib_list")]),
+    Function(EGLImageKHR, "eglCreateImageKHR", [(EGLDisplay, "dpy"), (EGLContext, "ctx"), (EGLenum, "target"), (EGLImageKHR, "buffer"), (EGLAttribList, "attrib_list")]),
     Function(EGLBoolean, "eglDestroyImageKHR", [(EGLDisplay, "dpy"), (EGLImageKHR, "image")]),
 
     # EGL_KHR_reusable_sync
@@ -392,6 +392,6 @@ eglapi.addFunctions([
     Function(EGLuint64NV, "eglGetSystemTimeNV", [], sideeffects=False),
 
     # GL_OES_EGL_image
-    GlFunction(Void, "glEGLImageTargetTexture2DOES", [(GLenum, "target"), (EGLImageKHR, "image")]),
+    GlFunction(Void, "glEGLImageTargetTexture2DOES", [(GLenum, "target"), (Blob(GLvoid, "_glEGLImageTargetTexture2DOES_size(target, image)", get_ptr="_glEGLImageTargetTexture2DOES_get_ptr(target, image)", put_ptr="_glEGLImageTargetTexture2DOES_put_ptr"), "image")]),
     GlFunction(Void, "glEGLImageTargetRenderbufferStorageOES", [(GLenum, "target"), (EGLImageKHR, "image")]),
 ])
diff --git a/specs/stdapi.py b/specs/stdapi.py
index b2bd904..1e74f5d 100644
--- a/specs/stdapi.py
+++ b/specs/stdapi.py
@@ -246,10 +246,12 @@ class Array(Type):
 
 class Blob(Type):
 
-    def __init__(self, type, size):
+    def __init__(self, type, size, get_ptr=None, put_ptr=None):
         Type.__init__(self, type.expr + ' *')
         self.type = type
         self.size = size
+        self.get_ptr = get_ptr
+        self.put_ptr = put_ptr
 
     def visit(self, visitor, *args, **kwargs):
         return visitor.visitBlob(self, *args, **kwargs)
diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt
index 461656e..13e8208 100644
--- a/wrappers/CMakeLists.txt
+++ b/wrappers/CMakeLists.txt
@@ -362,6 +362,7 @@ if (ENABLE_EGL AND NOT WIN32 AND NOT APPLE)
         egltrace.cpp
         glcaps.cpp
         gltrace_state.cpp
+        ${CMAKE_SOURCE_DIR}/helpers/eglsize.cpp
     )
 
     add_dependencies (egltrace glproc)
diff --git a/wrappers/egltrace.py b/wrappers/egltrace.py
index 48c15e6..04251a0 100644
--- a/wrappers/egltrace.py
+++ b/wrappers/egltrace.py
@@ -88,6 +88,14 @@ class EglTracer(GlTracer):
             print '    if (_result)'
             print '        gltrace::destroyContext((uintptr_t)ctx);'
 
+
+        if function.name == 'eglCreateImageKHR':
+            print '    _eglCreateImageKHR_prolog(dpy, ctx, target, buffer,'
+            print '                              attrib_list, _result);'
+
+        if function.name == 'eglDestroyImageKHR':
+            print '    _eglDestroyImageKHR_prolog(image);'
+
 if __name__ == '__main__':
     print '#include <stdlib.h>'
     print '#include <string.h>'
@@ -101,6 +109,7 @@ if __name__ == '__main__':
     print
     print '#include "glproc.hpp"'
     print '#include "glsize.hpp"'
+    print '#include "eglsize.hpp"'
     print
     
     api = API()
diff --git a/wrappers/trace.py b/wrappers/trace.py
index 749aff3..a1dec83 100644
--- a/wrappers/trace.py
+++ b/wrappers/trace.py
@@ -200,7 +200,14 @@ class ValueSerializer(stdapi.Visitor):
         print '    }'
 
     def visitBlob(self, blob, instance):
-        print '    trace::localWriter.writeBlob(%s, %s);' % (instance, blob.size)
+        print '    const void *_ptr_%s;' % instance
+        if blob.get_ptr:
+            print '    _ptr_%s = %s;' % (instance, blob.get_ptr)
+        else:
+            print '    _ptr_%s = %s;' % (instance, instance)
+        print '    trace::localWriter.writeBlob(_ptr_%s, %s);' % (instance, blob.size)
+        if blob.put_ptr:
+            print '    %s(_ptr_%s);' % (blob.put_ptr, instance)
 
     def visitEnum(self, enum, instance):
         print '    trace::localWriter.writeEnum(&_enum%s_sig, %s);' % (enum.tag, instance)
-- 
1.7.5.4



More information about the apitrace mailing list