]> git.cworth.org Git - apitrace/commitdiff
egl: EGL image trace support
authorImre Deak <imre.deak@intel.com>
Mon, 4 Jun 2012 11:14:24 +0000 (14:14 +0300)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Thu, 2 Aug 2012 06:13:49 +0000 (07:13 +0100)
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@intel.com>
helpers/eglsize.cpp [new file with mode: 0644]
helpers/eglsize.hpp [new file with mode: 0644]
retrace/glretrace_egl.cpp
wrappers/CMakeLists.txt
wrappers/egltrace.py

diff --git a/helpers/eglsize.cpp b/helpers/eglsize.cpp
new file mode 100644 (file)
index 0000000..20dc172
--- /dev/null
@@ -0,0 +1,260 @@
+/*********************************************************************
+ *
+ * Copyright 2012 Intel Corporation
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *********************************************************************/
+
+/*
+ * 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;
+}
+
+static struct image_info *
+get_image_info(EGLImageKHR image)
+{
+    struct image_info *info;
+
+    image_map_mutex.lock();
+    info = image_map[image];
+    image_map_mutex.unlock();
+
+    return info;
+}
+
+void
+_eglCreateImageKHR_epilog(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_epilog(EGLImageKHR image)
+{
+    struct image_info *info;
+
+    info = get_image_info(image);
+
+    image_map_mutex.lock();
+    image_map.erase(image);
+    image_map_mutex.unlock();
+
+    free(info);
+}
+
+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 (file)
index 0000000..edb2fa6
--- /dev/null
@@ -0,0 +1,64 @@
+
+/*********************************************************************
+ *
+ * Copyright 2012 Intel Corporation
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *********************************************************************/
+
+/*
+ * 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_epilog(EGLImageKHR image);
+
+void
+_eglCreateImageKHR_epilog(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
index 70fe3546835e704aa00ec1687025758a864fdf5d..72368a9ccfe1ee7b19c8bd6006c2189f690548a0 100644 (file)
@@ -32,6 +32,7 @@
 #include "retrace.hpp"
 #include "glretrace.hpp"
 #include "os.hpp"
+#include "eglsize.hpp"
 
 #ifndef EGL_OPENGL_ES_API
 #define EGL_OPENGL_ES_API              0x30A0
@@ -224,6 +225,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 glretrace::egl_callbacks[] = {
     {"eglGetError", &retrace::ignore},
     {"eglGetDisplay", &retrace::ignore},
@@ -259,5 +277,7 @@ const retrace::Entry glretrace::egl_callbacks[] = {
     {"eglSwapBuffers", &retrace_eglSwapBuffers},
     //{"eglCopyBuffers", &retrace::ignore},
     {"eglGetProcAddress", &retrace::ignore},
+    {"eglCreateImageKHR", &retrace::ignore},
+    {"glEGLImageTargetTexture2DOES", &retrace_glEGLImageTargetTexture2DOES},
     {NULL, NULL},
 };
index 17aff4c6e3a2e3e81b7dda4d4ba5e32761bab73e..a2bcfff14907adcb3ae8d52aef0a0df368f83634 100644 (file)
@@ -376,6 +376,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)
index 18058468049154acf0eb2d78d77a9d13368424ed..505ca797442c4f354847d0394ef47ec7bf4c4a0e 100644 (file)
@@ -81,6 +81,27 @@ class EglTracer(GlTracer):
             print '        gltrace::releaseContext((uintptr_t)ctx);'
             print '    }'
 
+        if function.name == 'eglCreateImageKHR':
+            print '    _eglCreateImageKHR_epilog(dpy, ctx, target, buffer,'
+            print '                              attrib_list, _result);'
+
+        if function.name == 'eglDestroyImageKHR':
+            print '    _eglDestroyImageKHR_epilog(image);'
+
+    def serializeArgValue(self, function, arg):
+        if function.name == 'glEGLImageTargetTexture2DOES' and \
+          arg.name == 'image':
+            print '    GLsizei blob_size;'
+            print '    GLvoid *blob_ptr;'
+            print '    blob_size = _glEGLImageTargetTexture2DOES_size(target, image);'
+            print '    blob_ptr = _glEGLImageTargetTexture2DOES_get_ptr(target, image);'
+            print '    trace::localWriter.writeBlob(blob_ptr, blob_size);'
+            print '    _glEGLImageTargetTexture2DOES_put_ptr(blob_ptr);'
+
+            return
+
+        GlTracer.serializeArgValue(self, function, arg)
+
 if __name__ == '__main__':
     print '#include <stdlib.h>'
     print '#include <string.h>'
@@ -94,6 +115,7 @@ if __name__ == '__main__':
     print
     print '#include "glproc.hpp"'
     print '#include "glsize.hpp"'
+    print '#include "eglsize.hpp"'
     print
     
     api = API()