From 2b745f6c67ac66bb31d6e4da6436a031f9c9ae42 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 4 Jun 2012 14:14:24 +0300 Subject: [PATCH] egl: EGL image trace support 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 --- helpers/eglsize.cpp | 260 ++++++++++++++++++++++++++++++++++++++ helpers/eglsize.hpp | 64 ++++++++++ retrace/glretrace_egl.cpp | 20 +++ wrappers/CMakeLists.txt | 1 + wrappers/egltrace.py | 22 ++++ 5 files changed, 367 insertions(+) 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..20dc172 --- /dev/null +++ b/helpers/eglsize.cpp @@ -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 +#include + +#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::mapimage_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 index 0000000..edb2fa6 --- /dev/null +++ b/helpers/eglsize.hpp @@ -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 diff --git a/retrace/glretrace_egl.cpp b/retrace/glretrace_egl.cpp index 70fe354..72368a9 100644 --- a/retrace/glretrace_egl.cpp +++ b/retrace/glretrace_egl.cpp @@ -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((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}, }; diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt index 17aff4c..a2bcfff 100644 --- a/wrappers/CMakeLists.txt +++ b/wrappers/CMakeLists.txt @@ -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) diff --git a/wrappers/egltrace.py b/wrappers/egltrace.py index 1805846..505ca79 100644 --- a/wrappers/egltrace.py +++ b/wrappers/egltrace.py @@ -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 ' print '#include ' @@ -94,6 +115,7 @@ if __name__ == '__main__': print print '#include "glproc.hpp"' print '#include "glsize.hpp"' + print '#include "eglsize.hpp"' print api = API() -- 2.43.0