| ########################################################################## |
| # |
| # Copyright 2008-2009 VMware, Inc. |
| # 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. |
| # |
| ##########################################################################/ |
| |
| |
| """WGL tracing code generator.""" |
| |
| |
| from gltrace import GlTracer |
| from specs.stdapi import Module, API, Void |
| from specs.glapi import glapi |
| from specs.wglapi import wglapi |
| |
| |
| class WglTracer(GlTracer): |
| |
| getProcAddressFunctionNames = [ |
| "wglGetProcAddress", |
| ] |
| |
| createContextFunctionNames = [ |
| 'wglCreateContext', |
| 'wglCreateContextAttribsARB', |
| 'wglCreateLayerContext', |
| ] |
| |
| destroyContextFunctionNames = [ |
| 'wglDeleteContext', |
| ] |
| |
| makeCurrentFunctionNames = [ |
| 'wglMakeCurrent', |
| 'wglMakeContextCurrentARB', |
| 'wglMakeContextCurrentEXT', |
| ] |
| |
| 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(' }') |
| |
| |
| if __name__ == '__main__': |
| print() |
| print('#define _GDI32_') |
| print() |
| print('#include <string.h>') |
| print('#include <windows.h>') |
| print() |
| print('#include <sstream>') |
| print() |
| print('#include "trace_writer_local.hpp"') |
| print('#include "os.hpp"') |
| print() |
| print('// To validate our prototypes') |
| print('#define GL_GLEXT_PROTOTYPES') |
| print('#define WGL_GLXEXT_PROTOTYPES') |
| print() |
| print('#include "glproc.hpp"') |
| print('#include "glsize.hpp"') |
| print() |
| print('static OS_THREAD_LOCAL uintptr_t _reentrant;') |
| print() |
| print('// Helper class to track reentries in function scope.') |
| print('struct ReentryScope {') |
| print('inline ReentryScope() { _reentrant = 1; }') |
| print('inline ~ReentryScope() { _reentrant = 0; }') |
| print('};') |
| print() |
| module = Module() |
| module.mergeModule(glapi) |
| module.mergeModule(wglapi) |
| api = API() |
| api.addModule(module) |
| tracer = WglTracer() |
| tracer.traceApi(api) |