/************************************************************************** * * Copyright 2011 LunarG, Inc. * All Rights Reserved. * * Based on glretrace_glx.cpp, which has * * Copyright 2011 Jose Fonseca * * 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. * **************************************************************************/ #include "glproc.hpp" #include "retrace.hpp" #include "glretrace.hpp" #include "os.hpp" #include "eglsize.hpp" #ifndef EGL_OPENGL_ES_API #define EGL_OPENGL_ES_API 0x30A0 #define EGL_OPENVG_API 0x30A1 #define EGL_OPENGL_API 0x30A2 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #endif using namespace glretrace; typedef std::map DrawableMap; typedef std::map ContextMap; typedef std::map ProfileMap; static DrawableMap drawable_map; static ContextMap context_map; static ProfileMap profile_map; /* FIXME: This should be tracked per thread. */ static unsigned int current_api = EGL_OPENGL_ES_API; /* * FIXME: Ideally we would defer the context creation until the profile was * clear, as explained in https://github.com/apitrace/apitrace/issues/197 , * instead of guessing. For now, start with a guess of ES2 profile, which * should be the most common case for EGL. */ static glfeatures::Profile last_profile(glfeatures::API_GLES, 2, 0); static glws::Drawable *null_drawable = NULL; static void createDrawable(unsigned long long orig_config, unsigned long long orig_surface); static glws::Drawable * getDrawable(unsigned long long surface_ptr) { if (surface_ptr == 0) { return NULL; } DrawableMap::const_iterator it; it = drawable_map.find(surface_ptr); if (it == drawable_map.end()) { // In Fennec we get the egl window surface from Java which isn't // traced, so just create a drawable if it doesn't exist in here createDrawable(0, surface_ptr); it = drawable_map.find(surface_ptr); assert(it != drawable_map.end()); } return (it != drawable_map.end()) ? it->second : NULL; } static Context * getContext(unsigned long long context_ptr) { if (context_ptr == 0) { return NULL; } ContextMap::const_iterator it; it = context_map.find(context_ptr); return (it != context_map.end()) ? it->second : NULL; } static void createDrawable(unsigned long long orig_config, unsigned long long orig_surface) { ProfileMap::iterator it = profile_map.find(orig_config); glfeatures::Profile profile; // If the requested config is associated with a profile, use that // profile. Otherwise, assume that the last used profile is what // the user wants. if (it != profile_map.end()) { profile = it->second; } else { profile = last_profile; } glws::Drawable *drawable = glretrace::createDrawable(profile); drawable_map[orig_surface] = drawable; } static void retrace_eglChooseConfig(trace::Call &call) { if (!call.ret->toSInt()) { return; } trace::Array *attrib_array = call.arg(1).toArray(); trace::Array *config_array = call.arg(2).toArray(); trace::Array *num_config_ptr = call.arg(4).toArray(); if (!attrib_array || !config_array || !num_config_ptr) { return; } glfeatures::Profile profile; unsigned renderableType = parseAttrib(attrib_array, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT); std::cerr << "renderableType = " << renderableType << "\n"; if (renderableType & EGL_OPENGL_BIT) { profile = glfeatures::Profile(glfeatures::API_GL, 1, 0); } else { profile.api = glfeatures::API_GLES; if (renderableType & EGL_OPENGL_ES3_BIT) { profile.major = 3; } else if (renderableType & EGL_OPENGL_ES2_BIT) { profile.major = 2; } else { profile.major = 1; } } unsigned num_config = num_config_ptr->values[0]->toUInt(); for (unsigned i = 0; i < num_config; ++i) { unsigned long long orig_config = config_array->values[i]->toUIntPtr(); profile_map[orig_config] = profile; } } static void retrace_eglCreateWindowSurface(trace::Call &call) { unsigned long long orig_config = call.arg(1).toUIntPtr(); unsigned long long orig_surface = call.ret->toUIntPtr(); createDrawable(orig_config, orig_surface); } static void retrace_eglCreatePbufferSurface(trace::Call &call) { unsigned long long orig_config = call.arg(1).toUIntPtr(); unsigned long long orig_surface = call.ret->toUIntPtr(); createDrawable(orig_config, orig_surface); // TODO: Respect the pbuffer dimensions too } static void retrace_eglDestroySurface(trace::Call &call) { unsigned long long orig_surface = call.arg(1).toUIntPtr(); DrawableMap::iterator it; it = drawable_map.find(orig_surface); if (it != drawable_map.end()) { glretrace::Context *currentContext = glretrace::getCurrentContext(); if (!currentContext || it->second != currentContext->drawable) { // TODO: reference count delete it->second; } drawable_map.erase(it); } } static void retrace_eglBindAPI(trace::Call &call) { if (!call.ret->toBool()) { return; } current_api = call.arg(0).toUInt(); } static void retrace_eglCreateContext(trace::Call &call) { unsigned long long orig_context = call.ret->toUIntPtr(); unsigned long long orig_config = call.arg(1).toUIntPtr(); Context *share_context = getContext(call.arg(2).toUIntPtr()); trace::Array *attrib_array = call.arg(3).toArray(); glfeatures::Profile profile; switch (current_api) { case EGL_OPENGL_API: profile.api = glfeatures::API_GL; profile.major = parseAttrib(attrib_array, EGL_CONTEXT_MAJOR_VERSION, 1); profile.minor = parseAttrib(attrib_array, EGL_CONTEXT_MINOR_VERSION, 0); if (profile.versionGreaterOrEqual(3,2)) { int profileMask = parseAttrib(attrib_array, EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR); if (profileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR) { profile.core = true; } int contextFlags = parseAttrib(attrib_array, EGL_CONTEXT_FLAGS_KHR, 0); if (contextFlags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) { profile.forwardCompatible = true; } } break; case EGL_OPENGL_ES_API: default: profile.api = glfeatures::API_GLES; profile.major = parseAttrib(attrib_array, EGL_CONTEXT_MAJOR_VERSION, 1); profile.minor = parseAttrib(attrib_array, EGL_CONTEXT_MINOR_VERSION, 0); break; } Context *context = glretrace::createContext(share_context, profile); assert(context); context_map[orig_context] = context; profile_map[orig_config] = profile; last_profile = profile; } static void retrace_eglDestroyContext(trace::Call &call) { unsigned long long orig_context = call.arg(1).toUIntPtr(); ContextMap::iterator it; it = context_map.find(orig_context); if (it != context_map.end()) { glretrace::Context *currentContext = glretrace::getCurrentContext(); if (it->second != currentContext) { // TODO: reference count it->second->release(); } context_map.erase(it); } } static void retrace_eglMakeCurrent(trace::Call &call) { if (!call.ret->toSInt()) { // Previously current rendering context and surfaces (if any) remain // unchanged. return; } glws::Drawable *new_drawable = getDrawable(call.arg(1).toUIntPtr()); Context *new_context = getContext(call.arg(3).toUIntPtr()); // Try to support GL_OES_surfaceless_context by creating a dummy drawable. if (new_context && !new_drawable) { if (!null_drawable) { null_drawable = glretrace::createDrawable(last_profile); } new_drawable = null_drawable; } glretrace::makeCurrent(call, new_drawable, new_context); } static void retrace_eglSwapBuffers(trace::Call &call) { glws::Drawable *drawable = getDrawable(call.arg(1).toUIntPtr()); frame_complete(call); if (retrace::doubleBuffer) { if (drawable) { drawable->swapBuffers(); } } else { glFlush(); } } const retrace::Entry glretrace::egl_callbacks[] = { {"eglGetError", &retrace::ignore}, {"eglGetDisplay", &retrace::ignore}, {"eglInitialize", &retrace::ignore}, {"eglTerminate", &retrace::ignore}, {"eglQueryString", &retrace::ignore}, {"eglGetConfigs", &retrace::ignore}, {"eglChooseConfig", &retrace_eglChooseConfig}, {"eglGetConfigAttrib", &retrace::ignore}, {"eglCreateWindowSurface", &retrace_eglCreateWindowSurface}, {"eglCreatePbufferSurface", &retrace_eglCreatePbufferSurface}, //{"eglCreatePixmapSurface", &retrace::ignore}, {"eglDestroySurface", &retrace_eglDestroySurface}, {"eglQuerySurface", &retrace::ignore}, {"eglBindAPI", &retrace_eglBindAPI}, {"eglQueryAPI", &retrace::ignore}, //{"eglWaitClient", &retrace::ignore}, //{"eglReleaseThread", &retrace::ignore}, //{"eglCreatePbufferFromClientBuffer", &retrace::ignore}, //{"eglSurfaceAttrib", &retrace::ignore}, //{"eglBindTexImage", &retrace::ignore}, //{"eglReleaseTexImage", &retrace::ignore}, {"eglSwapInterval", &retrace::ignore}, {"eglCreateContext", &retrace_eglCreateContext}, {"eglDestroyContext", &retrace_eglDestroyContext}, {"eglMakeCurrent", &retrace_eglMakeCurrent}, {"eglGetCurrentContext", &retrace::ignore}, {"eglGetCurrentSurface", &retrace::ignore}, {"eglGetCurrentDisplay", &retrace::ignore}, {"eglQueryContext", &retrace::ignore}, {"eglWaitGL", &retrace::ignore}, {"eglWaitNative", &retrace::ignore}, {"eglReleaseThread", &retrace::ignore}, {"eglSwapBuffers", &retrace_eglSwapBuffers}, {"eglSwapBuffersWithDamageEXT", &retrace_eglSwapBuffers}, // ignores additional params {"eglSwapBuffersWithDamageKHR", &retrace_eglSwapBuffers}, // ignores additional params //{"eglCopyBuffers", &retrace::ignore}, {"eglGetProcAddress", &retrace::ignore}, {"eglCreateImageKHR", &retrace::ignore}, {"eglDestroyImageKHR", &retrace::ignore}, {"glEGLImageTargetTexture2DOES", &retrace::ignore}, {"eglGetSyncValuesCHROMIUM", &retrace::ignore}, {NULL, NULL}, };