eglretrace
glproc.hpp
glretrace
-glretrace_gl.cpp
-glstate_params.cpp
install_manifest.txt
qapitrace
traces
##############################################################################
# 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
+++ /dev/null
-/**************************************************************************
- *
- * 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_ */
+++ /dev/null
-##########################################################################
-#
-# 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 <string.h>
-
-#include <iostream>
-
-#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)
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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_ */
+++ /dev/null
-##########################################################################
-#
-# 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 <string.h>
-
-#include "glproc.hpp"
-#include "glretrace.hpp"
-#include "glstate.hpp"
-
-
-'''
- api = glapi.glapi
- api.addApi(glesapi.glesapi)
- retracer = GlRetracer()
- retracer.retraceApi(api)
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-
-#include "glproc.hpp"
-#include "retrace.hpp"
-#include "glretrace.hpp"
-
-
-using namespace glretrace;
-
-
-typedef std::map<unsigned long long, glws::Drawable *> DrawableMap;
-typedef std::map<unsigned long long, glws::Context *> 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},
-};
-
+++ /dev/null
-/**************************************************************************
- *
- * 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<unsigned long long, glws::Drawable *> DrawableMap;
-typedef std::map<unsigned long long, glws::Context *> ContextMap;
-typedef std::map<unsigned long long, glws::Profile> 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<trace::Array *>(&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},
-};
+++ /dev/null
-/**************************************************************************
- *
- * 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<unsigned long, glws::Drawable *> DrawableMap;
-typedef std::map<unsigned long long, glws::Context *> 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},
-};
-
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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<unsigned long long, glws::Drawable *> DrawableMap;
-typedef std::map<unsigned long long, glws::Context *> 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}
-};
-
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-
-#include <algorithm>
-#include <iostream>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <ostream>
-
-#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_ */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-
-#include <algorithm>
-#include <iostream>
-
-#include "image.hpp"
-#include "json.hpp"
-#include "glproc.hpp"
-#include "glsize.hpp"
-#include "glstate.hpp"
-#include "glstate_internal.hpp"
-
-
-#ifdef __linux__
-#include <dlfcn.h>
-#endif
-
-#ifdef __APPLE__
-
-#include <Carbon/Carbon.h>
-
-#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 = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
- json.writeBase64(pngBuffer, pngBufferSize);
- free(pngBuffer);
- json.endMember(); // __data__
-
- delete [] pixels;
- json.endObject();
-}
-
-
-static inline GLuint
-downsampledFramebuffer(Context &context,
- GLuint oldFbo, GLint drawbuffer,
- GLint colorRb, GLint depthRb, GLint stencilRb,
- GLuint *rbs, GLint *numRbs)
-{
- GLuint fbo;
-
-
- *numRbs = 0;
-
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-
- {
- // color buffer
- ImageDesc desc;
- glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
- getBoundRenderbufferDesc(context, desc);
-
- glGenRenderbuffers(1, &rbs[*numRbs]);
- glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
- glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
- GL_RENDERBUFFER, rbs[*numRbs]);
-
- glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glDrawBuffer(drawbuffer);
- glReadBuffer(drawbuffer);
- glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- ++*numRbs;
- }
-
- if (stencilRb == depthRb && stencilRb) {
- //combined depth and stencil buffer
- ImageDesc desc;
- glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
- getBoundRenderbufferDesc(context, desc);
-
- glGenRenderbuffers(1, &rbs[*numRbs]);
- glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
- glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, rbs[*numRbs]);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
- GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- ++*numRbs;
- } else {
- if (depthRb) {
- ImageDesc desc;
- glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
- getBoundRenderbufferDesc(context, desc);
-
- glGenRenderbuffers(1, &rbs[*numRbs]);
- glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
- glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER,
- GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, rbs[*numRbs]);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glDrawBuffer(GL_DEPTH_ATTACHMENT);
- glReadBuffer(GL_DEPTH_ATTACHMENT);
- glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
- GL_DEPTH_BUFFER_BIT, GL_NEAREST);
- ++*numRbs;
- }
- if (stencilRb) {
- ImageDesc desc;
- glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
- getBoundRenderbufferDesc(context, desc);
-
- glGenRenderbuffers(1, &rbs[*numRbs]);
- glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
- glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER,
- GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, rbs[*numRbs]);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- glDrawBuffer(GL_STENCIL_ATTACHMENT);
- glReadBuffer(GL_STENCIL_ATTACHMENT);
- glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
- GL_STENCIL_BUFFER_BIT, GL_NEAREST);
- ++*numRbs;
- }
- }
-
- return fbo;
-}
-
-
-/**
- * Dump images of current draw drawable/window.
- */
-static void
-dumpDrawableImages(JSONWriter &json, Context &context)
-{
- GLint width, height;
-
- if (!getDrawableBounds(&width, &height)) {
- return;
- }
-
- GLint draw_buffer = GL_NONE;
- if (context.ES) {
- draw_buffer = GL_BACK;
- } else {
- glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
- glReadBuffer(draw_buffer);
- }
-
- if (draw_buffer != GL_NONE) {
- GLint read_buffer = GL_NONE;
- if (!context.ES) {
- glGetIntegerv(GL_READ_BUFFER, &read_buffer);
- }
-
- GLint alpha_bits = 0;
-#if 0
- // XXX: Ignore alpha until we are able to match the traced visual
- glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
-#endif
- GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
- json.beginMember(enumToString(draw_buffer));
- dumpReadBufferImage(json, width, height, format);
- json.endMember();
-
- if (!context.ES) {
- glReadBuffer(read_buffer);
- }
- }
-
- if (!context.ES) {
- GLint depth_bits = 0;
- glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
- if (depth_bits) {
- json.beginMember("GL_DEPTH_COMPONENT");
- dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
- json.endMember();
- }
-
- GLint stencil_bits = 0;
- glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
- if (stencil_bits) {
- json.beginMember("GL_STENCIL_INDEX");
- dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
- json.endMember();
- }
- }
-}
-
-
-/**
- * Dump the specified framebuffer attachment.
- *
- * In the case of a color attachment, it assumes it is already bound for read.
- */
-static void
-dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
-{
- ImageDesc desc;
- if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
- return;
- }
-
- json.beginMember(enumToString(attachment));
- dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
- json.endMember();
-}
-
-
-static void
-dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
-{
- GLint read_framebuffer = 0;
- glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
-
- GLint read_buffer = GL_NONE;
- glGetIntegerv(GL_READ_BUFFER, &read_buffer);
-
- GLint max_draw_buffers = 1;
- glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
- GLint max_color_attachments = 0;
- glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
-
- for (GLint i = 0; i < max_draw_buffers; ++i) {
- GLint draw_buffer = GL_NONE;
- glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
- if (draw_buffer != GL_NONE) {
- glReadBuffer(draw_buffer);
- GLint attachment;
- if (draw_buffer >= 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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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_ */
+++ /dev/null
-##########################################################################
-#
-# 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 <string.h>'
- 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()
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-
-#include <algorithm>
-#include <iostream>
-#include <map>
-#include <sstream>
-
-#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<std::string, std::string> 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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <vector>
-
-
-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<T> 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_ */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <stdlib.h>
-#include <iostream>
-
-#include <Cocoa/Cocoa.h>
-
-#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<const CocoaVisual *>(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<NSOpenGLPixelFormatAttribute> 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<const CocoaVisual *>(visual)->pixelFormat;
- NSOpenGLContext *share_context = nil;
- NSOpenGLContext *context;
-
- if (profile != PROFILE_COMPAT &&
- profile != PROFILE_CORE) {
- return nil;
- }
-
- if (shareContext) {
- share_context = static_cast<CocoaContext*>(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<CocoaDrawable *>(drawable);
- CocoaContext *cocoaContext = static_cast<CocoaContext *>(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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <stdlib.h>
-
-#include <iostream>
-
-#include <dlfcn.h>
-
-#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<const EglVisual *>(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<const EglVisual *>(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<EGLint> 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<const EglVisual *>(_visual);
- EGLContext share_context = EGL_NO_CONTEXT;
- EGLContext context;
- Attributes<EGLint> attribs;
-
- if (shareContext) {
- share_context = static_cast<EglContext*>(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<EglDrawable *>(drawable);
- EglContext *eglContext = static_cast<EglContext *>(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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <stdlib.h>
-
-#include <iostream>
-
-#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<const GlxVisual *>(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<int> 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<int> 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<const GlxVisual *>(_visual);
- GLXContext share_context = NULL;
- GLXContext context;
-
- if (shareContext) {
- share_context = static_cast<GlxContext*>(shareContext)->context;
- }
-
- if (glxVersion >= 0x0104 && has_GLX_ARB_create_context) {
- Attributes<int> 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<GlxDrawable *>(drawable);
- GlxContext *glxContext = static_cast<GlxContext *>(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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <iostream>
-
-#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<WglContext *>(shareContext));
-}
-
-bool
-makeCurrent(Drawable *drawable, Context *context)
-{
- if (!drawable || !context) {
- return wglMakeCurrent(NULL, NULL);
- } else {
- WglDrawable *wglDrawable = static_cast<WglDrawable *>(drawable);
- WglContext *wglContext = static_cast<WglContext *>(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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <string.h>
-#include <iostream>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <list>
-#include <map>
-#include <ostream>
-
-#include "trace_model.hpp"
-#include "trace_parser.hpp"
-
-
-namespace retrace {
-
-
-extern trace::Parser parser;
-
-
-/**
- * Handle map.
- *
- * It is just like a regular std::map<T, T> 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 T>
-class map
-{
-private:
- typedef std::map<T, T> 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<uintptr_t *>(malloc(sizeof(uintptr_t) + size));
- if (!buf) {
- return NULL;
- }
-
- *buf = next;
- next = reinterpret_cast<uintptr_t>(buf);
- assert((next & 1) == 0);
-
- return static_cast<void *>(&buf[1]);
- }
-
- template< class T >
- inline T *
- alloc(size_t n = 1) {
- return static_cast<T *>(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<const trace::Array *>(value);
- if (array) {
- return alloc<T>(array->size());
- }
- const trace::Null *null = dynamic_cast<const trace::Null *>(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<uintptr_t *>(ptr)[-1] |= 1;
- }
- }
-
- inline
- ~ScopedAllocator() {
- while (next) {
- uintptr_t temp = *reinterpret_cast<uintptr_t *>(next);
-
- bool bind = temp & 1;
- temp &= ~1;
-
- if (!bind) {
- free(reinterpret_cast<void *>(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<const char *, Callback, stringComparer> Map;
- Map map;
-
- std::vector<Callback> 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_ */
+++ /dev/null
-##########################################################################
-#
-# 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<const trace::Array *>(&%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<const trace::Array *>(&%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<const trace::Struct *>(&%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<const trace::Array *>(&%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<const trace::Array *>(&%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<const trace::Struct *>(&%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<unsigned long long, void *> _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
-
--- /dev/null
+glretrace_gl.cpp
+glstate_params.cpp
--- /dev/null
+##############################################################################
+# 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 ()
+
--- /dev/null
+/**************************************************************************
+ *
+ * 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_ */
--- /dev/null
+##########################################################################
+#
+# 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 <string.h>
+
+#include <iostream>
+
+#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)
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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_ */
--- /dev/null
+##########################################################################
+#
+# 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 <string.h>
+
+#include "glproc.hpp"
+#include "glretrace.hpp"
+#include "glstate.hpp"
+
+
+'''
+ api = glapi.glapi
+ api.addApi(glesapi.glesapi)
+ retracer = GlRetracer()
+ retracer.retraceApi(api)
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+
+#include "glproc.hpp"
+#include "retrace.hpp"
+#include "glretrace.hpp"
+
+
+using namespace glretrace;
+
+
+typedef std::map<unsigned long long, glws::Drawable *> DrawableMap;
+typedef std::map<unsigned long long, glws::Context *> 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},
+};
+
--- /dev/null
+/**************************************************************************
+ *
+ * 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<unsigned long long, glws::Drawable *> DrawableMap;
+typedef std::map<unsigned long long, glws::Context *> ContextMap;
+typedef std::map<unsigned long long, glws::Profile> 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<trace::Array *>(&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},
+};
--- /dev/null
+/**************************************************************************
+ *
+ * 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<unsigned long, glws::Drawable *> DrawableMap;
+typedef std::map<unsigned long long, glws::Context *> 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},
+};
+
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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<unsigned long long, glws::Drawable *> DrawableMap;
+typedef std::map<unsigned long long, glws::Context *> 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}
+};
+
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+
+#include <algorithm>
+#include <iostream>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <ostream>
+
+#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_ */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+
+#include <algorithm>
+#include <iostream>
+
+#include "image.hpp"
+#include "json.hpp"
+#include "glproc.hpp"
+#include "glsize.hpp"
+#include "glstate.hpp"
+#include "glstate_internal.hpp"
+
+
+#ifdef __linux__
+#include <dlfcn.h>
+#endif
+
+#ifdef __APPLE__
+
+#include <Carbon/Carbon.h>
+
+#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 = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
+ json.writeBase64(pngBuffer, pngBufferSize);
+ free(pngBuffer);
+ json.endMember(); // __data__
+
+ delete [] pixels;
+ json.endObject();
+}
+
+
+static inline GLuint
+downsampledFramebuffer(Context &context,
+ GLuint oldFbo, GLint drawbuffer,
+ GLint colorRb, GLint depthRb, GLint stencilRb,
+ GLuint *rbs, GLint *numRbs)
+{
+ GLuint fbo;
+
+
+ *numRbs = 0;
+
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ {
+ // color buffer
+ ImageDesc desc;
+ glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
+ getBoundRenderbufferDesc(context, desc);
+
+ glGenRenderbuffers(1, &rbs[*numRbs]);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+ glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
+ GL_RENDERBUFFER, rbs[*numRbs]);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ glDrawBuffer(drawbuffer);
+ glReadBuffer(drawbuffer);
+ glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ ++*numRbs;
+ }
+
+ if (stencilRb == depthRb && stencilRb) {
+ //combined depth and stencil buffer
+ ImageDesc desc;
+ glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
+ getBoundRenderbufferDesc(context, desc);
+
+ glGenRenderbuffers(1, &rbs[*numRbs]);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+ glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, rbs[*numRbs]);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+ GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ ++*numRbs;
+ } else {
+ if (depthRb) {
+ ImageDesc desc;
+ glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
+ getBoundRenderbufferDesc(context, desc);
+
+ glGenRenderbuffers(1, &rbs[*numRbs]);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+ glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, rbs[*numRbs]);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ glDrawBuffer(GL_DEPTH_ATTACHMENT);
+ glReadBuffer(GL_DEPTH_ATTACHMENT);
+ glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+ GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ ++*numRbs;
+ }
+ if (stencilRb) {
+ ImageDesc desc;
+ glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
+ getBoundRenderbufferDesc(context, desc);
+
+ glGenRenderbuffers(1, &rbs[*numRbs]);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+ glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, rbs[*numRbs]);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ glDrawBuffer(GL_STENCIL_ATTACHMENT);
+ glReadBuffer(GL_STENCIL_ATTACHMENT);
+ glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+ GL_STENCIL_BUFFER_BIT, GL_NEAREST);
+ ++*numRbs;
+ }
+ }
+
+ return fbo;
+}
+
+
+/**
+ * Dump images of current draw drawable/window.
+ */
+static void
+dumpDrawableImages(JSONWriter &json, Context &context)
+{
+ GLint width, height;
+
+ if (!getDrawableBounds(&width, &height)) {
+ return;
+ }
+
+ GLint draw_buffer = GL_NONE;
+ if (context.ES) {
+ draw_buffer = GL_BACK;
+ } else {
+ glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
+ glReadBuffer(draw_buffer);
+ }
+
+ if (draw_buffer != GL_NONE) {
+ GLint read_buffer = GL_NONE;
+ if (!context.ES) {
+ glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+ }
+
+ GLint alpha_bits = 0;
+#if 0
+ // XXX: Ignore alpha until we are able to match the traced visual
+ glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
+#endif
+ GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
+ json.beginMember(enumToString(draw_buffer));
+ dumpReadBufferImage(json, width, height, format);
+ json.endMember();
+
+ if (!context.ES) {
+ glReadBuffer(read_buffer);
+ }
+ }
+
+ if (!context.ES) {
+ GLint depth_bits = 0;
+ glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
+ if (depth_bits) {
+ json.beginMember("GL_DEPTH_COMPONENT");
+ dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
+ json.endMember();
+ }
+
+ GLint stencil_bits = 0;
+ glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+ if (stencil_bits) {
+ json.beginMember("GL_STENCIL_INDEX");
+ dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
+ json.endMember();
+ }
+ }
+}
+
+
+/**
+ * Dump the specified framebuffer attachment.
+ *
+ * In the case of a color attachment, it assumes it is already bound for read.
+ */
+static void
+dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
+{
+ ImageDesc desc;
+ if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
+ return;
+ }
+
+ json.beginMember(enumToString(attachment));
+ dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
+ json.endMember();
+}
+
+
+static void
+dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
+{
+ GLint read_framebuffer = 0;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
+
+ GLint read_buffer = GL_NONE;
+ glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+
+ GLint max_draw_buffers = 1;
+ glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
+ GLint max_color_attachments = 0;
+ glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
+
+ for (GLint i = 0; i < max_draw_buffers; ++i) {
+ GLint draw_buffer = GL_NONE;
+ glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
+ if (draw_buffer != GL_NONE) {
+ glReadBuffer(draw_buffer);
+ GLint attachment;
+ if (draw_buffer >= 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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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_ */
--- /dev/null
+##########################################################################
+#
+# 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 <string.h>'
+ 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()
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <sstream>
+
+#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<std::string, std::string> 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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <vector>
+
+
+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<T> 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_ */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <stdlib.h>
+#include <iostream>
+
+#include <Cocoa/Cocoa.h>
+
+#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<const CocoaVisual *>(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<NSOpenGLPixelFormatAttribute> 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<const CocoaVisual *>(visual)->pixelFormat;
+ NSOpenGLContext *share_context = nil;
+ NSOpenGLContext *context;
+
+ if (profile != PROFILE_COMPAT &&
+ profile != PROFILE_CORE) {
+ return nil;
+ }
+
+ if (shareContext) {
+ share_context = static_cast<CocoaContext*>(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<CocoaDrawable *>(drawable);
+ CocoaContext *cocoaContext = static_cast<CocoaContext *>(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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+
+#include <iostream>
+
+#include <dlfcn.h>
+
+#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<const EglVisual *>(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<const EglVisual *>(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<EGLint> 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<const EglVisual *>(_visual);
+ EGLContext share_context = EGL_NO_CONTEXT;
+ EGLContext context;
+ Attributes<EGLint> attribs;
+
+ if (shareContext) {
+ share_context = static_cast<EglContext*>(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<EglDrawable *>(drawable);
+ EglContext *eglContext = static_cast<EglContext *>(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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+
+#include <iostream>
+
+#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<const GlxVisual *>(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<int> 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<int> 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<const GlxVisual *>(_visual);
+ GLXContext share_context = NULL;
+ GLXContext context;
+
+ if (shareContext) {
+ share_context = static_cast<GlxContext*>(shareContext)->context;
+ }
+
+ if (glxVersion >= 0x0104 && has_GLX_ARB_create_context) {
+ Attributes<int> 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<GlxDrawable *>(drawable);
+ GlxContext *glxContext = static_cast<GlxContext *>(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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <iostream>
+
+#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<WglContext *>(shareContext));
+}
+
+bool
+makeCurrent(Drawable *drawable, Context *context)
+{
+ if (!drawable || !context) {
+ return wglMakeCurrent(NULL, NULL);
+ } else {
+ WglDrawable *wglDrawable = static_cast<WglDrawable *>(drawable);
+ WglContext *wglContext = static_cast<WglContext *>(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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string.h>
+#include <iostream>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <list>
+#include <map>
+#include <ostream>
+
+#include "trace_model.hpp"
+#include "trace_parser.hpp"
+
+
+namespace retrace {
+
+
+extern trace::Parser parser;
+
+
+/**
+ * Handle map.
+ *
+ * It is just like a regular std::map<T, T> 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 T>
+class map
+{
+private:
+ typedef std::map<T, T> 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<uintptr_t *>(malloc(sizeof(uintptr_t) + size));
+ if (!buf) {
+ return NULL;
+ }
+
+ *buf = next;
+ next = reinterpret_cast<uintptr_t>(buf);
+ assert((next & 1) == 0);
+
+ return static_cast<void *>(&buf[1]);
+ }
+
+ template< class T >
+ inline T *
+ alloc(size_t n = 1) {
+ return static_cast<T *>(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<const trace::Array *>(value);
+ if (array) {
+ return alloc<T>(array->size());
+ }
+ const trace::Null *null = dynamic_cast<const trace::Null *>(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<uintptr_t *>(ptr)[-1] |= 1;
+ }
+ }
+
+ inline
+ ~ScopedAllocator() {
+ while (next) {
+ uintptr_t temp = *reinterpret_cast<uintptr_t *>(next);
+
+ bool bind = temp & 1;
+ temp &= ~1;
+
+ if (!bind) {
+ free(reinterpret_cast<void *>(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<const char *, Callback, stringComparer> Map;
+ Map map;
+
+ std::vector<Callback> 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_ */
--- /dev/null
+##########################################################################
+#
+# 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<const trace::Array *>(&%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<const trace::Array *>(&%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<const trace::Struct *>(&%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<const trace::Array *>(&%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<const trace::Array *>(&%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<const trace::Struct *>(&%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<unsigned long long, void *> _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
+
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+
+#include <string.h>
+
+
+
+#include "trace_parser.hpp"
+#include "retrace.hpp"
+
+
+namespace retrace {
+
+struct Region
+{
+ void *buffer;
+ unsigned long long size;
+};
+
+typedef std::map<unsigned long long, Region> 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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-
-#include <string.h>
-
-
-
-#include "trace_parser.hpp"
-#include "retrace.hpp"
-
-
-namespace retrace {
-
-struct Region
-{
- void *buffer;
- unsigned long long size;
-};
-
-typedef std::map<unsigned long long, Region> 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 */