def traceFunctionImplBody(self, function): GlTracer.traceFunctionImplBody(self, function) if function.name == 'eglCreateContext': print ' if (_result != EGL_NO_CONTEXT)' print ' gltrace::createContext((uintptr_t)_result);' if function.name == 'eglMakeCurrent': print ' if (_result) {' print ' // update the profile' print ' if (ctx != EGL_NO_CONTEXT) {' print ' EGLint api = EGL_OPENGL_ES_API, version = 1;' print ' gltrace::setContext((uintptr_t)ctx);' print ' gltrace::Context *tr = gltrace::getContext();' print ' _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_TYPE, &api);' print ' _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &version);' print ' if (api == EGL_OPENGL_API)' print ' tr->profile = gltrace::PROFILE_COMPAT;' print ' else if (version == 1)' print ' tr->profile = gltrace::PROFILE_ES1;' print ' else' print ' tr->profile = gltrace::PROFILE_ES2;' print ' } else {' print ' gltrace::clearContext();' print ' }' print ' }' if function.name == 'eglDestroyContext': print ' if (_result) {' print ' gltrace::releaseContext((uintptr_t)ctx);' print ' }'
def traceFunctionImplBody(self, function): GlTracer.traceFunctionImplBody(self, function) if function.name == 'eglCreateContext': print ' if (_result != EGL_NO_CONTEXT)' print ' gltrace::createContext((uintptr_t)_result);' if function.name == 'eglMakeCurrent': print r' if (_result) {' print r' // update the profile' print r' if (ctx != EGL_NO_CONTEXT) {' print r' gltrace::setContext((uintptr_t)ctx);' print r' gltrace::Context *tr = gltrace::getContext();' print r' EGLint api = EGL_OPENGL_ES_API;' print r' _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_TYPE, &api);' print r' if (api == EGL_OPENGL_API) {' print r' assert(tr->profile.api == glfeatures::API_GL);' print r' } else if (api == EGL_OPENGL_ES_API) {' print r' EGLint client_version = 1;' print r' _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &client_version);' print r' if (tr->profile.api != glfeatures::API_GLES ||' print r' tr->profile.major < client_version) {' print r' std::string version = tr->profile.str();' print r' os::log("apitrace: warning: eglMakeCurrent: expected OpenGL ES %i.x context, but got %s\n",' print r' client_version, version.c_str());' print r' }' print r' } else {' print r' assert(0);' print r' }' print r' } else {' print r' gltrace::clearContext();' print r' }' print r' }' if function.name == 'eglDestroyContext': print ' if (_result) {' print ' gltrace::releaseContext((uintptr_t)ctx);' print ' }' if function.name == 'glEGLImageTargetTexture2DOES': print ' image_info *info = _EGLImageKHR_get_image_info(target, image);' print ' if (info) {' print ' GLint level = 0;' print ' GLint internalformat = info->internalformat;' print ' GLsizei width = info->width;' print ' GLsizei height = info->height;' print ' GLint border = 0;' print ' GLenum format = info->format;' print ' GLenum type = info->type;' print ' const GLvoid * pixels = info->pixels;' self.emitFakeTexture2D() print ' _EGLImageKHR_free_image_info(info);' print ' }'
def traceFunctionImplBody(self, function): GlTracer.traceFunctionImplBody(self, function) if function.name == 'eglCreateContext': print ' if (_result != EGL_NO_CONTEXT)' print ' gltrace::createContext((uintptr_t)_result);' if function.name == 'eglMakeCurrent': print ' if (_result) {' print ' // update the profile' print ' if (ctx != EGL_NO_CONTEXT) {' print ' gltrace::setContext((uintptr_t)ctx);' print '#ifndef NDEBUG' print ' gltrace::Context *tr = gltrace::getContext();' print ' EGLint api = EGL_OPENGL_ES_API;' print ' _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_TYPE, &api);' print ' if (api == EGL_OPENGL_API) {' print ' assert(tr->profile.api == glprofile::API_GL);' print ' } else if (api == EGL_OPENGL_ES_API) {' print ' assert(tr->profile.api == glprofile::API_GLES);' print ' EGLint version = 1;' print ' _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &version);' print ' assert(tr->profile.major >= version);' print ' } else {' print ' assert(0);' print ' }' print '#endif' print ' } else {' print ' gltrace::clearContext();' print ' }' print ' }' if function.name == 'eglDestroyContext': print ' if (_result) {' print ' gltrace::releaseContext((uintptr_t)ctx);' print ' }' if function.name == 'glEGLImageTargetTexture2DOES': print ' image_info *info = _EGLImageKHR_get_image_info(target, image);' print ' if (info) {' print ' GLint level = 0;' print ' GLint internalformat = info->internalformat;' print ' GLsizei width = info->width;' print ' GLsizei height = info->height;' print ' GLint border = 0;' print ' GLenum format = info->format;' print ' GLenum type = info->type;' print ' const GLvoid * pixels = info->pixels;' self.emitFakeTexture2D() print ' _EGLImageKHR_free_image_info(info);' print ' }'
def traceFunctionImplBody(self, function): GlTracer.traceFunctionImplBody(self, function) if function.name == 'eglMakeCurrent': print ' // update the profile' print ' if (ctx != EGL_NO_CONTEXT) {' print ' EGLint api = EGL_OPENGL_ES_API, version = 1;' print ' gltrace::Context *tr = gltrace::getContext();' print ' __eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_TYPE, &api);' print ' __eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &version);' print ' if (api == EGL_OPENGL_API)' print ' tr->profile = gltrace::PROFILE_COMPAT;' print ' else if (version == 1)' print ' tr->profile = gltrace::PROFILE_ES1;' print ' else' print ' tr->profile = gltrace::PROFILE_ES2;' print ' }'
def traceFunctionImplBody(self, function): GlTracer.traceFunctionImplBody(self, function) if function.name == "eglCreateContext": print " if (_result != EGL_NO_CONTEXT)" print " gltrace::createContext((uintptr_t)_result);" if function.name == "eglMakeCurrent": print " if (_result) {" print " // update the profile" print " if (ctx != EGL_NO_CONTEXT) {" print " EGLint api = EGL_OPENGL_ES_API, version = 1;" print " gltrace::setContext((uintptr_t)ctx);" print " gltrace::Context *tr = gltrace::getContext();" print " _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_TYPE, &api);" print " _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &version);" print " if (api == EGL_OPENGL_API)" print " tr->profile = gltrace::PROFILE_COMPAT;" print " else if (version == 1)" print " tr->profile = gltrace::PROFILE_ES1;" print " else" print " tr->profile = gltrace::PROFILE_ES2;" print " } else {" print " gltrace::clearContext();" print " }" print " }" if function.name == "eglDestroyContext": print " if (_result) {" print " gltrace::releaseContext((uintptr_t)ctx);" print " }" if function.name == "glEGLImageTargetTexture2DOES": print " image_info *info = _EGLImageKHR_get_image_info(target, image);" print " if (info) {" print " GLint level = 0;" print " GLint internalformat = info->internalformat;" print " GLsizei width = info->width;" print " GLsizei height = info->height;" print " GLint border = 0;" print " GLenum format = info->format;" print " GLenum type = info->type;" print " const GLvoid * pixels = info->pixels;" self.emitFakeTexture2D() print " _EGLImageKHR_free_image_info(info);" print " }"
def traceFunctionImplBody(self, function): if function.name in self.destroyContextFunctionNames: print ' gltrace::releaseContext((uintptr_t)ctx);' GlTracer.traceFunctionImplBody(self, function) if function.name in self.createContextFunctionNames: print ' if (_result != NULL)' print ' gltrace::createContext((uintptr_t)_result);' if function.name in self.makeCurrentFunctionNames: print ' if (_result) {' print ' if (ctx != NULL)' print ' gltrace::setContext((uintptr_t)ctx);' print ' else' print ' gltrace::clearContext();' print ' }'
def traceFunctionImplBody(self, function): GlTracer.traceFunctionImplBody(self, function) if function.name == "eglMakeCurrent": print " // update the profile" print " if (ctx != EGL_NO_CONTEXT) {" print " EGLint api = EGL_OPENGL_ES_API, version = 1;" print " gltrace::Context *tr = gltrace::getContext();" print " _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_TYPE, &api);" print " _eglQueryContext(dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &version);" print " if (api == EGL_OPENGL_API)" print " tr->profile = gltrace::PROFILE_COMPAT;" print " else if (version == 1)" print " tr->profile = gltrace::PROFILE_ES1;" print " else" print " tr->profile = gltrace::PROFILE_ES2;" print " }"
def traceFunctionImplBody(self, function): if function.name.startswith('wgl'): # When implementing WGL extensions OpenGL ICDs often have no # alternative to calling back into OPENGL32.DLL's wgl* entry points # due to lack of extensibility in the ICD interface. These # internal calls are not only visually confusing but can actually # cause problems when tracing, and replaying. A particularly nasty # case is wglCreateContextAttribsARB which ends up calling # wglCreateContext/wglCreateLayerContext to obtain a HGLRC that's # recognizable by OPENGL32.DLL. Therefore we need to detect and # dispatch internal calls, without further ado. print r' if (_reentrant) {' self.invokeFunction(function) if function.type is not Void: print ' return _result;' print r' }' print r' ReentryScope _reentry;' print r' (void)_reentry;' print if function.name in self.destroyContextFunctionNames: # Unlike other GL APIs like EGL or GLX, WGL will make the context # inactive if it's currently the active context. print ' if (_wglGetCurrentContext() == hglrc) {' print ' gltrace::clearContext();' print ' }' print ' gltrace::releaseContext((uintptr_t)hglrc);' GlTracer.traceFunctionImplBody(self, function) if function.name in self.createContextFunctionNames: print ' if (_result)' print ' gltrace::createContext((uintptr_t)_result);' if function.name in self.makeCurrentFunctionNames: print ' if (_result) {' print ' if (hglrc != NULL)' print ' gltrace::setContext((uintptr_t)hglrc);' print ' else' print ' gltrace::clearContext();' print ' }'
def traceFunctionImplBody(self, function): if function.name == 'CGLReleaseContext': # Unlike other GL APIs like EGL or GLX, CGL will make the context # not current if it's the current context. print(' if (_CGLGetContextRetainCount(ctx) == 1) {') print(' if (gltrace::releaseContext((uintptr_t)ctx)) {') print(' if (_CGLGetCurrentContext() == ctx) {') print(' gltrace::clearContext();') print(' }') print(' }') print(' }') if function.name == 'CGLDestroyContext': # The same rule applies here about the as for CGLReleaseContext. print(' if (gltrace::releaseContext((uintptr_t)ctx)) {') print(' if (_CGLGetCurrentContext() == ctx) {') print(' gltrace::clearContext();') print(' }') print(' }') GlTracer.traceFunctionImplBody(self, function) if function.name == 'CGLCreateContext': print(' if (_result == kCGLNoError) {') print( ' gltrace::createContext((uintptr_t)*ctx, (uintptr_t)share);' ) print(' }') if function.name == 'CGLSetCurrentContext': print(' if (_result == kCGLNoError) {') print(' if (ctx != NULL) {') print(' gltrace::setContext((uintptr_t)ctx);') print(' } else {') print(' gltrace::clearContext();') print(' }') print(' }') if function.name == 'CGLRetainContext': print(' gltrace::retainContext((uintptr_t)ctx);')
def traceFunctionImplBody(self, function): if function.name in self.destroyContextFunctionNames: # Unlike other GL APIs like EGL or GLX, WGL will make the context # inactive if it's currently the active context. print ' if (_wglGetCurrentContext() == hglrc) {' print ' gltrace::clearContext();' print ' }' print ' gltrace::releaseContext((uintptr_t)hglrc);' GlTracer.traceFunctionImplBody(self, function) if function.name in self.createContextFunctionNames: print ' if (_result)' print ' gltrace::createContext((uintptr_t)_result);' if function.name in self.makeCurrentFunctionNames: print ' if (_result) {' print ' if (hglrc != NULL)' print ' gltrace::setContext((uintptr_t)hglrc);' print ' else' print ' gltrace::clearContext();' print ' }'
def traceFunctionImplBody(self, function): if function.name == "CGLReleaseContext": # Unlike other GL APIs like EGL or GLX, CGL will make the context # not current if it's the current context. print " if (_CGLGetContextRetainCount(ctx) == 1) {" print " if (gltrace::releaseContext((uintptr_t)ctx)) {" print " if (_CGLGetCurrentContext() == ctx) {" print " gltrace::clearContext();" print " }" print " }" print " }" if function.name == "CGLDestroyContext": # The same rule applies here about the as for CGLReleaseContext. print " if (gltrace::releaseContext((uintptr_t)ctx)) {" print " if (_CGLGetCurrentContext() == ctx) {" print " gltrace::clearContext();" print " }" print " }" GlTracer.traceFunctionImplBody(self, function) if function.name == "CGLCreateContext": print " if (_result == kCGLNoError) {" print " gltrace::createContext((uintptr_t)*ctx);" print " }" if function.name == "CGLSetCurrentContext": print " if (_result == kCGLNoError) {" print " if (ctx != NULL) {" print " gltrace::setContext((uintptr_t)ctx);" print " } else {" print " gltrace::clearContext();" print " }" print " }" if function.name == "CGLRetainContext": print " gltrace::retainContext((uintptr_t)ctx);"
def traceFunctionImplBody(self, function): if function.name.startswith('wgl'): # When implementing WGL extensions OpenGL ICDs often have no # alternative to calling back into OPENGL32.DLL's wgl* entry points # due to lack of extensibility in the ICD interface. These # internal calls are not only visually confusing but can actually # cause problems when tracing, and replaying. A particularly nasty # case is wglCreateContextAttribsARB which ends up calling # wglCreateContext/wglCreateLayerContext to obtain a HGLRC that's # recognizable by OPENGL32.DLL. Therefore we need to detect and # dispatch internal calls, without further ado. print r' if (_reentrant) {' self.invokeFunction(function) if function.type is not Void: print ' return _result;' print r' }' print r' ReentryScope _reentry;' print r' (void)_reentry;' print if function.name in self.destroyContextFunctionNames: # Unlike other GL APIs like EGL or GLX, WGL will make the context # inactive if it's currently the active context. print ' if (_wglGetCurrentContext() == hglrc) {' print ' gltrace::clearContext();' print ' }' print ' gltrace::releaseContext((uintptr_t)hglrc);' # Emit fake glBitmap calls in the trace on wglUseFontBitmapsA. # This enables to capture the real bitmaps and replay them outside Windows. # # XXX: In spite what # https://msdn.microsoft.com/en-us/library/windows/desktop/dd374392.aspx # implies, GetGlyphOutline(GGO_BITMAP) does not seem to work with # certain fonts. The only solution is to draw the font charactors with # a HBITMAP like the old Mesa fxwgl.c code used to do. That too, seems # to be the way that opengl32.dll implements wglUseFontBitmaps. # if function.name == 'wglUseFontBitmapsA': self.invokeFunction(function) # Emit a fake string marker with the original call print r' {' print r' std::ostringstream ss;' print r' ss << __FUNCTION__ << "(hdc = " << hdc << ", first = " << first << ", count = " << count << ", listBase = " << listBase << ") = " << (_result ? "TRUE" : "FALSE");' print r' _fakeStringMarker(ss.str());' print r' }' print glNewList = glapi.getFunctionByName('glNewList') glBitmap = glapi.getFunctionByName('glBitmap') glEndList = glapi.getFunctionByName('glEndList') print r' if (_result) {' print r' HFONT hFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);' print r' assert (hFont != nullptr);' print r' LOGFONT lf;' print r' if (GetObject(hFont, sizeof lf, &lf) != 0) {' print r' std::ostringstream ss;' print r' ss << "lfFaceName = " << lf.lfFaceName' print r' << ", lfHeight = " << lf.lfHeight' print r' << ", lfWeight = " << lf.lfWeight;' print r' if (lf.lfItalic) ss << ", lfItalic = 1";' print r' if (lf.lfUnderline) ss << ", lfUnderline = 1";' print r' if (lf.lfStrikeOut) ss << ", lfStrikeOut = 1";' print r' _fakeStringMarker(ss.str());' print r' }' print print r' BOOL bRet;' print r' TEXTMETRIC tm;' print r' bRet = GetTextMetricsA(hdc, &tm);' print r' assert(bRet);' print print r' HDC memDC = CreateCompatibleDC(hdc);' print print r' SetMapMode(memDC, MM_TEXT);' print r' SetTextAlign(memDC, TA_BASELINE);' print r' SetBkColor(memDC, RGB(0, 0, 0));' print r' SetBkMode(memDC, OPAQUE);' print r' SetTextColor(memDC, RGB(255,255,255));' print print r' BITMAPINFO * bmi = (BITMAPINFO *)malloc(offsetof(BITMAPINFO, bmiColors[2]));' print r' ZeroMemory(&bmi->bmiHeader, sizeof bmi->bmiHeader);' print r' bmi->bmiHeader.biSize = sizeof bmi->bmiHeader;' print r' bmi->bmiHeader.biPlanes = 1;' print r' bmi->bmiHeader.biBitCount = 1;' print r' bmi->bmiHeader.biCompression = BI_RGB;' print r' bmi->bmiColors[0].rgbBlue = 0;' print r' bmi->bmiColors[0].rgbGreen = 0;' print r' bmi->bmiColors[0].rgbRed = 0;' print r' bmi->bmiColors[0].rgbReserved = 0;' print r' bmi->bmiColors[1].rgbBlue = 255;' print r' bmi->bmiColors[1].rgbGreen = 255;' print r' bmi->bmiColors[1].rgbRed = 255;' print r' bmi->bmiColors[1].rgbReserved = 0;' print print r' for (DWORD i = 0; i < count; ++i) {' print r' _fake_glNewList(listBase + i, GL_COMPILE);' print print r' char cChar = first + i;' print print r' // TODO: Use GetCharABSWidths' print r' // http://www.codeproject.com/Articles/14915/Width-of-text-in-italic-font' print r' // https://support.microsoft.com/en-us/kb/94646' print r' SIZE size;' print r' bRet = GetTextExtentPoint32A(hdc, &cChar, 1, &size);' print r' if (bRet) {' print r' assert(size.cx >= 0);' print r' assert(size.cy >= 0);' print print r' // Round width to 32 pixels' print r' int nWidth = (size.cx + 0x1f) & ~0x1f;' print r' int nHeight = size.cy;' print print r' DWORD dwBytes = nWidth / 8 * nHeight;' print r' LPVOID lpvBits = NULL;' print r' if (dwBytes) {' print r' lpvBits = malloc(dwBytes);' print print r' HBITMAP memBM = CreateCompatibleBitmap(memDC, nWidth, nHeight);' print print r' HGDIOBJ origBM = SelectObject(memDC, memBM);' print print r' PatBlt(memDC, 0, 0, nWidth, nHeight, BLACKNESS);' print r' SelectObject(memDC, hFont);' print print r' bmi->bmiHeader.biWidth = nWidth;' print r' bmi->bmiHeader.biHeight = nHeight;' print print r' bRet = TextOutA(memDC, 0, tm.tmAscent, &cChar, 1);' print r' assert(bRet);' print print r' SelectObject(memDC, origBM);' print print r' int nScanLines = GetDIBits(memDC, memBM, 0, nHeight, lpvBits, bmi, DIB_RGB_COLORS);' print r' assert(nScanLines == nHeight);' print print r' DeleteObject(memBM);' print r' }' print print r' GLsizei width = nWidth;' print r' GLfloat height = nHeight;' print r' GLfloat xorig = 0;' print r' GLfloat yorig = tm.tmDescent;' print r' GLfloat xmove = size.cx;' print r' GLfloat ymove = 0;' print r' const GLubyte *bitmap = (const GLubyte *)lpvBits;' print r' if (bitmap == NULL) {' print r' // We still need to emit an empty glBitmap for empty characters like spaces;' print r' width = height = xorig = yorig = 0;' print r' }' # FIXME: glPixelStorei(GL_UNPACK_ROW_LENGTH, width); # FIXME: glPixelStorei(GL_UNPACK_ALIGNMENT, 4); print print r' _fake_glBitmap(width, height, xorig, yorig, xmove, ymove, bitmap);' print print r' free(lpvBits);' print r' }' print r' _fake_glEndList();' print r' }' print print r' DeleteDC(memDC);' print r' free(bmi);' print r' } // _result' return GlTracer.traceFunctionImplBody(self, function) if function.name in self.createContextFunctionNames: print ' if (_result)' print ' gltrace::createContext((uintptr_t)_result);' if function.name in self.makeCurrentFunctionNames: print ' if (_result) {' print ' if (hglrc != NULL)' print ' gltrace::setContext((uintptr_t)hglrc);' print ' else' print ' gltrace::clearContext();' print ' }'
def traceFunctionImplBody(self, function): if function.name in self.destroyContextFunctionNames: print ' gltrace::releaseContext((uintptr_t)ctx);' GlTracer.traceFunctionImplBody(self, function) if function.name in self.createContextFunctionNames: print ' if (_result != NULL)' print ' gltrace::createContext((uintptr_t)_result);' if function.name in self.makeCurrentFunctionNames: print ' if (_result) {' print ' if (ctx != NULL)' print ' gltrace::setContext((uintptr_t)ctx);' print ' else' print ' gltrace::clearContext();' print ' }' if function.name == 'glXBindTexImageEXT': # FIXME: glXBindTexImageEXT gets called frequently, so we should # avoid recording the same data over and over again somehow, e.g.: # - get the pixels before and after glXBindTexImageEXT, and only # emit emitFakeTexture2D when it changes # - keep a global hash of the pixels # FIXME: Handle mipmaps print r''' unsigned glx_target = 0; _glXQueryDrawable(display, drawable, GLX_TEXTURE_TARGET_EXT, &glx_target); GLenum target; switch (glx_target) { // FIXME //case GLX_TEXTURE_1D_EXT: // target = GL_TEXTURE_1D; // break; case GLX_TEXTURE_2D_EXT: target = GL_TEXTURE_2D; break; case GLX_TEXTURE_RECTANGLE_EXT: target = GL_TEXTURE_RECTANGLE; break; default: os::log("apitrace: warning: %s: unsupported GLX_TEXTURE_TARGET_EXT 0x%u\n", __FUNCTION__, glx_target); target = GL_NONE; break; } GLint level = 0; GLint internalformat = GL_NONE; _glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &internalformat); // XXX: GL_TEXTURE_INTERNAL_FORMAT cannot be trusted on NVIDIA // -- it sometimes returns GL_BGRA, even though GL_BGR/BGRA is // not a valid internal format. switch (internalformat) { case GL_BGR: internalformat = GL_RGB; break; case GL_BGRA: internalformat = GL_RGBA; break; } GLint width = 0; _glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); GLint height = 0; _glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); GLint border = 0; // XXX: We always use GL_RGBA format to read the pixels because: // - some implementations (Mesa) seem to return bogus results // for GLX_TEXTURE_FORMAT_EXT // - hardware usually stores GL_RGB with 32bpp, so it should be // faster to read/write // - it is more robust against GL_(UN)PACK_ALIGNMENT state // changes // The drawback is that traces will be slightly bigger. GLenum format = GL_RGBA; GLenum type = GL_UNSIGNED_BYTE; if (target && internalformat && height && width) { // FIXME: This assumes (UN)PACK state (in particular // GL_(UN)PACK_ROW_LENGTH) is set to its defaults. We // really should temporarily reset the state here (and emit // according fake calls) to cope when its not. At very // least we need a heads up warning that this will cause // problems. GLint alignment = 4; GLint row_stride = _align(width * 4, alignment); GLvoid * pixels = malloc(height * row_stride); _glGetTexImage(target, level, format, type, pixels); ''' self.emitFakeTexture2D() print r'''
def traceFunctionImplBody(self, function): if function.name.startswith('wgl'): # When implementing WGL extensions OpenGL ICDs often have no # alternative to calling back into OPENGL32.DLL's wgl* entry points # due to lack of extensibility in the ICD interface. These # internal calls are not only visually confusing but can actually # cause problems when tracing, and replaying. A particularly nasty # case is wglCreateContextAttribsARB which ends up calling # wglCreateContext/wglCreateLayerContext to obtain a HGLRC that's # recognizable by OPENGL32.DLL. Therefore we need to detect and # dispatch internal calls, without further ado. print(r' if (_reentrant) {') self.invokeFunction(function) if function.type is not Void: print(' return _result;') print(r' }') print(r' ReentryScope _reentry;') print(r' (void)_reentry;') print() if function.name in self.destroyContextFunctionNames: # Unlike other GL APIs like EGL or GLX, WGL will make the context # inactive if it's currently the active context. print(' if (_wglGetCurrentContext() == hglrc) {') print(' gltrace::clearContext();') print(' }') print(' gltrace::releaseContext((uintptr_t)hglrc);') # Emit a fake string marker describing the current GDI font if function.name.startswith('wglUseFont'): print( r' HFONT hFont = static_cast<HFONT>(GetCurrentObject(hdc, OBJ_FONT));' ) print(r' if (hFont != nullptr) {') print(r' LOGFONT lf;') print(r' if (GetObject(hFont, sizeof lf, &lf) != 0) {') print(r' std::ostringstream ss;') print(r' ss << "lfFaceName = " << lf.lfFaceName') print(r' << ", lfHeight = " << lf.lfHeight') print(r' << ", lfWeight = " << lf.lfWeight;') print(r' if (lf.lfItalic) ss << ", lfItalic = 1";') print( r' if (lf.lfUnderline) ss << ", lfUnderline = 1";') print( r' if (lf.lfStrikeOut) ss << ", lfStrikeOut = 1";') print(r' _fakeStringMarker(ss.str());') print(r' }') print(r' }') print() # Emit fake glBitmap calls in the trace on wglUseFontBitmapsA. # This enables to capture the real bitmaps and replay them outside Windows. # # XXX: In spite what # https://msdn.microsoft.com/en-us/library/windows/desktop/dd374392.aspx # implies, GetGlyphOutline(GGO_BITMAP) does not seem to work with # certain fonts. The only solution is to draw the font charactors with # a HBITMAP like the old Mesa fxwgl.c code used to do. That too, seems # to be the way that opengl32.dll implements wglUseFontBitmaps. # if function.name == 'wglUseFontBitmapsA': self.invokeFunction(function) # Emit a fake string marker with the original call print(r' {') print(r' std::ostringstream ss;') print( r' ss << __FUNCTION__ << "(hdc = " << hdc << ", first = " << first << ", count = " << count << ", listBase = " << listBase << ") = " << (_result ? "TRUE" : "FALSE");' ) print(r' _fakeStringMarker(ss.str());') print(r' }') print() glNewList = glapi.getFunctionByName('glNewList') glBitmap = glapi.getFunctionByName('glBitmap') glEndList = glapi.getFunctionByName('glEndList') print(r' if (_result) {') print(r' assert(hFont != nullptr);') print() print(r' BOOL bRet;') print(r' TEXTMETRIC tm;') print(r' bRet = GetTextMetricsA(hdc, &tm);') print(r' assert(bRet);') print() print(r' HDC memDC = CreateCompatibleDC(hdc);') print() print(r' SetMapMode(memDC, MM_TEXT);') print(r' SetTextAlign(memDC, TA_BASELINE);') print(r' SetBkColor(memDC, RGB(0, 0, 0));') print(r' SetBkMode(memDC, OPAQUE);') print(r' SetTextColor(memDC, RGB(255,255,255));') print() print( r' BITMAPINFO * bmi = (BITMAPINFO *)malloc(offsetof(BITMAPINFO, bmiColors[2]));' ) print( r' ZeroMemory(&bmi->bmiHeader, sizeof bmi->bmiHeader);') print(r' bmi->bmiHeader.biSize = sizeof bmi->bmiHeader;') print(r' bmi->bmiHeader.biPlanes = 1;') print(r' bmi->bmiHeader.biBitCount = 1;') print(r' bmi->bmiHeader.biCompression = BI_RGB;') print(r' bmi->bmiColors[0].rgbBlue = 0;') print(r' bmi->bmiColors[0].rgbGreen = 0;') print(r' bmi->bmiColors[0].rgbRed = 0;') print(r' bmi->bmiColors[0].rgbReserved = 0;') print(r' bmi->bmiColors[1].rgbBlue = 255;') print(r' bmi->bmiColors[1].rgbGreen = 255;') print(r' bmi->bmiColors[1].rgbRed = 255;') print(r' bmi->bmiColors[1].rgbReserved = 0;') print() print(r' for (DWORD i = 0; i < count; ++i) {') print(r' _fake_glNewList(listBase + i, GL_COMPILE);') print() print(r' char cChar = first + i;') print() print(r' // TODO: Use GetCharABSWidths') print( r' // http://www.codeproject.com/Articles/14915/Width-of-text-in-italic-font' ) print( r' // https://support.microsoft.com/en-us/kb/94646') print(r' SIZE size;') print( r' bRet = GetTextExtentPoint32A(hdc, &cChar, 1, &size);' ) print(r' if (bRet) {') print(r' assert(size.cx >= 0);') print(r' assert(size.cy >= 0);') print() print(r' // Round width to 32 pixels') print(r' int nWidth = (size.cx + 0x1f) & ~0x1f;') print(r' int nHeight = size.cy;') print() print(r' DWORD dwBytes = nWidth / 8 * nHeight;') print(r' LPVOID lpvBits = NULL;') print(r' if (dwBytes) {') print(r' lpvBits = malloc(dwBytes);') print() print( r' HBITMAP memBM = CreateCompatibleBitmap(memDC, nWidth, nHeight);' ) print() print( r' HGDIOBJ origBM = SelectObject(memDC, memBM);' ) print() print( r' PatBlt(memDC, 0, 0, nWidth, nHeight, BLACKNESS);' ) print(r' SelectObject(memDC, hFont);') print() print(r' bmi->bmiHeader.biWidth = nWidth;') print(r' bmi->bmiHeader.biHeight = nHeight;') print() print( r' bRet = TextOutA(memDC, 0, tm.tmAscent, &cChar, 1);' ) print(r' assert(bRet);') print() print(r' SelectObject(memDC, origBM);') print() print( r' int nScanLines = GetDIBits(memDC, memBM, 0, nHeight, lpvBits, bmi, DIB_RGB_COLORS);' ) print(r' assert(nScanLines == nHeight);') print() print(r' DeleteObject(memBM);') print(r' }') print() print(r' GLsizei width = nWidth;') print(r' GLfloat height = nHeight;') print(r' GLfloat xorig = 0;') print(r' GLfloat yorig = tm.tmDescent;') print(r' GLfloat xmove = size.cx;') print(r' GLfloat ymove = 0;') print( r' const GLubyte *bitmap = (const GLubyte *)lpvBits;' ) print(r' if (bitmap == NULL) {') print( r' // We still need to emit an empty glBitmap for empty characters like spaces;' ) print(r' width = height = xorig = yorig = 0;') print(r' }') # FIXME: glPixelStorei(GL_UNPACK_ROW_LENGTH, width); # FIXME: glPixelStorei(GL_UNPACK_ALIGNMENT, 4); print() print( r' _fake_glBitmap(width, height, xorig, yorig, xmove, ymove, bitmap);' ) print() print(r' free(lpvBits);') print(r' }') print(r' _fake_glEndList();') print(r' }') print() print(r' DeleteDC(memDC);') print(r' free(bmi);') print(r' } // _result') return GlTracer.traceFunctionImplBody(self, function) if function.name == 'wglCreateContextAttribsARB': print(' if (_result)') print( ' gltrace::createContext((uintptr_t)_result, (uintptr_t)hShareContext);' ) elif function.name in self.createContextFunctionNames: print(' if (_result)') print(' gltrace::createContext((uintptr_t)_result, 0);') if function.name in self.makeCurrentFunctionNames: print(' if (_result) {') print(' if (hglrc != NULL)') print(' gltrace::setContext((uintptr_t)hglrc);') print(' else') print(' gltrace::clearContext();') print(' }')
def traceFunctionImplBody(self, function): if function.name in self.destroyContextFunctionNames: print(' gltrace::releaseContext((uintptr_t)ctx);') GlTracer.traceFunctionImplBody(self, function) if function.name in self.createContextFunctionNames: print(' if (_result != NULL)') print(' gltrace::createContext((uintptr_t)_result);') if function.name in self.makeCurrentFunctionNames: print(' if (_result) {') print(' if (ctx != NULL)') print(' gltrace::setContext((uintptr_t)ctx);') print(' else') print(' gltrace::clearContext();') print(' }') if function.name == 'glXBindTexImageEXT': # FIXME: glXBindTexImageEXT gets called frequently, so we should # avoid recording the same data over and over again somehow, e.g.: # - get the pixels before and after glXBindTexImageEXT, and only # emit emitFakeTexture2D when it changes # - keep a global hash of the pixels # FIXME: Handle mipmaps print(r''' unsigned glx_target = 0; _glXQueryDrawable(display, drawable, GLX_TEXTURE_TARGET_EXT, &glx_target); GLenum target; switch (glx_target) { // FIXME //case GLX_TEXTURE_1D_EXT: // target = GL_TEXTURE_1D; // break; case GLX_TEXTURE_2D_EXT: target = GL_TEXTURE_2D; break; case GLX_TEXTURE_RECTANGLE_EXT: target = GL_TEXTURE_RECTANGLE; break; default: os::log("apitrace: warning: %s: unsupported GLX_TEXTURE_TARGET_EXT 0x%u\n", __FUNCTION__, glx_target); target = GL_NONE; break; } GLint level = 0; GLint internalformat = GL_NONE; _glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &internalformat); // XXX: GL_TEXTURE_INTERNAL_FORMAT cannot be trusted on NVIDIA // -- it sometimes returns GL_BGRA, even though GL_BGR/BGRA is // not a valid internal format. switch (internalformat) { case GL_BGR: internalformat = GL_RGB; break; case GL_BGRA: internalformat = GL_RGBA; break; } GLint width = 0; _glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); GLint height = 0; _glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); GLint border = 0; // XXX: We always use GL_RGBA format to read the pixels because: // - some implementations (Mesa) seem to return bogus results // for GLX_TEXTURE_FORMAT_EXT // - hardware usually stores GL_RGB with 32bpp, so it should be // faster to read/write // - it is more robust against GL_(UN)PACK_ALIGNMENT state // changes // The drawback is that traces will be slightly bigger. GLenum format = GL_RGBA; GLenum type = GL_UNSIGNED_BYTE; if (target && internalformat && height && width) { // FIXME: This assumes (UN)PACK state (in particular // GL_(UN)PACK_ROW_LENGTH) is set to its defaults. We // really should temporarily reset the state here (and emit // according fake calls) to cope when its not. At very // least we need a heads up warning that this will cause // problems. GLint alignment = 4; GLint row_stride = _align(width * 4, alignment); std::unique_ptr<GLbyte[]> data(new GLbyte[height * row_stride]); GLvoid *pixels = data.get(); _glGetTexImage(target, level, format, type, pixels); ''') self.emitFakeTexture2D() print(r''' } ''')