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

Imre Deak imre.deak at intel.com
Fri Jun 15 03:30:30 PDT 2012


On Thu, 2012-06-14 at 18:55 +0300, Imre Deak wrote:
> 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.

This was sent - yet again - too early and has some bugs I noticed only
now:

> 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);

We don't need to erase info from the map here. Besides it's not even
correct, since we need to pass the map key - not value - to erase.

> +    image_map_mutex.unlock();
> +
> +    return info;
> +}
> +
> +void
> +_eglCreateImageKHR_prolog(EGLDisplay dpy, EGLContext ctx, EGLenum target,
> +                            EGLClientBuffer buffer, const EGLint *attrib_list,
> +                            EGLImageKHR image)

This should be renamed to _epilog, since it's called from the end of
eglCreateImageKHR.

> +{
> +    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)

Should be _epilog as above.

> +{
> +    image_map_mutex.lock();
> +    image_map.erase(image);
> +    image_map_mutex.unlock();
> +
> +    free(get_image_info(image));

We should call get image info before deleting it from the map.

> +}
> +
> +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;
>  }
>  
> -

Unnecessary change.

>  #endif /* _GL_SIZE_HPP_ */

[...]

I will resend this with the above fixed if I get more feedback on this
patch and the rest of the patchset. For now I pushed the fixed version
to the git://github.com/ideak/apitrace.git dev branch.

Any comments, review would be appreciated.

Thanks,
Imre



More information about the apitrace mailing list