#include "glstate_internal.hpp"
+#ifdef __linux__
+#include <dlfcn.h>
+#endif
+
#ifdef __APPLE__
#include <Carbon/Carbon.h>
};
+/**
+ * OpenGL ES does not support glGetTexLevelParameteriv, but it is possible to
+ * probe whether a texture has a given size by crafting a dummy glTexSubImage()
+ * call.
+ */
+static bool
+probeTextureLevelSizeOES(GLenum target, GLint level, const GLint size[3]) {
+ while (glGetError() != GL_NO_ERROR)
+ ;
+
+ GLenum internalFormat = GL_RGBA;
+ GLenum type = GL_UNSIGNED_BYTE;
+ GLint dummy = 0;
+
+ switch (target) {
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ glTexSubImage2D(target, level, size[0], size[1], 0, 0, internalFormat, type, &dummy);
+ break;
+ case GL_TEXTURE_3D_OES:
+ glTexSubImage3DOES(target, level, size[0], size[1], size[2], 0, 0, 0, internalFormat, type, &dummy);
+ default:
+ assert(0);
+ return false;
+ }
+
+ GLenum error = glGetError();
+
+ if (0) {
+ std::cerr << "(" << size[0] << ", " << size[1] << ", " << size[2] << ") = " << enumToString(error) << "\n";
+ }
+
+ if (error == GL_NO_ERROR) {
+ return true;
+ }
+
+ while (glGetError() != GL_NO_ERROR)
+ ;
+
+ return false;
+}
+
+
+/**
+ * Bisect the texture size along an axis.
+ *
+ * It is assumed that the texture exists.
+ */
+static GLint
+bisectTextureLevelSizeOES(GLenum target, GLint level, GLint axis, GLint max) {
+ GLint size[3] = {0, 0, 0};
+
+ assert(axis < 3);
+ assert(max >= 0);
+
+ GLint min = 0;
+ while (true) {
+ GLint test = (min + max) / 2;
+ if (test == min) {
+ return min;
+ }
+
+ size[axis] = test;
+
+ if (probeTextureLevelSizeOES(target, level, size)) {
+ min = test;
+ } else {
+ max = test;
+ }
+ }
+}
+
+
+/**
+ * Special path to obtain texture size on OpenGL ES, that does not rely on
+ * glGetTexLevelParameteriv
+ */
+static bool
+getActiveTextureLevelDescOES(Context &context, GLenum target, GLint level, ImageDesc &desc)
+{
+ if (target == GL_TEXTURE_1D) {
+ // OpenGL ES does not support 1D textures
+ return false;
+ }
+
+ const GLint size[3] = {1, 1, 1};
+ if (!probeTextureLevelSizeOES(target, level, size)) {
+ return false;
+ }
+
+ // XXX: mere guess
+ desc.internalFormat = GL_RGBA;
+
+ GLint maxSize = 0;
+ switch (target) {
+ case GL_TEXTURE_2D:
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+ desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
+ desc.height = bisectTextureLevelSizeOES(target, level, 1, maxSize);
+ desc.depth = 1;
+ break;
+ case GL_TEXTURE_CUBE_MAP:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxSize);
+ desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
+ desc.height = desc.width;
+ desc.depth = 1;
+ break;
+ case GL_TEXTURE_3D_OES:
+ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_OES, &maxSize);
+ desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
+ desc.width = bisectTextureLevelSizeOES(target, level, 1, maxSize);
+ desc.depth = bisectTextureLevelSizeOES(target, level, 2, maxSize);
+ break;
+ default:
+ return false;
+ }
+
+ if (0) {
+ std::cerr
+ << enumToString(target) << " "
+ << level << " "
+ << desc.width << "x" << desc.height << "x" << desc.depth
+ << "\n";
+ }
+
+ return desc.valid();
+}
+
+
static inline bool
getActiveTextureLevelDesc(Context &context, GLenum target, GLint level, ImageDesc &desc)
{
if (context.ES) {
- // XXX: OpenGL ES does not support glGetTexLevelParameteriv
- return false;
+ return getActiveTextureLevelDescOES(context, target, level, desc);
}
glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat);
}
+/**
+ * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
+ * texture to a framebuffer.
+ */
+static inline void
+getTexImageOES(GLenum target, GLint level, ImageDesc &desc, GLubyte *pixels)
+{
+ memset(pixels, 0x80, desc.height * desc.width * 4);
+
+ GLenum texture_binding = GL_NONE;
+ switch (target) {
+ case GL_TEXTURE_2D:
+ texture_binding = GL_TEXTURE_BINDING_2D;
+ break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ texture_binding = GL_TEXTURE_BINDING_CUBE_MAP;
+ break;
+ case GL_TEXTURE_3D_OES:
+ texture_binding = GL_TEXTURE_BINDING_3D_OES;
+ default:
+ return;
+ }
+
+ GLint texture = 0;
+ glGetIntegerv(texture_binding, &texture);
+ if (!texture) {
+ return;
+ }
+
+ GLint prev_fbo = 0;
+ GLuint fbo = 0;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ GLenum status;
+
+ switch (target) {
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ std::cerr << __FUNCTION__ << ": " << enumToString(status) << "\n";
+ }
+ glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ break;
+ case GL_TEXTURE_3D_OES:
+ for (int i = 0; i < desc.depth; i++) {
+ glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, level, i);
+ glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels + 4 * i * desc.width * desc.height);
+ }
+ break;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
+
+ glDeleteFramebuffers(1, &fbo);
+}
+
+
static inline void
dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level)
{
context.resetPixelPackState();
- glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ if (context.ES) {
+ getTexImageOES(target, level, desc, pixels);
+ } else {
+ glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ }
context.restorePixelPackState();
static bool
getDrawableBounds(GLint *width, GLint *height) {
-#if defined(TRACE_EGL)
+#if defined(__linux__)
+ if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
+ EGLContext currentContext = eglGetCurrentContext();
+ if (currentContext == EGL_NO_CONTEXT) {
+ return false;
+ }
- EGLContext currentContext = eglGetCurrentContext();
- if (currentContext == EGL_NO_CONTEXT) {
- return false;
- }
+ EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
+ if (currentSurface == EGL_NO_SURFACE) {
+ return false;
+ }
- EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
- if (currentSurface == EGL_NO_SURFACE) {
- return false;
- }
+ EGLDisplay currentDisplay = eglGetCurrentDisplay();
+ if (currentDisplay == EGL_NO_DISPLAY) {
+ return false;
+ }
- EGLDisplay currentDisplay = eglGetCurrentDisplay();
- if (currentDisplay == EGL_NO_DISPLAY) {
- return false;
- }
+ if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
+ !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
+ return false;
+ }
- if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
- !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
- return false;
+ return true;
}
+#endif
- return true;
-
-#elif defined(_WIN32)
+#if defined(_WIN32)
HDC hDC = wglGetCurrentDC();
if (!hDC) {