From: José Fonseca Date: Sat, 14 Apr 2012 16:22:57 +0000 (+0100) Subject: Move retracers to their own directory. X-Git-Url: https://git.cworth.org/git?p=apitrace;a=commitdiff_plain;h=9d27a54b0381610c30964880a5fdd4c27bb6e732 Move retracers to their own directory. --- diff --git a/.gitignore b/.gitignore index 25f71f6..93f029e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,8 +34,6 @@ dxsdk eglretrace glproc.hpp glretrace -glretrace_gl.cpp -glstate_params.cpp install_manifest.txt qapitrace traces diff --git a/CMakeLists.txt b/CMakeLists.txt index 855d73f..1d5c99f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,146 +304,8 @@ add_subdirectory (wrappers) ############################################################################## # API retracers -add_custom_command ( - OUTPUT glretrace_gl.cpp - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/glretrace.py > ${CMAKE_CURRENT_BINARY_DIR}/glretrace_gl.cpp - DEPENDS glretrace.py retrace.py specs/glapi.py specs/gltypes.py specs/stdapi.py -) - -add_custom_command ( - OUTPUT glstate_params.cpp - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/glstate_params.py > ${CMAKE_CURRENT_BINARY_DIR}/glstate_params.cpp - DEPENDS glstate_params.py specs/glparams.py specs/gltypes.py specs/stdapi.py -) - -add_library (retrace_common - glretrace_gl.cpp - glretrace_cgl.cpp - glretrace_glx.cpp - glretrace_wgl.cpp - glretrace_egl.cpp - glretrace_main.cpp - glstate.cpp - glstate_images.cpp - glstate_params.cpp - glstate_shaders.cpp - retrace.cpp - retrace_stdc.cpp - glws.cpp -) - -add_dependencies (retrace_common glproc) - -set_property ( - TARGET retrace_common - APPEND - PROPERTY COMPILE_DEFINITIONS "RETRACE" -) - -if (WIN32 OR APPLE OR X11_FOUND) - add_executable (glretrace - ${glws_os} - glproc_gl.cpp - ) - - add_dependencies (glretrace glproc) - - set_property ( - TARGET glretrace - APPEND - PROPERTY COMPILE_DEFINITIONS "RETRACE" - ) - - target_link_libraries (glretrace - retrace_common - common - ${PNG_LIBRARIES} - ${ZLIB_LIBRARIES} - ${SNAPPY_LIBRARIES} - ) +add_subdirectory (retrace) - if (WIN32) - else () - if (APPLE) - target_link_libraries (glretrace - "-framework Cocoa" - "-framework ApplicationServices" # CGS* - #"-framework OpenGL" # CGL* - ) - else () - target_link_libraries (glretrace ${X11_X11_LIB}) - endif () - - target_link_libraries (glretrace - # gdb doesn't like when pthreads is loaded through dlopen (which happens - # when dlopen'ing libGL), so link pthreads to avoid this issue. See also - # http://stackoverflow.com/questions/2702628/gdb-cannot-find-new-threads-generic-error - ${CMAKE_THREAD_LIBS_INIT} - dl - ) - - if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - target_link_libraries (glretrace rt) - endif () - - endif () - - install (TARGETS glretrace RUNTIME DESTINATION bin) -endif () - -if (ENABLE_EGL AND X11_FOUND AND NOT WIN32 AND NOT APPLE) - add_executable (eglretrace - glws_egl_xlib.cpp - glproc_egl.cpp - ) - - add_dependencies (eglretrace glproc) - - set_property ( - TARGET eglretrace - APPEND - PROPERTY COMPILE_DEFINITIONS "RETRACE" - ) - - target_link_libraries (eglretrace - retrace_common - common - ${PNG_LIBRARIES} - ${ZLIB_LIBRARIES} - ${SNAPPY_LIBRARIES} - ${X11_X11_LIB} - ${CMAKE_THREAD_LIBS_INIT} - dl - ) - - if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - target_link_libraries (eglretrace rt) - endif () - - install (TARGETS eglretrace RUNTIME DESTINATION bin) -endif () - -if (WIN32 AND DirectX_D3DX9_FOUND) - add_custom_command ( - OUTPUT d3dretrace_d3d9.cpp - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/d3dretrace.py > ${CMAKE_CURRENT_BINARY_DIR}/d3dretrace_d3d9.cpp - DEPENDS d3dretrace.py retrace.py specs/d3d9.py specs/d3d9types.py specs/d3d9caps.py specs/winapi.py specs/stdapi.py - ) - - include_directories (SYSTEM ${DirectX_D3DX9_INCLUDE_DIR}) - add_executable (d3dretrace - retrace.cpp - retrace_stdc.cpp - d3dretrace_main.cpp - d3dretrace_d3d9.cpp - ) - target_link_libraries (d3dretrace - common - ${ZLIB_LIBRARIES} - ${SNAPPY_LIBRARIES} - ${DirectX_D3D9_LIBRARY} - ) -endif () ############################################################################## # CLI diff --git a/d3dretrace.hpp b/d3dretrace.hpp deleted file mode 100644 index cac94cc..0000000 --- a/d3dretrace.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/************************************************************************** - * - * Copyright 2012 Jose Fonseca - * 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. - * - **************************************************************************/ - -#ifndef _D3DRETRACE_HPP_ -#define _D3DRETRACE_HPP_ - -#include "retrace.hpp" - - -namespace d3dretrace { - - -extern const retrace::Entry d3d9_callbacks[]; - - -} /* namespace d3dretrace */ - - -#endif /* _D3DRETRACE_HPP_ */ diff --git a/d3dretrace.py b/d3dretrace.py deleted file mode 100644 index a2ea75e..0000000 --- a/d3dretrace.py +++ /dev/null @@ -1,145 +0,0 @@ -########################################################################## -# -# Copyright 2011 Jose Fonseca -# 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. -# -##########################################################################/ - - -"""GL retracer generator.""" - - -import specs.stdapi as stdapi -from specs.d3d9 import d3d9 -from retrace import Retracer - - -class D3DRetracer(Retracer): - - table_name = 'd3dretrace::d3d9_callbacks' - - def invokeInterfaceMethod(self, interface, method): - if interface.name == 'IDirect3D9' and method.name == 'CreateDevice': - print 'HWND hWnd = createWindow(pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight);' - print 'pPresentationParameters->hDeviceWindow = hWnd;' - print 'hFocusWindow = hWnd;' - - Retracer.invokeInterfaceMethod(self, interface, method) - - if str(method.type) == 'HRESULT': - print r' if (__result != S_OK) {' - print r' retrace::warning(call) << "failed\n";' - print r' }' - - if interface.name == 'IDirect3DVertexBuffer9' and method.name == 'Lock': - print ' if (!SizeToLock) {' - print ' D3DVERTEXBUFFER_DESC Desc;' - print ' _this->GetDesc(&Desc);' - print ' SizeToLock = Desc.Size;' - print ' }' - - -if __name__ == '__main__': - print r''' -#include - -#include - -#include "d3d9imports.hpp" -#include "d3dretrace.hpp" - - -// XXX: Don't duplicate this code. - -static LRESULT CALLBACK -WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MINMAXINFO *pMMI; - switch (uMsg) { - case WM_GETMINMAXINFO: - // Allow to create a window bigger than the desktop - pMMI = (MINMAXINFO *)lParam; - pMMI->ptMaxSize.x = 60000; - pMMI->ptMaxSize.y = 60000; - pMMI->ptMaxTrackSize.x = 60000; - pMMI->ptMaxTrackSize.y = 60000; - break; - default: - break; - } - - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - - -static HWND -createWindow(int width, int height) { - static bool first = TRUE; - RECT rect; - - if (first) { - WNDCLASS wc; - memset(&wc, 0, sizeof wc); - wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.lpfnWndProc = WndProc; - wc.lpszClassName = "d3dretrace"; - wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); - first = FALSE; - } - - DWORD dwExStyle; - DWORD dwStyle; - HWND hWnd; - - dwExStyle = 0; - dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW; - - int x = 0, y = 0; - - rect.left = x; - rect.top = y; - rect.right = rect.left + width; - rect.bottom = rect.top + height; - - AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle); - - hWnd = CreateWindowEx(dwExStyle, - "d3dretrace", /* wc.lpszClassName */ - NULL, - dwStyle, - 0, /* x */ - 0, /* y */ - rect.right - rect.left, /* width */ - rect.bottom - rect.top, /* height */ - NULL, - NULL, - NULL, - NULL); - ShowWindow(hWnd, SW_SHOW); - return hWnd; -} - -''' - retracer = D3DRetracer() - retracer.retraceApi(d3d9) diff --git a/d3dretrace_main.cpp b/d3dretrace_main.cpp deleted file mode 100644 index 133f3a7..0000000 --- a/d3dretrace_main.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include "os_string.hpp" -#include "retrace.hpp" -#include "d3dretrace.hpp" - - -namespace d3dretrace { - -static void display(void) { - retrace::Retracer retracer; - - retracer.addCallbacks(d3d9_callbacks); - - trace::Call *call; - - while ((call = retrace::parser.parse_call())) { - retracer.retrace(*call); - - delete call; - } - - exit(0); -} - - -static void usage(void) { - std::cout << - "Usage: d3dretrace [OPTION] TRACE\n" - "Replay TRACE.\n" - "\n" - " -v increase output verbosity\n" - ; -} - - -extern "C" -int main(int argc, char **argv) -{ - - int i; - for (i = 1; i < argc; ++i) { - const char *arg = argv[i]; - - if (arg[0] != '-') { - break; - } - - if (!strcmp(arg, "--")) { - break; - } else if (!strcmp(arg, "--help")) { - usage(); - return 0; - } else if (!strcmp(arg, "-v")) { - ++retrace::verbosity; - } else { - std::cerr << "error: unknown option " << arg << "\n"; - usage(); - return 1; - } - } - - for ( ; i < argc; ++i) { - if (!retrace::parser.open(argv[i])) { - std::cerr << "error: failed to open " << argv[i] << "\n"; - return 1; - } - - display(); - - retrace::parser.close(); - } - - return 0; -} - -} /* namespace glretrace */ diff --git a/glretrace.hpp b/glretrace.hpp deleted file mode 100644 index 8ba2a80..0000000 --- a/glretrace.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - -#ifndef _GLRETRACE_HPP_ -#define _GLRETRACE_HPP_ - -#include "glws.hpp" -#include "retrace.hpp" - - -namespace glretrace { - - -extern bool double_buffer; -extern bool insideGlBeginEnd; -extern glws::Profile defaultProfile; -extern glws::Visual *visual[glws::PROFILE_MAX]; -extern glws::Drawable *drawable; -extern glws::Context *context; - -extern unsigned frame; -extern long long startTime; -extern bool wait; - -extern bool benchmark; - -extern unsigned dump_state; - -void -checkGlError(trace::Call &call); - -extern const retrace::Entry gl_callbacks[]; -extern const retrace::Entry cgl_callbacks[]; -extern const retrace::Entry glx_callbacks[]; -extern const retrace::Entry wgl_callbacks[]; -extern const retrace::Entry egl_callbacks[]; - -void frame_complete(trace::Call &call); - -void updateDrawable(int width, int height); - -} /* namespace glretrace */ - - -#endif /* _GLRETRACE_HPP_ */ diff --git a/glretrace.py b/glretrace.py deleted file mode 100644 index d91c8a2..0000000 --- a/glretrace.py +++ /dev/null @@ -1,438 +0,0 @@ -########################################################################## -# -# Copyright 2010 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. -# -##########################################################################/ - - -"""GL retracer generator.""" - - -import specs.stdapi as stdapi -import specs.glapi as glapi -import specs.glesapi as glesapi -from retrace import Retracer - - -class GlRetracer(Retracer): - - table_name = 'glretrace::gl_callbacks' - - def retraceFunction(self, function): - Retracer.retraceFunction(self, function) - - array_pointer_function_names = set(( - "glVertexPointer", - "glNormalPointer", - "glColorPointer", - "glIndexPointer", - "glTexCoordPointer", - "glEdgeFlagPointer", - "glFogCoordPointer", - "glSecondaryColorPointer", - - "glInterleavedArrays", - - "glVertexPointerEXT", - "glNormalPointerEXT", - "glColorPointerEXT", - "glIndexPointerEXT", - "glTexCoordPointerEXT", - "glEdgeFlagPointerEXT", - "glFogCoordPointerEXT", - "glSecondaryColorPointerEXT", - - "glVertexAttribPointer", - "glVertexAttribPointerARB", - "glVertexAttribPointerNV", - "glVertexAttribIPointer", - "glVertexAttribIPointerEXT", - "glVertexAttribLPointer", - "glVertexAttribLPointerEXT", - - #"glMatrixIndexPointerARB", - )) - - draw_array_function_names = set([ - "glDrawArrays", - "glDrawArraysEXT", - "glDrawArraysIndirect", - "glDrawArraysInstanced", - "glDrawArraysInstancedARB", - "glDrawArraysInstancedEXT", - "glDrawArraysInstancedBaseInstance", - "glDrawMeshArraysSUN", - "glMultiDrawArrays", - "glMultiDrawArraysEXT", - "glMultiModeDrawArraysIBM", - ]) - - draw_elements_function_names = set([ - "glDrawElements", - "glDrawElementsBaseVertex", - "glDrawElementsIndirect", - "glDrawElementsInstanced", - "glDrawElementsInstancedARB", - "glDrawElementsInstancedEXT", - "glDrawElementsInstancedBaseVertex", - "glDrawElementsInstancedBaseInstance", - "glDrawElementsInstancedBaseVertexBaseInstance", - "glDrawRangeElements", - "glDrawRangeElementsEXT", - "glDrawRangeElementsBaseVertex", - "glMultiDrawElements", - "glMultiDrawElementsBaseVertex", - "glMultiDrawElementsEXT", - "glMultiModeDrawElementsIBM", - ]) - - draw_indirect_function_names = set([ - "glDrawArraysIndirect", - "glDrawElementsIndirect", - ]) - - misc_draw_function_names = set([ - "glCallList", - "glCallLists", - "glClear", - "glEnd", - "glDrawPixels", - "glBlitFramebuffer", - "glBlitFramebufferEXT", - ]) - - bind_framebuffer_function_names = set([ - "glBindFramebuffer", - "glBindFramebufferEXT", - "glBindFramebufferOES", - ]) - - # Names of the functions that can pack into the current pixel buffer - # object. See also the ARB_pixel_buffer_object specification. - pack_function_names = set([ - 'glGetCompressedTexImage', - 'glGetConvolutionFilter', - 'glGetHistogram', - 'glGetMinmax', - 'glGetPixelMapfv', - 'glGetPixelMapuiv', - 'glGetPixelMapusv', - 'glGetPolygonStipple', - 'glGetSeparableFilter', - 'glGetTexImage', - 'glReadPixels', - 'glGetnCompressedTexImageARB', - 'glGetnConvolutionFilterARB', - 'glGetnHistogramARB', - 'glGetnMinmaxARB', - 'glGetnPixelMapfvARB', - 'glGetnPixelMapuivARB', - 'glGetnPixelMapusvARB', - 'glGetnPolygonStippleARB', - 'glGetnSeparableFilterARB', - 'glGetnTexImageARB', - 'glReadnPixelsARB', - ]) - - map_function_names = set([ - 'glMapBuffer', - 'glMapBufferARB', - 'glMapBufferOES', - 'glMapBufferRange', - 'glMapNamedBufferEXT', - 'glMapNamedBufferRangeEXT', - 'glMapObjectBufferATI', - ]) - - unmap_function_names = set([ - 'glUnmapBuffer', - 'glUnmapBufferARB', - 'glUnmapBufferOES', - 'glUnmapNamedBufferEXT', - 'glUnmapObjectBufferATI', - ]) - - def retraceFunctionBody(self, function): - is_array_pointer = function.name in self.array_pointer_function_names - is_draw_array = function.name in self.draw_array_function_names - is_draw_elements = function.name in self.draw_elements_function_names - is_misc_draw = function.name in self.misc_draw_function_names - - if is_array_pointer or is_draw_array or is_draw_elements: - print ' if (retrace::parser.version < 1) {' - - if is_array_pointer or is_draw_array: - print ' GLint __array_buffer = 0;' - print ' glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);' - print ' if (!__array_buffer) {' - self.failFunction(function) - print ' }' - - if is_draw_elements: - print ' GLint __element_array_buffer = 0;' - print ' glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);' - print ' if (!__element_array_buffer) {' - self.failFunction(function) - print ' }' - - print ' }' - - # When no pack buffer object is bound, the pack functions are no-ops. - if function.name in self.pack_function_names: - print ' GLint __pack_buffer = 0;' - print ' glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &__pack_buffer);' - print ' if (!__pack_buffer) {' - print ' return;' - print ' }' - - # Pre-snapshots - if function.name in self.bind_framebuffer_function_names: - print ' assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);' - if function.name == 'glFrameTerminatorGREMEDY': - print ' glretrace::frame_complete(call);' - return - - Retracer.retraceFunctionBody(self, function) - - # Post-snapshots - if function.name in ('glFlush', 'glFinish'): - print ' if (!glretrace::double_buffer) {' - print ' glretrace::frame_complete(call);' - print ' }' - if is_draw_array or is_draw_elements or is_misc_draw: - print ' assert(call.flags & trace::CALL_FLAG_RENDER);' - - - def invokeFunction(self, function): - # Infer the drawable size from GL calls - if function.name == "glViewport": - print ' glretrace::updateDrawable(x + width, y + height);' - if function.name == "glViewportArray": - # We are concerned about drawables so only care for the first viewport - print ' if (first == 0 && count > 0) {' - print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];' - print ' glretrace::updateDrawable(x + w, y + h);' - print ' }' - if function.name == "glViewportIndexedf": - print ' if (index == 0) {' - print ' glretrace::updateDrawable(x + w, y + h);' - print ' }' - if function.name == "glViewportIndexedfv": - print ' if (index == 0) {' - print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];' - print ' glretrace::updateDrawable(x + w, y + h);' - print ' }' - if function.name in ('glBlitFramebuffer', 'glBlitFramebufferEXT'): - # Some applications do all their rendering in a framebuffer, and - # then just blit to the drawable without ever calling glViewport. - print ' glretrace::updateDrawable(std::max(dstX0, dstX1), std::max(dstY0, dstY1));' - - if function.name == "glEnd": - print ' glretrace::insideGlBeginEnd = false;' - - if function.name.startswith('gl') and not function.name.startswith('glX'): - print r' if (!glretrace::context && !glretrace::benchmark && !retrace::profiling) {' - print r' retrace::warning(call) << "no current context\n";' - print r' }' - - if function.name == 'memcpy': - print ' if (!dest || !src || !n) return;' - - # Destroy the buffer mapping - if function.name in self.unmap_function_names: - print r' GLvoid *ptr = NULL;' - if function.name == 'glUnmapBuffer': - print r' glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &ptr);' - elif function.name == 'glUnmapBufferARB': - print r' glGetBufferPointervARB(target, GL_BUFFER_MAP_POINTER_ARB, &ptr);' - elif function.name == 'glUnmapBufferOES': - print r' glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &ptr);' - elif function.name == 'glUnmapNamedBufferEXT': - print r' glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &ptr);' - elif function.name == 'glUnmapObjectBufferATI': - # TODO - pass - else: - assert False - print r' if (ptr) {' - print r' retrace::delRegionByPointer(ptr);' - print r' } else {' - print r' retrace::warning(call) << "no current context\n";' - print r' }' - - Retracer.invokeFunction(self, function) - - # Error checking - if function.name == "glBegin": - print ' glretrace::insideGlBeginEnd = true;' - elif function.name.startswith('gl'): - # glGetError is not allowed inside glBegin/glEnd - print ' if (!glretrace::benchmark && !retrace::profiling && !glretrace::insideGlBeginEnd) {' - print ' glretrace::checkGlError(call);' - if function.name in ('glProgramStringARB', 'glProgramStringNV'): - print r' GLint error_position = -1;' - print r' glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_position);' - print r' if (error_position != -1) {' - print r' const char *error_string = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);' - print r' retrace::warning(call) << error_string << "\n";' - print r' }' - if function.name == 'glCompileShader': - print r' GLint compile_status = 0;' - print r' glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);' - print r' if (!compile_status) {' - print r' GLint info_log_length = 0;' - print r' glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);' - print r' GLchar *infoLog = new GLchar[info_log_length];' - print r' glGetShaderInfoLog(shader, info_log_length, NULL, infoLog);' - print r' retrace::warning(call) << infoLog << "\n";' - print r' delete [] infoLog;' - print r' }' - if function.name == 'glLinkProgram': - print r' GLint link_status = 0;' - print r' glGetProgramiv(program, GL_LINK_STATUS, &link_status);' - print r' if (!link_status) {' - print r' GLint info_log_length = 0;' - print r' glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);' - print r' GLchar *infoLog = new GLchar[info_log_length];' - print r' glGetProgramInfoLog(program, info_log_length, NULL, infoLog);' - print r' retrace::warning(call) << infoLog << "\n";' - print r' delete [] infoLog;' - print r' }' - if function.name == 'glCompileShaderARB': - print r' GLint compile_status = 0;' - print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);' - print r' if (!compile_status) {' - print r' GLint info_log_length = 0;' - print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);' - print r' GLchar *infoLog = new GLchar[info_log_length];' - print r' glGetInfoLogARB(shaderObj, info_log_length, NULL, infoLog);' - print r' retrace::warning(call) << infoLog << "\n";' - print r' delete [] infoLog;' - print r' }' - if function.name == 'glLinkProgramARB': - print r' GLint link_status = 0;' - print r' glGetObjectParameterivARB(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);' - print r' if (!link_status) {' - print r' GLint info_log_length = 0;' - print r' glGetObjectParameterivARB(programObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);' - print r' GLchar *infoLog = new GLchar[info_log_length];' - print r' glGetInfoLogARB(programObj, info_log_length, NULL, infoLog);' - print r' retrace::warning(call) << infoLog << "\n";' - print r' delete [] infoLog;' - print r' }' - if function.name in self.map_function_names: - print r' if (!__result) {' - print r' retrace::warning(call) << "failed to map buffer\n";' - print r' }' - if function.name in self.unmap_function_names and function.type is not stdapi.Void: - print r' if (!__result) {' - print r' retrace::warning(call) << "failed to unmap buffer\n";' - print r' }' - if function.name in ('glGetAttribLocation', 'glGetAttribLocationARB'): - print r' GLint __orig_result = call.ret->toSInt();' - print r' if (__result != __orig_result) {' - print r' retrace::warning(call) << "vertex attrib location mismatch " << __orig_result << " -> " << __result << "\n";' - print r' }' - if function.name in ('glCheckFramebufferStatus', 'glCheckFramebufferStatusEXT', 'glCheckNamedFramebufferStatusEXT'): - print r' GLint __orig_result = call.ret->toSInt();' - print r' if (__orig_result == GL_FRAMEBUFFER_COMPLETE &&' - print r' __result != GL_FRAMEBUFFER_COMPLETE) {' - print r' retrace::warning(call) << "incomplete framebuffer (" << glstate::enumToString(__result) << ")\n";' - print r' }' - print ' }' - - # Query the buffer length for whole buffer mappings - if function.name in self.map_function_names: - if 'length' in function.argNames(): - assert 'BufferRange' in function.name - else: - assert 'BufferRange' not in function.name - print r' GLint length = 0;' - if function.name in ('glMapBuffer', 'glMapBufferOES'): - print r' glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);' - elif function.name == 'glMapBufferARB': - print r' glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);' - elif function.name == 'glMapNamedBufferEXT': - print r' glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);' - elif function.name == 'glMapObjectBufferATI': - print r' glGetObjectBufferivATI(buffer, GL_OBJECT_BUFFER_SIZE_ATI, &length);' - else: - assert False - - def extractArg(self, function, arg, arg_type, lvalue, rvalue): - if function.name in self.array_pointer_function_names and arg.name == 'pointer': - print ' %s = static_cast<%s>(retrace::toPointer(%s, true));' % (lvalue, arg_type, rvalue) - return - - if function.name in self.draw_elements_function_names and arg.name == 'indices' or\ - function.name in self.draw_indirect_function_names and arg.name == 'indirect': - self.extractOpaqueArg(function, arg, arg_type, lvalue, rvalue) - return - - # Handle pointer with offsets into the current pack pixel buffer - # object. - if function.name in self.pack_function_names and arg.output: - assert isinstance(arg_type, (stdapi.Pointer, stdapi.Array, stdapi.Blob, stdapi.Opaque)) - print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, arg_type, rvalue) - return - - if arg.type is glapi.GLlocation \ - and 'program' not in function.argNames(): - print ' GLint program = -1;' - print ' glGetIntegerv(GL_CURRENT_PROGRAM, &program);' - - if arg.type is glapi.GLlocationARB \ - and 'programObj' not in function.argNames(): - print ' GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);' - - Retracer.extractArg(self, function, arg, arg_type, lvalue, rvalue) - - # Don't try to use more samples than the implementation supports - if arg.name == 'samples': - assert arg.type is glapi.GLsizei - print ' GLint max_samples = 0;' - print ' glGetIntegerv(GL_MAX_SAMPLES, &max_samples);' - print ' if (samples > max_samples) {' - print ' samples = max_samples;' - print ' }' - - # These parameters are referred beyond the call life-time - # TODO: Replace ad-hoc solution for bindable parameters with general one - if function.name in ('glFeedbackBuffer', 'glSelectBuffer') and arg.output: - print ' _allocator.bind(%s);' % arg.name - - - -if __name__ == '__main__': - print r''' -#include - -#include "glproc.hpp" -#include "glretrace.hpp" -#include "glstate.hpp" - - -''' - api = glapi.glapi - api.addApi(glesapi.glesapi) - retracer = GlRetracer() - retracer.retraceApi(api) diff --git a/glretrace_cgl.cpp b/glretrace_cgl.cpp deleted file mode 100644 index 63b94b4..0000000 --- a/glretrace_cgl.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include "glproc.hpp" -#include "retrace.hpp" -#include "glretrace.hpp" - - -using namespace glretrace; - - -typedef std::map DrawableMap; -typedef std::map ContextMap; -static DrawableMap drawable_map; -static ContextMap context_map; -static glws::Context *sharedContext = NULL; - - -static glws::Drawable * -getDrawable(unsigned long drawable_id) { - if (drawable_id == 0) { - return NULL; - } - - /* XXX: Support multiple drawables. */ - drawable_id = 1; - - DrawableMap::const_iterator it; - it = drawable_map.find(drawable_id); - if (it == drawable_map.end()) { - return (drawable_map[drawable_id] = glws::createDrawable(visual[glretrace::defaultProfile])); - } - - return it->second; -} - - -static glws::Context * -getContext(unsigned long long ctx) { - if (ctx == 0) { - return NULL; - } - - ContextMap::const_iterator it; - it = context_map.find(ctx); - if (it == context_map.end()) { - glws::Context *context; - context_map[ctx] = context = glws::createContext(visual[glretrace::defaultProfile], sharedContext, glretrace::defaultProfile); - if (!sharedContext) { - sharedContext = context; - } - return context; - } - - return it->second; -} - - -static void retrace_CGLSetCurrentContext(trace::Call &call) { - unsigned long long ctx = call.arg(0).toUIntPtr(); - - glws::Drawable *new_drawable = getDrawable(ctx); - glws::Context *new_context = getContext(ctx); - - bool result = glws::makeCurrent(new_drawable, new_context); - - if (new_drawable && new_context && result) { - drawable = new_drawable; - context = new_context; - } else { - drawable = NULL; - context = NULL; - } -} - - -static void retrace_CGLFlushDrawable(trace::Call &call) { - if (drawable && context) { - if (double_buffer) { - drawable->swapBuffers(); - } else { - glFlush(); - } - - frame_complete(call); - } -} - - -const retrace::Entry glretrace::cgl_callbacks[] = { - {"CGLSetCurrentContext", &retrace_CGLSetCurrentContext}, - {"CGLGetCurrentContext", &retrace::ignore}, - {"CGLEnable", &retrace::ignore}, - {"CGLDisable", &retrace::ignore}, - {"CGLSetParameter", &retrace::ignore}, - {"CGLGetParameter", &retrace::ignore}, - {"CGLFlushDrawable", &retrace_CGLFlushDrawable}, - {NULL, NULL}, -}; - diff --git a/glretrace_egl.cpp b/glretrace_egl.cpp deleted file mode 100644 index d7b14ec..0000000 --- a/glretrace_egl.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/************************************************************************** - * - * 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" - -#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; - -static unsigned int current_api = EGL_OPENGL_ES_API; -static glws::Profile last_profile = glws::PROFILE_COMPAT; - -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 glws::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); - glws::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::Visual *visual = glretrace::visual[profile]; - - glws::Drawable *drawable = glws::createDrawable(visual); - drawable_map[orig_surface] = drawable; -} - -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()) { - if (it->second != drawable) { - // TODO: reference count - delete it->second; - } - drawable_map.erase(it); - } -} - -static void retrace_eglBindAPI(trace::Call &call) { - 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(); - glws::Context *share_context = getContext(call.arg(2).toUIntPtr()); - trace::Array *attrib_array = dynamic_cast(&call.arg(3)); - glws::Profile profile; - - switch (current_api) { - case EGL_OPENGL_API: - profile = glws::PROFILE_COMPAT; - break; - case EGL_OPENGL_ES_API: - default: - profile = glws::PROFILE_ES1; - if (attrib_array) { - for (int i = 0; i < attrib_array->values.size(); i += 2) { - int v = attrib_array->values[i]->toSInt(); - if (v == EGL_CONTEXT_CLIENT_VERSION) { - v = attrib_array->values[i + 1]->toSInt(); - if (v == 2) - profile = glws::PROFILE_ES2; - break; - } - } - } - break; - } - - - glws::Context *context = glws::createContext(glretrace::visual[profile], share_context, profile); - if (!context) { - const char *name; - switch (profile) { - case glws::PROFILE_COMPAT: - name = "OpenGL"; - break; - case glws::PROFILE_ES1: - name = "OpenGL ES 1.1"; - break; - case glws::PROFILE_ES2: - name = "OpenGL ES 2.0"; - break; - default: - name = "unknown"; - break; - } - - retrace::warning(call) << "Failed to create " << name << " context.\n"; - os::abort(); - } - - 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()) { - delete it->second; - context_map.erase(it); - } -} - -static void retrace_eglMakeCurrent(trace::Call &call) { - glws::Drawable *new_drawable = getDrawable(call.arg(1).toUIntPtr()); - glws::Context *new_context = getContext(call.arg(3).toUIntPtr()); - - if (new_drawable == drawable && new_context == context) { - return; - } - - if (drawable && context) { - glFlush(); - if (!double_buffer) { - frame_complete(call); - } - } - - bool result = glws::makeCurrent(new_drawable, new_context); - - if (new_drawable && new_context && result) { - drawable = new_drawable; - context = new_context; - } else { - drawable = NULL; - context = NULL; - } -} - - -static void retrace_eglSwapBuffers(trace::Call &call) { - frame_complete(call); - - if (double_buffer && 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::ignore}, - {"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}, - {"eglSwapBuffers", &retrace_eglSwapBuffers}, - //{"eglCopyBuffers", &retrace::ignore}, - {"eglGetProcAddress", &retrace::ignore}, - {NULL, NULL}, -}; diff --git a/glretrace_glx.cpp b/glretrace_glx.cpp deleted file mode 100644 index 7342908..0000000 --- a/glretrace_glx.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include "glproc.hpp" -#include "retrace.hpp" -#include "glretrace.hpp" - - -using namespace glretrace; - - -typedef std::map DrawableMap; -typedef std::map ContextMap; -static DrawableMap drawable_map; -static ContextMap context_map; - - -static glws::Drawable * -getDrawable(unsigned long drawable_id) { - if (drawable_id == 0) { - return NULL; - } - - DrawableMap::const_iterator it; - it = drawable_map.find(drawable_id); - if (it == drawable_map.end()) { - return (drawable_map[drawable_id] = glws::createDrawable(visual[glretrace::defaultProfile])); - } - - return it->second; -} - -static glws::Context * -getContext(unsigned long long context_ptr) { - if (context_ptr == 0) { - return NULL; - } - - ContextMap::const_iterator it; - it = context_map.find(context_ptr); - if (it == context_map.end()) { - return (context_map[context_ptr] = glws::createContext(visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile)); - } - - return it->second; -} - -static void retrace_glXCreateContext(trace::Call &call) { - unsigned long long orig_context = call.ret->toUIntPtr(); - glws::Context *share_context = getContext(call.arg(2).toUIntPtr()); - - glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); - context_map[orig_context] = context; -} - -static void retrace_glXCreateContextAttribsARB(trace::Call &call) { - unsigned long long orig_context = call.ret->toUIntPtr(); - glws::Context *share_context = getContext(call.arg(2).toUIntPtr()); - - glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); - context_map[orig_context] = context; -} - -static void retrace_glXMakeCurrent(trace::Call &call) { - glws::Drawable *new_drawable = getDrawable(call.arg(1).toUInt()); - glws::Context *new_context = getContext(call.arg(2).toUIntPtr()); - - if (new_drawable == drawable && new_context == context) { - return; - } - - if (drawable && context) { - glFlush(); - if (!double_buffer) { - frame_complete(call); - } - } - - bool result = glws::makeCurrent(new_drawable, new_context); - - if (new_drawable && new_context && result) { - drawable = new_drawable; - context = new_context; - } else { - drawable = NULL; - context = NULL; - } -} - - -static void retrace_glXDestroyContext(trace::Call &call) { - glws::Context *context = getContext(call.arg(1).toUIntPtr()); - - if (!context) { - return; - } - - delete context; -} - -static void retrace_glXSwapBuffers(trace::Call &call) { - frame_complete(call); - if (double_buffer) { - drawable->swapBuffers(); - } else { - glFlush(); - } -} - -static void retrace_glXCreateNewContext(trace::Call &call) { - unsigned long long orig_context = call.ret->toUIntPtr(); - glws::Context *share_context = getContext(call.arg(3).toUIntPtr()); - - glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); - context_map[orig_context] = context; -} - -static void retrace_glXMakeContextCurrent(trace::Call &call) { - glws::Drawable *new_drawable = getDrawable(call.arg(1).toUInt()); - glws::Context *new_context = getContext(call.arg(3).toUIntPtr()); - - if (new_drawable == drawable && new_context == context) { - return; - } - - if (drawable && context) { - glFlush(); - if (!double_buffer) { - frame_complete(call); - } - } - - bool result = glws::makeCurrent(new_drawable, new_context); - - if (new_drawable && new_context && result) { - drawable = new_drawable; - context = new_context; - } else { - drawable = NULL; - context = NULL; - } -} - -const retrace::Entry glretrace::glx_callbacks[] = { - //{"glXBindChannelToWindowSGIX", &retrace_glXBindChannelToWindowSGIX}, - //{"glXBindSwapBarrierNV", &retrace_glXBindSwapBarrierNV}, - //{"glXBindSwapBarrierSGIX", &retrace_glXBindSwapBarrierSGIX}, - //{"glXBindTexImageEXT", &retrace_glXBindTexImageEXT}, - //{"glXChannelRectSGIX", &retrace_glXChannelRectSGIX}, - //{"glXChannelRectSyncSGIX", &retrace_glXChannelRectSyncSGIX}, - {"glXChooseFBConfig", &retrace::ignore}, - {"glXChooseFBConfigSGIX", &retrace::ignore}, - {"glXChooseVisual", &retrace::ignore}, - //{"glXCopyContext", &retrace_glXCopyContext}, - //{"glXCopyImageSubDataNV", &retrace_glXCopyImageSubDataNV}, - //{"glXCopySubBufferMESA", &retrace_glXCopySubBufferMESA}, - {"glXCreateContextAttribsARB", &retrace_glXCreateContextAttribsARB}, - {"glXCreateContext", &retrace_glXCreateContext}, - //{"glXCreateContextWithConfigSGIX", &retrace_glXCreateContextWithConfigSGIX}, - //{"glXCreateGLXPbufferSGIX", &retrace_glXCreateGLXPbufferSGIX}, - //{"glXCreateGLXPixmap", &retrace_glXCreateGLXPixmap}, - //{"glXCreateGLXPixmapWithConfigSGIX", &retrace_glXCreateGLXPixmapWithConfigSGIX}, - {"glXCreateNewContext", &retrace_glXCreateNewContext}, - //{"glXCreatePbuffer", &retrace_glXCreatePbuffer}, - //{"glXCreatePixmap", &retrace_glXCreatePixmap}, - //{"glXCreateWindow", &retrace_glXCreateWindow}, - //{"glXCushionSGI", &retrace_glXCushionSGI}, - {"glXDestroyContext", &retrace_glXDestroyContext}, - //{"glXDestroyGLXPbufferSGIX", &retrace_glXDestroyGLXPbufferSGIX}, - //{"glXDestroyGLXPixmap", &retrace_glXDestroyGLXPixmap}, - //{"glXDestroyPbuffer", &retrace_glXDestroyPbuffer}, - //{"glXDestroyPixmap", &retrace_glXDestroyPixmap}, - //{"glXDestroyWindow", &retrace_glXDestroyWindow}, - //{"glXFreeContextEXT", &retrace_glXFreeContextEXT}, - {"glXGetAGPOffsetMESA", &retrace::ignore}, - {"glXGetClientString", &retrace::ignore}, - {"glXGetConfig", &retrace::ignore}, - {"glXGetContextIDEXT", &retrace::ignore}, - {"glXGetCurrentContext", &retrace::ignore}, - {"glXGetCurrentDisplayEXT", &retrace::ignore}, - {"glXGetCurrentDisplay", &retrace::ignore}, - {"glXGetCurrentDrawable", &retrace::ignore}, - {"glXGetCurrentReadDrawable", &retrace::ignore}, - {"glXGetCurrentReadDrawableSGI", &retrace::ignore}, - {"glXGetFBConfigAttrib", &retrace::ignore}, - {"glXGetFBConfigAttribSGIX", &retrace::ignore}, - {"glXGetFBConfigFromVisualSGIX", &retrace::ignore}, - {"glXGetFBConfigs", &retrace::ignore}, - {"glXGetMscRateOML", &retrace::ignore}, - {"glXGetProcAddressARB", &retrace::ignore}, - {"glXGetProcAddress", &retrace::ignore}, - {"glXGetSelectedEvent", &retrace::ignore}, - {"glXGetSelectedEventSGIX", &retrace::ignore}, - {"glXGetSyncValuesOML", &retrace::ignore}, - {"glXGetVideoSyncSGI", &retrace::ignore}, - {"glXGetVisualFromFBConfig", &retrace::ignore}, - {"glXGetVisualFromFBConfigSGIX", &retrace::ignore}, - //{"glXImportContextEXT", &retrace_glXImportContextEXT}, - {"glXIsDirect", &retrace::ignore}, - //{"glXJoinSwapGroupNV", &retrace_glXJoinSwapGroupNV}, - //{"glXJoinSwapGroupSGIX", &retrace_glXJoinSwapGroupSGIX}, - {"glXMakeContextCurrent", &retrace_glXMakeContextCurrent}, - //{"glXMakeCurrentReadSGI", &retrace_glXMakeCurrentReadSGI}, - {"glXMakeCurrent", &retrace_glXMakeCurrent}, - {"glXQueryChannelDeltasSGIX", &retrace::ignore}, - {"glXQueryChannelRectSGIX", &retrace::ignore}, - {"glXQueryContextInfoEXT", &retrace::ignore}, - {"glXQueryContext", &retrace::ignore}, - {"glXQueryDrawable", &retrace::ignore}, - {"glXQueryExtension", &retrace::ignore}, - {"glXQueryExtensionsString", &retrace::ignore}, - {"glXQueryFrameCountNV", &retrace::ignore}, - {"glXQueryGLXPbufferSGIX", &retrace::ignore}, - {"glXQueryMaxSwapBarriersSGIX", &retrace::ignore}, - {"glXQueryMaxSwapGroupsNV", &retrace::ignore}, - {"glXQueryServerString", &retrace::ignore}, - {"glXQuerySwapGroupNV", &retrace::ignore}, - {"glXQueryVersion", &retrace::ignore}, - //{"glXReleaseBuffersMESA", &retrace_glXReleaseBuffersMESA}, - //{"glXReleaseTexImageEXT", &retrace_glXReleaseTexImageEXT}, - //{"glXResetFrameCountNV", &retrace_glXResetFrameCountNV}, - //{"glXSelectEvent", &retrace_glXSelectEvent}, - //{"glXSelectEventSGIX", &retrace_glXSelectEventSGIX}, - //{"glXSet3DfxModeMESA", &retrace_glXSet3DfxModeMESA}, - //{"glXSwapBuffersMscOML", &retrace_glXSwapBuffersMscOML}, - {"glXSwapBuffers", &retrace_glXSwapBuffers}, - {"glXSwapIntervalEXT", &retrace::ignore}, - {"glXSwapIntervalSGI", &retrace::ignore}, - //{"glXUseXFont", &retrace_glXUseXFont}, - {"glXWaitForMscOML", &retrace::ignore}, - {"glXWaitForSbcOML", &retrace::ignore}, - {"glXWaitGL", &retrace::ignore}, - {"glXWaitVideoSyncSGI", &retrace::ignore}, - {"glXWaitX", &retrace::ignore}, - {NULL, NULL}, -}; - diff --git a/glretrace_main.cpp b/glretrace_main.cpp deleted file mode 100644 index 313a563..0000000 --- a/glretrace_main.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include "os_binary.hpp" -#include "os_string.hpp" -#include "os_time.hpp" -#include "image.hpp" -#include "retrace.hpp" -#include "trace_callset.hpp" -#include "glproc.hpp" -#include "glstate.hpp" -#include "glretrace.hpp" - - -namespace glretrace { - -bool double_buffer = true; -bool insideGlBeginEnd = false; -glws::Profile defaultProfile = glws::PROFILE_COMPAT; -glws::Visual *visual[glws::PROFILE_MAX]; -glws::Drawable *drawable = NULL; -glws::Context *context = NULL; - -unsigned frame = 0; -long long startTime = 0; -bool wait = false; - -bool benchmark = false; -static const char *compare_prefix = NULL; -static const char *snapshot_prefix = NULL; -static trace::CallSet snapshot_frequency; -static trace::CallSet compare_frequency; - -unsigned dump_state = ~0; - -void -checkGlError(trace::Call &call) { - GLenum error = glGetError(); - if (error == GL_NO_ERROR) { - return; - } - - std::ostream & os = retrace::warning(call); - - os << "glGetError("; - os << call.name(); - os << ") = "; - - switch (error) { - case GL_INVALID_ENUM: - os << "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - os << "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - os << "GL_INVALID_OPERATION"; - break; - case GL_STACK_OVERFLOW: - os << "GL_STACK_OVERFLOW"; - break; - case GL_STACK_UNDERFLOW: - os << "GL_STACK_UNDERFLOW"; - break; - case GL_OUT_OF_MEMORY: - os << "GL_OUT_OF_MEMORY"; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - os << "GL_INVALID_FRAMEBUFFER_OPERATION"; - break; - case GL_TABLE_TOO_LARGE: - os << "GL_TABLE_TOO_LARGE"; - break; - default: - os << error; - break; - } - os << "\n"; -} - -/** - * Grow the current drawble. - * - * We need to infer the drawable size from GL calls because the drawable sizes - * are specified by OS specific calls which we do not trace. - */ -void -updateDrawable(int width, int height) { - if (!drawable) { - return; - } - - if (drawable->visible && - width <= drawable->width && - height <= drawable->height) { - return; - } - - // Ignore zero area viewports - if (width == 0 || height == 0) { - return; - } - - // Check for bound framebuffer last, as this may have a performance impact. - GLint draw_framebuffer = 0; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer); - if (draw_framebuffer != 0) { - return; - } - - drawable->resize(width, height); - drawable->show(); - - glScissor(0, 0, width, height); -} - - -static void -snapshot(unsigned call_no) { - assert(snapshot_prefix || compare_prefix); - - if (!drawable) { - return; - } - - image::Image *ref = NULL; - - if (compare_prefix) { - os::String filename = os::String::format("%s%010u.png", compare_prefix, call_no); - ref = image::readPNG(filename); - if (!ref) { - return; - } - if (retrace::verbosity >= 0) { - std::cout << "Read " << filename << "\n"; - } - } - - image::Image *src = glstate::getDrawBufferImage(); - if (!src) { - return; - } - - if (snapshot_prefix) { - if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) { - char comment[21]; - snprintf(comment, sizeof comment, "%u", call_no); - src->writePNM(std::cout, comment); - } else { - os::String filename = os::String::format("%s%010u.png", snapshot_prefix, call_no); - if (src->writePNG(filename) && retrace::verbosity >= 0) { - std::cout << "Wrote " << filename << "\n"; - } - } - } - - if (ref) { - std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n"; - delete ref; - } - - delete src; -} - - -void frame_complete(trace::Call &call) { - ++frame; - - if (!drawable) { - return; - } - - if (!drawable->visible) { - retrace::warning(call) << "could not infer drawable size (glViewport never called)\n"; - } -} - - -static void display(void) { - retrace::Retracer retracer; - - retracer.addCallbacks(gl_callbacks); - retracer.addCallbacks(glx_callbacks); - retracer.addCallbacks(wgl_callbacks); - retracer.addCallbacks(cgl_callbacks); - retracer.addCallbacks(egl_callbacks); - - startTime = os::getTime(); - trace::Call *call; - - while ((call = retrace::parser.parse_call())) { - bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET; - bool doSnapshot = - snapshot_frequency.contains(*call) || - compare_frequency.contains(*call) - ; - - // For calls which cause rendertargets to be swaped, we take the - // snapshot _before_ swapping the rendertargets. - if (doSnapshot && swapRenderTarget) { - if (call->flags & trace::CALL_FLAG_END_FRAME) { - // For swapbuffers/presents we still use this call number, - // spite not have been executed yet. - snapshot(call->no); - } else { - // Whereas for ordinate fbo/rendertarget changes we use the - // previous call's number. - snapshot(call->no - 1); - } - } - - retracer.retrace(*call); - - if (doSnapshot && !swapRenderTarget) { - snapshot(call->no); - } - - if (!insideGlBeginEnd && - drawable && context && - call->no >= dump_state) { - glstate::dumpCurrentContext(std::cout); - exit(0); - } - - delete call; - } - - // Reached the end of trace - glFlush(); - - long long endTime = os::getTime(); - float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency); - - if ((retrace::verbosity >= -1) || (retrace::profiling)) { - std::cout << - "Rendered " << frame << " frames" - " in " << timeInterval << " secs," - " average of " << (frame/timeInterval) << " fps\n"; - } - - if (wait) { - while (glws::processEvents()) {} - } else { - exit(0); - } -} - - -static void usage(void) { - std::cout << - "Usage: glretrace [OPTION] TRACE\n" - "Replay TRACE.\n" - "\n" - " -b benchmark mode (no error checking or warning messages)\n" - " -p profiling mode (run whole trace, dump profiling info)\n" - " -c PREFIX compare against snapshots\n" - " -C CALLSET calls to compare (default is every frame)\n" - " -core use core profile\n" - " -db use a double buffer visual (default)\n" - " -sb use a single buffer visual\n" - " -s PREFIX take snapshots; `-` for PNM stdout output\n" - " -S CALLSET calls to snapshot (default is every frame)\n" - " -v increase output verbosity\n" - " -D CALLNO dump state at specific call no\n" - " -w wait on final frame\n"; -} - -extern "C" -int main(int argc, char **argv) -{ - assert(compare_frequency.empty()); - assert(snapshot_frequency.empty()); - - int i; - for (i = 1; i < argc; ++i) { - const char *arg = argv[i]; - - if (arg[0] != '-') { - break; - } - - if (!strcmp(arg, "--")) { - break; - } else if (!strcmp(arg, "-b")) { - benchmark = true; - retrace::verbosity = -1; - glws::debug = false; - } else if (!strcmp(arg, "-p")) { - retrace::profiling = true; - retrace::verbosity = -1; - glws::debug = false; - } else if (!strcmp(arg, "-c")) { - compare_prefix = argv[++i]; - if (compare_frequency.empty()) { - compare_frequency = trace::CallSet(trace::FREQUENCY_FRAME); - } - } else if (!strcmp(arg, "-C")) { - compare_frequency = trace::CallSet(argv[++i]); - if (compare_prefix == NULL) { - compare_prefix = ""; - } - } else if (!strcmp(arg, "-D")) { - dump_state = atoi(argv[++i]); - retrace::verbosity = -2; - } else if (!strcmp(arg, "-core")) { - defaultProfile = glws::PROFILE_CORE; - } else if (!strcmp(arg, "-db")) { - double_buffer = true; - } else if (!strcmp(arg, "-sb")) { - double_buffer = false; - } else if (!strcmp(arg, "--help")) { - usage(); - return 0; - } else if (!strcmp(arg, "-s")) { - snapshot_prefix = argv[++i]; - if (snapshot_frequency.empty()) { - snapshot_frequency = trace::CallSet(trace::FREQUENCY_FRAME); - } - if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) { - os::setBinaryMode(stdout); - retrace::verbosity = -2; - } - } else if (!strcmp(arg, "-S")) { - snapshot_frequency = trace::CallSet(argv[++i]); - if (snapshot_prefix == NULL) { - snapshot_prefix = ""; - } - } else if (!strcmp(arg, "-v")) { - ++retrace::verbosity; - } else if (!strcmp(arg, "-w")) { - wait = true; - } else { - std::cerr << "error: unknown option " << arg << "\n"; - usage(); - return 1; - } - } - - glws::init(); - visual[glws::PROFILE_COMPAT] = glws::createVisual(double_buffer, glws::PROFILE_COMPAT); - visual[glws::PROFILE_CORE] = glws::createVisual(double_buffer, glws::PROFILE_CORE); - visual[glws::PROFILE_ES1] = glws::createVisual(double_buffer, glws::PROFILE_ES1); - visual[glws::PROFILE_ES2] = glws::createVisual(double_buffer, glws::PROFILE_ES2); - - for ( ; i < argc; ++i) { - if (!retrace::parser.open(argv[i])) { - std::cerr << "error: failed to open " << argv[i] << "\n"; - return 1; - } - - display(); - - retrace::parser.close(); - } - - for (int n = 0; n < glws::PROFILE_MAX; n++) { - delete visual[n]; - } - - glws::cleanup(); - - return 0; -} - -} /* namespace glretrace */ diff --git a/glretrace_wgl.cpp b/glretrace_wgl.cpp deleted file mode 100644 index 447d177..0000000 --- a/glretrace_wgl.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include "glproc.hpp" -#include "retrace.hpp" -#include "glretrace.hpp" - - -using namespace glretrace; - - -typedef std::map DrawableMap; -typedef std::map ContextMap; -static DrawableMap drawable_map; -static DrawableMap pbuffer_map; -static ContextMap context_map; - - -static glws::Drawable * -getDrawable(unsigned long long hdc) { - if (hdc == 0) { - return NULL; - } - - DrawableMap::const_iterator it; - it = drawable_map.find(hdc); - if (it == drawable_map.end()) { - return (drawable_map[hdc] = glws::createDrawable(visual[glretrace::defaultProfile])); - } - - return it->second; -} - -static void retrace_wglCreateContext(trace::Call &call) { - unsigned long long orig_context = call.ret->toUIntPtr(); - glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile); - context_map[orig_context] = context; -} - -static void retrace_wglDeleteContext(trace::Call &call) { -} - -static void retrace_wglMakeCurrent(trace::Call &call) { - if (drawable && context) { - glFlush(); - if (!double_buffer) { - frame_complete(call); - } - } - - glws::Drawable *new_drawable = getDrawable(call.arg(0).toUIntPtr()); - glws::Context *new_context = context_map[call.arg(1).toUIntPtr()]; - - bool result = glws::makeCurrent(new_drawable, new_context); - - if (new_drawable && new_context && result) { - drawable = new_drawable; - context = new_context; - } else { - drawable = NULL; - context = NULL; - } -} - -static void retrace_wglCopyContext(trace::Call &call) { -} - -static void retrace_wglChoosePixelFormat(trace::Call &call) { -} - -static void retrace_wglDescribePixelFormat(trace::Call &call) { -} - -static void retrace_wglSetPixelFormat(trace::Call &call) { -} - -static void retrace_wglSwapBuffers(trace::Call &call) { - frame_complete(call); - if (double_buffer) { - drawable->swapBuffers(); - } else { - glFlush(); - } -} - -static void retrace_wglShareLists(trace::Call &call) { - unsigned long long hglrc1 = call.arg(0).toUIntPtr(); - unsigned long long hglrc2 = call.arg(1).toUIntPtr(); - - glws::Context *share_context = context_map[hglrc1]; - glws::Context *old_context = context_map[hglrc2]; - - glws::Context *new_context = - glws::createContext(old_context->visual, share_context, glretrace::defaultProfile); - if (new_context) { - if (context == old_context) { - glws::makeCurrent(drawable, new_context); - } - - context_map[hglrc2] = new_context; - - delete old_context; - } -} - -static void retrace_wglCreateLayerContext(trace::Call &call) { - retrace_wglCreateContext(call); -} - -static void retrace_wglDescribeLayerPlane(trace::Call &call) { -} - -static void retrace_wglSetLayerPaletteEntries(trace::Call &call) { -} - -static void retrace_wglRealizeLayerPalette(trace::Call &call) { -} - -static void retrace_wglSwapLayerBuffers(trace::Call &call) { - retrace_wglSwapBuffers(call); -} - -static void retrace_wglUseFontBitmapsA(trace::Call &call) { -} - -static void retrace_wglUseFontBitmapsW(trace::Call &call) { -} - -static void retrace_wglSwapMultipleBuffers(trace::Call &call) { -} - -static void retrace_wglUseFontOutlinesA(trace::Call &call) { -} - -static void retrace_wglUseFontOutlinesW(trace::Call &call) { -} - -static void retrace_wglCreateBufferRegionARB(trace::Call &call) { -} - -static void retrace_wglDeleteBufferRegionARB(trace::Call &call) { -} - -static void retrace_wglSaveBufferRegionARB(trace::Call &call) { -} - -static void retrace_wglRestoreBufferRegionARB(trace::Call &call) { -} - -static void retrace_wglChoosePixelFormatARB(trace::Call &call) { -} - -static void retrace_wglMakeContextCurrentARB(trace::Call &call) { -} - -static void retrace_wglCreatePbufferARB(trace::Call &call) { - int iWidth = call.arg(2).toUInt(); - int iHeight = call.arg(3).toUInt(); - - unsigned long long orig_pbuffer = call.ret->toUIntPtr(); - glws::Drawable *drawable = glws::createDrawable(glretrace::visual[glretrace::defaultProfile]); - - drawable->resize(iWidth, iHeight); - drawable->show(); - - pbuffer_map[orig_pbuffer] = drawable; -} - -static void retrace_wglGetPbufferDCARB(trace::Call &call) { - glws::Drawable *pbuffer = pbuffer_map[call.arg(0).toUIntPtr()]; - - unsigned long long orig_hdc = call.ret->toUIntPtr(); - - drawable_map[orig_hdc] = pbuffer; -} - -static void retrace_wglReleasePbufferDCARB(trace::Call &call) { -} - -static void retrace_wglDestroyPbufferARB(trace::Call &call) { -} - -static void retrace_wglQueryPbufferARB(trace::Call &call) { -} - -static void retrace_wglBindTexImageARB(trace::Call &call) { -} - -static void retrace_wglReleaseTexImageARB(trace::Call &call) { -} - -static void retrace_wglSetPbufferAttribARB(trace::Call &call) { -} - -static void retrace_wglCreateContextAttribsARB(trace::Call &call) { - unsigned long long orig_context = call.ret->toUIntPtr(); - glws::Context *share_context = NULL; - - if (call.arg(1).toPointer()) { - share_context = context_map[call.arg(1).toUIntPtr()]; - } - - glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); - context_map[orig_context] = context; -} - -static void retrace_wglMakeContextCurrentEXT(trace::Call &call) { -} - -static void retrace_wglChoosePixelFormatEXT(trace::Call &call) { -} - -static void retrace_wglSwapIntervalEXT(trace::Call &call) { -} - -static void retrace_wglAllocateMemoryNV(trace::Call &call) { -} - -static void retrace_wglFreeMemoryNV(trace::Call &call) { -} - -static void retrace_glAddSwapHintRectWIN(trace::Call &call) { -} - -static void retrace_wglGetProcAddress(trace::Call &call) { -} - -const retrace::Entry glretrace::wgl_callbacks[] = { - {"glAddSwapHintRectWIN", &retrace_glAddSwapHintRectWIN}, - {"wglAllocateMemoryNV", &retrace_wglAllocateMemoryNV}, - {"wglBindTexImageARB", &retrace_wglBindTexImageARB}, - {"wglChoosePixelFormat", &retrace_wglChoosePixelFormat}, - {"wglChoosePixelFormatARB", &retrace_wglChoosePixelFormatARB}, - {"wglChoosePixelFormatEXT", &retrace_wglChoosePixelFormatEXT}, - {"wglCopyContext", &retrace_wglCopyContext}, - {"wglCreateBufferRegionARB", &retrace_wglCreateBufferRegionARB}, - {"wglCreateContext", &retrace_wglCreateContext}, - {"wglCreateContextAttribsARB", &retrace_wglCreateContextAttribsARB}, - {"wglCreateLayerContext", &retrace_wglCreateLayerContext}, - {"wglCreatePbufferARB", &retrace_wglCreatePbufferARB}, - {"wglDeleteBufferRegionARB", &retrace_wglDeleteBufferRegionARB}, - {"wglDeleteContext", &retrace_wglDeleteContext}, - {"wglDescribeLayerPlane", &retrace_wglDescribeLayerPlane}, - {"wglDescribePixelFormat", &retrace_wglDescribePixelFormat}, - {"wglDestroyPbufferARB", &retrace_wglDestroyPbufferARB}, - {"wglFreeMemoryNV", &retrace_wglFreeMemoryNV}, - {"wglGetCurrentContext", &retrace::ignore}, - {"wglGetCurrentDC", &retrace::ignore}, - {"wglGetCurrentReadDCARB", &retrace::ignore}, - {"wglGetCurrentReadDCEXT", &retrace::ignore}, - {"wglGetDefaultProcAddress", &retrace::ignore}, - {"wglGetExtensionsStringARB", &retrace::ignore}, - {"wglGetExtensionsStringEXT", &retrace::ignore}, - {"wglGetLayerPaletteEntries", &retrace::ignore}, - {"wglGetPbufferDCARB", &retrace_wglGetPbufferDCARB}, - {"wglGetPixelFormat", &retrace::ignore}, - {"wglGetPixelFormatAttribfvARB", &retrace::ignore}, - {"wglGetPixelFormatAttribfvEXT", &retrace::ignore}, - {"wglGetPixelFormatAttribivARB", &retrace::ignore}, - {"wglGetPixelFormatAttribivEXT", &retrace::ignore}, - {"wglGetProcAddress", &retrace_wglGetProcAddress}, - {"wglGetSwapIntervalEXT", &retrace::ignore}, - {"wglMakeContextCurrentARB", &retrace_wglMakeContextCurrentARB}, - {"wglMakeContextCurrentEXT", &retrace_wglMakeContextCurrentEXT}, - {"wglMakeCurrent", &retrace_wglMakeCurrent}, - {"wglQueryPbufferARB", &retrace_wglQueryPbufferARB}, - {"wglRealizeLayerPalette", &retrace_wglRealizeLayerPalette}, - {"wglReleasePbufferDCARB", &retrace_wglReleasePbufferDCARB}, - {"wglReleaseTexImageARB", &retrace_wglReleaseTexImageARB}, - {"wglRestoreBufferRegionARB", &retrace_wglRestoreBufferRegionARB}, - {"wglSaveBufferRegionARB", &retrace_wglSaveBufferRegionARB}, - {"wglSetLayerPaletteEntries", &retrace_wglSetLayerPaletteEntries}, - {"wglSetPbufferAttribARB", &retrace_wglSetPbufferAttribARB}, - {"wglSetPixelFormat", &retrace_wglSetPixelFormat}, - {"wglShareLists", &retrace_wglShareLists}, - {"wglSwapBuffers", &retrace_wglSwapBuffers}, - {"wglSwapIntervalEXT", &retrace_wglSwapIntervalEXT}, - {"wglSwapLayerBuffers", &retrace_wglSwapLayerBuffers}, - {"wglSwapMultipleBuffers", &retrace_wglSwapMultipleBuffers}, - {"wglUseFontBitmapsA", &retrace_wglUseFontBitmapsA}, - {"wglUseFontBitmapsW", &retrace_wglUseFontBitmapsW}, - {"wglUseFontOutlinesA", &retrace_wglUseFontOutlinesA}, - {"wglUseFontOutlinesW", &retrace_wglUseFontOutlinesW}, - {NULL, NULL} -}; - diff --git a/glstate.cpp b/glstate.cpp deleted file mode 100644 index 0d5a5f3..0000000 --- a/glstate.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include -#include - -#include "image.hpp" -#include "json.hpp" -#include "glproc.hpp" -#include "glsize.hpp" -#include "glstate.hpp" -#include "glstate_internal.hpp" - - -namespace glstate { - - -Context::Context(void) { - memset(this, 0, sizeof *this); - - const char *version = (const char *)glGetString(GL_VERSION); - if (version) { - if (version[0] == 'O' && - version[1] == 'p' && - version[2] == 'e' && - version[3] == 'n' && - version[4] == 'G' && - version[5] == 'L' && - version[6] == ' ' && - version[7] == 'E' && - version[8] == 'S' && - (version[9] == ' ' || version[9] == '-')) { - ES = true; - } - } - - ARB_draw_buffers = !ES; - - // TODO: Check extensions we use below -} - -void -Context::resetPixelPackState(void) { - if (!ES) { - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); - glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); - glPixelStorei(GL_PACK_SKIP_ROWS, 0); - glPixelStorei(GL_PACK_SKIP_PIXELS, 0); - glPixelStorei(GL_PACK_SKIP_IMAGES, 0); - } else { - packAlignment = 4; - glGetIntegerv(GL_PACK_ALIGNMENT, &packAlignment); - } - glPixelStorei(GL_PACK_ALIGNMENT, 1); -} - -void -Context::restorePixelPackState(void) { - if (!ES) { - glPopClientAttrib(); - } else { - glPixelStorei(GL_PACK_ALIGNMENT, packAlignment); - } -} - - -static const GLenum bindings[] = { - GL_DRAW_BUFFER, - GL_READ_BUFFER, - GL_PIXEL_PACK_BUFFER_BINDING, - GL_PIXEL_UNPACK_BUFFER_BINDING, - GL_TEXTURE_BINDING_1D, - GL_TEXTURE_BINDING_2D, - GL_TEXTURE_BINDING_3D, - GL_TEXTURE_BINDING_RECTANGLE, - GL_TEXTURE_BINDING_CUBE_MAP, - GL_DRAW_FRAMEBUFFER_BINDING, - GL_READ_FRAMEBUFFER_BINDING, - GL_RENDERBUFFER_BINDING, - GL_DRAW_BUFFER0, - GL_DRAW_BUFFER1, - GL_DRAW_BUFFER2, - GL_DRAW_BUFFER3, - GL_DRAW_BUFFER4, - GL_DRAW_BUFFER5, - GL_DRAW_BUFFER6, - GL_DRAW_BUFFER7, -}; - - -#define NUM_BINDINGS sizeof(bindings)/sizeof(bindings[0]) - - -void dumpCurrentContext(std::ostream &os) -{ - JSONWriter json(os); - -#ifndef NDEBUG - GLint old_bindings[NUM_BINDINGS]; - for (unsigned i = 0; i < NUM_BINDINGS; ++i) { - old_bindings[i] = 0; - glGetIntegerv(bindings[i], &old_bindings[i]); - } -#endif - - Context context; - - dumpParameters(json, context); - dumpShadersUniforms(json, context); - dumpTextures(json, context); - dumpFramebuffer(json, context); - -#ifndef NDEBUG - for (unsigned i = 0; i < NUM_BINDINGS; ++i) { - GLint new_binding = 0; - glGetIntegerv(bindings[i], &new_binding); - if (new_binding != old_bindings[i]) { - std::cerr << "warning: " << enumToString(bindings[i]) << " was clobbered\n"; - } - } -#endif - -} - - -} /* namespace glstate */ diff --git a/glstate.hpp b/glstate.hpp deleted file mode 100644 index 6fb615f..0000000 --- a/glstate.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - -#ifndef _GLSTATE_HPP_ -#define _GLSTATE_HPP_ - - -#include - -#include "glimports.hpp" - - -namespace image { - class Image; -} - - -namespace glstate { - - -const char *enumToString(GLenum pname); - -void dumpCurrentContext(std::ostream &os); - -image::Image * -getDrawBufferImage(void); - - -} /* namespace glstate */ - - -#endif /* _GLSTATE_HPP_ */ diff --git a/glstate_images.cpp b/glstate_images.cpp deleted file mode 100644 index f16cba4..0000000 --- a/glstate_images.cpp +++ /dev/null @@ -1,1185 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include -#include - -#include "image.hpp" -#include "json.hpp" -#include "glproc.hpp" -#include "glsize.hpp" -#include "glstate.hpp" -#include "glstate_internal.hpp" - - -#ifdef __linux__ -#include -#endif - -#ifdef __APPLE__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *); - -#ifdef __cplusplus -} -#endif - -#endif /* __APPLE__ */ - - -/* Change thi to one to force interpreting depth buffers as RGBA, which enables - * visualizing full dynamic range, until we can transmit HDR images to the GUI */ -#define DEPTH_AS_RGBA 0 - - -namespace glstate { - - -struct ImageDesc -{ - GLint width; - GLint height; - GLint depth; - GLint internalFormat; - - inline - ImageDesc() : - width(0), - height(0), - depth(0), - internalFormat(GL_NONE) - {} - - inline bool - valid(void) const { - return width > 0 && height > 0 && depth > 0; - } -}; - - -/** - * 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) { - return getActiveTextureLevelDescOES(context, target, level, desc); - } - - glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat); - - desc.width = 0; - glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &desc.width); - - if (target == GL_TEXTURE_1D) { - desc.height = 1; - desc.depth = 1; - } else { - desc.height = 0; - glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &desc.height); - if (target != GL_TEXTURE_3D) { - desc.depth = 1; - } else { - desc.depth = 0; - glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &desc.depth); - } - } - - return desc.valid(); -} - - -/** - * 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 GLboolean -isDepthFormat(GLenum internalFormat) -{ - switch (internalFormat) { - case GL_DEPTH_COMPONENT: - case GL_DEPTH_COMPONENT16: - case GL_DEPTH_COMPONENT24: - case GL_DEPTH_COMPONENT32: - case GL_DEPTH_COMPONENT32F: - case GL_DEPTH_COMPONENT32F_NV: - case GL_DEPTH_STENCIL: - case GL_DEPTH24_STENCIL8: - case GL_DEPTH32F_STENCIL8: - case GL_DEPTH32F_STENCIL8_NV: - return GL_TRUE; - } - return GL_FALSE; -} - - -static inline void -dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level) -{ - ImageDesc desc; - if (!getActiveTextureLevelDesc(context, target, level, desc)) { - return; - } - - char label[512]; - - GLint active_texture = GL_TEXTURE0; - glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture); - snprintf(label, sizeof label, "%s, %s, level = %d", - enumToString(active_texture), enumToString(target), level); - - json.beginMember(label); - - json.beginObject(); - - GLuint channels; - GLenum format; - if (!context.ES && isDepthFormat(desc.internalFormat)) { - format = GL_DEPTH_COMPONENT; - channels = 1; - } else { - format = GL_RGBA; - channels = 4; - } - - // Tell the GUI this is no ordinary object, but an image - json.writeStringMember("__class__", "image"); - - json.writeNumberMember("__width__", desc.width); - json.writeNumberMember("__height__", desc.height); - json.writeNumberMember("__depth__", desc.depth); - - json.writeStringMember("__format__", enumToString(desc.internalFormat)); - - // Hardcoded for now, but we could chose types more adequate to the - // texture internal format - json.writeStringMember("__type__", "uint8"); - json.writeBoolMember("__normalized__", true); - json.writeNumberMember("__channels__", channels); - - GLubyte *pixels = new GLubyte[desc.depth*desc.width*desc.height*channels]; - - context.resetPixelPackState(); - - if (context.ES) { - getTexImageOES(target, level, desc, pixels); - } else { - glGetTexImage(target, level, format, GL_UNSIGNED_BYTE, pixels); - } - - context.restorePixelPackState(); - - json.beginMember("__data__"); - char *pngBuffer; - int pngBufferSize; - image::writePixelsToBuffer(pixels, desc.width, desc.height, channels, true, &pngBuffer, &pngBufferSize); - json.writeBase64(pngBuffer, pngBufferSize); - free(pngBuffer); - json.endMember(); // __data__ - - delete [] pixels; - json.endObject(); -} - - -static inline void -dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding) -{ - GLint texture_binding = 0; - glGetIntegerv(binding, &texture_binding); - if (!glIsEnabled(target) && !texture_binding) { - return; - } - - GLint level = 0; - do { - ImageDesc desc; - if (!getActiveTextureLevelDesc(context, target, level, desc)) { - break; - } - - if (target == GL_TEXTURE_CUBE_MAP) { - for (int face = 0; face < 6; ++face) { - dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level); - } - } else { - dumpActiveTextureLevel(json, context, target, level); - } - - ++level; - } while(true); -} - - -void -dumpTextures(JSONWriter &json, Context &context) -{ - json.beginMember("textures"); - json.beginObject(); - GLint active_texture = GL_TEXTURE0; - glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture); - - GLint max_texture_coords = 0; - glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords); - GLint max_combined_texture_image_units = 0; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units); - GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords); - - /* - * At least the Android software GL implementation doesn't return the - * proper value for this, but rather returns 0. The GL(ES) specification - * mandates a minimum value of 2, so use this as a fall-back value. - */ - max_units = std::min(max_units, 2); - - for (GLint unit = 0; unit < max_units; ++unit) { - GLenum texture = GL_TEXTURE0 + unit; - glActiveTexture(texture); - dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D); - dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D); - dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D); - dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE); - dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP); - } - glActiveTexture(active_texture); - json.endObject(); - json.endMember(); // textures -} - - -static bool -getDrawableBounds(GLint *width, GLint *height) { -#if defined(__linux__) - if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) { - EGLContext currentContext = eglGetCurrentContext(); - if (currentContext == EGL_NO_CONTEXT) { - return false; - } - - EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW); - if (currentSurface == EGL_NO_SURFACE) { - 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; - } - - return true; - } -#endif - -#if defined(_WIN32) - - HDC hDC = wglGetCurrentDC(); - if (!hDC) { - return false; - } - - HWND hWnd = WindowFromDC(hDC); - RECT rect; - - if (!GetClientRect(hWnd, &rect)) { - return false; - } - - *width = rect.right - rect.left; - *height = rect.bottom - rect.top; - return true; - -#elif defined(__APPLE__) - - CGLContextObj ctx = CGLGetCurrentContext(); - if (ctx == NULL) { - return false; - } - - CGSConnectionID cid; - CGSWindowID wid; - CGSSurfaceID sid; - - if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) { - return false; - } - - CGRect rect; - - if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) { - return false; - } - - *width = rect.size.width; - *height = rect.size.height; - return true; - -#elif defined(HAVE_X11) - - Display *display; - Drawable drawable; - Window root; - int x, y; - unsigned int w, h, bw, depth; - - display = glXGetCurrentDisplay(); - if (!display) { - return false; - } - - drawable = glXGetCurrentDrawable(); - if (drawable == None) { - return false; - } - - if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) { - return false; - } - - *width = w; - *height = h; - return true; - -#else - - return false; - -#endif -} - - -static const GLenum texture_bindings[][2] = { - {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D}, - {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D}, - {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D}, - {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE}, - {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP} -}; - - -static bool -bindTexture(GLint texture, GLenum &target, GLint &bound_texture) -{ - - for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) { - target = texture_bindings[i][0]; - - GLenum binding = texture_bindings[i][1]; - - while (glGetError() != GL_NO_ERROR) - ; - - glGetIntegerv(binding, &bound_texture); - glBindTexture(target, texture); - - if (glGetError() == GL_NO_ERROR) { - return true; - } - - glBindTexture(target, bound_texture); - } - - target = GL_NONE; - - return false; -} - - -static bool -getTextureLevelDesc(Context &context, GLint texture, GLint level, ImageDesc &desc) -{ - GLenum target; - GLint bound_texture = 0; - if (!bindTexture(texture, target, bound_texture)) { - return false; - } - - getActiveTextureLevelDesc(context, target, level, desc); - - glBindTexture(target, bound_texture); - - return desc.valid(); -} - - -static bool -getBoundRenderbufferDesc(Context &context, ImageDesc &desc) -{ - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height); - desc.depth = 1; - - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat); - - return desc.valid(); -} - - -static bool -getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc) -{ - GLint bound_renderbuffer = 0; - glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); - - getBoundRenderbufferDesc(context, desc); - - glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer); - - return desc.valid(); -} - - -static bool -getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc) -{ - GLint object_type = GL_NONE; - glGetFramebufferAttachmentParameteriv(target, attachment, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, - &object_type); - if (object_type == GL_NONE) { - return false; - } - - GLint object_name = 0; - glGetFramebufferAttachmentParameteriv(target, attachment, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &object_name); - if (object_name == 0) { - return false; - } - - if (object_type == GL_RENDERBUFFER) { - return getRenderbufferDesc(context, object_name, desc); - } else if (object_type == GL_TEXTURE) { - GLint texture_level = 0; - glGetFramebufferAttachmentParameteriv(target, attachment, - GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, - &texture_level); - return getTextureLevelDesc(context, object_name, texture_level, desc); - } else { - std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n"; - return false; - } -} - - - -image::Image * -getDrawBufferImage() { - GLenum format = GL_RGB; - GLint channels = __gl_format_channels(format); - if (channels > 4) { - return NULL; - } - - Context context; - - GLenum framebuffer_binding; - GLenum framebuffer_target; - if (context.ES) { - framebuffer_binding = GL_FRAMEBUFFER_BINDING; - framebuffer_target = GL_FRAMEBUFFER; - } else { - framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING; - framebuffer_target = GL_DRAW_FRAMEBUFFER; - } - - GLint draw_framebuffer = 0; - glGetIntegerv(framebuffer_binding, &draw_framebuffer); - - GLint draw_buffer = GL_NONE; - ImageDesc desc; - if (draw_framebuffer) { - if (context.ARB_draw_buffers) { - glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer); - if (draw_buffer == GL_NONE) { - return NULL; - } - } - - if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) { - return NULL; - } - } else { - if (!context.ES) { - glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); - if (draw_buffer == GL_NONE) { - return NULL; - } - } - - if (!getDrawableBounds(&desc.width, &desc.height)) { - return NULL; - } - - desc.depth = 1; - } - - GLenum type = GL_UNSIGNED_BYTE; - -#if DEPTH_AS_RGBA - if (format == GL_DEPTH_COMPONENT) { - type = GL_UNSIGNED_INT; - channels = 4; - } -#endif - - image::Image *image = new image::Image(desc.width, desc.height, channels, true); - if (!image) { - return NULL; - } - - while (glGetError() != GL_NO_ERROR) {} - - GLint read_framebuffer = 0; - GLint read_buffer = GL_NONE; - if (!context.ES) { - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer); - glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer); - - glGetIntegerv(GL_READ_BUFFER, &read_buffer); - glReadBuffer(draw_buffer); - } - - // TODO: reset imaging state too - context.resetPixelPackState(); - - glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels); - - context.restorePixelPackState(); - - if (!context.ES) { - glReadBuffer(read_buffer); - glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer); - } - - GLenum error = glGetError(); - if (error != GL_NO_ERROR) { - do { - std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n"; - error = glGetError(); - } while(error != GL_NO_ERROR); - delete image; - return NULL; - } - - return image; -} - - -/** - * Dump the image of the currently bound read buffer. - */ -static inline void -dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format, - GLint internalFormat = GL_NONE) -{ - GLint channels = __gl_format_channels(format); - - Context context; - - json.beginObject(); - - // Tell the GUI this is no ordinary object, but an image - json.writeStringMember("__class__", "image"); - - json.writeNumberMember("__width__", width); - json.writeNumberMember("__height__", height); - json.writeNumberMember("__depth__", 1); - - json.writeStringMember("__format__", enumToString(internalFormat)); - - // Hardcoded for now, but we could chose types more adequate to the - // texture internal format - json.writeStringMember("__type__", "uint8"); - json.writeBoolMember("__normalized__", true); - json.writeNumberMember("__channels__", channels); - - GLenum type = GL_UNSIGNED_BYTE; - -#if DEPTH_AS_RGBA - if (format == GL_DEPTH_COMPONENT) { - type = GL_UNSIGNED_INT; - channels = 4; - } -#endif - - GLubyte *pixels = new GLubyte[width*height*channels]; - - // TODO: reset imaging state too - context.resetPixelPackState(); - - glReadPixels(0, 0, width, height, format, type, pixels); - - context.restorePixelPackState(); - - json.beginMember("__data__"); - char *pngBuffer; - int pngBufferSize; - image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize); - //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels) - // <<", after = "<= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) { - attachment = draw_buffer; - } else { - std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n"; - attachment = GL_COLOR_ATTACHMENT0; - } - GLint alpha_size = 0; - glGetFramebufferAttachmentParameteriv(target, attachment, - GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, - &alpha_size); - GLenum format = alpha_size ? GL_RGBA : GL_RGB; - dumpFramebufferAttachment(json, context, target, attachment, format); - } - } - - glReadBuffer(read_buffer); - - if (!context.ES) { - dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT); - dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer); -} - - -void -dumpFramebuffer(JSONWriter &json, Context &context) -{ - json.beginMember("framebuffer"); - json.beginObject(); - - GLint boundDrawFbo = 0, boundReadFbo = 0; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo); - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo); - if (!boundDrawFbo) { - dumpDrawableImages(json, context); - } else if (context.ES) { - dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER); - } else { - GLint colorRb = 0, stencilRb = 0, depthRb = 0; - GLint draw_buffer0 = GL_NONE; - glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0); - bool multisample = false; - - GLint boundRb = 0; - glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb); - - GLint object_type; - glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); - if (object_type == GL_RENDERBUFFER) { - glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb); - glBindRenderbuffer(GL_RENDERBUFFER, colorRb); - GLint samples = 0; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); - if (samples) { - multisample = true; - } - } - - glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); - if (object_type == GL_RENDERBUFFER) { - glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb); - glBindRenderbuffer(GL_RENDERBUFFER, depthRb); - GLint samples = 0; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); - if (samples) { - multisample = true; - } - } - - glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); - if (object_type == GL_RENDERBUFFER) { - glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb); - glBindRenderbuffer(GL_RENDERBUFFER, stencilRb); - GLint samples = 0; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); - if (samples) { - multisample = true; - } - } - - glBindRenderbuffer(GL_RENDERBUFFER, boundRb); - - GLuint rbs[3]; - GLint numRbs = 0; - GLuint fboCopy = 0; - - if (multisample) { - // glReadPixels doesnt support multisampled buffers so we need - // to blit the fbo to a temporary one - fboCopy = downsampledFramebuffer(context, - boundDrawFbo, draw_buffer0, - colorRb, depthRb, stencilRb, - rbs, &numRbs); - } - - dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER); - - if (multisample) { - glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb); - glDeleteRenderbuffers(numRbs, rbs); - glDeleteFramebuffers(1, &fboCopy); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo); - } - - json.endObject(); - json.endMember(); // framebuffer -} - - -} /* namespace glstate */ diff --git a/glstate_internal.hpp b/glstate_internal.hpp deleted file mode 100644 index aab7f98..0000000 --- a/glstate_internal.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - -#ifndef _GLSTATE_INTERNAL_HPP_ -#define _GLSTATE_INTERNAL_HPP_ - - -#include "glimports.hpp" - - -class JSONWriter; - - -namespace glstate { - - -struct Context -{ - bool ES; - - bool ARB_draw_buffers; - - Context(void); - - GLint packAlignment; - - void - resetPixelPackState(void); - - void - restorePixelPackState(void); -}; - - -void dumpEnum(JSONWriter &json, GLenum pname); - -void dumpParameters(JSONWriter &json, Context &context); - -void dumpShadersUniforms(JSONWriter &json, Context &context); - -void dumpTextures(JSONWriter &json, Context &context); - -void dumpFramebuffer(JSONWriter &json, Context &context); - - -} /* namespace glstate */ - - -#endif /* _GLSTATE_INTERNAL_HPP_ */ diff --git a/glstate_params.py b/glstate_params.py deleted file mode 100644 index 7c29932..0000000 --- a/glstate_params.py +++ /dev/null @@ -1,503 +0,0 @@ -########################################################################## -# -# Copyright 2011 Jose Fonseca -# 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. -# -##########################################################################/ - - -'''Generate code to dump most GL state into JSON.''' - - -from specs.stdapi import * - -from specs.gltypes import * -from specs.glparams import * - - -texture_targets = [ - ('GL_TEXTURE_1D', 'GL_TEXTURE_BINDING_1D'), - ('GL_TEXTURE_2D', 'GL_TEXTURE_BINDING_2D'), - ('GL_TEXTURE_3D', 'GL_TEXTURE_BINDING_3D'), - ('GL_TEXTURE_RECTANGLE', 'GL_TEXTURE_BINDING_RECTANGLE'), - ('GL_TEXTURE_CUBE_MAP', 'GL_TEXTURE_BINDING_CUBE_MAP') -] - -framebuffer_targets = [ - ('GL_DRAW_FRAMEBUFFER', 'GL_DRAW_FRAMEBUFFER_BINDING'), - ('GL_READ_FRAMEBUFFER', 'GL_READ_FRAMEBUFFER_BINDING'), -] - -class GetInflector: - '''Objects that describes how to inflect.''' - - reduced_types = { - B: I, - E: I, - I: F, - } - - def __init__(self, radical, inflections, suffix = ''): - self.radical = radical - self.inflections = inflections - self.suffix = suffix - - def reduced_type(self, type): - if type in self.inflections: - return type - if type in self.reduced_types: - return self.reduced_type(self.reduced_types[type]) - raise NotImplementedError - - def inflect(self, type): - return self.radical + self.inflection(type) + self.suffix - - def inflection(self, type): - type = self.reduced_type(type) - assert type in self.inflections - return self.inflections[type] - - def __str__(self): - return self.radical + self.suffix - - -class StateGetter(Visitor): - '''Type visitor that is able to extract the state via one of the glGet* - functions. - - It will declare any temporary variable - ''' - - def __init__(self, radical, inflections, suffix=''): - self.inflector = GetInflector(radical, inflections) - self.suffix = suffix - - def iter(self): - for function, type, count, name in parameters: - inflection = self.inflector.radical + self.suffix - if inflection not in function.split(','): - continue - if type is X: - continue - yield type, count, name - - def __call__(self, *args): - pname = args[-1] - - for type, count, name in self.iter(): - if name == pname: - if count != 1: - type = Array(type, str(count)) - - return type, self.visit(type, args) - - raise NotImplementedError - - def temp_name(self, args): - '''Return the name of a temporary variable to hold the state.''' - pname = args[-1] - - return pname[3:].lower() - - def visitConst(self, const, args): - return self.visit(const.type, args) - - def visitScalar(self, type, args): - temp_name = self.temp_name(args) - elem_type = self.inflector.reduced_type(type) - inflection = self.inflector.inflect(type) - if inflection.endswith('v'): - print ' %s %s = 0;' % (elem_type, temp_name) - print ' %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name) - else: - print ' %s %s = %s(%s);' % (elem_type, temp_name, inflection + self.suffix, ', '.join(args)) - return temp_name - - def visitString(self, string, args): - temp_name = self.temp_name(args) - inflection = self.inflector.inflect(string) - assert not inflection.endswith('v') - print ' %s %s = (%s)%s(%s);' % (string, temp_name, string, inflection + self.suffix, ', '.join(args)) - return temp_name - - def visitAlias(self, alias, args): - return self.visitScalar(alias, args) - - def visitEnum(self, enum, args): - return self.visit(GLint, args) - - def visitBitmask(self, bitmask, args): - return self.visit(GLint, args) - - def visitArray(self, array, args): - temp_name = self.temp_name(args) - if array.length == '1': - return self.visit(array.type) - elem_type = self.inflector.reduced_type(array.type) - inflection = self.inflector.inflect(array.type) - assert inflection.endswith('v') - print ' %s %s[%s + 1];' % (elem_type, temp_name, array.length) - print ' memset(%s, 0, %s * sizeof *%s);' % (temp_name, array.length, temp_name) - print ' %s[%s] = (%s)0xdeadc0de;' % (temp_name, array.length, elem_type) - print ' %s(%s, %s);' % (inflection + self.suffix, ', '.join(args), temp_name) - # Simple buffer overflow detection - print ' assert(%s[%s] == (%s)0xdeadc0de);' % (temp_name, array.length, elem_type) - return temp_name - - def visitOpaque(self, pointer, args): - temp_name = self.temp_name(args) - inflection = self.inflector.inflect(pointer) - assert inflection.endswith('v') - print ' GLvoid *%s;' % temp_name - print ' %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name) - return temp_name - - -glGet = StateGetter('glGet', { - B: 'Booleanv', - I: 'Integerv', - F: 'Floatv', - D: 'Doublev', - S: 'String', - P: 'Pointerv', -}) - -glGetMaterial = StateGetter('glGetMaterial', {I: 'iv', F: 'fv'}) -glGetLight = StateGetter('glGetLight', {I: 'iv', F: 'fv'}) -glGetVertexAttrib = StateGetter('glGetVertexAttrib', {I: 'iv', F: 'fv', D: 'dv', P: 'Pointerv'}) -glGetTexParameter = StateGetter('glGetTexParameter', {I: 'iv', F: 'fv'}) -glGetTexEnv = StateGetter('glGetTexEnv', {I: 'iv', F: 'fv'}) -glGetTexLevelParameter = StateGetter('glGetTexLevelParameter', {I: 'iv', F: 'fv'}) -glGetShader = StateGetter('glGetShaderiv', {I: 'iv'}) -glGetProgram = StateGetter('glGetProgram', {I: 'iv'}) -glGetProgramARB = StateGetter('glGetProgram', {I: 'iv', F: 'fv', S: 'Stringv'}, 'ARB') -glGetFramebufferAttachmentParameter = StateGetter('glGetFramebufferAttachmentParameter', {I: 'iv'}) - - -class JsonWriter(Visitor): - '''Type visitor that will dump a value of the specified type through the - JSON writer. - - It expects a previously declared JSONWriter instance named "json".''' - - def visitLiteral(self, literal, instance): - if literal.kind == 'Bool': - print ' json.writeBool(%s);' % instance - elif literal.kind in ('SInt', 'Uint', 'Float', 'Double'): - print ' json.writeNumber(%s);' % instance - else: - raise NotImplementedError - - def visitString(self, string, instance): - assert string.length is None - print ' json.writeString((const char *)%s);' % instance - - def visitEnum(self, enum, instance): - if enum.expr == 'GLenum': - print ' dumpEnum(json, %s);' % instance - else: - print ' json.writeNumber(%s);' % instance - - def visitBitmask(self, bitmask, instance): - raise NotImplementedError - - def visitAlias(self, alias, instance): - self.visit(alias.type, instance) - - def visitOpaque(self, opaque, instance): - print ' json.writeNumber((size_t)%s);' % instance - - __index = 0 - - def visitArray(self, array, instance): - index = '__i%u' % JsonWriter.__index - JsonWriter.__index += 1 - print ' json.beginArray();' - print ' for (unsigned %s = 0; %s < %s; ++%s) {' % (index, index, array.length, index) - self.visit(array.type, '%s[%s]' % (instance, index)) - print ' }' - print ' json.endArray();' - - - -class StateDumper: - '''Class to generate code to dump all GL state in JSON format via - stdout.''' - - def __init__(self): - pass - - def dump(self): - print '#include ' - print - print '#include "json.hpp"' - print '#include "glproc.hpp"' - print '#include "glsize.hpp"' - print '#include "glstate.hpp"' - print '#include "glstate_internal.hpp"' - print - print 'namespace glstate {' - print - - print 'const char *' - print 'enumToString(GLenum pname)' - print '{' - print ' switch (pname) {' - for name in GLenum.values: - print ' case %s:' % name - print ' return "%s";' % name - print ' default:' - print ' return NULL;' - print ' }' - print '}' - print - - print 'static void' - print 'dumpFramebufferAttachementParameters(JSONWriter &json, GLenum target, GLenum attachment)' - print '{' - self.dump_attachment_parameters('target', 'attachment') - print '}' - print - - print 'void' - print 'dumpEnum(JSONWriter &json, GLenum pname)' - print '{' - print ' const char *s = enumToString(pname);' - print ' if (s) {' - print ' json.writeString(s);' - print ' } else {' - print ' json.writeNumber(pname);' - print ' }' - print '}' - print - - print 'void dumpParameters(JSONWriter &json, Context &context)' - print '{' - print ' json.beginMember("parameters");' - print ' json.beginObject();' - - self.dump_atoms(glGet) - - self.dump_material_params() - self.dump_light_params() - self.dump_vertex_attribs() - self.dump_program_params() - self.dump_texture_parameters() - self.dump_framebuffer_parameters() - - print ' json.endObject();' - print ' json.endMember(); // parameters' - print '}' - print - - print '} /*namespace glstate */' - - def dump_material_params(self): - print ' if (!context.ES) {' - for face in ['GL_FRONT', 'GL_BACK']: - print ' json.beginMember("%s");' % face - print ' json.beginObject();' - self.dump_atoms(glGetMaterial, face) - print ' json.endObject();' - print ' }' - print - - def dump_light_params(self): - print ' GLint max_lights = 0;' - print ' __glGetIntegerv(GL_MAX_LIGHTS, &max_lights);' - print ' for (GLint index = 0; index < max_lights; ++index) {' - print ' GLenum light = GL_LIGHT0 + index;' - print ' if (glIsEnabled(light)) {' - print ' char name[32];' - print ' snprintf(name, sizeof name, "GL_LIGHT%i", index);' - print ' json.beginMember(name);' - print ' json.beginObject();' - self.dump_atoms(glGetLight, ' GL_LIGHT0 + index') - print ' json.endObject();' - print ' json.endMember(); // GL_LIGHTi' - print ' }' - print ' }' - print - - def texenv_param_target(self, name): - if name == 'GL_TEXTURE_LOD_BIAS': - return 'GL_TEXTURE_FILTER_CONTROL' - elif name == 'GL_COORD_REPLACE': - return 'GL_POINT_SPRITE' - else: - return 'GL_TEXTURE_ENV' - - def dump_texenv_params(self): - for target in ['GL_TEXTURE_ENV', 'GL_TEXTURE_FILTER_CONTROL', 'GL_POINT_SPRITE']: - print ' if (!context.ES) {' - print ' json.beginMember("%s");' % target - print ' json.beginObject();' - for _, _, name in glGetTexEnv.iter(): - if self.texenv_param_target(name) == target: - self.dump_atom(glGetTexEnv, target, name) - print ' json.endObject();' - print ' }' - - def dump_vertex_attribs(self): - print ' GLint max_vertex_attribs = 0;' - print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs);' - print ' for (GLint index = 0; index < max_vertex_attribs; ++index) {' - print ' char name[32];' - print ' snprintf(name, sizeof name, "GL_VERTEX_ATTRIB_ARRAY%i", index);' - print ' json.beginMember(name);' - print ' json.beginObject();' - self.dump_atoms(glGetVertexAttrib, 'index') - print ' json.endObject();' - print ' json.endMember(); // GL_VERTEX_ATTRIB_ARRAYi' - print ' }' - print - - program_targets = [ - 'GL_FRAGMENT_PROGRAM_ARB', - 'GL_VERTEX_PROGRAM_ARB', - ] - - def dump_program_params(self): - for target in self.program_targets: - print ' if (glIsEnabled(%s)) {' % target - print ' json.beginMember("%s");' % target - print ' json.beginObject();' - self.dump_atoms(glGetProgramARB, target) - print ' json.endObject();' - print ' }' - - def dump_texture_parameters(self): - print ' {' - print ' GLint active_texture = GL_TEXTURE0;' - print ' glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);' - print ' GLint max_texture_coords = 0;' - print ' glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);' - print ' GLint max_combined_texture_image_units = 0;' - print ' glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);' - print ' GLint max_units = std::min(std::max(max_combined_texture_image_units, max_texture_coords), 2);' - print ' for (GLint unit = 0; unit < max_units; ++unit) {' - print ' char name[32];' - print ' snprintf(name, sizeof name, "GL_TEXTURE%i", unit);' - print ' json.beginMember(name);' - print ' glActiveTexture(GL_TEXTURE0 + unit);' - print ' json.beginObject();' - print ' GLboolean enabled;' - print ' GLint binding;' - print - for target, binding in texture_targets: - print ' // %s' % target - print ' enabled = GL_FALSE;' - print ' glGetBooleanv(%s, &enabled);' % target - print ' json.writeBoolMember("%s", enabled);' % target - print ' binding = 0;' - print ' glGetIntegerv(%s, &binding);' % binding - print ' json.writeNumberMember("%s", binding);' % binding - print ' if (enabled || binding) {' - print ' json.beginMember("%s");' % target - print ' json.beginObject();' - self.dump_atoms(glGetTexParameter, target) - print ' if (!context.ES) {' - # We only dump the first level parameters - self.dump_atoms(glGetTexLevelParameter, target, "0") - print ' }' - print ' json.endObject();' - print ' json.endMember(); // %s' % target - print ' }' - print - print ' if (unit < max_texture_coords) {' - self.dump_texenv_params() - print ' }' - print ' json.endObject();' - print ' json.endMember(); // GL_TEXTUREi' - print ' }' - print ' glActiveTexture(active_texture);' - print ' }' - print - - def dump_framebuffer_parameters(self): - print ' {' - print ' GLint max_color_attachments = 0;' - print ' glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);' - print ' GLint framebuffer;' - for target, binding in framebuffer_targets: - print ' // %s' % target - print ' framebuffer = 0;' - print ' glGetIntegerv(%s, &framebuffer);' % binding - print ' if (framebuffer) {' - print ' json.beginMember("%s");' % target - print ' json.beginObject();' - print ' for (GLint i = 0; i < max_color_attachments; ++i) {' - print ' GLint color_attachment = GL_COLOR_ATTACHMENT0 + i;' - print ' dumpFramebufferAttachementParameters(json, %s, color_attachment);' % target - print ' }' - print ' dumpFramebufferAttachementParameters(json, %s, GL_DEPTH_ATTACHMENT);' % target - print ' dumpFramebufferAttachementParameters(json, %s, GL_STENCIL_ATTACHMENT);' % target - print ' json.endObject();' - print ' json.endMember(); // %s' % target - print ' }' - print - print ' }' - print - - def dump_attachment_parameters(self, target, attachment): - print ' {' - print ' GLint object_type = GL_NONE;' - print ' glGetFramebufferAttachmentParameteriv(%s, %s, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);' % (target, attachment) - print ' if (object_type != GL_NONE) {' - print ' json.beginMember(enumToString(%s));' % attachment - print ' json.beginObject();' - self.dump_atoms(glGetFramebufferAttachmentParameter, target, attachment) - print ' json.endObject();' - print ' json.endMember(); // GL_x_ATTACHMENT' - print ' }' - print ' }' - - def dump_atoms(self, getter, *args): - for _, _, name in getter.iter(): - self.dump_atom(getter, *(args + (name,))) - - def dump_atom(self, getter, *args): - name = args[-1] - - # Avoid crash on MacOSX - # XXX: The right fix would be to look at the support extensions.. - import platform - if name == 'GL_SAMPLER_BINDING' and platform.system() == 'Darwin': - return - - print ' // %s' % name - print ' {' - #print ' assert(glGetError() == GL_NO_ERROR);' - type, value = getter(*args) - print ' if (glGetError() != GL_NO_ERROR) {' - #print ' std::cerr << "warning: %s(%s) failed\\n";' % (inflection, name) - print ' while (glGetError() != GL_NO_ERROR) {}' - print ' } else {' - print ' json.beginMember("%s");' % name - JsonWriter().visit(type, value) - print ' json.endMember();' - print ' }' - print ' }' - print - - -if __name__ == '__main__': - StateDumper().dump() diff --git a/glstate_shaders.cpp b/glstate_shaders.cpp deleted file mode 100644 index 11286fb..0000000 --- a/glstate_shaders.cpp +++ /dev/null @@ -1,532 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include -#include -#include -#include - -#include "json.hpp" -#include "glproc.hpp" -#include "glsize.hpp" -#include "glstate.hpp" -#include "glstate_internal.hpp" - - -namespace glstate { - - -// Mapping from shader type to shader source, used to accumulated the sources -// of different shaders with same type. -typedef std::map ShaderMap; - - -static void -getShaderSource(ShaderMap &shaderMap, GLuint shader) -{ - if (!shader) { - return; - } - - GLint shader_type = 0; - glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type); - if (!shader_type) { - return; - } - - GLint source_length = 0; - glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length); - if (!source_length) { - return; - } - - GLchar *source = new GLchar[source_length]; - GLsizei length = 0; - source[0] = 0; - glGetShaderSource(shader, source_length, &length, source); - - shaderMap[enumToString(shader_type)] += source; - - delete [] source; -} - - -static void -getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj) -{ - if (!shaderObj) { - return; - } - - GLint object_type = 0; - glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &object_type); - if (object_type != GL_SHADER_OBJECT_ARB) { - return; - } - - GLint shader_type = 0; - glGetObjectParameterivARB(shaderObj, GL_OBJECT_SUBTYPE_ARB, &shader_type); - if (!shader_type) { - return; - } - - GLint source_length = 0; - glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length); - if (!source_length) { - return; - } - - GLcharARB *source = new GLcharARB[source_length]; - GLsizei length = 0; - source[0] = 0; - glGetShaderSource(shaderObj, source_length, &length, source); - - shaderMap[enumToString(shader_type)] += source; - - delete [] source; -} - - -static inline void -dumpProgram(JSONWriter &json, GLint program) -{ - GLint attached_shaders = 0; - glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders); - if (!attached_shaders) { - return; - } - - ShaderMap shaderMap; - - GLuint *shaders = new GLuint[attached_shaders]; - GLsizei count = 0; - glGetAttachedShaders(program, attached_shaders, &count, shaders); - std::sort(shaders, shaders + count); - for (GLsizei i = 0; i < count; ++ i) { - getShaderSource(shaderMap, shaders[i]); - } - delete [] shaders; - - for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) { - json.beginMember(it->first); - json.writeString(it->second); - json.endMember(); - } -} - - -static inline void -dumpProgramObj(JSONWriter &json, GLhandleARB programObj) -{ - GLint attached_shaders = 0; - glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders); - if (!attached_shaders) { - return; - } - - ShaderMap shaderMap; - - GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders]; - GLsizei count = 0; - glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs); - std::sort(shaderObjs, shaderObjs + count); - for (GLsizei i = 0; i < count; ++ i) { - getShaderObjSource(shaderMap, shaderObjs[i]); - } - delete [] shaderObjs; - - for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) { - json.beginMember(it->first); - json.writeString(it->second); - json.endMember(); - } -} - -/* - * When fetching the uniform name of an array we usually get name[0] - * so we need to cut the trailing "[0]" in order to properly construct - * array names later. Otherwise we endup with stuff like - * uniformArray[0][0], - * uniformArray[0][1], - * instead of - * uniformArray[0], - * uniformArray[1]. - */ -static std::string -resolveUniformName(const GLchar *name, GLint size) -{ - std::string qualifiedName(name); - if (size > 1) { - std::string::size_type nameLength = qualifiedName.length(); - static const char * const arrayStart = "[0]"; - static const int arrayStartLen = 3; - if (qualifiedName.rfind(arrayStart) == (nameLength - arrayStartLen)) { - qualifiedName = qualifiedName.substr(0, nameLength - 3); - } - } - return qualifiedName; -} - -static void -dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) { - GLenum elemType; - GLint numElems; - __gl_uniform_size(type, elemType, numElems); - if (elemType == GL_NONE) { - return; - } - - GLfloat fvalues[4*4]; - GLdouble dvalues[4*4]; - GLint ivalues[4*4]; - GLuint uivalues[4*4]; - - GLint i, j; - - std::string qualifiedName = resolveUniformName(name, size); - - for (i = 0; i < size; ++i) { - std::stringstream ss; - ss << qualifiedName; - - if (size > 1) { - ss << '[' << i << ']'; - } - - std::string elemName = ss.str(); - - json.beginMember(elemName); - - GLint location = glGetUniformLocation(program, elemName.c_str()); - - if (numElems > 1) { - json.beginArray(); - } - - switch (elemType) { - case GL_FLOAT: - glGetUniformfv(program, location, fvalues); - for (j = 0; j < numElems; ++j) { - json.writeNumber(fvalues[j]); - } - break; - case GL_DOUBLE: - glGetUniformdv(program, location, dvalues); - for (j = 0; j < numElems; ++j) { - json.writeNumber(dvalues[j]); - } - break; - case GL_INT: - glGetUniformiv(program, location, ivalues); - for (j = 0; j < numElems; ++j) { - json.writeNumber(ivalues[j]); - } - break; - case GL_UNSIGNED_INT: - glGetUniformuiv(program, location, uivalues); - for (j = 0; j < numElems; ++j) { - json.writeNumber(uivalues[j]); - } - break; - case GL_BOOL: - glGetUniformiv(program, location, ivalues); - for (j = 0; j < numElems; ++j) { - json.writeBool(ivalues[j]); - } - break; - default: - assert(0); - break; - } - - if (numElems > 1) { - json.endArray(); - } - - json.endMember(); - } -} - - -static void -dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) { - - GLenum elemType; - GLint numElems; - __gl_uniform_size(type, elemType, numElems); - if (elemType == GL_NONE) { - return; - } - - GLfloat fvalues[4*4]; - GLint ivalues[4*4]; - - GLint i, j; - - std::string qualifiedName = resolveUniformName(name, size); - - for (i = 0; i < size; ++i) { - std::stringstream ss; - ss << qualifiedName; - - if (size > 1) { - ss << '[' << i << ']'; - } - - std::string elemName = ss.str(); - - json.beginMember(elemName); - - GLint location = glGetUniformLocationARB(programObj, elemName.c_str()); - - if (numElems > 1) { - json.beginArray(); - } - - switch (elemType) { - case GL_DOUBLE: - // glGetUniformdvARB does not exists - case GL_FLOAT: - glGetUniformfvARB(programObj, location, fvalues); - for (j = 0; j < numElems; ++j) { - json.writeNumber(fvalues[j]); - } - break; - case GL_UNSIGNED_INT: - // glGetUniformuivARB does not exists - case GL_INT: - glGetUniformivARB(programObj, location, ivalues); - for (j = 0; j < numElems; ++j) { - json.writeNumber(ivalues[j]); - } - break; - case GL_BOOL: - glGetUniformivARB(programObj, location, ivalues); - for (j = 0; j < numElems; ++j) { - json.writeBool(ivalues[j]); - } - break; - default: - assert(0); - break; - } - - if (numElems > 1) { - json.endArray(); - } - - json.endMember(); - } -} - - -static inline void -dumpProgramUniforms(JSONWriter &json, GLint program) -{ - GLint active_uniforms = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms); - if (!active_uniforms) { - return; - } - - GLint active_uniform_max_length = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length); - GLchar *name = new GLchar[active_uniform_max_length]; - if (!name) { - return; - } - - for (GLint index = 0; index < active_uniforms; ++index) { - GLsizei length = 0; - GLint size = 0; - GLenum type = GL_NONE; - glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name); - - dumpUniform(json, program, size, type, name); - } - - delete [] name; -} - - -static inline void -dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj) -{ - GLint active_uniforms = 0; - glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms); - if (!active_uniforms) { - return; - } - - GLint active_uniform_max_length = 0; - glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length); - GLchar *name = new GLchar[active_uniform_max_length]; - if (!name) { - return; - } - - for (GLint index = 0; index < active_uniforms; ++index) { - GLsizei length = 0; - GLint size = 0; - GLenum type = GL_NONE; - glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name); - - dumpUniformARB(json, programObj, size, type, name); - } - - delete [] name; -} - - -static inline void -dumpArbProgram(JSONWriter &json, GLenum target) -{ - if (!glIsEnabled(target)) { - return; - } - - GLint program_length = 0; - glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length); - if (!program_length) { - return; - } - - GLchar *source = new GLchar[program_length + 1]; - source[0] = 0; - glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source); - source[program_length] = 0; - - json.beginMember(enumToString(target)); - json.writeString(source); - json.endMember(); - - delete [] source; -} - - -static inline void -dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix) -{ - if (!glIsEnabled(target)) { - return; - } - - GLint program_parameters = 0; - glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &program_parameters); - if (!program_parameters) { - return; - } - - GLint max_program_local_parameters = 0; - glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &max_program_local_parameters); - for (GLint index = 0; index < max_program_local_parameters; ++index) { - GLdouble params[4] = {0, 0, 0, 0}; - glGetProgramLocalParameterdvARB(target, index, params); - - if (!params[0] && !params[1] && !params[2] && !params[3]) { - continue; - } - - char name[256]; - snprintf(name, sizeof name, "%sprogram.local[%i]", prefix, index); - - json.beginMember(name); - json.beginArray(); - json.writeNumber(params[0]); - json.writeNumber(params[1]); - json.writeNumber(params[2]); - json.writeNumber(params[3]); - json.endArray(); - json.endMember(); - } - - GLint max_program_env_parameters = 0; - glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &max_program_env_parameters); - for (GLint index = 0; index < max_program_env_parameters; ++index) { - GLdouble params[4] = {0, 0, 0, 0}; - glGetProgramEnvParameterdvARB(target, index, params); - - if (!params[0] && !params[1] && !params[2] && !params[3]) { - continue; - } - - char name[256]; - snprintf(name, sizeof name, "%sprogram.env[%i]", prefix, index); - - json.beginMember(name); - json.beginArray(); - json.writeNumber(params[0]); - json.writeNumber(params[1]); - json.writeNumber(params[2]); - json.writeNumber(params[3]); - json.endArray(); - json.endMember(); - } -} - - -void -dumpShadersUniforms(JSONWriter &json, Context &context) -{ - GLint program = 0; - glGetIntegerv(GL_CURRENT_PROGRAM, &program); - - GLhandleARB programObj = 0; - if (!context.ES && !program) { - programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB); - } - - json.beginMember("shaders"); - json.beginObject(); - if (program) { - dumpProgram(json, program); - } else if (programObj) { - dumpProgramObj(json, programObj); - } else { - dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB); - dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB); - } - json.endObject(); - json.endMember(); // shaders - - json.beginMember("uniforms"); - json.beginObject(); - if (program) { - dumpProgramUniforms(json, program); - } else if (programObj) { - dumpProgramObjUniforms(json, programObj); - } else { - dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp."); - dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp."); - } - json.endObject(); - json.endMember(); // uniforms -} - - -} /* namespace glstate */ diff --git a/glws.cpp b/glws.cpp deleted file mode 100644 index c5c41eb..0000000 --- a/glws.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include "glws.hpp" - - -namespace glws { - - -bool debug = true; - - -bool -checkExtension(const char *extName, const char *extString) -{ - const char *p = extString; - const char *q = extName; - char c; - do { - c = *p++; - if (c == '\0' || c == ' ') { - if (q && *q == '\0') { - return true; - } else { - q = extName; - } - } else { - if (q && *q == c) { - ++q; - } else { - q = 0; - } - } - } while (c); - return false; -} - - -} /* namespace glws */ diff --git a/glws.hpp b/glws.hpp deleted file mode 100644 index 9afebf4..0000000 --- a/glws.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - -/* - * Abstraction for GL window system specific APIs (GLX, WGL). - */ - -#ifndef _GLWS_HPP_ -#define _GLWS_HPP_ - - -#include - - -namespace glws { - - -enum Profile { - PROFILE_COMPAT = 0, - PROFILE_CORE, - PROFILE_ES1, - PROFILE_ES2, - PROFILE_MAX -}; - - -extern bool debug; - - -bool -checkExtension(const char *extName, const char *extString); - - -template< class T > -class Attributes { -protected: - std::vector attribs; - -public: - void add(T param) { - attribs.push_back(param); - } - - void add(T pname, T pvalue) { - add(pname); - add(pvalue); - } - - void end(T terminator = 0) { - add(terminator); - } - - operator T * (void) { - return &attribs[0]; - } - - operator const T * (void) const { - return &attribs[0]; - } -}; - - -class Visual -{ -public: - unsigned long redMask; - unsigned long greenMask; - unsigned long blueMask; - unsigned long alphaMask; - bool doubleBuffer; - - virtual ~Visual() {} -}; - - -class Drawable -{ -public: - const Visual *visual; - int width; - int height; - bool visible; - - Drawable(const Visual *vis, int w, int h) : - visual(vis), - width(w), - height(h), - visible(false) - {} - - virtual ~Drawable() {} - - virtual void - resize(int w, int h) { - width = w; - height = h; - } - - virtual void - show(void) { - visible = true; - } - - virtual void swapBuffers(void) = 0; -}; - - -class Context -{ -public: - const Visual *visual; - Profile profile; - - Context(const Visual *vis, Profile prof) : - visual(vis), - profile(prof) - {} - - virtual ~Context() {} -}; - - -void -init(void); - -void -cleanup(void); - -Visual * -createVisual(bool doubleBuffer = false, Profile profile = PROFILE_COMPAT); - -Drawable * -createDrawable(const Visual *visual, int width = 32, int height = 32); - -Context * -createContext(const Visual *visual, Context *shareContext = 0, Profile profile = PROFILE_COMPAT); - -bool -makeCurrent(Drawable *drawable, Context *context); - -bool -processEvents(void); - - -} /* namespace glws */ - - -#endif /* _GLWS_HPP_ */ diff --git a/glws_cocoa.mm b/glws_cocoa.mm deleted file mode 100644 index 7f696fa..0000000 --- a/glws_cocoa.mm +++ /dev/null @@ -1,270 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 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. - * - **************************************************************************/ - - -/** - * Minimal Cocoa integration. - * - * See also: - * - http://developer.apple.com/library/mac/#samplecode/CocoaGL/Introduction/Intro.html - * - http://developer.apple.com/library/mac/#samplecode/Cocoa_With_Carbon_or_CPP/Introduction/Intro.html - * - http://developer.apple.com/library/mac/#samplecode/glut/Introduction/Intro.html - * - http://developer.apple.com/library/mac/#samplecode/GLEssentials/Introduction/Intro.html - * - http://www.glfw.org/ - */ - - -#include -#include - -#include - -#include "glws.hpp" - - -namespace glws { - - -NSAutoreleasePool *autoreleasePool = nil; - - -class CocoaVisual : public Visual -{ -public: - NSOpenGLPixelFormat *pixelFormat; - - CocoaVisual(NSOpenGLPixelFormat *pf) : - pixelFormat(pf) - {} - - ~CocoaVisual() { - [pixelFormat release]; - } -}; - - -class CocoaDrawable : public Drawable -{ -public: - NSWindow *window; - NSOpenGLContext *currentContext; - - CocoaDrawable(const Visual *vis, int w, int h) : - Drawable(vis, w, h), - currentContext(nil) - { - NSOpenGLPixelFormat *pixelFormat = static_cast(visual)->pixelFormat; - - NSRect winRect = NSMakeRect(0, 0, w, h); - - window = [[NSWindow alloc] - initWithContentRect:winRect - styleMask:NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask - backing:NSBackingStoreRetained - defer:NO]; - assert(window != nil); - - NSOpenGLView *view = [[NSOpenGLView alloc] - initWithFrame:winRect - pixelFormat:pixelFormat]; - assert(view != nil); - - [window setContentView:view]; - [window setTitle:@"glretrace"]; - - } - - ~CocoaDrawable() { - [window release]; - } - - void - resize(int w, int h) { - if (w == width && h == height) { - return; - } - - [window setContentSize:NSMakeSize(w, h)]; - - if (currentContext != nil) { - [currentContext update]; - [window makeKeyAndOrderFront:nil]; - [currentContext setView:[window contentView]]; - [currentContext makeCurrentContext]; - } - - Drawable::resize(w, h); - } - - void show(void) { - if (visible) { - return; - } - - // TODO - - Drawable::show(); - } - - void swapBuffers(void) { - if (currentContext != nil) { - [currentContext flushBuffer]; - } - } -}; - - -class CocoaContext : public Context -{ -public: - NSOpenGLContext *context; - - CocoaContext(const Visual *vis, Profile prof, NSOpenGLContext *ctx) : - Context(vis, prof), - context(ctx) - {} - - ~CocoaContext() { - [context release]; - } -}; - - -void -init(void) { - [NSApplication sharedApplication]; - - autoreleasePool = [[NSAutoreleasePool alloc] init]; - - [NSApp finishLaunching]; -} - - -void -cleanup(void) { - [autoreleasePool release]; -} - - -Visual * -createVisual(bool doubleBuffer, Profile profile) { - if (profile != PROFILE_COMPAT && - profile != PROFILE_CORE) { - return nil; - } - - Attributes attribs; - - attribs.add(NSOpenGLPFAAlphaSize, (NSOpenGLPixelFormatAttribute)1); - attribs.add(NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24); - if (doubleBuffer) { - attribs.add(NSOpenGLPFADoubleBuffer); - } - attribs.add(NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)1); - attribs.add(NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute)1); - if (profile == PROFILE_CORE) { -#if CGL_VERSION_1_3 - attribs.add(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); -#else - return NULL; -#endif - } - attribs.end(); - - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] - initWithAttributes:attribs]; - - return new CocoaVisual(pixelFormat); -} - -Drawable * -createDrawable(const Visual *visual, int width, int height) -{ - return new CocoaDrawable(visual, width, height); -} - -Context * -createContext(const Visual *visual, Context *shareContext, Profile profile) -{ - NSOpenGLPixelFormat *pixelFormat = static_cast(visual)->pixelFormat; - NSOpenGLContext *share_context = nil; - NSOpenGLContext *context; - - if (profile != PROFILE_COMPAT && - profile != PROFILE_CORE) { - return nil; - } - - if (shareContext) { - share_context = static_cast(shareContext)->context; - } - - context = [[NSOpenGLContext alloc] - initWithFormat:pixelFormat - shareContext:share_context]; - assert(context != nil); - - return new CocoaContext(visual, profile, context); -} - -bool -makeCurrent(Drawable *drawable, Context *context) -{ - if (!drawable || !context) { - [NSOpenGLContext clearCurrentContext]; - } else { - CocoaDrawable *cocoaDrawable = static_cast(drawable); - CocoaContext *cocoaContext = static_cast(context); - - [cocoaDrawable->window makeKeyAndOrderFront:nil]; - [cocoaContext->context setView:[cocoaDrawable->window contentView]]; - [cocoaContext->context makeCurrentContext]; - - cocoaDrawable->currentContext = cocoaContext->context; - } - - return TRUE; -} - -bool -processEvents(void) { - NSEvent* event; - - do { - event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event) - [NSApp sendEvent:event]; - } while (event); - - return true; -} - - -} /* namespace glws */ diff --git a/glws_egl_xlib.cpp b/glws_egl_xlib.cpp deleted file mode 100644 index 6655781..0000000 --- a/glws_egl_xlib.cpp +++ /dev/null @@ -1,441 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 LunarG, Inc. - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - -#include -#include - -#include - -#include - -#include "glproc.hpp" -#include "glws.hpp" - - -namespace glws { - - -static Display *display = NULL; -static EGLDisplay eglDisplay = EGL_NO_DISPLAY; -static int screen = 0; - - -class EglVisual : public Visual -{ -public: - EGLConfig config; - XVisualInfo *visinfo; - - EglVisual() : - config(0), - visinfo(0) - {} - - ~EglVisual() { - XFree(visinfo); - } -}; - - -static void describeEvent(const XEvent &event) { - if (0) { - switch (event.type) { - case ConfigureNotify: - std::cerr << "ConfigureNotify"; - break; - case Expose: - std::cerr << "Expose"; - break; - case KeyPress: - std::cerr << "KeyPress"; - break; - case MapNotify: - std::cerr << "MapNotify"; - break; - case ReparentNotify: - std::cerr << "ReparentNotify"; - break; - default: - std::cerr << "Event " << event.type; - } - std::cerr << " " << event.xany.window << "\n"; - } -} - -class EglDrawable : public Drawable -{ -public: - Window window; - EGLSurface surface; - EGLint api; - - EglDrawable(const Visual *vis, int w, int h) : - Drawable(vis, w, h), api(EGL_OPENGL_ES_API) - { - XVisualInfo *visinfo = static_cast(visual)->visinfo; - - Window root = RootWindow(display, screen); - - /* window attributes */ - XSetWindowAttributes attr; - attr.background_pixel = 0; - attr.border_pixel = 0; - attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone); - attr.event_mask = StructureNotifyMask; - - unsigned long mask; - mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - int x = 0, y = 0; - - window = XCreateWindow( - display, root, - x, y, width, height, - 0, - visinfo->depth, - InputOutput, - visinfo->visual, - mask, - &attr); - - XSizeHints sizehints; - sizehints.x = x; - sizehints.y = y; - sizehints.width = width; - sizehints.height = height; - sizehints.flags = USSize | USPosition; - XSetNormalHints(display, window, &sizehints); - - const char *name = "glretrace"; - XSetStandardProperties( - display, window, name, name, - None, (char **)NULL, 0, &sizehints); - - eglWaitNative(EGL_CORE_NATIVE_ENGINE); - - EGLConfig config = static_cast(visual)->config; - surface = eglCreateWindowSurface(eglDisplay, config, (EGLNativeWindowType)window, NULL); - } - - void waitForEvent(int type) { - XEvent event; - do { - XWindowEvent(display, window, StructureNotifyMask, &event); - describeEvent(event); - } while (event.type != type); - } - - ~EglDrawable() { - eglDestroySurface(eglDisplay, surface); - eglWaitClient(); - XDestroyWindow(display, window); - eglWaitNative(EGL_CORE_NATIVE_ENGINE); - } - - void - resize(int w, int h) { - if (w == width && h == height) { - return; - } - - eglWaitClient(); - - // We need to ensure that pending events are processed here, and XSync - // with discard = True guarantees that, but it appears the limited - // event processing we do so far is sufficient - //XSync(display, True); - - Drawable::resize(w, h); - - XResizeWindow(display, window, w, h); - - // Tell the window manager to respect the requested size - XSizeHints size_hints; - size_hints.max_width = size_hints.min_width = w; - size_hints.max_height = size_hints.min_height = h; - size_hints.flags = PMinSize | PMaxSize; - XSetWMNormalHints(display, window, &size_hints); - - waitForEvent(ConfigureNotify); - - eglWaitNative(EGL_CORE_NATIVE_ENGINE); - } - - void show(void) { - if (visible) { - return; - } - - eglWaitClient(); - - XMapWindow(display, window); - - waitForEvent(MapNotify); - - eglWaitNative(EGL_CORE_NATIVE_ENGINE); - - Drawable::show(); - } - - void swapBuffers(void) { - eglBindAPI(api); - eglSwapBuffers(eglDisplay, surface); - } -}; - - -class EglContext : public Context -{ -public: - EGLContext context; - - EglContext(const Visual *vis, Profile prof, EGLContext ctx) : - Context(vis, prof), - context(ctx) - {} - - ~EglContext() { - eglDestroyContext(eglDisplay, context); - } -}; - -/** - * Load the symbols from the specified shared object into global namespace, so - * that they can be later found by dlsym(RTLD_NEXT, ...); - */ -static void -load(const char *filename) -{ - if (!dlopen(filename, RTLD_GLOBAL | RTLD_LAZY)) { - std::cerr << "error: unable to open " << filename << "\n"; - exit(1); - } -} - -void -init(void) { - load("libEGL.so.1"); - - display = XOpenDisplay(NULL); - if (!display) { - std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n"; - exit(1); - } - - screen = DefaultScreen(display); - - eglDisplay = eglGetDisplay((EGLNativeDisplayType)display); - if (eglDisplay == EGL_NO_DISPLAY) { - std::cerr << "error: unable to get EGL display\n"; - XCloseDisplay(display); - exit(1); - } - - EGLint major, minor; - if (!eglInitialize(eglDisplay, &major, &minor)) { - std::cerr << "error: unable to initialize EGL display\n"; - XCloseDisplay(display); - exit(1); - } -} - -void -cleanup(void) { - if (display) { - eglTerminate(eglDisplay); - XCloseDisplay(display); - display = NULL; - } -} - -Visual * -createVisual(bool doubleBuffer, Profile profile) { - EglVisual *visual = new EglVisual(); - // possible combinations - const EGLint api_bits_gl[7] = { - EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT, - EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_BIT, - EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_ES2_BIT, - EGL_OPENGL_ES_BIT, - }; - const EGLint api_bits_gles1[7] = { - EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT, - EGL_OPENGL_ES_BIT, - EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_BIT, - EGL_OPENGL_ES2_BIT, - }; - const EGLint api_bits_gles2[7] = { - EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT, - EGL_OPENGL_ES2_BIT, - EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT, - EGL_OPENGL_BIT, - EGL_OPENGL_ES_BIT, - }; - const EGLint *api_bits; - - switch(profile) { - case PROFILE_COMPAT: - api_bits = api_bits_gl; - break; - case PROFILE_ES1: - api_bits = api_bits_gles1; - break; - case PROFILE_ES2: - api_bits = api_bits_gles2; - break; - default: - return NULL; - }; - - for (int i = 0; i < 7; i++) { - Attributes attribs; - - attribs.add(EGL_SURFACE_TYPE, EGL_WINDOW_BIT); - attribs.add(EGL_RED_SIZE, 1); - attribs.add(EGL_GREEN_SIZE, 1); - attribs.add(EGL_BLUE_SIZE, 1); - attribs.add(EGL_ALPHA_SIZE, 1); - attribs.add(EGL_DEPTH_SIZE, 1); - attribs.add(EGL_STENCIL_SIZE, 1); - attribs.add(EGL_RENDERABLE_TYPE, api_bits[i]); - attribs.end(EGL_NONE); - - EGLint num_configs, vid; - if (eglChooseConfig(eglDisplay, attribs, &visual->config, 1, &num_configs) && - num_configs == 1 && - eglGetConfigAttrib(eglDisplay, visual->config, EGL_NATIVE_VISUAL_ID, &vid)) { - XVisualInfo templ; - int num_visuals; - - templ.visualid = vid; - visual->visinfo = XGetVisualInfo(display, VisualIDMask, &templ, &num_visuals); - break; - } - } - - assert(visual->visinfo); - - return visual; -} - -Drawable * -createDrawable(const Visual *visual, int width, int height) -{ - return new EglDrawable(visual, width, height); -} - -Context * -createContext(const Visual *_visual, Context *shareContext, Profile profile) -{ - const EglVisual *visual = static_cast(_visual); - EGLContext share_context = EGL_NO_CONTEXT; - EGLContext context; - Attributes attribs; - - if (shareContext) { - share_context = static_cast(shareContext)->context; - } - - EGLint api = eglQueryAPI(); - - switch (profile) { - case PROFILE_COMPAT: - load("libGL.so.1"); - eglBindAPI(EGL_OPENGL_API); - break; - case PROFILE_CORE: - assert(0); - return NULL; - case PROFILE_ES1: - load("libGLESv1_CM.so.1"); - eglBindAPI(EGL_OPENGL_ES_API); - break; - case PROFILE_ES2: - load("libGLESv2.so.2"); - eglBindAPI(EGL_OPENGL_ES_API); - attribs.add(EGL_CONTEXT_CLIENT_VERSION, 2); - break; - default: - return NULL; - } - - attribs.end(EGL_NONE); - - context = eglCreateContext(eglDisplay, visual->config, share_context, attribs); - if (!context) - return NULL; - - eglBindAPI(api); - - return new EglContext(visual, profile, context); -} - -bool -makeCurrent(Drawable *drawable, Context *context) -{ - if (!drawable || !context) { - return eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } else { - EglDrawable *eglDrawable = static_cast(drawable); - EglContext *eglContext = static_cast(context); - EGLBoolean ok; - - ok = eglMakeCurrent(eglDisplay, eglDrawable->surface, - eglDrawable->surface, eglContext->context); - - if (ok) { - EGLint api; - - eglQueryContext(eglDisplay, eglContext->context, - EGL_CONTEXT_CLIENT_TYPE, &api); - - eglDrawable->api = api; - } - - return ok; - } -} - -bool -processEvents(void) { - while (XPending(display) > 0) { - XEvent event; - XNextEvent(display, &event); - describeEvent(event); - } - return true; -} - - -} /* namespace glws */ diff --git a/glws_glx.cpp b/glws_glx.cpp deleted file mode 100644 index c151db1..0000000 --- a/glws_glx.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - -#include -#include - -#include - -#include "glproc.hpp" -#include "glws.hpp" - - -namespace glws { - - -static Display *display = NULL; -static int screen = 0; - -static unsigned glxVersion = 0; -static const char *extensions = 0; -static bool has_GLX_ARB_create_context = false; - - -class GlxVisual : public Visual -{ -public: - GLXFBConfig fbconfig; - XVisualInfo *visinfo; - - GlxVisual() : - fbconfig(0), - visinfo(0) - {} - - ~GlxVisual() { - XFree(visinfo); - } -}; - - -static void describeEvent(const XEvent &event) { - if (0) { - switch (event.type) { - case ConfigureNotify: - std::cerr << "ConfigureNotify"; - break; - case Expose: - std::cerr << "Expose"; - break; - case KeyPress: - std::cerr << "KeyPress"; - break; - case MapNotify: - std::cerr << "MapNotify"; - break; - case ReparentNotify: - std::cerr << "ReparentNotify"; - break; - default: - std::cerr << "Event " << event.type; - } - std::cerr << " " << event.xany.window << "\n"; - } -} - -class GlxDrawable : public Drawable -{ -public: - Window window; - - GlxDrawable(const Visual *vis, int w, int h) : - Drawable(vis, w, h) - { - XVisualInfo *visinfo = static_cast(visual)->visinfo; - - Window root = RootWindow(display, screen); - - /* window attributes */ - XSetWindowAttributes attr; - attr.background_pixel = 0; - attr.border_pixel = 0; - attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone); - attr.event_mask = StructureNotifyMask; - - unsigned long mask; - mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - int x = 0, y = 0; - - window = XCreateWindow( - display, root, - x, y, width, height, - 0, - visinfo->depth, - InputOutput, - visinfo->visual, - mask, - &attr); - - XSizeHints sizehints; - sizehints.x = x; - sizehints.y = y; - sizehints.width = width; - sizehints.height = height; - sizehints.flags = USSize | USPosition; - XSetNormalHints(display, window, &sizehints); - - const char *name = "glretrace"; - XSetStandardProperties( - display, window, name, name, - None, (char **)NULL, 0, &sizehints); - - glXWaitX(); - } - - void waitForEvent(int type) { - XEvent event; - do { - XWindowEvent(display, window, StructureNotifyMask, &event); - describeEvent(event); - } while (event.type != type); - } - - ~GlxDrawable() { - XDestroyWindow(display, window); - } - - void - resize(int w, int h) { - if (w == width && h == height) { - return; - } - - glXWaitGL(); - - // We need to ensure that pending events are processed here, and XSync - // with discard = True guarantees that, but it appears the limited - // event processing we do so far is sufficient - //XSync(display, True); - - Drawable::resize(w, h); - - XResizeWindow(display, window, w, h); - - // Tell the window manager to respect the requested size - XSizeHints size_hints; - size_hints.max_width = size_hints.min_width = w; - size_hints.max_height = size_hints.min_height = h; - size_hints.flags = PMinSize | PMaxSize; - XSetWMNormalHints(display, window, &size_hints); - - waitForEvent(ConfigureNotify); - - glXWaitX(); - } - - void show(void) { - if (visible) { - return; - } - - glXWaitGL(); - - XMapWindow(display, window); - - waitForEvent(MapNotify); - - glXWaitX(); - - Drawable::show(); - } - - void swapBuffers(void) { - glXSwapBuffers(display, window); - } -}; - - -class GlxContext : public Context -{ -public: - GLXContext context; - - GlxContext(const Visual *vis, Profile prof, GLXContext ctx) : - Context(vis, prof), - context(ctx) - {} - - ~GlxContext() { - glXDestroyContext(display, context); - } -}; - -void -init(void) { - display = XOpenDisplay(NULL); - if (!display) { - std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n"; - exit(1); - } - - screen = DefaultScreen(display); - - int major = 0, minor = 0; - glXQueryVersion(display, &major, &minor); - glxVersion = (major << 8) | minor; - - extensions = glXQueryExtensionsString(display, screen); - has_GLX_ARB_create_context = checkExtension("GLX_ARB_create_context", extensions); -} - -void -cleanup(void) { - if (display) { - XCloseDisplay(display); - display = NULL; - } -} - -Visual * -createVisual(bool doubleBuffer, Profile profile) { - if (profile != PROFILE_COMPAT && - profile != PROFILE_CORE) { - return NULL; - } - - GlxVisual *visual = new GlxVisual; - - if (glxVersion >= 0x0103) { - Attributes attribs; - attribs.add(GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT); - attribs.add(GLX_RENDER_TYPE, GLX_RGBA_BIT); - attribs.add(GLX_RED_SIZE, 1); - attribs.add(GLX_GREEN_SIZE, 1); - attribs.add(GLX_BLUE_SIZE, 1); - attribs.add(GLX_ALPHA_SIZE, 1); - attribs.add(GLX_DOUBLEBUFFER, doubleBuffer ? GL_TRUE : GL_FALSE); - attribs.add(GLX_DEPTH_SIZE, 1); - attribs.add(GLX_STENCIL_SIZE, 1); - attribs.end(); - - int num_configs = 0; - GLXFBConfig * fbconfigs; - fbconfigs = glXChooseFBConfig(display, screen, attribs, &num_configs); - assert(num_configs && fbconfigs); - visual->fbconfig = fbconfigs[0]; - assert(visual->fbconfig); - visual->visinfo = glXGetVisualFromFBConfig(display, visual->fbconfig); - assert(visual->visinfo); - } else { - Attributes attribs; - attribs.add(GLX_RGBA); - attribs.add(GLX_RED_SIZE, 1); - attribs.add(GLX_GREEN_SIZE, 1); - attribs.add(GLX_BLUE_SIZE, 1); - attribs.add(GLX_ALPHA_SIZE, 1); - if (doubleBuffer) { - attribs.add(GLX_DOUBLEBUFFER); - } - attribs.add(GLX_DEPTH_SIZE, 1); - attribs.add(GLX_STENCIL_SIZE, 1); - attribs.end(); - - visual->visinfo = glXChooseVisual(display, screen, attribs); - } - - return visual; -} - -Drawable * -createDrawable(const Visual *visual, int width, int height) -{ - return new GlxDrawable(visual, width, height); -} - -Context * -createContext(const Visual *_visual, Context *shareContext, Profile profile) -{ - const GlxVisual *visual = static_cast(_visual); - GLXContext share_context = NULL; - GLXContext context; - - if (shareContext) { - share_context = static_cast(shareContext)->context; - } - - if (glxVersion >= 0x0104 && has_GLX_ARB_create_context) { - Attributes attribs; - - attribs.add(GLX_RENDER_TYPE, GLX_RGBA_TYPE); - if (debug) { - attribs.add(GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB); - } - - switch (profile) { - case PROFILE_COMPAT: - break; - case PROFILE_CORE: - // XXX: This will invariable return a 3.2 context, when supported. - // We probably should have a PROFILE_CORE_XX per version. - attribs.add(GLX_CONTEXT_MAJOR_VERSION_ARB, 3); - attribs.add(GLX_CONTEXT_MINOR_VERSION_ARB, 2); - attribs.add(GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB); - break; - default: - return NULL; - } - - attribs.end(); - - context = glXCreateContextAttribsARB(display, visual->fbconfig, share_context, True, attribs); - } else { - if (profile != PROFILE_COMPAT) { - return NULL; - } - - if (glxVersion >= 0x103) { - context = glXCreateNewContext(display, visual->fbconfig, GLX_RGBA_TYPE, share_context, True); - } else { - context = glXCreateContext(display, visual->visinfo, share_context, True); - } - } - - if (!context) { - return NULL; - } - - return new GlxContext(visual, profile, context); -} - -bool -makeCurrent(Drawable *drawable, Context *context) -{ - if (!drawable || !context) { - return glXMakeCurrent(display, None, NULL); - } else { - GlxDrawable *glxDrawable = static_cast(drawable); - GlxContext *glxContext = static_cast(context); - - return glXMakeCurrent(display, glxDrawable->window, glxContext->context); - } -} - -bool -processEvents(void) { - while (XPending(display) > 0) { - XEvent event; - XNextEvent(display, &event); - describeEvent(event); - } - return true; -} - - -} /* namespace glws */ diff --git a/glws_wgl.cpp b/glws_wgl.cpp deleted file mode 100644 index efefff0..0000000 --- a/glws_wgl.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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 bindings. - */ - - -#include - -#include "glproc.hpp" -#include "glws.hpp" - - -namespace glws { - - -/* - * Several WGL functions come in two flavors: - * - GDI (ChoosePixelFormat, SetPixelFormat, SwapBuffers, etc) - * - WGL (wglChoosePixelFormat, wglSetPixelFormat, wglSwapBuffers, etc) - * - * The GDI entrypoints will inevitably dispatch to the first module named - * "OPENGL32", loading "C:\Windows\System32\opengl32.dll" if none was loaded so - * far. - * - * In order to use a implementation other than the one installed in the system - * (when specified via the TRACE_LIBGL environment variable), we need to use - * WGL entrypoints. - * - * See also: - * - http://www.opengl.org/archives/resources/faq/technical/mswindows.htm - */ -static __PFNWGLCHOOSEPIXELFORMAT pfnChoosePixelFormat = &ChoosePixelFormat; -static __PFNWGLSETPIXELFORMAT pfnSetPixelFormat = &SetPixelFormat; -static __PFNWGLSWAPBUFFERS pfnSwapBuffers = &SwapBuffers; - - -static LRESULT CALLBACK -WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MINMAXINFO *pMMI; - switch (uMsg) { - case WM_GETMINMAXINFO: - // Allow to create a window bigger than the desktop - pMMI = (MINMAXINFO *)lParam; - pMMI->ptMaxSize.x = 60000; - pMMI->ptMaxSize.y = 60000; - pMMI->ptMaxTrackSize.x = 60000; - pMMI->ptMaxTrackSize.y = 60000; - break; - default: - break; - } - - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - - -class WglDrawable : public Drawable -{ -public: - DWORD dwExStyle; - DWORD dwStyle; - HWND hWnd; - HDC hDC; - PIXELFORMATDESCRIPTOR pfd; - int iPixelFormat; - - WglDrawable(const Visual *vis, int width, int height) : - Drawable(vis, width, height) - { - static bool first = TRUE; - RECT rect; - BOOL bRet; - - if (first) { - WNDCLASS wc; - memset(&wc, 0, sizeof wc); - wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.lpfnWndProc = WndProc; - wc.lpszClassName = "glretrace"; - wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - RegisterClass(&wc); - first = FALSE; - } - - dwExStyle = 0; - dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW; - - int x = 0, y = 0; - - rect.left = x; - rect.top = y; - rect.right = rect.left + width; - rect.bottom = rect.top + height; - - AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle); - - hWnd = CreateWindowEx(dwExStyle, - "glretrace", /* wc.lpszClassName */ - NULL, - dwStyle, - 0, /* x */ - 0, /* y */ - rect.right - rect.left, /* width */ - rect.bottom - rect.top, /* height */ - NULL, - NULL, - NULL, - NULL); - hDC = GetDC(hWnd); - - memset(&pfd, 0, sizeof pfd); - pfd.cColorBits = 4; - pfd.cRedBits = 1; - pfd.cGreenBits = 1; - pfd.cBlueBits = 1; - pfd.cAlphaBits = 1; - pfd.cDepthBits = 1; - pfd.cStencilBits = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - pfd.iLayerType = PFD_MAIN_PLANE; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - - if (visual->doubleBuffer) { - pfd.dwFlags |= PFD_DOUBLEBUFFER; - } - - iPixelFormat = pfnChoosePixelFormat(hDC, &pfd); - if (iPixelFormat <= 0) { - std::cerr << "error: ChoosePixelFormat failed\n"; - exit(1); - } - - bRet = pfnSetPixelFormat(hDC, iPixelFormat, &pfd); - if (!bRet) { - std::cerr << "error: SetPixelFormat failed\n"; - exit(1); - } - } - - ~WglDrawable() { - ReleaseDC(hWnd, hDC); - DestroyWindow(hWnd); - } - - void - resize(int w, int h) { - if (w == width && h == height) { - return; - } - - RECT rClient, rWindow; - GetClientRect(hWnd, &rClient); - GetWindowRect(hWnd, &rWindow); - w += (rWindow.right - rWindow.left) - rClient.right; - h += (rWindow.bottom - rWindow.top) - rClient.bottom; - SetWindowPos(hWnd, NULL, rWindow.left, rWindow.top, w, h, SWP_NOMOVE); - - Drawable::resize(w, h); - } - - void show(void) { - if (visible) { - return; - } - - ShowWindow(hWnd, SW_SHOW); - - Drawable::show(); - } - - void swapBuffers(void) { - BOOL bRet; - bRet = pfnSwapBuffers(hDC); - if (!bRet) { - std::cerr << "warning: SwapBuffers failed\n"; - } - - // Drain message queue to prevent window from being considered - // non-responsive - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -}; - - -class WglContext : public Context -{ -public: - HGLRC hglrc; - WglContext *shareContext; - - WglContext(const Visual *vis, Profile prof, WglContext *share) : - Context(vis, prof), - hglrc(0), - shareContext(share) - {} - - ~WglContext() { - if (hglrc) { - wglDeleteContext(hglrc); - } - } -}; - - -void -init(void) { - /* - * OpenGL library must be loaded by the time we call GDI. - */ - - const char * libgl_filename = getenv("TRACE_LIBGL"); - - if (libgl_filename) { - pfnChoosePixelFormat = &wglChoosePixelFormat; - pfnSetPixelFormat = &wglSetPixelFormat; - pfnSwapBuffers = &wglSwapBuffers; - } else { - libgl_filename = "OPENGL32"; - } - - __libGlHandle = LoadLibraryA(libgl_filename); - if (!__libGlHandle) { - std::cerr << "error: unable to open " << libgl_filename << "\n"; - exit(1); - } -} - -void -cleanup(void) { -} - -Visual * -createVisual(bool doubleBuffer, Profile profile) { - if (profile != PROFILE_COMPAT) { - return NULL; - } - - Visual *visual = new Visual(); - - visual->doubleBuffer = doubleBuffer; - - return visual; -} - -Drawable * -createDrawable(const Visual *visual, int width, int height) -{ - return new WglDrawable(visual, width, height); -} - -Context * -createContext(const Visual *visual, Context *shareContext, Profile profile) -{ - if (profile != PROFILE_COMPAT) { - return NULL; - } - - return new WglContext(visual, profile, static_cast(shareContext)); -} - -bool -makeCurrent(Drawable *drawable, Context *context) -{ - if (!drawable || !context) { - return wglMakeCurrent(NULL, NULL); - } else { - WglDrawable *wglDrawable = static_cast(drawable); - WglContext *wglContext = static_cast(context); - - if (!wglContext->hglrc) { - wglContext->hglrc = wglCreateContext(wglDrawable->hDC); - if (!wglContext->hglrc) { - std::cerr << "error: wglCreateContext failed\n"; - exit(1); - return false; - } - if (wglContext->shareContext) { - BOOL bRet; - bRet = wglShareLists(wglContext->shareContext->hglrc, - wglContext->hglrc); - if (!bRet) { - std::cerr << "warning: wglShareLists failed\n"; - } - } - } - - return wglMakeCurrent(wglDrawable->hDC, wglContext->hglrc); - } -} - -bool -processEvents(void) { - // TODO - return true; -} - - -} /* namespace glws */ diff --git a/retrace.cpp b/retrace.cpp deleted file mode 100644 index 1630995..0000000 --- a/retrace.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include -#include - -#include "os_time.hpp" -#include "trace_dump.hpp" -#include "retrace.hpp" - - -namespace retrace { - - -trace::Parser parser; - - -int verbosity = 0; -bool profiling = false; - - -static bool call_dumped = false; - - -static void dumpCall(trace::Call &call) { - if (verbosity >= 0 && !call_dumped) { - std::cout << call; - std::cout.flush(); - call_dumped = true; - } -} - - -std::ostream &warning(trace::Call &call) { - dumpCall(call); - - std::cerr << call.no << ": "; - std::cerr << "warning: "; - - return std::cerr; -} - - -void ignore(trace::Call &call) { - (void)call; -} - -void unsupported(trace::Call &call) { - warning(call) << "unsupported " << call.name() << " call\n"; -} - -inline void Retracer::addCallback(const Entry *entry) { - assert(entry->name); - assert(entry->callback); - map[entry->name] = entry->callback; -} - - -void Retracer::addCallbacks(const Entry *entries) { - while (entries->name && entries->callback) { - addCallback(entries++); - } -} - - -void Retracer::retrace(trace::Call &call) { - call_dumped = false; - - if (verbosity >= 1) { - if (verbosity >= 2 || - !(call.flags & trace::CALL_FLAG_VERBOSE)) { - dumpCall(call); - } - } - - Callback callback = 0; - - trace::Id id = call.sig->id; - if (id >= callbacks.size()) { - callbacks.resize(id + 1); - callback = 0; - } else { - callback = callbacks[id]; - } - - if (!callback) { - Map::const_iterator it = map.find(call.name()); - if (it == map.end()) { - callback = &unsupported; - } else { - callback = it->second; - } - callbacks[id] = callback; - } - - assert(callback); - assert(callbacks[id] == callback); - - if (retrace::profiling) { - long long startTime = os::getTime(); - callback(call); - long long stopTime = os::getTime(); - float timeInterval = (stopTime - startTime) * (1.0E6 / os::timeFrequency); - - std::cout - << call.no << " " - << "[" << timeInterval << " usec] " - ; - trace::dump(call, std::cout, trace::DUMP_FLAG_NO_CALL_NO | trace::DUMP_FLAG_NO_COLOR); - } else { - callback(call); - } -} - - -} /* namespace retrace */ diff --git a/retrace.hpp b/retrace.hpp deleted file mode 100644 index c1e556a..0000000 --- a/retrace.hpp +++ /dev/null @@ -1,242 +0,0 @@ -/************************************************************************** - * - * Copyright 2011-2012 Jose Fonseca - * 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. - * - **************************************************************************/ - -#ifndef _RETRACE_HPP_ -#define _RETRACE_HPP_ - -#include -#include -#include - -#include -#include -#include - -#include "trace_model.hpp" -#include "trace_parser.hpp" - - -namespace retrace { - - -extern trace::Parser parser; - - -/** - * Handle map. - * - * It is just like a regular std::map container, but lookups of missing - * keys return the key instead of default constructor. - * - * This is necessary for several GL named objects, where one can either request - * the implementation to generate an unique name, or pick a value never used - * before. - * - * XXX: In some cases, instead of returning the key, it would make more sense - * to return an unused data value (e.g., container count). - */ -template -class map -{ -private: - typedef std::map base_type; - base_type base; - -public: - - T & operator[] (const T &key) { - typename base_type::iterator it; - it = base.find(key); - if (it == base.end()) { - return (base[key] = key); - } - return it->second; - } - - const T & operator[] (const T &key) const { - typename base_type::const_iterator it; - it = base.find(key); - if (it == base.end()) { - return (base[key] = key); - } - return it->second; - } -}; - - -/** - * Similar to alloca(), but implemented with malloc. - */ -class ScopedAllocator -{ -private: - uintptr_t next; - -public: - ScopedAllocator() : - next(0) { - } - - inline void * - alloc(size_t size) { - if (!size) { - return NULL; - } - - uintptr_t * buf = static_cast(malloc(sizeof(uintptr_t) + size)); - if (!buf) { - return NULL; - } - - *buf = next; - next = reinterpret_cast(buf); - assert((next & 1) == 0); - - return static_cast(&buf[1]); - } - - template< class T > - inline T * - alloc(size_t n = 1) { - return static_cast(alloc(sizeof(T) * n)); - } - - /** - * Allocate an array with the same dimensions as the specified value. - */ - template< class T > - inline T * - alloc(const trace::Value *value) { - const trace::Array *array = dynamic_cast(value); - if (array) { - return alloc(array->size()); - } - const trace::Null *null = dynamic_cast(value); - if (null) { - return NULL; - } - assert(0); - return NULL; - } - - /** - * Prevent this pointer from being automatically freed. - */ - template< class T > - inline void - bind(T *ptr) { - if (ptr) { - reinterpret_cast(ptr)[-1] |= 1; - } - } - - inline - ~ScopedAllocator() { - while (next) { - uintptr_t temp = *reinterpret_cast(next); - - bool bind = temp & 1; - temp &= ~1; - - if (!bind) { - free(reinterpret_cast(next)); - } - - next = temp; - } - } -}; - - -void -addRegion(unsigned long long address, void *buffer, unsigned long long size); - -void -delRegionByPointer(void *ptr); - -void * -toPointer(trace::Value &value, bool bind = false); - - -/** - * Output verbosity when retracing files. - */ -extern int verbosity; - -/** - * Add profiling data to the dump when retracing. - */ -extern bool profiling; - - -std::ostream &warning(trace::Call &call); - - -void ignore(trace::Call &call); -void unsupported(trace::Call &call); - - -typedef void (*Callback)(trace::Call &call); - -struct Entry { - const char *name; - Callback callback; -}; - - -struct stringComparer { - bool operator() (const char *a, const char *b) const { - return strcmp(a, b) < 0; - } -}; - - -extern const Entry stdc_callbacks[]; - - -class Retracer -{ - typedef std::map Map; - Map map; - - std::vector callbacks; - -public: - Retracer() { - addCallbacks(stdc_callbacks); - } - - virtual ~Retracer() {} - - void addCallback(const Entry *entry); - void addCallbacks(const Entry *entries); - - void retrace(trace::Call &call); -}; - - -} /* namespace retrace */ - -#endif /* _RETRACE_HPP_ */ diff --git a/retrace.py b/retrace.py deleted file mode 100644 index 9e9af20..0000000 --- a/retrace.py +++ /dev/null @@ -1,501 +0,0 @@ -########################################################################## -# -# Copyright 2010 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. -# -##########################################################################/ - - -"""Generic retracing code generator.""" - - -import sys - -import specs.stdapi as stdapi -import specs.glapi as glapi - - -class UnsupportedType(Exception): - pass - - -class MutableRebuilder(stdapi.Rebuilder): - '''Type visitor which derives a mutable type.''' - - def visitConst(self, const): - # Strip out const qualifier - return const.type - - def visitAlias(self, alias): - # Tear the alias on type changes - type = self.visit(alias.type) - if type is alias.type: - return alias - return type - - def visitReference(self, reference): - # Strip out references - return reference.type - - -def lookupHandle(handle, value): - if handle.key is None: - return "__%s_map[%s]" % (handle.name, value) - else: - key_name, key_type = handle.key - return "__%s_map[%s][%s]" % (handle.name, key_name, value) - - -class ValueAllocator(stdapi.Visitor): - - def visitLiteral(self, literal, lvalue, rvalue): - pass - - def visitConst(self, const, lvalue, rvalue): - self.visit(const.type, lvalue, rvalue) - - def visitAlias(self, alias, lvalue, rvalue): - self.visit(alias.type, lvalue, rvalue) - - def visitEnum(self, enum, lvalue, rvalue): - pass - - def visitBitmask(self, bitmask, lvalue, rvalue): - pass - - def visitArray(self, array, lvalue, rvalue): - print ' %s = _allocator.alloc<%s>(&%s);' % (lvalue, array.type, rvalue) - - def visitPointer(self, pointer, lvalue, rvalue): - print ' %s = _allocator.alloc<%s>(&%s);' % (lvalue, pointer.type, rvalue) - - def visitIntPointer(self, pointer, lvalue, rvalue): - pass - - def visitObjPointer(self, pointer, lvalue, rvalue): - pass - - def visitLinearPointer(self, pointer, lvalue, rvalue): - pass - - def visitReference(self, reference, lvalue, rvalue): - self.visit(reference.type, lvalue, rvalue); - - def visitHandle(self, handle, lvalue, rvalue): - pass - - def visitBlob(self, blob, lvalue, rvalue): - pass - - def visitString(self, string, lvalue, rvalue): - pass - - def visitStruct(self, struct, lvalue, rvalue): - pass - - def visitPolymorphic(self, polymorphic, lvalue, rvalue): - self.visit(polymorphic.defaultType, lvalue, rvalue) - - def visitOpaque(self, opaque, lvalue, rvalue): - pass - - -class ValueDeserializer(stdapi.Visitor): - - def visitLiteral(self, literal, lvalue, rvalue): - print ' %s = (%s).to%s();' % (lvalue, rvalue, literal.kind) - - def visitConst(self, const, lvalue, rvalue): - self.visit(const.type, lvalue, rvalue) - - def visitAlias(self, alias, lvalue, rvalue): - self.visit(alias.type, lvalue, rvalue) - - def visitEnum(self, enum, lvalue, rvalue): - print ' %s = static_cast<%s>((%s).toSInt());' % (lvalue, enum, rvalue) - - def visitBitmask(self, bitmask, lvalue, rvalue): - self.visit(bitmask.type, lvalue, rvalue) - - def visitArray(self, array, lvalue, rvalue): - - tmp = '__a_' + array.tag + '_' + str(self.seq) - self.seq += 1 - - print ' if (%s) {' % (lvalue,) - print ' const trace::Array *%s = dynamic_cast(&%s);' % (tmp, rvalue) - length = '%s->values.size()' % (tmp,) - index = '__j' + array.tag - print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length) - try: - self.visit(array.type, '%s[%s]' % (lvalue, index), '*%s->values[%s]' % (tmp, index)) - finally: - print ' }' - print ' }' - - def visitPointer(self, pointer, lvalue, rvalue): - tmp = '__a_' + pointer.tag + '_' + str(self.seq) - self.seq += 1 - - print ' if (%s) {' % (lvalue,) - print ' const trace::Array *%s = dynamic_cast(&%s);' % (tmp, rvalue) - try: - self.visit(pointer.type, '%s[0]' % (lvalue,), '*%s->values[0]' % (tmp,)) - finally: - print ' }' - - def visitIntPointer(self, pointer, lvalue, rvalue): - print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, pointer, rvalue) - - def visitObjPointer(self, pointer, lvalue, rvalue): - old_lvalue = '(%s).toUIntPtr()' % (rvalue,) - new_lvalue = '_obj_map[%s]' % (old_lvalue,) - print ' if (retrace::verbosity >= 2) {' - print ' std::cout << std::hex << "obj 0x" << size_t(%s) << " <- 0x" << size_t(%s) << std::dec <<"\\n";' % (old_lvalue, new_lvalue) - print ' }' - print ' %s = static_cast<%s>(%s);' % (lvalue, pointer, new_lvalue) - - def visitLinearPointer(self, pointer, lvalue, rvalue): - print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, pointer, rvalue) - - def visitReference(self, reference, lvalue, rvalue): - self.visit(reference.type, lvalue, rvalue); - - def visitHandle(self, handle, lvalue, rvalue): - #OpaqueValueDeserializer().visit(handle.type, lvalue, rvalue); - self.visit(handle.type, lvalue, rvalue); - new_lvalue = lookupHandle(handle, lvalue) - print ' if (retrace::verbosity >= 2) {' - print ' std::cout << "%s " << size_t(%s) << " <- " << size_t(%s) << "\\n";' % (handle.name, lvalue, new_lvalue) - print ' }' - print ' %s = %s;' % (lvalue, new_lvalue) - - def visitBlob(self, blob, lvalue, rvalue): - print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, blob, rvalue) - - def visitString(self, string, lvalue, rvalue): - print ' %s = (%s)((%s).toString());' % (lvalue, string.expr, rvalue) - - seq = 0 - - def visitStruct(self, struct, lvalue, rvalue): - tmp = '__s_' + struct.tag + '_' + str(self.seq) - self.seq += 1 - - print ' const trace::Struct *%s = dynamic_cast(&%s);' % (tmp, rvalue) - print ' assert(%s);' % (tmp) - for i in range(len(struct.members)): - member_type, member_name = struct.members[i] - self.visit(member_type, '%s.%s' % (lvalue, member_name), '*%s->members[%s]' % (tmp, i)) - - def visitPolymorphic(self, polymorphic, lvalue, rvalue): - self.visit(polymorphic.defaultType, lvalue, rvalue) - - def visitOpaque(self, opaque, lvalue, rvalue): - raise UnsupportedType - - -class OpaqueValueDeserializer(ValueDeserializer): - '''Value extractor that also understands opaque values. - - Normally opaque values can't be retraced, unless they are being extracted - in the context of handles.''' - - def visitOpaque(self, opaque, lvalue, rvalue): - print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, opaque, rvalue) - - -class SwizzledValueRegistrator(stdapi.Visitor): - '''Type visitor which will register (un)swizzled value pairs, to later be - swizzled.''' - - def visitLiteral(self, literal, lvalue, rvalue): - pass - - def visitAlias(self, alias, lvalue, rvalue): - self.visit(alias.type, lvalue, rvalue) - - def visitEnum(self, enum, lvalue, rvalue): - pass - - def visitBitmask(self, bitmask, lvalue, rvalue): - pass - - def visitArray(self, array, lvalue, rvalue): - print ' const trace::Array *__a%s = dynamic_cast(&%s);' % (array.tag, rvalue) - print ' if (__a%s) {' % (array.tag) - length = '__a%s->values.size()' % array.tag - index = '__j' + array.tag - print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length) - try: - self.visit(array.type, '%s[%s]' % (lvalue, index), '*__a%s->values[%s]' % (array.tag, index)) - finally: - print ' }' - print ' }' - - def visitPointer(self, pointer, lvalue, rvalue): - print ' const trace::Array *__a%s = dynamic_cast(&%s);' % (pointer.tag, rvalue) - print ' if (__a%s) {' % (pointer.tag) - try: - self.visit(pointer.type, '%s[0]' % (lvalue,), '*__a%s->values[0]' % (pointer.tag,)) - finally: - print ' }' - - def visitIntPointer(self, pointer, lvalue, rvalue): - pass - - def visitObjPointer(self, pointer, lvalue, rvalue): - print r' _obj_map[(%s).toUIntPtr()] = %s;' % (rvalue, lvalue) - - def visitLinearPointer(self, pointer, lvalue, rvalue): - assert pointer.size is not None - if pointer.size is not None: - print r' retrace::addRegion((%s).toUIntPtr(), %s, %s);' % (rvalue, lvalue, pointer.size) - - def visitReference(self, reference, lvalue, rvalue): - pass - - def visitHandle(self, handle, lvalue, rvalue): - print ' %s __orig_result;' % handle.type - OpaqueValueDeserializer().visit(handle.type, '__orig_result', rvalue); - if handle.range is None: - rvalue = "__orig_result" - entry = lookupHandle(handle, rvalue) - print " %s = %s;" % (entry, lvalue) - print ' if (retrace::verbosity >= 2) {' - print ' std::cout << "{handle.name} " << {rvalue} << " -> " << {lvalue} << "\\n";'.format(**locals()) - print ' }' - else: - i = '__h' + handle.tag - lvalue = "%s + %s" % (lvalue, i) - rvalue = "__orig_result + %s" % (i,) - entry = lookupHandle(handle, rvalue) - print ' for ({handle.type} {i} = 0; {i} < {handle.range}; ++{i}) {{'.format(**locals()) - print ' {entry} = {lvalue};'.format(**locals()) - print ' if (retrace::verbosity >= 2) {' - print ' std::cout << "{handle.name} " << ({rvalue}) << " -> " << ({lvalue}) << "\\n";'.format(**locals()) - print ' }' - print ' }' - - def visitBlob(self, blob, lvalue, rvalue): - pass - - def visitString(self, string, lvalue, rvalue): - pass - - seq = 0 - - def visitStruct(self, struct, lvalue, rvalue): - tmp = '__s_' + struct.tag + '_' + str(self.seq) - self.seq += 1 - - print ' const trace::Struct *%s = dynamic_cast(&%s);' % (tmp, rvalue) - print ' assert(%s);' % (tmp,) - print ' (void)%s;' % (tmp,) - for i in range(len(struct.members)): - member_type, member_name = struct.members[i] - self.visit(member_type, '%s.%s' % (lvalue, member_name), '*%s->members[%s]' % (tmp, i)) - - def visitPolymorphic(self, polymorphic, lvalue, rvalue): - self.visit(polymorphic.defaultType, lvalue, rvalue) - - def visitOpaque(self, opaque, lvalue, rvalue): - pass - - -class Retracer: - - def retraceFunction(self, function): - print 'static void retrace_%s(trace::Call &call) {' % function.name - self.retraceFunctionBody(function) - print '}' - print - - def retraceInterfaceMethod(self, interface, method): - print 'static void retrace_%s__%s(trace::Call &call) {' % (interface.name, method.name) - self.retraceInterfaceMethodBody(interface, method) - print '}' - print - - def retraceFunctionBody(self, function): - assert function.sideeffects - - self.deserializeArgs(function) - - self.invokeFunction(function) - - self.swizzleValues(function) - - def retraceInterfaceMethodBody(self, interface, method): - assert method.sideeffects - - self.deserializeThisPointer(interface) - - self.deserializeArgs(method) - - self.invokeInterfaceMethod(interface, method) - - self.swizzleValues(method) - - def deserializeThisPointer(self, interface): - print r' %s *_this;' % (interface.name,) - print r' _this = static_cast<%s *>(_obj_map[call.arg(0).toUIntPtr()]);' % (interface.name,) - print r' if (!_this) {' - print r' retrace::warning(call) << "NULL this pointer\n";' - print r' return;' - print r' }' - - def deserializeArgs(self, function): - print ' retrace::ScopedAllocator _allocator;' - print ' (void)_allocator;' - success = True - for arg in function.args: - arg_type = MutableRebuilder().visit(arg.type) - print ' %s %s;' % (arg_type, arg.name) - rvalue = 'call.arg(%u)' % (arg.index,) - lvalue = arg.name - try: - self.extractArg(function, arg, arg_type, lvalue, rvalue) - except UnsupportedType: - success = False - print ' memset(&%s, 0, sizeof %s); // FIXME' % (arg.name, arg.name) - print - - if not success: - print ' if (1) {' - self.failFunction(function) - if function.name[-1].islower(): - sys.stderr.write('warning: unsupported %s call\n' % function.name) - print ' }' - - def swizzleValues(self, function): - for arg in function.args: - if arg.output: - arg_type = MutableRebuilder().visit(arg.type) - rvalue = 'call.arg(%u)' % (arg.index,) - lvalue = arg.name - try: - self.regiterSwizzledValue(arg_type, lvalue, rvalue) - except UnsupportedType: - print ' // XXX: %s' % arg.name - if function.type is not stdapi.Void: - rvalue = '*call.ret' - lvalue = '__result' - try: - self.regiterSwizzledValue(function.type, lvalue, rvalue) - except UnsupportedType: - raise - print ' // XXX: result' - - def failFunction(self, function): - print ' if (retrace::verbosity >= 0) {' - print ' retrace::unsupported(call);' - print ' }' - print ' return;' - - def extractArg(self, function, arg, arg_type, lvalue, rvalue): - ValueAllocator().visit(arg_type, lvalue, rvalue) - if arg.input: - ValueDeserializer().visit(arg_type, lvalue, rvalue) - - def extractOpaqueArg(self, function, arg, arg_type, lvalue, rvalue): - try: - ValueAllocator().visit(arg_type, lvalue, rvalue) - except UnsupportedType: - pass - OpaqueValueDeserializer().visit(arg_type, lvalue, rvalue) - - def regiterSwizzledValue(self, type, lvalue, rvalue): - visitor = SwizzledValueRegistrator() - visitor.visit(type, lvalue, rvalue) - - def invokeFunction(self, function): - arg_names = ", ".join(function.argNames()) - if function.type is not stdapi.Void: - print ' %s __result;' % (function.type) - print ' __result = %s(%s);' % (function.name, arg_names) - print ' (void)__result;' - else: - print ' %s(%s);' % (function.name, arg_names) - - def invokeInterfaceMethod(self, interface, method): - arg_names = ", ".join(method.argNames()) - if method.type is not stdapi.Void: - print ' %s __result;' % (method.type) - print ' __result = _this->%s(%s);' % (method.name, arg_names) - print ' (void)__result;' - else: - print ' _this->%s(%s);' % (method.name, arg_names) - - def filterFunction(self, function): - return True - - table_name = 'retrace::callbacks' - - def retraceApi(self, api): - - print '#include "os_time.hpp"' - print '#include "trace_parser.hpp"' - print '#include "retrace.hpp"' - print - - types = api.getAllTypes() - handles = [type for type in types if isinstance(type, stdapi.Handle)] - handle_names = set() - for handle in handles: - if handle.name not in handle_names: - if handle.key is None: - print 'static retrace::map<%s> __%s_map;' % (handle.type, handle.name) - else: - key_name, key_type = handle.key - print 'static std::map<%s, retrace::map<%s> > __%s_map;' % (key_type, handle.type, handle.name) - handle_names.add(handle.name) - print - - print 'static std::map _obj_map;' - print - - functions = filter(self.filterFunction, api.functions) - for function in functions: - if function.sideeffects: - self.retraceFunction(function) - interfaces = api.getAllInterfaces() - for interface in interfaces: - for method in interface.iterMethods(): - if method.sideeffects: - self.retraceInterfaceMethod(interface, method) - - print 'const retrace::Entry %s[] = {' % self.table_name - for function in functions: - if function.sideeffects: - print ' {"%s", &retrace_%s},' % (function.name, function.name) - else: - print ' {"%s", &retrace::ignore},' % (function.name,) - for interface in interfaces: - for method in interface.iterMethods(): - if method.sideeffects: - print ' {"%s::%s", &retrace_%s__%s},' % (interface.name, method.name, interface.name, method.name) - else: - print ' {"%s::%s", &retrace::ignore},' % (interface.name, method.name) - print ' {NULL, NULL}' - print '};' - print - diff --git a/retrace/.gitignore b/retrace/.gitignore new file mode 100644 index 0000000..ebcad87 --- /dev/null +++ b/retrace/.gitignore @@ -0,0 +1,2 @@ +glretrace_gl.cpp +glstate_params.cpp diff --git a/retrace/CMakeLists.txt b/retrace/CMakeLists.txt new file mode 100644 index 0000000..c45b33f --- /dev/null +++ b/retrace/CMakeLists.txt @@ -0,0 +1,146 @@ +############################################################################## +# API retracers + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +add_definitions (-DRETRACE) + +add_custom_command ( + OUTPUT glretrace_gl.cpp + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/glretrace.py > ${CMAKE_CURRENT_BINARY_DIR}/glretrace_gl.cpp + DEPENDS + glretrace.py + retrace.py + ${CMAKE_SOURCE_DIR}/specs/glapi.py + ${CMAKE_SOURCE_DIR}/specs/gltypes.py + ${CMAKE_SOURCE_DIR}/specs/stdapi.py +) + +add_custom_command ( + OUTPUT glstate_params.cpp + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/glstate_params.py > ${CMAKE_CURRENT_BINARY_DIR}/glstate_params.cpp + DEPENDS + glstate_params.py + ${CMAKE_SOURCE_DIR}/specs/glparams.py + ${CMAKE_SOURCE_DIR}/specs/gltypes.py + ${CMAKE_SOURCE_DIR}/specs/stdapi.py +) + +add_library (retrace_common + glretrace_gl.cpp + glretrace_cgl.cpp + glretrace_glx.cpp + glretrace_wgl.cpp + glretrace_egl.cpp + glretrace_main.cpp + glstate.cpp + glstate_images.cpp + glstate_params.cpp + glstate_shaders.cpp + retrace.cpp + retrace_stdc.cpp + glws.cpp +) + +add_dependencies (retrace_common glproc) + +if (WIN32 OR APPLE OR X11_FOUND) + add_executable (glretrace + ${glws_os} + ${CMAKE_SOURCE_DIR}/glproc_gl.cpp + ) + + add_dependencies (glretrace glproc) + + target_link_libraries (glretrace + retrace_common + common + ${PNG_LIBRARIES} + ${ZLIB_LIBRARIES} + ${SNAPPY_LIBRARIES} + ) + + if (WIN32) + else () + if (APPLE) + target_link_libraries (glretrace + "-framework Cocoa" + "-framework ApplicationServices" # CGS* + #"-framework OpenGL" # CGL* + ) + else () + target_link_libraries (glretrace ${X11_X11_LIB}) + endif () + + target_link_libraries (glretrace + # gdb doesn't like when pthreads is loaded through dlopen (which happens + # when dlopen'ing libGL), so link pthreads to avoid this issue. See also + # http://stackoverflow.com/questions/2702628/gdb-cannot-find-new-threads-generic-error + ${CMAKE_THREAD_LIBS_INIT} + dl + ) + + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries (glretrace rt) + endif () + + endif () + + install (TARGETS glretrace RUNTIME DESTINATION bin) +endif () + +if (ENABLE_EGL AND X11_FOUND AND NOT WIN32 AND NOT APPLE) + add_executable (eglretrace + glws_egl_xlib.cpp + ${CMAKE_SOURCE_DIR}/glproc_egl.cpp + ) + + add_dependencies (eglretrace glproc) + + target_link_libraries (eglretrace + retrace_common + common + ${PNG_LIBRARIES} + ${ZLIB_LIBRARIES} + ${SNAPPY_LIBRARIES} + ${X11_X11_LIB} + ${CMAKE_THREAD_LIBS_INIT} + dl + ) + + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries (eglretrace rt) + endif () + + install (TARGETS eglretrace RUNTIME DESTINATION bin) +endif () + +if (WIN32 AND DirectX_D3DX9_FOUND) + add_custom_command ( + OUTPUT d3dretrace_d3d9.cpp + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/d3dretrace.py > ${CMAKE_CURRENT_BINARY_DIR}/d3dretrace_d3d9.cpp + DEPENDS + d3dretrace.py + retrace.py + ${CMAKE_SOURCE_DIR}/specs/d3d9.py + ${CMAKE_SOURCE_DIR}/specs/d3d9types.py + ${CMAKE_SOURCE_DIR}/specs/d3d9caps.py + ${CMAKE_SOURCE_DIR}/specs/winapi.py + ${CMAKE_SOURCE_DIR}/specs/stdapi.py + ) + + include_directories (SYSTEM ${DirectX_D3DX9_INCLUDE_DIR}) + add_executable (d3dretrace + retrace.cpp + retrace_stdc.cpp + d3dretrace_main.cpp + d3dretrace_d3d9.cpp + ) + target_link_libraries (d3dretrace + common + ${ZLIB_LIBRARIES} + ${SNAPPY_LIBRARIES} + ${DirectX_D3D9_LIBRARY} + ) +endif () + diff --git a/retrace/d3dretrace.hpp b/retrace/d3dretrace.hpp new file mode 100644 index 0000000..cac94cc --- /dev/null +++ b/retrace/d3dretrace.hpp @@ -0,0 +1,41 @@ +/************************************************************************** + * + * Copyright 2012 Jose Fonseca + * 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. + * + **************************************************************************/ + +#ifndef _D3DRETRACE_HPP_ +#define _D3DRETRACE_HPP_ + +#include "retrace.hpp" + + +namespace d3dretrace { + + +extern const retrace::Entry d3d9_callbacks[]; + + +} /* namespace d3dretrace */ + + +#endif /* _D3DRETRACE_HPP_ */ diff --git a/retrace/d3dretrace.py b/retrace/d3dretrace.py new file mode 100644 index 0000000..f472a0a --- /dev/null +++ b/retrace/d3dretrace.py @@ -0,0 +1,145 @@ +########################################################################## +# +# Copyright 2011 Jose Fonseca +# 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. +# +##########################################################################/ + + +"""GL retracer generator.""" + + +from retrace import Retracer +import specs.stdapi as stdapi +from specs.d3d9 import d3d9 + + +class D3DRetracer(Retracer): + + table_name = 'd3dretrace::d3d9_callbacks' + + def invokeInterfaceMethod(self, interface, method): + if interface.name == 'IDirect3D9' and method.name == 'CreateDevice': + print 'HWND hWnd = createWindow(pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight);' + print 'pPresentationParameters->hDeviceWindow = hWnd;' + print 'hFocusWindow = hWnd;' + + Retracer.invokeInterfaceMethod(self, interface, method) + + if str(method.type) == 'HRESULT': + print r' if (__result != S_OK) {' + print r' retrace::warning(call) << "failed\n";' + print r' }' + + if interface.name == 'IDirect3DVertexBuffer9' and method.name == 'Lock': + print ' if (!SizeToLock) {' + print ' D3DVERTEXBUFFER_DESC Desc;' + print ' _this->GetDesc(&Desc);' + print ' SizeToLock = Desc.Size;' + print ' }' + + +if __name__ == '__main__': + print r''' +#include + +#include + +#include "d3d9imports.hpp" +#include "d3dretrace.hpp" + + +// XXX: Don't duplicate this code. + +static LRESULT CALLBACK +WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + MINMAXINFO *pMMI; + switch (uMsg) { + case WM_GETMINMAXINFO: + // Allow to create a window bigger than the desktop + pMMI = (MINMAXINFO *)lParam; + pMMI->ptMaxSize.x = 60000; + pMMI->ptMaxSize.y = 60000; + pMMI->ptMaxTrackSize.x = 60000; + pMMI->ptMaxTrackSize.y = 60000; + break; + default: + break; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + + +static HWND +createWindow(int width, int height) { + static bool first = TRUE; + RECT rect; + + if (first) { + WNDCLASS wc; + memset(&wc, 0, sizeof wc); + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.lpfnWndProc = WndProc; + wc.lpszClassName = "d3dretrace"; + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + first = FALSE; + } + + DWORD dwExStyle; + DWORD dwStyle; + HWND hWnd; + + dwExStyle = 0; + dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW; + + int x = 0, y = 0; + + rect.left = x; + rect.top = y; + rect.right = rect.left + width; + rect.bottom = rect.top + height; + + AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle); + + hWnd = CreateWindowEx(dwExStyle, + "d3dretrace", /* wc.lpszClassName */ + NULL, + dwStyle, + 0, /* x */ + 0, /* y */ + rect.right - rect.left, /* width */ + rect.bottom - rect.top, /* height */ + NULL, + NULL, + NULL, + NULL); + ShowWindow(hWnd, SW_SHOW); + return hWnd; +} + +''' + retracer = D3DRetracer() + retracer.retraceApi(d3d9) diff --git a/retrace/d3dretrace_main.cpp b/retrace/d3dretrace_main.cpp new file mode 100644 index 0000000..133f3a7 --- /dev/null +++ b/retrace/d3dretrace_main.cpp @@ -0,0 +1,103 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include "os_string.hpp" +#include "retrace.hpp" +#include "d3dretrace.hpp" + + +namespace d3dretrace { + +static void display(void) { + retrace::Retracer retracer; + + retracer.addCallbacks(d3d9_callbacks); + + trace::Call *call; + + while ((call = retrace::parser.parse_call())) { + retracer.retrace(*call); + + delete call; + } + + exit(0); +} + + +static void usage(void) { + std::cout << + "Usage: d3dretrace [OPTION] TRACE\n" + "Replay TRACE.\n" + "\n" + " -v increase output verbosity\n" + ; +} + + +extern "C" +int main(int argc, char **argv) +{ + + int i; + for (i = 1; i < argc; ++i) { + const char *arg = argv[i]; + + if (arg[0] != '-') { + break; + } + + if (!strcmp(arg, "--")) { + break; + } else if (!strcmp(arg, "--help")) { + usage(); + return 0; + } else if (!strcmp(arg, "-v")) { + ++retrace::verbosity; + } else { + std::cerr << "error: unknown option " << arg << "\n"; + usage(); + return 1; + } + } + + for ( ; i < argc; ++i) { + if (!retrace::parser.open(argv[i])) { + std::cerr << "error: failed to open " << argv[i] << "\n"; + return 1; + } + + display(); + + retrace::parser.close(); + } + + return 0; +} + +} /* namespace glretrace */ diff --git a/retrace/glretrace.hpp b/retrace/glretrace.hpp new file mode 100644 index 0000000..8ba2a80 --- /dev/null +++ b/retrace/glretrace.hpp @@ -0,0 +1,67 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + +#ifndef _GLRETRACE_HPP_ +#define _GLRETRACE_HPP_ + +#include "glws.hpp" +#include "retrace.hpp" + + +namespace glretrace { + + +extern bool double_buffer; +extern bool insideGlBeginEnd; +extern glws::Profile defaultProfile; +extern glws::Visual *visual[glws::PROFILE_MAX]; +extern glws::Drawable *drawable; +extern glws::Context *context; + +extern unsigned frame; +extern long long startTime; +extern bool wait; + +extern bool benchmark; + +extern unsigned dump_state; + +void +checkGlError(trace::Call &call); + +extern const retrace::Entry gl_callbacks[]; +extern const retrace::Entry cgl_callbacks[]; +extern const retrace::Entry glx_callbacks[]; +extern const retrace::Entry wgl_callbacks[]; +extern const retrace::Entry egl_callbacks[]; + +void frame_complete(trace::Call &call); + +void updateDrawable(int width, int height); + +} /* namespace glretrace */ + + +#endif /* _GLRETRACE_HPP_ */ diff --git a/retrace/glretrace.py b/retrace/glretrace.py new file mode 100644 index 0000000..21fc499 --- /dev/null +++ b/retrace/glretrace.py @@ -0,0 +1,438 @@ +########################################################################## +# +# Copyright 2010 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. +# +##########################################################################/ + + +"""GL retracer generator.""" + + +from retrace import Retracer +import specs.stdapi as stdapi +import specs.glapi as glapi +import specs.glesapi as glesapi + + +class GlRetracer(Retracer): + + table_name = 'glretrace::gl_callbacks' + + def retraceFunction(self, function): + Retracer.retraceFunction(self, function) + + array_pointer_function_names = set(( + "glVertexPointer", + "glNormalPointer", + "glColorPointer", + "glIndexPointer", + "glTexCoordPointer", + "glEdgeFlagPointer", + "glFogCoordPointer", + "glSecondaryColorPointer", + + "glInterleavedArrays", + + "glVertexPointerEXT", + "glNormalPointerEXT", + "glColorPointerEXT", + "glIndexPointerEXT", + "glTexCoordPointerEXT", + "glEdgeFlagPointerEXT", + "glFogCoordPointerEXT", + "glSecondaryColorPointerEXT", + + "glVertexAttribPointer", + "glVertexAttribPointerARB", + "glVertexAttribPointerNV", + "glVertexAttribIPointer", + "glVertexAttribIPointerEXT", + "glVertexAttribLPointer", + "glVertexAttribLPointerEXT", + + #"glMatrixIndexPointerARB", + )) + + draw_array_function_names = set([ + "glDrawArrays", + "glDrawArraysEXT", + "glDrawArraysIndirect", + "glDrawArraysInstanced", + "glDrawArraysInstancedARB", + "glDrawArraysInstancedEXT", + "glDrawArraysInstancedBaseInstance", + "glDrawMeshArraysSUN", + "glMultiDrawArrays", + "glMultiDrawArraysEXT", + "glMultiModeDrawArraysIBM", + ]) + + draw_elements_function_names = set([ + "glDrawElements", + "glDrawElementsBaseVertex", + "glDrawElementsIndirect", + "glDrawElementsInstanced", + "glDrawElementsInstancedARB", + "glDrawElementsInstancedEXT", + "glDrawElementsInstancedBaseVertex", + "glDrawElementsInstancedBaseInstance", + "glDrawElementsInstancedBaseVertexBaseInstance", + "glDrawRangeElements", + "glDrawRangeElementsEXT", + "glDrawRangeElementsBaseVertex", + "glMultiDrawElements", + "glMultiDrawElementsBaseVertex", + "glMultiDrawElementsEXT", + "glMultiModeDrawElementsIBM", + ]) + + draw_indirect_function_names = set([ + "glDrawArraysIndirect", + "glDrawElementsIndirect", + ]) + + misc_draw_function_names = set([ + "glCallList", + "glCallLists", + "glClear", + "glEnd", + "glDrawPixels", + "glBlitFramebuffer", + "glBlitFramebufferEXT", + ]) + + bind_framebuffer_function_names = set([ + "glBindFramebuffer", + "glBindFramebufferEXT", + "glBindFramebufferOES", + ]) + + # Names of the functions that can pack into the current pixel buffer + # object. See also the ARB_pixel_buffer_object specification. + pack_function_names = set([ + 'glGetCompressedTexImage', + 'glGetConvolutionFilter', + 'glGetHistogram', + 'glGetMinmax', + 'glGetPixelMapfv', + 'glGetPixelMapuiv', + 'glGetPixelMapusv', + 'glGetPolygonStipple', + 'glGetSeparableFilter', + 'glGetTexImage', + 'glReadPixels', + 'glGetnCompressedTexImageARB', + 'glGetnConvolutionFilterARB', + 'glGetnHistogramARB', + 'glGetnMinmaxARB', + 'glGetnPixelMapfvARB', + 'glGetnPixelMapuivARB', + 'glGetnPixelMapusvARB', + 'glGetnPolygonStippleARB', + 'glGetnSeparableFilterARB', + 'glGetnTexImageARB', + 'glReadnPixelsARB', + ]) + + map_function_names = set([ + 'glMapBuffer', + 'glMapBufferARB', + 'glMapBufferOES', + 'glMapBufferRange', + 'glMapNamedBufferEXT', + 'glMapNamedBufferRangeEXT', + 'glMapObjectBufferATI', + ]) + + unmap_function_names = set([ + 'glUnmapBuffer', + 'glUnmapBufferARB', + 'glUnmapBufferOES', + 'glUnmapNamedBufferEXT', + 'glUnmapObjectBufferATI', + ]) + + def retraceFunctionBody(self, function): + is_array_pointer = function.name in self.array_pointer_function_names + is_draw_array = function.name in self.draw_array_function_names + is_draw_elements = function.name in self.draw_elements_function_names + is_misc_draw = function.name in self.misc_draw_function_names + + if is_array_pointer or is_draw_array or is_draw_elements: + print ' if (retrace::parser.version < 1) {' + + if is_array_pointer or is_draw_array: + print ' GLint __array_buffer = 0;' + print ' glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);' + print ' if (!__array_buffer) {' + self.failFunction(function) + print ' }' + + if is_draw_elements: + print ' GLint __element_array_buffer = 0;' + print ' glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);' + print ' if (!__element_array_buffer) {' + self.failFunction(function) + print ' }' + + print ' }' + + # When no pack buffer object is bound, the pack functions are no-ops. + if function.name in self.pack_function_names: + print ' GLint __pack_buffer = 0;' + print ' glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &__pack_buffer);' + print ' if (!__pack_buffer) {' + print ' return;' + print ' }' + + # Pre-snapshots + if function.name in self.bind_framebuffer_function_names: + print ' assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);' + if function.name == 'glFrameTerminatorGREMEDY': + print ' glretrace::frame_complete(call);' + return + + Retracer.retraceFunctionBody(self, function) + + # Post-snapshots + if function.name in ('glFlush', 'glFinish'): + print ' if (!glretrace::double_buffer) {' + print ' glretrace::frame_complete(call);' + print ' }' + if is_draw_array or is_draw_elements or is_misc_draw: + print ' assert(call.flags & trace::CALL_FLAG_RENDER);' + + + def invokeFunction(self, function): + # Infer the drawable size from GL calls + if function.name == "glViewport": + print ' glretrace::updateDrawable(x + width, y + height);' + if function.name == "glViewportArray": + # We are concerned about drawables so only care for the first viewport + print ' if (first == 0 && count > 0) {' + print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];' + print ' glretrace::updateDrawable(x + w, y + h);' + print ' }' + if function.name == "glViewportIndexedf": + print ' if (index == 0) {' + print ' glretrace::updateDrawable(x + w, y + h);' + print ' }' + if function.name == "glViewportIndexedfv": + print ' if (index == 0) {' + print ' GLfloat x = v[0], y = v[1], w = v[2], h = v[3];' + print ' glretrace::updateDrawable(x + w, y + h);' + print ' }' + if function.name in ('glBlitFramebuffer', 'glBlitFramebufferEXT'): + # Some applications do all their rendering in a framebuffer, and + # then just blit to the drawable without ever calling glViewport. + print ' glretrace::updateDrawable(std::max(dstX0, dstX1), std::max(dstY0, dstY1));' + + if function.name == "glEnd": + print ' glretrace::insideGlBeginEnd = false;' + + if function.name.startswith('gl') and not function.name.startswith('glX'): + print r' if (!glretrace::context && !glretrace::benchmark && !retrace::profiling) {' + print r' retrace::warning(call) << "no current context\n";' + print r' }' + + if function.name == 'memcpy': + print ' if (!dest || !src || !n) return;' + + # Destroy the buffer mapping + if function.name in self.unmap_function_names: + print r' GLvoid *ptr = NULL;' + if function.name == 'glUnmapBuffer': + print r' glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &ptr);' + elif function.name == 'glUnmapBufferARB': + print r' glGetBufferPointervARB(target, GL_BUFFER_MAP_POINTER_ARB, &ptr);' + elif function.name == 'glUnmapBufferOES': + print r' glGetBufferPointervOES(target, GL_BUFFER_MAP_POINTER_OES, &ptr);' + elif function.name == 'glUnmapNamedBufferEXT': + print r' glGetNamedBufferPointervEXT(buffer, GL_BUFFER_MAP_POINTER, &ptr);' + elif function.name == 'glUnmapObjectBufferATI': + # TODO + pass + else: + assert False + print r' if (ptr) {' + print r' retrace::delRegionByPointer(ptr);' + print r' } else {' + print r' retrace::warning(call) << "no current context\n";' + print r' }' + + Retracer.invokeFunction(self, function) + + # Error checking + if function.name == "glBegin": + print ' glretrace::insideGlBeginEnd = true;' + elif function.name.startswith('gl'): + # glGetError is not allowed inside glBegin/glEnd + print ' if (!glretrace::benchmark && !retrace::profiling && !glretrace::insideGlBeginEnd) {' + print ' glretrace::checkGlError(call);' + if function.name in ('glProgramStringARB', 'glProgramStringNV'): + print r' GLint error_position = -1;' + print r' glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_position);' + print r' if (error_position != -1) {' + print r' const char *error_string = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);' + print r' retrace::warning(call) << error_string << "\n";' + print r' }' + if function.name == 'glCompileShader': + print r' GLint compile_status = 0;' + print r' glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);' + print r' if (!compile_status) {' + print r' GLint info_log_length = 0;' + print r' glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);' + print r' GLchar *infoLog = new GLchar[info_log_length];' + print r' glGetShaderInfoLog(shader, info_log_length, NULL, infoLog);' + print r' retrace::warning(call) << infoLog << "\n";' + print r' delete [] infoLog;' + print r' }' + if function.name == 'glLinkProgram': + print r' GLint link_status = 0;' + print r' glGetProgramiv(program, GL_LINK_STATUS, &link_status);' + print r' if (!link_status) {' + print r' GLint info_log_length = 0;' + print r' glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);' + print r' GLchar *infoLog = new GLchar[info_log_length];' + print r' glGetProgramInfoLog(program, info_log_length, NULL, infoLog);' + print r' retrace::warning(call) << infoLog << "\n";' + print r' delete [] infoLog;' + print r' }' + if function.name == 'glCompileShaderARB': + print r' GLint compile_status = 0;' + print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);' + print r' if (!compile_status) {' + print r' GLint info_log_length = 0;' + print r' glGetObjectParameterivARB(shaderObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);' + print r' GLchar *infoLog = new GLchar[info_log_length];' + print r' glGetInfoLogARB(shaderObj, info_log_length, NULL, infoLog);' + print r' retrace::warning(call) << infoLog << "\n";' + print r' delete [] infoLog;' + print r' }' + if function.name == 'glLinkProgramARB': + print r' GLint link_status = 0;' + print r' glGetObjectParameterivARB(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);' + print r' if (!link_status) {' + print r' GLint info_log_length = 0;' + print r' glGetObjectParameterivARB(programObj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length);' + print r' GLchar *infoLog = new GLchar[info_log_length];' + print r' glGetInfoLogARB(programObj, info_log_length, NULL, infoLog);' + print r' retrace::warning(call) << infoLog << "\n";' + print r' delete [] infoLog;' + print r' }' + if function.name in self.map_function_names: + print r' if (!__result) {' + print r' retrace::warning(call) << "failed to map buffer\n";' + print r' }' + if function.name in self.unmap_function_names and function.type is not stdapi.Void: + print r' if (!__result) {' + print r' retrace::warning(call) << "failed to unmap buffer\n";' + print r' }' + if function.name in ('glGetAttribLocation', 'glGetAttribLocationARB'): + print r' GLint __orig_result = call.ret->toSInt();' + print r' if (__result != __orig_result) {' + print r' retrace::warning(call) << "vertex attrib location mismatch " << __orig_result << " -> " << __result << "\n";' + print r' }' + if function.name in ('glCheckFramebufferStatus', 'glCheckFramebufferStatusEXT', 'glCheckNamedFramebufferStatusEXT'): + print r' GLint __orig_result = call.ret->toSInt();' + print r' if (__orig_result == GL_FRAMEBUFFER_COMPLETE &&' + print r' __result != GL_FRAMEBUFFER_COMPLETE) {' + print r' retrace::warning(call) << "incomplete framebuffer (" << glstate::enumToString(__result) << ")\n";' + print r' }' + print ' }' + + # Query the buffer length for whole buffer mappings + if function.name in self.map_function_names: + if 'length' in function.argNames(): + assert 'BufferRange' in function.name + else: + assert 'BufferRange' not in function.name + print r' GLint length = 0;' + if function.name in ('glMapBuffer', 'glMapBufferOES'): + print r' glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);' + elif function.name == 'glMapBufferARB': + print r' glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);' + elif function.name == 'glMapNamedBufferEXT': + print r' glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);' + elif function.name == 'glMapObjectBufferATI': + print r' glGetObjectBufferivATI(buffer, GL_OBJECT_BUFFER_SIZE_ATI, &length);' + else: + assert False + + def extractArg(self, function, arg, arg_type, lvalue, rvalue): + if function.name in self.array_pointer_function_names and arg.name == 'pointer': + print ' %s = static_cast<%s>(retrace::toPointer(%s, true));' % (lvalue, arg_type, rvalue) + return + + if function.name in self.draw_elements_function_names and arg.name == 'indices' or\ + function.name in self.draw_indirect_function_names and arg.name == 'indirect': + self.extractOpaqueArg(function, arg, arg_type, lvalue, rvalue) + return + + # Handle pointer with offsets into the current pack pixel buffer + # object. + if function.name in self.pack_function_names and arg.output: + assert isinstance(arg_type, (stdapi.Pointer, stdapi.Array, stdapi.Blob, stdapi.Opaque)) + print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, arg_type, rvalue) + return + + if arg.type is glapi.GLlocation \ + and 'program' not in function.argNames(): + print ' GLint program = -1;' + print ' glGetIntegerv(GL_CURRENT_PROGRAM, &program);' + + if arg.type is glapi.GLlocationARB \ + and 'programObj' not in function.argNames(): + print ' GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);' + + Retracer.extractArg(self, function, arg, arg_type, lvalue, rvalue) + + # Don't try to use more samples than the implementation supports + if arg.name == 'samples': + assert arg.type is glapi.GLsizei + print ' GLint max_samples = 0;' + print ' glGetIntegerv(GL_MAX_SAMPLES, &max_samples);' + print ' if (samples > max_samples) {' + print ' samples = max_samples;' + print ' }' + + # These parameters are referred beyond the call life-time + # TODO: Replace ad-hoc solution for bindable parameters with general one + if function.name in ('glFeedbackBuffer', 'glSelectBuffer') and arg.output: + print ' _allocator.bind(%s);' % arg.name + + + +if __name__ == '__main__': + print r''' +#include + +#include "glproc.hpp" +#include "glretrace.hpp" +#include "glstate.hpp" + + +''' + api = glapi.glapi + api.addApi(glesapi.glesapi) + retracer = GlRetracer() + retracer.retraceApi(api) diff --git a/retrace/glretrace_cgl.cpp b/retrace/glretrace_cgl.cpp new file mode 100644 index 0000000..63b94b4 --- /dev/null +++ b/retrace/glretrace_cgl.cpp @@ -0,0 +1,125 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include "glproc.hpp" +#include "retrace.hpp" +#include "glretrace.hpp" + + +using namespace glretrace; + + +typedef std::map DrawableMap; +typedef std::map ContextMap; +static DrawableMap drawable_map; +static ContextMap context_map; +static glws::Context *sharedContext = NULL; + + +static glws::Drawable * +getDrawable(unsigned long drawable_id) { + if (drawable_id == 0) { + return NULL; + } + + /* XXX: Support multiple drawables. */ + drawable_id = 1; + + DrawableMap::const_iterator it; + it = drawable_map.find(drawable_id); + if (it == drawable_map.end()) { + return (drawable_map[drawable_id] = glws::createDrawable(visual[glretrace::defaultProfile])); + } + + return it->second; +} + + +static glws::Context * +getContext(unsigned long long ctx) { + if (ctx == 0) { + return NULL; + } + + ContextMap::const_iterator it; + it = context_map.find(ctx); + if (it == context_map.end()) { + glws::Context *context; + context_map[ctx] = context = glws::createContext(visual[glretrace::defaultProfile], sharedContext, glretrace::defaultProfile); + if (!sharedContext) { + sharedContext = context; + } + return context; + } + + return it->second; +} + + +static void retrace_CGLSetCurrentContext(trace::Call &call) { + unsigned long long ctx = call.arg(0).toUIntPtr(); + + glws::Drawable *new_drawable = getDrawable(ctx); + glws::Context *new_context = getContext(ctx); + + bool result = glws::makeCurrent(new_drawable, new_context); + + if (new_drawable && new_context && result) { + drawable = new_drawable; + context = new_context; + } else { + drawable = NULL; + context = NULL; + } +} + + +static void retrace_CGLFlushDrawable(trace::Call &call) { + if (drawable && context) { + if (double_buffer) { + drawable->swapBuffers(); + } else { + glFlush(); + } + + frame_complete(call); + } +} + + +const retrace::Entry glretrace::cgl_callbacks[] = { + {"CGLSetCurrentContext", &retrace_CGLSetCurrentContext}, + {"CGLGetCurrentContext", &retrace::ignore}, + {"CGLEnable", &retrace::ignore}, + {"CGLDisable", &retrace::ignore}, + {"CGLSetParameter", &retrace::ignore}, + {"CGLGetParameter", &retrace::ignore}, + {"CGLFlushDrawable", &retrace_CGLFlushDrawable}, + {NULL, NULL}, +}; + diff --git a/retrace/glretrace_egl.cpp b/retrace/glretrace_egl.cpp new file mode 100644 index 0000000..d7b14ec --- /dev/null +++ b/retrace/glretrace_egl.cpp @@ -0,0 +1,284 @@ +/************************************************************************** + * + * 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" + +#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; + +static unsigned int current_api = EGL_OPENGL_ES_API; +static glws::Profile last_profile = glws::PROFILE_COMPAT; + +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 glws::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); + glws::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::Visual *visual = glretrace::visual[profile]; + + glws::Drawable *drawable = glws::createDrawable(visual); + drawable_map[orig_surface] = drawable; +} + +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()) { + if (it->second != drawable) { + // TODO: reference count + delete it->second; + } + drawable_map.erase(it); + } +} + +static void retrace_eglBindAPI(trace::Call &call) { + 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(); + glws::Context *share_context = getContext(call.arg(2).toUIntPtr()); + trace::Array *attrib_array = dynamic_cast(&call.arg(3)); + glws::Profile profile; + + switch (current_api) { + case EGL_OPENGL_API: + profile = glws::PROFILE_COMPAT; + break; + case EGL_OPENGL_ES_API: + default: + profile = glws::PROFILE_ES1; + if (attrib_array) { + for (int i = 0; i < attrib_array->values.size(); i += 2) { + int v = attrib_array->values[i]->toSInt(); + if (v == EGL_CONTEXT_CLIENT_VERSION) { + v = attrib_array->values[i + 1]->toSInt(); + if (v == 2) + profile = glws::PROFILE_ES2; + break; + } + } + } + break; + } + + + glws::Context *context = glws::createContext(glretrace::visual[profile], share_context, profile); + if (!context) { + const char *name; + switch (profile) { + case glws::PROFILE_COMPAT: + name = "OpenGL"; + break; + case glws::PROFILE_ES1: + name = "OpenGL ES 1.1"; + break; + case glws::PROFILE_ES2: + name = "OpenGL ES 2.0"; + break; + default: + name = "unknown"; + break; + } + + retrace::warning(call) << "Failed to create " << name << " context.\n"; + os::abort(); + } + + 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()) { + delete it->second; + context_map.erase(it); + } +} + +static void retrace_eglMakeCurrent(trace::Call &call) { + glws::Drawable *new_drawable = getDrawable(call.arg(1).toUIntPtr()); + glws::Context *new_context = getContext(call.arg(3).toUIntPtr()); + + if (new_drawable == drawable && new_context == context) { + return; + } + + if (drawable && context) { + glFlush(); + if (!double_buffer) { + frame_complete(call); + } + } + + bool result = glws::makeCurrent(new_drawable, new_context); + + if (new_drawable && new_context && result) { + drawable = new_drawable; + context = new_context; + } else { + drawable = NULL; + context = NULL; + } +} + + +static void retrace_eglSwapBuffers(trace::Call &call) { + frame_complete(call); + + if (double_buffer && 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::ignore}, + {"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}, + {"eglSwapBuffers", &retrace_eglSwapBuffers}, + //{"eglCopyBuffers", &retrace::ignore}, + {"eglGetProcAddress", &retrace::ignore}, + {NULL, NULL}, +}; diff --git a/retrace/glretrace_glx.cpp b/retrace/glretrace_glx.cpp new file mode 100644 index 0000000..7342908 --- /dev/null +++ b/retrace/glretrace_glx.cpp @@ -0,0 +1,260 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include "glproc.hpp" +#include "retrace.hpp" +#include "glretrace.hpp" + + +using namespace glretrace; + + +typedef std::map DrawableMap; +typedef std::map ContextMap; +static DrawableMap drawable_map; +static ContextMap context_map; + + +static glws::Drawable * +getDrawable(unsigned long drawable_id) { + if (drawable_id == 0) { + return NULL; + } + + DrawableMap::const_iterator it; + it = drawable_map.find(drawable_id); + if (it == drawable_map.end()) { + return (drawable_map[drawable_id] = glws::createDrawable(visual[glretrace::defaultProfile])); + } + + return it->second; +} + +static glws::Context * +getContext(unsigned long long context_ptr) { + if (context_ptr == 0) { + return NULL; + } + + ContextMap::const_iterator it; + it = context_map.find(context_ptr); + if (it == context_map.end()) { + return (context_map[context_ptr] = glws::createContext(visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile)); + } + + return it->second; +} + +static void retrace_glXCreateContext(trace::Call &call) { + unsigned long long orig_context = call.ret->toUIntPtr(); + glws::Context *share_context = getContext(call.arg(2).toUIntPtr()); + + glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); + context_map[orig_context] = context; +} + +static void retrace_glXCreateContextAttribsARB(trace::Call &call) { + unsigned long long orig_context = call.ret->toUIntPtr(); + glws::Context *share_context = getContext(call.arg(2).toUIntPtr()); + + glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); + context_map[orig_context] = context; +} + +static void retrace_glXMakeCurrent(trace::Call &call) { + glws::Drawable *new_drawable = getDrawable(call.arg(1).toUInt()); + glws::Context *new_context = getContext(call.arg(2).toUIntPtr()); + + if (new_drawable == drawable && new_context == context) { + return; + } + + if (drawable && context) { + glFlush(); + if (!double_buffer) { + frame_complete(call); + } + } + + bool result = glws::makeCurrent(new_drawable, new_context); + + if (new_drawable && new_context && result) { + drawable = new_drawable; + context = new_context; + } else { + drawable = NULL; + context = NULL; + } +} + + +static void retrace_glXDestroyContext(trace::Call &call) { + glws::Context *context = getContext(call.arg(1).toUIntPtr()); + + if (!context) { + return; + } + + delete context; +} + +static void retrace_glXSwapBuffers(trace::Call &call) { + frame_complete(call); + if (double_buffer) { + drawable->swapBuffers(); + } else { + glFlush(); + } +} + +static void retrace_glXCreateNewContext(trace::Call &call) { + unsigned long long orig_context = call.ret->toUIntPtr(); + glws::Context *share_context = getContext(call.arg(3).toUIntPtr()); + + glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); + context_map[orig_context] = context; +} + +static void retrace_glXMakeContextCurrent(trace::Call &call) { + glws::Drawable *new_drawable = getDrawable(call.arg(1).toUInt()); + glws::Context *new_context = getContext(call.arg(3).toUIntPtr()); + + if (new_drawable == drawable && new_context == context) { + return; + } + + if (drawable && context) { + glFlush(); + if (!double_buffer) { + frame_complete(call); + } + } + + bool result = glws::makeCurrent(new_drawable, new_context); + + if (new_drawable && new_context && result) { + drawable = new_drawable; + context = new_context; + } else { + drawable = NULL; + context = NULL; + } +} + +const retrace::Entry glretrace::glx_callbacks[] = { + //{"glXBindChannelToWindowSGIX", &retrace_glXBindChannelToWindowSGIX}, + //{"glXBindSwapBarrierNV", &retrace_glXBindSwapBarrierNV}, + //{"glXBindSwapBarrierSGIX", &retrace_glXBindSwapBarrierSGIX}, + //{"glXBindTexImageEXT", &retrace_glXBindTexImageEXT}, + //{"glXChannelRectSGIX", &retrace_glXChannelRectSGIX}, + //{"glXChannelRectSyncSGIX", &retrace_glXChannelRectSyncSGIX}, + {"glXChooseFBConfig", &retrace::ignore}, + {"glXChooseFBConfigSGIX", &retrace::ignore}, + {"glXChooseVisual", &retrace::ignore}, + //{"glXCopyContext", &retrace_glXCopyContext}, + //{"glXCopyImageSubDataNV", &retrace_glXCopyImageSubDataNV}, + //{"glXCopySubBufferMESA", &retrace_glXCopySubBufferMESA}, + {"glXCreateContextAttribsARB", &retrace_glXCreateContextAttribsARB}, + {"glXCreateContext", &retrace_glXCreateContext}, + //{"glXCreateContextWithConfigSGIX", &retrace_glXCreateContextWithConfigSGIX}, + //{"glXCreateGLXPbufferSGIX", &retrace_glXCreateGLXPbufferSGIX}, + //{"glXCreateGLXPixmap", &retrace_glXCreateGLXPixmap}, + //{"glXCreateGLXPixmapWithConfigSGIX", &retrace_glXCreateGLXPixmapWithConfigSGIX}, + {"glXCreateNewContext", &retrace_glXCreateNewContext}, + //{"glXCreatePbuffer", &retrace_glXCreatePbuffer}, + //{"glXCreatePixmap", &retrace_glXCreatePixmap}, + //{"glXCreateWindow", &retrace_glXCreateWindow}, + //{"glXCushionSGI", &retrace_glXCushionSGI}, + {"glXDestroyContext", &retrace_glXDestroyContext}, + //{"glXDestroyGLXPbufferSGIX", &retrace_glXDestroyGLXPbufferSGIX}, + //{"glXDestroyGLXPixmap", &retrace_glXDestroyGLXPixmap}, + //{"glXDestroyPbuffer", &retrace_glXDestroyPbuffer}, + //{"glXDestroyPixmap", &retrace_glXDestroyPixmap}, + //{"glXDestroyWindow", &retrace_glXDestroyWindow}, + //{"glXFreeContextEXT", &retrace_glXFreeContextEXT}, + {"glXGetAGPOffsetMESA", &retrace::ignore}, + {"glXGetClientString", &retrace::ignore}, + {"glXGetConfig", &retrace::ignore}, + {"glXGetContextIDEXT", &retrace::ignore}, + {"glXGetCurrentContext", &retrace::ignore}, + {"glXGetCurrentDisplayEXT", &retrace::ignore}, + {"glXGetCurrentDisplay", &retrace::ignore}, + {"glXGetCurrentDrawable", &retrace::ignore}, + {"glXGetCurrentReadDrawable", &retrace::ignore}, + {"glXGetCurrentReadDrawableSGI", &retrace::ignore}, + {"glXGetFBConfigAttrib", &retrace::ignore}, + {"glXGetFBConfigAttribSGIX", &retrace::ignore}, + {"glXGetFBConfigFromVisualSGIX", &retrace::ignore}, + {"glXGetFBConfigs", &retrace::ignore}, + {"glXGetMscRateOML", &retrace::ignore}, + {"glXGetProcAddressARB", &retrace::ignore}, + {"glXGetProcAddress", &retrace::ignore}, + {"glXGetSelectedEvent", &retrace::ignore}, + {"glXGetSelectedEventSGIX", &retrace::ignore}, + {"glXGetSyncValuesOML", &retrace::ignore}, + {"glXGetVideoSyncSGI", &retrace::ignore}, + {"glXGetVisualFromFBConfig", &retrace::ignore}, + {"glXGetVisualFromFBConfigSGIX", &retrace::ignore}, + //{"glXImportContextEXT", &retrace_glXImportContextEXT}, + {"glXIsDirect", &retrace::ignore}, + //{"glXJoinSwapGroupNV", &retrace_glXJoinSwapGroupNV}, + //{"glXJoinSwapGroupSGIX", &retrace_glXJoinSwapGroupSGIX}, + {"glXMakeContextCurrent", &retrace_glXMakeContextCurrent}, + //{"glXMakeCurrentReadSGI", &retrace_glXMakeCurrentReadSGI}, + {"glXMakeCurrent", &retrace_glXMakeCurrent}, + {"glXQueryChannelDeltasSGIX", &retrace::ignore}, + {"glXQueryChannelRectSGIX", &retrace::ignore}, + {"glXQueryContextInfoEXT", &retrace::ignore}, + {"glXQueryContext", &retrace::ignore}, + {"glXQueryDrawable", &retrace::ignore}, + {"glXQueryExtension", &retrace::ignore}, + {"glXQueryExtensionsString", &retrace::ignore}, + {"glXQueryFrameCountNV", &retrace::ignore}, + {"glXQueryGLXPbufferSGIX", &retrace::ignore}, + {"glXQueryMaxSwapBarriersSGIX", &retrace::ignore}, + {"glXQueryMaxSwapGroupsNV", &retrace::ignore}, + {"glXQueryServerString", &retrace::ignore}, + {"glXQuerySwapGroupNV", &retrace::ignore}, + {"glXQueryVersion", &retrace::ignore}, + //{"glXReleaseBuffersMESA", &retrace_glXReleaseBuffersMESA}, + //{"glXReleaseTexImageEXT", &retrace_glXReleaseTexImageEXT}, + //{"glXResetFrameCountNV", &retrace_glXResetFrameCountNV}, + //{"glXSelectEvent", &retrace_glXSelectEvent}, + //{"glXSelectEventSGIX", &retrace_glXSelectEventSGIX}, + //{"glXSet3DfxModeMESA", &retrace_glXSet3DfxModeMESA}, + //{"glXSwapBuffersMscOML", &retrace_glXSwapBuffersMscOML}, + {"glXSwapBuffers", &retrace_glXSwapBuffers}, + {"glXSwapIntervalEXT", &retrace::ignore}, + {"glXSwapIntervalSGI", &retrace::ignore}, + //{"glXUseXFont", &retrace_glXUseXFont}, + {"glXWaitForMscOML", &retrace::ignore}, + {"glXWaitForSbcOML", &retrace::ignore}, + {"glXWaitGL", &retrace::ignore}, + {"glXWaitVideoSyncSGI", &retrace::ignore}, + {"glXWaitX", &retrace::ignore}, + {NULL, NULL}, +}; + diff --git a/retrace/glretrace_main.cpp b/retrace/glretrace_main.cpp new file mode 100644 index 0000000..313a563 --- /dev/null +++ b/retrace/glretrace_main.cpp @@ -0,0 +1,390 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include "os_binary.hpp" +#include "os_string.hpp" +#include "os_time.hpp" +#include "image.hpp" +#include "retrace.hpp" +#include "trace_callset.hpp" +#include "glproc.hpp" +#include "glstate.hpp" +#include "glretrace.hpp" + + +namespace glretrace { + +bool double_buffer = true; +bool insideGlBeginEnd = false; +glws::Profile defaultProfile = glws::PROFILE_COMPAT; +glws::Visual *visual[glws::PROFILE_MAX]; +glws::Drawable *drawable = NULL; +glws::Context *context = NULL; + +unsigned frame = 0; +long long startTime = 0; +bool wait = false; + +bool benchmark = false; +static const char *compare_prefix = NULL; +static const char *snapshot_prefix = NULL; +static trace::CallSet snapshot_frequency; +static trace::CallSet compare_frequency; + +unsigned dump_state = ~0; + +void +checkGlError(trace::Call &call) { + GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + return; + } + + std::ostream & os = retrace::warning(call); + + os << "glGetError("; + os << call.name(); + os << ") = "; + + switch (error) { + case GL_INVALID_ENUM: + os << "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + os << "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + os << "GL_INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + os << "GL_STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + os << "GL_STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + os << "GL_OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + os << "GL_INVALID_FRAMEBUFFER_OPERATION"; + break; + case GL_TABLE_TOO_LARGE: + os << "GL_TABLE_TOO_LARGE"; + break; + default: + os << error; + break; + } + os << "\n"; +} + +/** + * Grow the current drawble. + * + * We need to infer the drawable size from GL calls because the drawable sizes + * are specified by OS specific calls which we do not trace. + */ +void +updateDrawable(int width, int height) { + if (!drawable) { + return; + } + + if (drawable->visible && + width <= drawable->width && + height <= drawable->height) { + return; + } + + // Ignore zero area viewports + if (width == 0 || height == 0) { + return; + } + + // Check for bound framebuffer last, as this may have a performance impact. + GLint draw_framebuffer = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer); + if (draw_framebuffer != 0) { + return; + } + + drawable->resize(width, height); + drawable->show(); + + glScissor(0, 0, width, height); +} + + +static void +snapshot(unsigned call_no) { + assert(snapshot_prefix || compare_prefix); + + if (!drawable) { + return; + } + + image::Image *ref = NULL; + + if (compare_prefix) { + os::String filename = os::String::format("%s%010u.png", compare_prefix, call_no); + ref = image::readPNG(filename); + if (!ref) { + return; + } + if (retrace::verbosity >= 0) { + std::cout << "Read " << filename << "\n"; + } + } + + image::Image *src = glstate::getDrawBufferImage(); + if (!src) { + return; + } + + if (snapshot_prefix) { + if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) { + char comment[21]; + snprintf(comment, sizeof comment, "%u", call_no); + src->writePNM(std::cout, comment); + } else { + os::String filename = os::String::format("%s%010u.png", snapshot_prefix, call_no); + if (src->writePNG(filename) && retrace::verbosity >= 0) { + std::cout << "Wrote " << filename << "\n"; + } + } + } + + if (ref) { + std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n"; + delete ref; + } + + delete src; +} + + +void frame_complete(trace::Call &call) { + ++frame; + + if (!drawable) { + return; + } + + if (!drawable->visible) { + retrace::warning(call) << "could not infer drawable size (glViewport never called)\n"; + } +} + + +static void display(void) { + retrace::Retracer retracer; + + retracer.addCallbacks(gl_callbacks); + retracer.addCallbacks(glx_callbacks); + retracer.addCallbacks(wgl_callbacks); + retracer.addCallbacks(cgl_callbacks); + retracer.addCallbacks(egl_callbacks); + + startTime = os::getTime(); + trace::Call *call; + + while ((call = retrace::parser.parse_call())) { + bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET; + bool doSnapshot = + snapshot_frequency.contains(*call) || + compare_frequency.contains(*call) + ; + + // For calls which cause rendertargets to be swaped, we take the + // snapshot _before_ swapping the rendertargets. + if (doSnapshot && swapRenderTarget) { + if (call->flags & trace::CALL_FLAG_END_FRAME) { + // For swapbuffers/presents we still use this call number, + // spite not have been executed yet. + snapshot(call->no); + } else { + // Whereas for ordinate fbo/rendertarget changes we use the + // previous call's number. + snapshot(call->no - 1); + } + } + + retracer.retrace(*call); + + if (doSnapshot && !swapRenderTarget) { + snapshot(call->no); + } + + if (!insideGlBeginEnd && + drawable && context && + call->no >= dump_state) { + glstate::dumpCurrentContext(std::cout); + exit(0); + } + + delete call; + } + + // Reached the end of trace + glFlush(); + + long long endTime = os::getTime(); + float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency); + + if ((retrace::verbosity >= -1) || (retrace::profiling)) { + std::cout << + "Rendered " << frame << " frames" + " in " << timeInterval << " secs," + " average of " << (frame/timeInterval) << " fps\n"; + } + + if (wait) { + while (glws::processEvents()) {} + } else { + exit(0); + } +} + + +static void usage(void) { + std::cout << + "Usage: glretrace [OPTION] TRACE\n" + "Replay TRACE.\n" + "\n" + " -b benchmark mode (no error checking or warning messages)\n" + " -p profiling mode (run whole trace, dump profiling info)\n" + " -c PREFIX compare against snapshots\n" + " -C CALLSET calls to compare (default is every frame)\n" + " -core use core profile\n" + " -db use a double buffer visual (default)\n" + " -sb use a single buffer visual\n" + " -s PREFIX take snapshots; `-` for PNM stdout output\n" + " -S CALLSET calls to snapshot (default is every frame)\n" + " -v increase output verbosity\n" + " -D CALLNO dump state at specific call no\n" + " -w wait on final frame\n"; +} + +extern "C" +int main(int argc, char **argv) +{ + assert(compare_frequency.empty()); + assert(snapshot_frequency.empty()); + + int i; + for (i = 1; i < argc; ++i) { + const char *arg = argv[i]; + + if (arg[0] != '-') { + break; + } + + if (!strcmp(arg, "--")) { + break; + } else if (!strcmp(arg, "-b")) { + benchmark = true; + retrace::verbosity = -1; + glws::debug = false; + } else if (!strcmp(arg, "-p")) { + retrace::profiling = true; + retrace::verbosity = -1; + glws::debug = false; + } else if (!strcmp(arg, "-c")) { + compare_prefix = argv[++i]; + if (compare_frequency.empty()) { + compare_frequency = trace::CallSet(trace::FREQUENCY_FRAME); + } + } else if (!strcmp(arg, "-C")) { + compare_frequency = trace::CallSet(argv[++i]); + if (compare_prefix == NULL) { + compare_prefix = ""; + } + } else if (!strcmp(arg, "-D")) { + dump_state = atoi(argv[++i]); + retrace::verbosity = -2; + } else if (!strcmp(arg, "-core")) { + defaultProfile = glws::PROFILE_CORE; + } else if (!strcmp(arg, "-db")) { + double_buffer = true; + } else if (!strcmp(arg, "-sb")) { + double_buffer = false; + } else if (!strcmp(arg, "--help")) { + usage(); + return 0; + } else if (!strcmp(arg, "-s")) { + snapshot_prefix = argv[++i]; + if (snapshot_frequency.empty()) { + snapshot_frequency = trace::CallSet(trace::FREQUENCY_FRAME); + } + if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) { + os::setBinaryMode(stdout); + retrace::verbosity = -2; + } + } else if (!strcmp(arg, "-S")) { + snapshot_frequency = trace::CallSet(argv[++i]); + if (snapshot_prefix == NULL) { + snapshot_prefix = ""; + } + } else if (!strcmp(arg, "-v")) { + ++retrace::verbosity; + } else if (!strcmp(arg, "-w")) { + wait = true; + } else { + std::cerr << "error: unknown option " << arg << "\n"; + usage(); + return 1; + } + } + + glws::init(); + visual[glws::PROFILE_COMPAT] = glws::createVisual(double_buffer, glws::PROFILE_COMPAT); + visual[glws::PROFILE_CORE] = glws::createVisual(double_buffer, glws::PROFILE_CORE); + visual[glws::PROFILE_ES1] = glws::createVisual(double_buffer, glws::PROFILE_ES1); + visual[glws::PROFILE_ES2] = glws::createVisual(double_buffer, glws::PROFILE_ES2); + + for ( ; i < argc; ++i) { + if (!retrace::parser.open(argv[i])) { + std::cerr << "error: failed to open " << argv[i] << "\n"; + return 1; + } + + display(); + + retrace::parser.close(); + } + + for (int n = 0; n < glws::PROFILE_MAX; n++) { + delete visual[n]; + } + + glws::cleanup(); + + return 0; +} + +} /* namespace glretrace */ diff --git a/retrace/glretrace_wgl.cpp b/retrace/glretrace_wgl.cpp new file mode 100644 index 0000000..447d177 --- /dev/null +++ b/retrace/glretrace_wgl.cpp @@ -0,0 +1,309 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include "glproc.hpp" +#include "retrace.hpp" +#include "glretrace.hpp" + + +using namespace glretrace; + + +typedef std::map DrawableMap; +typedef std::map ContextMap; +static DrawableMap drawable_map; +static DrawableMap pbuffer_map; +static ContextMap context_map; + + +static glws::Drawable * +getDrawable(unsigned long long hdc) { + if (hdc == 0) { + return NULL; + } + + DrawableMap::const_iterator it; + it = drawable_map.find(hdc); + if (it == drawable_map.end()) { + return (drawable_map[hdc] = glws::createDrawable(visual[glretrace::defaultProfile])); + } + + return it->second; +} + +static void retrace_wglCreateContext(trace::Call &call) { + unsigned long long orig_context = call.ret->toUIntPtr(); + glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], NULL, glretrace::defaultProfile); + context_map[orig_context] = context; +} + +static void retrace_wglDeleteContext(trace::Call &call) { +} + +static void retrace_wglMakeCurrent(trace::Call &call) { + if (drawable && context) { + glFlush(); + if (!double_buffer) { + frame_complete(call); + } + } + + glws::Drawable *new_drawable = getDrawable(call.arg(0).toUIntPtr()); + glws::Context *new_context = context_map[call.arg(1).toUIntPtr()]; + + bool result = glws::makeCurrent(new_drawable, new_context); + + if (new_drawable && new_context && result) { + drawable = new_drawable; + context = new_context; + } else { + drawable = NULL; + context = NULL; + } +} + +static void retrace_wglCopyContext(trace::Call &call) { +} + +static void retrace_wglChoosePixelFormat(trace::Call &call) { +} + +static void retrace_wglDescribePixelFormat(trace::Call &call) { +} + +static void retrace_wglSetPixelFormat(trace::Call &call) { +} + +static void retrace_wglSwapBuffers(trace::Call &call) { + frame_complete(call); + if (double_buffer) { + drawable->swapBuffers(); + } else { + glFlush(); + } +} + +static void retrace_wglShareLists(trace::Call &call) { + unsigned long long hglrc1 = call.arg(0).toUIntPtr(); + unsigned long long hglrc2 = call.arg(1).toUIntPtr(); + + glws::Context *share_context = context_map[hglrc1]; + glws::Context *old_context = context_map[hglrc2]; + + glws::Context *new_context = + glws::createContext(old_context->visual, share_context, glretrace::defaultProfile); + if (new_context) { + if (context == old_context) { + glws::makeCurrent(drawable, new_context); + } + + context_map[hglrc2] = new_context; + + delete old_context; + } +} + +static void retrace_wglCreateLayerContext(trace::Call &call) { + retrace_wglCreateContext(call); +} + +static void retrace_wglDescribeLayerPlane(trace::Call &call) { +} + +static void retrace_wglSetLayerPaletteEntries(trace::Call &call) { +} + +static void retrace_wglRealizeLayerPalette(trace::Call &call) { +} + +static void retrace_wglSwapLayerBuffers(trace::Call &call) { + retrace_wglSwapBuffers(call); +} + +static void retrace_wglUseFontBitmapsA(trace::Call &call) { +} + +static void retrace_wglUseFontBitmapsW(trace::Call &call) { +} + +static void retrace_wglSwapMultipleBuffers(trace::Call &call) { +} + +static void retrace_wglUseFontOutlinesA(trace::Call &call) { +} + +static void retrace_wglUseFontOutlinesW(trace::Call &call) { +} + +static void retrace_wglCreateBufferRegionARB(trace::Call &call) { +} + +static void retrace_wglDeleteBufferRegionARB(trace::Call &call) { +} + +static void retrace_wglSaveBufferRegionARB(trace::Call &call) { +} + +static void retrace_wglRestoreBufferRegionARB(trace::Call &call) { +} + +static void retrace_wglChoosePixelFormatARB(trace::Call &call) { +} + +static void retrace_wglMakeContextCurrentARB(trace::Call &call) { +} + +static void retrace_wglCreatePbufferARB(trace::Call &call) { + int iWidth = call.arg(2).toUInt(); + int iHeight = call.arg(3).toUInt(); + + unsigned long long orig_pbuffer = call.ret->toUIntPtr(); + glws::Drawable *drawable = glws::createDrawable(glretrace::visual[glretrace::defaultProfile]); + + drawable->resize(iWidth, iHeight); + drawable->show(); + + pbuffer_map[orig_pbuffer] = drawable; +} + +static void retrace_wglGetPbufferDCARB(trace::Call &call) { + glws::Drawable *pbuffer = pbuffer_map[call.arg(0).toUIntPtr()]; + + unsigned long long orig_hdc = call.ret->toUIntPtr(); + + drawable_map[orig_hdc] = pbuffer; +} + +static void retrace_wglReleasePbufferDCARB(trace::Call &call) { +} + +static void retrace_wglDestroyPbufferARB(trace::Call &call) { +} + +static void retrace_wglQueryPbufferARB(trace::Call &call) { +} + +static void retrace_wglBindTexImageARB(trace::Call &call) { +} + +static void retrace_wglReleaseTexImageARB(trace::Call &call) { +} + +static void retrace_wglSetPbufferAttribARB(trace::Call &call) { +} + +static void retrace_wglCreateContextAttribsARB(trace::Call &call) { + unsigned long long orig_context = call.ret->toUIntPtr(); + glws::Context *share_context = NULL; + + if (call.arg(1).toPointer()) { + share_context = context_map[call.arg(1).toUIntPtr()]; + } + + glws::Context *context = glws::createContext(glretrace::visual[glretrace::defaultProfile], share_context, glretrace::defaultProfile); + context_map[orig_context] = context; +} + +static void retrace_wglMakeContextCurrentEXT(trace::Call &call) { +} + +static void retrace_wglChoosePixelFormatEXT(trace::Call &call) { +} + +static void retrace_wglSwapIntervalEXT(trace::Call &call) { +} + +static void retrace_wglAllocateMemoryNV(trace::Call &call) { +} + +static void retrace_wglFreeMemoryNV(trace::Call &call) { +} + +static void retrace_glAddSwapHintRectWIN(trace::Call &call) { +} + +static void retrace_wglGetProcAddress(trace::Call &call) { +} + +const retrace::Entry glretrace::wgl_callbacks[] = { + {"glAddSwapHintRectWIN", &retrace_glAddSwapHintRectWIN}, + {"wglAllocateMemoryNV", &retrace_wglAllocateMemoryNV}, + {"wglBindTexImageARB", &retrace_wglBindTexImageARB}, + {"wglChoosePixelFormat", &retrace_wglChoosePixelFormat}, + {"wglChoosePixelFormatARB", &retrace_wglChoosePixelFormatARB}, + {"wglChoosePixelFormatEXT", &retrace_wglChoosePixelFormatEXT}, + {"wglCopyContext", &retrace_wglCopyContext}, + {"wglCreateBufferRegionARB", &retrace_wglCreateBufferRegionARB}, + {"wglCreateContext", &retrace_wglCreateContext}, + {"wglCreateContextAttribsARB", &retrace_wglCreateContextAttribsARB}, + {"wglCreateLayerContext", &retrace_wglCreateLayerContext}, + {"wglCreatePbufferARB", &retrace_wglCreatePbufferARB}, + {"wglDeleteBufferRegionARB", &retrace_wglDeleteBufferRegionARB}, + {"wglDeleteContext", &retrace_wglDeleteContext}, + {"wglDescribeLayerPlane", &retrace_wglDescribeLayerPlane}, + {"wglDescribePixelFormat", &retrace_wglDescribePixelFormat}, + {"wglDestroyPbufferARB", &retrace_wglDestroyPbufferARB}, + {"wglFreeMemoryNV", &retrace_wglFreeMemoryNV}, + {"wglGetCurrentContext", &retrace::ignore}, + {"wglGetCurrentDC", &retrace::ignore}, + {"wglGetCurrentReadDCARB", &retrace::ignore}, + {"wglGetCurrentReadDCEXT", &retrace::ignore}, + {"wglGetDefaultProcAddress", &retrace::ignore}, + {"wglGetExtensionsStringARB", &retrace::ignore}, + {"wglGetExtensionsStringEXT", &retrace::ignore}, + {"wglGetLayerPaletteEntries", &retrace::ignore}, + {"wglGetPbufferDCARB", &retrace_wglGetPbufferDCARB}, + {"wglGetPixelFormat", &retrace::ignore}, + {"wglGetPixelFormatAttribfvARB", &retrace::ignore}, + {"wglGetPixelFormatAttribfvEXT", &retrace::ignore}, + {"wglGetPixelFormatAttribivARB", &retrace::ignore}, + {"wglGetPixelFormatAttribivEXT", &retrace::ignore}, + {"wglGetProcAddress", &retrace_wglGetProcAddress}, + {"wglGetSwapIntervalEXT", &retrace::ignore}, + {"wglMakeContextCurrentARB", &retrace_wglMakeContextCurrentARB}, + {"wglMakeContextCurrentEXT", &retrace_wglMakeContextCurrentEXT}, + {"wglMakeCurrent", &retrace_wglMakeCurrent}, + {"wglQueryPbufferARB", &retrace_wglQueryPbufferARB}, + {"wglRealizeLayerPalette", &retrace_wglRealizeLayerPalette}, + {"wglReleasePbufferDCARB", &retrace_wglReleasePbufferDCARB}, + {"wglReleaseTexImageARB", &retrace_wglReleaseTexImageARB}, + {"wglRestoreBufferRegionARB", &retrace_wglRestoreBufferRegionARB}, + {"wglSaveBufferRegionARB", &retrace_wglSaveBufferRegionARB}, + {"wglSetLayerPaletteEntries", &retrace_wglSetLayerPaletteEntries}, + {"wglSetPbufferAttribARB", &retrace_wglSetPbufferAttribARB}, + {"wglSetPixelFormat", &retrace_wglSetPixelFormat}, + {"wglShareLists", &retrace_wglShareLists}, + {"wglSwapBuffers", &retrace_wglSwapBuffers}, + {"wglSwapIntervalEXT", &retrace_wglSwapIntervalEXT}, + {"wglSwapLayerBuffers", &retrace_wglSwapLayerBuffers}, + {"wglSwapMultipleBuffers", &retrace_wglSwapMultipleBuffers}, + {"wglUseFontBitmapsA", &retrace_wglUseFontBitmapsA}, + {"wglUseFontBitmapsW", &retrace_wglUseFontBitmapsW}, + {"wglUseFontOutlinesA", &retrace_wglUseFontOutlinesA}, + {"wglUseFontOutlinesW", &retrace_wglUseFontOutlinesW}, + {NULL, NULL} +}; + diff --git a/retrace/glstate.cpp b/retrace/glstate.cpp new file mode 100644 index 0000000..0d5a5f3 --- /dev/null +++ b/retrace/glstate.cpp @@ -0,0 +1,155 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include +#include + +#include "image.hpp" +#include "json.hpp" +#include "glproc.hpp" +#include "glsize.hpp" +#include "glstate.hpp" +#include "glstate_internal.hpp" + + +namespace glstate { + + +Context::Context(void) { + memset(this, 0, sizeof *this); + + const char *version = (const char *)glGetString(GL_VERSION); + if (version) { + if (version[0] == 'O' && + version[1] == 'p' && + version[2] == 'e' && + version[3] == 'n' && + version[4] == 'G' && + version[5] == 'L' && + version[6] == ' ' && + version[7] == 'E' && + version[8] == 'S' && + (version[9] == ' ' || version[9] == '-')) { + ES = true; + } + } + + ARB_draw_buffers = !ES; + + // TODO: Check extensions we use below +} + +void +Context::resetPixelPackState(void) { + if (!ES) { + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_SKIP_IMAGES, 0); + } else { + packAlignment = 4; + glGetIntegerv(GL_PACK_ALIGNMENT, &packAlignment); + } + glPixelStorei(GL_PACK_ALIGNMENT, 1); +} + +void +Context::restorePixelPackState(void) { + if (!ES) { + glPopClientAttrib(); + } else { + glPixelStorei(GL_PACK_ALIGNMENT, packAlignment); + } +} + + +static const GLenum bindings[] = { + GL_DRAW_BUFFER, + GL_READ_BUFFER, + GL_PIXEL_PACK_BUFFER_BINDING, + GL_PIXEL_UNPACK_BUFFER_BINDING, + GL_TEXTURE_BINDING_1D, + GL_TEXTURE_BINDING_2D, + GL_TEXTURE_BINDING_3D, + GL_TEXTURE_BINDING_RECTANGLE, + GL_TEXTURE_BINDING_CUBE_MAP, + GL_DRAW_FRAMEBUFFER_BINDING, + GL_READ_FRAMEBUFFER_BINDING, + GL_RENDERBUFFER_BINDING, + GL_DRAW_BUFFER0, + GL_DRAW_BUFFER1, + GL_DRAW_BUFFER2, + GL_DRAW_BUFFER3, + GL_DRAW_BUFFER4, + GL_DRAW_BUFFER5, + GL_DRAW_BUFFER6, + GL_DRAW_BUFFER7, +}; + + +#define NUM_BINDINGS sizeof(bindings)/sizeof(bindings[0]) + + +void dumpCurrentContext(std::ostream &os) +{ + JSONWriter json(os); + +#ifndef NDEBUG + GLint old_bindings[NUM_BINDINGS]; + for (unsigned i = 0; i < NUM_BINDINGS; ++i) { + old_bindings[i] = 0; + glGetIntegerv(bindings[i], &old_bindings[i]); + } +#endif + + Context context; + + dumpParameters(json, context); + dumpShadersUniforms(json, context); + dumpTextures(json, context); + dumpFramebuffer(json, context); + +#ifndef NDEBUG + for (unsigned i = 0; i < NUM_BINDINGS; ++i) { + GLint new_binding = 0; + glGetIntegerv(bindings[i], &new_binding); + if (new_binding != old_bindings[i]) { + std::cerr << "warning: " << enumToString(bindings[i]) << " was clobbered\n"; + } + } +#endif + +} + + +} /* namespace glstate */ diff --git a/retrace/glstate.hpp b/retrace/glstate.hpp new file mode 100644 index 0000000..6fb615f --- /dev/null +++ b/retrace/glstate.hpp @@ -0,0 +1,54 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + +#ifndef _GLSTATE_HPP_ +#define _GLSTATE_HPP_ + + +#include + +#include "glimports.hpp" + + +namespace image { + class Image; +} + + +namespace glstate { + + +const char *enumToString(GLenum pname); + +void dumpCurrentContext(std::ostream &os); + +image::Image * +getDrawBufferImage(void); + + +} /* namespace glstate */ + + +#endif /* _GLSTATE_HPP_ */ diff --git a/retrace/glstate_images.cpp b/retrace/glstate_images.cpp new file mode 100644 index 0000000..f16cba4 --- /dev/null +++ b/retrace/glstate_images.cpp @@ -0,0 +1,1185 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include +#include + +#include "image.hpp" +#include "json.hpp" +#include "glproc.hpp" +#include "glsize.hpp" +#include "glstate.hpp" +#include "glstate_internal.hpp" + + +#ifdef __linux__ +#include +#endif + +#ifdef __APPLE__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPLE__ */ + + +/* Change thi to one to force interpreting depth buffers as RGBA, which enables + * visualizing full dynamic range, until we can transmit HDR images to the GUI */ +#define DEPTH_AS_RGBA 0 + + +namespace glstate { + + +struct ImageDesc +{ + GLint width; + GLint height; + GLint depth; + GLint internalFormat; + + inline + ImageDesc() : + width(0), + height(0), + depth(0), + internalFormat(GL_NONE) + {} + + inline bool + valid(void) const { + return width > 0 && height > 0 && depth > 0; + } +}; + + +/** + * 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) { + return getActiveTextureLevelDescOES(context, target, level, desc); + } + + glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat); + + desc.width = 0; + glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &desc.width); + + if (target == GL_TEXTURE_1D) { + desc.height = 1; + desc.depth = 1; + } else { + desc.height = 0; + glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &desc.height); + if (target != GL_TEXTURE_3D) { + desc.depth = 1; + } else { + desc.depth = 0; + glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &desc.depth); + } + } + + return desc.valid(); +} + + +/** + * 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 GLboolean +isDepthFormat(GLenum internalFormat) +{ + switch (internalFormat) { + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + case GL_DEPTH_COMPONENT32F: + case GL_DEPTH_COMPONENT32F_NV: + case GL_DEPTH_STENCIL: + case GL_DEPTH24_STENCIL8: + case GL_DEPTH32F_STENCIL8: + case GL_DEPTH32F_STENCIL8_NV: + return GL_TRUE; + } + return GL_FALSE; +} + + +static inline void +dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level) +{ + ImageDesc desc; + if (!getActiveTextureLevelDesc(context, target, level, desc)) { + return; + } + + char label[512]; + + GLint active_texture = GL_TEXTURE0; + glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture); + snprintf(label, sizeof label, "%s, %s, level = %d", + enumToString(active_texture), enumToString(target), level); + + json.beginMember(label); + + json.beginObject(); + + GLuint channels; + GLenum format; + if (!context.ES && isDepthFormat(desc.internalFormat)) { + format = GL_DEPTH_COMPONENT; + channels = 1; + } else { + format = GL_RGBA; + channels = 4; + } + + // Tell the GUI this is no ordinary object, but an image + json.writeStringMember("__class__", "image"); + + json.writeNumberMember("__width__", desc.width); + json.writeNumberMember("__height__", desc.height); + json.writeNumberMember("__depth__", desc.depth); + + json.writeStringMember("__format__", enumToString(desc.internalFormat)); + + // Hardcoded for now, but we could chose types more adequate to the + // texture internal format + json.writeStringMember("__type__", "uint8"); + json.writeBoolMember("__normalized__", true); + json.writeNumberMember("__channels__", channels); + + GLubyte *pixels = new GLubyte[desc.depth*desc.width*desc.height*channels]; + + context.resetPixelPackState(); + + if (context.ES) { + getTexImageOES(target, level, desc, pixels); + } else { + glGetTexImage(target, level, format, GL_UNSIGNED_BYTE, pixels); + } + + context.restorePixelPackState(); + + json.beginMember("__data__"); + char *pngBuffer; + int pngBufferSize; + image::writePixelsToBuffer(pixels, desc.width, desc.height, channels, true, &pngBuffer, &pngBufferSize); + json.writeBase64(pngBuffer, pngBufferSize); + free(pngBuffer); + json.endMember(); // __data__ + + delete [] pixels; + json.endObject(); +} + + +static inline void +dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding) +{ + GLint texture_binding = 0; + glGetIntegerv(binding, &texture_binding); + if (!glIsEnabled(target) && !texture_binding) { + return; + } + + GLint level = 0; + do { + ImageDesc desc; + if (!getActiveTextureLevelDesc(context, target, level, desc)) { + break; + } + + if (target == GL_TEXTURE_CUBE_MAP) { + for (int face = 0; face < 6; ++face) { + dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level); + } + } else { + dumpActiveTextureLevel(json, context, target, level); + } + + ++level; + } while(true); +} + + +void +dumpTextures(JSONWriter &json, Context &context) +{ + json.beginMember("textures"); + json.beginObject(); + GLint active_texture = GL_TEXTURE0; + glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture); + + GLint max_texture_coords = 0; + glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords); + GLint max_combined_texture_image_units = 0; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units); + GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords); + + /* + * At least the Android software GL implementation doesn't return the + * proper value for this, but rather returns 0. The GL(ES) specification + * mandates a minimum value of 2, so use this as a fall-back value. + */ + max_units = std::min(max_units, 2); + + for (GLint unit = 0; unit < max_units; ++unit) { + GLenum texture = GL_TEXTURE0 + unit; + glActiveTexture(texture); + dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D); + dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D); + dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D); + dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE); + dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP); + } + glActiveTexture(active_texture); + json.endObject(); + json.endMember(); // textures +} + + +static bool +getDrawableBounds(GLint *width, GLint *height) { +#if defined(__linux__) + if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) { + EGLContext currentContext = eglGetCurrentContext(); + if (currentContext == EGL_NO_CONTEXT) { + return false; + } + + EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW); + if (currentSurface == EGL_NO_SURFACE) { + 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; + } + + return true; + } +#endif + +#if defined(_WIN32) + + HDC hDC = wglGetCurrentDC(); + if (!hDC) { + return false; + } + + HWND hWnd = WindowFromDC(hDC); + RECT rect; + + if (!GetClientRect(hWnd, &rect)) { + return false; + } + + *width = rect.right - rect.left; + *height = rect.bottom - rect.top; + return true; + +#elif defined(__APPLE__) + + CGLContextObj ctx = CGLGetCurrentContext(); + if (ctx == NULL) { + return false; + } + + CGSConnectionID cid; + CGSWindowID wid; + CGSSurfaceID sid; + + if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) { + return false; + } + + CGRect rect; + + if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) { + return false; + } + + *width = rect.size.width; + *height = rect.size.height; + return true; + +#elif defined(HAVE_X11) + + Display *display; + Drawable drawable; + Window root; + int x, y; + unsigned int w, h, bw, depth; + + display = glXGetCurrentDisplay(); + if (!display) { + return false; + } + + drawable = glXGetCurrentDrawable(); + if (drawable == None) { + return false; + } + + if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) { + return false; + } + + *width = w; + *height = h; + return true; + +#else + + return false; + +#endif +} + + +static const GLenum texture_bindings[][2] = { + {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D}, + {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D}, + {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D}, + {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE}, + {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP} +}; + + +static bool +bindTexture(GLint texture, GLenum &target, GLint &bound_texture) +{ + + for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) { + target = texture_bindings[i][0]; + + GLenum binding = texture_bindings[i][1]; + + while (glGetError() != GL_NO_ERROR) + ; + + glGetIntegerv(binding, &bound_texture); + glBindTexture(target, texture); + + if (glGetError() == GL_NO_ERROR) { + return true; + } + + glBindTexture(target, bound_texture); + } + + target = GL_NONE; + + return false; +} + + +static bool +getTextureLevelDesc(Context &context, GLint texture, GLint level, ImageDesc &desc) +{ + GLenum target; + GLint bound_texture = 0; + if (!bindTexture(texture, target, bound_texture)) { + return false; + } + + getActiveTextureLevelDesc(context, target, level, desc); + + glBindTexture(target, bound_texture); + + return desc.valid(); +} + + +static bool +getBoundRenderbufferDesc(Context &context, ImageDesc &desc) +{ + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height); + desc.depth = 1; + + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat); + + return desc.valid(); +} + + +static bool +getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc) +{ + GLint bound_renderbuffer = 0; + glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + + getBoundRenderbufferDesc(context, desc); + + glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer); + + return desc.valid(); +} + + +static bool +getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc) +{ + GLint object_type = GL_NONE; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + &object_type); + if (object_type == GL_NONE) { + return false; + } + + GLint object_name = 0; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &object_name); + if (object_name == 0) { + return false; + } + + if (object_type == GL_RENDERBUFFER) { + return getRenderbufferDesc(context, object_name, desc); + } else if (object_type == GL_TEXTURE) { + GLint texture_level = 0; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, + &texture_level); + return getTextureLevelDesc(context, object_name, texture_level, desc); + } else { + std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n"; + return false; + } +} + + + +image::Image * +getDrawBufferImage() { + GLenum format = GL_RGB; + GLint channels = __gl_format_channels(format); + if (channels > 4) { + return NULL; + } + + Context context; + + GLenum framebuffer_binding; + GLenum framebuffer_target; + if (context.ES) { + framebuffer_binding = GL_FRAMEBUFFER_BINDING; + framebuffer_target = GL_FRAMEBUFFER; + } else { + framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING; + framebuffer_target = GL_DRAW_FRAMEBUFFER; + } + + GLint draw_framebuffer = 0; + glGetIntegerv(framebuffer_binding, &draw_framebuffer); + + GLint draw_buffer = GL_NONE; + ImageDesc desc; + if (draw_framebuffer) { + if (context.ARB_draw_buffers) { + glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer); + if (draw_buffer == GL_NONE) { + return NULL; + } + } + + if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) { + return NULL; + } + } else { + if (!context.ES) { + glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); + if (draw_buffer == GL_NONE) { + return NULL; + } + } + + if (!getDrawableBounds(&desc.width, &desc.height)) { + return NULL; + } + + desc.depth = 1; + } + + GLenum type = GL_UNSIGNED_BYTE; + +#if DEPTH_AS_RGBA + if (format == GL_DEPTH_COMPONENT) { + type = GL_UNSIGNED_INT; + channels = 4; + } +#endif + + image::Image *image = new image::Image(desc.width, desc.height, channels, true); + if (!image) { + return NULL; + } + + while (glGetError() != GL_NO_ERROR) {} + + GLint read_framebuffer = 0; + GLint read_buffer = GL_NONE; + if (!context.ES) { + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer); + + glGetIntegerv(GL_READ_BUFFER, &read_buffer); + glReadBuffer(draw_buffer); + } + + // TODO: reset imaging state too + context.resetPixelPackState(); + + glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels); + + context.restorePixelPackState(); + + if (!context.ES) { + glReadBuffer(read_buffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer); + } + + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + do { + std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n"; + error = glGetError(); + } while(error != GL_NO_ERROR); + delete image; + return NULL; + } + + return image; +} + + +/** + * Dump the image of the currently bound read buffer. + */ +static inline void +dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format, + GLint internalFormat = GL_NONE) +{ + GLint channels = __gl_format_channels(format); + + Context context; + + json.beginObject(); + + // Tell the GUI this is no ordinary object, but an image + json.writeStringMember("__class__", "image"); + + json.writeNumberMember("__width__", width); + json.writeNumberMember("__height__", height); + json.writeNumberMember("__depth__", 1); + + json.writeStringMember("__format__", enumToString(internalFormat)); + + // Hardcoded for now, but we could chose types more adequate to the + // texture internal format + json.writeStringMember("__type__", "uint8"); + json.writeBoolMember("__normalized__", true); + json.writeNumberMember("__channels__", channels); + + GLenum type = GL_UNSIGNED_BYTE; + +#if DEPTH_AS_RGBA + if (format == GL_DEPTH_COMPONENT) { + type = GL_UNSIGNED_INT; + channels = 4; + } +#endif + + GLubyte *pixels = new GLubyte[width*height*channels]; + + // TODO: reset imaging state too + context.resetPixelPackState(); + + glReadPixels(0, 0, width, height, format, type, pixels); + + context.restorePixelPackState(); + + json.beginMember("__data__"); + char *pngBuffer; + int pngBufferSize; + image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize); + //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels) + // <<", after = "<= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) { + attachment = draw_buffer; + } else { + std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n"; + attachment = GL_COLOR_ATTACHMENT0; + } + GLint alpha_size = 0; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + &alpha_size); + GLenum format = alpha_size ? GL_RGBA : GL_RGB; + dumpFramebufferAttachment(json, context, target, attachment, format); + } + } + + glReadBuffer(read_buffer); + + if (!context.ES) { + dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT); + dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer); +} + + +void +dumpFramebuffer(JSONWriter &json, Context &context) +{ + json.beginMember("framebuffer"); + json.beginObject(); + + GLint boundDrawFbo = 0, boundReadFbo = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo); + if (!boundDrawFbo) { + dumpDrawableImages(json, context); + } else if (context.ES) { + dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER); + } else { + GLint colorRb = 0, stencilRb = 0, depthRb = 0; + GLint draw_buffer0 = GL_NONE; + glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0); + bool multisample = false; + + GLint boundRb = 0; + glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb); + + GLint object_type; + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); + if (object_type == GL_RENDERBUFFER) { + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb); + glBindRenderbuffer(GL_RENDERBUFFER, colorRb); + GLint samples = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + if (samples) { + multisample = true; + } + } + + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); + if (object_type == GL_RENDERBUFFER) { + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb); + glBindRenderbuffer(GL_RENDERBUFFER, depthRb); + GLint samples = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + if (samples) { + multisample = true; + } + } + + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); + if (object_type == GL_RENDERBUFFER) { + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb); + glBindRenderbuffer(GL_RENDERBUFFER, stencilRb); + GLint samples = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + if (samples) { + multisample = true; + } + } + + glBindRenderbuffer(GL_RENDERBUFFER, boundRb); + + GLuint rbs[3]; + GLint numRbs = 0; + GLuint fboCopy = 0; + + if (multisample) { + // glReadPixels doesnt support multisampled buffers so we need + // to blit the fbo to a temporary one + fboCopy = downsampledFramebuffer(context, + boundDrawFbo, draw_buffer0, + colorRb, depthRb, stencilRb, + rbs, &numRbs); + } + + dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER); + + if (multisample) { + glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb); + glDeleteRenderbuffers(numRbs, rbs); + glDeleteFramebuffers(1, &fboCopy); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo); + } + + json.endObject(); + json.endMember(); // framebuffer +} + + +} /* namespace glstate */ diff --git a/retrace/glstate_internal.hpp b/retrace/glstate_internal.hpp new file mode 100644 index 0000000..aab7f98 --- /dev/null +++ b/retrace/glstate_internal.hpp @@ -0,0 +1,71 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + +#ifndef _GLSTATE_INTERNAL_HPP_ +#define _GLSTATE_INTERNAL_HPP_ + + +#include "glimports.hpp" + + +class JSONWriter; + + +namespace glstate { + + +struct Context +{ + bool ES; + + bool ARB_draw_buffers; + + Context(void); + + GLint packAlignment; + + void + resetPixelPackState(void); + + void + restorePixelPackState(void); +}; + + +void dumpEnum(JSONWriter &json, GLenum pname); + +void dumpParameters(JSONWriter &json, Context &context); + +void dumpShadersUniforms(JSONWriter &json, Context &context); + +void dumpTextures(JSONWriter &json, Context &context); + +void dumpFramebuffer(JSONWriter &json, Context &context); + + +} /* namespace glstate */ + + +#endif /* _GLSTATE_INTERNAL_HPP_ */ diff --git a/retrace/glstate_params.py b/retrace/glstate_params.py new file mode 100644 index 0000000..e1f90d0 --- /dev/null +++ b/retrace/glstate_params.py @@ -0,0 +1,505 @@ +########################################################################## +# +# Copyright 2011 Jose Fonseca +# 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. +# +##########################################################################/ + + +'''Generate code to dump most GL state into JSON.''' + + +import retrace # to adjust sys.path + +from specs.stdapi import * + +from specs.gltypes import * +from specs.glparams import * + + +texture_targets = [ + ('GL_TEXTURE_1D', 'GL_TEXTURE_BINDING_1D'), + ('GL_TEXTURE_2D', 'GL_TEXTURE_BINDING_2D'), + ('GL_TEXTURE_3D', 'GL_TEXTURE_BINDING_3D'), + ('GL_TEXTURE_RECTANGLE', 'GL_TEXTURE_BINDING_RECTANGLE'), + ('GL_TEXTURE_CUBE_MAP', 'GL_TEXTURE_BINDING_CUBE_MAP') +] + +framebuffer_targets = [ + ('GL_DRAW_FRAMEBUFFER', 'GL_DRAW_FRAMEBUFFER_BINDING'), + ('GL_READ_FRAMEBUFFER', 'GL_READ_FRAMEBUFFER_BINDING'), +] + +class GetInflector: + '''Objects that describes how to inflect.''' + + reduced_types = { + B: I, + E: I, + I: F, + } + + def __init__(self, radical, inflections, suffix = ''): + self.radical = radical + self.inflections = inflections + self.suffix = suffix + + def reduced_type(self, type): + if type in self.inflections: + return type + if type in self.reduced_types: + return self.reduced_type(self.reduced_types[type]) + raise NotImplementedError + + def inflect(self, type): + return self.radical + self.inflection(type) + self.suffix + + def inflection(self, type): + type = self.reduced_type(type) + assert type in self.inflections + return self.inflections[type] + + def __str__(self): + return self.radical + self.suffix + + +class StateGetter(Visitor): + '''Type visitor that is able to extract the state via one of the glGet* + functions. + + It will declare any temporary variable + ''' + + def __init__(self, radical, inflections, suffix=''): + self.inflector = GetInflector(radical, inflections) + self.suffix = suffix + + def iter(self): + for function, type, count, name in parameters: + inflection = self.inflector.radical + self.suffix + if inflection not in function.split(','): + continue + if type is X: + continue + yield type, count, name + + def __call__(self, *args): + pname = args[-1] + + for type, count, name in self.iter(): + if name == pname: + if count != 1: + type = Array(type, str(count)) + + return type, self.visit(type, args) + + raise NotImplementedError + + def temp_name(self, args): + '''Return the name of a temporary variable to hold the state.''' + pname = args[-1] + + return pname[3:].lower() + + def visitConst(self, const, args): + return self.visit(const.type, args) + + def visitScalar(self, type, args): + temp_name = self.temp_name(args) + elem_type = self.inflector.reduced_type(type) + inflection = self.inflector.inflect(type) + if inflection.endswith('v'): + print ' %s %s = 0;' % (elem_type, temp_name) + print ' %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name) + else: + print ' %s %s = %s(%s);' % (elem_type, temp_name, inflection + self.suffix, ', '.join(args)) + return temp_name + + def visitString(self, string, args): + temp_name = self.temp_name(args) + inflection = self.inflector.inflect(string) + assert not inflection.endswith('v') + print ' %s %s = (%s)%s(%s);' % (string, temp_name, string, inflection + self.suffix, ', '.join(args)) + return temp_name + + def visitAlias(self, alias, args): + return self.visitScalar(alias, args) + + def visitEnum(self, enum, args): + return self.visit(GLint, args) + + def visitBitmask(self, bitmask, args): + return self.visit(GLint, args) + + def visitArray(self, array, args): + temp_name = self.temp_name(args) + if array.length == '1': + return self.visit(array.type) + elem_type = self.inflector.reduced_type(array.type) + inflection = self.inflector.inflect(array.type) + assert inflection.endswith('v') + print ' %s %s[%s + 1];' % (elem_type, temp_name, array.length) + print ' memset(%s, 0, %s * sizeof *%s);' % (temp_name, array.length, temp_name) + print ' %s[%s] = (%s)0xdeadc0de;' % (temp_name, array.length, elem_type) + print ' %s(%s, %s);' % (inflection + self.suffix, ', '.join(args), temp_name) + # Simple buffer overflow detection + print ' assert(%s[%s] == (%s)0xdeadc0de);' % (temp_name, array.length, elem_type) + return temp_name + + def visitOpaque(self, pointer, args): + temp_name = self.temp_name(args) + inflection = self.inflector.inflect(pointer) + assert inflection.endswith('v') + print ' GLvoid *%s;' % temp_name + print ' %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name) + return temp_name + + +glGet = StateGetter('glGet', { + B: 'Booleanv', + I: 'Integerv', + F: 'Floatv', + D: 'Doublev', + S: 'String', + P: 'Pointerv', +}) + +glGetMaterial = StateGetter('glGetMaterial', {I: 'iv', F: 'fv'}) +glGetLight = StateGetter('glGetLight', {I: 'iv', F: 'fv'}) +glGetVertexAttrib = StateGetter('glGetVertexAttrib', {I: 'iv', F: 'fv', D: 'dv', P: 'Pointerv'}) +glGetTexParameter = StateGetter('glGetTexParameter', {I: 'iv', F: 'fv'}) +glGetTexEnv = StateGetter('glGetTexEnv', {I: 'iv', F: 'fv'}) +glGetTexLevelParameter = StateGetter('glGetTexLevelParameter', {I: 'iv', F: 'fv'}) +glGetShader = StateGetter('glGetShaderiv', {I: 'iv'}) +glGetProgram = StateGetter('glGetProgram', {I: 'iv'}) +glGetProgramARB = StateGetter('glGetProgram', {I: 'iv', F: 'fv', S: 'Stringv'}, 'ARB') +glGetFramebufferAttachmentParameter = StateGetter('glGetFramebufferAttachmentParameter', {I: 'iv'}) + + +class JsonWriter(Visitor): + '''Type visitor that will dump a value of the specified type through the + JSON writer. + + It expects a previously declared JSONWriter instance named "json".''' + + def visitLiteral(self, literal, instance): + if literal.kind == 'Bool': + print ' json.writeBool(%s);' % instance + elif literal.kind in ('SInt', 'Uint', 'Float', 'Double'): + print ' json.writeNumber(%s);' % instance + else: + raise NotImplementedError + + def visitString(self, string, instance): + assert string.length is None + print ' json.writeString((const char *)%s);' % instance + + def visitEnum(self, enum, instance): + if enum.expr == 'GLenum': + print ' dumpEnum(json, %s);' % instance + else: + print ' json.writeNumber(%s);' % instance + + def visitBitmask(self, bitmask, instance): + raise NotImplementedError + + def visitAlias(self, alias, instance): + self.visit(alias.type, instance) + + def visitOpaque(self, opaque, instance): + print ' json.writeNumber((size_t)%s);' % instance + + __index = 0 + + def visitArray(self, array, instance): + index = '__i%u' % JsonWriter.__index + JsonWriter.__index += 1 + print ' json.beginArray();' + print ' for (unsigned %s = 0; %s < %s; ++%s) {' % (index, index, array.length, index) + self.visit(array.type, '%s[%s]' % (instance, index)) + print ' }' + print ' json.endArray();' + + + +class StateDumper: + '''Class to generate code to dump all GL state in JSON format via + stdout.''' + + def __init__(self): + pass + + def dump(self): + print '#include ' + print + print '#include "json.hpp"' + print '#include "glproc.hpp"' + print '#include "glsize.hpp"' + print '#include "glstate.hpp"' + print '#include "glstate_internal.hpp"' + print + print 'namespace glstate {' + print + + print 'const char *' + print 'enumToString(GLenum pname)' + print '{' + print ' switch (pname) {' + for name in GLenum.values: + print ' case %s:' % name + print ' return "%s";' % name + print ' default:' + print ' return NULL;' + print ' }' + print '}' + print + + print 'static void' + print 'dumpFramebufferAttachementParameters(JSONWriter &json, GLenum target, GLenum attachment)' + print '{' + self.dump_attachment_parameters('target', 'attachment') + print '}' + print + + print 'void' + print 'dumpEnum(JSONWriter &json, GLenum pname)' + print '{' + print ' const char *s = enumToString(pname);' + print ' if (s) {' + print ' json.writeString(s);' + print ' } else {' + print ' json.writeNumber(pname);' + print ' }' + print '}' + print + + print 'void dumpParameters(JSONWriter &json, Context &context)' + print '{' + print ' json.beginMember("parameters");' + print ' json.beginObject();' + + self.dump_atoms(glGet) + + self.dump_material_params() + self.dump_light_params() + self.dump_vertex_attribs() + self.dump_program_params() + self.dump_texture_parameters() + self.dump_framebuffer_parameters() + + print ' json.endObject();' + print ' json.endMember(); // parameters' + print '}' + print + + print '} /*namespace glstate */' + + def dump_material_params(self): + print ' if (!context.ES) {' + for face in ['GL_FRONT', 'GL_BACK']: + print ' json.beginMember("%s");' % face + print ' json.beginObject();' + self.dump_atoms(glGetMaterial, face) + print ' json.endObject();' + print ' }' + print + + def dump_light_params(self): + print ' GLint max_lights = 0;' + print ' __glGetIntegerv(GL_MAX_LIGHTS, &max_lights);' + print ' for (GLint index = 0; index < max_lights; ++index) {' + print ' GLenum light = GL_LIGHT0 + index;' + print ' if (glIsEnabled(light)) {' + print ' char name[32];' + print ' snprintf(name, sizeof name, "GL_LIGHT%i", index);' + print ' json.beginMember(name);' + print ' json.beginObject();' + self.dump_atoms(glGetLight, ' GL_LIGHT0 + index') + print ' json.endObject();' + print ' json.endMember(); // GL_LIGHTi' + print ' }' + print ' }' + print + + def texenv_param_target(self, name): + if name == 'GL_TEXTURE_LOD_BIAS': + return 'GL_TEXTURE_FILTER_CONTROL' + elif name == 'GL_COORD_REPLACE': + return 'GL_POINT_SPRITE' + else: + return 'GL_TEXTURE_ENV' + + def dump_texenv_params(self): + for target in ['GL_TEXTURE_ENV', 'GL_TEXTURE_FILTER_CONTROL', 'GL_POINT_SPRITE']: + print ' if (!context.ES) {' + print ' json.beginMember("%s");' % target + print ' json.beginObject();' + for _, _, name in glGetTexEnv.iter(): + if self.texenv_param_target(name) == target: + self.dump_atom(glGetTexEnv, target, name) + print ' json.endObject();' + print ' }' + + def dump_vertex_attribs(self): + print ' GLint max_vertex_attribs = 0;' + print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs);' + print ' for (GLint index = 0; index < max_vertex_attribs; ++index) {' + print ' char name[32];' + print ' snprintf(name, sizeof name, "GL_VERTEX_ATTRIB_ARRAY%i", index);' + print ' json.beginMember(name);' + print ' json.beginObject();' + self.dump_atoms(glGetVertexAttrib, 'index') + print ' json.endObject();' + print ' json.endMember(); // GL_VERTEX_ATTRIB_ARRAYi' + print ' }' + print + + program_targets = [ + 'GL_FRAGMENT_PROGRAM_ARB', + 'GL_VERTEX_PROGRAM_ARB', + ] + + def dump_program_params(self): + for target in self.program_targets: + print ' if (glIsEnabled(%s)) {' % target + print ' json.beginMember("%s");' % target + print ' json.beginObject();' + self.dump_atoms(glGetProgramARB, target) + print ' json.endObject();' + print ' }' + + def dump_texture_parameters(self): + print ' {' + print ' GLint active_texture = GL_TEXTURE0;' + print ' glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);' + print ' GLint max_texture_coords = 0;' + print ' glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);' + print ' GLint max_combined_texture_image_units = 0;' + print ' glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);' + print ' GLint max_units = std::min(std::max(max_combined_texture_image_units, max_texture_coords), 2);' + print ' for (GLint unit = 0; unit < max_units; ++unit) {' + print ' char name[32];' + print ' snprintf(name, sizeof name, "GL_TEXTURE%i", unit);' + print ' json.beginMember(name);' + print ' glActiveTexture(GL_TEXTURE0 + unit);' + print ' json.beginObject();' + print ' GLboolean enabled;' + print ' GLint binding;' + print + for target, binding in texture_targets: + print ' // %s' % target + print ' enabled = GL_FALSE;' + print ' glGetBooleanv(%s, &enabled);' % target + print ' json.writeBoolMember("%s", enabled);' % target + print ' binding = 0;' + print ' glGetIntegerv(%s, &binding);' % binding + print ' json.writeNumberMember("%s", binding);' % binding + print ' if (enabled || binding) {' + print ' json.beginMember("%s");' % target + print ' json.beginObject();' + self.dump_atoms(glGetTexParameter, target) + print ' if (!context.ES) {' + # We only dump the first level parameters + self.dump_atoms(glGetTexLevelParameter, target, "0") + print ' }' + print ' json.endObject();' + print ' json.endMember(); // %s' % target + print ' }' + print + print ' if (unit < max_texture_coords) {' + self.dump_texenv_params() + print ' }' + print ' json.endObject();' + print ' json.endMember(); // GL_TEXTUREi' + print ' }' + print ' glActiveTexture(active_texture);' + print ' }' + print + + def dump_framebuffer_parameters(self): + print ' {' + print ' GLint max_color_attachments = 0;' + print ' glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);' + print ' GLint framebuffer;' + for target, binding in framebuffer_targets: + print ' // %s' % target + print ' framebuffer = 0;' + print ' glGetIntegerv(%s, &framebuffer);' % binding + print ' if (framebuffer) {' + print ' json.beginMember("%s");' % target + print ' json.beginObject();' + print ' for (GLint i = 0; i < max_color_attachments; ++i) {' + print ' GLint color_attachment = GL_COLOR_ATTACHMENT0 + i;' + print ' dumpFramebufferAttachementParameters(json, %s, color_attachment);' % target + print ' }' + print ' dumpFramebufferAttachementParameters(json, %s, GL_DEPTH_ATTACHMENT);' % target + print ' dumpFramebufferAttachementParameters(json, %s, GL_STENCIL_ATTACHMENT);' % target + print ' json.endObject();' + print ' json.endMember(); // %s' % target + print ' }' + print + print ' }' + print + + def dump_attachment_parameters(self, target, attachment): + print ' {' + print ' GLint object_type = GL_NONE;' + print ' glGetFramebufferAttachmentParameteriv(%s, %s, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);' % (target, attachment) + print ' if (object_type != GL_NONE) {' + print ' json.beginMember(enumToString(%s));' % attachment + print ' json.beginObject();' + self.dump_atoms(glGetFramebufferAttachmentParameter, target, attachment) + print ' json.endObject();' + print ' json.endMember(); // GL_x_ATTACHMENT' + print ' }' + print ' }' + + def dump_atoms(self, getter, *args): + for _, _, name in getter.iter(): + self.dump_atom(getter, *(args + (name,))) + + def dump_atom(self, getter, *args): + name = args[-1] + + # Avoid crash on MacOSX + # XXX: The right fix would be to look at the support extensions.. + import platform + if name == 'GL_SAMPLER_BINDING' and platform.system() == 'Darwin': + return + + print ' // %s' % name + print ' {' + #print ' assert(glGetError() == GL_NO_ERROR);' + type, value = getter(*args) + print ' if (glGetError() != GL_NO_ERROR) {' + #print ' std::cerr << "warning: %s(%s) failed\\n";' % (inflection, name) + print ' while (glGetError() != GL_NO_ERROR) {}' + print ' } else {' + print ' json.beginMember("%s");' % name + JsonWriter().visit(type, value) + print ' json.endMember();' + print ' }' + print ' }' + print + + +if __name__ == '__main__': + StateDumper().dump() diff --git a/retrace/glstate_shaders.cpp b/retrace/glstate_shaders.cpp new file mode 100644 index 0000000..11286fb --- /dev/null +++ b/retrace/glstate_shaders.cpp @@ -0,0 +1,532 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include +#include +#include +#include + +#include "json.hpp" +#include "glproc.hpp" +#include "glsize.hpp" +#include "glstate.hpp" +#include "glstate_internal.hpp" + + +namespace glstate { + + +// Mapping from shader type to shader source, used to accumulated the sources +// of different shaders with same type. +typedef std::map ShaderMap; + + +static void +getShaderSource(ShaderMap &shaderMap, GLuint shader) +{ + if (!shader) { + return; + } + + GLint shader_type = 0; + glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type); + if (!shader_type) { + return; + } + + GLint source_length = 0; + glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length); + if (!source_length) { + return; + } + + GLchar *source = new GLchar[source_length]; + GLsizei length = 0; + source[0] = 0; + glGetShaderSource(shader, source_length, &length, source); + + shaderMap[enumToString(shader_type)] += source; + + delete [] source; +} + + +static void +getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj) +{ + if (!shaderObj) { + return; + } + + GLint object_type = 0; + glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &object_type); + if (object_type != GL_SHADER_OBJECT_ARB) { + return; + } + + GLint shader_type = 0; + glGetObjectParameterivARB(shaderObj, GL_OBJECT_SUBTYPE_ARB, &shader_type); + if (!shader_type) { + return; + } + + GLint source_length = 0; + glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length); + if (!source_length) { + return; + } + + GLcharARB *source = new GLcharARB[source_length]; + GLsizei length = 0; + source[0] = 0; + glGetShaderSource(shaderObj, source_length, &length, source); + + shaderMap[enumToString(shader_type)] += source; + + delete [] source; +} + + +static inline void +dumpProgram(JSONWriter &json, GLint program) +{ + GLint attached_shaders = 0; + glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders); + if (!attached_shaders) { + return; + } + + ShaderMap shaderMap; + + GLuint *shaders = new GLuint[attached_shaders]; + GLsizei count = 0; + glGetAttachedShaders(program, attached_shaders, &count, shaders); + std::sort(shaders, shaders + count); + for (GLsizei i = 0; i < count; ++ i) { + getShaderSource(shaderMap, shaders[i]); + } + delete [] shaders; + + for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) { + json.beginMember(it->first); + json.writeString(it->second); + json.endMember(); + } +} + + +static inline void +dumpProgramObj(JSONWriter &json, GLhandleARB programObj) +{ + GLint attached_shaders = 0; + glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders); + if (!attached_shaders) { + return; + } + + ShaderMap shaderMap; + + GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders]; + GLsizei count = 0; + glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs); + std::sort(shaderObjs, shaderObjs + count); + for (GLsizei i = 0; i < count; ++ i) { + getShaderObjSource(shaderMap, shaderObjs[i]); + } + delete [] shaderObjs; + + for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) { + json.beginMember(it->first); + json.writeString(it->second); + json.endMember(); + } +} + +/* + * When fetching the uniform name of an array we usually get name[0] + * so we need to cut the trailing "[0]" in order to properly construct + * array names later. Otherwise we endup with stuff like + * uniformArray[0][0], + * uniformArray[0][1], + * instead of + * uniformArray[0], + * uniformArray[1]. + */ +static std::string +resolveUniformName(const GLchar *name, GLint size) +{ + std::string qualifiedName(name); + if (size > 1) { + std::string::size_type nameLength = qualifiedName.length(); + static const char * const arrayStart = "[0]"; + static const int arrayStartLen = 3; + if (qualifiedName.rfind(arrayStart) == (nameLength - arrayStartLen)) { + qualifiedName = qualifiedName.substr(0, nameLength - 3); + } + } + return qualifiedName; +} + +static void +dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) { + GLenum elemType; + GLint numElems; + __gl_uniform_size(type, elemType, numElems); + if (elemType == GL_NONE) { + return; + } + + GLfloat fvalues[4*4]; + GLdouble dvalues[4*4]; + GLint ivalues[4*4]; + GLuint uivalues[4*4]; + + GLint i, j; + + std::string qualifiedName = resolveUniformName(name, size); + + for (i = 0; i < size; ++i) { + std::stringstream ss; + ss << qualifiedName; + + if (size > 1) { + ss << '[' << i << ']'; + } + + std::string elemName = ss.str(); + + json.beginMember(elemName); + + GLint location = glGetUniformLocation(program, elemName.c_str()); + + if (numElems > 1) { + json.beginArray(); + } + + switch (elemType) { + case GL_FLOAT: + glGetUniformfv(program, location, fvalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(fvalues[j]); + } + break; + case GL_DOUBLE: + glGetUniformdv(program, location, dvalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(dvalues[j]); + } + break; + case GL_INT: + glGetUniformiv(program, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(ivalues[j]); + } + break; + case GL_UNSIGNED_INT: + glGetUniformuiv(program, location, uivalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(uivalues[j]); + } + break; + case GL_BOOL: + glGetUniformiv(program, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeBool(ivalues[j]); + } + break; + default: + assert(0); + break; + } + + if (numElems > 1) { + json.endArray(); + } + + json.endMember(); + } +} + + +static void +dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) { + + GLenum elemType; + GLint numElems; + __gl_uniform_size(type, elemType, numElems); + if (elemType == GL_NONE) { + return; + } + + GLfloat fvalues[4*4]; + GLint ivalues[4*4]; + + GLint i, j; + + std::string qualifiedName = resolveUniformName(name, size); + + for (i = 0; i < size; ++i) { + std::stringstream ss; + ss << qualifiedName; + + if (size > 1) { + ss << '[' << i << ']'; + } + + std::string elemName = ss.str(); + + json.beginMember(elemName); + + GLint location = glGetUniformLocationARB(programObj, elemName.c_str()); + + if (numElems > 1) { + json.beginArray(); + } + + switch (elemType) { + case GL_DOUBLE: + // glGetUniformdvARB does not exists + case GL_FLOAT: + glGetUniformfvARB(programObj, location, fvalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(fvalues[j]); + } + break; + case GL_UNSIGNED_INT: + // glGetUniformuivARB does not exists + case GL_INT: + glGetUniformivARB(programObj, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(ivalues[j]); + } + break; + case GL_BOOL: + glGetUniformivARB(programObj, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeBool(ivalues[j]); + } + break; + default: + assert(0); + break; + } + + if (numElems > 1) { + json.endArray(); + } + + json.endMember(); + } +} + + +static inline void +dumpProgramUniforms(JSONWriter &json, GLint program) +{ + GLint active_uniforms = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms); + if (!active_uniforms) { + return; + } + + GLint active_uniform_max_length = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length); + GLchar *name = new GLchar[active_uniform_max_length]; + if (!name) { + return; + } + + for (GLint index = 0; index < active_uniforms; ++index) { + GLsizei length = 0; + GLint size = 0; + GLenum type = GL_NONE; + glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name); + + dumpUniform(json, program, size, type, name); + } + + delete [] name; +} + + +static inline void +dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj) +{ + GLint active_uniforms = 0; + glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms); + if (!active_uniforms) { + return; + } + + GLint active_uniform_max_length = 0; + glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length); + GLchar *name = new GLchar[active_uniform_max_length]; + if (!name) { + return; + } + + for (GLint index = 0; index < active_uniforms; ++index) { + GLsizei length = 0; + GLint size = 0; + GLenum type = GL_NONE; + glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name); + + dumpUniformARB(json, programObj, size, type, name); + } + + delete [] name; +} + + +static inline void +dumpArbProgram(JSONWriter &json, GLenum target) +{ + if (!glIsEnabled(target)) { + return; + } + + GLint program_length = 0; + glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length); + if (!program_length) { + return; + } + + GLchar *source = new GLchar[program_length + 1]; + source[0] = 0; + glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source); + source[program_length] = 0; + + json.beginMember(enumToString(target)); + json.writeString(source); + json.endMember(); + + delete [] source; +} + + +static inline void +dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix) +{ + if (!glIsEnabled(target)) { + return; + } + + GLint program_parameters = 0; + glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &program_parameters); + if (!program_parameters) { + return; + } + + GLint max_program_local_parameters = 0; + glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &max_program_local_parameters); + for (GLint index = 0; index < max_program_local_parameters; ++index) { + GLdouble params[4] = {0, 0, 0, 0}; + glGetProgramLocalParameterdvARB(target, index, params); + + if (!params[0] && !params[1] && !params[2] && !params[3]) { + continue; + } + + char name[256]; + snprintf(name, sizeof name, "%sprogram.local[%i]", prefix, index); + + json.beginMember(name); + json.beginArray(); + json.writeNumber(params[0]); + json.writeNumber(params[1]); + json.writeNumber(params[2]); + json.writeNumber(params[3]); + json.endArray(); + json.endMember(); + } + + GLint max_program_env_parameters = 0; + glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &max_program_env_parameters); + for (GLint index = 0; index < max_program_env_parameters; ++index) { + GLdouble params[4] = {0, 0, 0, 0}; + glGetProgramEnvParameterdvARB(target, index, params); + + if (!params[0] && !params[1] && !params[2] && !params[3]) { + continue; + } + + char name[256]; + snprintf(name, sizeof name, "%sprogram.env[%i]", prefix, index); + + json.beginMember(name); + json.beginArray(); + json.writeNumber(params[0]); + json.writeNumber(params[1]); + json.writeNumber(params[2]); + json.writeNumber(params[3]); + json.endArray(); + json.endMember(); + } +} + + +void +dumpShadersUniforms(JSONWriter &json, Context &context) +{ + GLint program = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + + GLhandleARB programObj = 0; + if (!context.ES && !program) { + programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB); + } + + json.beginMember("shaders"); + json.beginObject(); + if (program) { + dumpProgram(json, program); + } else if (programObj) { + dumpProgramObj(json, programObj); + } else { + dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB); + dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB); + } + json.endObject(); + json.endMember(); // shaders + + json.beginMember("uniforms"); + json.beginObject(); + if (program) { + dumpProgramUniforms(json, program); + } else if (programObj) { + dumpProgramObjUniforms(json, programObj); + } else { + dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp."); + dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp."); + } + json.endObject(); + json.endMember(); // uniforms +} + + +} /* namespace glstate */ diff --git a/retrace/glws.cpp b/retrace/glws.cpp new file mode 100644 index 0000000..c5c41eb --- /dev/null +++ b/retrace/glws.cpp @@ -0,0 +1,62 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include "glws.hpp" + + +namespace glws { + + +bool debug = true; + + +bool +checkExtension(const char *extName, const char *extString) +{ + const char *p = extString; + const char *q = extName; + char c; + do { + c = *p++; + if (c == '\0' || c == ' ') { + if (q && *q == '\0') { + return true; + } else { + q = extName; + } + } else { + if (q && *q == c) { + ++q; + } else { + q = 0; + } + } + } while (c); + return false; +} + + +} /* namespace glws */ diff --git a/retrace/glws.hpp b/retrace/glws.hpp new file mode 100644 index 0000000..9afebf4 --- /dev/null +++ b/retrace/glws.hpp @@ -0,0 +1,170 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + +/* + * Abstraction for GL window system specific APIs (GLX, WGL). + */ + +#ifndef _GLWS_HPP_ +#define _GLWS_HPP_ + + +#include + + +namespace glws { + + +enum Profile { + PROFILE_COMPAT = 0, + PROFILE_CORE, + PROFILE_ES1, + PROFILE_ES2, + PROFILE_MAX +}; + + +extern bool debug; + + +bool +checkExtension(const char *extName, const char *extString); + + +template< class T > +class Attributes { +protected: + std::vector attribs; + +public: + void add(T param) { + attribs.push_back(param); + } + + void add(T pname, T pvalue) { + add(pname); + add(pvalue); + } + + void end(T terminator = 0) { + add(terminator); + } + + operator T * (void) { + return &attribs[0]; + } + + operator const T * (void) const { + return &attribs[0]; + } +}; + + +class Visual +{ +public: + unsigned long redMask; + unsigned long greenMask; + unsigned long blueMask; + unsigned long alphaMask; + bool doubleBuffer; + + virtual ~Visual() {} +}; + + +class Drawable +{ +public: + const Visual *visual; + int width; + int height; + bool visible; + + Drawable(const Visual *vis, int w, int h) : + visual(vis), + width(w), + height(h), + visible(false) + {} + + virtual ~Drawable() {} + + virtual void + resize(int w, int h) { + width = w; + height = h; + } + + virtual void + show(void) { + visible = true; + } + + virtual void swapBuffers(void) = 0; +}; + + +class Context +{ +public: + const Visual *visual; + Profile profile; + + Context(const Visual *vis, Profile prof) : + visual(vis), + profile(prof) + {} + + virtual ~Context() {} +}; + + +void +init(void); + +void +cleanup(void); + +Visual * +createVisual(bool doubleBuffer = false, Profile profile = PROFILE_COMPAT); + +Drawable * +createDrawable(const Visual *visual, int width = 32, int height = 32); + +Context * +createContext(const Visual *visual, Context *shareContext = 0, Profile profile = PROFILE_COMPAT); + +bool +makeCurrent(Drawable *drawable, Context *context); + +bool +processEvents(void); + + +} /* namespace glws */ + + +#endif /* _GLWS_HPP_ */ diff --git a/retrace/glws_cocoa.mm b/retrace/glws_cocoa.mm new file mode 100644 index 0000000..7f696fa --- /dev/null +++ b/retrace/glws_cocoa.mm @@ -0,0 +1,270 @@ +/************************************************************************** + * + * Copyright 2011 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. + * + **************************************************************************/ + + +/** + * Minimal Cocoa integration. + * + * See also: + * - http://developer.apple.com/library/mac/#samplecode/CocoaGL/Introduction/Intro.html + * - http://developer.apple.com/library/mac/#samplecode/Cocoa_With_Carbon_or_CPP/Introduction/Intro.html + * - http://developer.apple.com/library/mac/#samplecode/glut/Introduction/Intro.html + * - http://developer.apple.com/library/mac/#samplecode/GLEssentials/Introduction/Intro.html + * - http://www.glfw.org/ + */ + + +#include +#include + +#include + +#include "glws.hpp" + + +namespace glws { + + +NSAutoreleasePool *autoreleasePool = nil; + + +class CocoaVisual : public Visual +{ +public: + NSOpenGLPixelFormat *pixelFormat; + + CocoaVisual(NSOpenGLPixelFormat *pf) : + pixelFormat(pf) + {} + + ~CocoaVisual() { + [pixelFormat release]; + } +}; + + +class CocoaDrawable : public Drawable +{ +public: + NSWindow *window; + NSOpenGLContext *currentContext; + + CocoaDrawable(const Visual *vis, int w, int h) : + Drawable(vis, w, h), + currentContext(nil) + { + NSOpenGLPixelFormat *pixelFormat = static_cast(visual)->pixelFormat; + + NSRect winRect = NSMakeRect(0, 0, w, h); + + window = [[NSWindow alloc] + initWithContentRect:winRect + styleMask:NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask + backing:NSBackingStoreRetained + defer:NO]; + assert(window != nil); + + NSOpenGLView *view = [[NSOpenGLView alloc] + initWithFrame:winRect + pixelFormat:pixelFormat]; + assert(view != nil); + + [window setContentView:view]; + [window setTitle:@"glretrace"]; + + } + + ~CocoaDrawable() { + [window release]; + } + + void + resize(int w, int h) { + if (w == width && h == height) { + return; + } + + [window setContentSize:NSMakeSize(w, h)]; + + if (currentContext != nil) { + [currentContext update]; + [window makeKeyAndOrderFront:nil]; + [currentContext setView:[window contentView]]; + [currentContext makeCurrentContext]; + } + + Drawable::resize(w, h); + } + + void show(void) { + if (visible) { + return; + } + + // TODO + + Drawable::show(); + } + + void swapBuffers(void) { + if (currentContext != nil) { + [currentContext flushBuffer]; + } + } +}; + + +class CocoaContext : public Context +{ +public: + NSOpenGLContext *context; + + CocoaContext(const Visual *vis, Profile prof, NSOpenGLContext *ctx) : + Context(vis, prof), + context(ctx) + {} + + ~CocoaContext() { + [context release]; + } +}; + + +void +init(void) { + [NSApplication sharedApplication]; + + autoreleasePool = [[NSAutoreleasePool alloc] init]; + + [NSApp finishLaunching]; +} + + +void +cleanup(void) { + [autoreleasePool release]; +} + + +Visual * +createVisual(bool doubleBuffer, Profile profile) { + if (profile != PROFILE_COMPAT && + profile != PROFILE_CORE) { + return nil; + } + + Attributes attribs; + + attribs.add(NSOpenGLPFAAlphaSize, (NSOpenGLPixelFormatAttribute)1); + attribs.add(NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24); + if (doubleBuffer) { + attribs.add(NSOpenGLPFADoubleBuffer); + } + attribs.add(NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)1); + attribs.add(NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute)1); + if (profile == PROFILE_CORE) { +#if CGL_VERSION_1_3 + attribs.add(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); +#else + return NULL; +#endif + } + attribs.end(); + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:attribs]; + + return new CocoaVisual(pixelFormat); +} + +Drawable * +createDrawable(const Visual *visual, int width, int height) +{ + return new CocoaDrawable(visual, width, height); +} + +Context * +createContext(const Visual *visual, Context *shareContext, Profile profile) +{ + NSOpenGLPixelFormat *pixelFormat = static_cast(visual)->pixelFormat; + NSOpenGLContext *share_context = nil; + NSOpenGLContext *context; + + if (profile != PROFILE_COMPAT && + profile != PROFILE_CORE) { + return nil; + } + + if (shareContext) { + share_context = static_cast(shareContext)->context; + } + + context = [[NSOpenGLContext alloc] + initWithFormat:pixelFormat + shareContext:share_context]; + assert(context != nil); + + return new CocoaContext(visual, profile, context); +} + +bool +makeCurrent(Drawable *drawable, Context *context) +{ + if (!drawable || !context) { + [NSOpenGLContext clearCurrentContext]; + } else { + CocoaDrawable *cocoaDrawable = static_cast(drawable); + CocoaContext *cocoaContext = static_cast(context); + + [cocoaDrawable->window makeKeyAndOrderFront:nil]; + [cocoaContext->context setView:[cocoaDrawable->window contentView]]; + [cocoaContext->context makeCurrentContext]; + + cocoaDrawable->currentContext = cocoaContext->context; + } + + return TRUE; +} + +bool +processEvents(void) { + NSEvent* event; + + do { + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp sendEvent:event]; + } while (event); + + return true; +} + + +} /* namespace glws */ diff --git a/retrace/glws_egl_xlib.cpp b/retrace/glws_egl_xlib.cpp new file mode 100644 index 0000000..6655781 --- /dev/null +++ b/retrace/glws_egl_xlib.cpp @@ -0,0 +1,441 @@ +/************************************************************************** + * + * Copyright 2011 LunarG, Inc. + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + +#include +#include + +#include + +#include + +#include "glproc.hpp" +#include "glws.hpp" + + +namespace glws { + + +static Display *display = NULL; +static EGLDisplay eglDisplay = EGL_NO_DISPLAY; +static int screen = 0; + + +class EglVisual : public Visual +{ +public: + EGLConfig config; + XVisualInfo *visinfo; + + EglVisual() : + config(0), + visinfo(0) + {} + + ~EglVisual() { + XFree(visinfo); + } +}; + + +static void describeEvent(const XEvent &event) { + if (0) { + switch (event.type) { + case ConfigureNotify: + std::cerr << "ConfigureNotify"; + break; + case Expose: + std::cerr << "Expose"; + break; + case KeyPress: + std::cerr << "KeyPress"; + break; + case MapNotify: + std::cerr << "MapNotify"; + break; + case ReparentNotify: + std::cerr << "ReparentNotify"; + break; + default: + std::cerr << "Event " << event.type; + } + std::cerr << " " << event.xany.window << "\n"; + } +} + +class EglDrawable : public Drawable +{ +public: + Window window; + EGLSurface surface; + EGLint api; + + EglDrawable(const Visual *vis, int w, int h) : + Drawable(vis, w, h), api(EGL_OPENGL_ES_API) + { + XVisualInfo *visinfo = static_cast(visual)->visinfo; + + Window root = RootWindow(display, screen); + + /* window attributes */ + XSetWindowAttributes attr; + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask; + + unsigned long mask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + int x = 0, y = 0; + + window = XCreateWindow( + display, root, + x, y, width, height, + 0, + visinfo->depth, + InputOutput, + visinfo->visual, + mask, + &attr); + + XSizeHints sizehints; + sizehints.x = x; + sizehints.y = y; + sizehints.width = width; + sizehints.height = height; + sizehints.flags = USSize | USPosition; + XSetNormalHints(display, window, &sizehints); + + const char *name = "glretrace"; + XSetStandardProperties( + display, window, name, name, + None, (char **)NULL, 0, &sizehints); + + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + + EGLConfig config = static_cast(visual)->config; + surface = eglCreateWindowSurface(eglDisplay, config, (EGLNativeWindowType)window, NULL); + } + + void waitForEvent(int type) { + XEvent event; + do { + XWindowEvent(display, window, StructureNotifyMask, &event); + describeEvent(event); + } while (event.type != type); + } + + ~EglDrawable() { + eglDestroySurface(eglDisplay, surface); + eglWaitClient(); + XDestroyWindow(display, window); + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + } + + void + resize(int w, int h) { + if (w == width && h == height) { + return; + } + + eglWaitClient(); + + // We need to ensure that pending events are processed here, and XSync + // with discard = True guarantees that, but it appears the limited + // event processing we do so far is sufficient + //XSync(display, True); + + Drawable::resize(w, h); + + XResizeWindow(display, window, w, h); + + // Tell the window manager to respect the requested size + XSizeHints size_hints; + size_hints.max_width = size_hints.min_width = w; + size_hints.max_height = size_hints.min_height = h; + size_hints.flags = PMinSize | PMaxSize; + XSetWMNormalHints(display, window, &size_hints); + + waitForEvent(ConfigureNotify); + + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + } + + void show(void) { + if (visible) { + return; + } + + eglWaitClient(); + + XMapWindow(display, window); + + waitForEvent(MapNotify); + + eglWaitNative(EGL_CORE_NATIVE_ENGINE); + + Drawable::show(); + } + + void swapBuffers(void) { + eglBindAPI(api); + eglSwapBuffers(eglDisplay, surface); + } +}; + + +class EglContext : public Context +{ +public: + EGLContext context; + + EglContext(const Visual *vis, Profile prof, EGLContext ctx) : + Context(vis, prof), + context(ctx) + {} + + ~EglContext() { + eglDestroyContext(eglDisplay, context); + } +}; + +/** + * Load the symbols from the specified shared object into global namespace, so + * that they can be later found by dlsym(RTLD_NEXT, ...); + */ +static void +load(const char *filename) +{ + if (!dlopen(filename, RTLD_GLOBAL | RTLD_LAZY)) { + std::cerr << "error: unable to open " << filename << "\n"; + exit(1); + } +} + +void +init(void) { + load("libEGL.so.1"); + + display = XOpenDisplay(NULL); + if (!display) { + std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n"; + exit(1); + } + + screen = DefaultScreen(display); + + eglDisplay = eglGetDisplay((EGLNativeDisplayType)display); + if (eglDisplay == EGL_NO_DISPLAY) { + std::cerr << "error: unable to get EGL display\n"; + XCloseDisplay(display); + exit(1); + } + + EGLint major, minor; + if (!eglInitialize(eglDisplay, &major, &minor)) { + std::cerr << "error: unable to initialize EGL display\n"; + XCloseDisplay(display); + exit(1); + } +} + +void +cleanup(void) { + if (display) { + eglTerminate(eglDisplay); + XCloseDisplay(display); + display = NULL; + } +} + +Visual * +createVisual(bool doubleBuffer, Profile profile) { + EglVisual *visual = new EglVisual(); + // possible combinations + const EGLint api_bits_gl[7] = { + EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT, + EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_BIT, + EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_ES2_BIT, + EGL_OPENGL_ES_BIT, + }; + const EGLint api_bits_gles1[7] = { + EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT, + EGL_OPENGL_ES_BIT, + EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_BIT, + EGL_OPENGL_ES2_BIT, + }; + const EGLint api_bits_gles2[7] = { + EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT, + EGL_OPENGL_ES2_BIT, + EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT, + EGL_OPENGL_BIT, + EGL_OPENGL_ES_BIT, + }; + const EGLint *api_bits; + + switch(profile) { + case PROFILE_COMPAT: + api_bits = api_bits_gl; + break; + case PROFILE_ES1: + api_bits = api_bits_gles1; + break; + case PROFILE_ES2: + api_bits = api_bits_gles2; + break; + default: + return NULL; + }; + + for (int i = 0; i < 7; i++) { + Attributes attribs; + + attribs.add(EGL_SURFACE_TYPE, EGL_WINDOW_BIT); + attribs.add(EGL_RED_SIZE, 1); + attribs.add(EGL_GREEN_SIZE, 1); + attribs.add(EGL_BLUE_SIZE, 1); + attribs.add(EGL_ALPHA_SIZE, 1); + attribs.add(EGL_DEPTH_SIZE, 1); + attribs.add(EGL_STENCIL_SIZE, 1); + attribs.add(EGL_RENDERABLE_TYPE, api_bits[i]); + attribs.end(EGL_NONE); + + EGLint num_configs, vid; + if (eglChooseConfig(eglDisplay, attribs, &visual->config, 1, &num_configs) && + num_configs == 1 && + eglGetConfigAttrib(eglDisplay, visual->config, EGL_NATIVE_VISUAL_ID, &vid)) { + XVisualInfo templ; + int num_visuals; + + templ.visualid = vid; + visual->visinfo = XGetVisualInfo(display, VisualIDMask, &templ, &num_visuals); + break; + } + } + + assert(visual->visinfo); + + return visual; +} + +Drawable * +createDrawable(const Visual *visual, int width, int height) +{ + return new EglDrawable(visual, width, height); +} + +Context * +createContext(const Visual *_visual, Context *shareContext, Profile profile) +{ + const EglVisual *visual = static_cast(_visual); + EGLContext share_context = EGL_NO_CONTEXT; + EGLContext context; + Attributes attribs; + + if (shareContext) { + share_context = static_cast(shareContext)->context; + } + + EGLint api = eglQueryAPI(); + + switch (profile) { + case PROFILE_COMPAT: + load("libGL.so.1"); + eglBindAPI(EGL_OPENGL_API); + break; + case PROFILE_CORE: + assert(0); + return NULL; + case PROFILE_ES1: + load("libGLESv1_CM.so.1"); + eglBindAPI(EGL_OPENGL_ES_API); + break; + case PROFILE_ES2: + load("libGLESv2.so.2"); + eglBindAPI(EGL_OPENGL_ES_API); + attribs.add(EGL_CONTEXT_CLIENT_VERSION, 2); + break; + default: + return NULL; + } + + attribs.end(EGL_NONE); + + context = eglCreateContext(eglDisplay, visual->config, share_context, attribs); + if (!context) + return NULL; + + eglBindAPI(api); + + return new EglContext(visual, profile, context); +} + +bool +makeCurrent(Drawable *drawable, Context *context) +{ + if (!drawable || !context) { + return eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } else { + EglDrawable *eglDrawable = static_cast(drawable); + EglContext *eglContext = static_cast(context); + EGLBoolean ok; + + ok = eglMakeCurrent(eglDisplay, eglDrawable->surface, + eglDrawable->surface, eglContext->context); + + if (ok) { + EGLint api; + + eglQueryContext(eglDisplay, eglContext->context, + EGL_CONTEXT_CLIENT_TYPE, &api); + + eglDrawable->api = api; + } + + return ok; + } +} + +bool +processEvents(void) { + while (XPending(display) > 0) { + XEvent event; + XNextEvent(display, &event); + describeEvent(event); + } + return true; +} + + +} /* namespace glws */ diff --git a/retrace/glws_glx.cpp b/retrace/glws_glx.cpp new file mode 100644 index 0000000..c151db1 --- /dev/null +++ b/retrace/glws_glx.cpp @@ -0,0 +1,377 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + +#include +#include + +#include + +#include "glproc.hpp" +#include "glws.hpp" + + +namespace glws { + + +static Display *display = NULL; +static int screen = 0; + +static unsigned glxVersion = 0; +static const char *extensions = 0; +static bool has_GLX_ARB_create_context = false; + + +class GlxVisual : public Visual +{ +public: + GLXFBConfig fbconfig; + XVisualInfo *visinfo; + + GlxVisual() : + fbconfig(0), + visinfo(0) + {} + + ~GlxVisual() { + XFree(visinfo); + } +}; + + +static void describeEvent(const XEvent &event) { + if (0) { + switch (event.type) { + case ConfigureNotify: + std::cerr << "ConfigureNotify"; + break; + case Expose: + std::cerr << "Expose"; + break; + case KeyPress: + std::cerr << "KeyPress"; + break; + case MapNotify: + std::cerr << "MapNotify"; + break; + case ReparentNotify: + std::cerr << "ReparentNotify"; + break; + default: + std::cerr << "Event " << event.type; + } + std::cerr << " " << event.xany.window << "\n"; + } +} + +class GlxDrawable : public Drawable +{ +public: + Window window; + + GlxDrawable(const Visual *vis, int w, int h) : + Drawable(vis, w, h) + { + XVisualInfo *visinfo = static_cast(visual)->visinfo; + + Window root = RootWindow(display, screen); + + /* window attributes */ + XSetWindowAttributes attr; + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(display, root, visinfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask; + + unsigned long mask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + int x = 0, y = 0; + + window = XCreateWindow( + display, root, + x, y, width, height, + 0, + visinfo->depth, + InputOutput, + visinfo->visual, + mask, + &attr); + + XSizeHints sizehints; + sizehints.x = x; + sizehints.y = y; + sizehints.width = width; + sizehints.height = height; + sizehints.flags = USSize | USPosition; + XSetNormalHints(display, window, &sizehints); + + const char *name = "glretrace"; + XSetStandardProperties( + display, window, name, name, + None, (char **)NULL, 0, &sizehints); + + glXWaitX(); + } + + void waitForEvent(int type) { + XEvent event; + do { + XWindowEvent(display, window, StructureNotifyMask, &event); + describeEvent(event); + } while (event.type != type); + } + + ~GlxDrawable() { + XDestroyWindow(display, window); + } + + void + resize(int w, int h) { + if (w == width && h == height) { + return; + } + + glXWaitGL(); + + // We need to ensure that pending events are processed here, and XSync + // with discard = True guarantees that, but it appears the limited + // event processing we do so far is sufficient + //XSync(display, True); + + Drawable::resize(w, h); + + XResizeWindow(display, window, w, h); + + // Tell the window manager to respect the requested size + XSizeHints size_hints; + size_hints.max_width = size_hints.min_width = w; + size_hints.max_height = size_hints.min_height = h; + size_hints.flags = PMinSize | PMaxSize; + XSetWMNormalHints(display, window, &size_hints); + + waitForEvent(ConfigureNotify); + + glXWaitX(); + } + + void show(void) { + if (visible) { + return; + } + + glXWaitGL(); + + XMapWindow(display, window); + + waitForEvent(MapNotify); + + glXWaitX(); + + Drawable::show(); + } + + void swapBuffers(void) { + glXSwapBuffers(display, window); + } +}; + + +class GlxContext : public Context +{ +public: + GLXContext context; + + GlxContext(const Visual *vis, Profile prof, GLXContext ctx) : + Context(vis, prof), + context(ctx) + {} + + ~GlxContext() { + glXDestroyContext(display, context); + } +}; + +void +init(void) { + display = XOpenDisplay(NULL); + if (!display) { + std::cerr << "error: unable to open display " << XDisplayName(NULL) << "\n"; + exit(1); + } + + screen = DefaultScreen(display); + + int major = 0, minor = 0; + glXQueryVersion(display, &major, &minor); + glxVersion = (major << 8) | minor; + + extensions = glXQueryExtensionsString(display, screen); + has_GLX_ARB_create_context = checkExtension("GLX_ARB_create_context", extensions); +} + +void +cleanup(void) { + if (display) { + XCloseDisplay(display); + display = NULL; + } +} + +Visual * +createVisual(bool doubleBuffer, Profile profile) { + if (profile != PROFILE_COMPAT && + profile != PROFILE_CORE) { + return NULL; + } + + GlxVisual *visual = new GlxVisual; + + if (glxVersion >= 0x0103) { + Attributes attribs; + attribs.add(GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT); + attribs.add(GLX_RENDER_TYPE, GLX_RGBA_BIT); + attribs.add(GLX_RED_SIZE, 1); + attribs.add(GLX_GREEN_SIZE, 1); + attribs.add(GLX_BLUE_SIZE, 1); + attribs.add(GLX_ALPHA_SIZE, 1); + attribs.add(GLX_DOUBLEBUFFER, doubleBuffer ? GL_TRUE : GL_FALSE); + attribs.add(GLX_DEPTH_SIZE, 1); + attribs.add(GLX_STENCIL_SIZE, 1); + attribs.end(); + + int num_configs = 0; + GLXFBConfig * fbconfigs; + fbconfigs = glXChooseFBConfig(display, screen, attribs, &num_configs); + assert(num_configs && fbconfigs); + visual->fbconfig = fbconfigs[0]; + assert(visual->fbconfig); + visual->visinfo = glXGetVisualFromFBConfig(display, visual->fbconfig); + assert(visual->visinfo); + } else { + Attributes attribs; + attribs.add(GLX_RGBA); + attribs.add(GLX_RED_SIZE, 1); + attribs.add(GLX_GREEN_SIZE, 1); + attribs.add(GLX_BLUE_SIZE, 1); + attribs.add(GLX_ALPHA_SIZE, 1); + if (doubleBuffer) { + attribs.add(GLX_DOUBLEBUFFER); + } + attribs.add(GLX_DEPTH_SIZE, 1); + attribs.add(GLX_STENCIL_SIZE, 1); + attribs.end(); + + visual->visinfo = glXChooseVisual(display, screen, attribs); + } + + return visual; +} + +Drawable * +createDrawable(const Visual *visual, int width, int height) +{ + return new GlxDrawable(visual, width, height); +} + +Context * +createContext(const Visual *_visual, Context *shareContext, Profile profile) +{ + const GlxVisual *visual = static_cast(_visual); + GLXContext share_context = NULL; + GLXContext context; + + if (shareContext) { + share_context = static_cast(shareContext)->context; + } + + if (glxVersion >= 0x0104 && has_GLX_ARB_create_context) { + Attributes attribs; + + attribs.add(GLX_RENDER_TYPE, GLX_RGBA_TYPE); + if (debug) { + attribs.add(GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB); + } + + switch (profile) { + case PROFILE_COMPAT: + break; + case PROFILE_CORE: + // XXX: This will invariable return a 3.2 context, when supported. + // We probably should have a PROFILE_CORE_XX per version. + attribs.add(GLX_CONTEXT_MAJOR_VERSION_ARB, 3); + attribs.add(GLX_CONTEXT_MINOR_VERSION_ARB, 2); + attribs.add(GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB); + break; + default: + return NULL; + } + + attribs.end(); + + context = glXCreateContextAttribsARB(display, visual->fbconfig, share_context, True, attribs); + } else { + if (profile != PROFILE_COMPAT) { + return NULL; + } + + if (glxVersion >= 0x103) { + context = glXCreateNewContext(display, visual->fbconfig, GLX_RGBA_TYPE, share_context, True); + } else { + context = glXCreateContext(display, visual->visinfo, share_context, True); + } + } + + if (!context) { + return NULL; + } + + return new GlxContext(visual, profile, context); +} + +bool +makeCurrent(Drawable *drawable, Context *context) +{ + if (!drawable || !context) { + return glXMakeCurrent(display, None, NULL); + } else { + GlxDrawable *glxDrawable = static_cast(drawable); + GlxContext *glxContext = static_cast(context); + + return glXMakeCurrent(display, glxDrawable->window, glxContext->context); + } +} + +bool +processEvents(void) { + while (XPending(display) > 0) { + XEvent event; + XNextEvent(display, &event); + describeEvent(event); + } + return true; +} + + +} /* namespace glws */ diff --git a/retrace/glws_wgl.cpp b/retrace/glws_wgl.cpp new file mode 100644 index 0000000..efefff0 --- /dev/null +++ b/retrace/glws_wgl.cpp @@ -0,0 +1,332 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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 bindings. + */ + + +#include + +#include "glproc.hpp" +#include "glws.hpp" + + +namespace glws { + + +/* + * Several WGL functions come in two flavors: + * - GDI (ChoosePixelFormat, SetPixelFormat, SwapBuffers, etc) + * - WGL (wglChoosePixelFormat, wglSetPixelFormat, wglSwapBuffers, etc) + * + * The GDI entrypoints will inevitably dispatch to the first module named + * "OPENGL32", loading "C:\Windows\System32\opengl32.dll" if none was loaded so + * far. + * + * In order to use a implementation other than the one installed in the system + * (when specified via the TRACE_LIBGL environment variable), we need to use + * WGL entrypoints. + * + * See also: + * - http://www.opengl.org/archives/resources/faq/technical/mswindows.htm + */ +static __PFNWGLCHOOSEPIXELFORMAT pfnChoosePixelFormat = &ChoosePixelFormat; +static __PFNWGLSETPIXELFORMAT pfnSetPixelFormat = &SetPixelFormat; +static __PFNWGLSWAPBUFFERS pfnSwapBuffers = &SwapBuffers; + + +static LRESULT CALLBACK +WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + MINMAXINFO *pMMI; + switch (uMsg) { + case WM_GETMINMAXINFO: + // Allow to create a window bigger than the desktop + pMMI = (MINMAXINFO *)lParam; + pMMI->ptMaxSize.x = 60000; + pMMI->ptMaxSize.y = 60000; + pMMI->ptMaxTrackSize.x = 60000; + pMMI->ptMaxTrackSize.y = 60000; + break; + default: + break; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + + +class WglDrawable : public Drawable +{ +public: + DWORD dwExStyle; + DWORD dwStyle; + HWND hWnd; + HDC hDC; + PIXELFORMATDESCRIPTOR pfd; + int iPixelFormat; + + WglDrawable(const Visual *vis, int width, int height) : + Drawable(vis, width, height) + { + static bool first = TRUE; + RECT rect; + BOOL bRet; + + if (first) { + WNDCLASS wc; + memset(&wc, 0, sizeof wc); + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.lpfnWndProc = WndProc; + wc.lpszClassName = "glretrace"; + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + first = FALSE; + } + + dwExStyle = 0; + dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW; + + int x = 0, y = 0; + + rect.left = x; + rect.top = y; + rect.right = rect.left + width; + rect.bottom = rect.top + height; + + AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle); + + hWnd = CreateWindowEx(dwExStyle, + "glretrace", /* wc.lpszClassName */ + NULL, + dwStyle, + 0, /* x */ + 0, /* y */ + rect.right - rect.left, /* width */ + rect.bottom - rect.top, /* height */ + NULL, + NULL, + NULL, + NULL); + hDC = GetDC(hWnd); + + memset(&pfd, 0, sizeof pfd); + pfd.cColorBits = 4; + pfd.cRedBits = 1; + pfd.cGreenBits = 1; + pfd.cBlueBits = 1; + pfd.cAlphaBits = 1; + pfd.cDepthBits = 1; + pfd.cStencilBits = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + + if (visual->doubleBuffer) { + pfd.dwFlags |= PFD_DOUBLEBUFFER; + } + + iPixelFormat = pfnChoosePixelFormat(hDC, &pfd); + if (iPixelFormat <= 0) { + std::cerr << "error: ChoosePixelFormat failed\n"; + exit(1); + } + + bRet = pfnSetPixelFormat(hDC, iPixelFormat, &pfd); + if (!bRet) { + std::cerr << "error: SetPixelFormat failed\n"; + exit(1); + } + } + + ~WglDrawable() { + ReleaseDC(hWnd, hDC); + DestroyWindow(hWnd); + } + + void + resize(int w, int h) { + if (w == width && h == height) { + return; + } + + RECT rClient, rWindow; + GetClientRect(hWnd, &rClient); + GetWindowRect(hWnd, &rWindow); + w += (rWindow.right - rWindow.left) - rClient.right; + h += (rWindow.bottom - rWindow.top) - rClient.bottom; + SetWindowPos(hWnd, NULL, rWindow.left, rWindow.top, w, h, SWP_NOMOVE); + + Drawable::resize(w, h); + } + + void show(void) { + if (visible) { + return; + } + + ShowWindow(hWnd, SW_SHOW); + + Drawable::show(); + } + + void swapBuffers(void) { + BOOL bRet; + bRet = pfnSwapBuffers(hDC); + if (!bRet) { + std::cerr << "warning: SwapBuffers failed\n"; + } + + // Drain message queue to prevent window from being considered + // non-responsive + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +}; + + +class WglContext : public Context +{ +public: + HGLRC hglrc; + WglContext *shareContext; + + WglContext(const Visual *vis, Profile prof, WglContext *share) : + Context(vis, prof), + hglrc(0), + shareContext(share) + {} + + ~WglContext() { + if (hglrc) { + wglDeleteContext(hglrc); + } + } +}; + + +void +init(void) { + /* + * OpenGL library must be loaded by the time we call GDI. + */ + + const char * libgl_filename = getenv("TRACE_LIBGL"); + + if (libgl_filename) { + pfnChoosePixelFormat = &wglChoosePixelFormat; + pfnSetPixelFormat = &wglSetPixelFormat; + pfnSwapBuffers = &wglSwapBuffers; + } else { + libgl_filename = "OPENGL32"; + } + + __libGlHandle = LoadLibraryA(libgl_filename); + if (!__libGlHandle) { + std::cerr << "error: unable to open " << libgl_filename << "\n"; + exit(1); + } +} + +void +cleanup(void) { +} + +Visual * +createVisual(bool doubleBuffer, Profile profile) { + if (profile != PROFILE_COMPAT) { + return NULL; + } + + Visual *visual = new Visual(); + + visual->doubleBuffer = doubleBuffer; + + return visual; +} + +Drawable * +createDrawable(const Visual *visual, int width, int height) +{ + return new WglDrawable(visual, width, height); +} + +Context * +createContext(const Visual *visual, Context *shareContext, Profile profile) +{ + if (profile != PROFILE_COMPAT) { + return NULL; + } + + return new WglContext(visual, profile, static_cast(shareContext)); +} + +bool +makeCurrent(Drawable *drawable, Context *context) +{ + if (!drawable || !context) { + return wglMakeCurrent(NULL, NULL); + } else { + WglDrawable *wglDrawable = static_cast(drawable); + WglContext *wglContext = static_cast(context); + + if (!wglContext->hglrc) { + wglContext->hglrc = wglCreateContext(wglDrawable->hDC); + if (!wglContext->hglrc) { + std::cerr << "error: wglCreateContext failed\n"; + exit(1); + return false; + } + if (wglContext->shareContext) { + BOOL bRet; + bRet = wglShareLists(wglContext->shareContext->hglrc, + wglContext->hglrc); + if (!bRet) { + std::cerr << "warning: wglShareLists failed\n"; + } + } + } + + return wglMakeCurrent(wglDrawable->hDC, wglContext->hglrc); + } +} + +bool +processEvents(void) { + // TODO + return true; +} + + +} /* namespace glws */ diff --git a/retrace/retrace.cpp b/retrace/retrace.cpp new file mode 100644 index 0000000..1630995 --- /dev/null +++ b/retrace/retrace.cpp @@ -0,0 +1,139 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include +#include + +#include "os_time.hpp" +#include "trace_dump.hpp" +#include "retrace.hpp" + + +namespace retrace { + + +trace::Parser parser; + + +int verbosity = 0; +bool profiling = false; + + +static bool call_dumped = false; + + +static void dumpCall(trace::Call &call) { + if (verbosity >= 0 && !call_dumped) { + std::cout << call; + std::cout.flush(); + call_dumped = true; + } +} + + +std::ostream &warning(trace::Call &call) { + dumpCall(call); + + std::cerr << call.no << ": "; + std::cerr << "warning: "; + + return std::cerr; +} + + +void ignore(trace::Call &call) { + (void)call; +} + +void unsupported(trace::Call &call) { + warning(call) << "unsupported " << call.name() << " call\n"; +} + +inline void Retracer::addCallback(const Entry *entry) { + assert(entry->name); + assert(entry->callback); + map[entry->name] = entry->callback; +} + + +void Retracer::addCallbacks(const Entry *entries) { + while (entries->name && entries->callback) { + addCallback(entries++); + } +} + + +void Retracer::retrace(trace::Call &call) { + call_dumped = false; + + if (verbosity >= 1) { + if (verbosity >= 2 || + !(call.flags & trace::CALL_FLAG_VERBOSE)) { + dumpCall(call); + } + } + + Callback callback = 0; + + trace::Id id = call.sig->id; + if (id >= callbacks.size()) { + callbacks.resize(id + 1); + callback = 0; + } else { + callback = callbacks[id]; + } + + if (!callback) { + Map::const_iterator it = map.find(call.name()); + if (it == map.end()) { + callback = &unsupported; + } else { + callback = it->second; + } + callbacks[id] = callback; + } + + assert(callback); + assert(callbacks[id] == callback); + + if (retrace::profiling) { + long long startTime = os::getTime(); + callback(call); + long long stopTime = os::getTime(); + float timeInterval = (stopTime - startTime) * (1.0E6 / os::timeFrequency); + + std::cout + << call.no << " " + << "[" << timeInterval << " usec] " + ; + trace::dump(call, std::cout, trace::DUMP_FLAG_NO_CALL_NO | trace::DUMP_FLAG_NO_COLOR); + } else { + callback(call); + } +} + + +} /* namespace retrace */ diff --git a/retrace/retrace.hpp b/retrace/retrace.hpp new file mode 100644 index 0000000..c1e556a --- /dev/null +++ b/retrace/retrace.hpp @@ -0,0 +1,242 @@ +/************************************************************************** + * + * Copyright 2011-2012 Jose Fonseca + * 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. + * + **************************************************************************/ + +#ifndef _RETRACE_HPP_ +#define _RETRACE_HPP_ + +#include +#include +#include + +#include +#include +#include + +#include "trace_model.hpp" +#include "trace_parser.hpp" + + +namespace retrace { + + +extern trace::Parser parser; + + +/** + * Handle map. + * + * It is just like a regular std::map container, but lookups of missing + * keys return the key instead of default constructor. + * + * This is necessary for several GL named objects, where one can either request + * the implementation to generate an unique name, or pick a value never used + * before. + * + * XXX: In some cases, instead of returning the key, it would make more sense + * to return an unused data value (e.g., container count). + */ +template +class map +{ +private: + typedef std::map base_type; + base_type base; + +public: + + T & operator[] (const T &key) { + typename base_type::iterator it; + it = base.find(key); + if (it == base.end()) { + return (base[key] = key); + } + return it->second; + } + + const T & operator[] (const T &key) const { + typename base_type::const_iterator it; + it = base.find(key); + if (it == base.end()) { + return (base[key] = key); + } + return it->second; + } +}; + + +/** + * Similar to alloca(), but implemented with malloc. + */ +class ScopedAllocator +{ +private: + uintptr_t next; + +public: + ScopedAllocator() : + next(0) { + } + + inline void * + alloc(size_t size) { + if (!size) { + return NULL; + } + + uintptr_t * buf = static_cast(malloc(sizeof(uintptr_t) + size)); + if (!buf) { + return NULL; + } + + *buf = next; + next = reinterpret_cast(buf); + assert((next & 1) == 0); + + return static_cast(&buf[1]); + } + + template< class T > + inline T * + alloc(size_t n = 1) { + return static_cast(alloc(sizeof(T) * n)); + } + + /** + * Allocate an array with the same dimensions as the specified value. + */ + template< class T > + inline T * + alloc(const trace::Value *value) { + const trace::Array *array = dynamic_cast(value); + if (array) { + return alloc(array->size()); + } + const trace::Null *null = dynamic_cast(value); + if (null) { + return NULL; + } + assert(0); + return NULL; + } + + /** + * Prevent this pointer from being automatically freed. + */ + template< class T > + inline void + bind(T *ptr) { + if (ptr) { + reinterpret_cast(ptr)[-1] |= 1; + } + } + + inline + ~ScopedAllocator() { + while (next) { + uintptr_t temp = *reinterpret_cast(next); + + bool bind = temp & 1; + temp &= ~1; + + if (!bind) { + free(reinterpret_cast(next)); + } + + next = temp; + } + } +}; + + +void +addRegion(unsigned long long address, void *buffer, unsigned long long size); + +void +delRegionByPointer(void *ptr); + +void * +toPointer(trace::Value &value, bool bind = false); + + +/** + * Output verbosity when retracing files. + */ +extern int verbosity; + +/** + * Add profiling data to the dump when retracing. + */ +extern bool profiling; + + +std::ostream &warning(trace::Call &call); + + +void ignore(trace::Call &call); +void unsupported(trace::Call &call); + + +typedef void (*Callback)(trace::Call &call); + +struct Entry { + const char *name; + Callback callback; +}; + + +struct stringComparer { + bool operator() (const char *a, const char *b) const { + return strcmp(a, b) < 0; + } +}; + + +extern const Entry stdc_callbacks[]; + + +class Retracer +{ + typedef std::map Map; + Map map; + + std::vector callbacks; + +public: + Retracer() { + addCallbacks(stdc_callbacks); + } + + virtual ~Retracer() {} + + void addCallback(const Entry *entry); + void addCallbacks(const Entry *entries); + + void retrace(trace::Call &call); +}; + + +} /* namespace retrace */ + +#endif /* _RETRACE_HPP_ */ diff --git a/retrace/retrace.py b/retrace/retrace.py new file mode 100644 index 0000000..369c13b --- /dev/null +++ b/retrace/retrace.py @@ -0,0 +1,505 @@ +########################################################################## +# +# Copyright 2010 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. +# +##########################################################################/ + + +"""Generic retracing code generator.""" + + +# Adjust path +import os.path +import sys +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + + +import specs.stdapi as stdapi +import specs.glapi as glapi + + +class UnsupportedType(Exception): + pass + + +class MutableRebuilder(stdapi.Rebuilder): + '''Type visitor which derives a mutable type.''' + + def visitConst(self, const): + # Strip out const qualifier + return const.type + + def visitAlias(self, alias): + # Tear the alias on type changes + type = self.visit(alias.type) + if type is alias.type: + return alias + return type + + def visitReference(self, reference): + # Strip out references + return reference.type + + +def lookupHandle(handle, value): + if handle.key is None: + return "__%s_map[%s]" % (handle.name, value) + else: + key_name, key_type = handle.key + return "__%s_map[%s][%s]" % (handle.name, key_name, value) + + +class ValueAllocator(stdapi.Visitor): + + def visitLiteral(self, literal, lvalue, rvalue): + pass + + def visitConst(self, const, lvalue, rvalue): + self.visit(const.type, lvalue, rvalue) + + def visitAlias(self, alias, lvalue, rvalue): + self.visit(alias.type, lvalue, rvalue) + + def visitEnum(self, enum, lvalue, rvalue): + pass + + def visitBitmask(self, bitmask, lvalue, rvalue): + pass + + def visitArray(self, array, lvalue, rvalue): + print ' %s = _allocator.alloc<%s>(&%s);' % (lvalue, array.type, rvalue) + + def visitPointer(self, pointer, lvalue, rvalue): + print ' %s = _allocator.alloc<%s>(&%s);' % (lvalue, pointer.type, rvalue) + + def visitIntPointer(self, pointer, lvalue, rvalue): + pass + + def visitObjPointer(self, pointer, lvalue, rvalue): + pass + + def visitLinearPointer(self, pointer, lvalue, rvalue): + pass + + def visitReference(self, reference, lvalue, rvalue): + self.visit(reference.type, lvalue, rvalue); + + def visitHandle(self, handle, lvalue, rvalue): + pass + + def visitBlob(self, blob, lvalue, rvalue): + pass + + def visitString(self, string, lvalue, rvalue): + pass + + def visitStruct(self, struct, lvalue, rvalue): + pass + + def visitPolymorphic(self, polymorphic, lvalue, rvalue): + self.visit(polymorphic.defaultType, lvalue, rvalue) + + def visitOpaque(self, opaque, lvalue, rvalue): + pass + + +class ValueDeserializer(stdapi.Visitor): + + def visitLiteral(self, literal, lvalue, rvalue): + print ' %s = (%s).to%s();' % (lvalue, rvalue, literal.kind) + + def visitConst(self, const, lvalue, rvalue): + self.visit(const.type, lvalue, rvalue) + + def visitAlias(self, alias, lvalue, rvalue): + self.visit(alias.type, lvalue, rvalue) + + def visitEnum(self, enum, lvalue, rvalue): + print ' %s = static_cast<%s>((%s).toSInt());' % (lvalue, enum, rvalue) + + def visitBitmask(self, bitmask, lvalue, rvalue): + self.visit(bitmask.type, lvalue, rvalue) + + def visitArray(self, array, lvalue, rvalue): + + tmp = '__a_' + array.tag + '_' + str(self.seq) + self.seq += 1 + + print ' if (%s) {' % (lvalue,) + print ' const trace::Array *%s = dynamic_cast(&%s);' % (tmp, rvalue) + length = '%s->values.size()' % (tmp,) + index = '__j' + array.tag + print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length) + try: + self.visit(array.type, '%s[%s]' % (lvalue, index), '*%s->values[%s]' % (tmp, index)) + finally: + print ' }' + print ' }' + + def visitPointer(self, pointer, lvalue, rvalue): + tmp = '__a_' + pointer.tag + '_' + str(self.seq) + self.seq += 1 + + print ' if (%s) {' % (lvalue,) + print ' const trace::Array *%s = dynamic_cast(&%s);' % (tmp, rvalue) + try: + self.visit(pointer.type, '%s[0]' % (lvalue,), '*%s->values[0]' % (tmp,)) + finally: + print ' }' + + def visitIntPointer(self, pointer, lvalue, rvalue): + print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, pointer, rvalue) + + def visitObjPointer(self, pointer, lvalue, rvalue): + old_lvalue = '(%s).toUIntPtr()' % (rvalue,) + new_lvalue = '_obj_map[%s]' % (old_lvalue,) + print ' if (retrace::verbosity >= 2) {' + print ' std::cout << std::hex << "obj 0x" << size_t(%s) << " <- 0x" << size_t(%s) << std::dec <<"\\n";' % (old_lvalue, new_lvalue) + print ' }' + print ' %s = static_cast<%s>(%s);' % (lvalue, pointer, new_lvalue) + + def visitLinearPointer(self, pointer, lvalue, rvalue): + print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, pointer, rvalue) + + def visitReference(self, reference, lvalue, rvalue): + self.visit(reference.type, lvalue, rvalue); + + def visitHandle(self, handle, lvalue, rvalue): + #OpaqueValueDeserializer().visit(handle.type, lvalue, rvalue); + self.visit(handle.type, lvalue, rvalue); + new_lvalue = lookupHandle(handle, lvalue) + print ' if (retrace::verbosity >= 2) {' + print ' std::cout << "%s " << size_t(%s) << " <- " << size_t(%s) << "\\n";' % (handle.name, lvalue, new_lvalue) + print ' }' + print ' %s = %s;' % (lvalue, new_lvalue) + + def visitBlob(self, blob, lvalue, rvalue): + print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, blob, rvalue) + + def visitString(self, string, lvalue, rvalue): + print ' %s = (%s)((%s).toString());' % (lvalue, string.expr, rvalue) + + seq = 0 + + def visitStruct(self, struct, lvalue, rvalue): + tmp = '__s_' + struct.tag + '_' + str(self.seq) + self.seq += 1 + + print ' const trace::Struct *%s = dynamic_cast(&%s);' % (tmp, rvalue) + print ' assert(%s);' % (tmp) + for i in range(len(struct.members)): + member_type, member_name = struct.members[i] + self.visit(member_type, '%s.%s' % (lvalue, member_name), '*%s->members[%s]' % (tmp, i)) + + def visitPolymorphic(self, polymorphic, lvalue, rvalue): + self.visit(polymorphic.defaultType, lvalue, rvalue) + + def visitOpaque(self, opaque, lvalue, rvalue): + raise UnsupportedType + + +class OpaqueValueDeserializer(ValueDeserializer): + '''Value extractor that also understands opaque values. + + Normally opaque values can't be retraced, unless they are being extracted + in the context of handles.''' + + def visitOpaque(self, opaque, lvalue, rvalue): + print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, opaque, rvalue) + + +class SwizzledValueRegistrator(stdapi.Visitor): + '''Type visitor which will register (un)swizzled value pairs, to later be + swizzled.''' + + def visitLiteral(self, literal, lvalue, rvalue): + pass + + def visitAlias(self, alias, lvalue, rvalue): + self.visit(alias.type, lvalue, rvalue) + + def visitEnum(self, enum, lvalue, rvalue): + pass + + def visitBitmask(self, bitmask, lvalue, rvalue): + pass + + def visitArray(self, array, lvalue, rvalue): + print ' const trace::Array *__a%s = dynamic_cast(&%s);' % (array.tag, rvalue) + print ' if (__a%s) {' % (array.tag) + length = '__a%s->values.size()' % array.tag + index = '__j' + array.tag + print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length) + try: + self.visit(array.type, '%s[%s]' % (lvalue, index), '*__a%s->values[%s]' % (array.tag, index)) + finally: + print ' }' + print ' }' + + def visitPointer(self, pointer, lvalue, rvalue): + print ' const trace::Array *__a%s = dynamic_cast(&%s);' % (pointer.tag, rvalue) + print ' if (__a%s) {' % (pointer.tag) + try: + self.visit(pointer.type, '%s[0]' % (lvalue,), '*__a%s->values[0]' % (pointer.tag,)) + finally: + print ' }' + + def visitIntPointer(self, pointer, lvalue, rvalue): + pass + + def visitObjPointer(self, pointer, lvalue, rvalue): + print r' _obj_map[(%s).toUIntPtr()] = %s;' % (rvalue, lvalue) + + def visitLinearPointer(self, pointer, lvalue, rvalue): + assert pointer.size is not None + if pointer.size is not None: + print r' retrace::addRegion((%s).toUIntPtr(), %s, %s);' % (rvalue, lvalue, pointer.size) + + def visitReference(self, reference, lvalue, rvalue): + pass + + def visitHandle(self, handle, lvalue, rvalue): + print ' %s __orig_result;' % handle.type + OpaqueValueDeserializer().visit(handle.type, '__orig_result', rvalue); + if handle.range is None: + rvalue = "__orig_result" + entry = lookupHandle(handle, rvalue) + print " %s = %s;" % (entry, lvalue) + print ' if (retrace::verbosity >= 2) {' + print ' std::cout << "{handle.name} " << {rvalue} << " -> " << {lvalue} << "\\n";'.format(**locals()) + print ' }' + else: + i = '__h' + handle.tag + lvalue = "%s + %s" % (lvalue, i) + rvalue = "__orig_result + %s" % (i,) + entry = lookupHandle(handle, rvalue) + print ' for ({handle.type} {i} = 0; {i} < {handle.range}; ++{i}) {{'.format(**locals()) + print ' {entry} = {lvalue};'.format(**locals()) + print ' if (retrace::verbosity >= 2) {' + print ' std::cout << "{handle.name} " << ({rvalue}) << " -> " << ({lvalue}) << "\\n";'.format(**locals()) + print ' }' + print ' }' + + def visitBlob(self, blob, lvalue, rvalue): + pass + + def visitString(self, string, lvalue, rvalue): + pass + + seq = 0 + + def visitStruct(self, struct, lvalue, rvalue): + tmp = '__s_' + struct.tag + '_' + str(self.seq) + self.seq += 1 + + print ' const trace::Struct *%s = dynamic_cast(&%s);' % (tmp, rvalue) + print ' assert(%s);' % (tmp,) + print ' (void)%s;' % (tmp,) + for i in range(len(struct.members)): + member_type, member_name = struct.members[i] + self.visit(member_type, '%s.%s' % (lvalue, member_name), '*%s->members[%s]' % (tmp, i)) + + def visitPolymorphic(self, polymorphic, lvalue, rvalue): + self.visit(polymorphic.defaultType, lvalue, rvalue) + + def visitOpaque(self, opaque, lvalue, rvalue): + pass + + +class Retracer: + + def retraceFunction(self, function): + print 'static void retrace_%s(trace::Call &call) {' % function.name + self.retraceFunctionBody(function) + print '}' + print + + def retraceInterfaceMethod(self, interface, method): + print 'static void retrace_%s__%s(trace::Call &call) {' % (interface.name, method.name) + self.retraceInterfaceMethodBody(interface, method) + print '}' + print + + def retraceFunctionBody(self, function): + assert function.sideeffects + + self.deserializeArgs(function) + + self.invokeFunction(function) + + self.swizzleValues(function) + + def retraceInterfaceMethodBody(self, interface, method): + assert method.sideeffects + + self.deserializeThisPointer(interface) + + self.deserializeArgs(method) + + self.invokeInterfaceMethod(interface, method) + + self.swizzleValues(method) + + def deserializeThisPointer(self, interface): + print r' %s *_this;' % (interface.name,) + print r' _this = static_cast<%s *>(_obj_map[call.arg(0).toUIntPtr()]);' % (interface.name,) + print r' if (!_this) {' + print r' retrace::warning(call) << "NULL this pointer\n";' + print r' return;' + print r' }' + + def deserializeArgs(self, function): + print ' retrace::ScopedAllocator _allocator;' + print ' (void)_allocator;' + success = True + for arg in function.args: + arg_type = MutableRebuilder().visit(arg.type) + print ' %s %s;' % (arg_type, arg.name) + rvalue = 'call.arg(%u)' % (arg.index,) + lvalue = arg.name + try: + self.extractArg(function, arg, arg_type, lvalue, rvalue) + except UnsupportedType: + success = False + print ' memset(&%s, 0, sizeof %s); // FIXME' % (arg.name, arg.name) + print + + if not success: + print ' if (1) {' + self.failFunction(function) + if function.name[-1].islower(): + sys.stderr.write('warning: unsupported %s call\n' % function.name) + print ' }' + + def swizzleValues(self, function): + for arg in function.args: + if arg.output: + arg_type = MutableRebuilder().visit(arg.type) + rvalue = 'call.arg(%u)' % (arg.index,) + lvalue = arg.name + try: + self.regiterSwizzledValue(arg_type, lvalue, rvalue) + except UnsupportedType: + print ' // XXX: %s' % arg.name + if function.type is not stdapi.Void: + rvalue = '*call.ret' + lvalue = '__result' + try: + self.regiterSwizzledValue(function.type, lvalue, rvalue) + except UnsupportedType: + raise + print ' // XXX: result' + + def failFunction(self, function): + print ' if (retrace::verbosity >= 0) {' + print ' retrace::unsupported(call);' + print ' }' + print ' return;' + + def extractArg(self, function, arg, arg_type, lvalue, rvalue): + ValueAllocator().visit(arg_type, lvalue, rvalue) + if arg.input: + ValueDeserializer().visit(arg_type, lvalue, rvalue) + + def extractOpaqueArg(self, function, arg, arg_type, lvalue, rvalue): + try: + ValueAllocator().visit(arg_type, lvalue, rvalue) + except UnsupportedType: + pass + OpaqueValueDeserializer().visit(arg_type, lvalue, rvalue) + + def regiterSwizzledValue(self, type, lvalue, rvalue): + visitor = SwizzledValueRegistrator() + visitor.visit(type, lvalue, rvalue) + + def invokeFunction(self, function): + arg_names = ", ".join(function.argNames()) + if function.type is not stdapi.Void: + print ' %s __result;' % (function.type) + print ' __result = %s(%s);' % (function.name, arg_names) + print ' (void)__result;' + else: + print ' %s(%s);' % (function.name, arg_names) + + def invokeInterfaceMethod(self, interface, method): + arg_names = ", ".join(method.argNames()) + if method.type is not stdapi.Void: + print ' %s __result;' % (method.type) + print ' __result = _this->%s(%s);' % (method.name, arg_names) + print ' (void)__result;' + else: + print ' _this->%s(%s);' % (method.name, arg_names) + + def filterFunction(self, function): + return True + + table_name = 'retrace::callbacks' + + def retraceApi(self, api): + + print '#include "os_time.hpp"' + print '#include "trace_parser.hpp"' + print '#include "retrace.hpp"' + print + + types = api.getAllTypes() + handles = [type for type in types if isinstance(type, stdapi.Handle)] + handle_names = set() + for handle in handles: + if handle.name not in handle_names: + if handle.key is None: + print 'static retrace::map<%s> __%s_map;' % (handle.type, handle.name) + else: + key_name, key_type = handle.key + print 'static std::map<%s, retrace::map<%s> > __%s_map;' % (key_type, handle.type, handle.name) + handle_names.add(handle.name) + print + + print 'static std::map _obj_map;' + print + + functions = filter(self.filterFunction, api.functions) + for function in functions: + if function.sideeffects: + self.retraceFunction(function) + interfaces = api.getAllInterfaces() + for interface in interfaces: + for method in interface.iterMethods(): + if method.sideeffects: + self.retraceInterfaceMethod(interface, method) + + print 'const retrace::Entry %s[] = {' % self.table_name + for function in functions: + if function.sideeffects: + print ' {"%s", &retrace_%s},' % (function.name, function.name) + else: + print ' {"%s", &retrace::ignore},' % (function.name,) + for interface in interfaces: + for method in interface.iterMethods(): + if method.sideeffects: + print ' {"%s::%s", &retrace_%s__%s},' % (interface.name, method.name, interface.name, method.name) + else: + print ' {"%s::%s", &retrace::ignore},' % (interface.name, method.name) + print ' {NULL, NULL}' + print '};' + print + diff --git a/retrace/retrace_stdc.cpp b/retrace/retrace_stdc.cpp new file mode 100644 index 0000000..e6b108b --- /dev/null +++ b/retrace/retrace_stdc.cpp @@ -0,0 +1,280 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * 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. + * + **************************************************************************/ + + +#include + +#include + + + +#include "trace_parser.hpp" +#include "retrace.hpp" + + +namespace retrace { + +struct Region +{ + void *buffer; + unsigned long long size; +}; + +typedef std::map RegionMap; +static RegionMap regionMap; + + +static inline bool +contains(RegionMap::iterator &it, unsigned long long address) { + return it->first <= address && (it->first + it->second.size) > address; +} + + +static inline bool +intersects(RegionMap::iterator &it, unsigned long long start, unsigned long long size) { + unsigned long it_start = it->first; + unsigned long it_stop = it->first + it->second.size; + unsigned long stop = start + size; + return it_start < stop && start < it_stop; +} + + +// Iterator to the first region that contains the address, or the first after +static RegionMap::iterator +lowerBound(unsigned long long address) { + RegionMap::iterator it = regionMap.lower_bound(address); + + while (it != regionMap.begin()) { + RegionMap::iterator pred = it; + --pred; + if (contains(pred, address)) { + it = pred; + } else { + break; + } + } + + return it; +} + +// Iterator to the first region that starts after the address +static RegionMap::iterator +upperBound(unsigned long long address) { + RegionMap::iterator it = regionMap.upper_bound(address); + + return it; +} + +void +addRegion(unsigned long long address, void *buffer, unsigned long long size) +{ + if (retrace::verbosity >= 2) { + std::cout + << "region " + << std::hex + << "0x" << address << "-0x" << (address + size) + << " -> " + << "0x" << (uintptr_t)buffer << "-0x" << ((uintptr_t)buffer + size) + << std::dec + << "\n"; + } + + if (!address) { + // Ignore NULL pointer + assert(!buffer); + return; + } + +#ifndef NDEBUG + RegionMap::iterator start = lowerBound(address); + RegionMap::iterator stop = upperBound(address + size); + if (0) { + // Forget all regions that intersect this new one. + regionMap.erase(start, stop); + } else { + for (RegionMap::iterator it = start; it != stop; ++it) { + std::cerr << std::hex << "warning: " + "region 0x" << address << "-0x" << (address + size) << " " + "intersects existing region 0x" << it->first << "-0x" << (it->first + it->second.size) << "\n" << std::dec; + assert(intersects(it, address, size)); + } + } +#endif + + assert(buffer); + + Region region; + region.buffer = buffer; + region.size = size; + + regionMap[address] = region; +} + +static RegionMap::iterator +lookupRegion(unsigned long long address) { + RegionMap::iterator it = regionMap.lower_bound(address); + + if (it == regionMap.end() || + it->first > address) { + if (it == regionMap.begin()) { + return regionMap.end(); + } else { + --it; + } + } + + assert(contains(it, address)); + return it; +} + +void +delRegion(unsigned long long address) { + RegionMap::iterator it = lookupRegion(address); + if (it != regionMap.end()) { + regionMap.erase(it); + } else { + assert(0); + } +} + + +void +delRegionByPointer(void *ptr) { + for (RegionMap::iterator it = regionMap.begin(); it != regionMap.end(); ++it) { + if (it->second.buffer == ptr) { + regionMap.erase(it); + return; + } + } + assert(0); +} + +void * +lookupAddress(unsigned long long address) { + RegionMap::iterator it = lookupRegion(address); + if (it != regionMap.end()) { + unsigned long long offset = address - it->first; + assert(offset < it->second.size); + void *addr = (char *)it->second.buffer + offset; + + if (retrace::verbosity >= 2) { + std::cout + << "region " + << std::hex + << "0x" << address + << " <- " + << "0x" << (uintptr_t)addr + << std::dec + << "\n"; + } + + return addr; + } + + if (address >= 0x00400000) { + std::cerr << "warning: could not translate address 0x" << std::hex << address << std::dec << "\n"; + } + + return (void *)(uintptr_t)address; +} + + +class Translator : protected trace::Visitor +{ +protected: + bool bind; + + void *result; + + void visit(trace::Null *) { + result = NULL; + } + + void visit(trace::Blob *blob) { + result = blob->toPointer(bind); + } + + void visit(trace::Pointer *p) { + result = lookupAddress(p->value); + } + +public: + Translator(bool _bind) : + bind(_bind), + result(NULL) + {} + + void * operator() (trace::Value *node) { + _visit(node); + return result; + } +}; + + +void * +toPointer(trace::Value &value, bool bind) { + return Translator(bind) (&value); +} + + +static void retrace_malloc(trace::Call &call) { + size_t size = call.arg(0).toUInt(); + unsigned long long address = call.ret->toUIntPtr(); + + if (!address) { + return; + } + + void *buffer = malloc(size); + if (!buffer) { + std::cerr << "error: failed to allocated " << size << " bytes."; + return; + } + + addRegion(address, buffer, size); +} + + +static void retrace_memcpy(trace::Call &call) { + void * dest = toPointer(call.arg(0)); + void * src = toPointer(call.arg(1)); + size_t n = call.arg(2).toUInt(); + + if (!dest || !src || !n) { + return; + } + + memcpy(dest, src, n); +} + + +const retrace::Entry stdc_callbacks[] = { + {"malloc", &retrace_malloc}, + {"memcpy", &retrace_memcpy}, + {NULL, NULL} +}; + + +} /* retrace */ diff --git a/retrace_stdc.cpp b/retrace_stdc.cpp deleted file mode 100644 index e6b108b..0000000 --- a/retrace_stdc.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * 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. - * - **************************************************************************/ - - -#include - -#include - - - -#include "trace_parser.hpp" -#include "retrace.hpp" - - -namespace retrace { - -struct Region -{ - void *buffer; - unsigned long long size; -}; - -typedef std::map RegionMap; -static RegionMap regionMap; - - -static inline bool -contains(RegionMap::iterator &it, unsigned long long address) { - return it->first <= address && (it->first + it->second.size) > address; -} - - -static inline bool -intersects(RegionMap::iterator &it, unsigned long long start, unsigned long long size) { - unsigned long it_start = it->first; - unsigned long it_stop = it->first + it->second.size; - unsigned long stop = start + size; - return it_start < stop && start < it_stop; -} - - -// Iterator to the first region that contains the address, or the first after -static RegionMap::iterator -lowerBound(unsigned long long address) { - RegionMap::iterator it = regionMap.lower_bound(address); - - while (it != regionMap.begin()) { - RegionMap::iterator pred = it; - --pred; - if (contains(pred, address)) { - it = pred; - } else { - break; - } - } - - return it; -} - -// Iterator to the first region that starts after the address -static RegionMap::iterator -upperBound(unsigned long long address) { - RegionMap::iterator it = regionMap.upper_bound(address); - - return it; -} - -void -addRegion(unsigned long long address, void *buffer, unsigned long long size) -{ - if (retrace::verbosity >= 2) { - std::cout - << "region " - << std::hex - << "0x" << address << "-0x" << (address + size) - << " -> " - << "0x" << (uintptr_t)buffer << "-0x" << ((uintptr_t)buffer + size) - << std::dec - << "\n"; - } - - if (!address) { - // Ignore NULL pointer - assert(!buffer); - return; - } - -#ifndef NDEBUG - RegionMap::iterator start = lowerBound(address); - RegionMap::iterator stop = upperBound(address + size); - if (0) { - // Forget all regions that intersect this new one. - regionMap.erase(start, stop); - } else { - for (RegionMap::iterator it = start; it != stop; ++it) { - std::cerr << std::hex << "warning: " - "region 0x" << address << "-0x" << (address + size) << " " - "intersects existing region 0x" << it->first << "-0x" << (it->first + it->second.size) << "\n" << std::dec; - assert(intersects(it, address, size)); - } - } -#endif - - assert(buffer); - - Region region; - region.buffer = buffer; - region.size = size; - - regionMap[address] = region; -} - -static RegionMap::iterator -lookupRegion(unsigned long long address) { - RegionMap::iterator it = regionMap.lower_bound(address); - - if (it == regionMap.end() || - it->first > address) { - if (it == regionMap.begin()) { - return regionMap.end(); - } else { - --it; - } - } - - assert(contains(it, address)); - return it; -} - -void -delRegion(unsigned long long address) { - RegionMap::iterator it = lookupRegion(address); - if (it != regionMap.end()) { - regionMap.erase(it); - } else { - assert(0); - } -} - - -void -delRegionByPointer(void *ptr) { - for (RegionMap::iterator it = regionMap.begin(); it != regionMap.end(); ++it) { - if (it->second.buffer == ptr) { - regionMap.erase(it); - return; - } - } - assert(0); -} - -void * -lookupAddress(unsigned long long address) { - RegionMap::iterator it = lookupRegion(address); - if (it != regionMap.end()) { - unsigned long long offset = address - it->first; - assert(offset < it->second.size); - void *addr = (char *)it->second.buffer + offset; - - if (retrace::verbosity >= 2) { - std::cout - << "region " - << std::hex - << "0x" << address - << " <- " - << "0x" << (uintptr_t)addr - << std::dec - << "\n"; - } - - return addr; - } - - if (address >= 0x00400000) { - std::cerr << "warning: could not translate address 0x" << std::hex << address << std::dec << "\n"; - } - - return (void *)(uintptr_t)address; -} - - -class Translator : protected trace::Visitor -{ -protected: - bool bind; - - void *result; - - void visit(trace::Null *) { - result = NULL; - } - - void visit(trace::Blob *blob) { - result = blob->toPointer(bind); - } - - void visit(trace::Pointer *p) { - result = lookupAddress(p->value); - } - -public: - Translator(bool _bind) : - bind(_bind), - result(NULL) - {} - - void * operator() (trace::Value *node) { - _visit(node); - return result; - } -}; - - -void * -toPointer(trace::Value &value, bool bind) { - return Translator(bind) (&value); -} - - -static void retrace_malloc(trace::Call &call) { - size_t size = call.arg(0).toUInt(); - unsigned long long address = call.ret->toUIntPtr(); - - if (!address) { - return; - } - - void *buffer = malloc(size); - if (!buffer) { - std::cerr << "error: failed to allocated " << size << " bytes."; - return; - } - - addRegion(address, buffer, size); -} - - -static void retrace_memcpy(trace::Call &call) { - void * dest = toPointer(call.arg(0)); - void * src = toPointer(call.arg(1)); - size_t n = call.arg(2).toUInt(); - - if (!dest || !src || !n) { - return; - } - - memcpy(dest, src, n); -} - - -const retrace::Entry stdc_callbacks[] = { - {"malloc", &retrace_malloc}, - {"memcpy", &retrace_memcpy}, - {NULL, NULL} -}; - - -} /* retrace */