Merge branch 'trunk'
diff --git a/.gitignore b/.gitignore
index ccf845b..73909e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,10 @@
 build
 build32
 dbuild
+rbuild
+vktrace/src/vktrace_extensions/vktracevulkan/codegen_vktrace_utils
+vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/codegen
+vktrace/src/vktrace_extensions/vktracevulkan/vulkan/codegen_utils
 external
 build-android/external
 *.config
diff --git a/BUILD.md b/BUILD.md
deleted file mode 100644
index 3358d13..0000000
--- a/BUILD.md
+++ /dev/null
@@ -1,360 +0,0 @@
-# Build Instructions
-This document contains the instructions for building this repository on Linux and Windows.
-
-This repository does not contain a Vulkan-capable driver.
-Before proceeding, it is strongly recommended that you obtain a Vulkan driver from your graphics hardware vendor
-and install it.
-
-## Contributing
-
-If you intend to contribute, the preferred work flow is for you to develop your contribution
-in a fork of this repo in your GitHub account and then submit a pull request.
-Please see the [CONTRIBUTING](CONTRIBUTING.md) file in this respository for more details.
-
-## Git the Bits
-
-To create your local git repository:
-```
-git clone https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
-```
-
-## Linux Build
-
-The build process uses CMake to generate makefiles for this project.
-The build generates the loader, layers, and tests.
-
-This repo has been built and tested on the two most recent Ubuntu LTS versions.
-It should be straightforward to use it on other Linux distros.
-
-These packages are needed to build this repository:
-```
-sudo apt-get install git cmake build-essential bison libx11-dev libxcb1-dev libxkbcommon-dev libmirclient-dev libwayland-dev
-```
-
-Example debug build (Note that the update\_external\_sources script used below builds external tools into predefined locations. See **Loader and Validation Layer Dependencies** for more information and other options):
-```
-cd Vulkan-LoaderAndValidationLayers  # cd to the root of the cloned git repository
-./update_external_sources.sh
-cmake -H. -Bdbuild -DCMAKE_BUILD_TYPE=Debug
-cd dbuild
-make
-```
-
-If you have installed a Vulkan driver obtained from your graphics hardware vendor, the install process should
-have configured the driver so that the Vulkan loader can find and load it.
-
-If you want to use the loader and layers that you have just built:
-```
-export LD_LIBRARY_PATH=<path to your repository root>/dbuild/loader
-export VK_LAYER_PATH=<path to your repository root>/dbuild/layers
-```
-You can run the `vulkaninfo` application to see which driver, loader and layers are being used.
-
-The `LoaderAndLayerInterface` document in the `loader` folder in this repository is a specification that
-describes both how ICDs and layers should be properly
-packaged, and how developers can point to ICDs and layers within their builds.
-
-### WSI Support Build Options
-By default, the Vulkan Loader and Validation Layers are built with support for all 4 Vulkan-defined WSI display systems, Xcb, Xlib, Wayland, and Mir.  It is recommended to build these modules with support for these
-display systems to maximize their usability across Linux platforms.
-If it is necessary to build these modules without support for one of the display systems, the appropriate CMake option of the form BUILD_WSI_xxx_SUPPORT can be set to OFF.   See the top-level CMakeLists.txt file for more info.
-
-### Linux Install to System Directories
-
-Installing the files resulting from your build to the systems directories is optional since
-environment variables can usually be used instead to locate the binaries.
-There are also risks with interfering with binaries installed by packages.
-If you are certain that you would like to install your binaries to system directories,
-you can proceed with these instructions.
-
-Assuming that you've built the code as described above and the current directory is still `dbuild`,
-you can execute:
-
-```
-sudo make install
-```
-
-This command installs files to:
-
-* `/usr/local/include/vulkan`:  Vulkan include files
-* `/usr/local/lib`:  Vulkan loader and layers shared objects
-* `/usr/local/bin`:  vulkaninfo application
-* `/usr/local/etc/vulkan/explicit_layer.d`:  Layer JSON files
-
-You may need to run `ldconfig` in order to refresh the system loader search cache on some Linux systems.
-
-The list of installed files appears in the build directory in a file named `install_manifest.txt`.
-You can easily remove the installed files with:
-
-```
-cat install_manifest.txt | sudo xargs rm
-```
-
-You can further customize the installation location by setting additional CMake variables
-to override their defaults.
-For example, if you would like to install to `/tmp/build` instead of `/usr/local`, specify:
-
-```
--DCMAKE_INSTALL_PREFIX=/tmp/build
--DDEST_DIR=/tmp/build
-```
-
-on your CMake command line and run `make install` as before.
-The install step places the files in `/tmp/build`.
-
-Using the `CMAKE_INSTALL_PREFIX` to customize the install location also modifies the
-loader search paths to include searching for layers in the specified install location.
-In this example, setting `CMAKE_INSTALL_PREFIX` to `/tmp/build` causes the loader to
-search `/tmp/build/etc/vulkan/explicit_layer.d` and `/tmp/build/share/vulkan/explicit_layer.d`
-for the layer JSON files.
-The loader also searches the "standard" system locations of `/etc/vulkan/explicit_layer.d`
-and `/usr/share/vulkan/explicit_layer.d` after searching the two locations under `/tmp/build`.
-
-You can further customize the installation directories by using the CMake variables
-`CMAKE_INSTALL_SYSCONFDIR` to rename the `etc` directory and `CMAKE_INSTALL_DATADIR`
-to rename the `share` directory.
-
-See the CMake documentation for more details on using these variables
-to further customize your installation.
-
-Also see the `LoaderAndLayerInterface` document in the `loader` folder in this repository for more
-information about loader operation.
-
-Note that some executables in this repository (e.g., `cube`) use the "rpath" linker directive
-to load the Vulkan loader from the build directory, `dbuild` in this example.
-This means that even after installing the loader to the system directories, these executables
-still use the loader from the build directory.
-
-### Linux 32-bit support
-
-Usage of this repository's contents in 32-bit Linux environments is not officially supported.
-However, since this repository is supported on 32-bit Windows, these modules should generally
-work on 32-bit Linux.
-
-Here are some notes for building 32-bit targets on a 64-bit Ubuntu "reference" platform:
-
-If not already installed, install the following 32-bit development libraries:
-
-`gcc-multilib g++-multilib libx11-dev:i386`
-
-This list may vary depending on your distro and which windowing systems you are building for.
-
-Set up your environment for building 32-bit targets:
-
-```
-export CFLAGS=-m32
-export CXXFLAGS=-m32
-export PKG_CONFIG_LIBDIR=/usr/lib/i386-linux-gnu
-```
-
-Again, your PKG_CONFIG configuration may be different, depending on your distro.
-
-If the libraries in the `external` directory have already been built
-for 64-bit targets,
-delete or "clean" this directory and rebuild it with
-the above settings using the `update_external_sources` shell script.
-This is required because the libraries in `external` must be built for
-32-bit in order to be usable by the rest of the components in the repository.
-
-Finally, rebuild the repository using `cmake` and `make`, as explained above.
-
-## Validation Test
-
-The test executables can be found in the dbuild/tests directory.
-Some of the tests that are available:
-- vk\_layer\_validation\_tests: Test Vulkan layers.
-
-There are also a few shell and Python scripts that run test collections (eg,
-`run_all_tests.sh`).
-
-## Linux Demos
-
-Some demos that can be found in the dbuild/demos directory are:
-- vulkaninfo: report GPU properties
-- cube: a textured spinning cube
-- smoke/smoke: A "smoke" test using a more complex Vulkan demo
-
-You can select which WSI subsystem is used to build the demos using a cmake option called DEMOS_WSI_SELECTION.
-Supported options are XCB (default), XLIB, WAYLAND, and MIR.  Note that you must build using the corresponding BUILD_WSI_*_SUPPORT enabled at the base repo level (all SUPPORT options are ON by default).
-For instance, creating a build that will use Xlib to build the demos, your cmake command line might look like:
-
-cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DDEMOS_WSI_SELECTION=XLIB
-
-## Windows System Requirements
-
-Windows 7+ with additional required software packages:
-
-- Microsoft Visual Studio 2013 Professional.  Note: it is possible that lesser/older versions may work, but that has not been tested.
-- [CMake](http://www.cmake.org/download/).  Notes:
-  - Tell the installer to "Add CMake to the system PATH" environment variable.
-- [Python 3](https://www.python.org/downloads).  Notes:
-  - Select to install the optional sub-package to add Python to the system PATH environment variable.
-  - Ensure the pip module is installed (it should be by default)
-  - Need python3.3 or later to get the Windows py.exe launcher that is used to get python3 rather than python2 if both are installed on Windows
-  - 32 bit python works
-- [Git](http://git-scm.com/download/win).
-  - Note: If you use Cygwin, you can normally use Cygwin's "git.exe".  However, in order to use the "update\_external\_sources.bat" script, you must have this version.
-  - Tell the installer to allow it to be used for "Developer Prompt" as well as "Git Bash".
-  - Tell the installer to treat line endings "as is" (i.e. both DOS and Unix-style line endings).
-  - Install each a 32-bit and a 64-bit version, as the 64-bit installer does not install the 32-bit libraries and tools.
-- glslang is required for demos and tests.
-  - [You can download and configure it (in a peer directory) here](https://github.com/KhronosGroup/glslang/blob/master/README.md)
-  - A windows batch file has been included that will pull and build the correct version.  Run it from Developer Command Prompt for VS2013 like so:
-    - update\_external\_sources.bat --build-glslang (Note: see **Loader and Validation Layer Dependencies** below for other options)
-
-## Windows Build - MSVC
-
-Before building on Windows, you may want to modify the customize section in loader/loader.rc to so as to
-set the version numbers and build description for your build. Doing so will set the information displayed
-for the Properites->Details tab of the loader vulkan-1.dll file that is built.
-
-Build all Windows targets after installing required software and cloning the Loader and Validation Layer repo as described above by completing the following steps in a "Developer Command Prompt for VS2013" window (Note that the update\_external\_sources script used below builds external tools into predefined locations. See **Loader and Validation Layer Dependencies** for more information and other options):
-```
-cd Vulkan-LoaderAndValidationLayers  # cd to the root of the cloned git repository
-update_external_sources.bat --all
-build_windows_targets.bat
-```
-
-At this point, you can use Windows Explorer to launch Visual Studio by double-clicking on the "VULKAN.sln" file in the \build folder.  Once Visual Studio comes up, you can select "Debug" or "Release" from a drop-down list.  You can start a build with either the menu (Build->Build Solution), or a keyboard shortcut (Ctrl+Shift+B).  As part of the build process, Python scripts will create additional Visual Studio files and projects, along with additional source files.  All of these auto-generated files are under the "build" folder.
-
-Vulkan programs must be able to find and use the vulkan-1.dll library. Make sure it is either installed in the C:\Windows\System32 folder, or the PATH environment variable includes the folder that it is located in.
-
-To run Vulkan programs you must tell the icd loader where to find the libraries.
-This is described in a `LoaderAndLayerInterface` document in the `loader` folder in this repository.
-This specification describes both how ICDs and layers should be properly
-packaged, and how developers can point to ICDs and layers within their builds.
-
-## Android Build
-Install the required tools for Linux and Windows covered above, then add the
-following.
-### Android Studio
-- Install [Android Studio 2.1](http://tools.android.com/download/studio/canary), latest Preview (tested with 4):
-- From the "Welcome to Android Studio" splash screen, add the following components using Configure > SDK Manager:
-  - SDK Platforms > Android N Preview
-  - SDK Tools > Android NDK
-
-#### Add NDK to path
-
-On Linux:
-```
-export PATH=$HOME/Android/sdk/ndk-bundle:$PATH
-```
-On Windows:
-```
-set PATH=%LOCALAPPDATA%\Android\sdk\ndk-bundle;%PATH%
-```
-On OSX:
-```
-export PATH=$HOME/Library/Android/sdk/ndk-bundle:$PATH
-```
-### Additional OSX System Requirements
-Tested on OSX version 10.11.4
-
- Setup Homebrew and components
-- Follow instructions on [brew.sh](http://brew.sh) to get homebrew installed.
-```
-/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-```
-- Ensure Homebrew is at the beginning of your PATH:
-```
-export PATH=/usr/local/bin:$PATH
-```
-- Add packages with the following (may need refinement)
-```
-brew install cmake python python3 git
-```
-### Build steps for Android
-Use the following to ensure the Android build works.
-#### Linux and OSX
-Follow the setup steps for Linux or OSX above, then from your terminal:
-```
-cd build-android
-./update_external_sources_android.sh
-./android-generate.sh
-ndk-build -j $(sysctl -n hw.ncpu)
-```
-#### Windows
-Follow the setup steps for Windows above, then from Developer Command Prompt for VS2013:
-```
-cd build-android
-update_external_sources_android.bat
-android-generate.bat
-ndk-build
-```
-#### Android demos
-Use the following steps to build, install, and run Cube and Tri for Android:
-```
-cd demos/android
-android update project -s -p . -t "android-23"
-ndk-build
-ant -buildfile cube debug
-adb install ./cube/bin/NativeActivity-debug.apk
-adb shell am start com.example.Cube/android.app.NativeActivity
-```
-To build, install, and run Cube with validation layers, first build layers using steps above, then run:
-```
-cd demos/android
-android update project -s -p . -t "android-23"
-ndk-build -j
-ant -buildfile cube-with-layers debug
-adb install ./cube-with-layers/bin/NativeActivity-debug.apk
-adb shell am start -a android.intent.action.MAIN -c android-intent.category.LAUNCH -n com.example.CubeWithLayers/android.app.NativeActivity --es args "--validate"
-```
-
-To build, install, and run the Smoke demo for Android, run the following, and any
-prompts that come back from the script:
-```
-./update_external_sources.sh --glslang
-cd demos/smoke/android
-export ANDROID_SDK_HOME=<path to Android/Sdk>
-export ANDROID_NDK_HOME=<path to Android/Sdk/ndk-bundle>
-./build-and-install
-adb shell am start -a android.intent.action.MAIN -c android-intent.category.LAUNCH -n com.example.Smoke/android.app.NativeActivity --es args "--validate"
-```
-
-## Ninja Builds - All Platforms
-The [Qt Creator IDE](https://qt.io/download-open-source/#section-2) can open a root CMakeList.txt as a project directly, and it provides tools within Creator to configure and generate Vulkan SDK build files for one to many targets concurrently, resolving configuration issues as needed. Alternatively, when invoking CMake use the -G Codeblocks Ninja option to generate Ninja build files to be used as project files for QtCreator
-
-- Follow the steps defined elsewhere for the OS using the update\_external\_sources script or as shown in **Loader and Validation Layer Dependencies** below
-- Open, configure, and build the gslang and spirv-tools CMakeList.txt files
-- Then do the same with the Vulkan-LoaderAndValidationLayers CMakeList.txt file.
-- In order to debug with QtCreator, a [Microsoft WDK: eg WDK 10](http://go.microsoft.com/fwlink/p/?LinkId=526733) is required. Note that installing the WDK breaks the MSVC vcvarsall.bat build scripts provided by MSVC, requiring that the LIB, INCLUDE, and PATH env variables be set to the WDK paths by some other means
-
-## Loader and Validation Layer Dependencies
-gslang and SPIRV-Tools repos are required to build and run Loader and Validation Layer components. They are not git sub-modules of Vulkan-LoaderAndValidationLayers but Vulkan-LoaderAndValidationLayers is linked to specific revisions of gslang and spirv-tools. These can be automatically cloned and built to predefined locations with the update\_external\_sources scripts. If a custom configuration is required, do the following steps:
-
-1) clone the repos:
-
-    git clone https://github.com/KhronosGroup/glslang.git
-    git clone https://github.com/KhronosGroup/SPIRV-Tools.git
-
-
-2) checkout the correct version of each tree based on the contents of the glslang\_revision and spirv-tools\_revision files at the root of the Vulkan-LoaderAndValidationLayers tree (do the same anytime that Vulkan-LoaderAndValidationLayers is updated from remote)
-
-_on windows_
-
-    git checkout < [path to Vulkan-LoaderAndValidationLayers]\glslang_revision [in glslang repo]
-	git checkout < [path to Vulkan-LoaderAndValidationLayers]\spirv-tools_revision[in spriv-tools repo]
-
-*non windows*
-
-    git checkout `cat [path to Vulkan-LoaderAndValidationLayers]\glslang_revision` [in glslang repo]
-	git checkout `cat [path to Vulkan-LoaderAndValidationLayers]\spirv-tools_revision` [in spriv-tools repo]
-
-3) Configure the gslang and spirv-tools source trees with cmake and build them with your IDE of choice
-
-4) Enable the CUSTOM\_GSLANG\_BIN\_PATH and CUSTOM\_SPIRV\_TOOLS\_BIN\_PATH options in the Vulkan-LoaderAndValidationLayers cmake configuration and point the GSLANG\_BINARY\_PATH and SPIRV\_TOOLS\_BINARY\_PATH variables to the correct location
-
-5) If building on Windows with MSVC, set DISABLE\_BUILDTGT\_DIR\_DECORATION to _On_. If building on Windows, but without MSVC set DISABLE\_BUILD\_PATH\_DECORATION to _On_
-
-## Optional software packages:
-
-- [Cygwin for windows](https://www.cygwin.com/).  Notes:
-  - Cygwin provides some Linux-like tools, which are valuable for obtaining the source code, and running CMake.
-    Especially valuable are the BASH shell and git packages.
-  - If you don't want to use Cygwin, there are other shells and environments that can be used.
-    You can also use a Git package that doesn't come from Cygwin.
-
-- [Ninja on all platforms](https://github.com/ninja-build/ninja/releases). [The Ninja-build project](ninja-build.org). [Ninja Users Manual](ninja-build.org/manual.html) 
-
-- [QtCreator as IDE for CMake builds on all platforms](https://qt.io/download-open-source/#section-2)
diff --git a/BUILDVT.md b/BUILDVT.md
new file mode 100644
index 0000000..a9da8dc
--- /dev/null
+++ b/BUILDVT.md
@@ -0,0 +1,288 @@
+# Build Instructions
+This document contains the instructions for building this repository on Linux and Windows.
+
+This repository contains additional layers and the VkTrace trace/replay tools, supplementing the
+loader and validation layer core components found at https://github.com/KhronosGroup.
+
+This repository previously contained a sample Intel Vulkan driver that has since been deprecated.
+The final stable version of that source code is available from this repo by checking out the git
+tag "Vulkan-ICD-Snapshot". This source code is no longer supported.
+
+## Git the Bits
+
+The public repository for the the LunarG VulkanTools is hosted at https://github.com/LunarG.
+
+If you intend to contribute, the preferred work flow is to fork the repo,
+create a branch in your forked repo, do the work,
+and create a pull request on GitHub to integrate that work back into the repo.
+
+## Linux System Requirements
+Ubuntu 14.04.3 LTS, 14.10, 15.04,15.10, and 16.04 LTS have been tested with this repo.
+
+These additional packages are needed for building the components in this repo.
+```
+# Dependencies from the LoaderAndValidationLayers repo:
+sudo apt-get install git cmake build-essential bison libx11-dev libxcb1-dev libxkbcommon-dev libmirclient-dev libwayland-dev
+# Additional dependencies for this repo:
+sudo apt-get install wget autotools-dev
+# If performing 32-bit builds, you'll also need:
+sudo apt-get install libc6-dev-i386 g++-multilib
+```
+
+## Clone the Repository
+
+Note that the Vulkan-LoaderAndValidationLayers repo content is included within the VulkanTools repo.
+
+To create your local git repository of VulkanTools:
+```
+cd YOUR_DEV_DIRECTORY
+git clone git@github.com:LunarG/VulkanTools.git
+cd VulkanTools
+# This will fetch and build glslang and spriv-tools
+./update_external_sources.sh         # linux
+./update_external_sources.bat --all  # windows
+```
+
+## Linux Build
+
+This build process builds vktrace and the LVL tests.
+
+Example debug build:
+```
+cd YOUR_DEV_DIRECTORY/VulkanTools  # cd to the root of the VulkanTools git repository
+cmake -H. -Bdbuild -DCMAKE_BUILD_TYPE=Debug
+cd dbuild
+make
+```
+
+## Windows System Requirements
+
+Windows 7+ with additional required software packages:
+
+- Microsoft Visual Studio 2013 Professional.  Note: it is possible that lesser/older versions may work, but that has not been tested.
+- CMake (from http://www.cmake.org/download/).  Notes:
+  - In order to build the VkTrace tools, you need at least version 3.0.
+  - Tell the installer to "Add CMake to the system PATH" environment variable.
+- Python 3 (from https://www.python.org/downloads).  Notes:
+  - Select to install the optional sub-package to add Python to the system PATH environment variable.
+  - Need python3.3 or later to get the Windows py.exe launcher that is used to get python3 rather than python2 if both are installed on Windows
+- Git (from http://git-scm.com/download/win).
+  - Note: If you use Cygwin, you can normally use Cygwin's "git.exe".  However, in order to use the "update_external_sources.bat" script, you must have this version.
+  - Tell the installer to allow it to be used for "Developer Prompt" as well as "Git Bash".
+  - Tell the installer to treat line endings "as is" (i.e. both DOS and Unix-style line endings).
+- glslang is required for tests.
+  - You can download and configure it (in a peer directory) here: https://github.com/KhronosGroup/glslang/blob/master/README.md
+  - A windows batch file has been included that will pull and build the correct version.  Run it from Developer Command Prompt for VS2013 like so:
+    - update_external_sources.bat --build-glslang
+
+Optional software packages:
+
+- Cygwin (from https://www.cygwin.com/).  Notes:
+  - Cygwin provides some Linux-like tools, which are valuable for obtaining the source code, and running CMake.
+    Especially valuable are the BASH shell and git packages.
+  - If you don't want to use Cygwin, there are other shells and environments that can be used.
+    You can also use a Git package that doesn't come from Cygwin.
+
+## Windows Build
+
+Cygwin is used in order to obtain a local copy of the Git repository, and to run the CMake command that creates Visual Studio files.  Visual Studio is used to build the software, and will re-run CMake as appropriate.
+
+To build all Windows targets (e.g. in a "Developer Command Prompt for VS2013" window):
+```
+cd VulkanTools  # cd to the root of the VulkanTools git repository
+mkdir build
+cd build
+cmake -G "Visual Studio 12 Win64" ..
+```
+
+At this point, you can use Windows Explorer to launch Visual Studio by double-clicking on the "VULKAN.sln" file in the \build folder.  
+Once Visual Studio comes up, you can select "Debug" or "Release" from a drop-down list.  
+You can start a build with either the menu (Build->Build Solution), or a keyboard shortcut (Ctrl+Shift+B).
+As part of the build process, Python scripts will create additional Visual Studio files and projects,
+along with additional source files.  
+All of these auto-generated files are under the "build" folder.
+
+Vulkan programs must be able to find and use the Vulkan-1.dll library.
+Make sure it is either installed in the C:\Windows\System32 folder,
+or the PATH environment variable includes the folder that it is located in.
+
+### Windows 64-bit Installation Notes
+If you plan on creating a Windows Install file (done in the windowsRuntimeInstaller sub-directory) you will need to build for both 32-bit and 64-bit Windows since both versions of EXEs and DLLs exist simultaneously on Windows 64.
+
+To do this, simply create and build the release versions of each target:
+```
+cd VulkanTools  # cd to the root of the Vulkan git repository
+mkdir build
+cd build
+cmake -G "Visual Studio 12 Win64" ..
+msbuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Release
+mkdir build32
+cd build32
+cmake -G "Visual Studio 12" ..
+msbuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Release
+```
+## Android Build
+Install the required tools for Linux and Windows covered above, then add the
+following.
+### Android Studio
+- Install 2.1 or later verion of [Android Studio](http://tools.android.com/download/studio/stable)
+- From the "Welcome to Android Studio" splash screen, add the following components using Configure > SDK Manager:
+  - SDK Tools > Android NDK
+
+#### Add NDK to path
+
+On Linux:
+```
+export PATH=$HOME/Android/sdk/ndk-bundle:$PATH
+```
+On Windows:
+```
+set PATH=%LOCALAPPDATA%\Android\sdk\ndk-bundle;%PATH%
+```
+On OSX:
+```
+export PATH=$HOME/Library/Android/sdk/ndk-bundle:$PATH
+```
+### Additional OSX System Requirements
+Tested on OSX version 10.11.4
+
+ Setup Homebrew and components
+- Follow instructions on [brew.sh](http://brew.sh) to get homebrew installed.
+```
+/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+```
+- Ensure Homebrew is at the beginning of your PATH:
+```
+export PATH=/usr/local/bin:$PATH
+```
+- Add packages with the following (may need refinement)
+```
+brew install cmake python python3 git
+```
+### Build steps for Android
+Use the following to ensure the Android build works.
+#### Linux
+Follow the setup steps for Linux, then from your terminal:
+```
+cd build-android
+./update_external_sources_android.sh
+./android-generate.sh
+ndk-build -j $(nproc)
+```
+#### OSX
+Follow the setup steps for OSX above, then from your terminal:
+```
+cd build-android
+./update_external_sources_android.sh
+./android-generate.sh
+ndk-build -j $(sysctl -n hw.ncpu)
+```
+#### Windows
+Follow the setup steps for Windows above, then from Developer Command Prompt for VS2013:
+```
+cd build-android
+update_external_sources_android.bat
+android-generate.bat
+ndk-build
+```
+
+## Android usage
+This documentation is preliminary and needs to be beefed up.
+
+See the [vktracereplay.sh](https://github.com/LunarG/VulkanTools/blob/master/build-android/vktracereplay.sh) file for a working example of how to use vktrace/vkreplay and screenshot layers.
+
+Two additional scripts have been added to facilitate tracing and replaying any APK.  Note that these two scripts do not install anything for you, so make sure your target APK, vktrace, vktrace_layer, and vkreplay all use the same ABI.
+```
+./create_trace.sh --serial 0123456789 --abi armeabi-v7a --package com.example.CubeWithLayers  --activity android.app.NativeActivity
+adb install --abi armeabi-v7a
+./replay_trace.sh --serial 0123456789 --tracefile com.example.CubeWithLayers0.vktrace
+```
+An example of using the scripts on Linux and macOS:
+```
+./build_vktracereplay.sh
+./vktracereplay.sh \
+ --serial 12345678 \
+ --abi armeabi-v7a \
+ --apk ../demos/android/cube-with-layers/bin/NativeActivity-debug.apk \
+ --package com.example.CubeWithLayers \
+ --frame 50
+```
+And on Windows:
+```
+build_vktracereplay.bat ^
+vktracereplay.bat ^
+ --serial 12345678 ^
+ --abi armeabi-v7a ^
+ --apk ..\demos\android\cube-with-layers\bin\NativeActivity-debug.apk ^
+ --package com.example.CubeWithLayers ^
+ --frame 50
+```
+### api_dump
+To enable, make the following changes to vk_layer_settings.txt
+```
+-lunarg_api_dump.file = FALSE
++lunarg_api_dump.file = TRUE
+
+-lunarg_api_dump.log_filename = stdout
++lunarg_api_dump.log_filename = /sdcard/Android/vk_apidump.txt
+```
+Then:
+```
+adb push vk_layer_settings.txt /sdcard/Android
+```
+And run your application with the following layer enabled:
+```
+VK_LAYER_LUNARG_api_dump
+```
+### screenshot
+To enable, set a property that contains target frame:
+```
+adb shell setprop debug.vulkan.screenshot <framenumber>
+```
+For production builds, be sure your application has access to read and write to external storage by adding the following to AndroidManifest.xml:
+```
+<!-- This allows writing log files to sdcard -->
+<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+```
+You may also need to grant it access with package manager:
+```
+adb shell pm grant com.example.Cube android.permission.READ_EXTERNAL_STORAGE
+adb shell pm grant com.example.Cube android.permission.WRITE_EXTERNAL_STORAGE
+```
+Run your application with the following layer enabled:
+```
+VK_LAYER_LUNARG_screenshot
+```
+Result screenshot will be in:
+```
+/sdcard/Android/<framenumber>.ppm
+```
+### vktrace
+To record a trace on Android, enable port forwarding from the device to the host:
+```
+adb reverse localabstract:vktrace tcp:34201
+```
+Start up vktrace on the host in server mode:
+```
+vktrace -v full -o cube.vktrace
+```
+Run your application with the following layer enabled:
+```
+VK_LAYER_LUNARG_vktrace
+```
+The trace will be recorded on the host.
+### vkreplay
+To replay a trace, push the trace to your device
+```
+adb push cube.vktrace /sdcard/cube.vktrace
+```
+Grant vkreplay the ability to read it
+```
+adb shell pm grant com.example.vkreplay android.permission.READ_EXTERNAL_STORAGE
+adb shell pm grant com.example.vkreplay android.permission.WRITE_EXTERNAL_STORAGE
+```
+And start the native activity
+```
+adb shell am start -a android.intent.action.MAIN -c android-intent.category.LAUNCH -n com.example.vkreplay/android.app.NativeActivity --es args "-v\ full\ -t\ /sdcard/cube.vktrace"
+```
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 27ab6e5..43e3f02 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@
 # refer to the root source directory of the project as ${VULKAN_SOURCE_DIR} and
 # to the root binary directory of the project as ${VULKAN_BINARY_DIR}.
 cmake_minimum_required(VERSION 2.8.11)
-project (VULKAN)
+project (VULKAN_TOOLS)
 # set (CMAKE_VERBOSE_MAKEFILE 1)
 
 # The API_NAME allows renaming builds to avoid conflicts with installed SDKs
@@ -68,7 +68,6 @@
 else()
     option(DISABLE_BUILD_PATH_DECORATION "Disable the decoration of the gslang and SPIRV-Tools build path with MSVC build type info" OFF)
     option(DISABLE_BUILDTGT_DIR_DECORATION "Disable the decoration of the gslang and SPIRV-Tools build path with target info" OFF)
-
     # For Windows, since 32-bit and 64-bit items can co-exist, we build each in its own build directory.
     # 32-bit target data goes in build32, and 64-bit target data goes into build.  So, include/link the
     # appropriate data at build time.
@@ -87,13 +86,37 @@
     endif()
 endif()
 
-option(BUILD_LOADER "Build loader" ON)
-option(BUILD_TESTS "Build tests" ON)
-option(BUILD_LAYERS "Build layers" ON)
-option(BUILD_DEMOS "Build demos" ON)
-option(BUILD_VKJSON "Build vkjson" ON)
-option(CUSTOM_GLSLANG_BIN_ROOT "Use the user defined GLSLANG_BINARY_ROOT" OFF)
-option(CUSTOM_SPIRV_TOOLS_BIN_ROOT "Use the user defined SPIRV_TOOLS_BINARY_ROOT" OFF)
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR
+    CMAKE_SYSTEM_NAME STREQUAL "Linux")
+
+    # These are unchanged from upstream file
+    option(BUILD_LOADER "Build loader" ON)
+    option(BUILD_TESTS "Build tests" ON)
+    option(BUILD_LAYERS "Build layers" ON)
+    option(BUILD_LAYERSVT "Build layersvt" ON)
+    option(BUILD_DEMOS "Build demos" ON)
+    option(BUILD_VKTRACE "Build VkTrace" ON)
+    option(BUILD_VKJSON "Build vkjson" ON)
+    option(BUILD_VIA "Build via" ON)
+    option(CUSTOM_GLSLANG_BIN_ROOT "Use the user defined GLSLANG_BINARY_ROOT" OFF)
+    option(CUSTOM_SPIRV_TOOLS_BIN_ROOT "Use the user defined SPIRV_TOOLS_BINARY_ROOT" OFF)
+
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+
+    # Only vktrace enable for macOS
+    option(BUILD_VKTRACE ON)
+    option(BUILD_LOADER OFF)
+    option(BUILD_TESTS OFF)
+    option(BUILD_LAYERS OFF)
+    option(BUILD_LAYERSVT OFF)
+    option(BUILD_VKTRACEVIEWER OFF)
+    option(BUILD_DEMOS OFF)
+    option(BUILD_VKJSON OFF)
+    option(BUILD_VIA OFF)
+    option(BUILD_VKTRACE_LAYER OFF)
+    option(BUILD_VKTRACE_REPLAY OFF)
+
+endif()
 
 #Choose natural default paths for glslang and SPIRV-Tools binaries to support custom definition by the user on the CMake command line or in the GUI
 set(GLSLANG_BINARY_ROOT "${CMAKE_BINARY_DIR}/../glslang" CACHE STRING "User defined path to the glslang binaries for this project")
@@ -175,27 +198,44 @@
                                                    "${EXTERNAL_SOURCE_ROOT}/source/spirv-tools/external/include"
                                              DOC "Path to spirv-tools/libspirv.h")
 
-find_library(GLSLANG_LIB NAMES glslang
+find_path(JSONCPP_INCLUDE_DIR json/json.h HINTS "${EXTERNAL_SOURCE_ROOT}/jsoncpp/dist"
+                                                   "${EXTERNAL_SOURCE_ROOT}/JsonCpp/dist"
+                                                   "${EXTERNAL_SOURCE_ROOT}/JsonCPP/dist"
+                                                   "${EXTERNAL_SOURCE_ROOT}/JSONCPP/dist"
+                                                   "${CMAKE_SOURCE_DIR}/../jsoncpp/dist"
+                                             DOC "Path to jsoncpp/dist/json/json.h")
+
+find_path(JSONCPP_SOURCE_DIR jsoncpp.cpp HINTS "${EXTERNAL_SOURCE_ROOT}/jsoncpp/dist"
+                                                   "${EXTERNAL_SOURCE_ROOT}/JsonCpp/dist"
+                                                   "${EXTERNAL_SOURCE_ROOT}/JsonCPP/dist"
+                                                   "${EXTERNAL_SOURCE_ROOT}/JSONCPP/dist"
+                                                   "${CMAKE_SOURCE_DIR}/../jsoncpp/dist"
+                                             DOC "Path to jsoncpp/dist/json.cpp")
+
+    find_library(GLSLANG_LIB NAMES glslang
+        HINTS ${GLSLANG_SEARCH_PATH} )
+
+    find_library(OGLCompiler_LIB NAMES OGLCompiler
+        HINTS ${GLSLANG_SEARCH_PATH} )
+
+    find_library(OSDependent_LIB NAMES OSDependent
+        HINTS ${GLSLANG_SEARCH_PATH} )
+
+    find_library(HLSL_LIB NAMES HLSL
+        HINTS ${GLSLANG_SEARCH_PATH} )
+
+    find_library(SPIRV_LIB NAMES SPIRV
+        HINTS ${GLSLANG_SEARCH_PATH} )
+
+    find_library(SPIRV_REMAPPER_LIB NAMES SPVRemapper
              HINTS ${GLSLANG_SEARCH_PATH} )
 
-find_library(OGLCompiler_LIB NAMES OGLCompiler
-             HINTS ${GLSLANG_SEARCH_PATH} )
-
-find_library(OSDependent_LIB NAMES OSDependent
-             HINTS ${GLSLANG_SEARCH_PATH} )
-
-find_library(HLSL_LIB NAMES HLSL
-             HINTS ${GLSLANG_SEARCH_PATH} )
-
-find_library(SPIRV_LIB NAMES SPIRV
-             HINTS ${GLSLANG_SEARCH_PATH} )
-
-find_library(SPIRV_REMAPPER_LIB NAMES SPVRemapper
-             HINTS ${GLSLANG_SEARCH_PATH} )
-
-find_library(SPIRV_TOOLS_LIB NAMES SPIRV-Tools
+    find_library(SPIRV_TOOLS_LIB NAMES SPIRV-Tools
              HINTS ${SPIRV_TOOLS_SEARCH_PATH} )
 
+    find_library(JSONCPP_LIB NAMES jsoncpp
+             HINTS ${JSONCPP_SEARCH_PATH} )
+
 if (WIN32)
     add_library(glslang     STATIC IMPORTED)
     add_library(OGLCompiler STATIC IMPORTED)
@@ -205,6 +245,7 @@
     add_library(SPVRemapper       STATIC IMPORTED)
     add_library(Loader      STATIC IMPORTED)
     add_library(SPIRV-Tools STATIC IMPORTED)
+    add_library(jsoncpp     STATIC IMPORTED)
 
     find_library(GLSLANG_DLIB NAMES glslangd
                  HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
@@ -220,19 +261,21 @@
                  HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
     find_library(SPIRV_TOOLS_DLIB NAMES SPIRV-Tools
                  HINTS ${SPIRV_TOOLS_DEBUG_SEARCH_PATH} )
+    find_library(JSONCPP_DLIB NAMES jsoncpp
+                 HINTS ${JSONCPP_DEBUG_SEARCH_PATH} )
 
     set_target_properties(glslang PROPERTIES
-                         IMPORTED_LOCATION       "${GLSLANG_LIB}"
-                         IMPORTED_LOCATION_DEBUG "${GLSLANG_DLIB}")
+                          IMPORTED_LOCATION       "${GLSLANG_LIB}"
+                          IMPORTED_LOCATION_DEBUG "${GLSLANG_DLIB}")
     set_target_properties(OGLCompiler PROPERTIES
-                         IMPORTED_LOCATION       "${OGLCompiler_LIB}"
-                         IMPORTED_LOCATION_DEBUG "${OGLCompiler_DLIB}")
+                          IMPORTED_LOCATION       "${OGLCompiler_LIB}"
+                          IMPORTED_LOCATION_DEBUG "${OGLCompiler_DLIB}")
     set_target_properties(OSDependent PROPERTIES
-                         IMPORTED_LOCATION       "${OSDependent_LIB}"
-                         IMPORTED_LOCATION_DEBUG "${OSDependent_DLIB}")
+                          IMPORTED_LOCATION       "${OSDependent_LIB}"
+                          IMPORTED_LOCATION_DEBUG "${OSDependent_DLIB}")
     set_target_properties(HLSL PROPERTIES
-                         IMPORTED_LOCATION       "${HLSL_LIB}"
-                         IMPORTED_LOCATION_DEBUG "${HLSL_DLIB}")
+                          IMPORTED_LOCATION       "${HLSL_LIB}"
+                          IMPORTED_LOCATION_DEBUG "${HLSL_DLIB}")
     set_target_properties(SPIRV PROPERTIES
                          IMPORTED_LOCATION       "${SPIRV_LIB}"
                          IMPORTED_LOCATION_DEBUG "${SPIRV_DLIB}")
@@ -240,8 +283,11 @@
                          IMPORTED_LOCATION       "${SPIRV_REMAPPER_LIB}"
                          IMPORTED_LOCATION_DEBUG "${SPIRV_REMAPPER_DLIB}")
     set_target_properties(SPIRV-Tools PROPERTIES
-                         IMPORTED_LOCATION       "${SPIRV_TOOLS_LIB}"
-                         IMPORTED_LOCATION_DEBUG "${SPIRV_TOOLS_DLIB}")
+                          IMPORTED_LOCATION       "${SPIRV_TOOLS_LIB}"
+                          IMPORTED_LOCATION_DEBUG "${SPIRV_TOOLS_DLIB}")
+    set_target_properties(jsoncpp PROPERTIES
+                         IMPORTED_LOCATION       "${JSONCPP_LIB}"
+                         IMPORTED_LOCATION_DEBUG "${JSONCPP_DLIB}")
 
     set (GLSLANG_LIBRARIES glslang OGLCompiler OSDependent HLSL SPIRV SPVRemapper)
     set (SPIRV_TOOLS_LIBRARIES SPIRV-Tools)
@@ -271,6 +317,12 @@
 
 if(UNIX)
     install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/vulkan" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+    if(NOT BUILD_WSI_XCB_SUPPORT)
+        set(BUILD_VKTRACE OFF)
+        if (NOT BUILD_WSI_XLIB_SUPPORT)
+            set(BUILD_LAYERSVT OFF)
+        endif()
+    endif()
 endif()
 
 # loader: Generic VULKAN ICD loader
@@ -287,10 +339,22 @@
     add_subdirectory(layers)
 endif()
 
+if(BUILD_LAYERSVT)
+    add_subdirectory(layersvt)
+endif()
+
 if(BUILD_DEMOS)
     add_subdirectory(demos)
 endif()
 
+if(BUILD_VKTRACE)
+    add_subdirectory(vktrace)
+endif()
+
 if(BUILD_VKJSON)
     add_subdirectory(libs/vkjson)
 endif()
+
+if(BUILD_VIA)
+    add_subdirectory(via)
+endif()
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index a30ee9e..0c9484f 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -1,4 +1,4 @@
-This file contains other licenses and their copyrights that appear in this

+This file contains other licenses and their copyrights that appear in this

 repository besides Apache 2.0 license.

 

 ===================================================

@@ -129,3 +129,62 @@
 /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

 /// THE SOFTWARE.

 ///

+

+===================================================

+The JsonCpp library's source code, including accompanying documentation, 

+tests and demonstration applications, are licensed under the following

+conditions...

+

+The author (Baptiste Lepilleur) explicitly disclaims copyright in all 

+jurisdictions which recognize such a disclaimer. In such jurisdictions, 

+this software is released into the Public Domain.

+

+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of

+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is

+released under the terms of the MIT License (see below).

+

+In jurisdictions which recognize Public Domain property, the user of this 

+software may choose to accept it either as 1) Public Domain, 2) under the 

+conditions of the MIT License (see below), or 3) under the terms of dual 

+Public Domain/MIT License conditions described here, as they choose.

+

+The MIT License is about as close to Public Domain as a license can get, and is

+described in clear, concise terms at:

+

+   http://en.wikipedia.org/wiki/MIT_License

+   

+The full text of the MIT License follows:

+

+========================================================================

+Copyright (c) 2007-2010 Baptiste Lepilleur

+

+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.

+========================================================================

+(END LICENSE TEXT)

+

+The MIT license is compatible with both the GPL and commercial

+software, affording one all of the rights of Public Domain with the

+minor nuisance of being required to keep the above copyright notice

+and license text in the source code. Note also that by accepting the

+Public Domain "license" you can re-license your copy using whatever

+license you like.

+

+===================================================

diff --git a/README.md b/README.md
index bb2769d..c3af369 100644
--- a/README.md
+++ b/README.md
@@ -1,43 +1,36 @@
 # Vulkan Ecosystem Components
 
-This project provides Khronos official ICD loader and validation layers for Vulkan developers on Windows and Linux.
+This project provides vktrace capture/replay tool and other layer tools and driver tests.
 
 ## CI Build Status
 | Platform | Build Status |
 |:--------:|:------------:|
-| Linux/Android | [![Build Status](https://travis-ci.org/KhronosGroup/Vulkan-LoaderAndValidationLayers.svg?branch=master)](https://travis-ci.org/KhronosGroup/Vulkan-LoaderAndValidationLayers) |
-| Windows |[![Build status](https://ci.appveyor.com/api/projects/status/ri4584d6qramrjiv/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/vulkan-loaderandvalidationlayers/branch/master) |
-
+| Linux/Android | [![Build Status](https://travis-ci.org/LunarG/VulkanTools.svg?branch=master)](https://travis-ci.org/LunarG/VulkanTools) |
+| Windows | [![Build status](https://ci.appveyor.com/api/projects/status/2ncmy766ufb2hnh2/branch/master?svg=true)](https://ci.appveyor.com/project/karl-lunarg/vulkantools/branch/master) |
 
 ## Introduction
 
-Vulkan is an Explicit API, enabling direct control over how GPUs actually work. No (or very little) validation
-or error checking is done inside a Vulkan driver. Applications have full control and responsibility. Any errors in
-how Vulkan is used often result in a crash. This project provides standard validation layers that can be enabled
-to ease development by helping developers verify their applications correctly use the Vulkan API.
+Branches within this repository include the Vulkan loader, validation layers, header files, and associated tests.  These pieces are mirrored from this Github repository:
+https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
+These pieces are required to enable this repository to be built standalone; that is without having to clone the Vulkan-LoaderAndValidationLayers repository.
 
-Vulkan supports multiple GPUs and multiple global contexts (VkInstance). The ICD loader is necessary to
-support multiple GPUs  and the VkInstance level Vulkan commands.  Additionally, the loader manages inserting
-Vulkan layer libraries, including validation layers between the application and the ICD.
-
-The following components are available in this repository:
-- Vulkan header files
-- [*ICD Loader*](loader/)
-- [*Validation Layers*](layers/)
-- Demos and tests for the loader and validation layers
+The following components are available in this repository over and above what is mirrored from Vulkan-LoaderAndValidationLayers repository
+- Api_dump, screenshot and example layers (layersvt/)
+- tests for the vktrace and vkreplay (tests/)
+- vktrace and vkreplay, API capture and replay  (vktrace/)
 
 ## Contributing
 
 If you intend to contribute, the preferred work flow is for you to develop your contribution
 in a fork of this repo in your GitHub account and then submit a pull request.
-Please see the [CONTRIBUTING](CONTRIBUTING.md) file in this respository for more details
+Please see the [CONTRIBUTING](CONTRIBUTING.md) file in this respository for more details.
 
 ## How to Build and Run
 
-[BUILD.md](BUILD.md)
-includes directions for building all the components, running the validation tests and running the demo applications.
+[BUILDVT.md](BUILDVT.md)
+includes directions for building all the components, running the tests and running the demo applications.
 
-Information on how to enable the various Validation layers is in
+Information on how to enable the various layers is in
 [layers/README.md](layers/README.md).
 
 Architecture and interface information for the loader is in
@@ -62,4 +55,3 @@
 project development; Google providing significant contributions to the validation layers;
 Khronos providing oversight and hosting of the project.
 
-
diff --git a/build-android/android-generate.bat b/build-android/android-generate.bat
index 283be55..e730898 100644
--- a/build-android/android-generate.bat
+++ b/build-android/android-generate.bat
@@ -20,8 +20,6 @@
 mkdir generated\include generated\common

 

 cd generated/include

-py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml vk_safe_struct.h

-py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml vk_safe_struct.cpp

 py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml vk_struct_size_helper.h

 py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml vk_struct_size_helper.c

 py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml vk_enum_string_helper.h

@@ -29,6 +27,20 @@
 py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml thread_check.h

 py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml parameter_validation.h

 py -3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml unique_objects_wrappers.h

+py -3 ../../../scripts/vt_genvk.py -registry ../../../scripts/vk.xml api_dump.cpp

+py -3 ../../../scripts/vt_genvk.py -registry ../../../scripts/vk.xml api_dump_text.h

+

+REM vktrace

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-trace-h vk_version_1_0 > vktrace_vk_vk.h

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-trace-c vk_version_1_0 > vktrace_vk_vk.cpp

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-core-trace-packets vk_version_1_0 > vktrace_vk_vk_packets.h

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-packet-id vk_version_1_0 > vktrace_vk_packet_id.h

+

+REM vkreplay

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-replay-vk-funcs vk_version_1_0 > vkreplay_vk_func_ptrs.h

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-replay-c vk_version_1_0 > vkreplay_vk_replay_gen.cpp

+py -3 ../../../vktrace/vktrace_generate.py AllPlatforms vktrace-replay-obj-mapper-h vk_version_1_0 > vkreplay_vk_objmapper.h

+

 cd ../..

 

 copy /Y ..\layers\vk_layer_config.cpp   generated\common\

@@ -40,18 +52,25 @@
 REM create build-script root directory

 mkdir generated\gradle-build

 cd generated\gradle-build

-mkdir  core_validation image object_tracker parameter_validation swapchain threading unique_objects

+mkdir  core_validation image object_tracker parameter_validation swapchain threading unique_objects api_dump screenshot

 cd ..\..

 mkdir generated\layer-src

 cd generated\layer-src

-mkdir  core_validation image object_tracker parameter_validation swapchain threading unique_objects

+mkdir  core_validation image object_tracker parameter_validation swapchain threading unique_objects api_dump screenshot

 cd ..\..

 xcopy /s gradle-templates\*   generated\gradle-build\

 for %%G in (core_validation image object_tracker parameter_validation swapchain threading unique_objects) Do (

     copy ..\layers\%%G.cpp   generated\layer-src\%%G

     echo apply from: "../common.gradle"  > generated\gradle-build\%%G\build.gradle

 )

+for %%G in (screenshot) Do (

+    copy ..\layersvt\%%G.cpp   generated\layer-src\%%G

+    echo apply from: "../common.gradle"  > generated\gradle-build\%%G\build.gradle

+)

+copy generated\include\api_dump.cpp   generated\layer-src\api_dump

 copy generated\common\descriptor_sets.cpp generated\layer-src\core_validation\descriptor_sets.cpp

 copy generated\include\vk_safe_struct.cpp generated\layer-src\core_validation\vk_safe_struct.cpp

 move generated\include\vk_safe_struct.cpp generated\layer-src\unique_objects\vk_safe_struct.cpp

 echo apply from: "../common.gradle"  > generated\gradle-build\unique_objects\build.gradle

+

+del  /f /q generated\include\api_dump.cpp

diff --git a/build-android/android-generate.sh b/build-android/android-generate.sh
index 09022a3..fcd9bb8 100755
--- a/build-android/android-generate.sh
+++ b/build-android/android-generate.sh
@@ -31,6 +31,20 @@
 ( cd generated/include; python3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml parameter_validation.h )
 ( cd generated/include; python3 ../../../scripts/lvl_genvk.py -registry ../../../scripts/vk.xml unique_objects_wrappers.h )
 
+( cd generated/include; python3 ../../../scripts/vt_genvk.py -registry ../../../scripts/vk.xml api_dump.cpp )
+( cd generated/include; python3 ../../../scripts/vt_genvk.py -registry ../../../scripts/vk.xml api_dump_text.h )
+
+# vktrace
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-trace-h vk_version_1_0 > generated/include/vktrace_vk_vk.h
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-trace-c vk_version_1_0 > generated/include/vktrace_vk_vk.cpp
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-core-trace-packets vk_version_1_0 > generated/include/vktrace_vk_vk_packets.h
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-packet-id vk_version_1_0 > generated/include/vktrace_vk_packet_id.h
+
+# vkreplay
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-replay-vk-funcs vk_version_1_0 > generated/include/vkreplay_vk_func_ptrs.h
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-replay-c vk_version_1_0 > generated/include/vkreplay_vk_replay_gen.cpp
+python3 ../vktrace/vktrace_generate.py AllPlatforms vktrace-replay-obj-mapper-h vk_version_1_0 > generated/include/vkreplay_vk_objmapper.h
+
 cp -f ../layers/vk_layer_config.cpp   generated/common/
 cp -f ../layers/vk_layer_extension_utils.cpp  generated/common/
 cp -f ../layers/vk_layer_utils.cpp    generated/common/
@@ -40,8 +54,8 @@
 # layer names and their original source files directory
 # 1 to 1 correspondence -- one layer one source file; additional files are copied
 # at fixup step
-declare layers=(core_validation image object_tracker parameter_validation swapchain threading unique_objects)
-declare src_dirs=(../layers ../layers ../layers ../layers ../layers ../layers ../layers)
+declare layers=(core_validation image object_tracker parameter_validation swapchain threading unique_objects api_dump screenshot)
+declare src_dirs=(../layers ../layers ../layers ../layers ../layers ../layers ../layers generated/include ../layersvt)
 
 SRC_ROOT=generated/layer-src
 BUILD_ROOT=generated/gradle-build
@@ -64,4 +78,7 @@
 cp  generated/include/vk_safe_struct.cpp ${SRC_ROOT}/core_validation/vk_safe_struct.cpp
 mv  generated/include/vk_safe_struct.cpp ${SRC_ROOT}/unique_objects/vk_safe_struct.cpp
 
+# fixup - remove copied files from generated/include
+rm  generated/include/api_dump.cpp
+
 exit 0
diff --git a/build-android/build_vktracereplay.bat b/build-android/build_vktracereplay.bat
new file mode 100644
index 0000000..a173da9
--- /dev/null
+++ b/build-android/build_vktracereplay.bat
@@ -0,0 +1,112 @@
+@echo off
+REM # Copyright 2016 The Android Open Source Project
+REM # Copyright (C) 2015 Valve Corporation
+REM
+REM # Licensed under the Apache License, Version 2.0 (the "License");
+REM # you may not use this file except in compliance with the License.
+REM # You may obtain a copy of the License at
+REM
+REM #      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM # Unless required by applicable law or agreed to in writing, software
+REM # distributed under the License is distributed on an "AS IS" BASIS,
+REM # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM # See the License for the specific language governing permissions and
+REM # limitations under the License.
+
+set num_cpus=%NUMBER_OF_PROCESSORS%
+
+REM
+REM build layers
+REM
+call update_external_sources_android.bat  || exit /b 1
+call android-generate.bat                 || exit /b 1
+call ndk-build -j %num_cpus%              || exit /b 1
+
+REM
+REM create vkreplay APK
+REM
+call android update project -s -p . -t "android-23" || exit /b 1
+call ant -buildfile vkreplay debug                  || exit /b 1
+
+REM
+REM build cube-with-layers
+REM
+pushd ..\demos\android                               || exit /b 1
+call android update project -s -p . -t "android-23"  || exit /b 1
+call ndk-build -j  %num_cpus%                        || exit /b 1
+call ant -buildfile cube-with-layers debug           || exit /b 1
+popd
+
+REM
+REM build vktrace
+REM
+pushd ..
+
+REM Attempt to use the current Visual Studio version
+python .\scripts\determine_vs_version.py > vsversion.tmp  || exit /b 1
+set /p VS_VERSION=< vsversion.tmp                         || exit /b 1
+del /Q /F vsversion.tmp                                   || exit /b 1
+echo VS_VERSION = %VS_VERSION%
+
+REM build the desktop shader toolchain
+call update_external_sources.bat --build-glslang --build-spirv-tools || exit /b 1
+
+REM check for 64-bit Qt install
+set Qt5_Dir_64=C:\Qt\5.6\msvc2013_64\lib\cmake\Qt5
+if not exist "%Qt5_Dir_64%" echo Please set Qt5_Dir_64 to 64-bit Qt install location. && exit /b 1
+
+REM check for 32-bit Qt install
+set Qt5_Dir_32=C:\Qt\5.6\msvc2013\lib\cmake\Qt5
+if not exist "%Qt5_Dir_32%" echo Please set Qt5_Dir_32 to 32-bit Qt install location. && exit /b 1
+
+REM build 64-bit vktrace and vktraceviewer
+if not exist "build\" mkdir build || exit /b 1
+pushd build
+set Qt5_Dir=%Qt5_Dir_64%
+cmake -G "Visual Studio %VS_VERSION% Win64" ^
+ -DBUILD_TESTS=Off ^
+ -DBUILD_LAYERS=Off ^
+ -DBUILD_LAYERSVT=Off ^
+ -DBUILD_DEMOS=Off ^
+ -DBUILD_VKJSON=Off ^
+ -DBUILD_VIA=Off ^
+ -DBUILD_VKTRACE_LAYER=Off ^
+ -DBUILD_VKTRACE_REPLAY=Off ^
+ -DBUILD_VKTRACE=On ^
+ -DBUILD_LOADER=On ^
+ -DBUILD_VKTRACEVIEWER=On ^
+ .. || exit /b 1
+echo Building 64-bit Debug
+msbuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Debug /maxcpucount /verbosity:quiet   || exit /b 1
+echo Building 64-bit Release
+msbuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Release /maxcpucount /verbosity:quiet || exit /b 1
+popd
+
+REM build 32-bit vktrace and vktraceviewer
+if not exist "build32\" mkdir build32 || exit /b 1
+pushd build32
+set Qt5_Dir=%Qt5_Dir_32%
+cmake -G "Visual Studio %VS_VERSION%" ^
+ -DBUILD_TESTS=Off ^
+ -DBUILD_LAYERS=Off ^
+ -DBUILD_LAYERSVT=Off ^
+ -DBUILD_DEMOS=Off ^
+ -DBUILD_VKJSON=Off ^
+ -DBUILD_VIA=Off ^
+ -DBUILD_VKTRACE_LAYER=Off ^
+ -DBUILD_VKTRACE_REPLAY=Off ^
+ -DBUILD_VKTRACE=On ^
+ -DBUILD_LOADER=On ^
+ -DBUILD_VKTRACEVIEWER=On ^
+ .. || exit /b 1
+echo Building 32-bit Debug
+msbuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Debug /maxcpucount /verbosity:quiet   || exit /b 1
+echo Building 32-bit Release
+msbuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Release /maxcpucount /verbosity:quiet || exit /b 1
+popd
+
+REM done building vktrace
+popd
+
+exit /b 0
\ No newline at end of file
diff --git a/build-android/build_vktracereplay.sh b/build-android/build_vktracereplay.sh
new file mode 100755
index 0000000..b89f700
--- /dev/null
+++ b/build-android/build_vktracereplay.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+set -ev
+
+if [[ $(uname) == "Linux" ]]; then
+    cores=$(ncpus)
+elif [[ $(uname) == "Darwin" ]]; then
+    cores=$(sysctl -n hw.ncpu)
+fi
+
+#
+# build layers
+#
+./update_external_sources_android.sh
+./android-generate.sh
+ndk-build -j $cores
+
+#
+# create vkreplay APK
+#
+android update project -s -p . -t "android-23"
+ant -buildfile vkreplay debug
+
+#
+# build cube-with-layers
+#
+(
+pushd ../demos/android
+android update project -s -p . -t "android-23"
+ndk-build -j $cores
+ant -buildfile cube-with-layers debug
+popd
+)
+
+#
+# build vktrace
+#
+(
+pushd ..
+./update_external_sources.sh -g -s
+mkdir -p build
+cd build
+cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_LOADER=Off -DBUILD_TESTS=Off -DBUILD_LAYERS=Off -DBUILD_VKTRACEVIEWER=Off -DBUILD_LAYERSVT=Off -DBUILD_DEMOS=Off -DBUILD_VKJSON=Off -DBUILD_VIA=Off -DBUILD_VKTRACE_LAYER=Off -DBUILD_VKTRACE_REPLAY=Off -DBUILD_VKTRACE=On ..
+make -j $cores vktrace
+popd
+)
+
+#
+# build vktrace32
+#
+(
+pushd ..
+mkdir -p build32
+cd build32
+cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_LOADER=Off -DBUILD_TESTS=Off -DBUILD_LAYERS=Off -DBUILD_VKTRACEVIEWER=Off -DBUILD_LAYERSVT=Off -DBUILD_DEMOS=Off -DBUILD_VKJSON=Off -DBUILD_VIA=Off -DBUILD_VKTRACE_LAYER=Off -DBUILD_VKTRACE_REPLAY=Off -DBUILD_X64=Off -DBUILD_VKTRACE=On ..
+make -j $cores vktrace
+popd
+)
+
+exit 0
diff --git a/build-android/create_trace.sh b/build-android/create_trace.sh
new file mode 100755
index 0000000..512ed0f
--- /dev/null
+++ b/build-android/create_trace.sh
@@ -0,0 +1,259 @@
+#!/bin/bash
+
+#set -vex
+
+script_start_time=$(date +%s)
+
+default_vktrace_exe=../build/vktrace/vktrace
+default_vktrace32_exe=../build32/vktrace/vktrace32
+default_target_abi=$(adb shell getprop ro.product.cpu.abi)
+default_activity=android.app.NativeActivity
+
+#
+# Parse parameters
+#
+
+function printUsage {
+   echo "Supported parameters are:"
+   echo "    --serial <target device serial number>"
+   echo "    --abi <abi to install>"
+   echo "    --vktrace <full path to vktrace on host> (optional)"
+   echo "    --package <package name>"
+   echo "    --actvity <launchable-activity name>"
+   echo "    --frame <frame number to capture> (optional)"
+   echo
+   echo "i.e. ${0##*/} --serial 01234567 \\"
+   echo "              --abi arm64-v8a \\"
+   echo "              --vktrace ../build/vktrace/vktrace \\"
+   echo "              --package com.example.foo \\"
+   echo "              --actvity android.app.NativeActivity \\"
+   echo "              --frame 100"
+   exit 1
+}
+
+if [[ $(($# % 2)) -ne 0 ]]
+then
+    echo Parameters must be provided in pairs.
+    echo parameter count = $#
+    echo
+    printUsage
+    exit 1
+fi
+
+while [[ $# -gt 0 ]]
+do
+    case $1 in
+        --serial)
+            # include the flag, because we need to leave it off if not provided
+            serial="$2"
+            serialFlag="-s $serial"
+            shift 2
+            ;;
+        --abi)
+            target_abi="$2"
+            shift 2
+            ;;
+        --vktrace)
+            vktrace_exe="$2"
+            shift 2
+            ;;
+        --package)
+            package="$2"
+            shift 2
+            ;;
+        --activity)
+            activity="$2"
+            shift 2
+            ;;
+        --frame)
+            frame="$2"
+            shift 2
+            ;;
+        -*)
+            # unknown option
+            echo Unknown option: $1
+            echo
+            printUsage
+            exit 1
+            ;;
+    esac
+done
+
+echo serial = $serial
+
+if [[ -z $serial ]]
+then
+    echo Please provide a serial number.
+    echo
+    printUsage
+    exit 1
+fi
+
+if [[ $(adb devices) != *"$serial"* ]];
+then
+    echo Device not found: $serial
+    echo
+    printUsage
+    exit 1
+fi
+
+if [[ -z $target_abi ]];
+then
+    echo Using default target_abi
+    target_abi=$default_target_abi
+fi
+echo target_abi = $target_abi
+
+if [[ $target_abi == "armeabi-v7a" ]] ||
+   [[ $target_abi == "mips" ]] ||
+   [[ $target_abi == "x86" ]];
+then
+    echo Targeting 32-bit abi $target_abi
+    target_32bit_abi=1
+fi
+
+if [[ -z $vktrace_exe ]];
+then
+    echo Using default vktrace_exe
+    vktrace_exe=$default_vktrace_exe
+    if [[ $target_32bit_abi ]]
+    then
+        vktrace_exe=$default_vktrace32_exe
+    fi
+else
+    if [[ $target_32bit_abi ]]
+    then
+       echo Ensure your vktrace is 32-bit, i.e. vktrace32
+    fi
+fi
+echo vktrace_exe = $vktrace_exe
+
+if [[ -z $package ]];
+then
+    echo target package name required
+    exit 1
+fi
+echo package = $package
+
+if [[ -z $activity ]];
+then
+    echo Using default activity
+    activity=$default_activity
+fi
+echo activity = $activity
+
+if [[ -z $frame ]];
+then
+    echo Not attempting to record any screenshot
+else
+    echo Attempting to record screenshot of frame $frame
+fi
+
+function printLayerBuild() {
+    echo "To build layers:"
+    echo "./update_external_sources_android.sh"
+    echo "./android-generate.sh"
+    echo "ndk-build -j"
+}
+
+function printvktraceBuild() {
+    echo "To build vktrace"
+    echo "pushd .."
+    echo "mkdir build"
+    echo "cd build"
+    echo "cmake -DCMAKE_BUILD_TYPE=Debug .."
+    echo "make -j"
+}
+
+#
+# If any parameter not found, print how to build it
+#
+
+if [ ! -f $vktrace_exe ]; then
+    echo "$vktrace_exe not found!"
+    printvktraceBuild
+    exit 1
+fi
+
+#
+# Check for required tools
+#
+
+adb_path=$(which adb)
+if [[ $? == 0 ]];
+then
+    echo using $adb_path
+else
+    echo adb not found, exiting
+    echo check your NDK for it and add to path
+    exit 1
+fi
+aapt_path=$(which aapt)
+if [[ $? == 0 ]];
+then
+    echo using $aapt_path
+else
+    echo aapt not found, exiting
+    echo check your NDK for it and add to path
+    exit 1
+fi
+jar_path=$(which jar)
+if [[ $? == 0 ]];
+then
+    echo using $jar_path
+else
+    echo jar not found, exiting
+    exit 1
+fi
+
+apk_contents=$(jar tvf $apk)
+if [[ $apk_contents != *"libVkLayer_screenshot.so"* ]] ||
+   [[ $apk_contents != *"libVkLayer_vktrace_layer.so"* ]];
+then
+    echo Your APK does not contain the following layers:
+    echo     libVkLayer_screenshot.so
+    echo     libVkLayer_vktrace_layer.so
+    echo You\'ll need to provide them another way.
+    echo Continuing...
+fi
+
+#
+# Start up
+#
+
+# We want to halt on errors here
+set -e
+
+# Wake up the device
+adb $serialFlag shell input keyevent "KEYCODE_MENU"
+adb $serialFlag shell input keyevent "KEYCODE_HOME"
+
+# clean up anything lingering from previous runs
+adb $serialFlag shell am force-stop $package
+
+# Ensure vktrace wasn't already running
+let "script_run_time=$(date +%s)-$script_start_time"
+killall --older-than "$script_run_time"s vktrace || echo continuing...
+
+# Enable trace layer
+adb $serialFlag shell setprop debug.vulkan.layer.1 VK_LAYER_LUNARG_vktrace
+
+# Start vktrace
+adb $serialFlag reverse localabstract:vktrace tcp:34201
+sleep 1
+$vktrace_exe -v full -o $package.vktrace &
+
+# Start the target activity
+adb $serialFlag shell am start $package/$activity
+
+# wait for a keystroke, indicating when tracing should stop
+read -rsn1
+
+# stop our background vktrace
+kill $!
+
+# clean up
+adb $serialFlag shell am force-stop $package
+adb $serialFlag shell setprop debug.vulkan.layer.1 '""'
+
+exit 0
diff --git a/build-android/jni/Android.mk b/build-android/jni/Android.mk
index 4577d0b..60e969f 100644
--- a/build-android/jni/Android.mk
+++ b/build-android/jni/Android.mk
@@ -137,6 +137,34 @@
 LOCAL_LDFLAGS   += -Wl,--exclude-libs,ALL
 include $(BUILD_SHARED_LIBRARY)
 
+include $(CLEAR_VARS)
+LOCAL_MODULE := VkLayer_api_dump
+LOCAL_SRC_FILES += $(LAYER_DIR)/layer-src/api_dump/api_dump.cpp
+LOCAL_SRC_FILES += $(LAYER_DIR)/common/vk_layer_table.cpp
+LOCAL_C_INCLUDES += $(SRC_DIR)/include \
+                    $(SRC_DIR)/layers \
+                    $(SRC_DIR)/layersvt \
+                    $(LAYER_DIR)/include \
+                    $(SRC_DIR)/loader
+LOCAL_STATIC_LIBRARIES += layer_utils
+LOCAL_CPPFLAGS += -DVK_USE_PLATFORM_ANDROID_KHR
+LOCAL_LDLIBS    := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := VkLayer_screenshot
+LOCAL_SRC_FILES += $(LAYER_DIR)/layer-src/screenshot/screenshot.cpp
+LOCAL_SRC_FILES += $(LAYER_DIR)/common/vk_layer_table.cpp
+LOCAL_C_INCLUDES += $(SRC_DIR)/include \
+                    $(SRC_DIR)/layers \
+                    $(SRC_DIR)/layersvt \
+                    $(LAYER_DIR)/include \
+                    $(SRC_DIR)/loader
+LOCAL_STATIC_LIBRARIES += layer_utils
+LOCAL_CPPFLAGS += -DVK_USE_PLATFORM_ANDROID_KHR
+LOCAL_LDLIBS    := -llog
+include $(BUILD_SHARED_LIBRARY)
+
 # Pull in prebuilt shaderc
 include $(CLEAR_VARS)
 LOCAL_MODULE := shaderc-prebuilt
@@ -225,5 +253,82 @@
 LOCAL_LDLIBS := -llog -landroid
 include $(BUILD_SHARED_LIBRARY)
 
+include $(CLEAR_VARS)
+LOCAL_MODULE := VkLayer_vktrace_layer
+LOCAL_SRC_FILES += $(LAYER_DIR)/include/vktrace_vk_vk.cpp
+LOCAL_SRC_FILES += $(LAYER_DIR)/include/vk_struct_size_helper.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_trace_packet_utils.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_filelike.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_interconnect.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_platform.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_process.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_settings.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_tracelog.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_vk_exts.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pageguard.cpp
+
+LOCAL_C_INCLUDES += $(SRC_DIR)/vktrace/include \
+		    $(SRC_DIR)/include \
+                    $(SRC_DIR)/layers \
+                    $(LAYER_DIR)/include \
+                    $(SRC_DIR)/vktrace/src/vktrace_common \
+                    $(SRC_DIR)/vktrace/src/vktrace_layer \
+                    $(SRC_DIR)/loader \
+                    $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.h \
+                    $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.h \
+                    $(SRC_DIR)/vktrace/src/vktrace_layer/vktrace_lib_pageguard.h \
+                    $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.h
+LOCAL_STATIC_LIBRARIES += layer_utils
+LOCAL_CPPFLAGS += -DVK_USE_PLATFORM_ANDROID_KHR
+LOCAL_CPPFLAGS += -DPLATFORM_LINUX=1
+LOCAL_CPPFLAGS += -DPAGEGUARD_MEMCPY_USE_PPL_LIB
+LOCAL_CFLAGS += -DPLATFORM_LINUX=1
+LOCAL_CFLAGS += -DPLATFORM_POSIX=1
+LOCAL_LDLIBS    := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vkreplay
+LOCAL_SRC_FILES += $(LAYER_DIR)/include/vk_struct_size_helper.c
+LOCAL_SRC_FILES += $(LAYER_DIR)/include/vkreplay_vk_replay_gen.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_trace_packet_utils.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_filelike.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_interconnect.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_platform.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_process.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_settings.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_common/vktrace_tracelog.c
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_replay/vkreplay_factory.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_replay/vkreplay_main.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_replay/vkreplay_seq.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.cpp
+LOCAL_SRC_FILES += $(SRC_DIR)/common/vulkan_wrapper.cpp
+LOCAL_C_INCLUDES += $(SRC_DIR)/vktrace/include \
+                    $(SRC_DIR)/include \
+                    $(SRC_DIR)/include/vulkan \
+                    $(SRC_DIR)/layers \
+                    $(LAYER_DIR)/include \
+                    $(SRC_DIR)/vktrace/src/vktrace_common \
+                    $(SRC_DIR)/vktrace/src/vktrace_layer \
+                    $(SRC_DIR)/vktrace/src/vktrace_replay \
+                    $(SRC_DIR)/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay \
+                    $(SRC_DIR)/loader
+LOCAL_STATIC_LIBRARIES += layer_utils android_native_app_glue
+LOCAL_SHARED_LIBRARIES += VkLayer_vktrace_layer
+LOCAL_CPPFLAGS += -DVK_USE_PLATFORM_ANDROID_KHR -DAPI_LOWERCASE=\"vulkan\" --include=$(SRC_DIR)/common/vulkan_wrapper.h -fexceptions
+LOCAL_CPPFLAGS += -DPLATFORM_LINUX=1
+LOCAL_CFLAGS += -DPLATFORM_LINUX=1
+LOCAL_CFLAGS += -DPLATFORM_POSIX=1
+LOCAL_LDLIBS    := -llog -landroid
+include $(BUILD_SHARED_LIBRARY)
+
 $(call import-module,android/native_app_glue)
 $(call import-module,third_party/googletest)
diff --git a/build-android/jni/Application.mk b/build-android/jni/Application.mk
index 8b4fb09..a7f19db 100644
--- a/build-android/jni/Application.mk
+++ b/build-android/jni/Application.mk
@@ -16,6 +16,6 @@
 APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 mips mips64

 APP_PLATFORM := android-22

 APP_STL := gnustl_static

-APP_MODULES := layer_utils VkLayer_core_validation VkLayer_image VkLayer_parameter_validation VkLayer_object_tracker VkLayer_threading VkLayer_swapchain VkLayer_unique_objects VkLayerValidationTests VulkanLayerValidationTests

+APP_MODULES := layer_utils VkLayer_core_validation VkLayer_image VkLayer_parameter_validation VkLayer_object_tracker VkLayer_threading VkLayer_swapchain VkLayer_unique_objects VkLayer_api_dump VkLayer_screenshot VkLayerValidationTests VkLayer_vktrace_layer vkreplay

 APP_CPPFLAGS += -std=c++11 -DVK_PROTOTYPES -Wall -Werror -Wno-unused-function -Wno-unused-const-variable -mxgot

 NDK_TOOLCHAIN_VERSION := clang

diff --git a/build-android/replay_trace.sh b/build-android/replay_trace.sh
new file mode 100755
index 0000000..b7e30e1
--- /dev/null
+++ b/build-android/replay_trace.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+#
+# Parse parameters
+#
+
+function printUsage {
+   echo "Supported parameters are:"
+   echo "    --serial <target device serial number>"
+   echo "    --tracefile <path to tracefile>"
+   echo
+   echo "i.e. ${0##*/} --serial 01234567 \\"
+   echo "              --tracefile cube.vktrace \\"
+   exit 1
+}
+
+if [[ $(($# % 2)) -ne 0 ]]
+then
+    echo Parameters must be provided in pairs.
+    echo parameter count = $#
+    echo
+    printUsage
+    exit 1
+fi
+
+while [[ $# -gt 0 ]]
+do
+    case $1 in
+        --serial)
+            # include the flag, because we need to leave it off if not provided
+            serial="$2"
+            serialFlag="-s $serial"
+            shift 2
+            ;;
+        --tracefile)
+            tracefile="$2"
+            shift 2
+            ;;
+        -*)
+            # unknown option
+            echo Unknown option: $1
+            echo
+            printUsage
+            exit 1
+            ;;
+    esac
+done
+
+echo serial = $serial
+
+if [[ -z $serial ]]
+then
+    echo Please provide a serial number.
+    echo
+    printUsage
+    exit 1
+fi
+
+if [[ $(adb devices) != *"$serial"* ]];
+then
+    echo Device not found: $serial
+    echo
+    printUsage
+    exit 1
+fi
+
+function printvkreplayBuild() {
+    echo "To build vkreplay apk"
+    echo "android update project -s -p . -t \"android-23\""
+    echo "ndk-build -j"
+    echo "ant -buildfile vkreplay debug"
+}
+
+#
+# If any parameter not found, print how to build it
+#
+
+if [ ! -f $tracefile ]; then
+    echo "$tracefile not found!"
+    exit 1
+fi
+
+#
+# Check for required tools
+#
+
+adb_path=$(which adb)
+if [[ $? == 0 ]];
+then
+    echo using $adb_path
+else
+    echo adb not found, exiting
+    echo check your NDK for it and add to path
+    exit 1
+fi
+
+#
+# Start up
+#
+
+# We want to halt on errors here
+set -e
+
+# clean up anything lingering from previous runs
+adb $serialFlag shell am force-stop com.example.vkreplay
+
+# push the trace to the device
+adb $serialFlag push $tracefile /sdcard/$tracefile
+
+# replay and screenshot
+adb $serialFlag shell pm grant com.example.vkreplay android.permission.READ_EXTERNAL_STORAGE
+adb $serialFlag shell pm grant com.example.vkreplay android.permission.WRITE_EXTERNAL_STORAGE
+sleep 1 # small pause to allow permission to take
+
+# Wake up the device
+adb $serialFlag shell input keyevent "KEYCODE_MENU"
+adb $serialFlag shell input keyevent "KEYCODE_HOME"
+
+# Start the replay
+adb $serialFlag shell am start -a android.intent.action.MAIN -c android-intent.category.LAUNCH -n com.example.vkreplay/android.app.NativeActivity --es args "-v\ full\ -t\ /sdcard/$tracefile"
+
+exit 0
diff --git a/build-android/vkreplay/AndroidManifest.xml b/build-android/vkreplay/AndroidManifest.xml
new file mode 100644
index 0000000..bdd315e
--- /dev/null
+++ b/build-android/vkreplay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>

+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.vkreplay" android:versionCode="1" android:versionName="1.0">

+

+    <!-- This is the platform API where NativeActivity was introduced. -->

+    <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="23"/>

+

+    <!-- This allows reading trace file from sdcard -->

+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

+

+    <!-- This .apk has no Java code itself, so set hasCode to false. -->

+    <application android:label="@string/app_name" android:hasCode="false" android:debuggable='true'>

+

+        <!-- Our activity is the built-in NativeActivity framework class.

+             This will take care of integrating with our NDK code. -->

+        <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:exported="true">

+            <!-- Tell NativeActivity the name of or .so -->

+            <meta-data android:name="android.app.lib_name" android:value="vkreplay"/>

+            <intent-filter>

+                <action android:name="android.intent.action.MAIN"/>

+                <category android:name="android.intent.category.LAUNCHER"/>

+            </intent-filter>

+        </activity>

+    </application>

+

+</manifest>

diff --git a/build-android/vkreplay/custom_rules.xml b/build-android/vkreplay/custom_rules.xml
new file mode 100644
index 0000000..697cb8c
--- /dev/null
+++ b/build-android/vkreplay/custom_rules.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="NativeActivity" default="help">
+<!-- Point ndk-build at the libs created in build-android -->
+<echo>vkreplay: Overriding native.libs.absolute.dir with ../libs</echo>
+<property name="native.libs.absolute.dir" location="../libs" />
+</project>
diff --git a/build-android/vkreplay/res/values/strings.xml b/build-android/vkreplay/res/values/strings.xml
new file mode 100644
index 0000000..70897c3
--- /dev/null
+++ b/build-android/vkreplay/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="app_name">vkreplay</string>
+
+</resources>
diff --git a/build-android/vktracereplay.bat b/build-android/vktracereplay.bat
new file mode 100644
index 0000000..2250e55
--- /dev/null
+++ b/build-android/vktracereplay.bat
@@ -0,0 +1,240 @@
+@echo off

+REM # Copyright 2016 The Android Open Source Project

+REM # Copyright (C) 2016 Valve Corporation

+REM

+REM # Licensed under the Apache License, Version 2.0 (the "License");

+REM # you may not use this file except in compliance with the License.

+REM # You may obtain a copy of the License at

+REM

+REM #      http://www.apache.org/licenses/LICENSE-2.0

+REM

+REM # Unless required by applicable law or agreed to in writing, software

+REM # distributed under the License is distributed on an "AS IS" BASIS,

+REM # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+REM # See the License for the specific language governing permissions and

+REM # limitations under the License.

+

+setlocal EnableDelayedExpansion

+

+set vktrace_exe=..\build\vktrace\Debug\vktrace.exe

+set vktrace32_exe=..\build32\vktrace\Debug\vktrace.exe

+set vkreplay_apk=.\vkreplay\bin\NativeActivity-debug.apk

+set activity=android.app.NativeActivity

+set frame=1

+for /f "delims=" %%i in ('adb shell getprop ro.product.cpu.abi') do set target_abi=%%i

+

+REM // ======== Parameter parsing ======== //

+

+   if "%1" == "" (

+       echo Supported parameters are:

+       echo     --serial ^<target device serial number^>

+       echo     --abi ^<abi to install^>

+       echo     --vktrace ^<full path to vktrace on host^> ^(optional^)

+       echo     --vkreplay ^<full path to vkreplay APK^> ^(optional^)

+       echo     --apk ^<full path to APK^>

+       echo     --package ^<package name^>

+       echo     --actvity ^<launchable-activity name^>

+       echo     --frame ^<frame number to capture^>

+       echo. 

+       echo i.e. %~nx0 --serial 01234567

+       echo                        --abi arm64-v8a

+       echo                        --vktrace ../build/vktrace/vktrace

+       echo                        --vkreplay ./vkreplay/bin/NativeActivity-debug.apk

+       echo                        --apk ~/Downloads/foo.apk.apk

+       echo                        --package com.example.foo

+       echo                        --actvity android.app.NativeActivity

+       echo                        --frame 1

+      goto:finish

+   )

+

+   REM Check for even parameter count

+   set arg_count=0

+   for %%x in (%*) do set /A arg_count+=1

+   set /A even_args=arg_count%2

+   if %even_args% neq 0 (

+       echo Arguments must be provided in pairs.

+       goto:finish

+   )

+   

+   :parameterLoop

+

+      if "%1"=="" goto:parameterContinue

+

+      if "%1" == "--serial" (

+         set serial=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--abi" (

+         set target_abi=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--vktrace" (

+         set vktrace_exe=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--vkreplay" (

+         set vkreplay_apk=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--apk" (

+         set apk=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--package" (

+         set package=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--activity" (

+         set activity=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      if "%1" == "--frame" (

+         set frame=%2

+         shift

+         shift

+         goto:parameterLoop

+      )

+

+      echo Unrecognized options "%1"

+      goto:error

+

+   :parameterContinue

+

+REM // ======== end Parameter parsing ======== //

+

+REM Match 32-bit and 64-bit ABIs between host and target

+if "%target_abi%" == "armeabi-v7a" ( set vktrace_exe=%vktrace32_exe% )

+if "%target_abi%" == "x86"         ( set vktrace_exe=%vktrace32_exe% )

+if "%target_abi%" == "mips"        ( set vktrace_exe=%vktrace32_exe% )

+

+

+echo serial = %serial%

+echo target_abi = %target_abi%

+echo vktrace_exe = %vktrace_exe%

+echo vkreplay_apk = %vkreplay_apk%

+echo apk = %apk%

+echo package = %package%

+echo activity = %activity%

+echo frame = %frame%

+

+

+REM Wake up the device

+adb -s %serial% shell input keyevent "KEYCODE_MENU"

+adb -s %serial% shell input keyevent "KEYCODE_HOME"

+

+REM clean up anything lingering from previous runs

+adb -s %serial% shell am force-stop %package%

+adb -s %serial% shell am force-stop com.example.vkreplay

+

+adb -s %serial% uninstall %package% && echo continuing...

+adb -s %serial% uninstall com.example.vkreplay && echo continuing...

+

+adb -s %serial% shell rm -f /sdcard/Android/%frame%.ppm

+adb -s %serial% shell rm -f /sdcard/Android/%package%.%frame%.vktrace.ppm

+adb -s %serial% shell rm -f /sdcard/Android/%package%.%frame%.vkreplay.ppm

+del /Q /F %package%.%frame%.vktrace.ppm

+del /Q /F %package%.%frame%.vkreplay.ppm

+

+REM install the latest APK, possibly packaged with vktrace and screenshot

+adb -s %serial% install --abi %target_abi% %apk%

+

+REM install vkreplay APK

+adb -s %serial% install --abi %target_abi% %vkreplay_apk%

+

+REM trace and screenshot

+adb -s %serial% shell setprop debug.vulkan.layer.1 VK_LAYER_LUNARG_vktrace

+adb -s %serial% shell setprop debug.vulkan.layer.2 VK_LAYER_LUNARG_screenshot

+adb -s %serial% shell pm grant %package% android.permission.READ_EXTERNAL_STORAGE

+adb -s %serial% shell pm grant %package% android.permission.WRITE_EXTERNAL_STORAGE

+adb -s %serial% shell setprop debug.vulkan.screenshot %frame%

+

+REM vktrace

+adb -s %serial% reverse localabstract:vktrace tcp:34201

+start /b %vktrace_exe% -v full -o %package%.vktrace

+adb -s %serial% shell am start %package%/%activity%

+

+

+REM WAIT HERE FOR SCREENSHOT

+REM TODO THIS SHOULD BE A LOOP

+timeout /t 5

+

+taskkill /f /im vktrace.exe

+taskkill /f /im vktrace32.exe

+

+REM set up for vkreplay

+adb -s %serial% shell am force-stop %package%

+adb -s %serial% push %package%0.vktrace /sdcard/%package%.vktrace

+

+REM grab the screenshot

+adb -s %serial% pull /sdcard/Android/%frame%.ppm %package%.%frame%.vktrace.ppm

+adb -s %serial% shell mv /sdcard/Android/%frame%.ppm /sdcard/Android/%package%.%frame%.vktrace.ppm

+

+REM replay and screenshot

+adb -s %serial% shell setprop debug.vulkan.layer.1 VK_LAYER_LUNARG_screenshot

+adb -s %serial% shell setprop debug.vulkan.layer.2 '""'

+adb -s %serial% shell setprop debug.vulkan.screenshot %frame%

+adb -s %serial% shell pm grant com.example.vkreplay android.permission.READ_EXTERNAL_STORAGE

+adb -s %serial% shell pm grant com.example.vkreplay android.permission.WRITE_EXTERNAL_STORAGE

+REM small pause to allow permission to take

+timeout /t 5

+

+REM Wake up the device

+adb -s %serial% shell input keyevent "KEYCODE_MENU"

+adb -s %serial% shell input keyevent "KEYCODE_HOME"

+

+adb -s %serial% shell am start -a android.intent.action.MAIN -c android-intent.category.LAUNCH -n com.example.vkreplay/android.app.NativeActivity --es args "-v\ full\ -t\ /sdcard/%package%.vktrace"

+

+REM WAIT HERE FOR SCREENSHOT

+REM TODO THIS SHOULD BE A LOOP

+timeout /t 5

+

+REM grab the screenshot

+adb -s %serial% pull /sdcard/Android/%frame%.ppm %package%.%frame%.vkreplay.ppm

+adb -s %serial% shell mv /sdcard/Android/%frame%.ppm /sdcard/Android/%package%.%frame%.vkreplay.ppm

+

+REM clean up

+adb -s %serial% shell am force-stop com.example.vkreplay

+adb -s %serial% shell setprop debug.vulkan.layer.1 '""'

+

+REM compare screenshots

+fc.exe /b %package%.%frame%.vktrace.ppm %package%.%frame%.vkreplay.ppm

+ 

+if %errorlevel% neq 0 (

+    echo Images DO NOT match - Failed

+    exit /b 1

+)

+

+if %errorlevel% equ 0 (

+    echo Images match - PASSED

+    exit /b 0

+)

+

+:error

+echo.

+echo Halting due to error

+goto:finish

+

+:finish

+endlocal

+goto:eof

diff --git a/build-android/vktracereplay.sh b/build-android/vktracereplay.sh
new file mode 100755
index 0000000..bf9a0c5
--- /dev/null
+++ b/build-android/vktracereplay.sh
@@ -0,0 +1,446 @@
+#!/bin/bash
+
+#set -vex
+
+script_start_time=$(date +%s)
+
+default_vkreplay_apk=./vkreplay/bin/NativeActivity-debug.apk
+default_vktrace_exe=../build/vktrace/vktrace
+default_vktrace32_exe=../build32/vktrace/vktrace32
+default_target_abi=$(adb shell getprop ro.product.cpu.abi)
+default_activity=android.app.NativeActivity
+default_frame=1
+
+#if [[ $(basename "$PWD") != "build-android" ]]
+#then
+#    echo "Please run this script from the build-android directory"
+#    exit 1
+#fi
+
+#
+# Parse parameters
+#
+
+function printUsage {
+   echo "Supported parameters are:"
+   echo "    --serial <target device serial number>"
+   echo "    --abi <abi to install>"
+   echo "    --vktrace <full path to vktrace on host> (optional)"
+   echo "    --vkreplay <full path to vkreplay APK> (optional)"
+   echo "    --apk <full path to APK>"
+   echo "    --package <package name>"
+   echo "    --actvity <launchable-activity name>"
+   echo "    --frame <frame number to capture>"
+   echo
+   echo "i.e. ${0##*/} --serial 01234567 \\"
+   echo "              --abi arm64-v8a \\"
+   echo "              --vktrace ../build/vktrace/vktrace \\"
+   echo "              --vkreplay ./vkreplay/bin/NativeActivity-debug.apk \\"
+   echo "              --apk ~/Downloads/foo.apk.apk \\"
+   echo "              --package com.example.foo \\"
+   echo "              --actvity android.app.NativeActivity \\"
+   echo "              --frame 1"
+   exit 1
+}
+
+if [[ $(($# % 2)) -ne 0 ]]
+then
+    echo Parameters must be provided in pairs.
+    echo parameter count = $#
+    echo
+    printUsage
+    exit 1
+fi
+
+while [[ $# -gt 0 ]]
+do
+    case $1 in
+        --serial)
+            # include the flag, because we need to leave it off if not provided
+            serial="$2"
+            serialFlag="-s $serial"
+            shift 2
+            ;;
+        --abi)
+            target_abi="$2"
+            shift 2
+            ;;
+        --vktrace)
+            vktrace_exe="$2"
+            shift 2
+            ;;
+        --vkreplay)
+            vkreplay_apk="$2"
+            shift 2
+            ;;
+        --apk)
+            apk="$2"
+            shift 2
+            ;;
+        --package)
+            package="$2"
+            shift 2
+            ;;
+        --activity)
+            activity="$2"
+            shift 2
+            ;;
+        --frame)
+            frame="$2"
+            shift 2
+            ;;
+        -*)
+            # unknown option
+            echo Unknown option: $1
+            echo
+            printUsage
+            exit 1
+            ;;
+    esac
+done
+
+echo serial = $serial
+
+if [[ -z $serial ]]
+then
+    echo Please provide a serial number.
+    echo
+    printUsage
+    exit 1
+fi
+
+if [[ $(adb devices) != *"$serial"* ]];
+then
+    echo Device not found: $serial
+    echo
+    printUsage
+    exit 1
+fi
+
+if [[ -z $target_abi ]];
+then
+    echo Using default target_abi
+    target_abi=$default_target_abi
+fi
+echo target_abi = $target_abi
+
+if [[ $target_abi == "armeabi-v7a" ]] ||
+   [[ $target_abi == "mips" ]] ||
+   [[ $target_abi == "x86" ]];
+then
+    echo Targeting 32-bit abi $target_abi
+    target_32bit_abi=1
+fi
+
+if [[ -z $vktrace_exe ]];
+then
+    echo Using default vktrace_exe
+    vktrace_exe=$default_vktrace_exe
+    if [[ $target_32bit_abi ]]
+    then
+        vktrace_exe=$default_vktrace32_exe
+    fi
+else
+    if [[ $target_32bit_abi ]]
+    then
+       echo Ensure your vktrace is 32-bit, i.e. vktrace32
+    fi
+fi
+echo vktrace_exe = $vktrace_exe
+
+if [[ -z $vkreplay_apk ]];
+then
+    echo Using default vkreplay_apk
+    vkreplay_apk=$default_vkreplay_apk
+fi
+echo vkreplay_apk = $vkreplay_apk
+
+if [[ -z $apk ]];
+then
+    echo target APK required
+    exit 1
+fi
+echo apk = $apk
+
+if [[ -z $package ]];
+then
+    echo target package name required
+    exit 1
+fi
+echo package = $package
+
+if [[ -z $activity ]];
+then
+    echo Using default activity
+    activity=$default_activity
+fi
+echo activity = $activity
+
+if [[ -z $frame ]];
+then
+    echo Using default screenshot frame
+    frame=$default_frame
+fi
+echo frame = $frame
+
+function printLayerBuild() {
+    echo "To build layers:"
+    echo "./update_external_sources_android.sh"
+    echo "./android-generate.sh"
+    echo "ndk-build -j"
+}
+
+function printvkreplayBuild() {
+    echo "To build vkreplay apk"
+    echo "android update project -s -p . -t \"android-23\""
+    echo "ndk-build -j"
+    echo "ant -buildfile vkreplay debug"
+}
+
+function printvktraceBuild() {
+    echo "To build vktrace"
+    echo "pushd .."
+    echo "mkdir build"
+    echo "cd build"
+    echo "cmake -DCMAKE_BUILD_TYPE=Debug .."
+    echo "make -j"
+}
+
+#
+# If any parameter not found, print how to build it
+#
+
+if [ ! -f $vkreplay_apk ]; then
+    echo "$vkreplay_apk not found!"
+    printvkreplayBuild
+    exit 1
+fi
+
+if [ ! -f $vktrace_exe ]; then
+    echo "$vktrace_exe not found!"
+    printvktraceBuild
+    exit 1
+fi
+
+#
+# Check for required tools
+#
+
+adb_path=$(which adb)
+if [[ $? == 0 ]];
+then
+    echo using $adb_path
+else
+    echo adb not found, exiting
+    echo check your NDK for it and add to path
+    exit 1
+fi
+aapt_path=$(which aapt)
+if [[ $? == 0 ]];
+then
+    echo using $aapt_path
+else
+    echo aapt not found, exiting
+    echo check your NDK for it and add to path
+    exit 1
+fi
+jar_path=$(which jar)
+if [[ $? == 0 ]];
+then
+    echo using $jar_path
+else
+    echo jar not found, exiting
+    exit 1
+fi
+
+#
+# Ensure APKs can be traced
+#
+
+apk_badging=$(aapt dump badging $apk)
+if [[ $apk_badging != *"uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'"* ]] ||
+   [[ $apk_badging != *"uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'"* ]];
+then
+    echo Please package APK with the following permissions:
+    echo     android.permission.READ_EXTERNAL_STORAGE
+    echo     android.permission.WRITE_EXTERNAL_STORAGE
+    exit 1
+fi
+apk_contents=$(jar tvf $apk)
+if [[ $apk_contents != *"libVkLayer_screenshot.so"* ]] ||
+   [[ $apk_contents != *"libVkLayer_vktrace_layer.so"* ]];
+then
+    echo Your APK does not contain the following layers:
+    echo     libVkLayer_screenshot.so
+    echo     libVkLayer_vktrace_layer.so
+    echo You\'ll need to provide them another way.
+    echo Continuing...
+fi
+
+#
+# Start up
+#
+
+# We want to halt on errors here
+set -e
+
+# Wake up the device
+adb $serialFlag shell input keyevent "KEYCODE_MENU"
+adb $serialFlag shell input keyevent "KEYCODE_HOME"
+
+# clean up anything lingering from previous runs
+adb $serialFlag shell am force-stop $package
+adb $serialFlag shell am force-stop com.example.vkreplay
+if [[ $(adb $serialFlag shell pm list packages $package) == "package:$package" ]]
+then
+    adb $serialFlag uninstall $package && echo continuing...
+fi
+if [[ $(adb $serialFlag shell pm list packages com.example.vkreplay) == "package:com.example.vkreplay" ]]
+then
+    adb $serialFlag uninstall com.example.vkreplay && echo continuing...
+fi
+adb $serialFlag shell rm -f /sdcard/Android/$frame.ppm
+adb $serialFlag shell rm -f /sdcard/Android/$package.$frame.vktrace.ppm
+adb $serialFlag shell rm -f /sdcard/Android/$package.$frame.vkreplay.ppm
+rm -f $package.$frame.vktrace.ppm
+rm -f $package.$frame.vkreplay.ppm
+rm -f $package.vktrace
+rm -f $package\0.vktrace
+
+# Ensure vktrace wasn't already running
+let "script_run_time=$(date +%s)-$script_start_time"
+killall --older-than "$script_run_time"s vktrace || echo continuing...
+
+# install the latest APK, possibly packaged with vktrace and screenshot
+adb $serialFlag install --abi $target_abi $apk
+
+# install vkreplay APK
+adb $serialFlag install --abi $target_abi $vkreplay_apk
+
+# trace and screenshot
+adb $serialFlag shell setprop debug.vulkan.layer.1 VK_LAYER_LUNARG_vktrace
+adb $serialFlag shell setprop debug.vulkan.layer.2 VK_LAYER_LUNARG_screenshot
+adb $serialFlag shell pm grant $package android.permission.READ_EXTERNAL_STORAGE
+adb $serialFlag shell pm grant $package android.permission.WRITE_EXTERNAL_STORAGE
+adb $serialFlag shell setprop debug.vulkan.screenshot $frame
+
+# vktrace
+adb $serialFlag reverse localabstract:vktrace tcp:34201
+$vktrace_exe -v full -o $package.vktrace &
+adb $serialFlag shell am start $package/$activity
+
+# don't halt on error for this loop
+set +e
+
+# wait until trace screenshot arrives, or a timeout
+vktrace_seconds=300                                    # Duration in seconds.
+vktrace_end_time=$(( $(date +%s) + vktrace_seconds ))  # Calculate end time.
+sleep 5 # pause to let the screenshot write finish
+until adb $serialFlag shell ls -la /sdcard/Android/$frame.ppm
+do
+    echo "Waiting for $package.vktrace screenshot on $serial"
+
+    if [ $(date +%s) -gt $vktrace_end_time ]
+    then
+        echo "vktrace timeout reached: $vktrace_seconds seconds"
+        echo "No vktrace screenshot, closing down"
+        exit 1
+    fi
+
+    sleep 5
+done
+
+# stop our background vktrace
+kill $!
+
+# pause for a moment to let our trace file finish writing
+sleep 1
+
+# halt on errors here
+set -e
+
+# set up for vkreplay
+adb $serialFlag shell am force-stop $package
+if [ -f $package.vktrace ]; then
+    adb $serialFlag push $package.vktrace /sdcard/$package.vktrace
+fi
+if [ -f $package\0.vktrace ]; then
+    adb $serialFlag push $package\0.vktrace /sdcard/$package.vktrace
+fi
+
+# grab the screenshot
+adb $serialFlag pull /sdcard/Android/$frame.ppm $package.$frame.vktrace.ppm
+adb $serialFlag shell mv /sdcard/Android/$frame.ppm /sdcard/Android/$package.$frame.vktrace.ppm
+
+# replay and screenshot
+adb $serialFlag shell setprop debug.vulkan.layer.1 VK_LAYER_LUNARG_screenshot
+adb $serialFlag shell setprop debug.vulkan.layer.2 '""'
+adb $serialFlag shell setprop debug.vulkan.screenshot $frame
+adb $serialFlag shell pm grant com.example.vkreplay android.permission.READ_EXTERNAL_STORAGE
+adb $serialFlag shell pm grant com.example.vkreplay android.permission.WRITE_EXTERNAL_STORAGE
+sleep 5 # small pause to allow permission to take
+
+# Wake up the device
+adb $serialFlag shell input keyevent "KEYCODE_MENU"
+adb $serialFlag shell input keyevent "KEYCODE_HOME"
+
+adb $serialFlag shell am start -a android.intent.action.MAIN -c android-intent.category.LAUNCH -n com.example.vkreplay/android.app.NativeActivity --es args "-v\ full\ -t\ /sdcard/$package.vktrace"
+
+# don't halt on the errors in this loop
+set +e
+
+# wait until vkreplay screenshot arrives, or a timeout
+vkreplay_seconds=300                                     # Duration in seconds.
+vkreplay_end_time=$(( $(date +%s) + vkreplay_seconds ))  # Calculate end time.
+sleep 5 # pause to let the screenshot write finish
+until adb $serialFlag shell ls -la /sdcard/Android/$frame.ppm
+do
+    echo "Waiting for vkreplay screenshot on $serial"
+
+    if [ $(date +%s) -gt $vkreplay_end_time ]
+    then
+        echo "vkreplay timeout reached: $vkreplay_seconds seconds"
+        echo "No vkreplay screenshot, closing down"
+        exit 1
+    fi
+    sleep 5
+done
+
+# halt on any errors here
+set -e
+
+# grab the screenshot
+adb $serialFlag pull /sdcard/Android/$frame.ppm $package.$frame.vkreplay.ppm
+adb $serialFlag shell mv /sdcard/Android/$frame.ppm /sdcard/Android/$package.$frame.vkreplay.ppm
+
+# clean up
+adb $serialFlag shell am force-stop com.example.vkreplay
+adb $serialFlag shell setprop debug.vulkan.layer.1 '""'
+
+# don't halt in the exit code below
+set +e
+
+# the rest is a quick port from vktracereplay.sh
+
+if [ -t 1 ] ; then
+    RED='\033[0;31m'
+    GREEN='\033[0;32m'
+    NC='\033[0m' # No Color
+else
+    RED=''
+    GREEN=''
+    NC=''
+fi
+
+cmp -s $package.$frame.vktrace.ppm $package.$frame.vkreplay.ppm
+
+if [ $? -eq 0 ] ; then
+    printf "$GREEN[  PASSED  ]$NC {$apk-$package}\n"
+else
+    printf "$RED[  FAILED  ]$NC screenshot file compare failed\n"
+    printf "$RED[  FAILED  ]$NC {$apk-$package}\n"
+    printf "TEST FAILED\n"
+    exit 1
+fi
+
+exit 0
diff --git a/cmake/FindPCIAccess.cmake b/cmake/FindPCIAccess.cmake
index 65f7d5c..d3c288c 100644
--- a/cmake/FindPCIAccess.cmake
+++ b/cmake/FindPCIAccess.cmake
@@ -1,6 +1,7 @@
 # - FindPCIAccess
 #
-# Copyright 2015 Valve Corporation
+# Copyright (C) 2015-2016 Valve Corporation
+# Copyright (C) 2015-2016 LunarG, Inc.
 
 find_package(PkgConfig)
 
diff --git a/cmake/FindPthreadStubs.cmake b/cmake/FindPthreadStubs.cmake
index 063bbe5..9fc33d5 100644
--- a/cmake/FindPthreadStubs.cmake
+++ b/cmake/FindPthreadStubs.cmake
@@ -1,6 +1,7 @@
 # - FindPthreadStubs
 #
-# Copyright (C) 2015 Valve Corporation
+# Copyright (C) 2015-2016 Valve Corporation
+# Copyright (C) 2015-2016 LunarG, Inc.
 
 find_package(PkgConfig)
 
diff --git a/cmake/FindUDev.cmake b/cmake/FindUDev.cmake
index e3d1699..d2727ad 100644
--- a/cmake/FindUDev.cmake
+++ b/cmake/FindUDev.cmake
@@ -1,6 +1,7 @@
 # - FindUDev
 #
-# Copyright (C) 2015 Valve Corporation
+# Copyright (C) 2015-2016 Valve Corporation
+# Copyright (C) 2015-2016 LunarG, Inc.
 
 find_package(PkgConfig)
 
diff --git a/cmake/FindValgrind.cmake b/cmake/FindValgrind.cmake
index 5c1fb56..fdc4d7a 100644
--- a/cmake/FindValgrind.cmake
+++ b/cmake/FindValgrind.cmake
@@ -1,6 +1,7 @@
 # - FindValgrind
 #
-# Copyright (C) 2015 Valve Corporation
+# Copyright (C) 2015-2016 Valve Corporation
+# Copyright (C) 2015-2016 LunarG, Inc.
 
 find_package(PkgConfig)
 
diff --git a/cmake/FindXCB.cmake b/cmake/FindXCB.cmake
index 2311591..360b8ee 100644
--- a/cmake/FindXCB.cmake
+++ b/cmake/FindXCB.cmake
@@ -1,6 +1,7 @@
 # - FindXCB
 #
-# Copyright (C) 2015 Valve Corporation
+# Copyright (C) 2015-2016 Valve Corporation
+# Copyright (C) 2015-2016 LunarG, Inc.
 
 find_package(PkgConfig)
 
diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt
index 9bf07e5..925d554 100644
--- a/demos/CMakeLists.txt
+++ b/demos/CMakeLists.txt
@@ -99,14 +99,10 @@
             COMMAND ${GLSLANG_VALIDATOR} -s -V -o cube-frag.spv ${PROJECT_SOURCE_DIR}/demos/cube.frag
             DEPENDS cube.frag ${GLSLANG_VALIDATOR}
             )
-    endif()         
+    endif()
 endif()
 
 if(WIN32)
-    include_directories (
-       "${PROJECT_SOURCE_DIR}/icd/common"
-       )
-
     set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")
     set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")
 endif()
diff --git a/external_revisions/jsoncpp_revision b/external_revisions/jsoncpp_revision
new file mode 100644
index 0000000..f2f09db
--- /dev/null
+++ b/external_revisions/jsoncpp_revision
@@ -0,0 +1 @@
+d8cd848ede1071a25846cd90b4fddf269d868ff1
\ No newline at end of file
diff --git a/layers/vk_layer_config.cpp b/layers/vk_layer_config.cpp
index d8fe87d..f5adb8c 100644
--- a/layers/vk_layer_config.cpp
+++ b/layers/vk_layer_config.cpp
@@ -43,6 +43,7 @@
 
   private:
     bool m_fileIsParsed;
+    std::string m_fileName;
     std::map<std::string, std::string> m_valueMap;
 
     void parseFile(const char *filename);
@@ -134,6 +135,13 @@
 // Constructor for ConfigFile. Initialize layers to log error messages to stdout by default. If a vk_layer_settings file is present,
 // its settings will override the defaults.
 ConfigFile::ConfigFile() : m_fileIsParsed(false) {
+
+#ifdef ANDROID
+    m_fileName = "/sdcard/Android/vk_layer_settings.txt";
+#else
+    m_fileName = "vk_layer_settings.txt";
+#endif
+
     m_valueMap["lunarg_core_validation.report_flags"] = "error";
     m_valueMap["lunarg_image.report_flags"] = "error";
     m_valueMap["lunarg_object_tracker.report_flags"] = "error";
diff --git a/layersvt/.clang-format b/layersvt/.clang-format
new file mode 100644
index 0000000..fca6670
--- /dev/null
+++ b/layersvt/.clang-format
@@ -0,0 +1,7 @@
+---
+# We'll use defaults from the LLVM style, but with 4 columns indentation.
+BasedOnStyle: LLVM
+IndentWidth: 4
+ColumnLimit: 132
+SortIncludes: false
+...
diff --git a/layersvt/CMakeLists.txt b/layersvt/CMakeLists.txt
new file mode 100644
index 0000000..c22959a
--- /dev/null
+++ b/layersvt/CMakeLists.txt
@@ -0,0 +1,177 @@
+cmake_minimum_required (VERSION 2.8.11)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+    add_definitions(-DVK_USE_PLATFORM_WIN32_KHR -DWIN32_LEAN_AND_MEAN)
+    set(DisplayServer Win32)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
+    add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    if (BUILD_WSI_XCB_SUPPORT)
+        add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
+        set(DisplayServer Xcb)
+    endif()
+
+    if (BUILD_WSI_XLIB_SUPPORT)
+        if (NOT DisplayServer)
+            set(DisplayServer Xlib)
+        endif()
+        add_definitions(-DVK_USE_PLATFORM_XLIB_KHR)
+    endif()
+
+    if (BUILD_WSI_WAYLAND_SUPPORT)
+       # TODO Add Wayland Support
+       # add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
+    endif()
+
+    if (BUILD_WSI_MIR_SUPPORT)
+       # TODO Add Mir Support
+       # add_definitions(-DVK_USE_PLATFORM_MIR_KHR)
+       # include_directories(${MIR_INCLUDE_DIR})
+    endif()
+else()
+    message(FATAL_ERROR "Unsupported Platform!")
+endif()
+
+add_custom_target( generate_api_cpp DEPENDS api_dump.cpp )
+add_custom_target( generate_api_h DEPENDS api_dump_text.h )
+
+macro(run_vk_xml_generate subcmd output)
+    add_custom_command(OUTPUT ${output}
+        COMMAND ${PYTHON_CMD} ${SCRIPTS_DIR}/vt_genvk.py -registry ${SCRIPTS_DIR}/vk.xml ${output}
+        DEPENDS ${SCRIPTS_DIR}/vk.xml ${SCRIPTS_DIR}/generator.py ${SCRIPTS_DIR}/vt_genvk.py ${SCRIPTS_DIR}/reg.py
+    )
+endmacro()
+
+set(LAYER_JSON_FILES
+    VkLayer_api_dump
+    VkLayer_monitor
+    VkLayer_screenshot
+    )
+
+set(VK_LAYER_RPATH /usr/lib/x86_64-linux-gnu/vulkan/layer:/usr/lib/i386-linux-gnu/vulkan/layer)
+set(CMAKE_INSTALL_RPATH ${VK_LAYER_RPATH})
+
+if (WIN32)
+    if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
+        if (CMAKE_GENERATOR MATCHES "^Visual Studio.*")
+            foreach (config_file ${LAYER_JSON_FILES})
+                FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/windows/${config_file}.json src_json)
+                FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>/${config_file}.json dst_json)
+                add_custom_target(${config_file}-json ALL
+                    COMMAND copy ${src_json} ${dst_json}
+                    VERBATIM
+                    )
+                add_dependencies(${config_file}-json ${config_file})
+            endforeach(config_file)
+        else()
+            foreach (config_file ${LAYER_JSON_FILES})
+                FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/windows/${config_file}.json src_json)
+                FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${config_file}.json dst_json)
+                add_custom_target(${config_file}-json ALL
+                    COMMAND copy ${src_json} ${dst_json}
+                    VERBATIM
+                    )
+                add_dependencies(${config_file}-json ${config_file})
+            endforeach(config_file)
+        endif()
+    endif()
+else()
+    # extra setup for out-of-tree builds
+    if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
+        foreach (config_file ${LAYER_JSON_FILES})
+            add_custom_target(${config_file}-json ALL
+                COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/linux/${config_file}.json
+                VERBATIM
+                )
+             add_dependencies(${config_file}-json ${config_file})
+        endforeach(config_file)
+    endif()
+endif()
+
+if (WIN32)
+    macro(add_vk_layer target)
+    FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/VkLayer_${target}.def DEF_FILE)
+    add_custom_target(copy-${target}-def-file ALL
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DEF_FILE} VkLayer_${target}.def
+        VERBATIM
+    )
+    add_library(VkLayer_${target} SHARED ${ARGN} VkLayer_${target}.def)
+    target_link_Libraries(VkLayer_${target} VkLayer_utilsvt)
+    add_dependencies(VkLayer_${target} generate_vt_helpers generate_api_cpp generate_api_h)
+    endmacro()
+else()
+    macro(add_vk_layer target)
+    add_library(VkLayer_${target} SHARED ${ARGN})
+    target_link_Libraries(VkLayer_${target} VkLayer_utilsvt)
+    add_dependencies(VkLayer_${target} generate_vt_helpers generate_api_cpp generate_api_h)
+    set_target_properties(VkLayer_${target} PROPERTIES LINK_FLAGS "-Wl,-Bsymbolic")
+    install(TARGETS VkLayer_${target} DESTINATION ${PROJECT_BINARY_DIR}/install_staging)
+    endmacro()
+endif()
+
+include_directories(
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/../loader
+    ${CMAKE_CURRENT_SOURCE_DIR}/../layers
+    ${CMAKE_CURRENT_SOURCE_DIR}/../include/vulkan
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${PROJECT_SOURCE_DIR}/../glslang/SPIRV
+)
+
+if (WIN32)
+    set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_CRT_SECURE_NO_WARNINGS")
+    set (CMAKE_C_FLAGS_RELEASE   "${CMAKE_C_FLAGS_RELEASE} -D_CRT_SECURE_NO_WARNINGS")
+
+    # For VS 2015, which uses compiler version 1900, core_validation.cpp fails with too many objects
+    # without either optimizations enabled, or setting the /bigobj compilation option.  Since
+    # optimizations are enabled in a release build, this only affects the debug build.  For now,
+    # enable /bigobj mode for all debug layer files. An alternative for the future is to split
+    # large source files into multiple files which will also alleviate the compilation error.
+    if (MSVC AND NOT (MSVC_VERSION LESS 1900))
+        set (CMAKE_CXX_FLAGS_DEBUG   "${CMAKE_CXX_FLAGS_DEBUG} -D_CRT_SECURE_NO_WARNINGS /bigobj")
+        set (CMAKE_C_FLAGS_DEBUG     "${CMAKE_C_FLAGS_DEBUG} -D_CRT_SECURE_NO_WARNINGS /bigobj")
+    else()
+        set (CMAKE_CXX_FLAGS_DEBUG   "${CMAKE_CXX_FLAGS_DEBUG} -D_CRT_SECURE_NO_WARNINGS")
+        set (CMAKE_C_FLAGS_DEBUG     "${CMAKE_C_FLAGS_DEBUG} -D_CRT_SECURE_NO_WARNINGS")
+    endif()
+else()
+    set (CMAKE_CXX_FLAGS "-std=c++11")
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpointer-arith")
+    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpointer-arith")
+endif()
+
+add_custom_target(generate_vt_helpers DEPENDS
+    vk_dispatch_table_helper.h
+    vk_enum_string_helper.h
+    vk_struct_size_helper.h
+    vk_struct_size_helper.c
+    vk_safe_struct.h
+    vk_safe_struct.cpp
+)
+
+#VulkanTools layers
+run_vk_xml_generate(helper_file_generator.py vk_safe_struct.h)
+run_vk_xml_generate(helper_file_generator.py vk_safe_struct.cpp)
+run_vk_xml_generate(helper_file_generator.py vk_struct_size_helper.h)
+run_vk_xml_generate(helper_file_generator.py vk_struct_size_helper.c)
+run_vk_xml_generate(helper_file_generator.py vk_enum_string_helper.h)
+run_vk_xml_generate(dispatch_table_generator.py vk_dispatch_table_helper.h)
+run_vk_xml_generate(ApiDump api_dump.cpp)
+run_vk_xml_generate(ApiDump api_dump_text.h)
+
+# Layer Utils Library
+# For Windows, we use a static lib because the Windows loader has a fairly restrictive loader search
+# path that can't be easily modified to point it to the same directory that contains the layers.
+if (WIN32)
+    add_library(VkLayer_utilsvt STATIC ../layers/vk_layer_config.cpp ../layers/vk_layer_extension_utils.cpp ../layers/vk_layer_utils.cpp)
+else()
+    add_library(VkLayer_utilsvt SHARED ../layers/vk_layer_config.cpp ../layers/vk_layer_extension_utils.cpp ../layers/vk_layer_utils.cpp)
+    install(TARGETS VkLayer_utilsvt DESTINATION ${PROJECT_BINARY_DIR}/install_staging)
+endif()
+
+# VulkanTools layers
+add_vk_layer(monitor monitor.cpp ../layers/vk_layer_table.cpp)
+# generated
+add_vk_layer(api_dump api_dump.cpp ../layers/vk_layer_table.cpp)
+add_vk_layer(screenshot screenshot.cpp ../layers/vk_layer_table.cpp)
+
diff --git a/layersvt/README.md b/layersvt/README.md
new file mode 100644
index 0000000..c171692
--- /dev/null
+++ b/layersvt/README.md
@@ -0,0 +1,53 @@
+# Layer Description and Status
+
+## Overview
+
+Layer libraries can be written to intercept or hook VK entry points for various
+debug and validation purposes.  One or more VK entry points can be defined in your Layer
+library.  Undefined entrypoints in the Layer library will be passed to the next Layer which
+may be the driver.  Multiple layer libraries can be chained (actually a hierarchy) together.
+vkEnumerateInstanceLayerProperties and vkEnumerateDeviceLayerProperties can be called to list the
+available layers and their properties. Layers can intercept Vulkan instance level entry points
+in which case they are called an Instance Layer.  Layers can intercept device entry  points
+in which case they are called a Device Layer. Instance level entry points are those with VkInstance
+or VkPhysicalDevice as first parameter.  Device level entry points are those with VkDevice, VkCommandBuffer,
+or VkQueue as the first parameter. Layers that want to intercept both instance and device
+level entrypoints are called Global Layers. vkXXXXGetProcAddr is used internally by the Layers and
+Loader to initialize dispatch tables. Device Layers are activated at vkCreateDevice time. Instance
+Layers are activated at vkCreateInstance time.  Layers can also be activated via environment variable:
+(VK_INSTANCE_LAYERS).
+
+### Layer library example code
+
+Note that some layers are code-generated and will therefore exist in the directory (build_dir)/layers
+
+-include/vkLayer.h  - header file for layer code.
+
+### Print API Calls and Parameter Values
+(build dir)/layers/api_dump.cpp (name=VK_LAYER_LUNARG_api_dump) - print out API calls along with parameter values
+
+### Capture Screenshots
+layersvt/screenshot.cpp (name='VK_LAYER_LUNARG_screenshot') - utility layer used to capture and save screenshots of running applications. 
+To specify frames to be captured, the environment variable 'VK_SCREENSHOT_FRAMES' can be set to a comma-separated list of frame numbers (ex: 4,8,15,16,23,42).
+
+### View Frames Per Second
+layersvt/monitor.cpp - utility layer that will display an applications FPS in the title bar of a windowed application.
+
+## Using Layers
+
+1. Build VK loader using normal steps (cmake and make)
+2. Place libVkLayer_<name>.so or VkLayer_<name>.dll in the same directory as your Vulkan test or application, or use the VK\_LAYER\_PATH environment variable to specify where the layer libraries reside.
+3. Create a vk_layer_settings.txt file in the same directory to specify how your layers should behave.
+
+    Model it after the following example:  [*vk_layer_settings.txt*](vk_layer_settings.txt)
+
+4. Specify which Layers to activate by using
+vkCreateDevice and/or vkCreateInstance or environment variables.
+
+    export VK\_INSTANCE\_LAYERS=VK_LAYER_LUNARG_api_dump
+    cd build/tests; ./vkinfo
+
+## Status
+
+
+### Current known issues
diff --git a/layersvt/VKLayer_monitor.def b/layersvt/VKLayer_monitor.def
new file mode 100644
index 0000000..e6c0045
--- /dev/null
+++ b/layersvt/VKLayer_monitor.def
@@ -0,0 +1,24 @@
+;;;;;;;;;;;;;
+; Vulkan
+;
+; Copyright (C) 2016 Valve Corporation
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.a
+;
+; The following is required on Windows, for exporting symbols from the DLL
+
+LIBRARY VkLayer_monitor
+EXPORTS
+vkGetInstanceProcAddr
+vkGetDeviceProcAddr
+
diff --git a/layersvt/VkLayer_api_dump.def b/layersvt/VkLayer_api_dump.def
new file mode 100644
index 0000000..e1f614d
--- /dev/null
+++ b/layersvt/VkLayer_api_dump.def
@@ -0,0 +1,29 @@
+

+;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+;

+; Copyright (c) 2015-2016 The Khronos Group Inc.

+; Copyright (c) 2015-2016 Valve Corporation

+; Copyright (c) 2015-2016 LunarG, Inc.

+;

+; Licensed under the Apache License, Version 2.0 (the "License");

+; you may not use this file except in compliance with the License.

+; You may obtain a copy of the License at

+;

+;     http://www.apache.org/licenses/LICENSE-2.0

+;

+; Unless required by applicable law or agreed to in writing, software

+; distributed under the License is distributed on an "AS IS" BASIS,

+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+; See the License for the specific language governing permissions and

+; limitations under the License.

+;

+;  Author: Courtney Goeltzenleuchter <courtney@LunarG.com>

+;

+;;;;  End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+

+LIBRARY VkLayer_api_dump

+EXPORTS

+vkGetInstanceProcAddr

+vkGetDeviceProcAddr

+vkEnumerateInstanceLayerProperties

+vkEnumerateInstanceExtensionProperties

diff --git a/layersvt/VkLayer_basic.def b/layersvt/VkLayer_basic.def
new file mode 100644
index 0000000..a393549
--- /dev/null
+++ b/layersvt/VkLayer_basic.def
@@ -0,0 +1,29 @@
+

+;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+;

+; Copyright (c) 2015-2016 The Khronos Group Inc.

+; Copyright (c) 2015-2016 Valve Corporation

+; Copyright (c) 2015-2016 LunarG, Inc.

+;

+; Licensed under the Apache License, Version 2.0 (the "License");

+; you may not use this file except in compliance with the License.

+; You may obtain a copy of the License at

+;

+;     http://www.apache.org/licenses/LICENSE-2.0

+;

+; Unless required by applicable law or agreed to in writing, software

+; distributed under the License is distributed on an "AS IS" BASIS,

+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+; See the License for the specific language governing permissions and

+; limitations under the License.

+;

+;  Author: Courtney Goeltzenleuchter <courtney@LunarG.com>

+;

+;;;;  End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+

+LIBRARY VkLayer_basic

+EXPORTS

+vkGetInstanceProcAddr

+vkGetDeviceProcAddr

+vkEnumerateInstanceLayerProperties

+vkEnumerateInstanceExtensionProperties

diff --git a/layersvt/VkLayer_screenshot.def b/layersvt/VkLayer_screenshot.def
new file mode 100644
index 0000000..7178e71
--- /dev/null
+++ b/layersvt/VkLayer_screenshot.def
@@ -0,0 +1,29 @@
+

+;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+;

+; Copyright (c) 2015-2016 The Khronos Group Inc.

+; Copyright (c) 2015-2016 Valve Corporation

+; Copyright (c) 2015-2016 LunarG, Inc.

+;

+; Licensed under the Apache License, Version 2.0 (the "License");

+; you may not use this file except in compliance with the License.

+; You may obtain a copy of the License at

+;

+;     http://www.apache.org/licenses/LICENSE-2.0

+;

+; Unless required by applicable law or agreed to in writing, software

+; distributed under the License is distributed on an "AS IS" BASIS,

+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+; See the License for the specific language governing permissions and

+; limitations under the License.

+;

+;  Author: Courtney Goeltzenleuchter <courtney@LunarG.com>

+;

+;;;;  End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+

+LIBRARY VkLayer_screenshot

+EXPORTS

+vkGetInstanceProcAddr

+vkGetDeviceProcAddr

+vkEnumerateInstanceLayerProperties

+vkEnumerateInstanceExtensionProperties

diff --git a/layersvt/api_dump.h b/layersvt/api_dump.h
new file mode 100644
index 0000000..a50b287
--- /dev/null
+++ b/layersvt/api_dump.h
@@ -0,0 +1,386 @@
+/* Copyright (c) 2015-2016 The Khronos Group Inc.
+ * Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ * Copyright (C) 2015-2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Lenny Komow <lenny@lunarg.com>
+ */
+
+#pragma once
+
+#define NOMINMAX
+
+#include "vk_loader_platform.h"
+#include "vulkan/vk_layer.h"
+#include "vk_layer_config.h"
+#include "vk_layer_table.h"
+#include "vk_layer_extension_utils.h"
+#include "vk_layer_utils.h"
+
+#include <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <ostream>
+#include <sstream>
+#include <string.h>
+#include <string>
+#include <type_traits>
+
+enum class ApiDumpFormat {
+    Text,
+};
+
+class ApiDumpSettings {
+  public:
+    ApiDumpSettings() {
+        // Get the output file settings and create a stream for it
+        const char *file_option = getLayerOption("lunarg_api_dump.file");
+        if (file_option != NULL && strcmp(file_option, "TRUE") == 0) {
+            use_cout = false;
+            const char *filename_option =
+                getLayerOption("lunarg_api_dump.log_filename");
+            if (filename_option != NULL && strcmp(filename_option, "") != 0)
+                output_stream.open(filename_option,
+                                   std::ofstream::out | std::ostream::trunc);
+            else
+                output_stream.open("vk_apidump.txt",
+                                   std::ofstream::out | std::ostream::trunc);
+        } else {
+            use_cout = true;
+        }
+        
+        output_format = ApiDumpFormat::Text;
+
+        // Get the remaining settings
+        show_params = readBoolOption("lunarg_api_dump.detailed", true);
+        show_address = !readBoolOption("lunarg_api_dump.no_addr", false);
+        should_flush = readBoolOption("lunarg_api_dump.flush", true);
+        indent_size =
+            std::max(readIntOption("lunarg_api_dump.indent_size", 4), 0);
+        show_type = readBoolOption("lunarg_api_dump.show_types", true);
+        name_size = std::max(readIntOption("lunarg_api_dump.name_size", 32), 0);
+        type_size = std::max(readIntOption("lunarg_api_dump.type_size", 0), 0);
+        use_spaces = readBoolOption("lunarg_api_dump.use_spaces", true);
+        show_shader = readBoolOption("lunarg_api_dump.show_shader", false);
+    }
+
+    ~ApiDumpSettings() {
+        if (!use_cout)
+            output_stream.close();
+    }
+    
+    inline ApiDumpFormat format() const { return output_format; }
+
+    std::ostream &formatNameType(std::ostream &stream, int indents,
+                                 const char *name, const char *type) const {
+        stream << indentation(indents) << name << ": ";
+
+        if (use_spaces)
+            stream << spaces(name_size - (int)strlen(name) - 2);
+        else
+            stream << tabs((name_size - (int)strlen(name) - 3 + indent_size) /
+                           indent_size);
+
+        if (show_type && use_spaces)
+            stream << type << spaces(type_size - (int)strlen(type));
+        else if (show_type && !use_spaces)
+            stream << type
+                   << tabs((type_size - (int)strlen(type) - 1 + indent_size) /
+                           indent_size);
+
+        return stream << " = ";
+    }
+
+    inline const char *indentation(int indents) const {
+        if (use_spaces)
+            return spaces(indents * indent_size);
+        else
+            return tabs(indents);
+    }
+
+    inline bool shouldFlush() const { return should_flush; }
+
+    inline bool showAddress() const { return show_address; }
+
+    inline bool showParams() const { return show_params; }
+    
+    inline bool showShader() const { return show_shader; }
+
+    inline std::ostream &stream() const {
+        return use_cout ? std::cout : *(std::ofstream *)&output_stream;
+    }
+
+  private:
+    inline static bool readBoolOption(const char *option, bool default_value) {
+        const char *string_option = getLayerOption(option);
+        if (string_option != NULL && strcmp(string_option, "TRUE") == 0)
+            return true;
+        else if (string_option != NULL && strcmp(string_option, "FALSE") == 0)
+            return false;
+        else
+            return default_value;
+    }
+
+    inline static int readIntOption(const char *option, int default_value) {
+        const char *string_option = getLayerOption(option);
+        int value;
+        if (sscanf(string_option, "%d", &value) != 1) {
+            return default_value;
+        } else {
+            return value;
+        }
+    }
+
+    inline static const char *spaces(int count) {
+        return SPACES + (MAX_SPACES - std::max(count, 0));
+    }
+
+    inline static const char *tabs(int count) {
+        return TABS + (MAX_TABS - std::max(count, 0));
+    }
+
+    bool use_cout;
+    std::ofstream output_stream;
+    ApiDumpFormat output_format;
+    bool show_params;
+    bool show_address;
+    bool should_flush;
+
+    bool show_type;
+    int indent_size;
+    int name_size;
+    int type_size;
+    bool use_spaces;
+    bool show_shader;
+
+    static const char *const SPACES;
+    static const int MAX_SPACES = 72;
+    static const char *const TABS;
+    static const int MAX_TABS = 18;
+};
+
+const char *const ApiDumpSettings::SPACES =
+    "                                                                        ";
+const char *const ApiDumpSettings::TABS =
+    "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+class ApiDumpInstance {
+  public:
+    inline ApiDumpInstance()
+        : dump_settings(NULL), frame_count(0), thread_count(0) {
+        loader_platform_thread_create_mutex(&output_mutex);
+        loader_platform_thread_create_mutex(&frame_mutex);
+        loader_platform_thread_create_mutex(&thread_mutex);
+    }
+
+    inline ~ApiDumpInstance() {
+        if (dump_settings != NULL)
+            delete dump_settings;
+        
+        loader_platform_thread_delete_mutex(&thread_mutex);
+        loader_platform_thread_delete_mutex(&frame_mutex);
+        loader_platform_thread_delete_mutex(&output_mutex);
+    }
+
+    inline uint64_t frameCount() {
+        loader_platform_thread_lock_mutex(&frame_mutex);
+        uint64_t count = frame_count;
+        loader_platform_thread_unlock_mutex(&frame_mutex);
+        return count;
+    }
+
+    inline void nextFrame() {
+        loader_platform_thread_lock_mutex(&frame_mutex);
+        ++frame_count;
+        loader_platform_thread_unlock_mutex(&frame_mutex);
+    }
+
+    inline loader_platform_thread_mutex *outputMutex() { return &output_mutex; }
+
+    inline const ApiDumpSettings &settings() {
+        if (dump_settings == NULL)
+            dump_settings = new ApiDumpSettings();
+
+        return *dump_settings;
+    }
+
+    uint32_t threadID() {
+        loader_platform_thread_id id = loader_platform_get_thread_id();
+        loader_platform_thread_lock_mutex(&thread_mutex);
+        for (uint32_t i = 0; i < thread_count; ++i) {
+            if (thread_map[i] == id) {
+                loader_platform_thread_unlock_mutex(&thread_mutex);
+                return i;
+            }
+        }
+
+        uint32_t new_index = thread_count;
+        thread_map[thread_count++] = id;
+        assert(thread_count < MAX_THREADS);
+        loader_platform_thread_unlock_mutex(&thread_mutex);
+        return new_index;
+    }
+
+    static inline ApiDumpInstance &current() { return current_instance; }
+
+  private:
+    static ApiDumpInstance current_instance;
+
+    ApiDumpSettings *dump_settings;
+    loader_platform_thread_mutex output_mutex;
+    loader_platform_thread_mutex frame_mutex;
+    uint64_t frame_count;
+
+    static const size_t MAX_THREADS = 513;
+    loader_platform_thread_mutex thread_mutex;
+    loader_platform_thread_id thread_map[MAX_THREADS];
+    uint32_t thread_count;
+};
+
+ApiDumpInstance ApiDumpInstance::current_instance;
+
+//==================================== Text Backend Helpers ======================================//
+
+template <typename T>
+inline void
+dump_text_array(const T *array, size_t len, const ApiDumpSettings &settings,
+                const char *type_string, const char *child_type,
+                const char *name, int indents,
+                std::ostream &(*dump)(const T, const ApiDumpSettings &, int)) {
+    settings.formatNameType(settings.stream(), indents, name, type_string);
+    if (array == NULL) {
+        settings.stream() << "NULL\n";
+        return;
+    }
+    if (settings.showAddress())
+        settings.stream() << (void *)array << "\n";
+    else
+        settings.stream() << "address\n";
+
+    for (size_t i = 0; i < len && array != NULL; ++i) {
+        std::stringstream stream;
+        stream << name << '[' << i << ']';
+        std::string indexName = stream.str();
+        dump_text_value(array[i], settings, child_type, indexName.c_str(),
+                        indents + 1, dump);
+    }
+}
+
+template <typename T>
+inline void dump_text_array(
+    const T *array, size_t len, const ApiDumpSettings &settings,
+    const char *type_string, const char *child_type, const char *name,
+    int indents,
+    std::ostream &(*dump)(const T &, const ApiDumpSettings &, int)) {
+    settings.formatNameType(settings.stream(), indents, name, type_string);
+    if (array == NULL) {
+        settings.stream() << "NULL\n";
+        return;
+    }
+    if (settings.showAddress())
+        settings.stream() << (void *)array << "\n";
+    else
+        settings.stream() << "address\n";
+
+    for (size_t i = 0; i < len && array != NULL; ++i) {
+        std::stringstream stream;
+        stream << name << '[' << i << ']';
+        std::string indexName = stream.str();
+        dump_text_value(array[i], settings, child_type, indexName.c_str(),
+                        indents + 1, dump);
+    }
+}
+
+template <typename T>
+inline void dump_text_pointer(
+    const T *pointer, const ApiDumpSettings &settings, const char *type_string,
+    const char *name, int indents,
+    std::ostream &(*dump)(const T, const ApiDumpSettings &, int)) {
+    if (pointer == NULL) {
+        settings.formatNameType(settings.stream(), indents, name, type_string);
+        settings.stream() << "NULL\n";
+    } else {
+        dump_text_value(*pointer, settings, type_string, name, indents, dump);
+    }
+}
+
+template <typename T>
+inline void dump_text_pointer(
+    const T *pointer, const ApiDumpSettings &settings, const char *type_string,
+    const char *name, int indents,
+    std::ostream &(*dump)(const T &, const ApiDumpSettings &, int)) {
+    if (pointer == NULL) {
+        settings.formatNameType(settings.stream(), indents, name, type_string);
+        settings.stream() << "NULL\n";
+    } else {
+        dump_text_value(*pointer, settings, type_string, name, indents, dump);
+    }
+}
+
+template <typename T>
+inline void
+dump_text_value(const T object, const ApiDumpSettings &settings,
+                const char *type_string, const char *name, int indents,
+                std::ostream &(*dump)(const T, const ApiDumpSettings &, int)) {
+    settings.formatNameType(settings.stream(), indents, name, type_string);
+    dump(object, settings, indents) << "\n";
+}
+
+template <typename T>
+inline void dump_text_value(
+    const T &object, const ApiDumpSettings &settings, const char *type_string,
+    const char *name, int indents,
+    std::ostream &(*dump)(const T &, const ApiDumpSettings &, int)) {
+    settings.formatNameType(settings.stream(), indents, name, type_string);
+    dump(object, settings, indents);
+}
+
+inline void dump_text_special(
+    const char *text, const ApiDumpSettings &settings, const char *type_string,
+    const char *name, int indents) {
+    settings.formatNameType(settings.stream(), indents, name, type_string);
+    settings.stream() << text << "\n";
+}
+
+inline bool dump_text_bitmaskOption(const std::string &option,
+                                    std::ostream &stream, bool isFirst) {
+    if (isFirst)
+        stream << " (";
+    else
+        stream << " | ";
+    stream << option;
+    return false;
+}
+
+inline std::ostream &dump_text_cstring(const char *object,
+                                       const ApiDumpSettings &settings,
+                                       int indents) {
+    if (object == NULL)
+        return settings.stream() << "NULL";
+    else
+        return settings.stream() << "\"" << object << "\"";
+}
+
+inline std::ostream &dump_text_void(const void *object,
+                                    const ApiDumpSettings &settings,
+                                    int indents) {
+    if (object == NULL)
+        return settings.stream() << "NULL";
+    else if (settings.showAddress())
+        return settings.stream() << object;
+    else
+        return settings.stream() << "address";
+}
diff --git a/layersvt/linux/VkLayer_api_dump.json b/layersvt/linux/VkLayer_api_dump.json
new file mode 100644
index 0000000..0e4ac36
--- /dev/null
+++ b/layersvt/linux/VkLayer_api_dump.json
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_api_dump",
+        "type": "GLOBAL",
+        "library_path": "./libVkLayer_api_dump.so",
+        "api_version": "1.0.38",
+        "implementation_version": "2",
+        "description": "LunarG debug layer"
+    }
+}
diff --git a/layersvt/linux/VkLayer_monitor.json b/layersvt/linux/VkLayer_monitor.json
new file mode 100644
index 0000000..64b23a7
--- /dev/null
+++ b/layersvt/linux/VkLayer_monitor.json
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_monitor",
+        "type": "GLOBAL",
+        "library_path": "./libVkLayer_monitor.so",
+        "api_version": "1.0.38",
+        "implementation_version": "1",
+        "description": "Execution Monitoring Layer"
+    }
+}
diff --git a/layersvt/linux/VkLayer_screenshot.json b/layersvt/linux/VkLayer_screenshot.json
new file mode 100644
index 0000000..44ebf7a
--- /dev/null
+++ b/layersvt/linux/VkLayer_screenshot.json
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_screenshot",
+        "type": "GLOBAL",
+        "library_path": "./libVkLayer_screenshot.so",
+        "api_version": "1.0.38",
+        "implementation_version": "1",
+        "description": "LunarG image capture layer"
+    }
+}
diff --git a/layersvt/monitor.cpp b/layersvt/monitor.cpp
new file mode 100644
index 0000000..06bae74
--- /dev/null
+++ b/layersvt/monitor.cpp
@@ -0,0 +1,318 @@
+/*
+ * Vulkan
+ *
+ * Copyright (C) 2016 Valve Corporation
+ * Copyright (C) 2016 LunarG, Inc.
+ * Copyright (C) 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Chris Forbes <chrisforbes@google.com>
+ * Author: Tony Barbour <tony@lunarg.com>
+ */
+#include "vk_layer_data.h"
+#include "vk_layer_extension_utils.h"
+#include "vk_layer_table.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unordered_map>
+#include <vk_dispatch_table_helper.h>
+#include <vk_loader_platform.h>
+#include <vulkan/vk_layer.h>
+#include <vulkan/vulkan.h>
+
+#if(!defined(VK_USE_PLATFORM_XCB_KHR) && !defined(VK_USE_PLATFORM_WIN32_KHR))
+#warning "Monitor layer only has code for XCB and Windows at this time"
+#endif
+
+#define TITLE_LENGTH 1000
+#define FPS_LENGTH 24
+struct layer_data {
+
+    VkLayerDispatchTable *device_dispatch_table;
+    VkLayerInstanceDispatchTable *instance_dispatch_table;
+
+    PFN_vkQueuePresentKHR pfnQueuePresentKHR;
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+    HWND hwnd;
+#elif defined(VK_USE_PLATFORM_XCB_KHR)
+    xcb_connection_t *connection;
+    xcb_window_t xcb_window;
+    bool xcb_fps;
+#endif
+    char base_title[TITLE_LENGTH];
+
+    VkPhysicalDevice gpu;
+    VkDevice device;
+
+    PFN_vkSetDeviceLoaderData pfn_dev_init;
+    int lastFrame;
+    time_t lastTime;
+    float fps;
+    int frame;
+};
+
+static std::unordered_map<void *, layer_data *> layer_data_map;
+
+template layer_data *
+get_my_data_ptr<layer_data>(void *data_key,
+                            std::unordered_map<void *, layer_data *> &data_map);
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+vkCreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo *pCreateInfo,
+               const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
+    VkLayerDeviceCreateInfo *chain_info =
+        get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+    assert(chain_info->u.pLayerInfo);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
+        chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr =
+        chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+    PFN_vkCreateDevice fpCreateDevice =
+        (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice");
+    if (fpCreateDevice == NULL) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // Advance the link info for the next element on the chain
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+    VkResult result = fpCreateDevice(gpu, pCreateInfo, pAllocator, pDevice);
+    if (result != VK_SUCCESS) {
+        return result;
+    }
+
+    layer_data *my_device_data =
+        get_my_data_ptr(get_dispatch_key(*pDevice), layer_data_map);
+
+    // Setup device dispatch table
+    my_device_data->device_dispatch_table = new VkLayerDispatchTable;
+    layer_init_device_dispatch_table(
+        *pDevice, my_device_data->device_dispatch_table, fpGetDeviceProcAddr);
+
+    // store the loader callback for initializing created dispatchable objects
+    chain_info = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
+    if (chain_info) {
+        my_device_data->pfn_dev_init = chain_info->u.pfnSetDeviceLoaderData;
+    } else {
+        my_device_data->pfn_dev_init = NULL;
+    }
+
+    my_device_data->gpu = gpu;
+    my_device_data->device = *pDevice;
+    my_device_data->frame = 0;
+    my_device_data->lastFrame = 0;
+    my_device_data->fps = 0.0;
+    time(&my_device_data->lastTime);
+
+    // Get our WSI hooks in
+    VkLayerDispatchTable *pTable = my_device_data->device_dispatch_table;
+    my_device_data->pfnQueuePresentKHR =
+        (PFN_vkQueuePresentKHR)pTable->GetDeviceProcAddr(*pDevice,
+                                                         "vkQueuePresentKHR");
+
+    return result;
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
+vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
+    dispatch_key key = get_dispatch_key(device);
+    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
+    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
+    pTable->DeviceWaitIdle(device);
+    pTable->DestroyDevice(device, pAllocator);
+    delete pTable;
+    layer_data_map.erase(key);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(
+    const VkInstanceCreateInfo *pCreateInfo,
+    const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) {
+    VkLayerInstanceCreateInfo *chain_info =
+        get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+    assert(chain_info->u.pLayerInfo);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
+        chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    PFN_vkCreateInstance fpCreateInstance =
+        (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
+    if (fpCreateInstance == NULL) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // Advance the link info for the next element on the chain
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+    VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
+    if (result != VK_SUCCESS)
+        return result;
+
+    layer_data *my_data =
+        get_my_data_ptr(get_dispatch_key(*pInstance), layer_data_map);
+    my_data->instance_dispatch_table = new VkLayerInstanceDispatchTable;
+    layer_init_instance_dispatch_table(
+        *pInstance, my_data->instance_dispatch_table, fpGetInstanceProcAddr);
+
+    return result;
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(
+    VkInstance instance, const VkAllocationCallbacks *pAllocator) {
+    dispatch_key key = get_dispatch_key(instance);
+    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
+    VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;
+    pTable->DestroyInstance(instance, pAllocator);
+    delete pTable;
+    layer_data_map.erase(key);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) {
+    layer_data *my_data =
+        get_my_data_ptr(get_dispatch_key(queue), layer_data_map);
+
+    time_t now;
+    time(&now);
+    float seconds = (float)difftime(now, my_data->lastTime);
+
+    if (seconds > 0.5) {
+        char str[TITLE_LENGTH + FPS_LENGTH];
+        char fpsstr[FPS_LENGTH];
+        layer_data *my_instance_data =
+            get_my_data_ptr(get_dispatch_key(my_data->gpu), layer_data_map);
+        my_data->fps = (my_data->frame - my_data->lastFrame) / seconds;
+        my_data->lastFrame = my_data->frame;
+        my_data->lastTime = now;
+        sprintf(fpsstr, "   FPS = %.2f", my_data->fps);
+        strcpy(str, my_instance_data->base_title);
+        strcat(str, fpsstr);
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+        SetWindowText(my_instance_data->hwnd, str);
+#elif defined(VK_USE_PLATFORM_XCB_KHR)
+        if (my_instance_data->xcb_fps) {
+            xcb_change_property(my_instance_data->connection,
+                                XCB_PROP_MODE_REPLACE,
+                                my_instance_data->xcb_window, XCB_ATOM_WM_NAME,
+                                XCB_ATOM_STRING, 8, strlen(str), str);
+            xcb_flush(my_instance_data->connection);
+        }
+#endif
+    }
+    my_data->frame++;
+
+    VkResult result = my_data->pfnQueuePresentKHR(queue, pPresentInfo);
+    return result;
+}
+
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR(
+    VkInstance instance, const VkWin32SurfaceCreateInfoKHR *pCreateInfo,
+    const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
+    layer_data *my_data =
+        get_my_data_ptr(get_dispatch_key(instance), layer_data_map);
+    my_data->hwnd = pCreateInfo->hwnd;
+    GetWindowText(my_data->hwnd, my_data->base_title, TITLE_LENGTH);
+
+    VkResult result = my_data->instance_dispatch_table->CreateWin32SurfaceKHR(
+        instance, pCreateInfo, pAllocator, pSurface);
+    return result;
+}
+#elif defined(VK_USE_PLATFORM_XCB_KHR)
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR(
+    VkInstance instance, const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
+    const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
+    xcb_get_property_cookie_t cookie;
+    xcb_get_property_reply_t *reply;
+    xcb_atom_t property = XCB_ATOM_WM_NAME;
+    xcb_atom_t type = XCB_ATOM_STRING;
+
+    layer_data *my_data =
+        get_my_data_ptr(get_dispatch_key(instance), layer_data_map);
+    my_data->xcb_window = pCreateInfo->window;
+    my_data->connection = pCreateInfo->connection;
+    cookie = xcb_get_property(my_data->connection, 0, my_data->xcb_window,
+                              property, type, 0, 0);
+    if ((reply = xcb_get_property_reply(my_data->connection, cookie, NULL))) {
+        my_data->xcb_fps = true;
+        int len = xcb_get_property_value_length(reply);
+        if (len > TITLE_LENGTH) {
+            my_data->xcb_fps = false;
+        } else if (len > 0) {
+            strcpy(my_data->base_title, (char *)xcb_get_property_value(reply));
+        } else {
+            // No window title - make base title null string
+            my_data->base_title[0] = 0;
+        }
+    }
+
+    VkResult result = my_data->instance_dispatch_table->CreateXcbSurfaceKHR(
+        instance, pCreateInfo, pAllocator, pSurface);
+    return result;
+}
+#endif
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
+#define ADD_HOOK(fn)                                                           \
+    if (!strncmp(#fn, funcName, sizeof(#fn)))                                  \
+    return (PFN_vkVoidFunction)fn
+
+    ADD_HOOK(vkGetDeviceProcAddr);
+    ADD_HOOK(vkDestroyDevice);
+    ADD_HOOK(vkQueuePresentKHR);
+#undef ADD_HOOK
+
+    if (dev == NULL)
+        return NULL;
+
+    layer_data *dev_data;
+    dev_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map);
+    VkLayerDispatchTable *pTable = dev_data->device_dispatch_table;
+
+    if (pTable->GetDeviceProcAddr == NULL)
+        return NULL;
+    return pTable->GetDeviceProcAddr(dev, funcName);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {
+#define ADD_HOOK(fn)                                                           \
+    if (!strncmp(#fn, funcName, sizeof(#fn)))                                  \
+    return (PFN_vkVoidFunction)fn
+
+    ADD_HOOK(vkCreateInstance);
+    ADD_HOOK(vkCreateDevice);
+    ADD_HOOK(vkDestroyInstance);
+    ADD_HOOK(vkGetInstanceProcAddr);
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+    ADD_HOOK(vkCreateWin32SurfaceKHR);
+#elif defined(VK_USE_PLATFORM_XCB_KHR)
+    ADD_HOOK(vkCreateXcbSurfaceKHR);
+#endif
+#undef ADD_HOOK
+
+    if (instance == NULL)
+        return NULL;
+
+    layer_data *instance_data;
+    instance_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);
+    VkLayerInstanceDispatchTable *pTable =
+        instance_data->instance_dispatch_table;
+
+    if (pTable->GetInstanceProcAddr == NULL)
+        return NULL;
+    return pTable->GetInstanceProcAddr(instance, funcName);
+}
diff --git a/layersvt/screenshot.cpp b/layersvt/screenshot.cpp
new file mode 100644
index 0000000..f23d77d
--- /dev/null
+++ b/layersvt/screenshot.cpp
@@ -0,0 +1,1329 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Cody Northrop <cody@lunarg.com>
+ * Author: David Pinedo <david@lunarg.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unordered_map>
+#include <iostream>
+#include <algorithm>
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+#include <fstream>
+
+using namespace std;
+
+#include "vk_dispatch_table_helper.h"
+#include "vk_layer_config.h"
+#include "vk_layer_table.h"
+#include "vk_layer_extension_utils.h"
+#include "vk_layer_utils.h"
+
+#ifdef ANDROID
+
+#include <android/log.h>
+#include <sys/system_properties.h>
+
+static char android_env[64] = {};
+const char* env_var = "debug.vulkan.screenshot";
+const char *env_var_old = env_var;
+
+char* android_exec(const char* cmd) {
+    FILE* pipe = popen(cmd, "r");
+    if (pipe != nullptr) {
+        fgets(android_env, 64, pipe);
+        pclose(pipe);
+    }
+
+    // Only if the value is set will we get a string back
+    if (strlen(android_env) > 0) {
+        __android_log_print(ANDROID_LOG_INFO, "screenshot", "Vulkan screenshot layer capturing: %s", android_env);
+        return android_env;
+    }
+
+    return nullptr;
+}
+
+char* android_getenv(const char *key)
+{
+    std::string command("getprop ");
+    command += key;
+    return android_exec(command.c_str());
+}
+
+static inline char *local_getenv(const char *name) {
+    return android_getenv(name);
+}
+
+static inline void local_free_getenv(const char *val) {}
+
+#elif defined(__linux__)
+
+const char *env_var_old = "_VK_SCREENSHOT";
+const char *env_var = "VK_SCREENSHOT_FRAMES";
+
+static inline char *local_getenv(const char *name) { return getenv(name); }
+
+static inline void local_free_getenv(const char *val) {}
+
+#elif defined(_WIN32)
+
+const char *env_var_old = "_VK_SCREENSHOT";
+const char *env_var = "VK_SCREENSHOT_FRAMES";
+
+static inline char *local_getenv(const char *name) {
+    char *retVal;
+    DWORD valSize;
+
+    valSize = GetEnvironmentVariableA(name, NULL, 0);
+
+    // valSize DOES include the null terminator, so for any set variable
+    // will always be at least 1. If it's 0, the variable wasn't set.
+    if (valSize == 0)
+        return NULL;
+
+    // TODO; FIXME This should be using any app defined memory allocation
+    retVal = (char *)malloc(valSize);
+
+    GetEnvironmentVariableA(name, retVal, valSize);
+
+    return retVal;
+}
+
+static inline void local_free_getenv(const char *val) { free((void *)val); }
+#endif
+
+namespace screenshot {
+
+static int globalLockInitialized = 0;
+static loader_platform_thread_mutex globalLock;
+
+// unordered map: associates a swap chain with a device, image extent, format,
+// and list of images
+typedef struct {
+    VkDevice device;
+    VkExtent2D imageExtent;
+    VkFormat format;
+    VkImage *imageList;
+} SwapchainMapStruct;
+static unordered_map<VkSwapchainKHR, SwapchainMapStruct *> swapchainMap;
+
+// unordered map: associates an image with a device, image extent, and format
+typedef struct {
+    VkDevice device;
+    VkExtent2D imageExtent;
+    VkFormat format;
+} ImageMapStruct;
+static unordered_map<VkImage, ImageMapStruct *> imageMap;
+
+// unordered map: associates a device with a queue, commandPool, and physical
+// device also contains per device info including dispatch table
+typedef struct {
+    VkLayerDispatchTable *device_dispatch_table;
+    bool wsi_enabled;
+    VkQueue queue;
+    VkCommandPool commandPool;
+    VkPhysicalDevice physicalDevice;
+    PFN_vkSetDeviceLoaderData pfn_dev_init;
+} DeviceMapStruct;
+static unordered_map<VkDevice, DeviceMapStruct *> deviceMap;
+
+// unordered map: associates a physical device with an instance
+typedef struct { VkInstance instance; } PhysDeviceMapStruct;
+static unordered_map<VkPhysicalDevice, PhysDeviceMapStruct *> physDeviceMap;
+
+// set: list of frames to take screenshots without duplication.
+static set<int> screenshotFrames;
+
+// Flag indicating we have received the frame list
+static bool screenshotFramesReceived = false;
+
+// Parse comma-separated frame list string into the set
+static void populate_frame_list(const char *vk_screenshot_frames) {
+    string spec(vk_screenshot_frames), word;
+    size_t start = 0, comma = 0;
+
+    while (start < spec.size()) {
+        int frameToAdd;
+        comma = spec.find(',', start);
+        if (comma == string::npos)
+            word = string(spec, start);
+        else
+            word = string(spec, start, comma - start);
+        frameToAdd = atoi(word.c_str());
+        // Add the frame number to set, but only do it if the word
+        // started with a digit and if
+        // it's not already in the list
+        if (*(word.c_str()) >= '0' && *(word.c_str()) <= '9') {
+            screenshotFrames.insert(frameToAdd);
+        }
+        if (comma == string::npos)
+            break;
+        start = comma + 1;
+    }
+
+    screenshotFramesReceived = true;
+}
+
+static bool
+memory_type_from_properties(VkPhysicalDeviceMemoryProperties *memory_properties,
+                            uint32_t typeBits, VkFlags requirements_mask,
+                            uint32_t *typeIndex) {
+    // Search memtypes to find first index with those properties
+    for (uint32_t i = 0; i < 32; i++) {
+        if ((typeBits & 1) == 1) {
+            // Type is available, does it match user properties?
+            if ((memory_properties->memoryTypes[i].propertyFlags &
+                 requirements_mask) == requirements_mask) {
+                *typeIndex = i;
+                return true;
+            }
+        }
+        typeBits >>= 1;
+    }
+    // No memory types matched, return failure
+    return false;
+}
+
+static DeviceMapStruct *get_dev_info(VkDevice dev) {
+    auto it = deviceMap.find(dev);
+    if (it == deviceMap.end())
+        return NULL;
+    else
+        return it->second;
+}
+
+static void init_screenshot() {
+    if (!globalLockInitialized) {
+        // TODO/TBD: Need to delete this mutex sometime.  How???  One
+        // suggestion is to call this during vkCreateInstance(), and then we
+        // can clean it up during vkDestroyInstance().  However, that requires
+        // that the layer have per-instance locks.  We need to come back and
+        // address this soon.
+        loader_platform_thread_create_mutex(&globalLock);
+        globalLockInitialized = 1;
+    }
+}
+
+// Track allocated resources in writePPM()
+// and clean them up when they go out of scope.
+struct WritePPMCleanupData {
+    VkDevice device;
+    VkLayerDispatchTable *pTableDevice;
+    VkImage image2;
+    VkImage image3;
+    VkDeviceMemory mem2;
+    VkDeviceMemory mem3;
+    bool mem2mapped;
+    bool mem3mapped;
+    VkCommandBuffer commandBuffer;
+    VkCommandPool commandPool;
+    ~WritePPMCleanupData();
+};
+
+WritePPMCleanupData::~WritePPMCleanupData() {
+    if (mem2mapped)
+        pTableDevice->UnmapMemory(device, mem2);
+    if (mem2)
+        pTableDevice->FreeMemory(device, mem2, NULL);
+    if (image2)
+        pTableDevice->DestroyImage(device, image2, NULL);
+
+    if (mem3mapped)
+        pTableDevice->UnmapMemory(device, mem3);
+    if (mem3)
+        pTableDevice->FreeMemory(device, mem3, NULL);
+    if (image3)
+        pTableDevice->DestroyImage(device, image3, NULL);
+
+    if (commandBuffer)
+        pTableDevice->FreeCommandBuffers(device, commandPool, 1,
+                                         &commandBuffer);
+}
+
+// Save an image to a PPM image file.
+//
+// This function issues commands to copy/convert the swapchain image
+// from whatever compatible format the swapchain image uses
+// to a single format (VK_FORMAT_R8G8B8A8_UNORM) so that the converted
+// result can be easily written to a PPM file.
+//
+// Error handling: If there is a problem, this function should silently
+// fail without affecting the Present operation going on in the caller.
+// The numerous debug asserts are to catch programming errors and are not
+// expected to assert.  Recovery and clean up are implemented for image memory
+// allocation failures.
+// (TODO) It would be nice to pass any failure info to DebugReport or something.
+static void writePPM(const char *filename, VkImage image1) {
+
+    VkResult err;
+    bool pass;
+
+    // Bail immediately if we can't find the image.
+    if (imageMap.empty() || imageMap.find(image1) == imageMap.end())
+        return;
+
+    // Collect object info from maps.  This info is generally recorded
+    // by the other functions hooked in this layer.
+    VkDevice device = imageMap[image1]->device;
+    VkPhysicalDevice physicalDevice = deviceMap[device]->physicalDevice;
+    VkInstance instance = physDeviceMap[physicalDevice]->instance;
+    VkQueue queue = deviceMap[device]->queue;
+    DeviceMapStruct *devMap = get_dev_info(device);
+    if (NULL == devMap) {
+        assert(0);
+        return;
+    }
+    VkLayerDispatchTable *pTableDevice = devMap->device_dispatch_table;
+    VkLayerDispatchTable *pTableQueue =
+        get_dev_info(static_cast<VkDevice>(static_cast<void *>(queue)))
+            ->device_dispatch_table;
+    VkLayerInstanceDispatchTable *pInstanceTable;
+    pInstanceTable = instance_dispatch_table(instance);
+
+    // Gather incoming image info and check image format for compatibility with
+    // the target format.
+    // This function supports both 24-bit and 32-bit swapchain images.
+    VkFormat const target32bitFormat = VK_FORMAT_R8G8B8A8_UNORM;
+    VkFormat const target24bitFormat = VK_FORMAT_R8G8B8_UNORM;
+    uint32_t const width = imageMap[image1]->imageExtent.width;
+    uint32_t const height = imageMap[image1]->imageExtent.height;
+    VkFormat const format = imageMap[image1]->format;
+    uint32_t const numChannels = vk_format_get_channel_count(format);
+    if ((vk_format_get_compatibility_class(target24bitFormat) !=
+         vk_format_get_compatibility_class(format)) &&
+        (vk_format_get_compatibility_class(target32bitFormat) !=
+         vk_format_get_compatibility_class(format))) {
+        assert(0);
+        return;
+    }
+    if ((3 != numChannels) && (4 != numChannels)) {
+        assert(0);
+        return;
+    }
+
+    // General Approach
+    //
+    // The idea here is to copy/convert the swapchain image into another image
+    // that can be mapped and read by the CPU to produce a PPM file.
+    // The image must be untiled and converted to a specific format for easy
+    // parsing.  The memory for the final image must be host-visible.
+    // Note that in Vulkan, a BLIT operation must be used to perform a format
+    // conversion.
+    //
+    // Devices vary in their ability to blit to/from linear and optimal tiling.
+    // So we must query the device properties to get this information.
+    //
+    // If the device cannot BLIT to a LINEAR image, then the operation must be
+    // done in two steps:
+    // 1) BLIT the swapchain image (image1) to a temp image (image2) that is
+    // created with TILING_OPTIMAL.
+    // 2) COPY image2 to another temp image (image3) that is created with
+    // TILING_LINEAR.
+    // 3) Map image 3 and write the PPM file.
+    //
+    // If the device can BLIT to a LINEAR image, then:
+    // 1) BLIT the swapchain image (image1) to a temp image (image2) that is
+    // created with TILING_LINEAR.
+    // 2) Map image 2 and write the PPM file.
+    //
+    // There seems to be no way to tell if the swapchain image (image1) is tiled
+    // or not.  We therefore assume that the BLIT operation can always read from
+    // both linear and optimal tiled (swapchain) images.
+    // There is therefore no point in looking at the BLIT_SRC properties.
+    //
+    // There is also the optimization where the incoming and target formats are
+    // the same.  In this case, just do a COPY.
+
+    VkFormatProperties targetFormatProps;
+    pInstanceTable->GetPhysicalDeviceFormatProperties(
+        physicalDevice,
+        (3 == numChannels) ? target24bitFormat : target32bitFormat,
+        &targetFormatProps);
+    bool need2steps = false;
+    bool copyOnly = false;
+    if ((target24bitFormat == format) || (target32bitFormat == format)) {
+        copyOnly = true;
+    } else {
+        bool const bltLinear = targetFormatProps.linearTilingFeatures &
+                                       VK_FORMAT_FEATURE_BLIT_DST_BIT
+                                   ? true
+                                   : false;
+        bool const bltOptimal = targetFormatProps.optimalTilingFeatures &
+                                        VK_FORMAT_FEATURE_BLIT_DST_BIT
+                                    ? true
+                                    : false;
+        if (!bltLinear && !bltOptimal) {
+            // Cannot blit to either target tiling type.  It should be pretty
+            // unlikely to have a device that cannot blit to either type.
+            // But punt by just doing a copy and possibly have the wrong
+            // colors.  This should be quite rare.
+            copyOnly = true;
+        } else if (!bltLinear && bltOptimal) {
+            // Cannot blit to a linear target but can blt to optimal, so copy
+            // after blit is needed.
+            need2steps = true;
+        }
+        // Else bltLinear is available and only 1 step is needed.
+    }
+
+    // Put resources that need to be cleaned up in a struct with a destructor
+    // so that things get cleaned up when this function is exited.
+    WritePPMCleanupData data = {};
+    data.device = device;
+    data.pTableDevice = pTableDevice;
+
+    // Set up the image creation info for both the blit and copy images, in case
+    // both are needed.
+    VkImageCreateInfo imgCreateInfo2 = {
+        VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+        NULL,
+        0,
+        VK_IMAGE_TYPE_2D,
+        VK_FORMAT_R8G8B8A8_UNORM,
+        {width, height, 1},
+        1,
+        1,
+        VK_SAMPLE_COUNT_1_BIT,
+        VK_IMAGE_TILING_LINEAR,
+        VK_IMAGE_USAGE_TRANSFER_DST_BIT,
+        VK_SHARING_MODE_EXCLUSIVE,
+        0,
+        NULL,
+        VK_IMAGE_LAYOUT_UNDEFINED,
+    };
+    VkImageCreateInfo imgCreateInfo3 = imgCreateInfo2;
+
+    // If we need both images, set up image2 to be read/write and tiled.
+    if (need2steps) {
+        imgCreateInfo2.tiling = VK_IMAGE_TILING_OPTIMAL;
+        imgCreateInfo2.usage =
+            VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    }
+
+    VkMemoryAllocateInfo memAllocInfo = {
+        VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL,
+        0, // allocationSize, queried later
+        0  // memoryTypeIndex, queried later
+    };
+    VkMemoryRequirements memRequirements;
+    VkPhysicalDeviceMemoryProperties memoryProperties;
+
+    // Create image2 and allocate its memory.  It could be the intermediate or
+    // final image.
+    err =
+        pTableDevice->CreateImage(device, &imgCreateInfo2, NULL, &data.image2);
+    assert(!err);
+    if (VK_SUCCESS != err)
+        return;
+    pTableDevice->GetImageMemoryRequirements(device, data.image2,
+                                             &memRequirements);
+    memAllocInfo.allocationSize = memRequirements.size;
+    pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice,
+                                                      &memoryProperties);
+    pass = memory_type_from_properties(
+        &memoryProperties, memRequirements.memoryTypeBits,
+        need2steps ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
+                   : VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+        &memAllocInfo.memoryTypeIndex);
+    assert(pass);
+    err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL, &data.mem2);
+    assert(!err);
+    if (VK_SUCCESS != err)
+        return;
+    err = pTableQueue->BindImageMemory(device, data.image2, data.mem2, 0);
+    assert(!err);
+    if (VK_SUCCESS != err)
+        return;
+
+    // Create image3 and allocate its memory, if needed.
+    if (need2steps) {
+        err = pTableDevice->CreateImage(device, &imgCreateInfo3, NULL,
+                                        &data.image3);
+        assert(!err);
+        if (VK_SUCCESS != err)
+            return;
+        pTableDevice->GetImageMemoryRequirements(device, data.image3,
+                                                 &memRequirements);
+        memAllocInfo.allocationSize = memRequirements.size;
+        pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice,
+                                                          &memoryProperties);
+        pass = memory_type_from_properties(
+            &memoryProperties, memRequirements.memoryTypeBits,
+            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex);
+        assert(pass);
+        err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL,
+                                           &data.mem3);
+        assert(!err);
+        if (VK_SUCCESS != err)
+            return;
+        err = pTableQueue->BindImageMemory(device, data.image3, data.mem3, 0);
+        assert(!err);
+        if (VK_SUCCESS != err)
+            return;
+    }
+
+    // Set up the command buffer.  We get a command buffer from a pool we saved
+    // in a hooked function, which would be the application's pool.
+    const VkCommandBufferAllocateInfo allocCommandBufferInfo = {
+        VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL,
+        deviceMap[device]->commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1};
+    data.commandPool = deviceMap[device]->commandPool;
+    err = pTableDevice->AllocateCommandBuffers(device, &allocCommandBufferInfo,
+                                               &data.commandBuffer);
+    assert(!err);
+    if (VK_SUCCESS != err)
+        return;
+
+    VkDevice cmdBuf =
+        static_cast<VkDevice>(static_cast<void *>(data.commandBuffer));
+    deviceMap.emplace(cmdBuf, devMap);
+    VkLayerDispatchTable *pTableCommandBuffer;
+    pTableCommandBuffer = get_dev_info(cmdBuf)->device_dispatch_table;
+
+    // We have just created a dispatchable object, but the dispatch table has
+    // not been placed in the object yet.  When a "normal" application creates
+    // a command buffer, the dispatch table is installed by the top-level api
+    // binding (trampoline.c). But here, we have to do it ourselves.
+    if (!devMap->pfn_dev_init) {
+        *((const void **)data.commandBuffer) = *(void **)device;
+    } else {
+        err = devMap->pfn_dev_init(device, (void *)data.commandBuffer);
+        assert(!err);
+    }
+
+    const VkCommandBufferBeginInfo commandBufferBeginInfo = {
+        VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
+        VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+    };
+    err = pTableCommandBuffer->BeginCommandBuffer(data.commandBuffer,
+                                                  &commandBufferBeginInfo);
+    assert(!err);
+
+    // This barrier is used to transition from/to present Layout
+    VkImageMemoryBarrier presentMemoryBarrier = {
+        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+        NULL,
+        VK_ACCESS_TRANSFER_WRITE_BIT,
+        VK_ACCESS_TRANSFER_READ_BIT,
+        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+        VK_QUEUE_FAMILY_IGNORED,
+        VK_QUEUE_FAMILY_IGNORED,
+        image1,
+        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
+
+    // This barrier is used to transition from a newly-created layout to a blt
+    // or copy destination layout.
+    VkImageMemoryBarrier destMemoryBarrier = {
+        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+        NULL,
+        0,
+        VK_ACCESS_TRANSFER_WRITE_BIT,
+        VK_IMAGE_LAYOUT_UNDEFINED,
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+        VK_QUEUE_FAMILY_IGNORED,
+        VK_QUEUE_FAMILY_IGNORED,
+        data.image2,
+        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
+
+    // This barrier is used to transition a dest layout to general layout.
+    VkImageMemoryBarrier generalMemoryBarrier = {
+        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+        NULL,
+        VK_ACCESS_TRANSFER_WRITE_BIT,
+        VK_ACCESS_TRANSFER_READ_BIT,
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+        VK_IMAGE_LAYOUT_GENERAL,
+        VK_QUEUE_FAMILY_IGNORED,
+        VK_QUEUE_FAMILY_IGNORED,
+        data.image2,
+        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
+
+    VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
+    VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+    // The source image needs to be transitioned from present to transfer
+    // source.
+    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
+                                            dstStages, 0, 0, NULL, 0, NULL, 1,
+                                            &presentMemoryBarrier);
+
+    // image2 needs to be transitioned from its undefined state to transfer
+    // destination.
+    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
+                                            dstStages, 0, 0, NULL, 0, NULL, 1,
+                                            &destMemoryBarrier);
+
+    const VkImageCopy imageCopyRegion = {{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
+                                         {0, 0, 0},
+                                         {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
+                                         {0, 0, 0},
+                                         {width, height, 1}};
+
+    if (copyOnly) {
+        pTableCommandBuffer->CmdCopyImage(
+            data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+            data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
+            &imageCopyRegion);
+    } else {
+        VkImageBlit imageBlitRegion = {};
+        imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+        imageBlitRegion.srcSubresource.baseArrayLayer = 0;
+        imageBlitRegion.srcSubresource.layerCount = 1;
+        imageBlitRegion.srcSubresource.mipLevel = 0;
+        imageBlitRegion.srcOffsets[1].x = width;
+        imageBlitRegion.srcOffsets[1].y = height;
+        imageBlitRegion.srcOffsets[1].z = 1;
+        imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+        imageBlitRegion.dstSubresource.baseArrayLayer = 0;
+        imageBlitRegion.dstSubresource.layerCount = 1;
+        imageBlitRegion.dstSubresource.mipLevel = 0;
+        imageBlitRegion.dstOffsets[1].x = width;
+        imageBlitRegion.dstOffsets[1].y = height;
+        imageBlitRegion.dstOffsets[1].z = 1;
+
+        pTableCommandBuffer->CmdBlitImage(
+            data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+            data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
+            &imageBlitRegion, VK_FILTER_NEAREST);
+        if (need2steps) {
+            // image 3 needs to be transitioned from its undefined state to a
+            // transfer destination.
+            destMemoryBarrier.image = data.image3;
+            pTableCommandBuffer->CmdPipelineBarrier(
+                data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL,
+                1, &destMemoryBarrier);
+
+            // Transition image2 so that it can be read for the upcoming copy to
+            // image 3.
+            destMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+            destMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+            destMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+            destMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+            destMemoryBarrier.image = data.image2;
+            pTableCommandBuffer->CmdPipelineBarrier(
+                data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL,
+                1, &destMemoryBarrier);
+
+            // This step essentially untiles the image.
+            pTableCommandBuffer->CmdCopyImage(
+                data.commandBuffer, data.image2,
+                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image3,
+                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion);
+            generalMemoryBarrier.image = data.image3;
+        }
+    }
+
+    // The destination needs to be transitioned from the optimal copy format to
+    // the format we can read with the CPU.
+    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
+                                            dstStages, 0, 0, NULL, 0, NULL, 1,
+                                            &generalMemoryBarrier);
+
+    // Restore the swap chain image layout to what it was before.
+    // This may not be strictly needed, but it is generally good to restore
+    // things to original state.
+    presentMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+    presentMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+    presentMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+    presentMemoryBarrier.dstAccessMask = 0;
+    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
+                                            dstStages, 0, 0, NULL, 0, NULL, 1,
+                                            &presentMemoryBarrier);
+
+    err = pTableCommandBuffer->EndCommandBuffer(data.commandBuffer);
+    assert(!err);
+
+    VkFence nullFence = {VK_NULL_HANDLE};
+    VkSubmitInfo submitInfo;
+    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submitInfo.pNext = NULL;
+    submitInfo.waitSemaphoreCount = 0;
+    submitInfo.pWaitSemaphores = NULL;
+    submitInfo.pWaitDstStageMask = NULL;
+    submitInfo.commandBufferCount = 1;
+    submitInfo.pCommandBuffers = &data.commandBuffer;
+    submitInfo.signalSemaphoreCount = 0;
+    submitInfo.pSignalSemaphores = NULL;
+
+    err = pTableQueue->QueueSubmit(queue, 1, &submitInfo, nullFence);
+    assert(!err);
+
+    err = pTableQueue->QueueWaitIdle(queue);
+    assert(!err);
+
+    err = pTableDevice->DeviceWaitIdle(device);
+    assert(!err);
+
+    // Map the final image so that the CPU can read it.
+    const VkImageSubresource sr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0};
+    VkSubresourceLayout srLayout;
+    const char *ptr;
+    if (!need2steps) {
+        pTableDevice->GetImageSubresourceLayout(device, data.image2, &sr,
+                                                &srLayout);
+        err = pTableDevice->MapMemory(device, data.mem2, 0, VK_WHOLE_SIZE, 0,
+                                      (void **)&ptr);
+        assert(!err);
+        if (VK_SUCCESS != err)
+            return;
+        data.mem2mapped = true;
+    } else {
+        pTableDevice->GetImageSubresourceLayout(device, data.image3, &sr,
+                                                &srLayout);
+        err = pTableDevice->MapMemory(device, data.mem3, 0, VK_WHOLE_SIZE, 0,
+                                      (void **)&ptr);
+        assert(!err);
+        if (VK_SUCCESS != err)
+            return;
+        data.mem3mapped = true;
+    }
+
+    // Write the data to a PPM file.
+    ofstream file(filename, ios::binary);
+    assert(file.is_open());
+
+    if (!file.is_open()) {
+#ifdef ANDROID
+        __android_log_print(ANDROID_LOG_DEBUG, "screenshot", "Failed to open output file: %s.  Be sure to grant read and write permissions.", filename);
+#endif
+       return;
+    }
+
+    file << "P6\n";
+    file << width << "\n";
+    file << height << "\n";
+    file << 255 << "\n";
+
+    ptr += srLayout.offset;
+    if (3 == numChannels) {
+        for (uint32_t y = 0; y < height; y++) {
+            file.write(ptr, 3 * width);
+            ptr += srLayout.rowPitch;
+        }
+    } else if (4 == numChannels) {
+        for (uint32_t y = 0; y < height; y++) {
+            const unsigned int *row = (const unsigned int *)ptr;
+            for (uint32_t x = 0; x < width; x++) {
+                file.write((char *)row, 3);
+                row++;
+            }
+            ptr += srLayout.rowPitch;
+        }
+    }
+    file.close();
+
+    // Clean up handled by ~WritePPMCleanupData()
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
+               const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) {
+    VkLayerInstanceCreateInfo *chain_info =
+        get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+    assert(chain_info->u.pLayerInfo);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
+        chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    assert(fpGetInstanceProcAddr);
+    PFN_vkCreateInstance fpCreateInstance =
+        (PFN_vkCreateInstance)fpGetInstanceProcAddr(VK_NULL_HANDLE,
+                                                    "vkCreateInstance");
+    if (fpCreateInstance == NULL) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // Advance the link info for the next element on the chain
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+    VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
+    if (result != VK_SUCCESS)
+        return result;
+
+    initInstanceTable(*pInstance, fpGetInstanceProcAddr);
+
+    init_screenshot();
+
+    return result;
+}
+
+// TODO hook DestroyInstance to cleanup
+
+static void
+createDeviceRegisterExtensions(const VkDeviceCreateInfo *pCreateInfo,
+                               VkDevice device) {
+    uint32_t i;
+    DeviceMapStruct *devMap = get_dev_info(device);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+    PFN_vkGetDeviceProcAddr gpa = pDisp->GetDeviceProcAddr;
+    pDisp->CreateSwapchainKHR =
+        (PFN_vkCreateSwapchainKHR)gpa(device, "vkCreateSwapchainKHR");
+    pDisp->GetSwapchainImagesKHR =
+        (PFN_vkGetSwapchainImagesKHR)gpa(device, "vkGetSwapchainImagesKHR");
+    pDisp->AcquireNextImageKHR =
+        (PFN_vkAcquireNextImageKHR)gpa(device, "vkAcquireNextImageKHR");
+    pDisp->QueuePresentKHR =
+        (PFN_vkQueuePresentKHR)gpa(device, "vkQueuePresentKHR");
+    devMap->wsi_enabled = false;
+    for (i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
+        if (strcmp(pCreateInfo->ppEnabledExtensionNames[i],
+                   VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0)
+            devMap->wsi_enabled = true;
+    }
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+CreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo *pCreateInfo,
+             const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
+    VkLayerDeviceCreateInfo *chain_info =
+        get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+    assert(chain_info->u.pLayerInfo);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
+        chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr =
+        chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+    VkInstance instance = physDeviceMap[gpu]->instance;
+    PFN_vkCreateDevice fpCreateDevice =
+        (PFN_vkCreateDevice)fpGetInstanceProcAddr(instance, "vkCreateDevice");
+    if (fpCreateDevice == NULL) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // Advance the link info for the next element on the chain
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+    VkResult result = fpCreateDevice(gpu, pCreateInfo, pAllocator, pDevice);
+    if (result != VK_SUCCESS) {
+        return result;
+    }
+
+    assert(deviceMap.find(*pDevice) == deviceMap.end());
+    DeviceMapStruct *deviceMapElem = new DeviceMapStruct;
+    deviceMap[*pDevice] = deviceMapElem;
+
+    // Setup device dispatch table
+    deviceMapElem->device_dispatch_table = new VkLayerDispatchTable;
+    layer_init_device_dispatch_table(
+        *pDevice, deviceMapElem->device_dispatch_table, fpGetDeviceProcAddr);
+
+    createDeviceRegisterExtensions(pCreateInfo, *pDevice);
+    // Create a mapping from a device to a physicalDevice
+    deviceMapElem->physicalDevice = gpu;
+
+    // store the loader callback for initializing created dispatchable objects
+    chain_info = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
+    if (chain_info) {
+        deviceMapElem->pfn_dev_init = chain_info->u.pfnSetDeviceLoaderData;
+    } else {
+        deviceMapElem->pfn_dev_init = NULL;
+    }
+    return result;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+EnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount,
+                         VkPhysicalDevice *pPhysicalDevices) {
+    VkResult result;
+
+    VkLayerInstanceDispatchTable *pTable = instance_dispatch_table(instance);
+    result = pTable->EnumeratePhysicalDevices(instance, pPhysicalDeviceCount,
+                                              pPhysicalDevices);
+    if (result == VK_SUCCESS && *pPhysicalDeviceCount > 0 && pPhysicalDevices) {
+        for (uint32_t i = 0; i < *pPhysicalDeviceCount; i++) {
+            // Create a mapping from a physicalDevice to an instance
+            if (physDeviceMap[pPhysicalDevices[i]] == NULL) {
+                PhysDeviceMapStruct *physDeviceMapElem =
+                    new PhysDeviceMapStruct;
+                physDeviceMap[pPhysicalDevices[i]] = physDeviceMapElem;
+            }
+            physDeviceMap[pPhysicalDevices[i]]->instance = instance;
+        }
+    }
+    return result;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+DestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
+    DeviceMapStruct *devMap = get_dev_info(device);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+    pDisp->DestroyDevice(device, pAllocator);
+
+    loader_platform_thread_lock_mutex(&globalLock);
+    delete pDisp;
+    delete devMap;
+
+    deviceMap.erase(device);
+    loader_platform_thread_unlock_mutex(&globalLock);
+}
+
+VKAPI_ATTR void VKAPI_CALL GetDeviceQueue(VkDevice device,
+                                          uint32_t queueNodeIndex,
+                                          uint32_t queueIndex,
+                                          VkQueue *pQueue) {
+    DeviceMapStruct *devMap = get_dev_info(device);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+    pDisp->GetDeviceQueue(device, queueNodeIndex, queueIndex, pQueue);
+
+    // Save the device queue in a map if we are taking screenshots.
+    loader_platform_thread_lock_mutex(&globalLock);
+    if (screenshotFramesReceived && screenshotFrames.empty()) {
+        // No screenshots in the list to take
+        loader_platform_thread_unlock_mutex(&globalLock);
+        return;
+    }
+
+    VkDevice que = static_cast<VkDevice>(static_cast<void *>(*pQueue));
+    deviceMap.emplace(que, devMap);
+
+    // Create a mapping from a device to a queue
+    devMap->queue = *pQueue;
+    loader_platform_thread_unlock_mutex(&globalLock);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL CreateCommandPool(
+    VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo,
+    const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool) {
+    DeviceMapStruct *devMap = get_dev_info(device);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+    VkResult result =
+        pDisp->CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
+
+    // Save the command pool on a map if we are taking screenshots.
+    loader_platform_thread_lock_mutex(&globalLock);
+    if (screenshotFramesReceived && screenshotFrames.empty()) {
+        // No screenshots in the list to take
+        loader_platform_thread_unlock_mutex(&globalLock);
+        return result;
+    }
+
+    // Create a mapping from a device to a commandPool
+    devMap->commandPool = *pCommandPool;
+    loader_platform_thread_unlock_mutex(&globalLock);
+    return result;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL CreateSwapchainKHR(
+    VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo,
+    const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) {
+
+    DeviceMapStruct *devMap = get_dev_info(device);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+
+    // This layer does an image copy later on, and the copy command expects the
+    // transfer src bit to be on.
+    VkSwapchainCreateInfoKHR myCreateInfo = *pCreateInfo;
+    myCreateInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+    VkResult result = pDisp->CreateSwapchainKHR(device, &myCreateInfo,
+                                                pAllocator, pSwapchain);
+
+    // Save the swapchain in a map of we are taking screenshots.
+    loader_platform_thread_lock_mutex(&globalLock);
+    if (screenshotFramesReceived && screenshotFrames.empty()) {
+        // No screenshots in the list to take
+        loader_platform_thread_unlock_mutex(&globalLock);
+        return result;
+    }
+
+    if (result == VK_SUCCESS) {
+        // Create a mapping for a swapchain to a device, image extent, and
+        // format
+        SwapchainMapStruct *swapchainMapElem = new SwapchainMapStruct;
+        swapchainMapElem->device = device;
+        swapchainMapElem->imageExtent = pCreateInfo->imageExtent;
+        swapchainMapElem->format = pCreateInfo->imageFormat;
+        swapchainMap.insert(make_pair(*pSwapchain, swapchainMapElem));
+
+        // Create a mapping for the swapchain object into the dispatch table
+        // TODO is this needed? screenshot_device_table_map.emplace((void
+        // *)pSwapchain, pTable);
+    }
+    loader_platform_thread_unlock_mutex(&globalLock);
+
+    return result;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain,
+                      uint32_t *pCount, VkImage *pSwapchainImages) {
+    DeviceMapStruct *devMap = get_dev_info(device);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+    VkResult result = pDisp->GetSwapchainImagesKHR(device, swapchain, pCount,
+                                                   pSwapchainImages);
+
+    // Save the swapchain images in a map if we are taking screenshots
+    loader_platform_thread_lock_mutex(&globalLock);
+    if (screenshotFramesReceived && screenshotFrames.empty()) {
+        // No screenshots in the list to take
+        loader_platform_thread_unlock_mutex(&globalLock);
+        return result;
+    }
+
+    if (result == VK_SUCCESS && pSwapchainImages && !swapchainMap.empty() &&
+        swapchainMap.find(swapchain) != swapchainMap.end()) {
+        unsigned i;
+
+        for (i = 0; i < *pCount; i++) {
+            // Create a mapping for an image to a device, image extent, and
+            // format
+            if (imageMap[pSwapchainImages[i]] == NULL) {
+                ImageMapStruct *imageMapElem = new ImageMapStruct;
+                imageMap[pSwapchainImages[i]] = imageMapElem;
+            }
+            imageMap[pSwapchainImages[i]]->device =
+                swapchainMap[swapchain]->device;
+            imageMap[pSwapchainImages[i]]->imageExtent =
+                swapchainMap[swapchain]->imageExtent;
+            imageMap[pSwapchainImages[i]]->format =
+                swapchainMap[swapchain]->format;
+        }
+
+        // Add list of images to swapchain to image map
+        SwapchainMapStruct *swapchainMapElem = swapchainMap[swapchain];
+        if (i >= 1 && swapchainMapElem) {
+            VkImage *imageList = new VkImage[i];
+            swapchainMapElem->imageList = imageList;
+            for (unsigned j = 0; j < i; j++) {
+                swapchainMapElem->imageList[j] = pSwapchainImages[j];
+            }
+        }
+    }
+    loader_platform_thread_unlock_mutex(&globalLock);
+    return result;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) {
+    static int frameNumber = 0;
+    if (frameNumber == 10) {
+        fflush(stdout); /* *((int*)0)=0; */
+    }
+    DeviceMapStruct *devMap = get_dev_info((VkDevice)queue);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+    VkResult result = pDisp->QueuePresentKHR(queue, pPresentInfo);
+    loader_platform_thread_lock_mutex(&globalLock);
+
+    if (!screenshotFramesReceived) {
+        const char *vk_screenshot_frames = local_getenv(env_var);
+        if (vk_screenshot_frames && *vk_screenshot_frames) {
+            populate_frame_list(vk_screenshot_frames);
+        }
+        // Backwards compatibility
+        else {
+            const char *_vk_screenshot = local_getenv(env_var_old);
+            if (_vk_screenshot && *_vk_screenshot) {
+                populate_frame_list(_vk_screenshot);
+            }
+            local_free_getenv(_vk_screenshot);
+        }
+
+        local_free_getenv(vk_screenshot_frames);
+    }
+
+    if (result == VK_SUCCESS && !screenshotFrames.empty()) {
+        set<int>::iterator it;
+        it = screenshotFrames.find(frameNumber);
+        if (it != screenshotFrames.end()) {
+            string fileName;
+
+#ifdef ANDROID
+            // std::to_string is not supported currently
+            char buffer [64];
+            snprintf(buffer, sizeof(buffer), "/sdcard/Android/%d", frameNumber);
+            std::string base(buffer);
+            fileName = base + ".ppm";
+#else
+            fileName = to_string(frameNumber) + ".ppm";
+#endif
+
+            VkImage image;
+            VkSwapchainKHR swapchain;
+            // We'll dump only one image: the first
+            swapchain = pPresentInfo->pSwapchains[0];
+            image = swapchainMap[swapchain]
+                        ->imageList[pPresentInfo->pImageIndices[0]];
+            writePPM(fileName.c_str(), image);
+            screenshotFrames.erase(it);
+
+            if (screenshotFrames.empty()) {
+                // Free all our maps since we are done with them.
+                for (auto it = swapchainMap.begin(); it != swapchainMap.end();
+                     it++) {
+                    SwapchainMapStruct *swapchainMapElem = it->second;
+                    delete swapchainMapElem;
+                }
+                for (auto it = imageMap.begin(); it != imageMap.end(); it++) {
+                    ImageMapStruct *imageMapElem = it->second;
+                    delete imageMapElem;
+                }
+                for (auto it = physDeviceMap.begin(); it != physDeviceMap.end();
+                     it++) {
+                    PhysDeviceMapStruct *physDeviceMapElem = it->second;
+                    delete physDeviceMapElem;
+                }
+                swapchainMap.clear();
+                imageMap.clear();
+                physDeviceMap.clear();
+            }
+        }
+    }
+    frameNumber++;
+    loader_platform_thread_unlock_mutex(&globalLock);
+    return result;
+}
+
+// Unused, but this could be provided as an extension or utility to the
+// application in the future.
+VKAPI_ATTR VkResult VKAPI_CALL SpecifyScreenshotFrames(const char *frameList) {
+    populate_frame_list(frameList);
+    return VK_SUCCESS;
+}
+
+static const VkLayerProperties global_layer = {
+    "VK_LAYER_LUNARG_screenshot", VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), 1,
+    "Layer: screenshot",
+};
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateInstanceLayerProperties(
+    uint32_t *pCount, VkLayerProperties *pProperties) {
+    return util_GetLayerProperties(1, &global_layer, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceLayerProperties(
+    VkPhysicalDevice physicalDevice, uint32_t *pCount,
+    VkLayerProperties *pProperties) {
+    return util_GetLayerProperties(1, &global_layer, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+EnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pCount,
+                                     VkExtensionProperties *pProperties) {
+    if (pLayerName && !strcmp(pLayerName, global_layer.layerName))
+        return util_GetExtensionProperties(0, NULL, pCount, pProperties);
+
+    return VK_ERROR_LAYER_NOT_PRESENT;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(
+    VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pCount,
+    VkExtensionProperties *pProperties) {
+    if (pLayerName && !strcmp(pLayerName, global_layer.layerName))
+        return util_GetExtensionProperties(0, NULL, pCount, pProperties);
+
+    assert(physicalDevice);
+
+    VkLayerInstanceDispatchTable *pTable =
+        instance_dispatch_table(physicalDevice);
+    return pTable->EnumerateDeviceExtensionProperties(
+        physicalDevice, pLayerName, pCount, pProperties);
+}
+
+static PFN_vkVoidFunction intercept_core_instance_command(const char *name);
+
+static PFN_vkVoidFunction intercept_core_device_command(const char *name);
+
+static PFN_vkVoidFunction intercept_khr_swapchain_command(const char *name,
+                                                          VkDevice dev);
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+GetDeviceProcAddr(VkDevice dev, const char *funcName) {
+    PFN_vkVoidFunction proc = intercept_core_device_command(funcName);
+    if (proc)
+        return proc;
+
+    if (dev == NULL) {
+        return NULL;
+    }
+
+    proc = intercept_khr_swapchain_command(funcName, dev);
+    if (proc)
+        return proc;
+
+    DeviceMapStruct *devMap = get_dev_info(dev);
+    assert(devMap);
+    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
+
+    if (pDisp->GetDeviceProcAddr == NULL)
+        return NULL;
+    return pDisp->GetDeviceProcAddr(dev, funcName);
+}
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+GetInstanceProcAddr(VkInstance instance, const char *funcName) {
+    PFN_vkVoidFunction proc = intercept_core_instance_command(funcName);
+    if (proc)
+        return proc;
+
+    assert(instance);
+
+    proc = intercept_core_device_command(funcName);
+    if (!proc)
+        proc = intercept_khr_swapchain_command(funcName, VK_NULL_HANDLE);
+    if (proc)
+        return proc;
+
+    VkLayerInstanceDispatchTable *pTable = instance_dispatch_table(instance);
+    if (pTable->GetInstanceProcAddr == NULL)
+        return NULL;
+    return pTable->GetInstanceProcAddr(instance, funcName);
+}
+
+static PFN_vkVoidFunction intercept_core_instance_command(const char *name) {
+
+    static const struct {
+        const char *name;
+        PFN_vkVoidFunction proc;
+    } core_instance_commands[] = {
+        {"vkGetInstanceProcAddr",
+         reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr)},
+        {"vkCreateInstance",
+         reinterpret_cast<PFN_vkVoidFunction>(CreateInstance)},
+        {"vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(CreateDevice)},
+        {"vkEnumeratePhysicalDevices",
+         reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices)},
+        {"vkEnumerateInstanceLayerProperties",
+         reinterpret_cast<PFN_vkVoidFunction>(
+             EnumerateInstanceLayerProperties)},
+        {"vkEnumerateDeviceLayerProperties",
+         reinterpret_cast<PFN_vkVoidFunction>(EnumerateDeviceLayerProperties)},
+        {"vkEnumerateInstanceExtensionProperties",
+         reinterpret_cast<PFN_vkVoidFunction>(
+             EnumerateInstanceExtensionProperties)},
+        {"vkEnumerateDeviceExtensionProperties",
+         reinterpret_cast<PFN_vkVoidFunction>(
+             EnumerateDeviceExtensionProperties)}};
+
+    for (size_t i = 0; i < ARRAY_SIZE(core_instance_commands); i++) {
+        if (!strcmp(core_instance_commands[i].name, name))
+            return core_instance_commands[i].proc;
+    }
+
+    return nullptr;
+}
+
+static PFN_vkVoidFunction intercept_core_device_command(const char *name) {
+    static const struct {
+        const char *name;
+        PFN_vkVoidFunction proc;
+    } core_device_commands[] = {
+        {"vkGetDeviceProcAddr",
+         reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr)},
+        {"vkGetDeviceQueue",
+         reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue)},
+        {"vkCreateCommandPool",
+         reinterpret_cast<PFN_vkVoidFunction>(CreateCommandPool)},
+        {"vkDestroyDevice",
+         reinterpret_cast<PFN_vkVoidFunction>(DestroyDevice)},
+    };
+
+    for (size_t i = 0; i < ARRAY_SIZE(core_device_commands); i++) {
+        if (!strcmp(core_device_commands[i].name, name))
+            return core_device_commands[i].proc;
+    }
+
+    return nullptr;
+}
+
+static PFN_vkVoidFunction intercept_khr_swapchain_command(const char *name,
+                                                          VkDevice dev) {
+    static const struct {
+        const char *name;
+        PFN_vkVoidFunction proc;
+    } khr_swapchain_commands[] = {
+        {"vkCreateSwapchainKHR",
+         reinterpret_cast<PFN_vkVoidFunction>(CreateSwapchainKHR)},
+        {"vkGetSwapchainImagesKHR",
+         reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR)},
+        {"vkQueuePresentKHR",
+         reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR)},
+    };
+
+    if (dev) {
+        DeviceMapStruct *devMap = get_dev_info(dev);
+        if (!devMap->wsi_enabled)
+            return nullptr;
+    }
+
+    for (size_t i = 0; i < ARRAY_SIZE(khr_swapchain_commands); i++) {
+        if (!strcmp(khr_swapchain_commands[i].name, name))
+            return khr_swapchain_commands[i].proc;
+    }
+
+    return nullptr;
+}
+
+} // namespace screenshot
+
+// loader-layer interface v0, just wrappers since there is only a layer
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+vkEnumerateInstanceLayerProperties(uint32_t *pCount,
+                                   VkLayerProperties *pProperties) {
+    return screenshot::EnumerateInstanceLayerProperties(pCount, pProperties);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(
+    VkPhysicalDevice physicalDevice, uint32_t *pCount,
+    VkLayerProperties *pProperties) {
+    // the layer command handles VK_NULL_HANDLE just fine internally
+    assert(physicalDevice == VK_NULL_HANDLE);
+    return screenshot::EnumerateDeviceLayerProperties(VK_NULL_HANDLE, pCount,
+                                                      pProperties);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pCount,
+                                       VkExtensionProperties *pProperties) {
+    return screenshot::EnumerateInstanceExtensionProperties(pLayerName, pCount,
+                                                            pProperties);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+                                     const char *pLayerName, uint32_t *pCount,
+                                     VkExtensionProperties *pProperties) {
+    // the layer command handles VK_NULL_HANDLE just fine internally
+    assert(physicalDevice == VK_NULL_HANDLE);
+    return screenshot::EnumerateDeviceExtensionProperties(
+        VK_NULL_HANDLE, pLayerName, pCount, pProperties);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
+    return screenshot::GetDeviceProcAddr(dev, funcName);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {
+    return screenshot::GetInstanceProcAddr(instance, funcName);
+}
diff --git a/layersvt/vk_layer_settings.txt b/layersvt/vk_layer_settings.txt
new file mode 100644
index 0000000..abf18fd
--- /dev/null
+++ b/layersvt/vk_layer_settings.txt
@@ -0,0 +1,78 @@
+################################################################################
+#
+#  This file contains per-layer settings that configure layer behavior at
+#  execution time. Comments in this file are denoted with the "#" char.
+#  Settings lines are of the form:
+#      "<LayerIdentifier>.<SettingName> = <SettingValue>"
+#
+#  <LayerIdentifier> is typically the official layer name, minus the VK_LAYER
+#  prefix and all lower-camel-case -- i.e., for VK_LAYER_LUNARG_api_dump, the
+#  layer identifier is 'lunarg_api_dump'.
+#
+################################################################################
+################################################################################
+#  VK_LAYER_LUNARG_api_dump Settings:
+#  ==================================
+#
+#    DETAILED:
+#    =========
+#    <LayerIdentifer>.detailed : Setting this to TRUE causes parameter details
+#    to be dumped in addition to API calls.
+#    
+#    NO_ADDR:
+#    ========
+#    <LayerIdentifier>.no_addr : Setting this to TRUE causes "address" to be
+#    dumped in place of hex addresses.
+#
+#    FILE:
+#    =====
+#    <LayerIdentifer>.file : Setting this to TRUE indicates that output
+#    should be written to file instead of STDOUT.
+#
+#    LOG_FILENAME:
+#    =============
+#    <LayerIdentifer>.log_filename : Specifies the file to dump to when
+#    "file = TRUE".  The default is "vk_apidump.txt".
+#
+#    FLUSH:
+#    ======
+#    <LayerIdentifier>.flush : Setting this to TRUE causes IO to be flushed
+#    each API call that is written
+#
+#   INDENT SIZE
+#   ==============
+#   <LayerIdentifier>.indent_size : Specifies the number of spaces that a tab
+#   is equal to
+#
+#   SHOW TYPES
+#   ==============
+#   <LayerIdentifier>.show_types : Setting this to TRUE causes types to be
+#   dumped in addition to values
+#
+#   NAME SIZE
+#   ==============
+#   <LayerIdentifier>.name_size : The number of characters the name of a
+#   variable should consume, assuming more are not required
+#
+#   TYPE SIZE
+#   ==============
+#   <LayerIdentifier>.type_size : The number of characters the type of a
+#   variable should consume, assuming more are not requires
+#
+#   USE_SPACES
+#   ==============
+#   <LayerIdentifier>.use_spaces : Setting this to TRUE causes all tabs
+#   characters to be replaced with spaces
+
+#  VK_LUNARG_LAYER_api_dump Settings
+lunarg_api_dump.detailed = TRUE
+lunarg_api_dump.no_addr = FALSE
+lunarg_api_dump.file = FALSE
+lunarg_api_dump.log_filename = vk_apidump.txt
+lunarg_api_dump.flush = TRUE
+lunarg_api_dump.indent_size = 4
+lunarg_api_dump.show_types = TRUE
+lunarg_api_dump.name_size = 32
+lunarg_api_dump.type_size = 0
+lunarg_api_dump.use_spaces = TRUE
+lunarg_api_dump.show_shader = FALSE
diff --git a/layersvt/windows/VkLayer_api_dump.json b/layersvt/windows/VkLayer_api_dump.json
new file mode 100644
index 0000000..593dedf
--- /dev/null
+++ b/layersvt/windows/VkLayer_api_dump.json
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_api_dump",
+        "type": "GLOBAL",
+        "library_path": ".\\VkLayer_api_dump.dll",
+        "api_version": "1.0.38",
+        "implementation_version": "2",
+        "description": "LunarG debug layer"
+    }
+}
diff --git a/layersvt/windows/VkLayer_monitor.json b/layersvt/windows/VkLayer_monitor.json
new file mode 100644
index 0000000..a34459e
--- /dev/null
+++ b/layersvt/windows/VkLayer_monitor.json
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_monitor",
+        "type": "GLOBAL",
+        "library_path": ".\\VkLayer_monitor.dll",
+        "api_version": "1.0.38",
+        "implementation_version": "1",
+        "description": "Execution Monitoring Layer"
+    }
+}
diff --git a/layersvt/windows/VkLayer_screenshot.json b/layersvt/windows/VkLayer_screenshot.json
new file mode 100644
index 0000000..4689165
--- /dev/null
+++ b/layersvt/windows/VkLayer_screenshot.json
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_screenshot",
+        "type": "GLOBAL",
+        "library_path": ".\\VkLayer_screenshot.dll",
+        "api_version": "1.0.38",
+        "implementation_version": "1",
+        "description": "LunarG image capture layer"
+    }
+}
diff --git a/libs/vkjson/CMakeLists.txt b/libs/vkjson/CMakeLists.txt
index 2e79d91..8c218b1 100644
--- a/libs/vkjson/CMakeLists.txt
+++ b/libs/vkjson/CMakeLists.txt
@@ -1,23 +1,16 @@
 # Copyright 2015 Google Inc. All rights reserved.
 #
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and/or associated documentation files (the
-# "Materials"), to deal in the Materials without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Materials, and to
-# permit persons to whom the Materials are furnished to do so, subject to
-# the following conditions:
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
 #
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Materials.
+#     http://www.apache.org/licenses/LICENSE-2.0
 #
-# 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.
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
 include_directories(
 	${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/scripts/api_dump_generator.py b/scripts/api_dump_generator.py
new file mode 100644
index 0000000..9f0381d
--- /dev/null
+++ b/scripts/api_dump_generator.py
@@ -0,0 +1,1370 @@
+#!/usr/bin/python3 -i
+#
+# Copyright (c) 2015-2016 Valve Corporation
+# Copyright (c) 2015-2016 LunarG, Inc.
+# Copyright (c) 2015-2016 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Lenny Komow <lenny@lunarg.com>
+#
+# The API dump layer works by passing custom format strings to the ApiDumpGenerator. These format
+# strings are C++ code, with 3-ish exceptions:
+#   * Anything beginning with @ will be expanded by the ApiDumpGenerator. These are used to allow
+#       iteration over various items within the Vulkan spec, usch as functions, enums, etc.
+#   * Anything surrounded by { and } will be substituted when the ApiDumpGenerator expands the @
+#       directives. This gives a way to get things like data types or names for anything that can
+#       be iterated over in an @ directive.
+#   * Curly braces must be doubled like {{ for a single curly brace to appear in the output code.
+#
+# The API dump uses separate format strings for each output file, but passes them to a common
+# generator. This allows greater flexibility, as changing the output codegen means just changing
+# the corresponding format string.
+#
+# Currently, the API dump layer generates the following files from the following strings:
+#   * api_dump.cpp: COMMON_CODEGEN - Provides all entrypoints for functions and dispatches the calls
+#       to the proper back end
+#   * api_dump_text.h: TEXT_CODEGEN - Provides the back end for dumping to a text file
+#
+
+import generator as gen
+import re
+import sys
+import xml.etree;
+
+COMMON_CODEGEN = """
+/* Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ * Copyright (c) 2015-2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Lenny Komow <lenny@lunarg.com>
+ */
+ 
+/*
+ * This file is generated from the Khronos Vulkan XML API Registry.
+ */
+ 
+#include "api_dump_text.h"
+
+//============================= Dump Functions ==============================//
+
+@foreach function where('{funcReturn}' != 'void')
+inline void dump_{funcName}(ApiDumpInstance& dump_inst, {funcReturn} result, {funcTypedParams})
+{{
+    loader_platform_thread_lock_mutex(dump_inst.outputMutex());
+    switch(dump_inst.settings().format())
+    {{
+    case ApiDumpFormat::Text:
+        dump_text_{funcName}(dump_inst, result, {funcNamedParams});
+        break;
+    }}
+    loader_platform_thread_unlock_mutex(dump_inst.outputMutex());
+}}
+@end function
+
+@foreach function where('{funcReturn}' == 'void')
+inline void dump_{funcName}(ApiDumpInstance& dump_inst, {funcTypedParams})
+{{
+    loader_platform_thread_lock_mutex(dump_inst.outputMutex());
+    switch(dump_inst.settings().format())
+    {{
+    case ApiDumpFormat::Text:
+        dump_text_{funcName}(dump_inst, {funcNamedParams});
+        break;
+    }}
+    loader_platform_thread_unlock_mutex(dump_inst.outputMutex());
+}}
+@end function
+
+//============================= API EntryPoints =============================//
+
+// Specifically implemented functions
+
+@foreach function where('{funcName}' == 'vkCreateInstance')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    // Get the function pointer
+    VkLayerInstanceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+    assert(chain_info->u.pLayerInfo != 0);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    assert(fpGetInstanceProcAddr != 0);
+    PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance) fpGetInstanceProcAddr(NULL, "vkCreateInstance");
+    if(fpCreateInstance == NULL) {{
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }}
+    
+    // Call the function and create the dispatch table
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+    {funcReturn} result = fpCreateInstance({funcNamedParams});
+    if(result == VK_SUCCESS) {{
+        initInstanceTable(*pInstance, fpGetInstanceProcAddr);
+    }}
+
+    // Output the API dump
+    dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
+    return result;
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkDestroyInstance')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    // Destroy the dispatch table
+    dispatch_key key = get_dispatch_key({funcDispatchParam});
+    instance_dispatch_table({funcDispatchParam})->DestroyInstance({funcNamedParams});
+    destroy_instance_dispatch_table(key);
+    
+    // Output the API dump
+    dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkCreateDevice')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    // Get the function pointer
+    VkLayerDeviceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+    assert(chain_info->u.pLayerInfo != 0);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+    PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice) fpGetInstanceProcAddr(NULL, "vkCreateDevice");
+    if(fpCreateDevice == NULL) {{
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }}
+    
+    // Call the function and create the dispatch table
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+    {funcReturn} result = fpCreateDevice({funcNamedParams});
+    if(result == VK_SUCCESS) {{
+        initDeviceTable(*pDevice, fpGetDeviceProcAddr);
+    }}
+    
+    // Output the API dump
+    dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
+    return result;
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkDestroyDevice')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    // Destroy the dispatch table
+    dispatch_key key = get_dispatch_key({funcDispatchParam});
+    device_dispatch_table({funcDispatchParam})->DestroyDevice({funcNamedParams});
+    destroy_device_dispatch_table(key);
+    
+    // Output the API dump
+    dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkEnumerateInstanceExtensionProperties')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    return util_GetExtensionProperties(0, NULL, pPropertyCount, pProperties);
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkEnumerateInstanceLayerProperties')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    static const VkLayerProperties layerProperties[] = {{
+        {{
+            "VK_LAYER_LUNARG_api_dump",
+            VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), // specVersion
+            VK_MAKE_VERSION(0, 2, 0), // implementationVersion
+            "layer: api_dump",
+        }}
+    }};
+    
+    return util_GetLayerProperties(ARRAY_SIZE(layerProperties), layerProperties, pPropertyCount, pProperties);
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkEnumerateDeviceLayerProperties')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    static const VkLayerProperties layerProperties[] = {{
+        {{
+            "VK_LAYER_LUNARG_api_dump",
+            VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION),
+            VK_MAKE_VERSION(0, 2, 0),
+            "layer: api_dump",
+        }}
+    }};
+    
+    return util_GetLayerProperties(ARRAY_SIZE(layerProperties), layerProperties, pPropertyCount, pProperties);
+}}
+@end function
+
+@foreach function where('{funcName}' == 'vkQueuePresentKHR')
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    {funcReturn} result = device_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
+    dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
+    ApiDumpInstance::current().nextFrame();
+    return result;
+}}
+@end function
+
+// Autogen instance functions
+
+@foreach function where('{funcType}' == 'instance' and '{funcReturn}' != 'void' and '{funcName}' not in ['vkCreateInstance', 'vkDestroyInstance', 'vkCreateDevice', 'vkGetInstanceProcAddr', 'vkEnumerateDeviceExtensionProperties', 'vkEnumerateDeviceLayerProperties'])
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    {funcReturn} result = instance_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
+    dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
+    return result;
+}}
+@end function
+
+@foreach function where('{funcType}' == 'instance' and '{funcReturn}' == 'void' and '{funcName}' not in ['vkCreateInstance', 'vkDestroyInstance', 'vkCreateDevice', 'vkGetInstanceProcAddr', 'vkEnumerateDeviceExtensionProperties', 'vkEnumerateDeviceLayerProperties'])
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    instance_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
+    dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
+}}
+@end function
+
+// Autogen device functions
+
+@foreach function where('{funcType}' == 'device' and '{funcReturn}' != 'void' and '{funcName}' not in ['vkDestroyDevice', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkQueuePresentKHR', 'vkGetDeviceProcAddr'])
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    {funcReturn} result = device_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
+    dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
+    return result;
+}}
+@end function
+
+@foreach function where('{funcType}' == 'device' and '{funcReturn}' == 'void' and '{funcName}' not in ['vkDestroyDevice', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkGetDeviceProcAddr'])
+VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
+{{
+    device_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
+    dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
+}}
+@end function
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* pName)
+{{
+    @foreach function where('{funcType}' == 'instance'  and '{funcName}' not in [ 'vkEnumerateDeviceExtensionProperties' ])
+    if(strcmp(pName, "{funcName}") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>({funcName});
+    @end function
+    
+    if(instance_dispatch_table(instance)->GetInstanceProcAddr == NULL)
+        return NULL;
+    return instance_dispatch_table(instance)->GetInstanceProcAddr(instance, pName);
+}}
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName)
+{{
+    @foreach function where('{funcType}' == 'device')
+    if(strcmp(pName, "{funcName}") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>({funcName});
+    @end function
+    
+    if(device_dispatch_table(device)->GetDeviceProcAddr == NULL)
+        return NULL;
+    return device_dispatch_table(device)->GetDeviceProcAddr(device, pName);
+}}
+"""
+
+TEXT_CODEGEN = """
+/* Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ * Copyright (c) 2015-2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Lenny Komow <lenny@lunarg.com>
+ */
+ 
+/*
+ * This file is generated from the Khronos Vulkan XML API Registry.
+ */
+ 
+#pragma once
+ 
+#include "api_dump.h"
+
+@foreach struct
+std::ostream& dump_text_{sctName}(const {sctName}& object, const ApiDumpSettings& settings, int indents);
+@end struct
+@foreach union
+std::ostream& dump_text_{unName}(const {unName}& object, const ApiDumpSettings& settings, int indents);
+@end union
+
+//=========================== Type Implementations ==========================//
+
+@foreach type where('{etyName}' != 'void')
+inline std::ostream& dump_text_{etyName}({etyName} object, const ApiDumpSettings& settings, int indents)
+{{
+    @if('{etyName}' != 'uint8_t')
+    return settings.stream() << object;
+    @end if
+    @if('{etyName}' == 'uint8_t')
+    return settings.stream() << (uint32_t) object;
+    @end if
+}}
+@end type
+
+//========================= Basetype Implementations ========================//
+
+@foreach basetype
+inline std::ostream& dump_text_{baseName}({baseName} object, const ApiDumpSettings& settings, int indents)
+{{
+    return settings.stream() << object;
+}}
+@end basetype
+
+//======================= System Type Implementations =======================//
+
+@foreach systype
+inline std::ostream& dump_text_{sysName}(const {sysType} object, const ApiDumpSettings& settings, int indents)
+{{
+    return settings.stream() << object;
+}}
+@end systype
+
+//========================== Handle Implementations =========================//
+
+@foreach handle
+inline std::ostream& dump_text_{hdlName}(const {hdlName} object, const ApiDumpSettings& settings, int indents)
+{{
+    if(settings.showAddress())
+        return settings.stream() << object;
+    else
+        return settings.stream() << "address";
+}}
+@end handle
+
+//=========================== Enum Implementations ==========================//
+
+@foreach enum
+std::ostream& dump_text_{enumName}({enumName} object, const ApiDumpSettings& settings, int indents)
+{{
+    switch((int64_t) object)
+    {{
+    @foreach option
+    case {optValue}:
+        settings.stream() << "{optName} (";
+        break;
+    @end option
+    default:
+        settings.stream() << "UNKNOWN (";
+    }}
+    return settings.stream() << object << ")";
+}}
+@end enum
+
+//========================= Bitmask Implementations =========================//
+
+@foreach bitmask
+std::ostream& dump_text_{bitName}({bitName} object, const ApiDumpSettings& settings, int indents)
+{{
+    bool is_first = true;
+    //settings.formatNameType(stream, indents, name, type_string) << object;
+    settings.stream() << object;
+    @foreach option
+    if(object & {optValue})
+        is_first = dump_text_bitmaskOption("{optName}", settings.stream(), is_first);
+    @end option
+    if(!is_first)
+        settings.stream() << ")";
+    return settings.stream();
+}}
+@end bitmask
+
+//=========================== Flag Implementations ==========================//
+
+@foreach flag where('{flagEnum}' != 'None')
+inline std::ostream& dump_text_{flagName}({flagName} object, const ApiDumpSettings& settings, int indents)
+{{
+    return dump_text_{flagEnum}(({flagEnum}) object, settings, indents);
+}}
+@end flag
+@foreach flag where('{flagEnum}' == 'None')
+inline std::ostream& dump_text_{flagName}({flagName} object, const ApiDumpSettings& settings, int indents)
+{{
+    return settings.stream() << object;
+}}
+@end flag
+
+//======================= Func Pointer Implementations ======================//
+
+@foreach funcpointer
+inline std::ostream& dump_text_{pfnName}({pfnName} object, const ApiDumpSettings& settings, int indents)
+{{
+    if(settings.showAddress())
+        return settings.stream() << object;
+    else
+        return settings.stream() << "address";
+}}
+@end funcpointer
+
+//========================== Struct Implementations =========================//
+
+@foreach struct where('{sctName}' != 'VkShaderModuleCreateInfo')
+std::ostream& dump_text_{sctName}(const {sctName}& object, const ApiDumpSettings& settings, int indents)
+{{
+    if(settings.showAddress())
+        settings.stream() << &object << ":\\n";
+    else
+        settings.stream() << "address:\\n";
+    
+    @foreach member
+    @if('{memCondition}' != 'None')
+    if({memCondition})
+    @end if
+    
+    @if({memPtrLevel} == 0)
+    dump_text_value<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if({memPtrLevel} == 1 and '{memLength}' == 'None')
+    dump_text_pointer<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if({memPtrLevel} == 1 and '{memLength}' != 'None' and not {memLengthIsMember})
+    dump_text_array<const {memBaseType}>(object.{memName}, {memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if({memPtrLevel} == 1 and '{memLength}' != 'None' and {memLengthIsMember})
+    dump_text_array<const {memBaseType}>(object.{memName}, object.{memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    
+    @if('{memCondition}' != 'None')
+    else
+        dump_text_special("UNUSED", settings, "{memType}", "{memName}", indents + 1);
+    @end if
+    @end member
+    return settings.stream();
+}}
+@end struct
+
+@foreach struct where('{sctName}' == 'VkShaderModuleCreateInfo')
+std::ostream& dump_text_{sctName}(const {sctName}& object, const ApiDumpSettings& settings, int indents)
+{{
+    if(settings.showAddress())
+        settings.stream() << &object << ":\\n";
+    else
+        settings.stream() << "address:\\n";
+        
+    @foreach member
+    @if('{memCondition}' != 'None')
+    if({memCondition})
+    @end if
+    
+    @if({memPtrLevel} == 0)
+    dump_text_value<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if({memPtrLevel} == 1 and '{memLength}' == 'None')
+    dump_text_pointer<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if({memPtrLevel} == 1 and '{memLength}' != 'None' and not {memLengthIsMember} and '{memName}' != 'pCode')
+    dump_text_array<const {memBaseType}>(object.{memName}, {memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if({memPtrLevel} == 1 and '{memLength}' != 'None' and {memLengthIsMember} and '{memName}' != 'pCode')
+    dump_text_array<const {memBaseType}>(object.{memName}, object.{memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    @end if
+    @if('{memName}' == 'pCode')
+    if(settings.showShader())
+        dump_text_array<const {memBaseType}>(object.{memName}, object.{memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
+    else
+        dump_text_special("SHADER DATA", settings, "{memType}", "{memName}", indents + 1);
+    @end if
+    
+    @if('{memCondition}' != 'None')
+    else
+        dump_text_special("UNUSED", settings, "{memType}", "{memName}", indents + 1);
+    @end if
+    @end member
+    return settings.stream();
+}}
+@end struct
+
+//========================== Union Implementations ==========================//
+
+@foreach union
+std::ostream& dump_text_{unName}(const {unName}& object, const ApiDumpSettings& settings, int indents)
+{{
+    if(settings.showAddress())
+        settings.stream() << &object << " (Union):\\n";
+    else
+        settings.stream() << "address (Union):\\n";
+        
+    @foreach choice
+    @if({chcPtrLevel} == 0)
+    dump_text_value<const {chcBaseType}>(object.{chcName}, settings, "{chcType}", "{chcName}", indents + 1, dump_text_{chcTypeID});
+    @end if
+    @if({chcPtrLevel} == 1 and '{chcLength}' == 'None')
+    dump_text_pointer<const {chcBaseType}>(object.{chcName}, settings, "{chcType}", "{chcName}", indents + 1, dump_text_{chcTypeID});
+    @end if
+    @if({chcPtrLevel} == 1 and '{chcLength}' != 'None')
+    dump_text_array<const {chcBaseType}>(object.{chcName}, {chcLength}, settings, "{chcType}", "{chcChildType}", "{chcName}", indents + 1, dump_text_{chcTypeID});
+    @end if
+    @end choice
+    return settings.stream();
+}}
+@end union
+
+//========================= Function Implementations ========================//
+
+@foreach function where('{funcReturn}' != 'void')
+std::ostream& dump_text_{funcName}(ApiDumpInstance& dump_inst, {funcReturn} result, {funcTypedParams})
+{{
+    const ApiDumpSettings& settings(dump_inst.settings());
+    settings.stream() << "Thread " << dump_inst.threadID() << ", Frame " << dump_inst.frameCount() << ":\\n";
+    settings.stream() << "{funcName}({funcNamedParams}) returns {funcReturn} ";
+    dump_text_{funcReturn}(result, settings, 0) << ":\\n";
+    
+    if(settings.showParams())
+    {{
+        @foreach parameter
+        @if({prmPtrLevel} == 0)
+        dump_text_value<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
+        @end if
+        @if({prmPtrLevel} == 1 and '{prmLength}' == 'None')
+        dump_text_pointer<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
+        @end if
+        @if({prmPtrLevel} == 1 and '{prmLength}' != 'None')
+        dump_text_array<const {prmBaseType}>({prmName}, {prmLength}, settings, "{prmType}", "{prmChildType}", "{prmName}", 1, dump_text_{prmTypeID});
+        @end if
+        @end parameter
+    }}
+    settings.shouldFlush() ? settings.stream() << std::endl : settings.stream() << "\\n";
+    
+    return settings.stream();
+}}
+@end function
+
+@foreach function where('{funcReturn}' == 'void')
+std::ostream& dump_text_{funcName}(ApiDumpInstance& dump_inst, {funcTypedParams})
+{{
+    const ApiDumpSettings& settings(dump_inst.settings());
+    settings.stream() << "Thread " << dump_inst.threadID() << ", Frame " << dump_inst.frameCount() << ":\\n";
+    settings.stream() << "{funcName}({funcNamedParams}) returns {funcReturn}:\\n";
+    
+    if(settings.showParams())
+    {{
+        @foreach parameter
+        @if({prmPtrLevel} == 0)
+        dump_text_value<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
+        @end if
+        @if({prmPtrLevel} == 1 and '{prmLength}' == 'None')
+        dump_text_pointer<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
+        @end if
+        @if({prmPtrLevel} == 1 and '{prmLength}' != 'None')
+        dump_text_array<const {prmBaseType}>({prmName}, {prmLength}, settings, "{prmType}", "{prmChildType}", "{prmName}", 1, dump_text_{prmTypeID});
+        @end if
+        @end parameter
+    }}
+    settings.shouldFlush() ? settings.stream() << std::endl : settings.stream() << "\\n";
+    
+    return settings.stream();
+}}
+@end function
+"""
+
+POINTER_TYPES = ['void', 'xcb_connection_t', 'Display', 'SECURITY_ATTRIBUTES', 'ANativeWindow']
+VALIDITY_CHECKS = {
+    'VkBufferCreateInfo': {
+        'pQueueFamilyIndices': 'object.sharingMode == VK_SHARING_MODE_CONCURRENT',
+    },
+    'VkCommandBufferBeginInfo': {
+        'pInheritanceInfo': 'false',    # No easy way to tell if this is a primary command buffer
+    },
+    'VkDescriptorSetLayoutBinding': {
+        'pImmutableSamplers':
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)',
+    },
+    'VkImageCreateInfo': {
+        'pQueueFamilyIndices': 'object.sharingMode == VK_SHARING_MODE_CONCURRENT',
+    },
+    'VkPipelineViewportStateCreateInfo': {
+        'pViewports': 'false',      # No easy way to check if viewport state is dynamic
+        'pScissors': 'false',       # No easy way to check if scissor state is dynamic
+    },
+    'VkSwapchainCreateInfoKHR': {
+        'pQueueFamilyIndices': 'object.imageSharingMode == VK_SHARING_MODE_CONCURRENT',
+    },
+    'VkWriteDescriptorSet': {
+        'pImageInfo':
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)',
+        'pBufferInfo':
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)',
+        'pTexelBufferView':
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) || ' +
+            '(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)',
+    },
+}
+
+class ApiDumpGeneratorOptions(gen.GeneratorOptions):
+    
+    def __init__(self,
+                 input = None,
+                 filename = None,
+                 directory = '.',
+                 apiname = None,
+                 profile = None,
+                 versions = '.*',
+                 emitversions = '.*',
+                 defaultExtensions = None,
+                 addExtensions = None,
+                 removeExtensions = None,
+                 sortProcedure = None,
+                 prefixText = "",
+                 genFuncPointers = True,
+                 protectFile = True,
+                 protectFeature = True,
+                 protectProto = None,
+                 protectProtoStr = None,
+                 apicall = '',
+                 apientry = '',
+                 apientryp = '',
+                 indentFuncProto = True,
+                 indentFuncPointer = False,
+                 alignFuncParam = 0):
+        gen.GeneratorOptions.__init__(self, filename, directory, apiname, profile,
+            versions, emitversions, defaultExtensions,
+            addExtensions, removeExtensions, sortProcedure)
+        self.input           = input
+        self.prefixText      = prefixText
+        self.genFuncPointers = genFuncPointers
+        self.protectFile     = protectFile
+        self.protectFeature  = protectFeature
+        self.protectProto    = protectProto
+        self.protectProtoStr = protectProtoStr
+        self.apicall         = apicall
+        self.apientry        = apientry
+        self.apientryp       = apientryp
+        self.indentFuncProto = indentFuncProto
+        self.indentFuncPointer = indentFuncPointer
+        self.alignFuncParam  = alignFuncParam
+
+
+class ApiDumpOutputGenerator(gen.OutputGenerator):
+    
+    def __init__(self,
+                 errFile = sys.stderr,
+                 warnFile = sys.stderr,
+                 diagFile = sys.stdout,
+                 registryFile = None):
+        gen.OutputGenerator.__init__(self, errFile, warnFile, diagFile)
+        self.format = None
+        
+        self.constants = {}
+        self.extensions = set()
+        self.extFuncs = {}
+        self.extTypes = {}
+        self.includes = {}
+        
+        self.basetypes = set()
+        self.bitmasks = set()
+        self.enums = set()
+        self.externalTypes = set()
+        self.flags = set()
+        self.funcPointers = set()
+        self.functions = set()
+        self.handles = set()
+        self.structs = set()
+        self.unions = set()
+        
+        self.registryFile = registryFile
+        
+    def beginFile(self, genOpts):
+        gen.OutputGenerator.beginFile(self, genOpts)
+        self.format = genOpts.input
+
+        if self.registryFile != None:
+            root = xml.etree.ElementTree.parse(self.registryFile)
+        else:
+            root = self.registry.reg
+            
+        for node in root.find('extensions').findall('extension'):
+            ext = VulkanExtension(node)
+            self.extensions.add(ext)
+            for item in ext.vktypes:
+                self.extTypes[item] = ext
+            for item in ext.vkfuncs:
+                self.extFuncs[item] = ext
+                
+        for node in self.registry.reg.findall('enums'):
+            if node.get('name') == 'API Constants':
+                for item in node.findall('enum'):
+                    self.constants[item.get('name')] = item.get('value')
+                    
+        for node in self.registry.reg.find('types').findall('type'):
+            if node.get('category') == 'include':
+                self.includes[node.get('name')] = ''.join(node.itertext())
+                
+        
+    def endFile(self):
+        # Find all of the extensions that use the system types
+        self.sysTypes = set()
+        for node in self.registry.reg.find('types').findall('type'):
+            if node.get('category') == None and node.get('requires') in self.includes and node.get('requires') != 'vk_platform':
+                for extension in self.extTypes:
+                    for structName in self.extTypes[extension].vktypes:
+                        for struct in self.structs:
+                            if struct.name == structName:
+                                for member in struct.members:
+                                    if node.get('name') == member.baseType or node.get('name') + '*' == member.baseType:
+                                        sysType = VulkanSystemType(node.get('name'), self.extTypes[structName])
+                                        if sysType not in self.sysTypes:
+                                            self.sysTypes.add(sysType)
+                    for funcName in self.extTypes[extension].vkfuncs:
+                        for func in self.functions:
+                            if func.name == funcName:
+                                for param in func.parameters:
+                                    if node.get('name') == param.baseType or node.get('name') + '*' == param.baseType:
+                                        sysType = VulkanSystemType(node.get('name'), self.extFuncs[funcName])
+                                        if sysType not in self.sysTypes:
+                                            self.sysTypes.add(sysType)
+        
+        # Find every @foreach, @if, and @end
+        forIter = re.finditer('(^\\s*\\@foreach\\s+[a-z]+(\\s+where\\(.*\\))?\\s*^)|(\\@foreach [a-z]+(\\s+where\\(.*\\))?\\b)', self.format, flags=re.MULTILINE)
+        ifIter = re.finditer('(^\\s*\\@if\\(.*\\)\\s*^)|(\\@if\\(.*\\))', self.format, flags=re.MULTILINE)
+        endIter = re.finditer('(^\\s*\\@end\\s+[a-z]+\\s*^)|(\\@end [a-z]+\\b)', self.format, flags=re.MULTILINE)
+        try:
+            nextFor = next(forIter)
+        except StopIteration:
+            nextFor = None
+        try:
+            nextIf = next(ifIter)
+        except StopIteration:
+            nextIf = None
+        try:
+            nextEnd = next(endIter)
+        except StopIteration:
+            nextEnd = None
+        
+        # Match the beginnings to the ends
+        loops = []
+        unassignedControls = []
+        depth = 0
+        while nextFor != None or nextFor != None or nextEnd != None:
+            # If this is a @foreach
+            if nextFor != None and ((nextIf == None or nextFor.start() < nextIf.start()) and nextFor.start() < nextEnd.start()):
+                depth += 1
+                forType = re.search('(?<=\\s)[a-z]+', self.format[nextFor.start():nextFor.end()])
+                text = self.format[forType.start()+nextFor.start():forType.end()+nextFor.start()]
+                whereMatch = re.search('(?<=where\\().*(?=\\))', self.format[nextFor.start():nextFor.end()])
+                condition = None if whereMatch == None else self.format[whereMatch.start()+nextFor.start():whereMatch.end()+nextFor.start()]
+                unassignedControls.append((nextFor.start(), nextFor.end(), text, condition))
+                
+                try:
+                    nextFor = next(forIter)
+                except StopIteration:
+                    nextFor = None
+            
+            # If this is an @if    
+            elif nextIf != None and nextIf.start() < nextEnd.start():
+                depth += 1
+                condMatch = re.search('(?<=if\\().*(?=\\))', self.format[nextIf.start():nextIf.end()])
+                condition = None if condMatch == None else self.format[condMatch.start()+nextIf.start():condMatch.end()+nextIf.start()]
+                unassignedControls.append((nextIf.start(), nextIf.end(), 'if', condition))
+                
+                try:
+                    nextIf = next(ifIter)
+                except StopIteration:
+                    nextIf = None
+                    
+            # Else this is an @end
+            else:
+                depth -= 1
+                endType = re.search('(?<=\\s)[a-z]+', self.format[nextEnd.start():nextEnd.end()])
+                text = self.format[endType.start()+nextEnd.start():endType.end()+nextEnd.start()]
+
+                start = unassignedControls.pop(-1)
+                assert(start[2] == text)
+                
+                item = Control(self.format, start[0:2], (nextEnd.start(), nextEnd.end()), text, start[3])
+                if len(loops) < 1 or depth < loops[-1][0]:
+                    while len(loops) > 0 and depth < loops[-1][0]:
+                        item.children.insert(0, loops.pop(-1)[1])
+                    loops.append((depth, item))
+                else:
+                    loops.append((depth, item))
+
+                try:
+                    nextEnd = next(endIter)
+                except StopIteration:
+                    nextEnd = None                
+        
+        # Expand each loop into its full form
+        lastIndex = 0
+        for _, loop in loops:
+            gen.write(self.format[lastIndex:loop.startPos[0]].format(**{}), file=self.outFile)
+            gen.write(self.expand(loop), file=self.outFile)
+            lastIndex = loop.endPos[1]
+        gen.write(self.format[lastIndex:-1].format(**{}), file=self.outFile)
+        
+        gen.OutputGenerator.endFile(self)
+        
+    def genCmd(self, cmd, name):
+        gen.OutputGenerator.genCmd(self, cmd, name)
+        self.functions.add(VulkanFunction(cmd.elem, self.constants))
+        
+    # These are actually constants
+    def genEnum(self, enuminfo, name):
+        gen.OutputGenerator.genEnum(self, enuminfo, name)
+    
+    # These are actually enums
+    def genGroup(self, groupinfo, groupName):
+        gen.OutputGenerator.genGroup(self, groupinfo, groupName)
+        
+        if groupinfo.elem.get('type') == 'bitmask':
+            self.bitmasks.add(VulkanBitmask(groupinfo.elem, self.extensions))
+        elif groupinfo.elem.get('type') == 'enum':
+            self.enums.add(VulkanEnum(groupinfo.elem, self.extensions))
+
+    def genType(self, typeinfo, name):
+        gen.OutputGenerator.genType(self, typeinfo, name)
+        
+        if typeinfo.elem.get('category') == 'struct':
+            self.structs.add(VulkanStruct(typeinfo.elem, self.constants))
+        elif typeinfo.elem.get('category') == 'basetype':
+            self.basetypes.add(VulkanBasetype(typeinfo.elem))
+        elif typeinfo.elem.get('category') == None and typeinfo.elem.get('requires') == 'vk_platform':
+            self.externalTypes.add(VulkanExternalType(typeinfo.elem))
+        elif typeinfo.elem.get('category') == 'handle':
+            self.handles.add(VulkanHandle(typeinfo.elem))
+        elif typeinfo.elem.get('category') == 'union':
+            self.unions.add(VulkanUnion(typeinfo.elem, self.constants))
+        elif typeinfo.elem.get('category') == 'bitmask':
+            self.flags.add(VulkanFlags(typeinfo.elem))
+        elif typeinfo.elem.get('category') == 'funcpointer':
+            self.funcPointers.add(VulkanFunctionPointer(typeinfo.elem))
+            
+    def expand(self, loop, parents=[]):
+        # Figure out what we're dealing with
+        if loop.text == 'if':
+            subjects = [ Control.IfDummy() ]
+        elif loop.text == 'basetype':
+            subjects = self.basetypes
+        elif loop.text == 'bitmask':
+            subjects = self.bitmasks
+        elif loop.text == 'choice':
+            subjects = self.findByType([VulkanUnion], parents).choices
+        elif loop.text == 'enum':
+            subjects = self.enums
+        elif loop.text == 'extension':
+            subjects = self.extensions
+        elif loop.text == 'flag':
+            subjects = self.flags
+        elif loop.text == 'funcpointer':
+            subjects = self.funcPointers
+        elif loop.text == 'function':
+            subjects = self.functions
+        elif loop.text == 'handle':
+            subjects = self.handles
+        elif loop.text == 'option':
+            subjects = self.findByType([VulkanEnum, VulkanBitmask], parents).options
+        elif loop.text == 'member':
+            subjects = self.findByType([VulkanStruct], parents).members
+        elif loop.text == 'parameter':
+            subjects = self.findByType([VulkanFunction], parents).parameters
+        elif loop.text == 'struct':
+            subjects = self.structs
+        elif loop.text == 'systype':
+            subjects = self.sysTypes
+        elif loop.text == 'type':
+            subjects = self.externalTypes
+        elif loop.text == 'union':
+            subjects = self.unions
+        else:
+            assert(False)
+            
+        # Generate the output string
+        out = ''
+        for item in subjects:
+            
+            # Merge the values and the parent values
+            values = item.values().copy()
+            for parent in parents:
+                values.update(parent.values())
+        
+            # Check if the condition is met
+            if loop.condition != None:
+                cond = eval(loop.condition.format(**values))
+                assert(cond == True or cond == False)
+                if not cond:
+                    continue
+                    
+            # Check if an ifdef is needed
+            if item.name in self.extFuncs:
+                ext = self.extFuncs[item.name]
+            elif item.name in self.extTypes:
+                ext = self.extTypes[item.name]
+            elif item in self.sysTypes:
+                ext = item.ext
+            else:
+                ext = None
+            if ext != None and ext.guard != None:
+                out += '#if defined({})\n'.format(ext.guard)
+                    
+            # Format the string
+            lastIndex = loop.startPos[1]
+            for child in loop.children:
+                out += loop.fullString[lastIndex:child.startPos[0]].format(**values)
+                out += self.expand(child, parents=[item]+parents)
+                lastIndex = child.endPos[1]
+            out += loop.fullString[lastIndex:loop.endPos[0]].format(**values)
+            
+            # Close the ifdef
+            if ext != None and ext.guard != None:
+                out += '#endif // {}\n'.format(ext.guard)
+            
+        return out
+        
+    def findByType(self, types, objects):
+        value = None
+        for item in objects:
+            for ty in types:
+                if isinstance(item, ty):
+                    value = item
+                    break
+        assert(value != None)
+        return value
+            
+class Control:
+    
+    class IfDummy:
+        
+        def __init__(self):
+            self.name = 'ifdummy'
+        
+        def values(self):
+            return {}
+    
+    def __init__(self, fullString, start, end, text, condition):
+        self.fullString = fullString
+        self.startPos = start
+        self.endPos = end
+        self.text = text
+        self.condition = condition
+        self.children = []
+        
+# Base class for VulkanStruct.Member and VulkanStruct.Parameter
+class VulkanVariable:
+    
+    def __init__(self, rootNode, constants):
+        # Set basic properties
+        self.name = rootNode.find('name').text      # Variable name
+        self.typeID = rootNode.find('type').text    # Typename, dereferenced and converted to a useable C++ token
+        self.baseType = self.typeID                     # Type, dereferenced to the non-pointer type
+        self.childType = None                       # Type, dereferenced to the non-pointer type (None if it isn't a pointer)
+        self.arrayLength = None                     # Length of the array, or None if it isn't an array
+        
+        typeMatch = re.search('.+(?=' + self.name + ')', ''.join(rootNode.itertext()))
+        self.type = typeMatch.string[typeMatch.start():typeMatch.end()]
+        self.type = ' '.join(self.type.split())
+        bracketMatch = re.search('(?<=\\[)[a-zA-Z0-9_]+(?=\\])', ''.join(rootNode.itertext()))
+        if bracketMatch != None:
+            matchText = bracketMatch.string[bracketMatch.start():bracketMatch.end()]
+            self.childType = self.type
+            self.type += '[' + matchText + ']'
+            if matchText in constants:
+                self.arrayLength = constants[matchText]
+            else:
+                self.arrayLength = matchText
+        
+        self.lengthMember = False
+        lengthString = rootNode.get('len')
+        lengths = []
+        if lengthString != None:
+            lengths = re.split(',', lengthString)
+            lengths = list(filter(('null-terminated').__ne__, lengths))
+        assert(len(lengths) <= 1)
+        if self.arrayLength == None and len(lengths) > 0:
+            self.childType = '*'.join(self.type.split('*')[0:-1])
+            self.arrayLength = lengths[0]
+            self.lengthMember = True
+        if self.arrayLength != None and self.arrayLength.startswith('latexmath'):
+            code = self.arrayLength[10:len(self.arrayLength)]
+            code = re.sub('\\[\\$', '', code)
+            code = re.sub('\\$\\]', '', code)
+            code = re.sub('\\\\(lceil|rceil)', '', code)
+            code = re.sub('{|}', '', code)
+            code = re.sub('\\\\mathit', '', code)
+            code = re.sub('\\\\over', '/', code)
+            self.arrayLength = code
+            
+        # Dereference if necessary and handle members of variables
+        if self.arrayLength != None:
+            self.arrayLength = re.sub('::', '->', self.arrayLength)
+            sections = self.arrayLength.split('->')
+            if sections[-1][0] == 'p' and sections[0][1].isupper():
+                self.arrayLength = '*' + self.arrayLength
+        
+        self.pointerLevels = len(re.findall('\\*|\\[', ''.join(rootNode.itertext())))
+        if self.typeID == 'char' and self.pointerLevels > 0:
+            self.baseType += '*'
+            self.pointerLevels -= 1
+            self.typeID = 'cstring'
+        elif self.typeID in POINTER_TYPES:
+            self.baseType += '*'
+            self.pointerLevels -= 1
+        assert(self.pointerLevels >= 0)
+        
+class VulkanBasetype:
+    
+    def __init__(self, rootNode):
+        self.name = rootNode.get('name')
+        self.type = rootNode.get('type')
+        
+    def values(self):
+        return {
+            'baseName': self.name,
+            'baseType': self.type,
+        }
+        
+class VulkanBitmask:
+    
+    def __init__(self, rootNode, extensions):
+        self.name = rootNode.get('name')
+        self.type = rootNode.get('type')
+        
+        # Read each value that the enum contains
+        self.options = []
+        for child in rootNode:
+            childName = child.get('name')
+            childValue = child.get('value')
+            childBitpos = child.get('bitpos')
+            childComment = child.get('comment')
+            if childName == None or (childValue == None and childBitpos == None):
+                continue
+                
+            self.options.append(VulkanEnum.Option(childName, childValue, childBitpos, childComment))
+            
+        for ext in extensions:
+            if self.name in ext.enumValues:
+                childName, childValue = ext.enumValues[self.name]
+                self.options.append(VulkanEnum.Option(childName, childValue, None, None))
+            
+    def values(self):
+        return {
+            'bitName': self.name,
+            'bitType': self.type,
+        }
+        
+        
+class VulkanEnum:
+    
+    class Option:
+        
+        def __init__(self, name, value, bitpos, comment):
+            self.name = name
+            self.comment = comment
+            
+            if value == 0 or value == None:
+                value = 1 << int(bitpos)
+            self.value = value
+            
+        def values(self):
+            return {
+                'optName': self.name,
+                'optValue': self.value,
+                'optComment': self.comment,
+            }
+    
+    def __init__(self, rootNode, extensions):
+        self.name = rootNode.get('name')
+        self.type = rootNode.get('type')
+        
+        # Read each value that the enum contains
+        self.options = []
+        for child in rootNode:
+            childName = child.get('name')
+            childValue = child.get('value')
+            childBitpos = child.get('bitpos')
+            childComment = child.get('comment')
+            if childName == None or (childValue == None and childBitpos == None):
+                continue
+                
+            self.options.append(VulkanEnum.Option(childName, childValue, childBitpos, childComment))
+            
+        for ext in extensions:
+            if self.name in ext.enumValues:
+                childName, childValue = ext.enumValues[self.name]
+                self.options.append(VulkanEnum.Option(childName, childValue, None, None))
+        
+    def values(self):
+        return {
+            'enumName': self.name,
+            'enumType': self.type,
+        }
+            
+class VulkanExtension:
+    
+    def __init__(self, rootNode):
+        self.name = rootNode.get('name')
+        self.number = int(rootNode.get('number'))
+        self.type = rootNode.get('type')
+        self.dependency = rootNode.get('requires')
+        self.guard = rootNode.get('protect')
+        self.supported = rootNode.get('supported')
+        
+        self.vktypes = []
+        for ty in rootNode.find('require').findall('type'):
+            self.vktypes.append(ty.get('name'))
+        self.vkfuncs = []
+        for func in rootNode.find('require').findall('command'):
+            self.vkfuncs.append(func.get('name'))
+            
+        self.constants = {}
+        self.enumValues = {}
+        for enum in rootNode.find('require').findall('enum'):
+            base = enum.get('extends')
+            name = enum.get('name')
+            value = enum.get('value')
+            bitpos = enum.get('bitpos')
+            offset = enum.get('offset')
+            
+            if value == None and bitpos != None:
+                value = 1 << int(bitpos)
+            
+            if offset != None:
+                offset = int(offset)
+            if base != None and offset != None:
+                enumValue = 1000000000 + 1000*(self.number - 1) + offset
+                if enum.get('dir') == '-':
+                    enumValue = -enumValue;
+                self.enumValues[base] = (name, enumValue)
+            else:
+                self.constants[name] = value
+        
+    def values(self):
+        return {
+            'extName': self.name,
+            'extNumber': self.number,
+            'extType': self.type,
+            'extDependency': self.dependency,
+            'extGuard': self.guard,
+            'extSupported': self.supported,
+        }
+        
+class VulkanExternalType:
+    
+    def __init__(self, rootNode):
+        self.name = rootNode.get('name')
+        self.dependency = rootNode.get('requires')
+        
+    def values(self):
+        return {
+            'etyName': self.name,
+            'etyDependency': self.dependency,
+        }
+        
+class VulkanFlags:
+    
+    def __init__(self, rootNode):
+        self.name = rootNode.get('name')
+        self.type = rootNode.get('type')
+        self.enum = rootNode.get('requires')
+        
+    def values(self):
+        return {
+            'flagName': self.name,
+            'flagType': self.type,
+            'flagEnum': self.enum,
+        }
+        
+class VulkanFunction:
+    
+    class Parameter(VulkanVariable):
+        
+        def __init__(self, rootNode, constants):
+            VulkanVariable.__init__(self, rootNode, constants)
+            self.text = ''.join(rootNode.itertext())
+
+        def values(self):
+            return {
+                'prmName': self.name,
+                'prmBaseType': self.baseType,
+                'prmTypeID': self.typeID,
+                'prmType': self.type,
+                'prmChildType': self.childType,
+                'prmPtrLevel': self.pointerLevels,
+                'prmLength': self.arrayLength,
+            }
+    
+    def __init__(self, rootNode, constants):
+        self.name = rootNode.find('proto').find('name').text
+        self.returnType = rootNode.find('proto').find('type').text
+
+        self.parameters = []
+        self.namedParams = ''
+        self.typedParams = ''
+        for node in rootNode.findall('param'):
+            self.parameters.append(VulkanFunction.Parameter(node, constants))
+            self.namedParams += self.parameters[-1].name + ', '
+            self.typedParams += self.parameters[-1].text + ', '
+        if len(self.parameters) > 0:
+            self.namedParams = self.namedParams[0:-2]
+            self.typedParams = self.typedParams[0:-2]
+            
+        if self.parameters[0].type in ['VkInstance', 'VkPhysicalDevice'] or self.name == 'vkCreateInstance':
+            self.type = 'instance'
+        else:
+            self.type = 'device' 
+            
+    def values(self):
+        return {
+            'funcName': self.name,
+            'funcShortName': self.name[2:len(self.name)],
+            'funcType': self.type,
+            'funcReturn': self.returnType,
+            'funcNamedParams': self.namedParams,
+            'funcTypedParams': self.typedParams,
+            'funcDispatchParam': self.parameters[0].name
+        }
+        
+class VulkanFunctionPointer:
+    
+    def __init__(self, rootNode):
+        self.name = rootNode.get('name')
+        
+    def values(self):
+        return {
+            'pfnName': self.name,
+        }
+            
+class VulkanHandle:
+    
+    def __init__(self, rootNode):
+        self.name = rootNode.get('name')
+        self.type = rootNode.get('type')
+        self.parent = rootNode.get('parent')
+        
+    def values(self):
+        return {
+            'hdlName': self.name,
+            'hdlType': self.type,
+            'hdlParent': self.parent,
+        }
+            
+class VulkanStruct:
+    
+    class Member(VulkanVariable):
+        
+        def __init__(self, rootNode, constants, parentName):
+            VulkanVariable.__init__(self, rootNode, constants)
+            
+            # Search for a member condition
+            self.condition = None
+            if rootNode.get('noautovalidity') == 'true' and parentName in VALIDITY_CHECKS and self.name in VALIDITY_CHECKS[parentName]:
+                self.condition = VALIDITY_CHECKS[parentName][self.name]
+            
+        def values(self):
+            return {
+                'memName': self.name,
+                'memBaseType': self.baseType,
+                'memTypeID': self.typeID,
+                'memType': self.type,
+                'memChildType': self.childType,
+                'memPtrLevel': self.pointerLevels,
+                'memLength': self.arrayLength,
+                'memLengthIsMember': self.lengthMember,
+                'memCondition': self.condition,
+            }
+            
+    
+    def __init__(self, rootNode, constants):
+        self.name = rootNode.get('name')
+        self.members = []
+        for node in rootNode.findall('member'):
+            self.members.append(VulkanStruct.Member(node, constants, self.name))
+            
+    def values(self):
+        return {
+            'sctName': self.name,
+        }
+            
+class VulkanSystemType:
+    
+    def __init__(self, name, ext):
+        self.name = name
+        self.type = self.name if name not in POINTER_TYPES else self.name + '*'
+        self.ext = ext
+        
+    def __eq__(self, that):
+        return self.name == that.name and self.type == that.type
+        
+    def __hash__(self):
+        return hash(self.name) | hash(self.type)
+        
+    def values(self):
+        return {
+            'sysName': self.name,
+            'sysType': self.type,
+        }
+            
+class VulkanUnion:
+    
+    class Choice(VulkanVariable):
+        
+        def __init__(self, rootNode, constants):
+            VulkanVariable.__init__(self, rootNode, constants)
+            
+        def values(self):
+            return {
+                'chcName': self.name,
+                'chcBaseType': self.baseType,
+                'chcTypeID': self.typeID,
+                'chcType': self.type,
+                'chcChildType': self.childType,
+                'chcPtrLevel': self.pointerLevels,
+                'chcLength': self.arrayLength,
+                #'chcLengthIsMember': self.lengthMember,
+            }
+    
+    def __init__(self, rootNode, constants):
+        self.name = rootNode.get('name')
+        self.choices = []
+        for node in rootNode.findall('member'):
+            self.choices.append(VulkanUnion.Choice(node, constants))
+        
+    def values(self):
+        return {
+            'unName': self.name,
+        }
+            
\ No newline at end of file
diff --git a/scripts/vt_genvk.py b/scripts/vt_genvk.py
new file mode 100644
index 0000000..5275e84
--- /dev/null
+++ b/scripts/vt_genvk.py
@@ -0,0 +1,281 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2013-2016 The Khronos Group Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse, cProfile, pdb, string, sys, time
+from reg import *
+from generator import write
+
+#
+# VulkanTools Generator Additions
+from api_dump_generator import ApiDumpGeneratorOptions, ApiDumpOutputGenerator, COMMON_CODEGEN, TEXT_CODEGEN
+from dispatch_table_generator import DispatchTableOutputGenerator, DispatchTableOutputGeneratorOptions
+
+# Simple timer functions
+startTime = None
+
+def startTimer(timeit):
+    global startTime
+    startTime = time.clock()
+
+def endTimer(timeit, msg):
+    global startTime
+    endTime = time.clock()
+    if (timeit):
+        write(msg, endTime - startTime, file=sys.stderr)
+        startTime = None
+
+# Turn a list of strings into a regexp string matching exactly those strings
+def makeREstring(list):
+    return '^(' + '|'.join(list) + ')$'
+
+# Returns a directory of [ generator function, generator options ] indexed
+# by specified short names. The generator options incorporate the following
+# parameters:
+#
+# extensions - list of extension names to include.
+# protect - True if re-inclusion protection should be added to headers
+# directory - path to directory in which to generate the target(s)
+def makeGenOpts(extensions = [], protect = True, directory = '.'):
+    global genOpts
+    genOpts = {}
+
+    # Descriptive names for various regexp patterns used to select
+    # versions and extensions
+    allVersions     = allExtensions = '.*'
+    noVersions      = noExtensions = None
+
+    addExtensions     = makeREstring(extensions)
+    removeExtensions  = makeREstring([])
+
+    # Copyright text prefixing all headers (list of strings).
+    prefixStrings = [
+        '/*',
+        '** Copyright (c) 2015-2016 The Khronos Group Inc.',
+        '**',
+        '** Licensed under the Apache License, Version 2.0 (the "License");',
+        '** you may not use this file except in compliance with the License.',
+        '** You may obtain a copy of the License at',
+        '**',
+        '**     http://www.apache.org/licenses/LICENSE-2.0',
+        '**',
+        '** Unless required by applicable law or agreed to in writing, software',
+        '** distributed under the License is distributed on an "AS IS" BASIS,',
+        '** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
+        '** See the License for the specific language governing permissions and',
+        '** limitations under the License.',
+        '*/',
+        ''
+    ]
+
+    # Text specific to Vulkan headers
+    vkPrefixStrings = [
+        '/*',
+        '** This header is generated from the Khronos Vulkan XML API Registry.',
+        '**',
+        '*/',
+        ''
+    ]
+
+    # Defaults for generating re-inclusion protection wrappers (or not)
+    protectFile = protect
+    protectFeature = protect
+    protectProto = protect
+
+    #
+    # VulkanTools Generators
+    
+    # Options for api dump layer    
+    genOpts['api_dump.cpp'] = [
+        ApiDumpOutputGenerator,
+        ApiDumpGeneratorOptions(
+            input             = COMMON_CODEGEN,
+            filename          = 'api_dump.cpp',
+            apiname           = 'vulkan',
+            profile           = None,
+            versions          = allVersions,
+            emitversions      = allVersions,
+            defaultExtensions = 'vulkan',
+            addExtensions     = None,
+            removeExtensions  = None,
+            prefixText        = prefixStrings + vkPrefixStrings,
+            genFuncPointers   = True,
+            protectFile       = protectFile,
+            protectFeature    = False,
+            protectProto      = None,
+            protectProtoStr   = 'VK_NO_PROTOTYPES',
+            apicall           = 'VKAPI_ATTR ',
+            apientry          = 'VKAPI_CALL ',
+            apientryp         = 'VKAPI_PTR *',
+            alignFuncParam    = 48)
+        ]
+    genOpts['api_dump_text.h'] = [
+        ApiDumpOutputGenerator,
+        ApiDumpGeneratorOptions(
+            input             = TEXT_CODEGEN,
+            filename          = 'api_dump_text.h',
+            apiname           = 'vulkan',
+            profile           = None,
+            versions          = allVersions,
+            emitversions      = allVersions,
+            defaultExtensions = 'vulkan',
+            addExtensions     = None,
+            removeExtensions  = None,
+            prefixText        = prefixStrings + vkPrefixStrings,
+            genFuncPointers   = True,
+            protectFile       = protectFile,
+            protectFeature    = False,
+            protectProto      = None,
+            protectProtoStr   = 'VK_NO_PROTOTYPES',
+            apicall           = 'VKAPI_ATTR ',
+            apientry          = 'VKAPI_CALL ',
+            apientryp         = 'VKAPI_PTR *',
+            alignFuncParam    = 48)
+    ]
+
+    # Options for dispatch table helper generator
+    genOpts['vk_dispatch_table_helper.h'] = [
+          DispatchTableOutputGenerator,
+          DispatchTableOutputGeneratorOptions(
+            filename          = 'vk_dispatch_table_helper.h',
+            directory         = directory,
+            apiname           = 'vulkan',
+            profile           = None,
+            versions          = allVersions,
+            emitversions      = allVersions,
+            defaultExtensions = 'vulkan',
+            addExtensions     = addExtensions,
+            removeExtensions  = removeExtensions,
+            prefixText        = prefixStrings + vkPrefixStrings,
+            protectFeature    = False,
+            apicall           = 'VKAPI_ATTR ',
+            apientry          = 'VKAPI_CALL ',
+            apientryp         = 'VKAPI_PTR *',
+            alignFuncParam    = 48)
+        ]
+
+# Generate a target based on the options in the matching genOpts{} object.
+# This is encapsulated in a function so it can be profiled and/or timed.
+# The args parameter is an parsed argument object containing the following
+# fields that are used:
+#   target - target to generate
+#   directory - directory to generate it in
+#   protect - True if re-inclusion wrappers should be created
+#   extensions - list of additional extensions to include in generated
+#   interfaces
+def genTarget(args):
+    global genOpts
+
+    # Create generator options with specified parameters
+    makeGenOpts(extensions = args.extension,
+                protect = args.protect,
+                directory = args.directory)
+
+    if (args.target in genOpts.keys()):
+        createGenerator = genOpts[args.target][0]
+        options = genOpts[args.target][1]
+
+        write('* Building', options.filename, file=sys.stderr)
+
+        startTimer(args.time)
+        gen = createGenerator(errFile=errWarn,
+                              warnFile=errWarn,
+                              diagFile=diag)
+        reg.setGenerator(gen)
+        reg.apiGen(options)
+        write('* Generated', options.filename, file=sys.stderr)
+        endTimer(args.time, '* Time to generate ' + options.filename + ' =')
+    else:
+        write('No generator options for unknown target:',
+              args.target, file=sys.stderr)
+
+# -extension name - may be a single extension name, a a space-separated list
+# of names, or a regular expression.
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('-extension', action='append',
+                        default=[],
+                        help='Specify an extension or extensions to add to targets')
+    parser.add_argument('-debug', action='store_true',
+                        help='Enable debugging')
+    parser.add_argument('-dump', action='store_true',
+                        help='Enable dump to stderr')
+    parser.add_argument('-diagfile', action='store',
+                        default=None,
+                        help='Write diagnostics to specified file')
+    parser.add_argument('-errfile', action='store',
+                        default=None,
+                        help='Write errors and warnings to specified file instead of stderr')
+    parser.add_argument('-noprotect', dest='protect', action='store_false',
+                        help='Disable inclusion protection in output headers')
+    parser.add_argument('-profile', action='store_true',
+                        help='Enable profiling')
+    parser.add_argument('-registry', action='store',
+                        default='vk.xml',
+                        help='Use specified registry file instead of vk.xml')
+    parser.add_argument('-time', action='store_true',
+                        help='Enable timing')
+    parser.add_argument('-validate', action='store_true',
+                        help='Enable group validation')
+    parser.add_argument('-o', action='store', dest='directory',
+                        default='.',
+                        help='Create target and related files in specified directory')
+    parser.add_argument('target', metavar='target', nargs='?',
+                        help='Specify target')
+
+    args = parser.parse_args()
+
+    # This splits arguments which are space-separated lists
+    args.extension = [name for arg in args.extension for name in arg.split()]
+
+    # Load & parse registry
+    reg = Registry()
+
+    startTimer(args.time)
+    tree = etree.parse(args.registry)
+    endTimer(args.time, '* Time to make ElementTree =')
+
+    startTimer(args.time)
+    reg.loadElementTree(tree)
+    endTimer(args.time, '* Time to parse ElementTree =')
+
+    if (args.validate):
+        reg.validateGroups()
+
+    if (args.dump):
+        write('* Dumping registry to regdump.txt', file=sys.stderr)
+        reg.dumpReg(filehandle = open('regdump.txt','w'))
+
+    # create error/warning & diagnostic files
+    if (args.errfile):
+        errWarn = open(args.errfile, 'w')
+    else:
+        errWarn = sys.stderr
+
+    if (args.diagfile):
+        diag = open(args.diagfile, 'w')
+    else:
+        diag = None
+
+    if (args.debug):
+        pdb.run('genTarget(args)')
+    elif (args.profile):
+        import cProfile, pstats
+        cProfile.run('genTarget(args)', 'profile.txt')
+        p = pstats.Stats('profile.txt')
+        p.strip_dirs().sort_stats('time').print_stats(50)
+    else:
+        genTarget(args)
\ No newline at end of file
diff --git a/scripts/vulkan.py b/scripts/vulkan.py
new file mode 100644
index 0000000..65728c7
--- /dev/null
+++ b/scripts/vulkan.py
@@ -0,0 +1,1536 @@
+" ""VK API description"""
+
+# Copyright (c) 2015-2016 The Khronos Group Inc.
+# Copyright (c) 2015-2016 Valve Corporation
+# Copyright (c) 2015-2016 LunarG, Inc.
+# Copyright (c) 2015-2016 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Chia-I Wu <olv@lunarg.com>
+# Author: Jon Ashburn <jon@lunarg.com>
+# Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+# Author: Tobin Ehlis <tobin@lunarg.com>
+# Author: Tony Barbour <tony@LunarG.com>
+# Author: Gwan-gyeong Mun <kk.moon@samsung.com>
+
+class Param(object):
+    """A function parameter."""
+
+    def __init__(self, ty, name):
+        self.ty = ty
+        self.name = name
+
+    def c(self):
+        """Return the parameter in C."""
+        idx = self.ty.find("[")
+
+        # arrays have a different syntax
+        if idx >= 0:
+            return "%s %s%s" % (self.ty[:idx], self.name, self.ty[idx:])
+        else:
+            return "%s %s" % (self.ty, self.name)
+
+    def indirection_level(self):
+        """Return the level of indirection."""
+        return self.ty.count("*") + self.ty.count("[")
+
+    def dereferenced_type(self, level=0):
+        """Return the type after dereferencing."""
+        if not level:
+            level = self.indirection_level()
+
+        deref = self.ty if level else ""
+        while level > 0:
+            idx = deref.rfind("[")
+            if idx < 0:
+                idx = deref.rfind("*")
+            if idx < 0:
+                deref = ""
+                break
+            deref = deref[:idx]
+            level -= 1;
+
+        return deref.rstrip()
+
+class Proto(object):
+    """A function prototype."""
+
+    def __init__(self, ret, name, params=[]):
+        # the proto has only a param
+        if not isinstance(params, list):
+            params = [params]
+
+        self.ret = ret
+        self.name = name
+        self.params = params
+
+    def c_params(self, need_type=True, need_name=True):
+        """Return the parameter list in C."""
+        if self.params and (need_type or need_name):
+            if need_type and need_name:
+                return ", ".join([param.c() for param in self.params])
+            elif need_type:
+                return ", ".join([param.ty for param in self.params])
+            else:
+                return ", ".join([param.name for param in self.params])
+        else:
+            return "void" if need_type else ""
+
+    def c_decl(self, name, attr="", typed=False, need_param_names=True):
+        """Return a named declaration in C."""
+        if typed:
+            return "%s (%s*%s)(%s)" % (
+                self.ret,
+                attr + "_PTR " if attr else "",
+                name,
+                self.c_params(need_name=need_param_names))
+        else:
+            return "%s%s %s%s(%s)" % (
+                attr + "_ATTR " if attr else "",
+                self.ret,
+                attr + "_CALL " if attr else "",
+                name,
+                self.c_params(need_name=need_param_names))
+
+    def c_pretty_decl(self, name, attr=""):
+        """Return a named declaration in C, with vulkan.h formatting."""
+        plist = []
+        for param in self.params:
+            idx = param.ty.find("[")
+            if idx < 0:
+                idx = len(param.ty)
+
+            pad = 44 - idx
+            if pad <= 0:
+                pad = 1
+
+            plist.append("    %s%s%s%s" % (param.ty[:idx],
+                " " * pad, param.name, param.ty[idx:]))
+
+        return "%s%s %s%s(\n%s)" % (
+                attr + "_ATTR " if attr else "",
+                self.ret,
+                attr + "_CALL " if attr else "",
+                name,
+                ",\n".join(plist))
+
+    def c_func(self, prefix="", attr=""):
+        """Return the prototype in C."""
+        return self.c_decl(prefix + self.name, attr=attr, typed=False)
+
+    def c_call(self):
+        """Return a call to the prototype in C."""
+        return "%s(%s)" % (self.name, self.c_params(need_type=False))
+
+    def object_in_params(self):
+        """Return the params that are simple VK objects and are inputs."""
+        return [param for param in self.params if param.ty in objects]
+
+    def object_out_params(self):
+        """Return the params that are simple VK objects and are outputs."""
+        return [param for param in self.params
+                if param.dereferenced_type() in objects]
+
+    def __repr__(self):
+        param_strs = []
+        for param in self.params:
+            param_strs.append(str(param))
+        param_str = "    [%s]" % (",\n     ".join(param_strs))
+
+        return "Proto(\"%s\", \"%s\",\n%s)" % \
+                (self.ret, self.name, param_str)
+
+class Extension(object):
+    def __init__(self, name, headers, objects, protos, ifdef = None):
+        self.name = name
+        self.headers = headers
+        self.objects = objects
+        self.protos = protos
+        self.ifdef = ifdef
+
+# VK core API
+VK_VERSION_1_0 = Extension(
+    name="VK_VERSION_1_0",
+    headers=["vulkan/vulkan.h"],
+    objects=[
+        "VkInstance",
+        "VkPhysicalDevice",
+        "VkDevice",
+        "VkQueue",
+        "VkSemaphore",
+        "VkCommandBuffer",
+        "VkFence",
+        "VkDeviceMemory",
+        "VkBuffer",
+        "VkImage",
+        "VkEvent",
+        "VkQueryPool",
+        "VkBufferView",
+        "VkImageView",
+        "VkShaderModule",
+        "VkPipelineCache",
+        "VkPipelineLayout",
+        "VkRenderPass",
+        "VkPipeline",
+        "VkDescriptorSetLayout",
+        "VkSampler",
+        "VkDescriptorPool",
+        "VkDescriptorSet",
+        "VkFramebuffer",
+        "VkCommandPool",
+    ],
+    protos=[
+        Proto("VkResult", "CreateInstance",
+            [Param("const VkInstanceCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkInstance*", "pInstance")]),
+
+        Proto("void", "DestroyInstance",
+            [Param("VkInstance", "instance"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "EnumeratePhysicalDevices",
+            [Param("VkInstance", "instance"),
+             Param("uint32_t*", "pPhysicalDeviceCount"),
+             Param("VkPhysicalDevice*", "pPhysicalDevices")]),
+
+        Proto("void", "GetPhysicalDeviceFeatures",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkPhysicalDeviceFeatures*", "pFeatures")]),
+
+        Proto("void", "GetPhysicalDeviceFormatProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkFormat", "format"),
+             Param("VkFormatProperties*", "pFormatProperties")]),
+
+        Proto("VkResult", "GetPhysicalDeviceImageFormatProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkFormat", "format"),
+             Param("VkImageType", "type"),
+             Param("VkImageTiling", "tiling"),
+             Param("VkImageUsageFlags", "usage"),
+             Param("VkImageCreateFlags", "flags"),
+             Param("VkImageFormatProperties*", "pImageFormatProperties")]),
+
+        Proto("void", "GetPhysicalDeviceProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkPhysicalDeviceProperties*", "pProperties")]),
+
+        Proto("void", "GetPhysicalDeviceQueueFamilyProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t*", "pQueueFamilyPropertyCount"),
+             Param("VkQueueFamilyProperties*", "pQueueFamilyProperties")]),
+
+        Proto("void", "GetPhysicalDeviceMemoryProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkPhysicalDeviceMemoryProperties*", "pMemoryProperties")]),
+
+        Proto("PFN_vkVoidFunction", "GetInstanceProcAddr",
+            [Param("VkInstance", "instance"),
+             Param("const char*", "pName")]),
+
+        Proto("PFN_vkVoidFunction", "GetDeviceProcAddr",
+            [Param("VkDevice", "device"),
+             Param("const char*", "pName")]),
+
+        Proto("VkResult", "CreateDevice",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("const VkDeviceCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkDevice*", "pDevice")]),
+
+        Proto("void", "DestroyDevice",
+            [Param("VkDevice", "device"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "EnumerateInstanceExtensionProperties",
+            [Param("const char*", "pLayerName"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkExtensionProperties*", "pProperties")]),
+
+        Proto("VkResult", "EnumerateDeviceExtensionProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("const char*", "pLayerName"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkExtensionProperties*", "pProperties")]),
+
+        Proto("VkResult", "EnumerateInstanceLayerProperties",
+            [Param("uint32_t*", "pPropertyCount"),
+             Param("VkLayerProperties*", "pProperties")]),
+
+        Proto("VkResult", "EnumerateDeviceLayerProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkLayerProperties*", "pProperties")]),
+
+        Proto("void", "GetDeviceQueue",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "queueFamilyIndex"),
+             Param("uint32_t", "queueIndex"),
+             Param("VkQueue*", "pQueue")]),
+
+        Proto("VkResult", "QueueSubmit",
+            [Param("VkQueue", "queue"),
+             Param("uint32_t", "submitCount"),
+             Param("const VkSubmitInfo*", "pSubmits"),
+             Param("VkFence", "fence")]),
+
+        Proto("VkResult", "QueueWaitIdle",
+            [Param("VkQueue", "queue")]),
+
+        Proto("VkResult", "DeviceWaitIdle",
+            [Param("VkDevice", "device")]),
+
+        Proto("VkResult", "AllocateMemory",
+            [Param("VkDevice", "device"),
+             Param("const VkMemoryAllocateInfo*", "pAllocateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkDeviceMemory*", "pMemory")]),
+
+        Proto("void", "FreeMemory",
+            [Param("VkDevice", "device"),
+             Param("VkDeviceMemory", "memory"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "MapMemory",
+            [Param("VkDevice", "device"),
+             Param("VkDeviceMemory", "memory"),
+             Param("VkDeviceSize", "offset"),
+             Param("VkDeviceSize", "size"),
+             Param("VkMemoryMapFlags", "flags"),
+             Param("void**", "ppData")]),
+
+        Proto("void", "UnmapMemory",
+            [Param("VkDevice", "device"),
+             Param("VkDeviceMemory", "memory")]),
+
+        Proto("VkResult", "FlushMappedMemoryRanges",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "memoryRangeCount"),
+             Param("const VkMappedMemoryRange*", "pMemoryRanges")]),
+
+        Proto("VkResult", "InvalidateMappedMemoryRanges",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "memoryRangeCount"),
+             Param("const VkMappedMemoryRange*", "pMemoryRanges")]),
+
+        Proto("void", "GetDeviceMemoryCommitment",
+            [Param("VkDevice", "device"),
+             Param("VkDeviceMemory", "memory"),
+             Param("VkDeviceSize*", "pCommittedMemoryInBytes")]),
+
+        Proto("VkResult", "BindBufferMemory",
+            [Param("VkDevice", "device"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceMemory", "memory"),
+             Param("VkDeviceSize", "memoryOffset")]),
+
+        Proto("VkResult", "BindImageMemory",
+            [Param("VkDevice", "device"),
+             Param("VkImage", "image"),
+             Param("VkDeviceMemory", "memory"),
+             Param("VkDeviceSize", "memoryOffset")]),
+
+        Proto("void", "GetBufferMemoryRequirements",
+            [Param("VkDevice", "device"),
+             Param("VkBuffer", "buffer"),
+             Param("VkMemoryRequirements*", "pMemoryRequirements")]),
+
+        Proto("void", "GetImageMemoryRequirements",
+            [Param("VkDevice", "device"),
+             Param("VkImage", "image"),
+             Param("VkMemoryRequirements*", "pMemoryRequirements")]),
+
+        Proto("void", "GetImageSparseMemoryRequirements",
+            [Param("VkDevice", "device"),
+             Param("VkImage", "image"),
+             Param("uint32_t*", "pSparseMemoryRequirementCount"),
+             Param("VkSparseImageMemoryRequirements*", "pSparseMemoryRequirements")]),
+
+        Proto("void", "GetPhysicalDeviceSparseImageFormatProperties",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkFormat", "format"),
+             Param("VkImageType", "type"),
+             Param("VkSampleCountFlagBits", "samples"),
+             Param("VkImageUsageFlags", "usage"),
+             Param("VkImageTiling", "tiling"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkSparseImageFormatProperties*", "pProperties")]),
+
+        Proto("VkResult", "QueueBindSparse",
+            [Param("VkQueue", "queue"),
+             Param("uint32_t", "bindInfoCount"),
+             Param("const VkBindSparseInfo*", "pBindInfo"),
+             Param("VkFence", "fence")]),
+
+        Proto("VkResult", "CreateFence",
+            [Param("VkDevice", "device"),
+             Param("const VkFenceCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkFence*", "pFence")]),
+
+        Proto("void", "DestroyFence",
+            [Param("VkDevice", "device"),
+             Param("VkFence", "fence"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "ResetFences",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "fenceCount"),
+             Param("const VkFence*", "pFences")]),
+
+        Proto("VkResult", "GetFenceStatus",
+            [Param("VkDevice", "device"),
+             Param("VkFence", "fence")]),
+
+        Proto("VkResult", "WaitForFences",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "fenceCount"),
+             Param("const VkFence*", "pFences"),
+             Param("VkBool32", "waitAll"),
+             Param("uint64_t", "timeout")]),
+
+        Proto("VkResult", "CreateSemaphore",
+            [Param("VkDevice", "device"),
+             Param("const VkSemaphoreCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSemaphore*", "pSemaphore")]),
+
+        Proto("void", "DestroySemaphore",
+            [Param("VkDevice", "device"),
+             Param("VkSemaphore", "semaphore"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateEvent",
+            [Param("VkDevice", "device"),
+             Param("const VkEventCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkEvent*", "pEvent")]),
+
+        Proto("void", "DestroyEvent",
+            [Param("VkDevice", "device"),
+             Param("VkEvent", "event"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "GetEventStatus",
+            [Param("VkDevice", "device"),
+             Param("VkEvent", "event")]),
+
+        Proto("VkResult", "SetEvent",
+            [Param("VkDevice", "device"),
+             Param("VkEvent", "event")]),
+
+        Proto("VkResult", "ResetEvent",
+            [Param("VkDevice", "device"),
+             Param("VkEvent", "event")]),
+
+        Proto("VkResult", "CreateQueryPool",
+            [Param("VkDevice", "device"),
+             Param("const VkQueryPoolCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkQueryPool*", "pQueryPool")]),
+
+        Proto("void", "DestroyQueryPool",
+            [Param("VkDevice", "device"),
+             Param("VkQueryPool", "queryPool"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "GetQueryPoolResults",
+            [Param("VkDevice", "device"),
+             Param("VkQueryPool", "queryPool"),
+             Param("uint32_t", "firstQuery"),
+             Param("uint32_t", "queryCount"),
+             Param("size_t", "dataSize"),
+             Param("void*", "pData"),
+             Param("VkDeviceSize", "stride"),
+             Param("VkQueryResultFlags", "flags")]),
+
+        Proto("VkResult", "CreateBuffer",
+            [Param("VkDevice", "device"),
+             Param("const VkBufferCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkBuffer*", "pBuffer")]),
+
+        Proto("void", "DestroyBuffer",
+            [Param("VkDevice", "device"),
+             Param("VkBuffer", "buffer"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateBufferView",
+            [Param("VkDevice", "device"),
+             Param("const VkBufferViewCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkBufferView*", "pView")]),
+
+        Proto("void", "DestroyBufferView",
+            [Param("VkDevice", "device"),
+             Param("VkBufferView", "bufferView"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateImage",
+            [Param("VkDevice", "device"),
+             Param("const VkImageCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkImage*", "pImage")]),
+
+        Proto("void", "DestroyImage",
+            [Param("VkDevice", "device"),
+             Param("VkImage", "image"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("void", "GetImageSubresourceLayout",
+            [Param("VkDevice", "device"),
+             Param("VkImage", "image"),
+             Param("const VkImageSubresource*", "pSubresource"),
+             Param("VkSubresourceLayout*", "pLayout")]),
+
+        Proto("VkResult", "CreateImageView",
+            [Param("VkDevice", "device"),
+             Param("const VkImageViewCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkImageView*", "pView")]),
+
+        Proto("void", "DestroyImageView",
+            [Param("VkDevice", "device"),
+             Param("VkImageView", "imageView"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateShaderModule",
+            [Param("VkDevice", "device"),
+             Param("const VkShaderModuleCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkShaderModule*", "pShaderModule")]),
+
+        Proto("void", "DestroyShaderModule",
+            [Param("VkDevice", "device"),
+             Param("VkShaderModule", "shaderModule"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreatePipelineCache",
+            [Param("VkDevice", "device"),
+             Param("const VkPipelineCacheCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkPipelineCache*", "pPipelineCache")]),
+
+        Proto("void", "DestroyPipelineCache",
+            [Param("VkDevice", "device"),
+             Param("VkPipelineCache", "pipelineCache"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "GetPipelineCacheData",
+            [Param("VkDevice", "device"),
+             Param("VkPipelineCache", "pipelineCache"),
+             Param("size_t*", "pDataSize"),
+             Param("void*", "pData")]),
+
+        Proto("VkResult", "MergePipelineCaches",
+            [Param("VkDevice", "device"),
+             Param("VkPipelineCache", "dstCache"),
+             Param("uint32_t", "srcCacheCount"),
+             Param("const VkPipelineCache*", "pSrcCaches")]),
+
+        Proto("VkResult", "CreateGraphicsPipelines",
+            [Param("VkDevice", "device"),
+             Param("VkPipelineCache", "pipelineCache"),
+             Param("uint32_t", "createInfoCount"),
+             Param("const VkGraphicsPipelineCreateInfo*", "pCreateInfos"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkPipeline*", "pPipelines")]),
+
+        Proto("VkResult", "CreateComputePipelines",
+            [Param("VkDevice", "device"),
+             Param("VkPipelineCache", "pipelineCache"),
+             Param("uint32_t", "createInfoCount"),
+             Param("const VkComputePipelineCreateInfo*", "pCreateInfos"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkPipeline*", "pPipelines")]),
+
+        Proto("void", "DestroyPipeline",
+            [Param("VkDevice", "device"),
+             Param("VkPipeline", "pipeline"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreatePipelineLayout",
+            [Param("VkDevice", "device"),
+             Param("const VkPipelineLayoutCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkPipelineLayout*", "pPipelineLayout")]),
+
+        Proto("void", "DestroyPipelineLayout",
+            [Param("VkDevice", "device"),
+             Param("VkPipelineLayout", "pipelineLayout"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateSampler",
+            [Param("VkDevice", "device"),
+             Param("const VkSamplerCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSampler*", "pSampler")]),
+
+        Proto("void", "DestroySampler",
+            [Param("VkDevice", "device"),
+             Param("VkSampler", "sampler"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateDescriptorSetLayout",
+            [Param("VkDevice", "device"),
+             Param("const VkDescriptorSetLayoutCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkDescriptorSetLayout*", "pSetLayout")]),
+
+        Proto("void", "DestroyDescriptorSetLayout",
+            [Param("VkDevice", "device"),
+             Param("VkDescriptorSetLayout", "descriptorSetLayout"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateDescriptorPool",
+            [Param("VkDevice", "device"),
+             Param("const VkDescriptorPoolCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkDescriptorPool*", "pDescriptorPool")]),
+
+        Proto("void", "DestroyDescriptorPool",
+            [Param("VkDevice", "device"),
+             Param("VkDescriptorPool", "descriptorPool"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "ResetDescriptorPool",
+            [Param("VkDevice", "device"),
+             Param("VkDescriptorPool", "descriptorPool"),
+             Param("VkDescriptorPoolResetFlags", "flags")]),
+
+        Proto("VkResult", "AllocateDescriptorSets",
+            [Param("VkDevice", "device"),
+             Param("const VkDescriptorSetAllocateInfo*", "pAllocateInfo"),
+             Param("VkDescriptorSet*", "pDescriptorSets")]),
+
+        Proto("VkResult", "FreeDescriptorSets",
+            [Param("VkDevice", "device"),
+             Param("VkDescriptorPool", "descriptorPool"),
+             Param("uint32_t", "descriptorSetCount"),
+             Param("const VkDescriptorSet*", "pDescriptorSets")]),
+
+        Proto("void", "UpdateDescriptorSets",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "descriptorWriteCount"),
+             Param("const VkWriteDescriptorSet*", "pDescriptorWrites"),
+             Param("uint32_t", "descriptorCopyCount"),
+             Param("const VkCopyDescriptorSet*", "pDescriptorCopies")]),
+
+        Proto("VkResult", "CreateFramebuffer",
+            [Param("VkDevice", "device"),
+             Param("const VkFramebufferCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkFramebuffer*", "pFramebuffer")]),
+
+        Proto("void", "DestroyFramebuffer",
+            [Param("VkDevice", "device"),
+             Param("VkFramebuffer", "framebuffer"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateRenderPass",
+            [Param("VkDevice", "device"),
+             Param("const VkRenderPassCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkRenderPass*", "pRenderPass")]),
+
+        Proto("void", "DestroyRenderPass",
+            [Param("VkDevice", "device"),
+             Param("VkRenderPass", "renderPass"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("void", "GetRenderAreaGranularity",
+            [Param("VkDevice", "device"),
+             Param("VkRenderPass", "renderPass"),
+             Param("VkExtent2D*", "pGranularity")]),
+
+        Proto("VkResult", "CreateCommandPool",
+            [Param("VkDevice", "device"),
+             Param("const VkCommandPoolCreateInfo*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkCommandPool*", "pCommandPool")]),
+
+        Proto("void", "DestroyCommandPool",
+            [Param("VkDevice", "device"),
+             Param("VkCommandPool", "commandPool"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "ResetCommandPool",
+            [Param("VkDevice", "device"),
+             Param("VkCommandPool", "commandPool"),
+             Param("VkCommandPoolResetFlags", "flags")]),
+
+        Proto("VkResult", "AllocateCommandBuffers",
+            [Param("VkDevice", "device"),
+             Param("const VkCommandBufferAllocateInfo*", "pAllocateInfo"),
+             Param("VkCommandBuffer*", "pCommandBuffers")]),
+
+        Proto("void", "FreeCommandBuffers",
+            [Param("VkDevice", "device"),
+             Param("VkCommandPool", "commandPool"),
+             Param("uint32_t", "commandBufferCount"),
+             Param("const VkCommandBuffer*", "pCommandBuffers")]),
+
+        Proto("VkResult", "BeginCommandBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("const VkCommandBufferBeginInfo*", "pBeginInfo")]),
+
+        Proto("VkResult", "EndCommandBuffer",
+            [Param("VkCommandBuffer", "commandBuffer")]),
+
+        Proto("VkResult", "ResetCommandBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkCommandBufferResetFlags", "flags")]),
+
+        Proto("void", "CmdBindPipeline",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkPipelineBindPoint", "pipelineBindPoint"),
+             Param("VkPipeline", "pipeline")]),
+
+        Proto("void", "CmdSetViewport",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "firstViewport"),
+             Param("uint32_t", "viewportCount"),
+             Param("const VkViewport*", "pViewports")]),
+
+        Proto("void", "CmdSetScissor",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "firstScissor"),
+             Param("uint32_t", "scissorCount"),
+             Param("const VkRect2D*", "pScissors")]),
+
+        Proto("void", "CmdSetLineWidth",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("float", "lineWidth")]),
+
+        Proto("void", "CmdSetDepthBias",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("float", "depthBiasConstantFactor"),
+             Param("float", "depthBiasClamp"),
+             Param("float", "depthBiasSlopeFactor")]),
+
+        Proto("void", "CmdSetBlendConstants",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("const float[4]", "blendConstants")]),
+
+        Proto("void", "CmdSetDepthBounds",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("float", "minDepthBounds"),
+             Param("float", "maxDepthBounds")]),
+
+        Proto("void", "CmdSetStencilCompareMask",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkStencilFaceFlags", "faceMask"),
+             Param("uint32_t", "compareMask")]),
+
+        Proto("void", "CmdSetStencilWriteMask",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkStencilFaceFlags", "faceMask"),
+             Param("uint32_t", "writeMask")]),
+
+        Proto("void", "CmdSetStencilReference",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkStencilFaceFlags", "faceMask"),
+             Param("uint32_t", "reference")]),
+
+        Proto("void", "CmdBindDescriptorSets",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkPipelineBindPoint", "pipelineBindPoint"),
+             Param("VkPipelineLayout", "layout"),
+             Param("uint32_t", "firstSet"),
+             Param("uint32_t", "descriptorSetCount"),
+             Param("const VkDescriptorSet*", "pDescriptorSets"),
+             Param("uint32_t", "dynamicOffsetCount"),
+             Param("const uint32_t*", "pDynamicOffsets")]),
+
+        Proto("void", "CmdBindIndexBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceSize", "offset"),
+             Param("VkIndexType", "indexType")]),
+
+        Proto("void", "CmdBindVertexBuffers",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "firstBinding"),
+             Param("uint32_t", "bindingCount"),
+             Param("const VkBuffer*", "pBuffers"),
+             Param("const VkDeviceSize*", "pOffsets")]),
+
+        Proto("void", "CmdDraw",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "vertexCount"),
+             Param("uint32_t", "instanceCount"),
+             Param("uint32_t", "firstVertex"),
+             Param("uint32_t", "firstInstance")]),
+
+        Proto("void", "CmdDrawIndexed",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "indexCount"),
+             Param("uint32_t", "instanceCount"),
+             Param("uint32_t", "firstIndex"),
+             Param("int32_t", "vertexOffset"),
+             Param("uint32_t", "firstInstance")]),
+
+        Proto("void", "CmdDrawIndirect",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceSize", "offset"),
+             Param("uint32_t", "drawCount"),
+             Param("uint32_t", "stride")]),
+
+        Proto("void", "CmdDrawIndexedIndirect",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceSize", "offset"),
+             Param("uint32_t", "drawCount"),
+             Param("uint32_t", "stride")]),
+
+        Proto("void", "CmdDispatch",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "x"),
+             Param("uint32_t", "y"),
+             Param("uint32_t", "z")]),
+
+        Proto("void", "CmdDispatchIndirect",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceSize", "offset")]),
+
+        Proto("void", "CmdCopyBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "srcBuffer"),
+             Param("VkBuffer", "dstBuffer"),
+             Param("uint32_t", "regionCount"),
+             Param("const VkBufferCopy*", "pRegions")]),
+
+        Proto("void", "CmdCopyImage",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkImage", "srcImage"),
+             Param("VkImageLayout", "srcImageLayout"),
+             Param("VkImage", "dstImage"),
+             Param("VkImageLayout", "dstImageLayout"),
+             Param("uint32_t", "regionCount"),
+             Param("const VkImageCopy*", "pRegions")]),
+
+        Proto("void", "CmdBlitImage",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkImage", "srcImage"),
+             Param("VkImageLayout", "srcImageLayout"),
+             Param("VkImage", "dstImage"),
+             Param("VkImageLayout", "dstImageLayout"),
+             Param("uint32_t", "regionCount"),
+             Param("const VkImageBlit*", "pRegions"),
+             Param("VkFilter", "filter")]),
+
+        Proto("void", "CmdCopyBufferToImage",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "srcBuffer"),
+             Param("VkImage", "dstImage"),
+             Param("VkImageLayout", "dstImageLayout"),
+             Param("uint32_t", "regionCount"),
+             Param("const VkBufferImageCopy*", "pRegions")]),
+
+        Proto("void", "CmdCopyImageToBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkImage", "srcImage"),
+             Param("VkImageLayout", "srcImageLayout"),
+             Param("VkBuffer", "dstBuffer"),
+             Param("uint32_t", "regionCount"),
+             Param("const VkBufferImageCopy*", "pRegions")]),
+
+        Proto("void", "CmdUpdateBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "dstBuffer"),
+             Param("VkDeviceSize", "dstOffset"),
+             Param("VkDeviceSize", "dataSize"),
+             Param("const void*", "pData")]),
+
+        Proto("void", "CmdFillBuffer",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "dstBuffer"),
+             Param("VkDeviceSize", "dstOffset"),
+             Param("VkDeviceSize", "size"),
+             Param("uint32_t", "data")]),
+
+        Proto("void", "CmdClearColorImage",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkImage", "image"),
+             Param("VkImageLayout", "imageLayout"),
+             Param("const VkClearColorValue*", "pColor"),
+             Param("uint32_t", "rangeCount"),
+             Param("const VkImageSubresourceRange*", "pRanges")]),
+
+        Proto("void", "CmdClearDepthStencilImage",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkImage", "image"),
+             Param("VkImageLayout", "imageLayout"),
+             Param("const VkClearDepthStencilValue*", "pDepthStencil"),
+             Param("uint32_t", "rangeCount"),
+             Param("const VkImageSubresourceRange*", "pRanges")]),
+
+        Proto("void", "CmdClearAttachments",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "attachmentCount"),
+             Param("const VkClearAttachment*", "pAttachments"),
+             Param("uint32_t", "rectCount"),
+             Param("const VkClearRect*", "pRects")]),
+
+        Proto("void", "CmdResolveImage",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkImage", "srcImage"),
+             Param("VkImageLayout", "srcImageLayout"),
+             Param("VkImage", "dstImage"),
+             Param("VkImageLayout", "dstImageLayout"),
+             Param("uint32_t", "regionCount"),
+             Param("const VkImageResolve*", "pRegions")]),
+
+        Proto("void", "CmdSetEvent",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkEvent", "event"),
+             Param("VkPipelineStageFlags", "stageMask")]),
+
+        Proto("void", "CmdResetEvent",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkEvent", "event"),
+             Param("VkPipelineStageFlags", "stageMask")]),
+
+        Proto("void", "CmdWaitEvents",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "eventCount"),
+             Param("const VkEvent*", "pEvents"),
+             Param("VkPipelineStageFlags", "srcStageMask"),
+             Param("VkPipelineStageFlags", "dstStageMask"),
+             Param("uint32_t", "memoryBarrierCount"),
+             Param("const VkMemoryBarrier*", "pMemoryBarriers"),
+             Param("uint32_t", "bufferMemoryBarrierCount"),
+             Param("const VkBufferMemoryBarrier*", "pBufferMemoryBarriers"),
+             Param("uint32_t", "imageMemoryBarrierCount"),
+             Param("const VkImageMemoryBarrier*", "pImageMemoryBarriers")]),
+
+        Proto("void", "CmdPipelineBarrier",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkPipelineStageFlags", "srcStageMask"),
+             Param("VkPipelineStageFlags", "dstStageMask"),
+             Param("VkDependencyFlags", "dependencyFlags"),
+             Param("uint32_t", "memoryBarrierCount"),
+             Param("const VkMemoryBarrier*", "pMemoryBarriers"),
+             Param("uint32_t", "bufferMemoryBarrierCount"),
+             Param("const VkBufferMemoryBarrier*", "pBufferMemoryBarriers"),
+             Param("uint32_t", "imageMemoryBarrierCount"),
+             Param("const VkImageMemoryBarrier*", "pImageMemoryBarriers")]),
+
+        Proto("void", "CmdBeginQuery",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkQueryPool", "queryPool"),
+             Param("uint32_t", "query"),
+             Param("VkQueryControlFlags", "flags")]),
+
+        Proto("void", "CmdEndQuery",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkQueryPool", "queryPool"),
+             Param("uint32_t", "query")]),
+
+        Proto("void", "CmdResetQueryPool",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkQueryPool", "queryPool"),
+             Param("uint32_t", "firstQuery"),
+             Param("uint32_t", "queryCount")]),
+
+        Proto("void", "CmdWriteTimestamp",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkPipelineStageFlagBits", "pipelineStage"),
+             Param("VkQueryPool", "queryPool"),
+             Param("uint32_t", "query")]),
+
+        Proto("void", "CmdCopyQueryPoolResults",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkQueryPool", "queryPool"),
+             Param("uint32_t", "firstQuery"),
+             Param("uint32_t", "queryCount"),
+             Param("VkBuffer", "dstBuffer"),
+             Param("VkDeviceSize", "dstOffset"),
+             Param("VkDeviceSize", "stride"),
+             Param("VkQueryResultFlags", "flags")]),
+
+        Proto("void", "CmdPushConstants",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkPipelineLayout", "layout"),
+             Param("VkShaderStageFlags", "stageFlags"),
+             Param("uint32_t", "offset"),
+             Param("uint32_t", "size"),
+             Param("const void*", "pValues")]),
+
+        Proto("void", "CmdBeginRenderPass",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("const VkRenderPassBeginInfo*", "pRenderPassBegin"),
+             Param("VkSubpassContents", "contents")]),
+
+        Proto("void", "CmdNextSubpass",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkSubpassContents", "contents")]),
+
+        Proto("void", "CmdEndRenderPass",
+            [Param("VkCommandBuffer", "commandBuffer")]),
+
+        Proto("void", "CmdExecuteCommands",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("uint32_t", "commandBufferCount"),
+             Param("const VkCommandBuffer*", "pCommandBuffers")]),
+    ],
+)
+
+VK_AMD_draw_indirect_count = Extension(
+    name="VK_AMD_draw_indirect_count",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("void", "CmdDrawIndirectCountAMD",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceSize", "offset"),
+             Param("VkBuffer", "countBuffer"),
+             Param("VkDeviceSize", "countBufferOffset"),
+             Param("uint32_t", "maxDrawCount"),
+             Param("uint32_t", "stride")]),
+
+        Proto("void", "CmdDrawIndexedIndirectCountAMD",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkBuffer", "buffer"),
+             Param("VkDeviceSize", "offset"),
+             Param("VkBuffer", "countBuffer"),
+             Param("VkDeviceSize", "countBufferOffset"),
+             Param("uint32_t", "maxDrawCount"),
+             Param("uint32_t", "stride")]),
+    ],
+)
+
+VK_NV_external_memory_capabilities = Extension(
+    name="VK_NV_external_memory_capabilities",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("VkResult", "GetPhysicalDeviceExternalImageFormatPropertiesNV",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkFormat", "format"),
+             Param("VkImageType", "type"),
+             Param("VkImageTiling", "tiling"),
+             Param("VkImageUsageFlags", "usage"),
+             Param("VkImageCreateFlags", "flags"),
+             Param("VkExternalMemoryHandleTypeFlagsNV", "externalHandleType"),
+             Param("VkExternalImageFormatPropertiesNV*", "pExternalImageFormatProperties")]),
+    ],
+)
+
+VK_NV_external_memory_win32 = Extension(
+    name="VK_NV_external_memory_win32",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    ifdef="VK_USE_PLATFORM_WIN32_KHR",
+    protos=[
+        Proto("VkResult", "GetMemoryWin32HandleNV",
+            [Param("VkDevice", "device"),
+             Param("VkDeviceMemory", "memory"),
+             Param("VkExternalMemoryHandleTypeFlagsNV", "handleType"),
+             Param("HANDLE*", "pHandle")]),
+    ],
+)
+
+VK_KHR_surface = Extension(
+    name="VK_KHR_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=["vkSurfaceKHR"],
+    protos=[
+        Proto("void", "DestroySurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("VkSurfaceKHR", "surface"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "GetPhysicalDeviceSurfaceSupportKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "queueFamilyIndex"),
+             Param("VkSurfaceKHR", "surface"),
+             Param("VkBool32*", "pSupported")]),
+
+        Proto("VkResult", "GetPhysicalDeviceSurfaceCapabilitiesKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkSurfaceKHR", "surface"),
+             Param("VkSurfaceCapabilitiesKHR*", "pSurfaceCapabilities")]),
+
+        Proto("VkResult", "GetPhysicalDeviceSurfaceFormatsKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkSurfaceKHR", "surface"),
+             Param("uint32_t*", "pSurfaceFormatCount"),
+             Param("VkSurfaceFormatKHR*", "pSurfaceFormats")]),
+
+        Proto("VkResult", "GetPhysicalDeviceSurfacePresentModesKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkSurfaceKHR", "surface"),
+             Param("uint32_t*", "pPresentModeCount"),
+             Param("VkPresentModeKHR*", "pPresentModes")]),
+    ],
+)
+
+VK_KHR_display = Extension(
+    name="VK_KHR_display",
+    headers=["vulkan/vulkan.h"],
+    objects=['VkSurfaceKHR', 'VkDisplayModeKHR'],
+    protos=[
+        Proto("VkResult", "GetPhysicalDeviceDisplayPropertiesKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkDisplayPropertiesKHR*", "pProperties")]),
+
+        Proto("VkResult", "GetPhysicalDeviceDisplayPlanePropertiesKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkDisplayPlanePropertiesKHR*", "pProperties")]),
+
+        Proto("VkResult", "GetDisplayPlaneSupportedDisplaysKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "planeIndex"),
+             Param("uint32_t*", "pDisplayCount"),
+             Param("VkDisplayKHR*", "pDisplays")]),
+
+        Proto("VkResult", "GetDisplayModePropertiesKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkDisplayKHR", "display"),
+             Param("uint32_t*", "pPropertyCount"),
+             Param("VkDisplayModePropertiesKHR*", "pProperties")]),
+
+        Proto("VkResult", "CreateDisplayModeKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkDisplayKHR", "display"),
+             Param("const VkDisplayModeCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkDisplayModeKHR*", "pMode")]),
+
+        Proto("VkResult", "GetDisplayPlaneCapabilitiesKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkDisplayModeKHR", "mode"),
+             Param("uint32_t", "planeIndex"),
+             Param("VkDisplayPlaneCapabilitiesKHR*", "pCapabilities")]),
+
+        Proto("VkResult", "CreateDisplayPlaneSurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkDisplaySurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+    ],
+)
+
+VK_KHR_swapchain = Extension(
+    name="VK_KHR_swapchain",
+    headers=["vulkan/vulkan.h"],
+    objects=["VkSwapchainKHR"],
+    protos=[
+        Proto("VkResult", "CreateSwapchainKHR",
+            [Param("VkDevice", "device"),
+             Param("const VkSwapchainCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSwapchainKHR*", "pSwapchain")]),
+
+        Proto("void", "DestroySwapchainKHR",
+            [Param("VkDevice", "device"),
+             Param("VkSwapchainKHR", "swapchain"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "GetSwapchainImagesKHR",
+            [Param("VkDevice", "device"),
+         Param("VkSwapchainKHR", "swapchain"),
+         Param("uint32_t*", "pSwapchainImageCount"),
+             Param("VkImage*", "pSwapchainImages")]),
+
+        Proto("VkResult", "AcquireNextImageKHR",
+            [Param("VkDevice", "device"),
+             Param("VkSwapchainKHR", "swapchain"),
+             Param("uint64_t", "timeout"),
+             Param("VkSemaphore", "semaphore"),
+             Param("VkFence", "fence"),
+             Param("uint32_t*", "pImageIndex")]),
+
+        Proto("VkResult", "QueuePresentKHR",
+            [Param("VkQueue", "queue"),
+             Param("const VkPresentInfoKHR*", "pPresentInfo")]),
+    ],
+)
+
+VK_KHR_display_swapchain = Extension(
+    name="VK_KHR_display_swapchain",
+    headers=["vulkan/vulkan.h"],
+    objects=["VkDisplayPresentInfoKHR"],
+    protos=[
+        Proto("VkResult", "CreateSharedSwapchainsKHR",
+            [Param("VkDevice", "device"),
+             Param("uint32_t", "swapchainCount"),
+             Param("const VkSwapchainCreateInfoKHR*", "pCreateInfos"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSwapchainKHR*", "pSwapchains")]),
+    ],
+)
+
+VK_KHR_xcb_surface = Extension(
+    name="VK_KHR_xcb_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("VkResult", "CreateXcbSurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkXcbSurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+
+        Proto("VkBool32", "GetPhysicalDeviceXcbPresentationSupportKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "queueFamilyIndex"),
+             Param("xcb_connection_t*", "connection"),
+             Param("xcb_visualid_t", "visual_id")]),
+    ],
+)
+
+VK_KHR_xlib_surface = Extension(
+    name="VK_KHR_xlib_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    ifdef="VK_USE_PLATFORM_XLIB_KHR",
+    protos=[
+        Proto("VkResult", "CreateXlibSurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkXlibSurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+
+        Proto("VkBool32", "GetPhysicalDeviceXlibPresentationSupportKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "queueFamilyIndex"),
+             Param("Display*", "dpy"),
+             Param("VisualID", "visualID")]),
+    ],
+)
+
+VK_KHR_wayland_surface = Extension(
+    name="VK_KHR_wayland_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("VkResult", "CreateWaylandSurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkWaylandSurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+
+        Proto("VkBool32", "GetPhysicalDeviceWaylandPresentationSupportKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "queueFamilyIndex"),
+             Param("struct wl_display*", "display")]),
+    ],
+)
+
+VK_KHR_mir_surface = Extension(
+    name="VK_KHR_mir_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("VkResult", "CreateMirSurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkMirSurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+
+        Proto("VkBool32", "GetPhysicalDeviceMirPresentationSupportKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "queueFamilyIndex"),
+             Param("MirConnection*", "connection")]),
+    ],
+)
+
+VK_KHR_android_surface = Extension(
+    name="VK_KHR_android_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("VkResult", "CreateAndroidSurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkAndroidSurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+    ],
+)
+
+VK_KHR_win32_surface = Extension(
+    name="VK_KHR_win32_surface",
+    headers=["vulkan/vulkan.h"],
+    objects=[],
+    protos=[
+        Proto("VkResult", "CreateWin32SurfaceKHR",
+            [Param("VkInstance", "instance"),
+             Param("const VkWin32SurfaceCreateInfoKHR*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkSurfaceKHR*", "pSurface")]),
+
+        Proto("VkBool32", "GetPhysicalDeviceWin32PresentationSupportKHR",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("uint32_t", "queueFamilyIndex")]),
+    ],
+)
+
+VK_EXT_debug_report = Extension(
+    name="VK_EXT_debug_report",
+    headers=["vulkan/vulkan.h"],
+    objects=[
+        "VkDebugReportCallbackEXT",
+    ],
+    protos=[
+        Proto("VkResult", "CreateDebugReportCallbackEXT",
+            [Param("VkInstance", "instance"),
+             Param("const VkDebugReportCallbackCreateInfoEXT*", "pCreateInfo"),
+             Param("const VkAllocationCallbacks*", "pAllocator"),
+             Param("VkDebugReportCallbackEXT*", "pCallback")]),
+
+        Proto("void", "DestroyDebugReportCallbackEXT",
+            [Param("VkInstance", "instance"),
+             Param("VkDebugReportCallbackEXT", "callback"),
+             Param("const VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("void", "DebugReportMessageEXT",
+            [Param("VkInstance", "instance"),
+             Param("VkDebugReportFlagsEXT", "flags"),
+             Param("VkDebugReportObjectTypeEXT", "objType"),
+             Param("uint64_t", "object"),
+             Param("size_t", "location"),
+             Param("int32_t", "msgCode"),
+             Param("const char *", "pLayerPrefix"),
+             Param("const char *", "pMsg")]),
+    ],
+)
+
+VK_EXT_debug_marker = Extension(
+    name="VK_EXT_debug_marker",
+    headers=["vulkan/vulkan.h"],
+    objects=[
+        "VkDebugMarkerObjectNameInfoEXT",
+        "VkDebugMarkerObjectTagInfoEXT",
+        "VkDebugMarkerMarkerInfoEXT"
+    ],
+    protos=[
+        Proto("VkResult", "DebugMarkerSetObjectTagEXT",
+            [Param("VkDevice", "device"),
+             Param("VkDebugMarkerObjectTagInfoEXT*", "pTagInfo")]),
+
+        Proto("VkResult", "DebugMarkerSetObjectNameEXT",
+            [Param("VkDevice", "device"),
+             Param("VkDebugMarkerObjectNameInfoEXT*", "pNameInfo")]),
+
+        Proto("void", "CmdDebugMarkerBeginEXT",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkDebugMarkerMarkerInfoEXT*", "pMarkerInfo")]),
+
+        Proto("void", "CmdDebugMarkerEndEXT",
+            [Param("VkCommandBuffer", "commandBuffer")]),
+
+        Proto("void", "CmdDebugMarkerInsertEXT",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkDebugMarkerMarkerInfoEXT*", "pMarkerInfo")]),
+    ],
+)
+
+VK_NVX_device_generated_commands = Extension(
+    name="VK_EXT_debug_marker",
+    headers=["vulkan/vulkan.h"],
+    objects=[
+        "VkObjectTableNVX",
+        "VkIndirectCommandsLayoutNVX",
+    ],
+    protos=[
+        Proto("void", "CmdProcessCommandsNVX",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkCmdProcessCommandsInfoNVX*", "pProcessCommandsInfo")]),
+
+        Proto("void", "CmdReserveSpaceForCommandsNV",
+            [Param("VkCommandBuffer", "commandBuffer"),
+             Param("VkCmdReserveSpaceForCommandsInfoNVX*", "pReserveSpaceInfo")]),
+
+        Proto("VkResult", "CreateIndirectCommandsLayoutNVX",
+            [Param("VkDevice", "device"),
+             Param("VkIndirectCommandsLayoutCreateInfoNVX*", "pCreateInfo"),
+             Param("VkAllocationCallbacks*", "pAllocator"),
+             Param("VkIndirectCommandsLayoutNVX*", "pIndirectCommandsLayout")]),
+
+        Proto("void", "DestroyIndirectCommandsLayoutNVX",
+            [Param("VkDevice", "device"),
+             Param("VkIndirectCommandsLayoutNVX", "indirectCommandsLayout"),
+             Param("VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "CreateObjectTableNVX)",
+            [Param("VkDevice", "device"),
+             Param("VkObjectTableCreateInfoNVX*", "pCreateInfo"),
+             Param("VkAllocationCallbacks*", "pAllocator"),
+             Param("VkObjectTableNVX*", "pObjectTable")]),
+
+        Proto("void", "DestroyObjectTableNVX",
+            [Param("VkDevice", "device"),
+             Param("VkObjectTableNVX", "objectTable"),
+             Param("VkAllocationCallbacks*", "pAllocator")]),
+
+        Proto("VkResult", "RegisterObjectsNVX",
+            [Param("VkDevice", "device"),
+             Param("VkObjectTableNVX", "objectTable"),
+             Param("uint32_t", "objectCount"),
+             Param("VkObjectTableEntryNVX**", "ppObjectTableEntries"),
+             Param("uint32_t*", "pObjectIndices")]),
+
+        Proto("VkResult", "UnregisterObjectsNVX)",
+            [Param("VkDevice", "device"),
+             Param("VkObjectTableNVX*", "objectTable"),
+             Param("uint32_t*", "objectCount"),
+             Param("VkObjectEntryTypeNVX*", "pObjectEntryTypes"),
+             Param("uint32_t*", "pObjectIndices")]),
+
+        Proto("void", "GetPhysicalDeviceGeneratedCommandsPropertiesNVX",
+            [Param("VkPhysicalDevice", "physicalDevice"),
+             Param("VkDeviceGeneratedCommandsFeaturesNVX*", "pFeatures"),
+             Param("VkDeviceGeneratedCommandsLimitsNVX*", "pLimits")]),
+    ],
+)
+
+
+import sys
+
+wsi_linux = ['Xcb', 'Xlib', 'Wayland', 'Mir', 'Display']
+
+# Set up platform-specific display servers
+linux_display_servers = ['Xcb', 'Xlib', 'Wayland', 'Mir', 'Display']
+win32_display_servers = ['Win32']
+android_display_servers = ['Android']
+
+# Define non-WSI platform-specific extensions
+android_only_exts = []
+linux_only_exts = []
+win32_only_exts = [VK_NV_external_memory_win32,
+#                  VK_NV_win32_keyed_mutex,
+                  ]
+
+# Define platform-specific WSI extensions
+android_wsi_exts = [VK_KHR_android_surface,
+                   ]
+linux_wsi_exts = [VK_KHR_xlib_surface,
+                  VK_KHR_xcb_surface,
+                  VK_KHR_wayland_surface,
+                  VK_KHR_mir_surface,
+                 ]
+win32_wsi_exts = [VK_KHR_win32_surface
+                 ]
+
+# Define extensions common to all configurations
+common_exts = [VK_VERSION_1_0,
+               VK_KHR_surface,
+               VK_KHR_swapchain,
+               VK_KHR_display,
+               VK_KHR_display_swapchain,
+              ]
+
+# Define extensions not exported by the loader
+non_exported_exts = [VK_NV_external_memory_capabilities,
+                     VK_AMD_draw_indirect_count,
+                     VK_EXT_debug_report,
+                     VK_EXT_debug_marker,
+#                    VK_KHR_sampler_mirror_clamp_to_edge,
+#                    VK_NV_glsl_shader,
+#                    VK_IMG_filter_cubic,
+#                    VK_AMD_rasterization_order,
+#                    VK_AMD_shader_trinary_minmax,
+#                    VK_AMD_shader_explicit_vertex_parameter,
+#                    VK_AMD_gcn_shader,
+#                    VK_NV_dedicated_allocation,
+#                    VK_NV_external_memory,
+#                    VK_EXT_validation_flags,
+#                    VK_AMD_negative_viewport_height,
+#                    VK_AMD_gpu_shader_half_float,
+#                    VK_AMD_shader_ballot,
+#                    VK_IMG_format_pvrtc,
+#                    VK_NVX_device_generated_commands,
+                    ]
+
+extensions = common_exts
+extensions_all = non_exported_exts
+
+if sys.argv[1] in win32_display_servers:
+    extensions += win32_wsi_exts
+    extensions_all += extensions + win32_only_exts
+elif sys.argv[1] in linux_display_servers:
+    extensions += linux_wsi_exts
+    extensions_all += extensions + linux_only_exts
+elif sys.argv[1] in android_display_servers:
+    extensions += android_wsi_exts
+    extensions_all += extensions + android_only_exts
+else:
+    extensions += win32_wsi_exts + linux_wsi_exts + android_wsi_exts
+    extensions_all += extensions + win32_only_exts + linux_only_exts + android_only_exts
+
+object_dispatch_list = [
+    "VkInstance",
+    "VkPhysicalDevice",
+    "VkDevice",
+    "VkQueue",
+    "VkCommandBuffer",
+]
+
+object_non_dispatch_list = [
+    "VkCommandPool",
+    "VkFence",
+    "VkDeviceMemory",
+    "VkBuffer",
+    "VkImage",
+    "VkSemaphore",
+    "VkEvent",
+    "VkQueryPool",
+    "VkBufferView",
+    "VkImageView",
+    "VkShaderModule",
+    "VkPipelineCache",
+    "VkPipelineLayout",
+    "VkPipeline",
+    "VkDescriptorSetLayout",
+    "VkSampler",
+    "VkDescriptorPool",
+    "VkDescriptorSet",
+    "VkRenderPass",
+    "VkFramebuffer",
+    "VkSwapchainKHR",
+    "VkSurfaceKHR",
+    "VkDebugReportCallbackEXT",
+    "VkDisplayKHR",
+    "VkDisplayModeKHR",
+]
+
+object_type_list = object_dispatch_list + object_non_dispatch_list
+
+headers = []
+objects = []
+protos = []
+for ext in extensions:
+    headers.extend(ext.headers)
+    objects.extend(ext.objects)
+    protos.extend(ext.protos)
+
+proto_names = [proto.name for proto in protos]
+
+headers_all = []
+objects_all = []
+protos_all = []
+for ext in extensions_all:
+    headers_all.extend(ext.headers)
+    objects_all.extend(ext.objects)
+    protos_all.extend(ext.protos)
+
+proto_all_names = [proto.name for proto in protos_all]
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1cbdaf0..567715f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -41,7 +41,7 @@
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
 
 if(WIN32)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16")
 
     # If MSVC, disable some signed/unsigned mismatch warnings.
     if (MSVC)
@@ -49,7 +49,7 @@
     endif()
 
 else()
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16")
 endif()
 
 set (LIBGLM_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/libs)
@@ -65,6 +65,7 @@
     "${PROJECT_SOURCE_DIR}/tests/gtest-1.7.0/include"
     "${PROJECT_SOURCE_DIR}/icd/common"
     "${PROJECT_SOURCE_DIR}/layers"
+    ${XCB_INCLUDE_DIRS}
     ${GLSLANG_SPIRV_INCLUDE_DIR}
     ${LIBGLM_INCLUDE_DIR}
     )
@@ -78,16 +79,20 @@
             COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/run_loader_tests.sh
             COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/run_extra_loader_tests.sh
             COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/vkvalidatelayerdoc.sh
+            # Files unique to VulkanTools go below this line
+            COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/vktracereplay.sh
             VERBATIM
             )
     endif()
 else()
     if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
         FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/_run_all_tests.ps1 RUN_ALL)
-        FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/_vkvalidatelayerdoc.ps1 VALIDATE_DOC)
+        FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/_vktracereplay.ps1 VKTRACEREPLAY)
+        FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/_vkvalidatelayerdoc.ps1 VKVALIDATELAYERDOC)
         add_custom_target(binary-dir-symlinks ALL
-            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RUN_ALL} run_all_tests.ps1
-            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VALIDATE_DOC} vkvalidatelayerdoc.ps1
+            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RUN_ALL} _run_all_tests.ps1
+            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VKTRACEREPLAY} _vktracereplay.ps1
+            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VKVALIDATELAYERDOC} _vkvalidatelayerdoc.ps1
             VERBATIM
             )
     endif()
@@ -95,8 +100,10 @@
 
 if(WIN32)
    set (LIBVK "${API_LOWERCASE}-${MAJOR}")
+   set (TEST_LIBRARIES ${GLSLANG_LIBRARIES})
 elseif(UNIX)
    set (LIBVK "${API_LOWERCASE}")
+   set (TEST_LIBRARIES ${GLSLANG_LIBRARIES} ${XCB_LIBRARIES} ${X11_LIBRARIES})
 else()
 endif()
 
@@ -104,22 +111,13 @@
 set_target_properties(vk_layer_validation_tests
    PROPERTIES
    COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
-if(NOT WIN32)
-    if (BUILD_WSI_XCB_SUPPORT OR BUILD_WSI_XLIB_SUPPORT)
-        target_link_libraries(vk_layer_validation_tests ${LIBVK} ${XCB_LIBRARIES} ${X11_LIBRARIES} gtest gtest_main VkLayer_utils ${GLSLANG_LIBRARIES})
-    else()
-        target_link_libraries(vk_layer_validation_tests ${LIBVK} gtest gtest_main VkLayer_utils ${GLSLANG_LIBRARIES})
-    endif()
-endif()
-if(WIN32)
-   target_link_libraries(vk_layer_validation_tests ${LIBVK} gtest gtest_main VkLayer_utils ${GLSLANG_LIBRARIES})
-endif()
+target_link_libraries(vk_layer_validation_tests ${LIBVK} gtest gtest_main VkLayer_utils ${TEST_LIBRARIES})
 
 add_executable(vk_loader_validation_tests loader_validation_tests.cpp ${COMMON_CPP})
 set_target_properties(vk_loader_validation_tests
    PROPERTIES
    COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
-target_link_libraries(vk_loader_validation_tests ${LIBVK} gtest gtest_main VkLayer_utils ${GLSLANG_LIBRARIES})
+target_link_libraries(vk_loader_validation_tests ${LIBVK} gtest gtest_main VkLayer_utils ${TEST_LIBRARIES})
 
 add_subdirectory(gtest-1.7.0)
 add_subdirectory(layers)
diff --git a/tests/_vktracereplay.ps1 b/tests/_vktracereplay.ps1
new file mode 100644
index 0000000..44099b5
--- /dev/null
+++ b/tests/_vktracereplay.ps1
@@ -0,0 +1,163 @@
+# Powershell script for running the vktrace trace/replay auto test

+# To run this test:

+#    cd <this-dir>

+#    powershell C:\src\LoaderAndValidationLayers\vktracereplay.ps1 [-Replay <tracepath>] [-Debug]

+#

+#    <tracepath> example: "C:\trace" would result in the script testing against "C:\trace.vktrace" and "C:\trace.ppm"

+param (

+    [switch]$Debug,

+    [string]$Replay

+)

+

+$exitstatus = 0

+

+if ($Debug) {

+    $dPath = "Debug"

+} else {

+    $dPath = "Release"

+}

+

+write-host -background black -foreground green "[  RUN     ] " -nonewline

+write-host "vktracereplay.ps1: Vktrace trace/replay"

+

+# Create a temp directory to run the test in

+

+if (Test-Path .\vktracereplay_tmp) {

+    rm -recurse -force .\vktracereplay_tmp  > $null 2> $null

+}

+new-item vktracereplay_tmp -itemtype directory > $null 2> $null

+

+# Copy everything we need into the temp directory, so we

+# can make sure we are using the correct dll and exe files

+cd vktracereplay_tmp

+cp ..\..\vktrace\$dPath\vkreplay.exe .

+cp ..\..\vktrace\$dPath\vktrace.exe .

+cp ..\..\demos\$dPath\cube.exe .

+cp ..\..\demos\$dPath\smoketest.exe .

+cp ..\..\demos\*.ppm .

+cp ..\..\demos\*.spv .

+cp ..\..\loader\$dPath\vulkan-1.dll .

+cp ..\..\layersvt\$dPath\VkLayer_screenshot.dll .

+cp ..\..\layersvt\$dPath\VkLayer_screenshot.json .

+cp ..\..\layersvt\$dPath\VkLayer_vktrace_layer.dll .

+cp ..\..\layersvt\$dPath\VkLayer_vktrace_layer.json .

+

+# Change PATH to the temp directory

+$oldpath = $Env:PATH

+$Env:PATH = $pwd

+

+# Set up some modified env vars

+$Env:VK_LAYER_PATH = $pwd

+

+# Do a trace and replay for cube

+& vktrace -o c01.vktrace -s 1 -p cube -a "--c 10" --PMB true > trace.sout 2> trace.serr

+rename-item -path 1.ppm -newname 1-cubetrace.ppm

+& vkreplay  -s 1 -o  c01.vktrace > replay.sout 2> replay.serr

+rename-item -path 1.ppm -newname 1-cubereplay.ppm

+

+# Do a trace and replay for smoketest

+& vktrace -o s01.vktrace -s 1 -p smoketest -a "--c 10" --PMB true > trace.sout 2> trace.serr

+rename-item -path 1.ppm -newname 1-smoketrace.ppm

+& vkreplay  -s 1 -o  s01.vktrace > replay.sout 2> replay.serr

+rename-item -path 1.ppm -newname 1-smokereplay.ppm

+

+# Replay old trace if specified

+if ($Replay) {

+    & vkreplay -s 1 -o "$Replay.vktrace" > replayold.sout 2> replayold.serr

+    rename-item -path 1.ppm -newname 1-replayold.ppm

+}

+

+# Force a failure - for testing this script

+#cp vulkan.dll 1-cubereplay.ppm

+#rm 1-cubetrace.ppm

+#rm 1-cubereplay.ppm

+

+# Restore PATH

+$Env:PATH = $oldpath

+

+if ($exitstatus -eq 0) {

+   # Check that two screenshots were created, and the third if replaying an old trace

+   if (!(Test-Path 1-cubetrace.ppm) -or !(Test-Path 1-cubereplay.ppm) -or

+       #!(Test-Path 1-smoketrace.ppm) -or !(Test-Path 1-smokereplay.ppm) -or

+        ($Replay -and !(Test-Path 1-replayold.ppm))) {

+           echo 'Trace file does not exist'

+           write-host -background black -foreground red "[  FAILED  ] "  -nonewline;

+           $exitstatus = 1

+   }

+}

+

+if ($exitstatus -eq 0) {

+    # ensure the trace and replay snapshots are identical

+    fc.exe /b 1-cubetrace.ppm 1-cubereplay.ppm > $null

+    if (!(Test-Path 1-cubetrace.ppm) -or !(Test-Path 1-cubereplay.ppm) -or $LastExitCode -eq 1) {

+        echo 'Cube trace files do not match'

+        write-host -background black -foreground red "[  FAILED  ] "  -nonewline;

+        $exitstatus = 1

+    }

+    fc.exe /b 1-smoketrace.ppm 1-smokereplay.ppm > $null

+    if (!(Test-Path 1-smoketrace.ppm) -or !(Test-Path 1-smokereplay.ppm) -or $LastExitCode -eq 1) {

+        echo 'Smoke trace files do not match'

+        write-host -background black -foreground red "[  FAILED  ] "  -nonewline;

+        $exitstatus = 1

+    }

+    if ($Replay) {

+        # check old trace

+        fc.exe /b "$Replay.ppm" 1-replayold.ppm > $null

+        if (!(Test-Path "$Replay.ppm") -or !(Test-Path 1-replayold.ppm) -or $LastExitCode -eq 1) {

+            echo 'Old trace does not match'

+            write-host -background black -foreground red "[  FAILED  ] "  -nonewline;

+            $exitstatus = 1

+        }

+    }

+}

+

+# check the average pixel value of each screenshot to ensure something plausible was written

+#if ($exitstatus -eq 0) {

+#    $trace_mean = (convert 1-cubetrace.ppm -format "%[mean]" info:)

+#    $replay_mean = (convert 1-cubereplay.ppm -format "%[mean]" info:)

+#    $version = (identify -version)

+#

+#    # normalize the values so we can support Q8 and Q16 imagemagick installations

+#    if ($version -match "Q8") {

+#        $trace_mean = $trace_mean   / 255 # 2^8-1

+#        $replay_mean = $replay_mean / 255 # 2^8-1

+#    } else {

+#        $trace_mean = $trace_mean   / 65535 # 2^16-1

+#        $replay_mean = $replay_mean / 65535 # 2^16-1

+#    }

+#

+#    # if either screenshot is too bright or too dark, it either failed, or is a bad test

+#    if (($trace_mean -lt 0.10) -or ($trace_mean -gt 0.90)){

+#        echo ''

+#        echo 'Trace screenshot failed mean check, must be in range [0.1, 0.9]'

+#        write-host 'Detected mean:' $trace_mean

+#        echo ''

+#        write-host -background black -foreground red "[  FAILED  ] "  -nonewline;

+#        $exitstatus = 1

+#    }

+#    if (($replay_mean -lt 0.10) -or ($replay_mean -gt 0.90)){

+#        echo ''

+#        echo 'Replay screenshot failed mean check, must be in range [0.1, 0.9]'

+#        write-host 'Detected mean:' $replay_mean

+#        echo ''

+#        write-host -background black -foreground red "[  FAILED  ] "  -nonewline;

+#        $exitstatus = 1

+#    }

+#}

+

+# if we passed all the checks, the test is good

+if ($exitstatus -eq 0) {

+   write-host -background black -foreground green "[  PASSED  ] " -nonewline;

+}

+

+write-host "vktracereplay.ps1: Vktrace trace/replay"

+write-host

+if ($exitstatus) {

+    echo '1 FAILED TEST'

+}

+

+# cleanup

+cd ..

+rm -recurse -force vktracereplay_tmp  > $null 2> $null

+Remove-Item Env:\VK_LAYER_PATH

+exit $exitstatus

diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 947eaf2..7b17a23 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -51,6 +51,7 @@
 #define SHADER_CHECKER_TESTS 1
 #define DEVICE_LIMITS_TESTS 1
 #define IMAGE_TESTS 1
+#define API_DUMP_TESTS 0
 
 //--------------------------------------------------------------------------------------
 // Mesh and VertexFormat Data
@@ -314,6 +315,7 @@
   protected:
     ErrorMonitor *m_errorMonitor;
     bool m_enableWSI;
+    bool m_enableApiDump;
 
     virtual void SetUp() {
         std::vector<const char *> instance_layer_names;
@@ -321,6 +323,11 @@
         std::vector<const char *> device_extension_names;
 
         instance_extension_names.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
+
+        if (m_enableApiDump) {
+            instance_layer_names.push_back("VK_LAYER_LUNARG_api_dump");
+        }
+
         /*
          * Since CreateDbgMsgCallback is an instance level extension call
          * any extension / layer that utilizes that feature also needs
@@ -378,7 +385,10 @@
         delete m_errorMonitor;
     }
 
-    VkLayerTest() { m_enableWSI = false; }
+    VkLayerTest() {
+        m_enableWSI = false;
+        m_enableApiDump = false;
+    }
 };
 
 void VkLayerTest::VKTriangleTest(const char *vertShaderText, const char *fragShaderText, BsoFailSelect failMask) {
@@ -553,6 +563,15 @@
     VkWsiEnabledLayerTest() { m_enableWSI = true; }
 };
 
+class VkApiDumpEnabledLayerTest : public VkLayerTest {
+  public:
+protected:
+    VkApiDumpEnabledLayerTest() {
+        m_enableApiDump = true;
+    }
+};
+
+
 class VkBufferTest {
   public:
     enum eTestEnFlags {
@@ -766,6 +785,7 @@
 };
 
 uint32_t VkVerticesObj::BindIdGenerator;
+
 // ********************************************************************************************************************
 // ********************************************************************************************************************
 // ********************************************************************************************************************
@@ -20557,6 +20577,17 @@
 }
 #endif
 
+#if API_DUMP_TESTS
+TEST_F(VkApiDumpEnabledLayerTest, TestApiDump) {
+
+    // This test invokes the framework only, dumping commands.
+    // Ideally we would have a test harness that automatically verifies
+    // we can dump the entire API.
+    // This test just checks for a pulse, using visual inspection.
+    TEST_DESCRIPTION("Empty test with ApiDump enabled.");
+}
+#endif
+
 int main(int argc, char **argv) {
     int result;
 
diff --git a/tests/run_all_tests.sh b/tests/run_all_tests.sh
index 8cf691e..430840a 100755
--- a/tests/run_all_tests.sh
+++ b/tests/run_all_tests.sh
@@ -16,3 +16,6 @@
 # catch the errors that they are supposed to by intentionally doing things
 # that are wrong
 ./vk_layer_validation_tests
+
+# vktracereplay.sh tests vktrace trace and replay
+./vktracereplay.sh
diff --git a/tests/vktestframework.h b/tests/vktestframework.h
index cfd60a5..1387c91 100644
--- a/tests/vktestframework.h
+++ b/tests/vktestframework.h
@@ -22,7 +22,6 @@
 #ifndef VKTESTFRAMEWORK_H
 #define VKTESTFRAMEWORK_H
 
-//#include "gtest-1.7.0/include/gtest/gtest.h"
 #include "SPIRV/GLSL.std.450.h"
 #include "glslang/Public/ShaderLang.h"
 #include "icd-spv.h"
diff --git a/tests/vktestframeworkandroid.h b/tests/vktestframeworkandroid.h
index 9d16097..922c452 100644
--- a/tests/vktestframeworkandroid.h
+++ b/tests/vktestframeworkandroid.h
@@ -4,6 +4,9 @@
 //  Copyright (c) 2015-2016 Valve Corporation
 //  Copyright (c) 2015-2016 LunarG, Inc.
 //  Copyright (c) 2015-2016 Google, Inc.
+//  Copyright (C) 2015-2016 Valve Corporation
+//  Copyright (C) 2015-2016 LunarG, Inc.
+//  Copyright (C) 2015 Google, Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/tests/vktracereplay.sh b/tests/vktracereplay.sh
new file mode 100755
index 0000000..f61dfb1
--- /dev/null
+++ b/tests/vktracereplay.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+#set -x
+if [ -t 1 ] ; then
+    RED='\033[0;31m'
+    GREEN='\033[0;32m'
+    NC='\033[0m' # No Color
+else
+    RED=''
+    GREEN=''
+    NC=''
+fi
+
+printf "$GREEN[ RUN      ]$NC $0\n"
+
+export LD_LIBRARY_PATH=${PWD}/../loader:${LD_LIBRARY_PATH}
+export VK_LAYER_PATH=${PWD}/../layersvt
+
+function trace_replay {
+	PGM=$1
+	PARGS=$2
+	TARGS=$3
+	VKTRACE=${PWD}/../vktrace/vktrace
+	VKREPLAY=${PWD}/../vktrace/vkreplay
+	APPDIR=${PWD}/../demos
+	printf "$GREEN[ TRACE    ]$NC ${PGM}\n"
+	${VKTRACE}	--Program ${APPDIR}/${PGM} \
+			--Arguments "--c 100 ${PARGS}" \
+			--WorkingDir ${APPDIR} \
+			--OutputTrace ${PGM}.vktrace \
+			${TARGS} \
+			-s 1
+	printf "$GREEN[ REPLAY   ]$NC ${PGM}\n"
+	${VKREPLAY}	--Open ${PGM}.vktrace \
+			-s 1
+	rm -f ${PGM}.vktrace
+	cmp -s 1.ppm ${APPDIR}/1.ppm
+	RES=$?
+	rm 1.ppm ${APPDIR}/1.ppm
+	if [ $RES -eq 0 ] ; then
+	   printf "$GREEN[  PASSED  ]$NC ${PGM}\n"
+	else
+	   printf "$RED[  FAILED  ]$NC screenshot file compare failed\n"
+	   printf "$RED[  FAILED  ]$NC ${PGM}\n"
+	   printf "TEST FAILED\n"
+	   exit 1
+	fi
+}
+
+trace_replay cube "" "--PMB false"
+# Test smoketest with pageguard
+trace_replay smoketest "" "--PMB true"
+# Test smoketest without pageguard, using push constants
+trace_replay smoketest "-p" "--PMB false"
+# Test smoketest without pageguard, using flush call
+trace_replay smoketest "--flush" "--PMB false"
+
+exit 0
+
diff --git a/update_external_sources.bat b/update_external_sources.bat
index 91b49ec..77e2e3a 100644
--- a/update_external_sources.bat
+++ b/update_external_sources.bat
@@ -17,6 +17,7 @@
 set REVISION_DIR="%BUILD_DIR%external_revisions"
 set GLSLANG_DIR=%BASE_DIR%\glslang
 set SPIRV_TOOLS_DIR=%BASE_DIR%\spirv-tools
+set JSONCPP_DIR=%BASE_DIR%\jsoncpp
 
 REM // ======== Parameter parsing ======== //
 
@@ -26,16 +27,20 @@
       echo Available options:
       echo   --sync-glslang      just pull glslang_revision
       echo   --sync-spirv-tools  just pull spirv-tools_revision
+      echo   --sync-jsoncpp      just pull jsoncpp HEAD
       echo   --build-glslang     pulls glslang_revision, configures CMake, builds Release and Debug
       echo   --build-spirv-tools pulls spirv-tools_revision, configures CMake, builds Release and Debug
-      echo   --all               sync and build glslang, LunarGLASS, spirv-tools
+      echo   --build-jsoncpp     pulls jsoncpp HEAD, configures CMake, builds Release and Debug
+      echo   --all               sync and build glslang, spirv-tools, and jsoncpp
       goto:finish
    )
 
    set sync-glslang=0
    set sync-spirv-tools=0
+   set sync-jsoncpp=0
    set build-glslang=0
    set build-spirv-tools=0
+   set build-jsoncpp=0
    set check-glslang-build-dependencies=0
 
    :parameterLoop
@@ -54,6 +59,12 @@
          goto:parameterLoop
       )
 
+      if "%1" == "--sync-jsoncpp" (
+         set sync-jsoncpp=1
+         shift
+         goto:parameterLoop
+      )
+
       if "%1" == "--build-glslang" (
          set sync-glslang=1
          set check-glslang-build-dependencies=1
@@ -71,11 +82,20 @@
          goto:parameterLoop
       )
 
+      if "%1" == "--build-jsoncpp" (
+         set sync-jsoncpps=1
+         set build-jsoncpp=1
+         shift
+         goto:parameterLoop
+      )
+
       if "%1" == "--all" (
          set sync-glslang=1
          set sync-spirv-tools=1
+         set sync-jsoncpp=1
          set build-glslang=1
          set build-spirv-tools=1
+         set build-jsoncpp=1
          set check-glslang-build-dependencies=1
          shift
          goto:parameterLoop
@@ -143,13 +163,22 @@
    goto:error
 )
 
+if not exist %REVISION_DIR%\jsoncpp_revision (
+   echo.
+   echo Missing jsoncpp_revision file!  Place it in %REVISION_DIR% with target version in it.
+   set errorCode=1
+   goto:error
+)
+
+
 set /p GLSLANG_REVISION= < %REVISION_DIR%\glslang_revision
 set /p SPIRV_TOOLS_REVISION= < %REVISION_DIR%\spirv-tools_revision
 set /p SPIRV_HEADERS_REVISION= < %REVISION_DIR%\spirv-headers_revision
+set /p JSONCPP_REVISION= < %REVISION_DIR%\jsoncpp_revision
 echo GLSLANG_REVISION=%GLSLANG_REVISION%
 echo SPIRV_TOOLS_REVISION=%SPIRV_TOOLS_REVISION%
 echo SPIRV_HEADERS_REVISION=%SPIRV_HEADERS_REVISION%
-
+echo JSONCPP_REVISION=%JSONCPP_REVISION%
 
 echo Creating and/or updating glslang, spirv-tools in %BASE_DIR%
 
@@ -172,6 +201,19 @@
    if %errorCode% neq 0 (goto:error)
 )
 
+if %sync-jsoncpp% equ 1 (
+   if exist %JSONCPP_DIR% (
+      rd /S /Q %JSONCPP_DIR%
+   )
+   if %errorlevel% neq 0 (goto:error)
+   if not exist %JSONCPP_DIR% (
+      call:create_jsoncpp
+   )
+   if %errorCode% neq 0 (goto:error)
+   call:update_jsoncpp
+   if %errorCode% neq 0 (goto:error)
+)
+
 if %build-glslang% equ 1 (
    call:build_glslang
    if %errorCode% neq 0 (goto:error)
@@ -182,6 +224,11 @@
    if %errorCode% neq 0 (goto:error)
 )
 
+if %build-jsoncpp% equ 1 (
+   call:build_jsoncpp
+   if %errorCode% neq 0 (goto:error)
+)
+
 echo.
 echo Exiting
 goto:finish
@@ -196,8 +243,6 @@
 endlocal
 goto:eof
 
-
-
 REM // ======== Functions ======== //
 
 :create_glslang
@@ -395,3 +440,103 @@
       set errorCode=1
    )
 goto:eof
+
+:create_jsoncpp
+   echo.
+   echo Creating local jsoncpp repository %JSONCPP_DIR%)
+   mkdir %JSONCPP_DIR%
+   cd %JSONCPP_DIR%
+   git clone https://github.com/open-source-parsers/jsoncpp.git .
+   git checkout %JSONCPP_REVISION%
+   if not exist %JSONCPP_DIR%\include\json\json.h (
+      echo jsoncpp source download failed!
+      set errorCode=1
+   )
+goto:eof
+
+:update_jsoncpp
+   echo.
+   echo Updating %JSONCPP_DIR%
+   cd %JSONCPP_DIR%
+   git fetch --all
+   git checkout %JSONCPP_REVISION%
+goto:eof
+
+:build_jsoncpp
+   echo.
+   echo Building %JSONCPP_DIR%
+   cd  %JSONCPP_DIR%
+   python amalgamate.py
+   
+   if not exist %JSONCPP_DIR%\dist\json\json.h (
+      echo.
+      echo JsonCPP Amalgamation failed to generate %JSONCPP_DIR%\dist\json\json.h
+      set errorCode=1
+   )
+
+REM    REM Cleanup any old directories lying around.
+REM    if exist build32 (
+REM       rmdir /s /q build32
+REM    )
+REM    if exist build (
+REM       rmdir /s /q build
+REM    )
+REM 
+REM    echo Making 32-bit jsoncpp
+REM    echo *************************
+REM    mkdir build32
+REM    set JSONCPP_BUILD_DIR=%JSONCPP_DIR%\build32
+REM    cd %JSONCPP_BUILD_DIR%
+REM 
+REM    echo Generating 32-bit JsonCPP CMake files for Visual Studio %VS_VERSION%
+REM    cmake -G "Visual Studio %VS_VERSION%" .. -DMSVC_RUNTIME=static
+REM 
+REM    echo Building 32-bit JsonCPP: MSBuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Debug
+REM    msbuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Debug /verbosity:quiet
+REM 
+REM    REM Check for existence of one lib, even though we should check for all results
+REM    if not exist %JSONCPP_BUILD_DIR%\src\lib_json\Debug\jsoncpp.lib (
+REM       echo.
+REM       echo jsoncpp 32-bit Debug build failed!
+REM       set errorCode=1
+REM    )
+REM    echo B Building 32-bit JsonCPP: MSBuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Release
+REM    msbuild ALL_BUILD.vcxproj /p:Platform=x86 /p:Configuration=Release /verbosity:quiet
+REM 
+REM    REM Check for existence of one lib, even though we should check for all results
+REM    if not exist %JSONCPP_BUILD_DIR%\src\lib_json\Release\jsoncpp.lib (
+REM       echo.
+REM       echo jsoncpp 32-bit Release build failed!
+REM       set errorCode=1
+REM    )
+REM 
+REM    cd ..
+REM 
+REM    echo Making 64-bit jsoncpp
+REM    echo *************************
+REM    mkdir build
+REM    set JSONCPP_BUILD_DIR=%JSONCPP_DIR%\build
+REM    cd %JSONCPP_BUILD_DIR%
+REM 
+REM    echo Generating 64-bit JsonCPP CMake files for Visual Studio %VS_VERSION%
+REM    cmake -G "Visual Studio %VS_VERSION% Win64" .. -DMSVC_RUNTIME=static
+REM 
+REM    echo Building 64-bit JsonCPP: MSBuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Debug
+REM    msbuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Debug /verbosity:quiet
+REM 
+REM    REM Check for existence of one lib, even though we should check for all results
+REM    if not exist %JSONCPP_BUILD_DIR%\src\lib_json\Debug\jsoncpp.lib (
+REM       echo.
+REM       echo jsoncpp 64-bit Debug build failed!
+REM       set errorCode=1
+REM    )
+REM    echo Building 64-bit JsonCPP: MSBuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Release
+REM    msbuild ALL_BUILD.vcxproj /p:Platform=x64 /p:Configuration=Release /verbosity:quiet
+REM 
+REM    REM Check for existence of one lib, even though we should check for all results
+REM    if not exist %JSONCPP_BUILD_DIR%\src\lib_json\Release\jsoncpp.lib (
+REM       echo.
+REM       echo jsoncpp 64-bit Release build failed!
+REM       set errorCode=1
+REM    )
+goto:eof
diff --git a/update_external_sources.sh b/update_external_sources.sh
index 06dca16..2a8fcd8 100755
--- a/update_external_sources.sh
+++ b/update_external_sources.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Update source for glslang, LunarGLASS, spirv-tools
+# Update source for glslang, spirv-tools
 
 set -e
 
@@ -17,9 +17,11 @@
 GLSLANG_REVISION=$(cat "${REVISION_DIR}/glslang_revision")
 SPIRV_TOOLS_REVISION=$(cat "${REVISION_DIR}/spirv-tools_revision")
 SPIRV_HEADERS_REVISION=$(cat "${REVISION_DIR}/spirv-headers_revision")
+JSONCPP_REVISION=$(cat "${REVISION_DIR}/jsoncpp_revision")
 echo "GLSLANG_REVISION=${GLSLANG_REVISION}"
 echo "SPIRV_TOOLS_REVISION=${SPIRV_TOOLS_REVISION}"
 echo "SPIRV_HEADERS_REVISION=${SPIRV_HEADERS_REVISION}"
+echo "JSONCPP_REVISION=${JSONCPP_REVISION}"
 
 BUILDDIR=${CURRENT_DIR}
 BASEDIR="$BUILDDIR/external"
@@ -88,17 +90,40 @@
    make -j $CORE_COUNT
 }
 
-# If any options are provided, just compile those tools
-# If no options are provided, build everything
+function create_jsoncpp () {
+   rm -rf ${BASEDIR}/jsoncpp
+   echo "Creating local jsoncpp repository (${BASEDIR}/jsoncpp)."
+   mkdir -p ${BASEDIR}/jsoncpp
+   cd ${BASEDIR}/jsoncpp
+   git clone https://github.com/open-source-parsers/jsoncpp.git .
+   git checkout ${JSONCPP_REVISION}
+}
+
+function update_jsoncpp () {
+   echo "Updating ${BASEDIR}/jsoncpp"
+   cd ${BASEDIR}/jsoncpp
+   git fetch --all
+   git checkout ${JSONCPP_REVISION}
+}
+
+function build_jsoncpp () {
+   echo "Building ${BASEDIR}/jsoncpp"
+   cd ${BASEDIR}/jsoncpp
+   python amalgamate.py
+}
+
 INCLUDE_GLSLANG=false
 INCLUDE_SPIRV_TOOLS=false
+INCLUDE_JSONCPP=false
 
 if [ "$#" == 0 ]; then
-  echo "Building glslang, spirv-tools"
+  # If no options are provided, build everything
+  echo "Building glslang, spirv-tools, and jsoncpp"
   INCLUDE_GLSLANG=true
   INCLUDE_SPIRV_TOOLS=true
+  INCLUDE_JSONCPP=true
 else
-  # Parse options
+  # If any options are provided, just compile those tools
   while [[ $# > 0 ]]
   do
     option="$1"
@@ -109,16 +134,25 @@
         INCLUDE_GLSLANG=true
         echo "Building glslang ($option)"
         ;;
+
         # options to specify build of spirv-tools components
         -s|--spirv-tools)
         INCLUDE_SPIRV_TOOLS=true
         echo "Building spirv-tools ($option)"
         ;;
+
+        # options to specify build of jsoncpp components
+        -j|--jsoncpp)
+        INCLUDE_JSONCPP=true
+        echo "Building jsoncpp ($option)"
+        ;;
+
         *)
         echo "Unrecognized option: $option"
         echo "Try the following:"
         echo " -g | --glslang      # enable glslang"
         echo " -s | --spirv-tools  # enable spirv-tools"
+        echo " -j | --jsoncpp      # enable jsoncpp"
         exit 1
         ;;
     esac
@@ -134,7 +168,6 @@
   build_glslang
 fi
 
-
 if [ ${INCLUDE_SPIRV_TOOLS} == "true" ]; then
     if [ ! -d "${BASEDIR}/spirv-tools" -o ! -d "${BASEDIR}/spirv-tools/.git" ]; then
        create_spirv-tools
@@ -142,3 +175,11 @@
     update_spirv-tools
     build_spirv-tools
 fi
+
+if [ ${INCLUDE_JSONCPP} == "true" ]; then
+    if [ ! -d "${BASEDIR}/jsoncpp" -o ! -d "${BASEDIR}/jsoncpp/.git" ]; then
+       create_jsoncpp
+    fi
+    update_jsoncpp
+    build_jsoncpp
+fi
diff --git a/via/CMakeLists.txt b/via/CMakeLists.txt
new file mode 100644
index 0000000..3280357
--- /dev/null
+++ b/via/CMakeLists.txt
@@ -0,0 +1,68 @@
+file(GLOB TEXTURES
+   "${PROJECT_SOURCE_DIR}/via/images/*"
+)
+file(COPY ${TEXTURES} DESTINATION ${CMAKE_BINARY_DIR}/via/images)
+
+
+if(WIN32)
+    set (LIBRARIES "${API_LOWERCASE}-${MAJOR}")
+
+    # For Windows, since 32-bit and 64-bit items can co-exist, we build each in its own build directory.
+    # 32-bit target data goes in build32, and 64-bit target data goes into build.  So, include/link the
+    # appropriate data at build time.
+    if (CMAKE_CL_64)
+        set (BUILDTGT_DIR build)
+    else ()
+        set (BUILDTGT_DIR build32)
+    endif()
+
+    # Use static MSVCRT libraries
+    foreach(configuration in CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO
+                             CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+        if(${configuration} MATCHES "/MD")
+            string(REGEX REPLACE "/MD" "/MT" ${configuration} "${${configuration}}")
+        endif()
+    endforeach()
+
+    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")
+
+else()
+
+    if(UNIX)
+        set (LIBRARIES "${API_LOWERCASE}")
+    endif()
+
+    if (BUILD_WSI_XCB_SUPPORT)
+        find_package(XCB REQUIRED)
+
+        include_directories(${XCB_INCLUDE_DIRS})
+        link_libraries(${XCB_LIBRARIES})
+    endif()
+    if (BUILD_WSI_XLIB_SUPPORT)
+        find_package(X11 REQUIRED)
+
+        include_directories(${X11_INCLUDE_DIRS})
+        link_libraries(${X11_LIBRARIES})
+    endif()
+    if (BUILD_WSI_WAYLAND_SUPPORT)
+        find_package(Wayland REQUIRED)
+
+        include_directories(${WAYLAND_CLIENT_INCLUDE_DIR})
+        link_libraries(${WAYLAND_CLIENT_LIBRARIES})
+    endif()
+
+    # Disable the RPATH for VIA because we want it to use
+    # the environment setup by the user
+    SET(CMAKE_SKIP_BUILD_RPATH  TRUE)
+
+    link_libraries(${API_LOWERCASE} m)
+
+endif()
+
+add_executable(via via.cpp ${JSONCPP_SOURCE_DIR}/jsoncpp.cpp)
+target_include_directories(via PUBLIC ${JSONCPP_INCLUDE_DIR})
+target_link_libraries(via ${LIBRARIES})
+if(WIN32)
+    target_link_libraries(via version)
+endif()
diff --git a/via/README.md b/via/README.md
new file mode 100644
index 0000000..03dc318
--- /dev/null
+++ b/via/README.md
@@ -0,0 +1,371 @@
+#![LunarG's Vulkan Installation Analyzer (VIA)](images/lunarg_via_title.png)
+This document is an overview of how to use the [LunarG Vulkan Installation Analyzer (VIA)](https://vulkan.lunarg.com/doc/sdk/latest/windows/via.html).
+VIA is a tool that can:
+ 1. Determine the state of Vulkan components on your system
+ 2. Validate that your Vulkan Loader and drivers are installed properly
+ 3. Capture your system state in a form that can be used as an attachment when submitting bugs
+
+ This document describes where to find the source for VIA, building it, runnning it, and how to understand the resulting command line output that is generated.
+
+<BR />
+
+
+## Building
+Many components of the LunarG Vulkan SDK are Open Source, including VIA.  VIA is currently part of the LunarG
+[VulkanTools](https://github.com/LunarG/VulkanTools) GitHub respository.
+
+**Windows Note:** VIA is already pre-built as part of the LunarG Windows Vulkan SDK, but should you wish to build a
+debug version or find the source, this section should provide you with the information you need.  Otherwise, simply
+skip down to the "Running" section below.
+
+#### Building VIA in VulkanTools
+Because it is part of a group of tools, you build it from the top folder by
+following the instructions in the [BuilidVT.md](https://github.com/LunarG/VulkanTools/blob/master/BUILDVT.md)
+file at the top of the source tree.
+
+#### Building VIA in the Linux Vulkan SDK
+The source for VIA can also be found in the LunarG Linux [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) in the "source/via" directory.
+ 1. Download and install the Linux SDK
+ 2. Run "source setup-env.sh" in the SDK root directory
+ 3. Run "./build_tools.sh"
+
+<BR />
+
+## Running
+You will find the VIA binary in a different location depending on which OS you are using and whether you have built it, or installed it as part of the SDK.  The following information states where to find the proper executable.
+
+Please note that if you are trying to diagnose a troublesome application, the **best way** to run VIA to assist in diagnosis is to change to the location of the application, and run via in that folder locally (by typing in a relative or absolute path to the via executable).
+
+#### In the Windows Vulkan SDK
+VIA is installed into your start menu as part of the Windows Vulkan SDK.  Simply open your start menu, search for the "Vulkan SDK" and click "via".  This will output the resulting via.html directly to your desktop.
+
+If you need to run via from the command-line, you will find it in your SDK folder (defined by the environment variable "VULKAN_SDK") under the "Bin" folder for 64-bit executables, and "Bin32" folder for 32-bit executables.  From there, simply run:
+```
+via.exe
+```
+
+#### In the Linux Vulkan SDK
+Once built, VIA can be found in the x86_64/bin directory.  You can simply execute it from there using:
+
+```
+via
+```
+
+<BR />
+
+
+#### If Built from VulkanTools
+Go into the folder where you generated the build items from the above building step.
+
+**Linux**
+
+Simply run:
+```
+via
+```
+
+
+**Windows**
+
+Windows has an additional required step to perform if you are building from VulkanTools before you can run VIA in the proper way.  This is because the CMake scripts have an important folder being copied one level too low.  To run, the first time you will have to copy this folder into the appropriate location.
+
+Steps to run the first time:
+ 1. Go into the folder you built, and then go into the "via" sub-folder.
+ 2. In this folder you should see an "images" folder.  Copy this into either or both of your "Debug" or "Release" folders.
+ 3. Go into the "Debug" or "Release" folder (whichever you are desiring to work with) 
+ 4. Run 
+```
+via.exe
+```
+
+After the first time, you just need to go into the folder and re-run "via.exe".
+
+<BR />
+
+### Resulting Output
+VIA outputs two things:
+ - A command-line output indicating the overall status
+ - An HTML file (called via.html) containing the details which will be output to one of two locations:
+  1. If the current directory is writeable, the HTML will be placed in that location.
+  2. Otherwise, it will be saved to your home folder, except for the Windows Start Menu short-cut which writes the file to your desktop.
+
+Your home folder is the following location (based on your OS):
+ - Windows: Wherever your environment variables %HOMEDRIVE%\%HOMEPATH% point to.
+ - Linux: It will be placed in your home folder ("~/.").
+
+<BR />
+
+#### Additional command-line arguments
+There are additional command-line parameters which can be used.  These simply augment existing behavior and do not capture any more information.
+The available command-line arguments are:
+
+##### --unique_output
+The --unique_output argument, if provided, will cause the output html to be generated with a date/time suffix.  This will allow you to perform
+multiple state captures on your system without accidentally erasing previous results.  The new file has the following format:
+
+_via_YYYY_MM_DD_HH_MM.html_
+
+Where each component stands for the numeric values for year (YYYY), month (MM), day (DD), hour (HH), and minute (MM).
+
+#### --output_path
+The --output_path arument allows the user to specify a location for the output html file. For
+example, if the user runs `via --output_path /home/me/Documents`, then the output file will be
+`/home/me/Documents/via.html`.
+
+<BR />
+
+## Common Command-Line Outputs
+
+#### "SUCCESS: Validation completed properly"
+
+##### Problem:
+LunarG's VIA could detect no problems with your setup.
+
+##### Possible Reason:
+Your system is likely setup properly.  If you have trouble running Vulkan from another location, it could be that your environment variables aren't setup properly.
+
+##### Next Step:
+Re-run VIA from the location your Vulkan application/game is supposed to run.
+
+
+#### "ERROR: Failed to find Vulkan Driver JSON in registry"
+
+##### Problem:
+This is a Windows-specific error that indicates that no Vulkan Driver JSON files were referenced in the appropriate place in the Windows Registry.
+
+##### Possible Reason:
+This can indicate that a Vulkan driver failed to install properly.
+
+##### Next Step:
+See the [Vulkan Graphics Driver Problems](#vulkan-graphics-driver-problems) section for more info.
+
+
+#### "ERROR: Failed to find Vulkan Driver JSON"
+
+##### Problem:
+The Vulkan loader on your system failed to find any Vulkan Driver JSON files in the appropriate places.
+
+##### Possible Reason:
+This can indicate that a Vulkan driver failed to install properly.
+
+##### Next Step:
+See the [Vulkan Graphics Driver Problems](#vulkan-graphics-driver-problems) section for more info.
+
+
+#### "ERROR: Failed to properly parse Vulkan Driver JSON"
+
+##### Problem:
+The Vulkan loader on your system found at least one Vulkan Driver JSON file, but it contained invalid data.
+
+##### Possible Reason:
+This can indicate that a Vulkan driver failed to install properly.
+
+##### Next Step:
+See the [Vulkan Graphics Driver Problems](#vulkan-graphics-driver-problems) section for more info.
+
+
+#### "ERROR: Failed to find Vulkan Driver Lib"
+
+##### Problem:
+The Vulkan loader on your system found at least one Vulkan Driver JSON file, but the Driver library referenced in that file appears invalid.
+
+##### Possible Reason:
+This can indicate that a Vulkan driver failed to install properly.
+
+##### Next Step:
+See the [Vulkan Graphics Driver Problems](#vulkan-graphics-driver-problems) section for more info.
+
+
+#### "ERROR: Failed to find Vulkan Layer JSON"
+
+##### Problem:
+Normally a layer isn't a requirement, but in this case a layer was attempted to be used by VIA, which should have existed, and did not exist
+in the appropriate location.
+
+##### Possible Reason:
+If the layer is associated with a driver vendor (for example Nvidia), it likely failed during the driver install.  If the layer is associated
+with an application (for example RenderDoc), then it likely failed during application install.  Otherwise, it could be an invalid runtime
+or SDK install.
+
+##### Next Step:
+Look at the HTML file to determine which layer caused the problem, and then contact the appropriate person.  If it's an SDK layer, or you
+are having a hard time figuring out exactly what the problem is, follow the instructions in the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### "ERROR: Failed to properly parse Vulkan Layer JSON"
+
+##### Problem:
+Normally a layer isn't a requirement, but in this case a layer was attempted to be used by VIA, and the JSON file was found to be invalid.
+
+##### Possible Reason:
+If the layer is associated with a driver vendor (for example Nvidia), it likely failed during the driver install.  If the layer is associated
+with an application (for example RenderDoc), then it likely failed during application install.  Otherwise, it could be an invalid runtime
+or SDK install.
+
+##### Next Step:
+Look at the HTML file to determine which layer caused the problem, and then contact the appropriate person.  If it's an SDK layer, or you
+are having a hard time figuring out exactly what the problem is, follow the instructions in the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### "ERROR: Failed to find Vulkan Layer Lib"
+
+##### Problem:
+Normally a layer isn't a requirement, but in this case a layer was attempted to be used by VIA, and the JSON file was found to point to an
+invalid layer library file.
+
+##### Possible Reason:
+If the layer is associated with a driver vendor (for example Nvidia), it likely failed during the driver install.  If the layer is associated
+with an application (for example RenderDoc), then it likely failed during application install.  Otherwise, it could be an invalid runtime
+or SDK install.
+
+##### Next Step:
+Look at the HTML file to determine which layer caused the problem, and then contact the appropriate person.  If it's an SDK layer, or you
+are having a hard time figuring out exactly what the problem is, follow the instructions in the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+
+#### "ERROR: Vulkan failed to find a compatible driver"
+
+##### Problem:
+All the components appeared to be in place to allow Vulkan to load (from an external stand-point), but the loader still failed to find
+a Vulkan compatible driver.
+
+##### Possible Reason:
+This can indicate that either a Vulkan driver failed to install properly, or the run-time is failing for some reason.
+
+##### Next Step:
+See the [Vulkan Graphics Driver Problems](#vulkan-graphics-driver-problems) section for more info.
+
+
+#### "ERROR: Vulkan failed to find a Vulkan Runtime to use"
+
+##### Problem:
+The Vulkan loader "vulkan-1.dll" couldn't be found on your system.  This file is typically installed with some Vulkan driver installs,
+some Vulkan-capable games, or the LunarG Vulkan SDK.
+
+##### Possible Reason:
+The last Vulkan Runtime install that executed on your system failed to behave properly.  Or, you have never installed a Vulkan loader
+on your system.
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### "ERROR: Unknown error while attempting to create Vulkan Instance"
+
+##### Problem:
+Some error occurred when trying to call Vulkan's vkCreateInstance function.
+
+##### Possible Reason:
+This can usually indicate that there's something about the installed driver that the Vulkan Loader does not like.
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### "ERROR: Unknown error while attempting to create Vulkan Device"
+
+##### Problem:
+Some error occurred when trying to call Vulkan's vkCreateDevice function.
+
+##### Possible Reason:
+This can usually indicate that there's something about the installed driver that the Vulkan Loader does not like.
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### "ERROR: Failed to find expected Vulkan Extensions"
+
+##### Problem:
+Some error occurred when trying to query the available drivers for the Vulkan Extensions that are supported.
+
+##### Possible Reason:
+This can usually indicate that there's something about the installed driver that the Vulkan Loader does not like.
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### "ERROR: Vulkan Loader, Layer, or Driver ran out of memory"
+
+##### Problem:
+The Vulkan Loader, a Layer, or a Vulkan Driver failed to be able to allocate memory for a required item.
+
+##### Possible Reason:
+Check to see how much memory is available on your system.  It could also be caused by low hard-drive space.
+
+##### Next Step:
+Attempt to free up some hard-drive space if you're currently using more that 90% of your hard-drive.
+
+Other things to try:
+ * Follow the instructions in [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+ * If that doesn't help, try [installing a new Vulkan Driver](#vulkan-graphics-driver-problems)
+
+ 
+#### "ERROR: Unknown Test failure occurred"
+
+##### Problem:
+One of the Vulkan tests failed for some reason.
+
+##### Possible Reason:
+This could just indicate an error in setting up the test environment.  Currently, the only tests used are in the SDK, so
+it's possible the SDK did not install properly.
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### [WINDOWS] Dialog box pop's up indicating "vulkan-1.dll is missing from your computer."
+
+##### Problem:
+The Vulkan loader "vulkan-1.dll" couldn't be found on your system.  This file is typically installed with some Vulkan driver installs,
+some Vulkan-capable games, or the LunarG Vulkan SDK.
+
+##### Possible Reason:
+The last Vulkan Runtime install that executed on your system failed to behave properly.  Or, you have never installed a Vulkan loader
+on your system.
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+
+#### [LINUX] Message indicating "error while loading shared libraries: libvulkan.so.1"
+
+##### Problem:
+The Vulkan loader "libvulkan.so.1" couldn't be found on your system.  This file is typically installed with some Vulkan driver installs,
+some Vulkan-capable games, or the LunarG Vulkan SDK.
+
+##### Possible Reason:
+ 1. There is no Vulkan loader installed to a system folder.
+ 2. The user didn't set up the LD_LIBRARY_PATH environment variable to point to a local folder containing libvulkan.so.1.
+```
+ export LD_LIBRARY_PATH=/home/<user>/sdk/VulkanSDK/1.0.39.0/x64_86/lib
+```
+
+
+##### Next Step:
+See the [Vulkan SDK Issues](#vulkan-sdk-issues) section below.
+
+<BR />
+
+
+## Vulkan SDK Issues
+If the problem you've encountered is possibly related to an SDK issue.  Visit [LunarXchange](https://vulkan.lunarg.com/), and install
+the latest Vulkan SDK.  If that does not help, attempt to install a new [Vulkan Driver](#vulkan-graphics-driver-problems).  If that still
+fails, file an issue on [LunarXchange](https://vulkan.lunarg.com/).  To file an issue, you may be required to create a free account
+(only requires an email address).
+
+<BR />
+
+
+## Vulkan Graphics Driver Problems
+If the problem is possibly related to your Graphics Driver, it could be for several reasons:
+ 1. The hardware you have doesn't support Vulkan.
+ 2. Your hardware supports Vulkan, but you haven't yet installed a driver with Vulkan support.
+ 3. There is no Vulkan driver with support for the OS on which you are currently running.
+    - Sometimes, the company may provide Vulkan support for some devices on one Operatings System (say Windows), while still waiting to complete Vulkan on other systems.
+ 4. Everything supports Vulkan, but the driver failed to install properly.
+
+Before approaching your Graphics driver vendor, it would help if you verified that your current driver for your current hardware on your current
+operating system **does** support Vulkan.
diff --git a/via/images/bg-starfield.jpg b/via/images/bg-starfield.jpg
new file mode 100644
index 0000000..946be81
--- /dev/null
+++ b/via/images/bg-starfield.jpg
Binary files differ
diff --git a/via/images/lunarg_via.png b/via/images/lunarg_via.png
new file mode 100644
index 0000000..21778e7
--- /dev/null
+++ b/via/images/lunarg_via.png
Binary files differ
diff --git a/via/images/lunarg_via_title.png b/via/images/lunarg_via_title.png
new file mode 100644
index 0000000..e498b19
--- /dev/null
+++ b/via/images/lunarg_via_title.png
Binary files differ
diff --git a/via/via.cpp b/via/via.cpp
new file mode 100644
index 0000000..a23b9ec
--- /dev/null
+++ b/via/via.cpp
@@ -0,0 +1,5069 @@
+/*
+ * Copyright (c) 2016 Valve Corporation
+ * Copyright (c) 2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Mark Young <marky@lunarg.com>
+ */
+
+#include <cstring>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <time.h>
+#include <inttypes.h>
+
+const char APP_VERSION[] = "Version 1.1";
+#define MAX_STRING_LENGTH 1024
+
+#ifdef _WIN32
+#pragma warning(disable : 4996)
+#include "shlwapi.h"
+#else
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <sys/utsname.h>
+#include <dirent.h>
+#include <unistd.h>
+#endif
+
+#include <json/json.h>
+
+#include <vulkan/vulkan.h>
+
+#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) || defined MINGW_HAS_SECURE_API
+#include <basetsd.h>
+#define snprintf sprintf_s
+#endif
+
+enum ElementAlign { ALIGN_LEFT = 0, ALIGN_CENTER, ALIGN_RIGHT };
+
+struct PhysicalDeviceInfo {
+    VkPhysicalDevice vulkan_phys_dev;
+    std::vector<VkQueueFamilyProperties> queue_fam_props;
+};
+
+struct GlobalItems {
+    std::ofstream html_file_stream;
+    bool sdk_found;
+    std::string sdk_path;
+    VkInstance instance;
+    std::vector<PhysicalDeviceInfo> phys_devices;
+    std::vector<VkDevice> log_devices;
+    uint32_t cur_table;
+    std::string exe_directory;
+    bool is_odd_row;
+
+#ifdef _WIN32
+    bool is_wow64;
+#endif
+};
+
+// Create a global variable used to store the global settings
+GlobalItems global_items = {};
+
+// Error messages
+enum ErrorResults {
+    SUCCESSFUL = 0,
+
+    UNKNOWN_ERROR = -1,
+    SYSTEM_CALL_FAILURE = -2,
+
+    MISSING_DRIVER_REGISTRY = -20,
+    MISSING_DRIVER_JSON = -21,
+    DRIVER_JSON_PARSING_ERROR = -22,
+    MISSING_DRIVER_LIB = -23,
+    MISSING_LAYER_JSON = -24,
+    LAYER_JSON_PARSING_ERROR = -25,
+    MISSING_LAYER_LIB = -26,
+
+    VULKAN_CANT_FIND_RUNTIME = -40,
+    VULKAN_CANT_FIND_DRIVER = -41,
+    VULKAN_CANT_FIND_EXTENSIONS = -42,
+    VULKAN_FAILED_CREATE_INSTANCE = -43,
+    VULKAN_FAILED_CREATE_DEVICE = -44,
+    VULKAN_FAILED_OUT_OF_MEM = -45,
+
+    TEST_FAILED = -60,
+};
+
+// Structure used to store name/value pairs read from the
+// Vulkan layer settings file (if one exists).
+struct SettingPair {
+    std::string name;
+    std::string value;
+};
+
+void StartOutput(std::string title);
+void EndOutput();
+ErrorResults PrintSystemInfo(void);
+ErrorResults PrintVulkanInfo(void);
+ErrorResults PrintDriverInfo(void);
+ErrorResults PrintRunTimeInfo(void);
+ErrorResults PrintSDKInfo(void);
+void PrintExplicitLayerJsonInfo(const char *layer_json_filename,
+                                Json::Value root, uint32_t num_cols);
+void PrintImplicitLayerJsonInfo(const char *layer_json_filename,
+                                Json::Value root);
+ErrorResults PrintLayerInfo(void);
+ErrorResults PrintLayerSettingsFileInfo(void);
+ErrorResults PrintTestResults(void);
+std::string TrimWhitespace(const std::string &str,
+                           const std::string &whitespace = " \t\n\r");
+
+int main(int argc, char **argv) {
+    int err_val = 0;
+    time_t time_raw_format;
+    struct tm *ptr_time;
+    char html_file_name[MAX_STRING_LENGTH];
+    char full_file[MAX_STRING_LENGTH];
+    char temp[MAX_STRING_LENGTH];
+    const char *output_path = NULL;
+    bool generate_unique_file = false;
+    ErrorResults res = SUCCESSFUL;
+    size_t file_name_offset = 0;
+#ifdef _WIN32
+    int bytes;
+#elif __GNUC__
+    ssize_t len;
+#endif
+
+    // Check and handle command-line arguments
+    if (argc > 1) {
+        for (int iii = 1; iii < argc; iii++) {
+            if (0 == strcmp("--unique_output", argv[iii])) {
+                generate_unique_file = true;
+            } else if (0 == strcmp("--output_path", argv[iii]) &&
+                       argc > (iii + 1)) {
+                output_path = argv[iii + 1];
+                ++iii;
+            } else {
+                std::cout
+                    << "Usage of via.exe:" << std::endl
+                    << "    via.exe [--unique_output] "
+                       "[--output_path <path>]"
+                    << std::endl
+                    << "          [--unique_output] Optional "
+                       "parameter to generate a unique html"
+                    << std::endl
+                    << "                            "
+                       "output file in the form "
+                       "\'via_YYYY_MM_DD_HH_MM.html\'"
+                    << std::endl
+                    << "          [--output_path <path>"
+                       "] Optional parameter to generate the output at"
+                    << std::endl
+                    << "                               "
+                       "  a given path"
+                    << std::endl;
+                goto out;
+            }
+        }
+    }
+
+    // If the user wants a specific output path, write it to the buffer
+    // and then continue writing the rest of the name below
+    if (output_path != NULL) {
+        file_name_offset = strlen(output_path) + 1;
+        strncpy(html_file_name, output_path, MAX_STRING_LENGTH - 1);
+#ifdef _WIN32
+        strncpy(html_file_name + file_name_offset - 1, "\\",
+                MAX_STRING_LENGTH - file_name_offset);
+#else
+        strncpy(html_file_name + file_name_offset - 1, "/",
+                MAX_STRING_LENGTH - file_name_offset);
+#endif
+    }
+
+    // If the user wants a unique file, generate a file with the current
+    // time and date incorporated into it.
+    if (generate_unique_file) {
+        time(&time_raw_format);
+        ptr_time = localtime(&time_raw_format);
+        if (strftime(html_file_name + file_name_offset,
+                     MAX_STRING_LENGTH - 1, "via_%Y_%m_%d_%H_%M.html",
+                     ptr_time) == 0) {
+            std::cerr << "Couldn't prepare formatted string" << std::endl;
+            goto out;
+        }
+    } else {
+        strncpy(html_file_name + file_name_offset, "via.html",
+                MAX_STRING_LENGTH - 1 - file_name_offset);
+    }
+
+    // Write the output file to the current executing directory, or, if
+    // that fails, write it out to the user's home folder.
+    global_items.html_file_stream.open(html_file_name);
+    if (global_items.html_file_stream.fail()) {
+#ifdef _WIN32
+        char home_drive[32];
+        if (0 != GetEnvironmentVariableA("HOMEDRIVE", home_drive, 31) ||
+            0 != GetEnvironmentVariableA("HOMEPATH", temp,
+                                         MAX_STRING_LENGTH - 1)) {
+            std::cerr << "Error failed to get either HOMEDRIVE or HOMEPATH "
+                         "from environment settings!"
+                      << std::endl;
+            goto out;
+        }
+        snprintf(full_file, MAX_STRING_LENGTH - 1, "%s%s\\%s", home_drive,
+                 temp, html_file_name);
+#else
+        snprintf(full_file, MAX_STRING_LENGTH - 1, "~/%s", html_file_name);
+#endif
+        global_items.html_file_stream.open(full_file);
+        if (global_items.html_file_stream.fail()) {
+            std::cerr << "Error failed opening html file stream to "
+                         "either current"
+                         " folder as "
+                      << html_file_name << " or home folder as "
+                      << full_file << std::endl;
+            goto out;
+        }
+    }
+
+    global_items.cur_table = 0;
+
+// Determine where we are executing at.
+#ifdef _WIN32
+    bytes = GetModuleFileName(NULL, temp, MAX_STRING_LENGTH - 1);
+    if (0 < bytes) {
+        std::string exe_location = temp;
+        global_items.exe_directory =
+            exe_location.substr(0, exe_location.rfind("\\"));
+
+        size_t index = 0;
+        while (true) {
+            index = global_items.exe_directory.find("\\", index);
+            if (index == std::string::npos) {
+                break;
+            }
+            global_items.exe_directory.replace(index, 1, "/");
+            index++;
+        }
+    } else {
+        global_items.exe_directory = "";
+    }
+
+#elif __GNUC__
+    len = ::readlink("/proc/self/exe", temp, MAX_STRING_LENGTH - 1);
+    if (0 < len) {
+        std::string exe_location = temp;
+        global_items.exe_directory =
+            exe_location.substr(0, exe_location.rfind("/"));
+    } else {
+        global_items.exe_directory = "";
+    }
+#endif
+
+    StartOutput("LunarG VIA");
+
+    res = PrintSystemInfo();
+    if (res != SUCCESSFUL) {
+        goto out;
+    }
+    res = PrintVulkanInfo();
+    if (res != SUCCESSFUL) {
+        goto out;
+    }
+    res = PrintTestResults();
+    EndOutput();
+
+out:
+
+    // Print out a useful message for any common errors.
+    switch (res) {
+    case SUCCESSFUL:
+        std::cout << "SUCCESS: Validation completed properly." << std::endl;
+        break;
+    case SYSTEM_CALL_FAILURE:
+        std::cout << "ERROR: Failure occurred during system call." << std::endl;
+        break;
+    case MISSING_DRIVER_REGISTRY:
+        std::cout << "ERROR: Failed to find Vulkan Driver JSON in registry."
+                  << std::endl;
+        break;
+    case MISSING_DRIVER_JSON:
+        std::cout << "ERROR: Failed to find Vulkan Driver JSON."
+                  << std::endl;
+        break;
+    case DRIVER_JSON_PARSING_ERROR:
+        std::cout << "ERROR: Failed to properly parse Vulkan Driver JSON."
+                  << std::endl;
+        break;
+    case MISSING_DRIVER_LIB:
+        std::cout << "ERROR: Failed to find Vulkan Driver Lib." << std::endl;
+        break;
+    case MISSING_LAYER_JSON:
+        std::cout << "ERROR: Failed to find Vulkan Layer JSON."
+                  << std::endl;
+        break;
+    case LAYER_JSON_PARSING_ERROR:
+        std::cout << "ERROR: Failed to properly parse Vulkan Layer JSON."
+                  << std::endl;
+        break;
+    case MISSING_LAYER_LIB:
+        std::cout << "ERROR: Failed to find Vulkan Layer Lib." << std::endl;
+        break;
+    case VULKAN_CANT_FIND_RUNTIME:
+        std::cout << "ERROR: Vulkan failed to find a Vulkan Runtime to use."
+                  << std::endl;
+        break;
+    case VULKAN_CANT_FIND_DRIVER:
+        std::cout << "ERROR: Vulkan failed to find a compatible driver."
+                  << std::endl;
+        break;
+    case VULKAN_CANT_FIND_EXTENSIONS:
+        std::cout << "ERROR: Failed to find expected Vulkan Extensions."
+                  << "  This may indicate a bad driver install." << std::endl;
+        break;
+    case VULKAN_FAILED_CREATE_INSTANCE:
+        std::cout << "ERROR: Unknown error while attempting to create Vulkan Instance."
+                  << std::endl;
+        break;
+    case VULKAN_FAILED_CREATE_DEVICE:
+        std::cout << "ERROR: Unknown error while attempting to create Vulkan Device."
+                  << std::endl;
+        break;
+    case VULKAN_FAILED_OUT_OF_MEM:
+        std::cout << "ERROR: Vulkan Loader, Layer, or Driver ran out of memory."
+                  << std::endl;
+        break;
+    case TEST_FAILED:
+        std::cout << "ERROR: Unknown Test failure occurred." << std::endl;
+        break;
+    case UNKNOWN_ERROR:
+    default:
+        std::cout << "ERROR: Uknown failure occurred.  Refer to HTML for "
+                     "more info"
+                  << std::endl;
+        break;
+    }
+    err_val = static_cast<int>(res);
+
+    global_items.html_file_stream.close();
+
+    return err_val;
+}
+
+// Output helper functions:
+//=============================
+
+// Start writing to the HTML file by creating the appropriate
+// header information including the appropriate CSS and JavaScript
+// items.
+void StartOutput(std::string output) {
+    global_items.html_file_stream << "<!DOCTYPE html>" << std::endl;
+    global_items.html_file_stream << "<HTML lang=\"en\" xml:lang=\"en\" "
+                                     "xmlns=\"http://www.w3.org/1999/xhtml\">"
+                                  << std::endl;
+    global_items.html_file_stream << std::endl
+                                  << "<HEAD>" << std::endl
+                                  << "    <TITLE>" << output << "</TITLE>"
+                                  << std::endl;
+
+    global_items.html_file_stream
+        << "    <META charset=\"UTF-8\">" << std::endl
+        << "    <style media=\"screen\" type=\"text/css\">" << std::endl
+        << "        html {" << std::endl
+        // By defining the color first, this won't override the background image
+        // (unless the images aren't there).
+        << "            background-color: #0b1e48;" << std::endl
+        // The following changes try to load the text image twice (locally, then
+        // off the web) followed by the background image twice (locally, then
+        // off the web).  The background color will only show if both background
+        // image loads fail.  In this way, a user will see their local copy on
+        // their machine, while a person they share it with will see the web
+        // images (or the background color).
+        << "            background-image: url(\"file:///"
+        << global_items.exe_directory << "/images/lunarg_via.png\"), "
+        << "url(\"https://vulkan.lunarg.com/img/lunarg_via.png\"), "
+           "url(\"file:///"
+        << global_items.exe_directory << "/images/bg-starfield.jpg\"), "
+        << "url(\"https://vulkan.lunarg.com/img/bg-starfield.jpg\");"
+        << std::endl
+        << "            background-position: center top, center top, center, "
+           "center;"
+        << std::endl
+        << "            -webkit-background-size: auto, auto, cover, cover;"
+        << std::endl
+        << "            -moz-background-size: auto, auto, cover, cover;"
+        << std::endl
+        << "            -o-background-size: auto, auto, cover, cover;"
+        << std::endl
+        << "            background-size: auto, auto, cover, cover;" << std::endl
+        << "            background-attachment: scroll, scroll, fixed, fixed;"
+        << std::endl
+        << "            background-repeat: no-repeat, no-repeat, no-repeat, "
+           "no-repeat;"
+        << std::endl
+        << "        }" << std::endl
+        // h1.section is used for section headers, and h1.version is used to
+        // print out the application version text (which shows up just under
+        // the title).
+        << "        h1.section {" << std::endl
+        << "            font-family: sans-serif;" << std::endl
+        << "            font-size: 35px;" << std::endl
+        << "            color: #FFFFFF;" << std::endl
+        << "        }" << std::endl
+        << "        h1.version {" << std::endl
+        << "            font-family: sans-serif;" << std::endl
+        << "            font-size: 25px;" << std::endl
+        << "            color: #FFFFFF;" << std::endl
+        << "        }" << std::endl
+        << "        h2.note {" << std::endl
+        << "            font-family: sans-serif;" << std::endl
+        << "            font-size: 22px;" << std::endl
+        << "            color: #FFFFFF;" << std::endl
+        << "        }" << std::endl
+        << "        table {" << std::endl
+        << "            min-width: 600px;" << std::endl
+        << "            width: 70%;" << std::endl
+        << "            border-collapse: collapse;" << std::endl
+        << "            border-color: grey;" << std::endl
+        << "            font-family: sans-serif;" << std::endl
+        << "        }" << std::endl
+        << "        td.header {" << std::endl
+        << "            padding: 18px;" << std::endl
+        << "            border: 1px solid #ccc;" << std::endl
+        << "            font-size: 18px;" << std::endl
+        << "            color: #fff;" << std::endl
+        << "        }" << std::endl
+        << "        td.odd {" << std::endl
+        << "            padding: 10px;" << std::endl
+        << "            border: 1px solid #ccc;" << std::endl
+        << "            font-size: 16px;" << std::endl
+        << "            color: rgb(255, 255, 255);" << std::endl
+        << "        }" << std::endl
+        << "        td.even {" << std::endl
+        << "            padding: 10px;" << std::endl
+        << "            border: 1px solid #ccc;" << std::endl
+        << "            font-size: 16px;" << std::endl
+        << "            color: rgb(220, 220, 220);" << std::endl
+        << "        }" << std::endl
+        << "        tr.header {" << std::endl
+        << "            background-color: rgba(255,255,255,0.5);" << std::endl
+        << "        }" << std::endl
+        << "        tr.odd {" << std::endl
+        << "            background-color: rgba(0,0,0,0.6);" << std::endl
+        << "        }" << std::endl
+        << "        tr.even {" << std::endl
+        << "            background-color: rgba(0,0,0,0.7);" << std::endl
+        << "        }" << std::endl
+        << "    </style>" << std::endl
+        << "    <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/"
+        << "2.2.4/jquery.min.js\"></script>" << std::endl
+        << "    <script type=\"text/javascript\">" << std::endl
+        << "        $( document ).ready(function() {" << std::endl
+        << "            $('table tr:not(.header)').hide();" << std::endl
+        << "            $('.header').click(function() {" << std::endl
+        << "                "
+           "$(this).nextUntil('tr.header').slideToggle(300);"
+        << std::endl
+        << "            });" << std::endl
+        << "        });" << std::endl
+        << "    </script>" << std::endl
+        << "</HEAD>" << std::endl
+        << std::endl
+        << "<BODY>" << std::endl
+        << std::endl;
+    // We need space from the top for the VIA texture
+    for (uint32_t space = 0; space < 15; space++) {
+        global_items.html_file_stream << "    <BR />" << std::endl;
+    }
+    // All the silly "&nbsp;" are to make sure the version lines up directly
+    // under the  VIA portion of the log.
+    global_items.html_file_stream << "    <H1 class=\"version\"><center>";
+    for (uint32_t space = 0; space < 65; space++) {
+        global_items.html_file_stream << "&nbsp;";
+    }
+    global_items.html_file_stream << APP_VERSION << "</center></h1>"
+                                  << std::endl
+                                  << "    <BR />" << std::endl;
+
+    global_items.html_file_stream
+        << "<center><h2 class=\"note\">< NOTE: Click on section name to expand "
+           "table ></h2></center>"
+        << std::endl
+        << "    <BR />" << std::endl;
+}
+
+// Close out writing to the HTML file.
+void EndOutput() {
+    global_items.html_file_stream << "</BODY>" << std::endl
+                                  << std::endl
+                                  << "</HTML>" << std::endl;
+}
+
+void BeginSection(std::string section_str) {
+    global_items.html_file_stream << "    <H1 class=\"section\"><center>"
+                                  << section_str << "</center></h1>"
+                                  << std::endl;
+}
+
+void EndSection() {
+    global_items.html_file_stream << "    <BR/>" << std::endl
+                                  << "    <BR/>" << std::endl;
+}
+
+void PrintStandardText(std::string section) {
+    global_items.html_file_stream << "    <H2><font color=\"White\">" << section
+                                  << "</font></H2>" << std::endl;
+}
+
+void PrintBeginTable(const char *table_name, uint32_t num_cols) {
+
+    global_items.html_file_stream
+        << "    <table align=\"center\">" << std::endl
+        << "        <tr class=\"header\">" << std::endl
+        << "            <td colspan=\"" << num_cols << "\" class=\"header\">"
+        << table_name << "</td>" << std::endl
+        << "         </tr>" << std::endl;
+
+    global_items.is_odd_row = true;
+}
+
+void PrintBeginTableRow() {
+    std::string class_str = "";
+    if (global_items.is_odd_row) {
+        class_str = " class=\"odd\"";
+    } else {
+        class_str = " class=\"even\"";
+    }
+    global_items.html_file_stream << "        <tr" << class_str << ">"
+                                  << std::endl;
+}
+
+void PrintTableElement(std::string element, ElementAlign align = ALIGN_LEFT) {
+    std::string align_str = "";
+    std::string class_str = "";
+    if (align == ALIGN_RIGHT) {
+        align_str = " align=\"right\"";
+    } else if (align == ALIGN_CENTER) {
+        align_str = " align=\"center\"";
+    }
+    if (global_items.is_odd_row) {
+        class_str = " class=\"odd\"";
+    } else {
+        class_str = " class=\"even\"";
+    }
+    global_items.html_file_stream << "            <td" << align_str << class_str
+                                  << ">" << element << "</td>" << std::endl;
+}
+
+void PrintEndTableRow() {
+    global_items.html_file_stream << "        </tr>" << std::endl;
+    global_items.is_odd_row = !global_items.is_odd_row;
+}
+
+void PrintEndTable() {
+    global_items.html_file_stream << "    </table>" << std::endl;
+}
+
+// Generate the full library location for a file based on the location of
+// the JSON file referencing it, and the library location contained in that
+// JSON file.
+bool GenerateLibraryPath(const char *json_location, const char *library_info,
+                         const uint32_t max_length, char *library_location) {
+    bool success = false;
+    char final_path[MAX_STRING_LENGTH];
+    char *working_string_ptr;
+    uint32_t len =
+        (max_length > MAX_STRING_LENGTH) ? MAX_STRING_LENGTH : max_length;
+
+    if (NULL == json_location || NULL == library_info ||
+        NULL == library_location) {
+        goto out;
+    }
+
+    // Remove json file from json path to get just the file base location
+    strncpy(final_path, json_location, len);
+    working_string_ptr = strrchr(final_path, '\\');
+    if (working_string_ptr == NULL) {
+        working_string_ptr = strrchr(final_path, '/');
+    }
+    if (working_string_ptr != NULL) {
+        working_string_ptr++;
+        *working_string_ptr = '\0';
+    }
+
+    // Determine if the library is relative or absolute
+    if (library_info[0] == '\\' || library_info[0] == '/' ||
+        library_info[1] == ':') {
+        // Absolute path
+        strncpy(library_location, library_info, len);
+        success = true;
+    } else {
+        uint32_t i = 0;
+        // Relative path, so we need to use the JSON's location
+        while (library_info[i] == '.' && library_info[i + 1] == '.' &&
+               (library_info[i + 2] == '\\' || library_info[i + 2] == '/')) {
+            i += 3;
+            // Go up a folder in the json path
+            working_string_ptr = strrchr(final_path, '\\');
+            if (working_string_ptr == NULL) {
+                working_string_ptr = strrchr(final_path, '/');
+            }
+            if (working_string_ptr != NULL) {
+                working_string_ptr++;
+                *working_string_ptr = '\0';
+            }
+        }
+        while (library_info[i] == '.' &&
+               (library_info[i + 1] == '\\' || library_info[i + 1] == '/')) {
+            i += 2;
+        }
+        strncpy(library_location, final_path, MAX_STRING_LENGTH - 1);
+        strncat(library_location, &library_info[i], len);
+        success = true;
+    }
+
+out:
+    return success;
+}
+
+#ifdef _WIN32
+// Registry utility fuctions to simplify reading data from the
+// Windows registry.
+
+const char g_uninstall_reg_path[] =
+    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
+
+bool ReadRegKeyString(HKEY regFolder, const char *keyPath,
+                      const char *valueName, const int maxLength,
+                      char *retString) {
+    bool retVal = false;
+    DWORD bufLen = maxLength;
+    DWORD keyFlags = KEY_READ;
+    HKEY hKey;
+    LONG lret;
+
+    if (global_items.is_wow64) {
+        keyFlags |= KEY_WOW64_64KEY;
+    }
+
+    *retString = '\0';
+    lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey);
+    if (lret == ERROR_SUCCESS) {
+        lret = RegQueryValueExA(hKey, valueName, NULL, NULL, (BYTE *)retString,
+                                &bufLen);
+        if (lret == ERROR_SUCCESS) {
+            retVal = true;
+        }
+        RegCloseKey(hKey);
+    }
+
+    return retVal;
+}
+
+bool WriteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName,
+                       char *valueValue) {
+    bool retVal = false;
+    DWORD keyFlags = KEY_WRITE;
+    HKEY hKey;
+    LONG lret;
+
+    if (global_items.is_wow64) {
+        keyFlags |= KEY_WOW64_64KEY;
+    }
+
+    lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey);
+    if (lret == ERROR_SUCCESS) {
+        lret = RegSetKeyValueA(hKey, NULL, valueName, REG_SZ,
+                               (BYTE *)valueValue, (DWORD)(strlen(valueValue)));
+        if (lret == ERROR_SUCCESS) {
+            retVal = true;
+        }
+        RegCloseKey(hKey);
+    }
+
+    return retVal;
+}
+
+bool DeleteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName) {
+    bool retVal = false;
+    DWORD keyFlags = KEY_WRITE;
+    HKEY hKey;
+    LONG lret;
+
+    if (global_items.is_wow64) {
+        keyFlags |= KEY_WOW64_64KEY;
+    }
+
+    lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey);
+    if (lret == ERROR_SUCCESS) {
+        lret = RegDeleteKeyValueA(hKey, NULL, valueName);
+        if (lret == ERROR_SUCCESS) {
+            retVal = true;
+        }
+        RegCloseKey(hKey);
+    }
+
+    return retVal;
+}
+
+bool ReadRegKeyDword(HKEY regFolder, const char *keyPath, const char *valueName,
+                     unsigned int *returnInt) {
+    bool retVal = false;
+    DWORD bufLen = sizeof(DWORD);
+    DWORD keyFlags = KEY_READ;
+    HKEY hKey;
+    LONG lret;
+
+    if (global_items.is_wow64) {
+        keyFlags |= KEY_WOW64_64KEY;
+    }
+
+    *returnInt = 0;
+    lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey);
+    if (lret == ERROR_SUCCESS) {
+        lret = RegQueryValueExA(hKey, valueName, NULL, NULL, (BYTE *)returnInt,
+                                &bufLen);
+        if (lret == ERROR_SUCCESS) {
+            retVal = true;
+        }
+        RegCloseKey(hKey);
+    }
+
+    return retVal;
+}
+
+bool FindNextRegKey(HKEY regFolder, const char *keyPath, const char *keySearch,
+                    const int itemIndex, const int maxLength, char *retString) {
+    bool retVal = false;
+    DWORD bufLen = MAX_STRING_LENGTH - 1;
+    DWORD keyFlags = KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE;
+    HKEY hKey;
+    LONG lret;
+    int itemCount = 0;
+
+    if (global_items.is_wow64) {
+        keyFlags |= KEY_WOW64_64KEY;
+    }
+
+    *retString = '\0';
+    lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey);
+    if (lret == ERROR_SUCCESS) {
+        DWORD index = 0;
+        char keyName[MAX_STRING_LENGTH];
+
+        do {
+            lret = RegEnumKeyExA(hKey, index, keyName, &bufLen, NULL, NULL,
+                                 NULL, NULL);
+            if (ERROR_SUCCESS != lret) {
+                break;
+            }
+            if (strlen(keySearch) == 0 || NULL != strstr(keyName, keySearch)) {
+                if (itemIndex == itemCount) {
+                    strncpy_s(retString, maxLength, keyName, bufLen);
+                    retVal = true;
+                    break;
+                } else {
+                    itemCount++;
+                }
+            }
+            bufLen = MAX_STRING_LENGTH - 1;
+            ++index;
+        } while (true);
+    }
+
+    return retVal;
+}
+
+bool FindNextRegValue(HKEY regFolder, const char *keyPath,
+                      const char *valueSearch, const int startIndex,
+                      const int maxLength, char *retString,
+                      uint32_t *retValue) {
+    bool retVal = false;
+    DWORD bufLen = MAX_STRING_LENGTH - 1;
+    DWORD keyFlags = KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE;
+    HKEY hKey = 0;
+    LONG lret;
+
+    if (global_items.is_wow64) {
+        keyFlags |= KEY_WOW64_64KEY;
+    }
+
+    *retValue = 0;
+    *retString = '\0';
+    lret = RegOpenKeyExA(regFolder, keyPath, 0, keyFlags, &hKey);
+    if (lret == ERROR_SUCCESS) {
+        DWORD index = startIndex;
+        char valueName[MAX_STRING_LENGTH];
+
+        do {
+            DWORD type = REG_DWORD;
+            DWORD value = 0;
+            DWORD len = 4;
+            valueName[0] = '\0';
+
+            lret = RegEnumValueA(hKey, index, valueName, &bufLen, NULL, &type,
+                                 (LPBYTE)&value, &len);
+            if (ERROR_SUCCESS != lret) {
+                break;
+            }
+            if (type == REG_DWORD) {
+                *retValue = value;
+            }
+            if (strlen(valueSearch) == 0 ||
+                NULL != strstr(valueName, valueSearch)) {
+                strncpy_s(retString, maxLength, valueName, bufLen);
+                retVal = true;
+                break;
+            }
+
+            bufLen = MAX_STRING_LENGTH - 1;
+            ++index;
+        } while (true);
+    }
+
+    return retVal;
+}
+
+// Registry prototypes for Windows
+bool ReadRegKeyDword(HKEY regFolder, const char *keyPath, const char *valueName,
+                     unsigned int *returnInt);
+bool ReadRegKeyString(HKEY regFolder, const char *keyPath,
+                      const char *valueName, const int maxLength,
+                      char *retString);
+bool FindNextRegKey(HKEY regFolder, const char *keyPath, const char *keySearch,
+                    const int startIndex, const int maxLength, char *retString);
+bool FindNextRegValue(HKEY regFolder, const char *keyPath,
+                      const char *valueSearch, const int startIndex,
+                      const int maxLength, char *retString, uint32_t *retValue);
+bool WriteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName,
+                       char *valueValue);
+bool DeleteRegKeyString(HKEY regFolder, const char *keyPath, char *valueName);
+
+// Functionality to determine if this 32-bit process is running on Windows 64.
+//
+void IsWow64() {
+    typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
+
+    // IsWow64Process is not available on all supported versions of Windows.
+    // Use GetModuleHandle to get a handle to the DLL that contains the function
+    // and GetProcAddress to get a pointer to the function if available.
+
+    LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
+        GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
+
+    if (NULL != fnIsWow64Process) {
+        BOOL isWOW = FALSE;
+        if (!fnIsWow64Process(GetCurrentProcess(), &isWOW)) {
+            printf("Error : Failed to determine properly if on Win64!");
+        }
+
+        if (isWOW == TRUE) {
+            global_items.is_wow64 = true;
+        }
+    }
+}
+
+// Run the test in the specified directory with the corresponding
+// command-line arguments.
+// Returns 0 on no error, 1 if test file wasn't found, and -1
+// on any other errors.
+int RunTestInDirectory(std::string path, std::string test,
+                       std::string cmd_line) {
+    int err_code = -1;
+    char orig_dir[MAX_STRING_LENGTH];
+    orig_dir[0] = '\0';
+    if (0 != GetCurrentDirectoryA(MAX_STRING_LENGTH - 1, orig_dir) &&
+        TRUE == SetCurrentDirectoryA(path.c_str())) {
+        if (TRUE == PathFileExists(test.c_str())) {
+            err_code = system(cmd_line.c_str());
+        } else {
+            // Path to specific exe doesn't exist
+            err_code = 1;
+        }
+        SetCurrentDirectoryA(orig_dir);
+    } else {
+        // Path to test doesn't exist.
+        err_code = 1;
+    }
+    return err_code;
+}
+
+// Print out any information about the current system that we can
+// capture to ease in debugging/investigation at a later time.
+ErrorResults PrintSystemInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    OSVERSIONINFOEX os_info;
+    SYSTEM_INFO sys_info;
+    MEMORYSTATUSEX mem_stat;
+    DWORD ser_ver = 0;
+    DWORD sect_per_cluster = 0;
+    DWORD bytes_per_sect = 0;
+    DWORD num_free_cluster = 0;
+    DWORD total_num_cluster = 0;
+    char system_root_dir[MAX_STRING_LENGTH];
+    char generic_string[MAX_STRING_LENGTH];
+    char output_string[MAX_STRING_LENGTH];
+    char os_size[32];
+    std::string cur_directory;
+    std::string exe_directory;
+
+    // Determine if this 32-bit process is on Win64.
+    IsWow64();
+
+#if _WIN64
+    strncpy(os_size, " 64-bit", 31);
+#else
+    // If WOW64 support is present, then it's a 64-bit Windows
+    if (global_items.is_wow64) {
+        strncpy(os_size, " 64-bit", 31);
+    } else {
+        strncpy(os_size, " 32-bit", 31);
+    }
+#endif
+
+    BeginSection("System Info");
+
+    // Environment section has information about the OS and the
+    // execution environment.
+    PrintBeginTable("Environment", 3);
+
+    ZeroMemory(&sys_info, sizeof(SYSTEM_INFO));
+    GetSystemInfo(&sys_info);
+
+    ZeroMemory(&os_info, sizeof(OSVERSIONINFOEX));
+    os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+    ZeroMemory(&mem_stat, sizeof(MEMORYSTATUSEX));
+    mem_stat.dwLength = sizeof(MEMORYSTATUSEX);
+
+    // Since this is Windows #ifdef code, determine the version of Windows
+    // that the applciation is running on.  It's not trivial and has to
+    // refer to items queried in the above structures as well as the
+    // Windows registry.
+    if (TRUE == GetVersionEx((LPOSVERSIONINFO)(&os_info))) {
+        switch (os_info.dwMajorVersion) {
+        case 10:
+            if (os_info.wProductType == VER_NT_WORKSTATION) {
+                if (ReadRegKeyString(
+                        HKEY_LOCAL_MACHINE,
+                        "Software\\Microsoft\\Windows NT\\CurrentVersion",
+                        "ProductName", MAX_STRING_LENGTH - 1, generic_string)) {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement(generic_string);
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+
+                    if (ReadRegKeyString(
+                            HKEY_LOCAL_MACHINE,
+                            "Software\\Microsoft\\Windows NT\\CurrentVersion",
+                            "CurrentBuild", MAX_STRING_LENGTH - 1,
+                            output_string)) {
+                        PrintBeginTableRow();
+                        PrintTableElement("");
+                        PrintTableElement("Build");
+                        PrintTableElement(output_string);
+                        PrintEndTableRow();
+                        if (ReadRegKeyString(
+                                HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windo"
+                                                    "ws NT\\CurrentVersion",
+                                "BuildBranch", MAX_STRING_LENGTH - 1,
+                                output_string)) {
+                            PrintBeginTableRow();
+                            PrintTableElement("");
+                            PrintTableElement("Branch");
+                            PrintTableElement(output_string);
+                            PrintEndTableRow();
+                        }
+                    }
+                } else {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows 10 (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                }
+            } else {
+                PrintBeginTableRow();
+                PrintTableElement("Windows");
+                PrintTableElement("Windows Server 2016 (or newer)");
+                PrintTableElement(os_size);
+                PrintEndTableRow();
+            }
+            break;
+        case 6:
+            switch (os_info.dwMinorVersion) {
+            case 3:
+                if (os_info.wProductType == VER_NT_WORKSTATION) {
+                    if (ReadRegKeyString(
+                            HKEY_LOCAL_MACHINE,
+                            "Software\\Microsoft\\Windows NT\\CurrentVersion",
+                            "ProductName", MAX_STRING_LENGTH - 1,
+                            generic_string)) {
+                        PrintBeginTableRow();
+                        PrintTableElement("Windows");
+                        PrintTableElement(generic_string);
+                        PrintTableElement(os_size);
+                        PrintEndTableRow();
+
+                        if (ReadRegKeyString(
+                                HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windo"
+                                                    "ws NT\\CurrentVersion",
+                                "CurrentBuild", MAX_STRING_LENGTH - 1,
+                                output_string)) {
+                            PrintBeginTableRow();
+                            PrintTableElement("");
+                            PrintTableElement("Build");
+                            PrintTableElement(output_string);
+                            PrintEndTableRow();
+
+                            if (ReadRegKeyString(HKEY_LOCAL_MACHINE,
+                                                 "Software\\Microsoft\\Windo"
+                                                 "ws NT\\CurrentVersion",
+                                                 "BuildBranch",
+                                                 MAX_STRING_LENGTH - 1,
+                                                 output_string)) {
+                                PrintBeginTableRow();
+                                PrintTableElement("");
+                                PrintTableElement("Branch");
+                                PrintTableElement(output_string);
+                                PrintEndTableRow();
+                            }
+                        }
+                    }
+                } else {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows Server 2012 R2 (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                }
+                break;
+            case 2:
+                if (os_info.wProductType == VER_NT_WORKSTATION) {
+                    if (ReadRegKeyString(
+                            HKEY_LOCAL_MACHINE,
+                            "Software\\Microsoft\\Windows NT\\CurrentVersion",
+                            "ProductName", MAX_STRING_LENGTH - 1,
+                            generic_string)) {
+                        PrintBeginTableRow();
+                        PrintTableElement("Windows");
+                        PrintTableElement(generic_string);
+                        PrintTableElement(os_size);
+                        PrintEndTableRow();
+
+                        if (ReadRegKeyString(
+                                HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windo"
+                                                    "ws NT\\CurrentVersion",
+                                "CurrentBuild", MAX_STRING_LENGTH - 1,
+                                output_string)) {
+                            PrintBeginTableRow();
+                            PrintTableElement("");
+                            PrintTableElement("Build");
+                            PrintTableElement(output_string);
+                            PrintEndTableRow();
+                            if (ReadRegKeyString(HKEY_LOCAL_MACHINE,
+                                                 "Software\\Microsoft\\Windo"
+                                                 "ws NT\\CurrentVersion",
+                                                 "BuildBranch",
+                                                 MAX_STRING_LENGTH - 1,
+                                                 output_string)) {
+                                PrintBeginTableRow();
+                                PrintTableElement("");
+                                PrintTableElement("Branch");
+                                PrintTableElement(output_string);
+                                PrintEndTableRow();
+                            }
+                        }
+                    }
+                } else {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows Server 2012 (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                }
+                break;
+            case 1:
+                if (os_info.wProductType == VER_NT_WORKSTATION) {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows 7 (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                } else {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows Server 2008 R2 (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                }
+                break;
+            default:
+                if (os_info.wProductType == VER_NT_WORKSTATION) {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows Vista (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                } else {
+                    PrintBeginTableRow();
+                    PrintTableElement("Windows");
+                    PrintTableElement("Windows Server 2008 (or newer)");
+                    PrintTableElement(os_size);
+                    PrintEndTableRow();
+                }
+                break;
+            }
+            break;
+        case 5:
+            ser_ver = GetSystemMetrics(SM_SERVERR2);
+            switch (os_info.dwMinorVersion) {
+            case 2:
+                if ((os_info.wProductType == VER_NT_WORKSTATION) &&
+                    (sys_info.wProcessorArchitecture ==
+                     PROCESSOR_ARCHITECTURE_AMD64)) {
+                    strncpy(generic_string, "Windows XP Professional x64",
+                            MAX_STRING_LENGTH - 1);
+                } else if (os_info.wSuiteMask & VER_SUITE_WH_SERVER) {
+                    strncpy(generic_string, "Windows Home Server",
+                            MAX_STRING_LENGTH - 1);
+                } else if (ser_ver != 0) {
+                    strncpy(generic_string, "Windows Server 2003 R2",
+                            MAX_STRING_LENGTH - 1);
+                } else {
+                    strncpy(generic_string, "Windows Server 2003",
+                            MAX_STRING_LENGTH - 1);
+                }
+                PrintBeginTableRow();
+                PrintTableElement("Windows");
+                PrintTableElement(generic_string);
+                PrintTableElement(os_size);
+                PrintEndTableRow();
+                break;
+            case 1:
+                PrintBeginTableRow();
+                PrintTableElement("Windows");
+                PrintTableElement("Windows XP");
+                PrintTableElement(os_size);
+                PrintEndTableRow();
+                break;
+            case 0:
+                PrintBeginTableRow();
+                PrintTableElement("Windows");
+                PrintTableElement("Windows 2000");
+                PrintTableElement(os_size);
+                PrintEndTableRow();
+                break;
+            default:
+                PrintBeginTableRow();
+                PrintTableElement("Windows");
+                PrintTableElement("Unknown Windows OS");
+                PrintTableElement(os_size);
+                PrintEndTableRow();
+                break;
+            }
+            break;
+        }
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement("Windows");
+        PrintTableElement("Error retrieving Windows Version");
+        PrintTableElement("");
+        PrintEndTableRow();
+        res = UNKNOWN_ERROR;
+        goto out;
+    }
+
+    if (0 != GetEnvironmentVariableA("SYSTEMROOT", system_root_dir,
+                                     MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("System Root");
+        PrintTableElement(system_root_dir);
+        PrintEndTableRow();
+    }
+    if (0 != GetEnvironmentVariableA("PROGRAMDATA", generic_string,
+                                     MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Program Data");
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+    }
+    if (0 != GetEnvironmentVariableA("PROGRAMFILES", generic_string,
+                                     MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Program Files");
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+    }
+    if (0 != GetEnvironmentVariableA("PROGRAMFILES(X86)", generic_string,
+                                     MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Program Files (x86)");
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+    }
+    if (0 != GetEnvironmentVariableA("TEMP", generic_string,
+                                     MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("TEMP");
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+    }
+    if (0 !=
+        GetEnvironmentVariableA("TMP", generic_string, MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("TMP");
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+    }
+
+    PrintEndTable();
+
+    // Output whatever generic hardware information we can find out about the
+    // system.  Including how much memory and disk space is available.
+    PrintBeginTable("Hardware", 3);
+
+    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u",
+             sys_info.dwNumberOfProcessors);
+    PrintBeginTableRow();
+    PrintTableElement("CPUs");
+    PrintTableElement("Number of Logical Cores");
+    PrintTableElement(generic_string);
+    PrintEndTableRow();
+
+    switch (sys_info.wProcessorArchitecture) {
+    case PROCESSOR_ARCHITECTURE_AMD64:
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Type");
+        PrintTableElement("x86_64");
+        PrintEndTableRow();
+        break;
+    case PROCESSOR_ARCHITECTURE_ARM:
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Type");
+        PrintTableElement("ARM");
+        PrintEndTableRow();
+        break;
+    case PROCESSOR_ARCHITECTURE_IA64:
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Type");
+        PrintTableElement("IA64");
+        PrintEndTableRow();
+        break;
+    case PROCESSOR_ARCHITECTURE_INTEL:
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Type");
+        PrintTableElement("x86");
+        PrintEndTableRow();
+        break;
+    default:
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Type");
+        PrintTableElement("Unknown");
+        PrintEndTableRow();
+        break;
+    }
+
+    if (TRUE == GlobalMemoryStatusEx(&mem_stat)) {
+        if ((mem_stat.ullTotalPhys >> 40) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB",
+                     static_cast<uint32_t>(mem_stat.ullTotalPhys >> 40));
+            PrintBeginTableRow();
+            PrintTableElement("Memory");
+            PrintTableElement("Physical");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((mem_stat.ullTotalPhys >> 30) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB",
+                     static_cast<uint32_t>(mem_stat.ullTotalPhys >> 30));
+            PrintBeginTableRow();
+            PrintTableElement("Memory");
+            PrintTableElement("Physical");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((mem_stat.ullTotalPhys >> 20) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB",
+                     static_cast<uint32_t>(mem_stat.ullTotalPhys >> 20));
+            PrintBeginTableRow();
+            PrintTableElement("Memory");
+            PrintTableElement("Physical");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((mem_stat.ullTotalPhys >> 10) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB",
+                     static_cast<uint32_t>(mem_stat.ullTotalPhys >> 10));
+            PrintBeginTableRow();
+            PrintTableElement("Memory");
+            PrintTableElement("Physical");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u bytes",
+                     static_cast<uint32_t>(mem_stat.ullTotalPhys));
+            PrintBeginTableRow();
+            PrintTableElement("Memory");
+            PrintTableElement("Physical");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        }
+    }
+
+    if (TRUE == GetDiskFreeSpaceA(NULL, &sect_per_cluster, &bytes_per_sect,
+                                  &num_free_cluster, &total_num_cluster)) {
+        uint64_t bytes_free = (uint64_t)bytes_per_sect *
+                              (uint64_t)sect_per_cluster *
+                              (uint64_t)num_free_cluster;
+        uint64_t bytes_total = (uint64_t)bytes_per_sect *
+                               (uint64_t)sect_per_cluster *
+                               (uint64_t)total_num_cluster;
+        double perc_free = (double)bytes_free / (double)bytes_total;
+        if ((bytes_total >> 40) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB",
+                     static_cast<uint32_t>(bytes_total >> 40));
+            PrintBeginTableRow();
+            PrintTableElement("Disk Space");
+            PrintTableElement("Total");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((bytes_total >> 30) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB",
+                     static_cast<uint32_t>(bytes_total >> 30));
+            PrintBeginTableRow();
+            PrintTableElement("Disk Space");
+            PrintTableElement("Total");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((bytes_total >> 20) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB",
+                     static_cast<uint32_t>(bytes_total >> 20));
+            PrintBeginTableRow();
+            PrintTableElement("Disk Space");
+            PrintTableElement("Total");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((bytes_total >> 10) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB",
+                     static_cast<uint32_t>(bytes_total >> 10));
+            PrintBeginTableRow();
+            PrintTableElement("Disk Space");
+            PrintTableElement("Total");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        }
+        snprintf(output_string, MAX_STRING_LENGTH - 1, "%4.2f%%",
+                 (static_cast<float>(perc_free) * 100.f));
+        if ((bytes_free >> 40) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB",
+                     static_cast<uint32_t>(bytes_free >> 40));
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free Perc");
+            PrintTableElement(output_string);
+            PrintEndTableRow();
+        } else if ((bytes_free >> 30) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB",
+                     static_cast<uint32_t>(bytes_free >> 30));
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free Perc");
+            PrintTableElement(output_string);
+            PrintEndTableRow();
+        } else if ((bytes_free >> 20) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB",
+                     static_cast<uint32_t>(bytes_free >> 20));
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free Perc");
+            PrintTableElement(output_string);
+            PrintEndTableRow();
+        } else if ((bytes_free >> 10) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB",
+                     static_cast<uint32_t>(bytes_free >> 10));
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Free Perc");
+            PrintTableElement(output_string);
+            PrintEndTableRow();
+        }
+    }
+
+    PrintEndTable();
+
+    // Print out information about this executable.
+    PrintBeginTable("Executable", 2);
+
+    PrintBeginTableRow();
+    PrintTableElement("Exe Directory");
+    PrintTableElement(global_items.exe_directory);
+    PrintEndTableRow();
+
+    if (0 != GetCurrentDirectoryA(MAX_STRING_LENGTH - 1, generic_string)) {
+        cur_directory = generic_string;
+        PrintBeginTableRow();
+        PrintTableElement("Current Directory");
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+    } else {
+        cur_directory = "";
+    }
+
+    PrintBeginTableRow();
+    PrintTableElement("Vulkan API Version");
+    uint32_t major = VK_VERSION_MAJOR(VK_API_VERSION_1_0);
+    uint32_t minor = VK_VERSION_MINOR(VK_API_VERSION_1_0);
+    uint32_t patch = VK_VERSION_PATCH(VK_HEADER_VERSION);
+    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d", major, minor,
+             patch);
+    PrintTableElement(generic_string);
+    PrintEndTableRow();
+
+    PrintBeginTableRow();
+    PrintTableElement("Byte Format");
+#if _WIN64 || __x86_64__ || __ppc64__
+    PrintTableElement("64-bit");
+#else
+    PrintTableElement("32-bit");
+#endif
+    PrintEndTableRow();
+
+    PrintEndTable();
+
+    // Now print out the remaining system info.
+    res = PrintDriverInfo();
+    if (res != SUCCESSFUL) {
+        goto out;
+    }
+    PrintRunTimeInfo();
+    res = PrintSDKInfo();
+    res = PrintLayerInfo();
+    res = PrintLayerSettingsFileInfo();
+    EndSection();
+
+out:
+
+    return res;
+}
+
+// Determine what version an executable or library file is.
+bool GetFileVersion(const char *filename, const uint32_t max_len,
+                    char *version_string) {
+    DWORD ver_handle;
+    UINT size = 0;
+    LPBYTE buffer = NULL;
+    DWORD ver_size = GetFileVersionInfoSize(filename, &ver_handle);
+    bool success = false;
+
+    if (ver_size > 0) {
+        LPSTR ver_data = (LPSTR)malloc(sizeof(char) * ver_size);
+
+        if (GetFileVersionInfo(filename, ver_handle, ver_size, ver_data)) {
+            if (VerQueryValue(ver_data, "\\", (VOID FAR * FAR *)&buffer,
+                              &size)) {
+                if (size) {
+                    VS_FIXEDFILEINFO *ver_info = (VS_FIXEDFILEINFO *)buffer;
+                    if (ver_info->dwSignature == 0xfeef04bd) {
+                        DWORD max_size =
+                            ver_size > max_len ? max_len : ver_size;
+                        snprintf(version_string, max_len, "%d.%d.%d.%d",
+                                 (ver_info->dwFileVersionMS >> 16) & 0xffff,
+                                 (ver_info->dwFileVersionMS >> 0) & 0xffff,
+                                 (ver_info->dwFileVersionLS >> 16) & 0xffff,
+                                 (ver_info->dwFileVersionLS >> 0) & 0xffff);
+                        success = true;
+                    }
+                }
+            }
+        }
+        free(ver_data);
+    }
+
+    return success;
+}
+
+bool ReadDriverJson(std::string cur_driver_json, std::string system_path,
+                    bool &found_lib) {
+    bool found_json = false;
+    std::ifstream *stream = NULL;
+    Json::Value root = Json::nullValue;
+    Json::Value dev_exts = Json::nullValue;
+    Json::Value inst_exts = Json::nullValue;
+    Json::Reader reader;
+    char full_driver_path[MAX_STRING_LENGTH];
+    char generic_string[MAX_STRING_LENGTH];
+    uint32_t j = 0;
+
+    stream = new std::ifstream(cur_driver_json.c_str(), std::ifstream::in);
+    if (nullptr == stream || stream->fail()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Error reading JSON file");
+        PrintTableElement(cur_driver_json);
+        PrintEndTableRow();
+        goto out;
+    }
+
+    if (!reader.parse(*stream, root, false) || root.isNull()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Error reading JSON file");
+        PrintTableElement(reader.getFormattedErrorMessages());
+        PrintEndTableRow();
+        goto out;
+    }
+
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("JSON File Version");
+    if (!root["file_format_version"].isNull()) {
+        PrintTableElement(root["file_format_version"].asString());
+    } else {
+        PrintTableElement("MISSING!");
+    }
+    PrintEndTableRow();
+
+    if (root["ICD"].isNull()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("ICD Section");
+        PrintTableElement("MISSING!");
+        PrintEndTableRow();
+        goto out;
+    }
+
+    found_json = true;
+
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("API Version");
+    if (!root["ICD"]["api_version"].isNull()) {
+        PrintTableElement(root["ICD"]["api_version"].asString());
+    } else {
+        PrintTableElement("MISSING!");
+    }
+    PrintEndTableRow();
+
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("Library Path");
+    if (!root["ICD"]["library_path"].isNull()) {
+        std::string driver_name = root["ICD"]["library_path"].asString();
+        PrintTableElement(driver_name);
+        PrintEndTableRow();
+
+        if (GenerateLibraryPath(cur_driver_json.c_str(), driver_name.c_str(),
+                                MAX_STRING_LENGTH, full_driver_path)) {
+            std::string driver_name = root["ICD"]["library_path"].asString();
+            std::string system_name = system_path;
+            system_name += "\\";
+            system_name += driver_name;
+
+            if (GetFileVersion(full_driver_path, MAX_STRING_LENGTH - 1,
+                               generic_string)) {
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("Library File Version");
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+
+                found_lib = true;
+            } else if (GetFileVersion(system_name.c_str(),
+                                      MAX_STRING_LENGTH - 1, generic_string)) {
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("Library File Version");
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+
+                found_lib = true;
+            } else {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                         "Failed to find driver %s "
+                         " or %sreferenced by JSON %s",
+                         root["ICD"]["library_path"].asString().c_str(),
+                         full_driver_path, cur_driver_json.c_str());
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+            }
+        } else {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                     "Failed to find driver %s "
+                     "referenced by JSON %s",
+                     full_driver_path, cur_driver_json.c_str());
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        }
+    } else {
+        PrintTableElement("MISSING!");
+        PrintEndTableRow();
+    }
+
+    char count_str[MAX_STRING_LENGTH];
+    j = 0;
+    dev_exts = root["ICD"]["device_extensions"];
+    if (!dev_exts.isNull() && dev_exts.isArray()) {
+        snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", dev_exts.size());
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Device Extensions");
+        PrintTableElement(count_str);
+        PrintEndTableRow();
+
+        for (Json::ValueIterator dev_ext_it = dev_exts.begin();
+             dev_ext_it != dev_exts.end(); dev_ext_it++) {
+            Json::Value dev_ext = (*dev_ext_it);
+            Json::Value dev_ext_name = dev_ext["name"];
+            if (!dev_ext_name.isNull()) {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", j);
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement(dev_ext_name.asString());
+                PrintEndTableRow();
+            }
+        }
+    }
+    inst_exts = root["ICD"]["instance_extensions"];
+    j = 0;
+    if (!inst_exts.isNull() && inst_exts.isArray()) {
+        snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", inst_exts.size());
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Instance Extensions");
+        PrintTableElement(count_str);
+        PrintEndTableRow();
+
+        for (Json::ValueIterator inst_ext_it =
+
+                 inst_exts.begin();
+             inst_ext_it != inst_exts.end(); inst_ext_it++) {
+            Json::Value inst_ext = (*inst_ext_it);
+            Json::Value inst_ext_name = inst_ext["name"];
+            if (!inst_ext_name.isNull()) {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", j);
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement(inst_ext_name.asString());
+                PrintEndTableRow();
+            }
+        }
+    }
+
+out:
+
+    if (nullptr != stream) {
+        stream->close();
+        delete stream;
+        stream = NULL;
+    }
+
+    return found_json;
+}
+
+// Print out the information for every driver in the appropriate
+// Windows registry location and its corresponding JSON file.
+ErrorResults PrintDriverInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    const char vulkan_reg_base[] = "SOFTWARE\\Khronos\\Vulkan";
+    const char vulkan_reg_base_wow64[] =
+        "SOFTWARE\\WOW6432Node\\Khronos\\Vulkan";
+    char reg_key_loc[MAX_STRING_LENGTH];
+    char cur_vulkan_driver_json[MAX_STRING_LENGTH];
+    char generic_string[MAX_STRING_LENGTH];
+    char system_path[MAX_STRING_LENGTH];
+    char env_value[MAX_STRING_LENGTH];
+    uint32_t i = 0;
+    std::ifstream *stream = NULL;
+    bool found_registry = false;
+    bool found_json = false;
+    bool found_lib = false;
+    bool found_this_lib = false;
+
+    GetEnvironmentVariableA("SYSTEMROOT", generic_string, MAX_STRING_LENGTH);
+#if _WIN64 || __x86_64__ || __ppc64__
+    snprintf(system_path, MAX_STRING_LENGTH - 1, "%s\\system32\\",
+             generic_string);
+    snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\Drivers",
+             vulkan_reg_base);
+#else
+    if (global_items.is_wow64) {
+        snprintf(system_path, MAX_STRING_LENGTH - 1, "%s\\sysWOW64\\",
+                 generic_string);
+        snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\Drivers",
+                 vulkan_reg_base_wow64);
+    } else {
+        snprintf(system_path, MAX_STRING_LENGTH - 1, "%s\\system32\\",
+                 generic_string);
+        snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\Drivers",
+                 vulkan_reg_base);
+    }
+#endif
+
+    PrintBeginTable("Vulkan Driver Info", 3);
+    PrintBeginTableRow();
+    PrintTableElement("Drivers in Registry");
+    PrintTableElement(reg_key_loc);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    // Find the registry settings indicating the location of the driver
+    // JSON files.
+    uint32_t returned_value = 0;
+    while (FindNextRegValue(HKEY_LOCAL_MACHINE, reg_key_loc, "", i,
+                            MAX_STRING_LENGTH - 1, cur_vulkan_driver_json,
+                            &returned_value)) {
+        found_registry = true;
+
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "Driver %d", i++);
+
+        PrintBeginTableRow();
+        PrintTableElement(generic_string, ALIGN_RIGHT);
+        PrintTableElement(cur_vulkan_driver_json);
+
+        if (returned_value != 0) {
+            PrintTableElement("DISABLED");
+        } else {
+            PrintTableElement("ENABLED");
+        }
+        PrintEndTableRow();
+
+        // Parse the driver JSON file.
+        if (ReadDriverJson(cur_vulkan_driver_json, system_path,
+                           found_this_lib)) {
+            found_json = true;
+            found_lib |= found_this_lib;
+        }
+    }
+
+    // The user can override the drivers path manually
+    if (0 != GetEnvironmentVariableA("VK_DRIVERS_PATH", env_value,
+                                     MAX_STRING_LENGTH - 1) &&
+        0 != strlen(env_value)) {
+        WIN32_FIND_DATAA ffd;
+        HANDLE hFind;
+        char *tok = NULL;
+        bool keep_looping = false;
+        char full_driver_path[MAX_STRING_LENGTH];
+        char cur_driver_path[MAX_STRING_LENGTH];
+        uint32_t path = 0;
+
+        PrintBeginTableRow();
+        PrintTableElement("VK_DRIVERS_PATH");
+        PrintTableElement(env_value);
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        tok = strtok(env_value, ";");
+        if (NULL != tok) {
+            keep_looping = true;
+            strncpy(cur_driver_path, tok, MAX_STRING_LENGTH - 1);
+        } else {
+            strncpy(cur_driver_path, env_value, MAX_STRING_LENGTH - 1);
+        }
+
+        do {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "Path %d", path++);
+            PrintBeginTableRow();
+            PrintTableElement(generic_string, ALIGN_CENTER);
+            PrintTableElement(cur_driver_path);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            // Look for any JSON files in that folder.
+            snprintf(full_driver_path, MAX_STRING_LENGTH - 1, "%s\\*.json",
+                     cur_driver_path);
+            hFind = FindFirstFileA(full_driver_path, &ffd);
+            if (hFind != INVALID_HANDLE_VALUE) {
+                do {
+                    if (0 ==
+                        (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+                        snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                                 "Driver %d", i++);
+                        snprintf(cur_vulkan_driver_json, MAX_STRING_LENGTH - 1,
+                                 "%s\\%s", cur_driver_path, ffd.cFileName);
+
+                        PrintBeginTableRow();
+                        PrintTableElement(generic_string, ALIGN_RIGHT);
+                        PrintTableElement(ffd.cFileName);
+                        PrintTableElement("");
+                        PrintEndTableRow();
+
+                        // Parse the driver JSON file.
+                        if (ReadDriverJson(cur_vulkan_driver_json, system_path,
+                                           found_this_lib)) {
+                            found_json = true;
+                            found_lib |= found_this_lib;
+                        }
+                    }
+                } while (FindNextFileA(hFind, &ffd) != 0);
+                FindClose(hFind);
+            }
+
+            tok = strtok(NULL, ";");
+            if (NULL == tok) {
+                keep_looping = false;
+            } else {
+                strncpy(cur_driver_path, tok, MAX_STRING_LENGTH - 1);
+            }
+        } while (keep_looping);
+    }
+
+    // The user can override the driver file manually
+    if (0 != GetEnvironmentVariableA("VK_ICD_FILENAMES", env_value,
+                                     MAX_STRING_LENGTH - 1) &&
+        0 != strlen(env_value)) {
+        WIN32_FIND_DATAA ffd;
+        HANDLE hFind;
+        char *tok = NULL;
+        bool keep_looping = false;
+        char full_driver_path[MAX_STRING_LENGTH];
+
+        PrintBeginTableRow();
+        PrintTableElement("VK_ICD_FILENAMES");
+        PrintTableElement(env_value);
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        tok = strtok(env_value, ";");
+        if (NULL != tok) {
+            keep_looping = true;
+            strncpy(full_driver_path, tok, MAX_STRING_LENGTH - 1);
+        } else {
+            strncpy(full_driver_path, env_value, MAX_STRING_LENGTH - 1);
+        }
+
+        do {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "Driver %d", i++);
+            PrintBeginTableRow();
+            PrintTableElement(generic_string, ALIGN_RIGHT);
+            PrintTableElement(full_driver_path);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            hFind = FindFirstFileA(full_driver_path, &ffd);
+            if (hFind != INVALID_HANDLE_VALUE) {
+                if (0 == (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+                    strcpy(cur_vulkan_driver_json, full_driver_path);
+                    // Parse the driver JSON file.
+                    if (ReadDriverJson(cur_vulkan_driver_json, system_path,
+                                       found_this_lib)) {
+                        found_json = true;
+                        found_lib |= found_this_lib;
+                    }
+                }
+                FindClose(hFind);
+            } else {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("Driver Not Found");
+                PrintTableElement("");
+                PrintEndTableRow();
+            }
+
+            tok = strtok(NULL, ";");
+            if (NULL == tok) {
+                keep_looping = false;
+            } else {
+                strncpy(full_driver_path, tok, MAX_STRING_LENGTH - 1);
+            }
+        } while (keep_looping);
+    }
+
+    PrintEndTable();
+
+    if (!found_registry) {
+        res = MISSING_DRIVER_REGISTRY;
+    } else if (!found_json) {
+        res = MISSING_DRIVER_JSON;
+    } else if (!found_lib) {
+        res = MISSING_DRIVER_LIB;
+    }
+
+    return res;
+}
+
+// Print out whatever Vulkan runtime information we can gather from the system
+// using the registry, standard system paths, etc.
+ErrorResults PrintRunTimeInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    char generic_string[MAX_STRING_LENGTH];
+    char count_string[MAX_STRING_LENGTH];
+    char version_string[MAX_STRING_LENGTH];
+    char output_string[MAX_STRING_LENGTH];
+    char dll_search[MAX_STRING_LENGTH];
+    char dll_prefix[MAX_STRING_LENGTH];
+    uint32_t i = 0;
+    uint32_t install_count = 0;
+    FILE *fp = NULL;
+    bool found = false;
+
+    PrintBeginTable("Vulkan Runtimes", 3);
+
+    PrintBeginTableRow();
+    PrintTableElement("Runtimes In Registry");
+    PrintTableElement(g_uninstall_reg_path);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    // Find all Vulkan Runtime keys in the registry, and loop through each.
+    while (FindNextRegKey(HKEY_LOCAL_MACHINE, g_uninstall_reg_path, "VulkanRT",
+                          i, MAX_STRING_LENGTH - 1, output_string)) {
+        snprintf(count_string, MAX_STRING_LENGTH - 1, "[%d]", i++);
+
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "%s\\%s",
+                 g_uninstall_reg_path, output_string);
+
+        // Get the version from the registry
+        if (ReadRegKeyString(HKEY_LOCAL_MACHINE, generic_string,
+                             "DisplayVersion", MAX_STRING_LENGTH - 1,
+                             version_string)) {
+        } else {
+            strncpy(version_string, output_string, MAX_STRING_LENGTH - 1);
+        }
+
+        // Get the install count for this runtime from the registry
+        if (ReadRegKeyDword(HKEY_LOCAL_MACHINE, generic_string, "InstallCount",
+                            &install_count)) {
+            snprintf(output_string, MAX_STRING_LENGTH - 1,
+                     "%s  [Install Count = %d]", version_string, install_count);
+        } else {
+            snprintf(output_string, MAX_STRING_LENGTH - 1, "%s",
+                     version_string);
+        }
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement(count_string, ALIGN_RIGHT);
+        PrintTableElement(output_string);
+        PrintEndTableRow();
+    }
+
+    i = 0;
+    GetEnvironmentVariableA("SYSTEMROOT", generic_string, MAX_STRING_LENGTH);
+#if _WIN64 || __x86_64__ || __ppc64__
+    snprintf(dll_prefix, MAX_STRING_LENGTH - 1, "%s\\system32\\",
+             generic_string);
+#else
+    if (global_items.is_wow64) {
+        snprintf(dll_prefix, MAX_STRING_LENGTH - 1, "%s\\sysWOW64\\",
+                 generic_string);
+    } else {
+        snprintf(dll_prefix, MAX_STRING_LENGTH - 1, "%s\\system32\\",
+                 generic_string);
+    }
+#endif
+
+    PrintBeginTableRow();
+    PrintTableElement("Runtimes in System Folder");
+    PrintTableElement(dll_prefix);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    strncpy(dll_search, dll_prefix, MAX_STRING_LENGTH - 1);
+    strncat(dll_search, "Vulkan-*.dll", MAX_STRING_LENGTH - 1);
+
+    WIN32_FIND_DATAA ffd;
+    HANDLE hFind = FindFirstFileA(dll_search, &ffd);
+    if (hFind != INVALID_HANDLE_VALUE) {
+        do {
+            if (0 == (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+                snprintf(count_string, MAX_STRING_LENGTH - 1, "DLL %d", i++);
+
+                PrintBeginTableRow();
+                PrintTableElement(count_string, ALIGN_RIGHT);
+                PrintTableElement(ffd.cFileName);
+
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "%s\\%s",
+                         dll_prefix, ffd.cFileName);
+                if (GetFileVersion(generic_string, MAX_STRING_LENGTH - 1,
+                                   version_string)) {
+                    snprintf(output_string, MAX_STRING_LENGTH - 1, "Version %s",
+                             version_string);
+                    PrintTableElement(output_string);
+                } else {
+                    PrintTableElement("");
+                }
+                PrintEndTableRow();
+            }
+        } while (FindNextFileA(hFind, &ffd) != 0);
+        FindClose(hFind);
+    }
+
+    PrintBeginTableRow();
+    PrintTableElement("Runtime Used by App");
+    if (!system("where vulkan-1.dll > where_vulkan")) {
+        fp = fopen("where_vulkan", "rt");
+        if (NULL != fp) {
+            if (NULL != fgets(generic_string, MAX_STRING_LENGTH - 1, fp)) {
+                int i = (int)strlen(generic_string) - 1;
+                while (generic_string[i] == '\n' || generic_string[i] == '\r' ||
+                       generic_string[i] == '\t' || generic_string[i] == ' ') {
+                    generic_string[i] = '\0';
+                    i--;
+                }
+
+                if (GetFileVersion(generic_string, MAX_STRING_LENGTH - 1,
+                                   version_string)) {
+                    PrintTableElement(generic_string);
+                    PrintTableElement(version_string);
+                } else {
+                    PrintTableElement(generic_string);
+                    PrintTableElement("");
+                }
+                found = true;
+            }
+            fclose(fp);
+        }
+        DeleteFileA("where_vulkan");
+    } else {
+        PrintTableElement("Unknown");
+        PrintTableElement("Unknown");
+    }
+    PrintEndTableRow();
+
+    PrintEndTable();
+
+    if (!found) {
+        res = VULKAN_CANT_FIND_RUNTIME;
+    }
+
+    return res;
+}
+
+// Print out information on whatever LunarG Vulkan SDKs we can find on
+// the system using the registry, and environmental variables.  This
+// includes listing what layers are available from the SDK.
+ErrorResults PrintSDKInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    const char vulkan_reg_base[] = "SOFTWARE\\Khronos\\Vulkan";
+    const char vulkan_reg_base_wow64[] =
+        "SOFTWARE\\WOW6432Node\\Khronos\\Vulkan";
+    char generic_string[MAX_STRING_LENGTH];
+    char count_string[MAX_STRING_LENGTH];
+    char output_string[MAX_STRING_LENGTH];
+    char cur_vulkan_layer_json[MAX_STRING_LENGTH];
+    char sdk_env_dir[MAX_STRING_LENGTH];
+    char reg_key_loc[MAX_STRING_LENGTH];
+    uint32_t i = 0;
+    uint32_t j = 0;
+    FILE *fp = NULL;
+    bool found = false;
+
+    PrintBeginTable("LunarG Vulkan SDKs", 3);
+    PrintBeginTableRow();
+    PrintTableElement("SDKs Found In Registry");
+    PrintTableElement(g_uninstall_reg_path);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    while (FindNextRegKey(HKEY_LOCAL_MACHINE, g_uninstall_reg_path, "VulkanSDK",
+                          i, MAX_STRING_LENGTH, output_string)) {
+        found = true;
+        snprintf(count_string, MAX_STRING_LENGTH - 1, "[%d]", i++);
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "%s\\%s",
+                 g_uninstall_reg_path, output_string);
+        if (ReadRegKeyString(HKEY_LOCAL_MACHINE, generic_string, "InstallDir",
+                             MAX_STRING_LENGTH, output_string)) {
+        }
+
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement(count_string, ALIGN_RIGHT);
+        PrintTableElement(output_string);
+        PrintEndTableRow();
+    }
+    if (!found) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("NONE FOUND", ALIGN_RIGHT);
+        PrintTableElement("");
+        PrintEndTableRow();
+    }
+
+    if (0 != GetEnvironmentVariableA("VK_SDK_PATH", sdk_env_dir,
+                                     MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("VK_SDK_PATH");
+        global_items.sdk_found = true;
+        global_items.sdk_path = sdk_env_dir;
+        PrintTableElement(sdk_env_dir);
+        PrintTableElement("");
+        PrintEndTableRow();
+    } else if (0 != GetEnvironmentVariableA("VULKAN_SDK", sdk_env_dir,
+                                            MAX_STRING_LENGTH - 1)) {
+        PrintBeginTableRow();
+        PrintTableElement("VULKAN_SDK");
+        global_items.sdk_found = true;
+        global_items.sdk_path = sdk_env_dir;
+        PrintTableElement(sdk_env_dir);
+        PrintTableElement("");
+        PrintEndTableRow();
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement("VK_SDK_PATH");
+        PrintTableElement("No installed SDK");
+        PrintTableElement("");
+        PrintEndTableRow();
+    }
+
+#if _WIN64 || __x86_64__ || __ppc64__
+    snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\ExplicitLayers",
+             vulkan_reg_base);
+#else
+    if (global_items.is_wow64) {
+        snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\ExplicitLayers",
+                 vulkan_reg_base_wow64);
+    } else {
+        snprintf(reg_key_loc, MAX_STRING_LENGTH - 1, "%s\\ExplicitLayers",
+                 vulkan_reg_base);
+    }
+#endif
+
+    PrintBeginTableRow();
+    PrintTableElement("SDK Explicit Layers");
+    PrintTableElement(generic_string);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    found = false;
+    i = 0;
+    uint32_t returned_value = 0;
+    while (FindNextRegValue(HKEY_LOCAL_MACHINE, reg_key_loc, "", i,
+                            MAX_STRING_LENGTH, cur_vulkan_layer_json,
+                            &returned_value)) {
+        found = true;
+
+        // Create a short json file name so we don't use up too much space
+        snprintf(output_string, MAX_STRING_LENGTH - 1, ".%s",
+                 &cur_vulkan_layer_json[strlen(sdk_env_dir)]);
+
+        snprintf(count_string, MAX_STRING_LENGTH - 1, "[%d]", i++);
+        PrintBeginTableRow();
+        PrintTableElement(count_string, ALIGN_RIGHT);
+        PrintTableElement(output_string);
+
+        snprintf(output_string, MAX_STRING_LENGTH - 1, "0x%08x",
+                 returned_value);
+        PrintTableElement(output_string);
+        PrintEndTableRow();
+
+        std::ifstream *stream = NULL;
+        stream = new std::ifstream(cur_vulkan_layer_json, std::ifstream::in);
+        if (nullptr == stream || stream->fail()) {
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("ERROR reading JSON file!");
+            PrintTableElement("");
+            PrintEndTableRow();
+            res = MISSING_LAYER_JSON;
+        } else {
+            Json::Value root = Json::nullValue;
+            Json::Reader reader;
+            if (!reader.parse(*stream, root, false) || root.isNull()) {
+                // Report to the user the failure and their locations in the
+                // document.
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("ERROR parsing JSON file!");
+                PrintTableElement(reader.getFormattedErrorMessages());
+                PrintEndTableRow();
+                res = LAYER_JSON_PARSING_ERROR;
+            } else {
+                PrintExplicitLayerJsonInfo(cur_vulkan_layer_json, root, 3);
+            }
+
+            stream->close();
+            delete stream;
+            stream = NULL;
+        }
+    }
+    if (!found) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("NONE FOUND", ALIGN_RIGHT);
+        PrintTableElement("");
+        PrintEndTableRow();
+    }
+
+    PrintEndTable();
+
+    return res;
+}
+
+// Print out whatever layers we can find out from the Windows'
+// registry and other environmental variables that may be used
+// to point the Vulkan loader at a layer path.
+ErrorResults PrintLayerInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    const char vulkan_reg_base[] = "SOFTWARE\\Khronos\\Vulkan";
+    const char vulkan_reg_base_wow64[] =
+        "SOFTWARE\\WOW6432Node\\Khronos\\Vulkan";
+    char vulkan_impl_layer_reg_key[MAX_STRING_LENGTH];
+    char cur_vulkan_layer_json[MAX_STRING_LENGTH];
+    char generic_string[MAX_STRING_LENGTH];
+    char full_layer_path[MAX_STRING_LENGTH];
+    char env_value[MAX_STRING_LENGTH];
+    uint32_t i = 0;
+    uint32_t j = 0;
+    FILE *fp = NULL;
+
+// Dump implicit layer information first.
+#if _WIN64 || __x86_64__ || __ppc64__
+    snprintf(vulkan_impl_layer_reg_key, MAX_STRING_LENGTH - 1,
+             "%s\\ImplicitLayers", vulkan_reg_base);
+#else
+    if (global_items.is_wow64) {
+        snprintf(vulkan_impl_layer_reg_key, MAX_STRING_LENGTH - 1,
+                 "%s\\ImplicitLayers", vulkan_reg_base_wow64);
+    } else {
+        snprintf(vulkan_impl_layer_reg_key, MAX_STRING_LENGTH - 1,
+                 "%s\\ImplicitLayers", vulkan_reg_base);
+    }
+#endif
+
+    PrintBeginTable("Implicit Layers", 4);
+    PrintBeginTableRow();
+    PrintTableElement("Layers in Registry");
+    PrintTableElement(vulkan_impl_layer_reg_key);
+    PrintTableElement("");
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    // For each implicit layer listed in the registry, find its JSON and
+    // print out the useful information stored in it.
+    uint32_t returned_value = 0;
+    while (FindNextRegValue(HKEY_LOCAL_MACHINE, vulkan_impl_layer_reg_key, "",
+                            i, MAX_STRING_LENGTH, cur_vulkan_layer_json,
+                            &returned_value)) {
+
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++);
+
+        PrintBeginTableRow();
+        PrintTableElement(generic_string, ALIGN_RIGHT);
+        PrintTableElement(cur_vulkan_layer_json);
+        PrintTableElement("");
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%08x",
+                 returned_value);
+        PrintTableElement(generic_string);
+        PrintEndTableRow();
+
+        std::ifstream *stream = NULL;
+        stream = new std::ifstream(cur_vulkan_layer_json, std::ifstream::in);
+        if (nullptr == stream || stream->fail()) {
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("ERROR reading JSON file!");
+            PrintTableElement("");
+            PrintEndTableRow();
+            res = MISSING_LAYER_JSON;
+        } else {
+            Json::Value root = Json::nullValue;
+            Json::Reader reader;
+            if (!reader.parse(*stream, root, false) || root.isNull()) {
+                // Report to the user the failure and their locations in the
+                // document.
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("ERROR parsing JSON file!");
+                PrintTableElement(reader.getFormattedErrorMessages());
+                PrintEndTableRow();
+                res = LAYER_JSON_PARSING_ERROR;
+            } else {
+                PrintImplicitLayerJsonInfo(cur_vulkan_layer_json, root);
+            }
+
+            stream->close();
+            delete stream;
+            stream = NULL;
+        }
+    }
+    PrintEndTable();
+
+    // If the user's system has VK_LAYER_PATH set, dump out the layer
+    // information found in that folder.  This is important because if
+    // a user is having problems with the layers, they may be using
+    // non-standard layers.
+    if (0 != GetEnvironmentVariableA("VK_LAYER_PATH", env_value,
+                                     MAX_STRING_LENGTH - 1)) {
+        WIN32_FIND_DATAA ffd;
+        HANDLE hFind;
+        std::string cur_layer_path;
+        bool keep_looping = false;
+        uint32_t path = 0;
+
+        PrintBeginTable("VK_LAYER_PATH Explicit Layers", 3);
+        PrintBeginTableRow();
+        PrintTableElement("VK_LAYER_PATH");
+        PrintTableElement(env_value);
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        // VK_LAYER_PATH may have multiple folders listed in it (colon
+        // ';' delimited)
+        char *tok = strtok(env_value, ";");
+        if (tok != NULL) {
+            cur_layer_path = tok;
+            keep_looping = true;
+        } else {
+            cur_layer_path = env_value;
+        }
+
+        do {
+            if (keep_looping) {
+                PrintBeginTableRow();
+                sprintf(generic_string, "Path %d", path++);
+                PrintTableElement(generic_string, ALIGN_CENTER);
+                PrintTableElement(cur_layer_path);
+                PrintTableElement("");
+                PrintEndTableRow();
+            }
+
+            // Look for any JSON files in that folder.
+            snprintf(full_layer_path, MAX_STRING_LENGTH - 1, "%s\\*.json",
+                     cur_layer_path.c_str());
+            i = 0;
+            hFind = FindFirstFileA(full_layer_path, &ffd);
+            if (hFind != INVALID_HANDLE_VALUE) {
+                do {
+                    if (0 ==
+                        (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+                        snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]",
+                                 i++);
+                        snprintf(cur_vulkan_layer_json, MAX_STRING_LENGTH - 1,
+                                 "%s\\%s", cur_layer_path.c_str(),
+                                 ffd.cFileName);
+
+                        PrintBeginTableRow();
+                        PrintTableElement(generic_string, ALIGN_RIGHT);
+                        PrintTableElement(ffd.cFileName);
+                        PrintTableElement("");
+                        PrintEndTableRow();
+
+                        std::ifstream *stream = NULL;
+                        stream = new std::ifstream(cur_vulkan_layer_json,
+                                                   std::ifstream::in);
+                        if (nullptr == stream || stream->fail()) {
+                            PrintBeginTableRow();
+                            PrintTableElement("");
+                            PrintTableElement("ERROR reading JSON file!");
+                            PrintTableElement("");
+                            PrintEndTableRow();
+                            res = MISSING_LAYER_JSON;
+                        } else {
+                            Json::Value root = Json::nullValue;
+                            Json::Reader reader;
+                            if (!reader.parse(*stream, root, false) ||
+                                root.isNull()) {
+                                // Report to the user the failure and their
+                                // locations in the document.
+                                PrintBeginTableRow();
+                                PrintTableElement("");
+                                PrintTableElement("ERROR parsing JSON file!");
+                                PrintTableElement(
+                                    reader.getFormattedErrorMessages());
+                                PrintEndTableRow();
+                                res = LAYER_JSON_PARSING_ERROR;
+                            } else {
+                                PrintExplicitLayerJsonInfo(
+                                    cur_vulkan_layer_json, root, 3);
+                            }
+
+                            stream->close();
+                            delete stream;
+                            stream = NULL;
+                        }
+                    }
+                } while (FindNextFileA(hFind, &ffd) != 0);
+
+                FindClose(hFind);
+            }
+
+            tok = strtok(NULL, ";");
+            if (tok == NULL) {
+                keep_looping = false;
+            } else {
+                cur_layer_path = tok;
+            }
+        } while (keep_looping);
+
+        PrintEndTable();
+    }
+
+    return res;
+}
+
+#elif __GNUC__
+
+// Utility function to determine if a driver may exist in the folder.
+bool CheckDriver(std::string &folder_loc, std::string &object_name) {
+    bool success = false;
+    std::string full_name = folder_loc;
+    if (folder_loc.c_str()[folder_loc.size() - 1] != '/') {
+        full_name += "/";
+    }
+    full_name += object_name;
+    if (access(full_name.c_str(), R_OK) != -1) {
+        success = true;
+    }
+    return success;
+}
+
+// Pointer to a function sed to validate if the system object is found
+typedef bool (*PFN_CheckIfValid)(std::string &folder_loc,
+                                 std::string &object_name);
+
+bool FindLinuxSystemObject(std::string object_name, PFN_CheckIfValid func,
+                           bool break_on_first) {
+    bool found_one = false;
+    std::string path_to_check;
+    char *env_value = getenv("LD_LIBRARY_PATH");
+
+    for (uint32_t iii = 0; iii < 5; iii++) {
+        switch (iii) {
+        case 0:
+            path_to_check = "/usr/lib";
+            break;
+        case 1:
+#if __x86_64__ || __ppc64__
+            path_to_check = "/usr/lib/x86_64-linux-gnu";
+#else
+            path_to_check = "/usr/lib/i386-linux-gnu";
+#endif
+            break;
+        case 2:
+#if __x86_64__ || __ppc64__
+            path_to_check = "/usr/lib64";
+#else
+            path_to_check = "/usr/lib32";
+#endif
+            break;
+        case 3:
+            path_to_check = "/usr/local/lib";
+            break;
+        case 4:
+#if __x86_64__ || __ppc64__
+            path_to_check = "/usr/local/lib64";
+#else
+            path_to_check = "/usr/local/lib32";
+#endif
+            break;
+        default:
+            continue;
+        }
+
+        if (func(path_to_check, object_name)) {
+            // We found one runtime, clear any failures
+            found_one = true;
+            if (break_on_first) {
+                goto out;
+            }
+        }
+    }
+
+    // LD_LIBRARY_PATH may have multiple folders listed in it (colon
+    // ':' delimited)
+    if (env_value != NULL) {
+        char *tok = strtok(env_value, ":");
+        while (tok != NULL) {
+            if (strlen(tok) > 0) {
+                path_to_check = tok;
+                if (func(path_to_check, object_name)) {
+                    // We found one runtime, clear any failures
+                    found_one = true;
+                }
+            }
+            tok = strtok(NULL, ":");
+        }
+    }
+
+out:
+    return found_one;
+}
+
+// Print out any information about the current system that we can
+// capture to ease in debugging/investigation at a later time.
+ErrorResults PrintSystemInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    FILE *fp;
+    char path[1035];
+    char generic_string[MAX_STRING_LENGTH];
+    utsname buffer;
+    struct statvfs fs_stats;
+    int num_cpus;
+    uint64_t memory;
+    char *env_value;
+    std::string cur_directory;
+    std::string exe_directory;
+    std::string desktop_session;
+
+    BeginSection("System Info");
+
+    // Environment section has information about the OS and the
+    // execution environment.
+    PrintBeginTable("Environment", 3);
+
+    fp = popen("cat /etc/os-release", "r");
+    if (fp == NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("ERROR");
+        PrintTableElement("Failed to cat /etc/os-release");
+        PrintTableElement("");
+        PrintEndTableRow();
+        res = SYSTEM_CALL_FAILURE;
+    } else {
+        // Read the output a line at a time - output it.
+        while (fgets(path, sizeof(path) - 1, fp) != NULL) {
+            if (NULL != strstr(path, "PRETTY_NAME")) {
+                uint32_t index;
+                index = strlen(path) - 1;
+                while (path[index] == ' ' || path[index] == '\t' ||
+                       path[index] == '\r' || path[index] == '\n' ||
+                       path[index] == '\"') {
+                    path[index] = '\0';
+                    index = strlen(path) - 1;
+                }
+                index = 13;
+                while (path[index] == ' ' || path[index] == '\t' ||
+                       path[index] == '\"') {
+                    index++;
+                }
+                PrintBeginTableRow();
+                PrintTableElement("Linux");
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintEndTableRow();
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("Distro");
+                PrintTableElement(&path[index]);
+                PrintEndTableRow();
+                break;
+            }
+        }
+        pclose(fp);
+    }
+
+    errno = 0;
+    if (uname(&buffer) != 0) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("ERROR");
+        PrintTableElement("Failed to query uname");
+        PrintEndTableRow();
+        res = SYSTEM_CALL_FAILURE;
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Kernel Build");
+        PrintTableElement(buffer.release);
+        PrintEndTableRow();
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Machine Target");
+        PrintTableElement(buffer.machine);
+        PrintEndTableRow();
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Version");
+        PrintTableElement(buffer.version);
+        PrintEndTableRow();
+    }
+
+    env_value = getenv("DESKTOP_SESSION");
+    if (env_value != NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("DESKTOP_SESSION");
+        PrintTableElement(env_value);
+        PrintEndTableRow();
+
+        desktop_session = env_value;
+    }
+    env_value = getenv("LD_LIBRARY_PATH");
+    if (env_value != NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("LD_LIBRARY_PATH");
+        PrintTableElement(env_value);
+        PrintEndTableRow();
+    }
+    env_value = getenv("GDK_BACKEND");
+    if (env_value != NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("GDK_BACKEND");
+        PrintTableElement(env_value);
+        PrintEndTableRow();
+    }
+    env_value = getenv("DISPLAY");
+    if (env_value != NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("DISPLAY");
+        PrintTableElement(env_value);
+        PrintEndTableRow();
+    }
+    env_value = getenv("WAYLAND_DISPLAY");
+    if (env_value != NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("WAYLAND_DISPLAY");
+        PrintTableElement(env_value);
+        PrintEndTableRow();
+    }
+    env_value = getenv("MIR_SOCKET");
+    if (env_value != NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("MIR_SOCKET");
+        PrintTableElement(env_value);
+        PrintEndTableRow();
+    }
+
+    PrintEndTable();
+
+    if (getcwd(generic_string, MAX_STRING_LENGTH - 1) != NULL) {
+        cur_directory = generic_string;
+    } else {
+        cur_directory = "";
+    }
+
+    // Output whatever generic hardware information we can find out about the
+    // system.  Including how much memory and disk space is available.
+    PrintBeginTable("Hardware", 3);
+
+    num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", num_cpus);
+
+    PrintBeginTableRow();
+    PrintTableElement("CPUs");
+    PrintTableElement(generic_string);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    memory = (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE)) >> 10;
+    if ((memory >> 10) > 0) {
+        memory >>= 10;
+        if ((memory >> 20) > 0) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB",
+                     static_cast<uint32_t>(memory >> 20));
+        } else if ((memory >> 10) > 0) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB",
+                     static_cast<uint32_t>(memory >> 10));
+        } else {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB",
+                     static_cast<uint32_t>(memory));
+        }
+    } else {
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB",
+                 static_cast<uint32_t>(memory));
+    }
+    PrintBeginTableRow();
+    PrintTableElement("Memory");
+    PrintTableElement("Physical");
+    PrintTableElement(generic_string);
+    PrintEndTableRow();
+
+    // Print system disk space usage
+    if (0 == statvfs("/etc/os-release", &fs_stats)) {
+        uint64_t bytes_total =
+            (uint64_t)fs_stats.f_bsize * (uint64_t)fs_stats.f_bavail;
+        if ((bytes_total >> 40) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u TB",
+                     static_cast<uint32_t>(bytes_total >> 40));
+            PrintBeginTableRow();
+            PrintTableElement("System Disk Space");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((bytes_total >> 30) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u GB",
+                     static_cast<uint32_t>(bytes_total >> 30));
+            PrintBeginTableRow();
+            PrintTableElement("System Disk Space");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+        } else if ((bytes_total >> 20) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u MB",
+                     static_cast<uint32_t>(bytes_total >> 20));
+            PrintBeginTableRow();
+            PrintTableElement("System Disk Space");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else if ((bytes_total >> 10) > 0x0ULL) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u KB",
+                     static_cast<uint32_t>(bytes_total >> 10));
+            PrintBeginTableRow();
+            PrintTableElement("System Disk Space");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        } else {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%u bytes",
+                     static_cast<uint32_t>(bytes_total));
+            PrintBeginTableRow();
+            PrintTableElement("System Disk Space");
+            PrintTableElement("Free");
+            PrintTableElement(generic_string);
+            PrintEndTableRow();
+        }
+    }
+
+    // Print current directory disk space info
+    sprintf(generic_string,
+            "df -h %s | awk \'{ print $4 } \' | tail -n 1",
+            cur_directory.c_str());
+    fp = popen(generic_string, "r");
+    if (fp == NULL) {
+        PrintBeginTableRow();
+        PrintTableElement("Current Dir Disk Space");
+        PrintTableElement("WARNING");
+        PrintTableElement(
+            "Failed to determine current directory disk space");
+        PrintEndTableRow();
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement("Current Dir Disk Space");
+        PrintTableElement("Free");
+        if (fgets(path, sizeof(path) - 1, fp) != NULL) {
+            PrintTableElement(path);
+        } else {
+            PrintTableElement(
+                "Failed to determine current directory disk space");
+        }
+        PrintEndTableRow();
+        pclose(fp);
+    }
+    PrintEndTable();
+
+    // Print out information about this executable.
+    PrintBeginTable("Executable", 2);
+
+    PrintBeginTableRow();
+    PrintTableElement("Exe Directory");
+    PrintTableElement(global_items.exe_directory);
+    PrintEndTableRow();
+
+    PrintBeginTableRow();
+    PrintTableElement("Current Directory");
+    PrintTableElement(cur_directory);
+    PrintEndTableRow();
+
+    PrintBeginTableRow();
+    PrintTableElement("App Version");
+    PrintTableElement(APP_VERSION);
+    PrintEndTableRow();
+
+    uint32_t major = VK_VERSION_MAJOR(VK_API_VERSION_1_0);
+    uint32_t minor = VK_VERSION_MINOR(VK_API_VERSION_1_0);
+    uint32_t patch = VK_VERSION_PATCH(VK_HEADER_VERSION);
+    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d", major, minor,
+             patch);
+
+    PrintBeginTableRow();
+    PrintTableElement("Vulkan API Version");
+    PrintTableElement(generic_string);
+    PrintEndTableRow();
+
+    PrintBeginTableRow();
+    PrintTableElement("Byte Format");
+#if __x86_64__ || __ppc64__
+    PrintTableElement("64-bit");
+#else
+    PrintTableElement("32-bit");
+#endif
+    PrintEndTableRow();
+
+    PrintEndTable();
+
+    // Print out the rest of the useful system information.
+    res = PrintDriverInfo();
+    res = PrintRunTimeInfo();
+    res = PrintSDKInfo();
+    res = PrintLayerInfo();
+    res = PrintLayerSettingsFileInfo();
+    EndSection();
+
+    return res;
+}
+
+bool ReadDriverJson(std::string cur_driver_json, bool &found_lib) {
+    bool found_json = false;
+    std::ifstream *stream = NULL;
+    Json::Value root = Json::nullValue;
+    Json::Value inst_exts = Json::nullValue;
+    Json::Value dev_exts = Json::nullValue;
+    Json::Reader reader;
+    char full_driver_path[MAX_STRING_LENGTH];
+    char generic_string[MAX_STRING_LENGTH];
+    uint32_t j = 0;
+
+    stream = new std::ifstream(cur_driver_json.c_str(), std::ifstream::in);
+    if (nullptr == stream || stream->fail()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Error reading JSON file");
+        PrintTableElement(cur_driver_json);
+        PrintEndTableRow();
+        goto out;
+    }
+
+    if (!reader.parse(*stream, root, false) || root.isNull()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Error reading JSON file");
+        PrintTableElement(reader.getFormattedErrorMessages());
+        PrintEndTableRow();
+        goto out;
+    }
+
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("JSON File Version");
+    if (!root["file_format_version"].isNull()) {
+        PrintTableElement(root["file_format_version"].asString());
+    } else {
+        PrintTableElement("MISSING!");
+    }
+    PrintEndTableRow();
+
+    if (root["ICD"].isNull()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("ICD Section");
+        PrintTableElement("MISSING!");
+        PrintEndTableRow();
+        goto out;
+    }
+
+    found_json = true;
+
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("API Version");
+    if (!root["ICD"]["api_version"].isNull()) {
+        PrintTableElement(root["ICD"]["api_version"].asString());
+    } else {
+        PrintTableElement("MISSING!");
+    }
+    PrintEndTableRow();
+
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("Library Path");
+    if (!root["ICD"]["library_path"].isNull()) {
+        std::string driver_name = root["ICD"]["library_path"].asString();
+        PrintTableElement(driver_name);
+        PrintEndTableRow();
+
+        if (GenerateLibraryPath(cur_driver_json.c_str(), driver_name.c_str(),
+                                MAX_STRING_LENGTH, full_driver_path)) {
+            // First try the generated path.
+            if (access(full_driver_path, R_OK) != -1) {
+                found_lib = true;
+            } else if (driver_name.find("/") == std::string::npos) {
+                if (FindLinuxSystemObject(driver_name, CheckDriver, true)) {
+                    found_lib = true;
+                }
+            }
+        }
+        if (!found_lib) {
+            FILE *fp;
+            sprintf(generic_string,
+                    "/sbin/ldconfig -v -N -p | grep %s | awk \'{ print $4 }\'",
+                    driver_name.c_str());
+            fp = popen(generic_string, "r");
+            if (fp == NULL) {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                         "Failed to find driver %s "
+                         "referenced by JSON %s",
+                         driver_name.c_str(), cur_driver_json.c_str());
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+            } else {
+                char query_res[MAX_STRING_LENGTH];
+
+                // Read the output a line at a time - output it.
+                if (fgets(query_res, sizeof(query_res) - 1, fp) != NULL) {
+                    sprintf(generic_string, "Found at %s", query_res);
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+                    found_lib = true;
+                }
+                fclose(fp);
+            }
+        }
+    } else {
+        PrintTableElement("MISSING!");
+        PrintEndTableRow();
+    }
+
+    char count_str[MAX_STRING_LENGTH];
+    j = 0;
+    dev_exts = root["ICD"]["device_extensions"];
+    if (!dev_exts.isNull() && dev_exts.isArray()) {
+        snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", dev_exts.size());
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Device Extensions");
+        PrintTableElement(count_str);
+        PrintEndTableRow();
+
+        for (Json::ValueIterator dev_ext_it = dev_exts.begin();
+             dev_ext_it != dev_exts.end(); dev_ext_it++) {
+            Json::Value dev_ext = (*dev_ext_it);
+            Json::Value dev_ext_name = dev_ext["name"];
+            if (!dev_ext_name.isNull()) {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", j);
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement(dev_ext_name.asString());
+                PrintEndTableRow();
+            }
+        }
+    }
+    inst_exts = root["ICD"]["instance_extensions"];
+    j = 0;
+    if (!inst_exts.isNull() && inst_exts.isArray()) {
+        snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", inst_exts.size());
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Instance Extensions");
+        PrintTableElement(count_str);
+        PrintEndTableRow();
+
+        for (Json::ValueIterator inst_ext_it =
+
+                 inst_exts.begin();
+             inst_ext_it != inst_exts.end(); inst_ext_it++) {
+            Json::Value inst_ext = (*inst_ext_it);
+            Json::Value inst_ext_name = inst_ext["name"];
+            if (!inst_ext_name.isNull()) {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", j);
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement(inst_ext_name.asString());
+                PrintEndTableRow();
+            }
+        }
+    }
+
+out:
+
+    if (nullptr != stream) {
+        stream->close();
+        delete stream;
+        stream = NULL;
+    }
+
+    return found_json;
+}
+
+// Print out the information for every driver JSON in the appropriate
+// system folders.
+ErrorResults PrintDriverInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    bool found_json = false;
+    bool found_lib = false;
+    bool found_this_lib = false;
+    uint32_t i = 0;
+    char generic_string[MAX_STRING_LENGTH];
+    char cur_vulkan_driver_json[MAX_STRING_LENGTH];
+    char *home_env_value = NULL;
+    char *drivers_env_value = NULL;
+    char *icd_env_value = NULL;
+    std::vector<std::string> driver_paths;
+    int drivers_path_index = -1;
+
+    PrintBeginTable("Vulkan Driver Info", 3);
+
+    // There are several folders ICD JSONs could be in.  So,
+    // try all of them.
+    driver_paths.push_back("/etc/vulkan/icd.d");
+    driver_paths.push_back("/usr/share/vulkan/icd.d");
+    driver_paths.push_back("/usr/local/etc/vulkan/icd.d");
+    driver_paths.push_back("/usr/local/share/vulkan/icd.d");
+
+    home_env_value = getenv("HOME");
+    if (NULL == home_env_value) {
+        driver_paths.push_back("~/.local/share/vulkan/icd.d");
+    } else {
+        std::string home_icd_dir = home_env_value;
+        home_icd_dir += "/.local/share/vulkan/icd.d";
+        driver_paths.push_back(home_icd_dir);
+    }
+
+    // The user can override the drivers path manually
+    drivers_env_value = getenv("VK_DRIVERS_PATH");
+    if (NULL != drivers_env_value) {
+        drivers_path_index = driver_paths.size();
+        // VK_DRIVERS_PATH may have multiple folders listed in it (colon
+        // ':' delimited)
+        char *tok = strtok(drivers_env_value, ":");
+        if (tok != NULL) {
+            while (tok != NULL) {
+                driver_paths.push_back(tok);
+                tok = strtok(NULL, ":");
+            }
+        } else {
+            driver_paths.push_back(drivers_env_value);
+        }
+    }
+
+    // Loop through all folders discovered above.
+    for (size_t dir = 0; dir < driver_paths.size(); dir++) {
+
+        // Just to make things clear, make sure to add a
+        // identifier before the drivers path results.
+        if (dir == 0) {
+            PrintBeginTableRow();
+            PrintTableElement("Standard Paths");
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintEndTableRow();
+        } else if (drivers_path_index >= 0 &&
+                   dir == static_cast<size_t>(drivers_path_index)) {
+            PrintBeginTableRow();
+            PrintTableElement("VK_DRIVERS_PATH");
+            PrintTableElement(drivers_env_value);
+            PrintTableElement("");
+            PrintEndTableRow();
+        }
+
+        // Make sure the directory exists.
+        DIR *driver_dir = opendir(driver_paths[dir].c_str());
+        if (NULL == driver_dir) {
+
+            PrintBeginTableRow();
+            PrintTableElement(driver_paths[dir], ALIGN_RIGHT);
+            PrintTableElement("No such folder");
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            continue;
+        }
+
+        PrintBeginTableRow();
+        PrintTableElement(driver_paths[dir], ALIGN_RIGHT);
+        PrintTableElement("");
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        dirent *cur_ent;
+        i = 0;
+        while ((cur_ent = readdir(driver_dir)) != NULL) {
+            if (NULL != strstr(cur_ent->d_name, ".json")) {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++);
+                snprintf(cur_vulkan_driver_json, MAX_STRING_LENGTH - 1, "%s/%s",
+                         driver_paths[dir].c_str(), cur_ent->d_name);
+
+                PrintBeginTableRow();
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement(cur_ent->d_name);
+                PrintTableElement("");
+                PrintEndTableRow();
+
+                if (ReadDriverJson(cur_vulkan_driver_json, found_this_lib)) {
+                    found_json = true;
+                    found_lib |= found_this_lib;
+                }
+            }
+        }
+    }
+
+    // The user can specify particularly what driver files to use
+    icd_env_value = getenv("VK_ICD_FILENAMES");
+    if (NULL != icd_env_value) {
+        PrintBeginTableRow();
+        PrintTableElement("VK_ICD_FILENAMES");
+        PrintTableElement(icd_env_value);
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        // VK_ICD_FILENAMES may have multiple folders listed in it (colon
+        // ':' delimited)
+        char *tok = strtok(icd_env_value, ":");
+        if (tok != NULL) {
+            while (tok != NULL) {
+                if (access(tok, R_OK) != -1) {
+                    PrintBeginTableRow();
+                    PrintTableElement(tok, ALIGN_RIGHT);
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintEndTableRow();
+                    if (ReadDriverJson(tok, found_this_lib)) {
+                        found_json = true;
+                        found_lib |= found_this_lib;
+                    }
+                } else {
+                    PrintBeginTableRow();
+                    PrintTableElement(tok, ALIGN_RIGHT);
+                    PrintTableElement("No such file");
+                    PrintTableElement("");
+                    PrintEndTableRow();
+                }
+                tok = strtok(NULL, ":");
+            }
+        } else {
+            if (access(icd_env_value, R_OK) != -1) {
+                PrintBeginTableRow();
+                PrintTableElement(icd_env_value, ALIGN_RIGHT);
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintEndTableRow();
+                if (ReadDriverJson(icd_env_value, found_this_lib)) {
+                    found_json = true;
+                    found_lib |= found_this_lib;
+                }
+            } else {
+                PrintBeginTableRow();
+                PrintTableElement(icd_env_value, ALIGN_RIGHT);
+                PrintTableElement("No such file");
+                PrintTableElement("");
+                PrintEndTableRow();
+            }
+        }
+    }
+
+    PrintEndTable();
+
+    if (!found_json) {
+        res = MISSING_DRIVER_JSON;
+    } else if (!found_lib) {
+        res = MISSING_DRIVER_LIB;
+    }
+
+    return res;
+}
+
+// Print out all the runtime files found in a given location.  This way we
+// capture the full state of the system.
+ErrorResults PrintRuntimesInFolder(std::string &folder_loc, std::string &object_name,
+                           bool print_header = true) {
+    DIR *runtime_dir;
+    ErrorResults res = SUCCESSFUL;
+
+    runtime_dir = opendir(folder_loc.c_str());
+    if (NULL != runtime_dir) {
+        bool file_found = false;
+        FILE *pfp;
+        uint32_t i = 0;
+        dirent *cur_ent;
+        std::string command_str;
+        std::stringstream generic_str;
+        char path[1035];
+
+        if (print_header) {
+            PrintBeginTableRow();
+            PrintTableElement(folder_loc, ALIGN_RIGHT);
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintEndTableRow();
+        }
+
+        while ((cur_ent = readdir(runtime_dir)) != NULL) {
+            if (NULL != strstr(cur_ent->d_name, object_name.c_str()) &&
+                strlen(cur_ent->d_name) == 14) {
+
+                // Get the source of this symbolic link
+                command_str = "stat -c%N ";
+                command_str += folder_loc;
+                command_str += "/";
+                command_str += cur_ent->d_name;
+                pfp = popen(command_str.c_str(), "r");
+
+                generic_str << "[" << i++ << "]";
+
+                PrintBeginTableRow();
+                PrintTableElement(generic_str.str(), ALIGN_RIGHT);
+
+                file_found = true;
+
+                if (pfp == NULL) {
+                    PrintTableElement(cur_ent->d_name);
+                    PrintTableElement("Failed to retrieve symbolic link");
+                    res = SYSTEM_CALL_FAILURE;
+                } else {
+                    if (NULL != fgets(path, sizeof(path) - 1, pfp)) {
+                        std::string cmd = path;
+                        size_t arrow_loc = cmd.find("->");
+                        if (arrow_loc == std::string::npos) {
+                            std::string trimmed_path =
+                                TrimWhitespace(path, " \t\n\r\'\"");
+
+                            PrintTableElement(trimmed_path);
+                            PrintTableElement("");
+                        } else {
+                            std::string before_arrow = cmd.substr(0, arrow_loc);
+                            std::string trim_before =
+                                TrimWhitespace(before_arrow, " \t\n\r\'\"");
+                            std::string after_arrow =
+                                cmd.substr(arrow_loc + 2, std::string::npos);
+                            std::string trim_after =
+                                TrimWhitespace(after_arrow, " \t\n\r\'\"");
+                            PrintTableElement(trim_before);
+                            PrintTableElement(trim_after);
+                        }
+                    } else {
+                        PrintTableElement(cur_ent->d_name);
+                        PrintTableElement("Failed to retrieve symbolic link");
+                    }
+
+                    PrintEndTableRow();
+
+                    pclose(pfp);
+                }
+            }
+        }
+        if (!file_found) {
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("No libvulkan.so files found");
+            PrintTableElement("");
+            PrintEndTableRow();
+        }
+        closedir(runtime_dir);
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement(folder_loc, ALIGN_RIGHT);
+        PrintTableElement("No such folder");
+        PrintTableElement("");
+        PrintEndTableRow();
+    }
+
+    return res;
+}
+
+// Utility function to determine if a runtime exists in the folder
+bool CheckRuntime(std::string &folder_loc, std::string &object_name) {
+    return (SUCCESSFUL == PrintRuntimesInFolder(folder_loc, object_name));
+}
+
+// Print out whatever Vulkan runtime information we can gather from the
+// standard system paths, etc.
+ErrorResults PrintRunTimeInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    const char vulkan_so_prefix[] = "libvulkan.so.";
+    char path[1035];
+    char generic_string[MAX_STRING_LENGTH];
+    char buff[PATH_MAX];
+    std::string runtime_dir_name;
+    FILE *pfp;
+    PrintBeginTable("Vulkan Runtimes", 3);
+
+    PrintBeginTableRow();
+    PrintTableElement("Possible Runtime Folders");
+    PrintTableElement("");
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    if (!FindLinuxSystemObject(vulkan_so_prefix, CheckRuntime, false)) {
+        res = VULKAN_CANT_FIND_RUNTIME;
+    }
+
+    ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff) - 1);
+    if (len != -1) {
+        buff[len] = '\0';
+
+        std::string runtime_dir_id = "Runtime Folder Used By via";
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "ldd %s", buff);
+        pfp = popen(generic_string, "r");
+        if (pfp == NULL) {
+            PrintBeginTableRow();
+            PrintTableElement(runtime_dir_id);
+            PrintTableElement("Failed to query via library info");
+            PrintTableElement("");
+            PrintEndTableRow();
+            res = SYSTEM_CALL_FAILURE;
+        } else {
+            bool found = false;
+            while (fgets(path, sizeof(path) - 1, pfp) != NULL) {
+                if (NULL != strstr(path, vulkan_so_prefix)) {
+                    std::string cmd = path;
+                    size_t arrow_loc = cmd.find("=>");
+                    if (arrow_loc == std::string::npos) {
+                        std::string trimmed_path =
+                            TrimWhitespace(path, " \t\n\r\'\"");
+                        PrintBeginTableRow();
+                        PrintTableElement(runtime_dir_id);
+                        PrintTableElement(trimmed_path);
+                        PrintTableElement("");
+                        PrintEndTableRow();
+                    } else {
+                        std::string after_arrow = cmd.substr(arrow_loc + 2);
+                        std::string before_slash =
+                            after_arrow.substr(0, after_arrow.rfind("/"));
+                        std::string trimmed =
+                            TrimWhitespace(before_slash, " \t\n\r\'\"");
+
+                        PrintBeginTableRow();
+                        PrintTableElement(runtime_dir_id);
+                        PrintTableElement(trimmed);
+                        PrintTableElement("");
+                        PrintEndTableRow();
+
+                        std::string find_so = vulkan_so_prefix;
+                        ErrorResults temp_res = PrintRuntimesInFolder(trimmed, find_so, false);
+                        if (!found) {
+                            res = temp_res;
+                        } else {
+                            // We found one runtime, clear any failures
+                            if (res == VULKAN_CANT_FIND_RUNTIME) {
+                                res = SUCCESSFUL;
+                                found = true;
+                            }
+                        }
+                    }
+                    break;
+                }
+            }
+            if (!found) {
+                PrintBeginTableRow();
+                PrintTableElement(runtime_dir_id);
+                PrintTableElement("Failed to find Vulkan SO used for via");
+                PrintTableElement("");
+                PrintEndTableRow();
+            }
+            pclose(pfp);
+        }
+        PrintEndTableRow();
+    }
+
+    PrintEndTable();
+
+    return res;
+}
+
+// Print out the explicit layers that are stored in any of the standard
+// locations.
+ErrorResults PrintExplicitLayersInFolder(std::string &id, std::string &folder_loc) {
+    ErrorResults res = SUCCESSFUL;
+    DIR *layer_dir;
+
+    layer_dir = opendir(folder_loc.c_str());
+    if (NULL != layer_dir) {
+        dirent *cur_ent;
+        std::string cur_layer;
+        char generic_string[MAX_STRING_LENGTH];
+        uint32_t i = 0;
+        bool found_json = false;
+
+        PrintBeginTableRow();
+        PrintTableElement(id, ALIGN_RIGHT);
+        PrintTableElement(folder_loc);
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        // Loop through each JSON in a given folder
+        while ((cur_ent = readdir(layer_dir)) != NULL) {
+            if (NULL != strstr(cur_ent->d_name, ".json")) {
+                found_json = true;
+
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", i++);
+                cur_layer = folder_loc;
+                cur_layer += "/";
+                cur_layer += cur_ent->d_name;
+
+                // Parse the JSON file
+                std::ifstream *stream = NULL;
+                stream = new std::ifstream(cur_layer, std::ifstream::in);
+                if (nullptr == stream || stream->fail()) {
+                    PrintBeginTableRow();
+                    PrintTableElement(generic_string, ALIGN_RIGHT);
+                    PrintTableElement(cur_ent->d_name);
+                    PrintTableElement("ERROR reading JSON file!");
+                    PrintEndTableRow();
+                    res = MISSING_LAYER_JSON;
+                } else {
+                    Json::Value root = Json::nullValue;
+                    Json::Reader reader;
+                    if (!reader.parse(*stream, root, false) || root.isNull()) {
+                        // Report to the user the failure and their
+                        // locations in the document.
+                        PrintBeginTableRow();
+                        PrintTableElement(generic_string, ALIGN_RIGHT);
+                        PrintTableElement(cur_ent->d_name);
+                        PrintTableElement(reader.getFormattedErrorMessages());
+                        PrintEndTableRow();
+                        res = LAYER_JSON_PARSING_ERROR;
+                    } else {
+                        PrintBeginTableRow();
+                        PrintTableElement(generic_string, ALIGN_RIGHT);
+                        PrintTableElement(cur_ent->d_name);
+                        PrintTableElement("");
+                        PrintEndTableRow();
+
+                        // Dump out the standard explicit layer information.
+                        PrintExplicitLayerJsonInfo(cur_layer.c_str(), root, 3);
+                    }
+
+                    stream->close();
+                    delete stream;
+                    stream = NULL;
+                }
+            }
+        }
+        if (!found_json) {
+            PrintBeginTableRow();
+            PrintTableElement(id, ALIGN_RIGHT);
+            PrintTableElement(folder_loc);
+            PrintTableElement("No JSON files found");
+            PrintEndTableRow();
+        }
+        closedir(layer_dir);
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement(id, ALIGN_RIGHT);
+        PrintTableElement(folder_loc);
+        PrintTableElement("No such folder");
+        PrintEndTableRow();
+    }
+
+    return res;
+}
+
+// Print out information on whatever LunarG Vulkan SDKs we can find on
+// the system using the standard locations and environmental variables.
+// This includes listing what layers are available from the SDK.
+ErrorResults PrintSDKInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    bool sdk_exists = false;
+    std::string sdk_path;
+    std::string sdk_env_name;
+    const char vulkan_so_prefix[] = "libvulkan.so.";
+    DIR *sdk_dir;
+    dirent *cur_ent;
+    char *env_value;
+
+    PrintBeginTable("LunarG Vulkan SDKs", 3);
+
+    for (uint32_t dir = 0; dir < 2; dir++) {
+        switch (dir) {
+        case 0:
+            sdk_env_name = "VK_SDK_PATH";
+            env_value = getenv(sdk_env_name.c_str());
+            if (env_value == NULL) {
+                continue;
+            }
+            sdk_path = env_value;
+            break;
+        case 1:
+            sdk_env_name = "VULKAN_SDK";
+            env_value = getenv(sdk_env_name.c_str());
+            if (env_value == NULL) {
+                continue;
+            }
+            sdk_path = env_value;
+            break;
+        default:
+            res = UNKNOWN_ERROR;
+            continue;
+        }
+
+        std::string explicit_layer_path = sdk_path;
+        explicit_layer_path += "/etc/explicit_layer.d";
+
+        sdk_dir = opendir(explicit_layer_path.c_str());
+        if (NULL != sdk_dir) {
+            while ((cur_ent = readdir(sdk_dir)) != NULL) {
+                if (NULL != strstr(cur_ent->d_name, vulkan_so_prefix) &&
+                    strlen(cur_ent->d_name) == 14) {
+                }
+            }
+            closedir(sdk_dir);
+
+            res = PrintExplicitLayersInFolder(sdk_env_name,
+                                              explicit_layer_path);
+
+            global_items.sdk_found = true;
+            global_items.sdk_path = sdk_path;
+            sdk_exists = true;
+        }
+    }
+
+    if (!sdk_exists) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("No installed SDKs found");
+        PrintTableElement("");
+        PrintEndTableRow();
+    }
+
+    PrintEndTable();
+
+    return res;
+}
+
+// Print out whatever layers we can find out from other environmental
+// variables that may be used to point the Vulkan loader at a layer path.
+ErrorResults PrintLayerInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    uint32_t i = 0;
+    char generic_string[MAX_STRING_LENGTH];
+    char cur_vulkan_layer_json[MAX_STRING_LENGTH];
+    DIR *layer_dir;
+    dirent *cur_ent;
+    std::string layer_path;
+    char *env_value = NULL;
+
+    // Dump out implicit layer information first
+    PrintBeginTable("Implicit Layers", 3);
+
+    // There are several folders implicit layers could be in.  So,
+    // try all of them.
+    for (uint32_t dir = 0; dir < 5; dir++) {
+        std::string cur_layer_path;
+        switch (dir) {
+        case 0:
+            cur_layer_path = "/etc/vulkan/implicit_layer.d";
+            break;
+        case 1:
+            cur_layer_path = "/usr/share/vulkan/implicit_layer.d";
+            break;
+        case 2:
+            cur_layer_path = "/usr/local/etc/vulkan/implicit_layer.d";
+            break;
+        case 3:
+            cur_layer_path = "/usr/local/share/vulkan/implicit_layer.d";
+            break;
+        case 4:
+            env_value = getenv("HOME");
+            if (NULL == env_value) {
+                cur_layer_path = "~/.local/share/vulkan/implicit_layer.d";
+            } else {
+                cur_layer_path = env_value;
+                cur_layer_path += "/.local/share/vulkan/implicit_layer.d";
+            }
+            break;
+        default:
+            continue;
+        }
+
+        layer_dir = opendir(cur_layer_path.c_str());
+        if (NULL != layer_dir) {
+            PrintBeginTableRow();
+            PrintTableElement(cur_layer_path, ALIGN_RIGHT);
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintEndTableRow();
+            while ((cur_ent = readdir(layer_dir)) != NULL) {
+                if (NULL != strstr(cur_ent->d_name, ".json")) {
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]",
+                             i++);
+                    snprintf(cur_vulkan_layer_json, MAX_STRING_LENGTH - 1,
+                             "%s/%s", cur_layer_path.c_str(), cur_ent->d_name);
+
+                    PrintBeginTableRow();
+                    PrintTableElement(generic_string, ALIGN_RIGHT);
+                    PrintTableElement(cur_ent->d_name);
+                    PrintTableElement("");
+                    PrintEndTableRow();
+
+                    std::ifstream *stream = NULL;
+                    stream = new std::ifstream(cur_vulkan_layer_json,
+                                               std::ifstream::in);
+                    if (nullptr == stream || stream->fail()) {
+                        PrintBeginTableRow();
+                        PrintTableElement("");
+                        PrintTableElement("ERROR reading JSON file!");
+                        PrintTableElement("");
+                        PrintEndTableRow();
+                        res = MISSING_LAYER_JSON;
+                    } else {
+                        Json::Value root = Json::nullValue;
+                        Json::Reader reader;
+                        if (!reader.parse(*stream, root, false) ||
+                            root.isNull()) {
+                            // Report to the user the failure and their
+                            // locations in the document.
+                            PrintBeginTableRow();
+                            PrintTableElement("");
+                            PrintTableElement("ERROR parsing JSON file!");
+                            PrintTableElement(
+                                reader.getFormattedErrorMessages());
+                            PrintEndTableRow();
+                            res = LAYER_JSON_PARSING_ERROR;
+                        } else {
+                            PrintExplicitLayerJsonInfo(cur_vulkan_layer_json,
+                                                       root, 3);
+                        }
+
+                        stream->close();
+                        delete stream;
+                        stream = NULL;
+                    }
+                }
+            }
+            closedir(layer_dir);
+        } else {
+            PrintBeginTableRow();
+            PrintTableElement(cur_layer_path, ALIGN_RIGHT);
+            PrintTableElement("Directory does not exist");
+            PrintTableElement("");
+            PrintEndTableRow();
+        }
+    }
+    PrintEndTable();
+
+    // Dump out any explicit layer information.
+    PrintBeginTable("Explicit Layers", 3);
+
+    PrintBeginTableRow();
+    PrintTableElement("Standard Paths");
+    PrintTableElement("");
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    // There are several folders explicit layers could be in.  So,
+    // try all of them.
+    for (uint32_t dir = 0; dir < 5; dir++) {
+        std::string cur_layer_path;
+        std::string explicit_layer_id;
+        std::string explicit_layer_path = cur_layer_path;
+        char *env_value = NULL;
+        switch (dir) {
+        case 0:
+            cur_layer_path = "/etc/vulkan/explicit_layer.d";
+            explicit_layer_id = "/etc/vulkan";
+            break;
+        case 1:
+            cur_layer_path = "/usr/share/vulkan/explicit_layer.d";
+            explicit_layer_id = "/usr/share/vulkan";
+            break;
+        case 2:
+            cur_layer_path = "/usr/local/etc/vulkan/explicit_layer.d";
+            explicit_layer_id = "/usr/local/etc/vulkan";
+            break;
+        case 3:
+            cur_layer_path = "/usr/local/share/vulkan/explicit_layer.d";
+            explicit_layer_id = "/usr/local/share/vulkan";
+            break;
+        case 4:
+            explicit_layer_id = "$HOME/.local/share/vulkan/explicit_layer.d";
+            env_value = getenv("HOME");
+            if (NULL == env_value) {
+                cur_layer_path = "~/.local/share/vulkan/explicit_layer.d";
+            } else {
+                cur_layer_path = env_value;
+                cur_layer_path += "/.local/share/vulkan/explicit_layer.d";
+            }
+            break;
+        default:
+            continue;
+        }
+
+        res = PrintExplicitLayersInFolder(explicit_layer_id, cur_layer_path);
+    }
+
+    // Look at the VK_LAYER_PATH environment variable paths if it is set.
+    env_value = getenv("VK_LAYER_PATH");
+    std::string cur_json;
+    if (NULL != env_value) {
+        char *tok = strtok(env_value, ":");
+        std::string explicit_layer_id = "VK_LAYER_PATH";
+
+        PrintBeginTableRow();
+        PrintTableElement("VK_LAYER_PATH");
+        PrintTableElement("");
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        if (NULL != tok) {
+            uint32_t offset = 0;
+            std::stringstream cur_name;
+            while (NULL != tok) {
+                cur_json = tok;
+                cur_name.str("");
+                cur_name << "Path " << offset++;
+                explicit_layer_id = cur_name.str();
+                res = PrintExplicitLayersInFolder(explicit_layer_id, cur_json);
+                tok = strtok(NULL, ":");
+            }
+        } else {
+            cur_json = env_value;
+            res = PrintExplicitLayersInFolder(explicit_layer_id, cur_json);
+        }
+    }
+
+    PrintEndTable();
+
+    return res;
+}
+
+// Run the test in the specified directory with the corresponding
+// command-line arguments.
+// Returns 0 on no error, 1 if test file wasn't found, and -1
+// on any other errors.
+int RunTestInDirectory(std::string path, std::string test,
+                       std::string cmd_line) {
+    char orig_dir[MAX_STRING_LENGTH];
+    int err_code = -1;
+    orig_dir[0] = '\0';
+    if (NULL != getcwd(orig_dir, MAX_STRING_LENGTH - 1)) {
+        int err = chdir(path.c_str());
+        if (-1 != err) {
+            if (-1 != access(test.c_str(), X_OK)) {
+                printf("cmd_line - %s\n", cmd_line.c_str());
+                err_code = system(cmd_line.c_str());
+            } else {
+                // Can't run because it's either not there or an actual
+                // exe.  So, just return a separate error code.
+                err_code = 1;
+            }
+        } else {
+            // Path doesn't exist at all
+            err_code = 1;
+        }
+        chdir(orig_dir);
+    }
+    return err_code;
+}
+
+#endif
+
+// Following functions should be OS agnostic:
+//==========================================
+
+// Trim any whitespace preceeding or following the actual
+// content inside of a string.  The actual items labeled
+// as whitespace are passed in as the second set of
+// parameters.
+std::string TrimWhitespace(const std::string &str,
+                           const std::string &whitespace) {
+    const auto strBegin = str.find_first_not_of(whitespace);
+    if (strBegin == std::string::npos) {
+        return ""; // no content
+    }
+
+    const auto strEnd = str.find_last_not_of(whitespace);
+    const auto strRange = strEnd - strBegin + 1;
+
+    return str.substr(strBegin, strRange);
+}
+
+// Print any information found on the current vk_layer_settings.txt
+// file being used.  It looks in the current folder first, and then will
+// look in any defined by the registry variable VK_LAYER_SETTINGS_PATH.
+ErrorResults PrintLayerSettingsFileInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    char *settings_path = NULL;
+    std::string settings_file;
+    std::map<std::string, std::vector<SettingPair>> settings;
+
+    PrintBeginTable("Layer Settings File", 4);
+
+// If the settings path environment variable is set, use that.
+#ifdef _WIN32
+    char generic_string[MAX_STRING_LENGTH];
+    if (0 != GetEnvironmentVariableA("VK_LAYER_SETTINGS_PATH", generic_string,
+                                     MAX_STRING_LENGTH - 1)) {
+        settings_path = generic_string;
+        settings_file = settings_path;
+        settings_file += '\\';
+    }
+#else
+    settings_path = getenv("VK_LAYER_SETTINGS_PATH");
+    if (NULL != settings_path) {
+        settings_file = settings_path;
+        settings_file += '/';
+    }
+#endif
+    settings_file += "vk_layer_settings.txt";
+
+    PrintBeginTableRow();
+    PrintTableElement("VK_LAYER_SETTINGS_PATH");
+    if (NULL != settings_path) {
+        PrintTableElement(settings_path);
+    } else {
+        PrintTableElement("Not Defined");
+    }
+    PrintTableElement("");
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    // Load the file from the appropriate location
+    PrintBeginTableRow();
+    PrintTableElement("Settings File");
+    PrintTableElement("vk_layer_settings.txt");
+    std::ifstream *settings_stream =
+        new std::ifstream(settings_file, std::ifstream::in);
+    if (nullptr == settings_stream || settings_stream->fail()) {
+        // No file was found.  This is NOT an error.
+        PrintTableElement("Not Found");
+        PrintTableElement("");
+        PrintEndTableRow();
+    } else {
+        // We found a file, so parse it.
+        PrintTableElement("Found");
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        // The settings file is a text file where:
+        //  - # indicates a comment
+        //  - Settings are stored in the fasion:
+        //        <layer_name>.<setting> = <value>
+        while (settings_stream->good()) {
+            std::string cur_line;
+            getline(*settings_stream, cur_line);
+            std::string trimmed_line = TrimWhitespace(cur_line);
+
+            // Skip blank and comment lines
+            if (trimmed_line.length() == 0 || trimmed_line.c_str()[0] == '#') {
+                continue;
+            }
+
+            // If no equal, treat as unknown
+            size_t equal_loc = trimmed_line.find("=");
+            if (equal_loc == std::string::npos) {
+                continue;
+            }
+
+            SettingPair new_pair;
+
+            std::string before_equal = trimmed_line.substr(0, equal_loc);
+            std::string after_equal =
+                trimmed_line.substr(equal_loc + 1, std::string::npos);
+            new_pair.value = TrimWhitespace(after_equal);
+
+            std::string trimmed_setting = TrimWhitespace(before_equal);
+
+            // Look for period
+            std::string setting_layer = "--None--";
+            std::string setting_name = "";
+            size_t period_loc = trimmed_setting.find(".");
+            if (period_loc == std::string::npos) {
+                setting_name = trimmed_setting;
+            } else {
+                setting_layer = trimmed_setting.substr(0, period_loc);
+                setting_name =
+                    trimmed_setting.substr(period_loc + 1, std::string::npos);
+            }
+            new_pair.name = setting_name;
+
+            // Add items to settings map for now
+            if (settings.find(setting_layer) == settings.end()) {
+                // Not found
+                std::vector<SettingPair> new_vector;
+                new_vector.push_back(new_pair);
+                settings[setting_layer] = new_vector;
+            } else {
+                // Already exists
+                std::vector<SettingPair> &cur_vector = settings[setting_layer];
+                cur_vector.push_back(new_pair);
+            }
+        }
+
+        // Now that all items have been grouped in the settings map
+        // appropriately, print
+        // them out
+        for (auto layer_iter = settings.begin(); layer_iter != settings.end();
+             layer_iter++) {
+            std::vector<SettingPair> &cur_vector = layer_iter->second;
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement(layer_iter->first, ALIGN_RIGHT);
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintEndTableRow();
+            for (uint32_t cur_item = 0; cur_item < cur_vector.size();
+                 cur_item++) {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintTableElement(cur_vector[cur_item].name);
+                PrintTableElement(cur_vector[cur_item].value);
+                PrintEndTableRow();
+            }
+        }
+
+        settings_stream->close();
+        delete settings_stream;
+    }
+    PrintEndTable();
+
+    return res;
+}
+
+// Print out the information stored in an explicit layer's JSON file.
+void PrintExplicitLayerJsonInfo(const char *layer_json_filename,
+                                Json::Value root, uint32_t num_cols) {
+    char generic_string[MAX_STRING_LENGTH];
+    uint32_t cur_col;
+    uint32_t ext;
+    if (!root["layer"].isNull()) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Name");
+        if (!root["layer"]["name"].isNull()) {
+            PrintTableElement(root["layer"]["name"].asString());
+        } else {
+            PrintTableElement("MISSING!");
+        }
+        cur_col = 3;
+        while (num_cols > cur_col) {
+            PrintTableElement("");
+            cur_col++;
+        }
+        PrintEndTableRow();
+
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Description");
+        if (!root["layer"]["description"].isNull()) {
+            PrintTableElement(root["layer"]["description"].asString());
+        } else {
+            PrintTableElement("MISSING!");
+        }
+        cur_col = 3;
+        while (num_cols > cur_col) {
+            PrintTableElement("");
+            cur_col++;
+        }
+        PrintEndTableRow();
+
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("API Version");
+        if (!root["layer"]["api_version"].isNull()) {
+            PrintTableElement(root["layer"]["api_version"].asString());
+        } else {
+            PrintTableElement("MISSING!");
+        }
+        cur_col = 3;
+        while (num_cols > cur_col) {
+            PrintTableElement("");
+            cur_col++;
+        }
+        PrintEndTableRow();
+
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("JSON File Version");
+        if (!root["file_format_version"].isNull()) {
+            PrintTableElement(root["file_format_version"].asString());
+        } else {
+            PrintTableElement("MISSING!");
+        }
+        cur_col = 3;
+        while (num_cols > cur_col) {
+            PrintTableElement("");
+            cur_col++;
+        }
+        PrintEndTableRow();
+
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Library Path");
+        if (!root["layer"]["library_path"].isNull()) {
+            PrintTableElement(root["layer"]["library_path"].asString());
+            cur_col = 3;
+            while (num_cols > cur_col) {
+                PrintTableElement("");
+                cur_col++;
+            }
+            PrintEndTableRow();
+
+#ifdef _WIN32
+            // On Windows, we can query the file version, so do so.
+            char full_layer_path[MAX_STRING_LENGTH];
+            if (GenerateLibraryPath(
+                    layer_json_filename,
+                    root["layer"]["library_path"].asString().c_str(),
+                    MAX_STRING_LENGTH, full_layer_path) &&
+                GetFileVersion(full_layer_path, MAX_STRING_LENGTH,
+                               generic_string)) {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("Layer File Version");
+                PrintTableElement(generic_string);
+                cur_col = 3;
+                while (num_cols > cur_col) {
+                    PrintTableElement("");
+                    cur_col++;
+                }
+                PrintEndTableRow();
+            }
+#endif
+        } else {
+            PrintTableElement("MISSING!");
+            cur_col = 3;
+            while (num_cols > cur_col) {
+                PrintTableElement("");
+                cur_col++;
+            }
+            PrintEndTableRow();
+        }
+
+        char count_str[MAX_STRING_LENGTH];
+        Json::Value dev_exts = root["layer"]["device_extensions"];
+        ext = 0;
+        if (!dev_exts.isNull() && dev_exts.isArray()) {
+            snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", dev_exts.size());
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Device Extensions");
+            PrintTableElement(count_str);
+            cur_col = 3;
+            while (num_cols > cur_col) {
+                PrintTableElement("");
+                cur_col++;
+            }
+            PrintEndTableRow();
+
+            for (Json::ValueIterator dev_ext_it = dev_exts.begin();
+                 dev_ext_it != dev_exts.end(); dev_ext_it++) {
+                Json::Value dev_ext = (*dev_ext_it);
+                Json::Value dev_ext_name = dev_ext["name"];
+                if (!dev_ext_name.isNull()) {
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]",
+                             ext);
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement(generic_string, ALIGN_RIGHT);
+                    PrintTableElement(dev_ext_name.asString());
+                    cur_col = 3;
+                    while (num_cols > cur_col) {
+                        PrintTableElement("");
+                        cur_col++;
+                    }
+                    PrintEndTableRow();
+                }
+            }
+        }
+        Json::Value inst_exts = root["layer"]["instance_extensions"];
+        ext = 0;
+        if (!inst_exts.isNull() && inst_exts.isArray()) {
+            snprintf(count_str, MAX_STRING_LENGTH - 1, "%d", inst_exts.size());
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Instance Extensions");
+            PrintTableElement(count_str);
+            cur_col = 3;
+            while (num_cols > cur_col) {
+                PrintTableElement("");
+                cur_col++;
+            }
+            PrintEndTableRow();
+
+            for (Json::ValueIterator inst_ext_it = inst_exts.begin();
+                 inst_ext_it != inst_exts.end(); inst_ext_it++) {
+                Json::Value inst_ext = (*inst_ext_it);
+                Json::Value inst_ext_name = inst_ext["name"];
+                if (!inst_ext_name.isNull()) {
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]",
+                             ext);
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement(generic_string, ALIGN_RIGHT);
+                    PrintTableElement(inst_ext_name.asString());
+                    cur_col = 3;
+                    while (num_cols > cur_col) {
+                        PrintTableElement("");
+                        cur_col++;
+                    }
+                    PrintEndTableRow();
+                }
+            }
+        }
+    } else {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Layer Section");
+        PrintTableElement("MISSING!");
+        cur_col = 3;
+        while (num_cols > cur_col) {
+            PrintTableElement("");
+            cur_col++;
+        }
+        PrintEndTableRow();
+    }
+}
+
+// Print out the information about an Implicit layer stored in
+// it's JSON file.  For the most part, it is similar to an
+// explicit layer, so we re-use that code.  However, implicit
+// layers have a DISABLE environment variable that can be used
+// to disable the layer by default.  Additionally, some implicit
+// layers have an ENABLE environment variable so that they are
+// disabled by default, but can be enabled.
+void PrintImplicitLayerJsonInfo(const char *layer_json_filename,
+                                Json::Value root) {
+    bool enabled = true;
+    std::string enable_env_variable = "--NONE--";
+    bool enable_var_set = false;
+    char enable_env_value[16];
+    std::string disable_env_variable = "--NONE--";
+    bool disable_var_set = false;
+    char disable_env_value[16];
+
+    PrintExplicitLayerJsonInfo(layer_json_filename, root, 4);
+
+    Json::Value enable = root["layer"]["enable_environment"];
+    if (!enable.isNull()) {
+        for (Json::Value::iterator en_iter = enable.begin();
+             en_iter != enable.end(); en_iter++) {
+            if (en_iter.key().isNull()) {
+                continue;
+            }
+            enable_env_variable = en_iter.key().asString();
+            // If an enable define exists, set it to disabled by default.
+            enabled = false;
+#ifdef _WIN32
+            if (0 != GetEnvironmentVariableA(enable_env_variable.c_str(),
+                                             enable_env_value, 15)) {
+#else
+            char *enable_env = getenv(enable_env_variable.c_str());
+            if (NULL != enable_env) {
+                strncpy(enable_env_value, enable_env, 15);
+                enable_env_value[15] = '\0';
+#endif
+                if (atoi(enable_env_value) != 0) {
+                    enable_var_set = true;
+                    enabled = true;
+                }
+            }
+            break;
+        }
+    }
+    Json::Value disable = root["layer"]["disable_environment"];
+    if (!disable.isNull()) {
+        for (Json::Value::iterator dis_iter = disable.begin();
+             dis_iter != disable.end(); dis_iter++) {
+            if (dis_iter.key().isNull()) {
+                continue;
+            }
+            disable_env_variable = dis_iter.key().asString();
+#ifdef _WIN32
+            if (0 != GetEnvironmentVariableA(disable_env_variable.c_str(),
+                                             disable_env_value, 15)) {
+#else
+            char *disable_env = getenv(disable_env_variable.c_str());
+            if (NULL != disable_env) {
+                strncpy(disable_env_value, disable_env, 15);
+                disable_env_value[15] = '\0';
+#endif
+                if (atoi(disable_env_value) > 0) {
+                    disable_var_set = true;
+                    enabled = false;
+                }
+            }
+            break;
+        }
+    }
+
+    // Print the overall state (ENABLED or DISABLED) so we can
+    // quickly determine if this layer is being used.
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("Enabled State");
+    PrintTableElement(enabled ? "ENABLED" : "DISABLED");
+    PrintTableElement("");
+    PrintEndTableRow();
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("Enable Env Var", ALIGN_RIGHT);
+    PrintTableElement(enable_env_variable);
+    if (enable_var_set) {
+        PrintTableElement("");
+    } else {
+        PrintTableElement("Not Defined");
+    }
+    PrintEndTableRow();
+    PrintBeginTableRow();
+    PrintTableElement("");
+    PrintTableElement("Disable Env Var", ALIGN_RIGHT);
+    PrintTableElement(disable_env_variable);
+    if (disable_var_set) {
+        PrintTableElement(disable_env_value);
+    } else {
+        PrintTableElement("Not Defined");
+    }
+    PrintEndTableRow();
+}
+
+// Perform Vulkan commands to find out what extensions are available
+// to a Vulkan Instance, and attempt to create one.
+ErrorResults PrintInstanceInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    VkApplicationInfo app_info;
+    VkInstanceCreateInfo inst_info;
+    uint32_t ext_count;
+    std::vector<VkExtensionProperties> ext_props;
+    VkResult status;
+    char generic_string[MAX_STRING_LENGTH];
+
+    memset(&app_info, 0, sizeof(VkApplicationInfo));
+    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+    app_info.pNext = NULL;
+    app_info.pApplicationName = "via";
+    app_info.applicationVersion = 1;
+    app_info.pEngineName = "via";
+    app_info.engineVersion = 1;
+    app_info.apiVersion = VK_API_VERSION_1_0;
+
+    memset(&inst_info, 0, sizeof(VkInstanceCreateInfo));
+    inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+    inst_info.pNext = NULL;
+    inst_info.pApplicationInfo = &app_info;
+    inst_info.enabledLayerCount = 0;
+    inst_info.ppEnabledLayerNames = NULL;
+    inst_info.enabledExtensionCount = 0;
+    inst_info.ppEnabledExtensionNames = NULL;
+
+    PrintBeginTable("Instance", 3);
+
+    PrintBeginTableRow();
+    PrintTableElement("vkEnumerateInstanceExtensionProperties");
+    status = vkEnumerateInstanceExtensionProperties(NULL, &ext_count, NULL);
+    if (status) {
+        snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                 "ERROR: Failed to determine num inst extensions - %d", status);
+        PrintTableElement(generic_string);
+        PrintTableElement("");
+        PrintEndTableRow();
+        res = VULKAN_CANT_FIND_EXTENSIONS;
+    } else {
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d extensions found",
+                 ext_count);
+        PrintTableElement(generic_string);
+        PrintTableElement("");
+        PrintEndTableRow();
+
+        ext_props.resize(ext_count);
+        status = vkEnumerateInstanceExtensionProperties(NULL, &ext_count,
+                                                        ext_props.data());
+        if (status) {
+            PrintBeginTableRow();
+            PrintTableElement("");
+            snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                     "ERROR: Failed to enumerate inst extensions - %d", status);
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+            res = VULKAN_CANT_FIND_EXTENSIONS;
+        } else {
+            for (uint32_t iii = 0; iii < ext_count; iii++) {
+                PrintBeginTableRow();
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", iii);
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement(ext_props[iii].extensionName);
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "Spec Vers %d",
+                         ext_props[iii].specVersion);
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+            }
+        }
+    }
+
+    PrintBeginTableRow();
+    PrintTableElement("vkCreateInstance");
+    status = vkCreateInstance(&inst_info, NULL, &global_items.instance);
+    if (status == VK_ERROR_INCOMPATIBLE_DRIVER) {
+        PrintTableElement("ERROR: Incompatible Driver");
+        res = VULKAN_CANT_FIND_DRIVER;
+    } else if (status == VK_ERROR_OUT_OF_HOST_MEMORY) {
+        PrintTableElement("ERROR: Out of memory");
+        res = VULKAN_FAILED_OUT_OF_MEM;
+    } else if (status) {
+        snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                 "ERROR: Failed to create - %d", status);
+        PrintTableElement(generic_string);
+        res = VULKAN_FAILED_CREATE_INSTANCE;
+    } else {
+        PrintTableElement("SUCCESSFUL");
+    }
+    PrintTableElement("");
+    PrintEndTableRow();
+    PrintEndTable();
+
+    return res;
+}
+
+// Print out any information we can find out about physical devices using
+// the Vulkan commands.  There should be one for each Vulkan capable device
+// on the system.
+ErrorResults PrintPhysDevInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    VkPhysicalDeviceProperties props;
+    std::vector<VkPhysicalDevice> phys_devices;
+    VkResult status;
+    char generic_string[MAX_STRING_LENGTH];
+    uint32_t gpu_count = 0;
+    uint32_t iii;
+    uint32_t jjj;
+
+    PrintBeginTable("Physical Devices", 4);
+
+    PrintBeginTableRow();
+    PrintTableElement("vkEnumeratePhysicalDevices");
+    status =
+        vkEnumeratePhysicalDevices(global_items.instance, &gpu_count, NULL);
+    if (status) {
+        snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                 "ERROR: Failed to query - %d", status);
+        PrintTableElement(generic_string);
+        res = VULKAN_CANT_FIND_DRIVER;
+        goto out;
+    } else {
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", gpu_count);
+        PrintTableElement(generic_string);
+    }
+    PrintTableElement("");
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    phys_devices.resize(gpu_count);
+    global_items.phys_devices.resize(gpu_count);
+    status = vkEnumeratePhysicalDevices(global_items.instance, &gpu_count,
+                                        phys_devices.data());
+    if (VK_SUCCESS != status && VK_INCOMPLETE != status) {
+        PrintBeginTableRow();
+        PrintTableElement("");
+        PrintTableElement("Failed to enumerate physical devices!");
+        PrintTableElement("");
+        PrintEndTableRow();
+        res = VULKAN_CANT_FIND_DRIVER;
+        goto out;
+    }
+    for (iii = 0; iii < gpu_count; iii++) {
+        global_items.phys_devices[iii].vulkan_phys_dev = phys_devices[iii];
+
+        PrintBeginTableRow();
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", iii);
+        PrintTableElement(generic_string, ALIGN_RIGHT);
+        if (status) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                     "ERROR: Failed to query - %d", status);
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintEndTableRow();
+        } else {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%p",
+                     phys_devices[iii]);
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            vkGetPhysicalDeviceProperties(phys_devices[iii], &props);
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Vendor");
+            switch (props.vendorID) {
+            case 0x8086:
+            case 0x8087:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                         "Intel [0x%04x]", props.vendorID);
+                break;
+            case 0x1002:
+            case 0x1022:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "AMD [0x%04x]",
+                         props.vendorID);
+                break;
+            case 0x10DE:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                         "Nvidia [0x%04x]", props.vendorID);
+                break;
+            case 0x1EB5:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "ARM [0x%04x]",
+                         props.vendorID);
+                break;
+            case 0x5143:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                         "Qualcomm [0x%04x]", props.vendorID);
+                break;
+            case 0x1099:
+            case 0x10C3:
+            case 0x1249:
+            case 0x4E8:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                         "Samsung [0x%04x]", props.vendorID);
+                break;
+            default:
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%04x",
+                         props.vendorID);
+                break;
+            }
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Device Name");
+            PrintTableElement(props.deviceName);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Device ID");
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x",
+                     props.deviceID);
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Device Type");
+            switch (props.deviceType) {
+            case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+                PrintTableElement("Integrated GPU");
+                break;
+            case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+                PrintTableElement("Discrete GPU");
+                break;
+            case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+                PrintTableElement("Virtual GPU");
+                break;
+            case VK_PHYSICAL_DEVICE_TYPE_CPU:
+                PrintTableElement("CPU");
+                break;
+            case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+                PrintTableElement("Other");
+                break;
+            default:
+                PrintTableElement("INVALID!");
+                break;
+            }
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Driver Version");
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d",
+                     VK_VERSION_MAJOR(props.driverVersion),
+                     VK_VERSION_MINOR(props.driverVersion),
+                     VK_VERSION_PATCH(props.driverVersion));
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("API Version");
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d.%d.%d",
+                     VK_VERSION_MAJOR(props.apiVersion),
+                     VK_VERSION_MINOR(props.apiVersion),
+                     VK_VERSION_PATCH(props.apiVersion));
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            uint32_t queue_fam_count;
+            vkGetPhysicalDeviceQueueFamilyProperties(phys_devices[iii],
+                                                     &queue_fam_count, NULL);
+            if (queue_fam_count > 0) {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("Queue Families");
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d",
+                         queue_fam_count);
+                PrintTableElement(generic_string);
+                PrintTableElement("");
+                PrintEndTableRow();
+
+                global_items.phys_devices[iii].queue_fam_props.resize(
+                    queue_fam_count);
+                vkGetPhysicalDeviceQueueFamilyProperties(
+                    phys_devices[iii], &queue_fam_count,
+                    global_items.phys_devices[iii].queue_fam_props.data());
+                for (jjj = 0; jjj < queue_fam_count; jjj++) {
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]",
+                             jjj);
+                    PrintTableElement(generic_string, ALIGN_RIGHT);
+                    PrintTableElement("Queue Count");
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d",
+                             global_items.phys_devices[iii]
+                                 .queue_fam_props[jjj]
+                                 .queueCount);
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement("Queue Flags");
+                    generic_string[0] = '\0';
+                    bool prev_set = false;
+                    if (global_items.phys_devices[iii]
+                            .queue_fam_props[jjj]
+                            .queueFlags &
+                        VK_QUEUE_GRAPHICS_BIT) {
+                        strncat(generic_string, "GRAPHICS",
+                                MAX_STRING_LENGTH - 1);
+                        prev_set = true;
+                    }
+                    if (global_items.phys_devices[iii]
+                            .queue_fam_props[jjj]
+                            .queueFlags &
+                        VK_QUEUE_COMPUTE_BIT) {
+                        if (prev_set) {
+                            strncat(generic_string, " | ",
+                                    MAX_STRING_LENGTH - 1);
+                        }
+                        strncat(generic_string, "COMPUTE",
+                                MAX_STRING_LENGTH - 1);
+                        prev_set = true;
+                    }
+                    if (global_items.phys_devices[iii]
+                            .queue_fam_props[jjj]
+                            .queueFlags &
+                        VK_QUEUE_TRANSFER_BIT) {
+                        if (prev_set) {
+                            strncat(generic_string, " | ",
+                                    MAX_STRING_LENGTH - 1);
+                        }
+                        strncat(generic_string, "TRANSFER",
+                                MAX_STRING_LENGTH - 1);
+                        prev_set = true;
+                    }
+                    if (global_items.phys_devices[iii]
+                            .queue_fam_props[jjj]
+                            .queueFlags &
+                        VK_QUEUE_SPARSE_BINDING_BIT) {
+                        if (prev_set) {
+                            strncat(generic_string, " | ",
+                                    MAX_STRING_LENGTH - 1);
+                        }
+                        strncat(generic_string, "SPARSE_BINDING",
+                                MAX_STRING_LENGTH - 1);
+                        prev_set = true;
+                    }
+                    if (!prev_set) {
+                        strncat(generic_string, "--NONE--",
+                                MAX_STRING_LENGTH - 1);
+                    }
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement("Timestamp Valid Bits");
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x",
+                             global_items.phys_devices[iii]
+                                 .queue_fam_props[jjj]
+                                 .timestampValidBits);
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement("Image Granularity");
+                    PrintTableElement("");
+                    PrintEndTableRow();
+
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement("Width", ALIGN_RIGHT);
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x",
+                             global_items.phys_devices[iii]
+                                 .queue_fam_props[jjj]
+                                 .minImageTransferGranularity.width);
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement("Height", ALIGN_RIGHT);
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x",
+                             global_items.phys_devices[iii]
+                                 .queue_fam_props[jjj]
+                                 .minImageTransferGranularity.height);
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+
+                    PrintBeginTableRow();
+                    PrintTableElement("");
+                    PrintTableElement("");
+                    PrintTableElement("Depth", ALIGN_RIGHT);
+                    snprintf(generic_string, MAX_STRING_LENGTH - 1, "0x%x",
+                             global_items.phys_devices[iii]
+                                 .queue_fam_props[jjj]
+                                 .minImageTransferGranularity.depth);
+                    PrintTableElement(generic_string);
+                    PrintEndTableRow();
+                }
+            } else {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("vkGetPhysicalDeviceQueueFamilyProperties");
+                PrintTableElement("FAILED: Returned 0!");
+                PrintTableElement("");
+                PrintEndTableRow();
+            }
+
+            VkPhysicalDeviceMemoryProperties memory_props;
+            vkGetPhysicalDeviceMemoryProperties(phys_devices[iii],
+                                                &memory_props);
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Memory Heaps");
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d",
+                     memory_props.memoryHeapCount);
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            for (jjj = 0; jjj < memory_props.memoryHeapCount; jjj++) {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", jjj);
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement("Property Flags");
+                generic_string[0] = '\0';
+                bool prev_set = false;
+                if (memory_props.memoryHeaps[jjj].flags &
+                    VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
+                    strncat(generic_string, "DEVICE_LOCAL",
+                            MAX_STRING_LENGTH - 1);
+                    prev_set = true;
+                }
+                if (!prev_set) {
+                    strncat(generic_string, "--NONE--", MAX_STRING_LENGTH - 1);
+                }
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintTableElement("Heap Size");
+                snprintf(
+                    generic_string, MAX_STRING_LENGTH - 1, "%" PRIu64 "",
+                    static_cast<uint64_t>(memory_props.memoryHeaps[jjj].size));
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+            }
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Memory Types");
+            snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d",
+                     memory_props.memoryTypeCount);
+            PrintTableElement(generic_string);
+            PrintTableElement("");
+            PrintEndTableRow();
+
+            for (jjj = 0; jjj < memory_props.memoryTypeCount; jjj++) {
+                PrintBeginTableRow();
+                PrintTableElement("");
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", jjj);
+                PrintTableElement(generic_string, ALIGN_RIGHT);
+                PrintTableElement("Property Flags");
+                generic_string[0] = '\0';
+                bool prev_set = false;
+                if (memory_props.memoryTypes[jjj].propertyFlags &
+                    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+                    strncat(generic_string, "DEVICE_LOCAL",
+                            MAX_STRING_LENGTH - 1);
+                    prev_set = true;
+                }
+                if (memory_props.memoryTypes[jjj].propertyFlags &
+                    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+                    if (prev_set) {
+                        strncat(generic_string, " | ", MAX_STRING_LENGTH - 1);
+                    }
+                    strncat(generic_string, "HOST_VISIBLE",
+                            MAX_STRING_LENGTH - 1);
+                    prev_set = true;
+                }
+                if (memory_props.memoryTypes[jjj].propertyFlags &
+                    VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
+                    if (prev_set) {
+                        strncat(generic_string, " | ", MAX_STRING_LENGTH - 1);
+                    }
+                    strncat(generic_string, "HOST_COHERENT",
+                            MAX_STRING_LENGTH - 1);
+                    prev_set = true;
+                }
+                if (memory_props.memoryTypes[jjj].propertyFlags &
+                    VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
+                    if (prev_set) {
+                        strncat(generic_string, " | ", MAX_STRING_LENGTH - 1);
+                    }
+                    strncat(generic_string, "HOST_CACHED",
+                            MAX_STRING_LENGTH - 1);
+                    prev_set = true;
+                }
+                if (memory_props.memoryTypes[jjj].propertyFlags &
+                    VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
+                    if (prev_set) {
+                        strncat(generic_string, " | ", MAX_STRING_LENGTH - 1);
+                    }
+                    strncat(generic_string, "LAZILY_ALLOC",
+                            MAX_STRING_LENGTH - 1);
+                    prev_set = true;
+                }
+                if (!prev_set) {
+                    strncat(generic_string, "--NONE--", MAX_STRING_LENGTH - 1);
+                }
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+
+                PrintBeginTableRow();
+                PrintTableElement("");
+                PrintTableElement("");
+                PrintTableElement("Heap Index");
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d",
+                         memory_props.memoryTypes[jjj].heapIndex);
+                PrintTableElement(generic_string);
+                PrintEndTableRow();
+            }
+
+            uint32_t num_ext_props;
+            std::vector<VkExtensionProperties> ext_props;
+
+            PrintBeginTableRow();
+            PrintTableElement("");
+            PrintTableElement("Device Extensions");
+            status = vkEnumerateDeviceExtensionProperties(
+                phys_devices[iii], NULL, &num_ext_props, NULL);
+            if (VK_SUCCESS != status) {
+                PrintTableElement("FAILED querying number of extensions");
+                PrintTableElement("");
+                PrintEndTableRow();
+
+                res = VULKAN_CANT_FIND_EXTENSIONS;
+            } else {
+                snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d",
+                         num_ext_props);
+                PrintTableElement(generic_string);
+                ext_props.resize(num_ext_props);
+                status = vkEnumerateDeviceExtensionProperties(
+                    phys_devices[iii], NULL, &num_ext_props, ext_props.data());
+                if (VK_SUCCESS != status) {
+                    PrintTableElement("FAILED querying actual extension info");
+                    PrintEndTableRow();
+
+                    res = VULKAN_CANT_FIND_EXTENSIONS;
+                } else {
+                    PrintTableElement("");
+                    PrintEndTableRow();
+
+                    for (jjj = 0; jjj < num_ext_props; jjj++) {
+                        PrintBeginTableRow();
+                        PrintTableElement("");
+                        snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]",
+                                 jjj);
+                        PrintTableElement(generic_string, ALIGN_RIGHT);
+                        PrintTableElement(ext_props[jjj].extensionName);
+                        snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                                 "Spec Vers %d", ext_props[jjj].specVersion);
+                        PrintTableElement(generic_string);
+                        PrintEndTableRow();
+                    }
+                }
+            }
+        }
+    }
+
+    PrintEndTable();
+
+out:
+
+    return res;
+}
+
+// Using the previously determine information, attempt to create a logical
+// device for each physical device we found.
+ErrorResults PrintLogicalDeviceInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    VkDeviceCreateInfo device_create_info;
+    VkDeviceQueueCreateInfo queue_create_info;
+    VkResult status = VK_SUCCESS;
+    uint32_t dev_count =
+        static_cast<uint32_t>(global_items.phys_devices.size());
+    char generic_string[MAX_STRING_LENGTH];
+    bool found_driver = false;
+
+    PrintBeginTable("Logical Devices", 3);
+
+    PrintBeginTableRow();
+    PrintTableElement("vkCreateDevice");
+    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", dev_count);
+    PrintTableElement(generic_string);
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    global_items.log_devices.resize(dev_count);
+    for (uint32_t dev = 0; dev < dev_count; dev++) {
+        memset(&device_create_info, 0, sizeof(VkDeviceCreateInfo));
+        device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+        device_create_info.pNext = NULL;
+        device_create_info.queueCreateInfoCount = 0;
+        device_create_info.pQueueCreateInfos = NULL;
+        device_create_info.enabledLayerCount = 0;
+        device_create_info.ppEnabledLayerNames = NULL;
+        device_create_info.enabledExtensionCount = 0;
+        device_create_info.ppEnabledExtensionNames = NULL;
+        device_create_info.queueCreateInfoCount = 1;
+        device_create_info.enabledLayerCount = 0;
+        device_create_info.ppEnabledLayerNames = NULL;
+        device_create_info.enabledExtensionCount = 0;
+        device_create_info.ppEnabledExtensionNames = NULL;
+
+        memset(&queue_create_info, 0, sizeof(VkDeviceQueueCreateInfo));
+        float queue_priority = 0;
+        queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        queue_create_info.pNext = NULL;
+        queue_create_info.queueCount = 1;
+        queue_create_info.pQueuePriorities = &queue_priority;
+
+        for (uint32_t queue = 0;
+             queue < global_items.phys_devices[dev].queue_fam_props.size();
+             queue++) {
+            if (0 != (global_items.phys_devices[dev]
+                          .queue_fam_props[queue]
+                          .queueFlags &
+                      VK_QUEUE_GRAPHICS_BIT)) {
+                queue_create_info.queueFamilyIndex = queue;
+                break;
+            }
+        }
+        device_create_info.pQueueCreateInfos = &queue_create_info;
+
+        PrintBeginTableRow();
+        PrintTableElement("");
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", dev);
+        PrintTableElement(generic_string);
+
+        status = vkCreateDevice(global_items.phys_devices[dev].vulkan_phys_dev,
+                                &device_create_info, NULL,
+                                &global_items.log_devices[dev]);
+        if (VK_ERROR_INCOMPATIBLE_DRIVER == status) {
+            PrintTableElement("FAILED: Incompatible Driver");
+            if (!found_driver) {
+                res = VULKAN_CANT_FIND_DRIVER;
+            }
+        } else if (VK_ERROR_OUT_OF_HOST_MEMORY == status) {
+            PrintTableElement("FAILED: Out of Host Memory");
+            // If we haven't already found a driver, set an error
+            if (!found_driver) {
+                res = VULKAN_FAILED_OUT_OF_MEM;
+            }
+        } else if (VK_SUCCESS != status) {
+            snprintf(generic_string, MAX_STRING_LENGTH - 1,
+                     "FAILED : VkResult code = 0x%x", status);
+            PrintTableElement(generic_string);
+            // If we haven't already found a driver, set an error
+            if (!found_driver) {
+                res = VULKAN_FAILED_CREATE_DEVICE;
+            }
+        } else {
+            PrintTableElement("SUCCESSFUL");
+            found_driver = true;
+            // Clear any potential previous errors
+            res = SUCCESSFUL;
+        }
+
+        PrintEndTableRow();
+    }
+
+    PrintEndTable();
+
+    return res;
+}
+
+// Clean up all the Vulkan items we previously created and print
+// out if there are any problems.
+void PrintCleanupInfo(void) {
+    char generic_string[MAX_STRING_LENGTH];
+    uint32_t dev_count =
+        static_cast<uint32_t>(global_items.phys_devices.size());
+
+    PrintBeginTable("Cleanup", 3);
+
+    PrintBeginTableRow();
+    PrintTableElement("vkDestroyDevice");
+    snprintf(generic_string, MAX_STRING_LENGTH - 1, "%d", dev_count);
+    PrintTableElement(generic_string);
+    PrintTableElement("");
+    PrintEndTableRow();
+    for (uint32_t dev = 0; dev < dev_count; dev++) {
+        vkDestroyDevice(global_items.log_devices[dev], NULL);
+        PrintBeginTableRow();
+        PrintTableElement("");
+        snprintf(generic_string, MAX_STRING_LENGTH - 1, "[%d]", dev);
+        PrintTableElement(generic_string, ALIGN_RIGHT);
+        PrintTableElement("SUCCESSFUL");
+        PrintEndTableRow();
+    }
+
+    PrintBeginTableRow();
+    PrintTableElement("vkDestroyInstance");
+    vkDestroyInstance(global_items.instance, NULL);
+    PrintTableElement("SUCCESSFUL");
+    PrintTableElement("");
+    PrintEndTableRow();
+
+    PrintEndTable();
+}
+
+// Run any external tests we can find, and print the results of those
+// tests.
+ErrorResults PrintTestResults(void) {
+    ErrorResults res = SUCCESSFUL;
+
+    BeginSection("External Tests");
+    if (global_items.sdk_found) {
+        std::string cube_exe;
+        std::string full_cmd;
+        std::string path = global_items.sdk_path;
+
+#ifdef _WIN32
+        cube_exe = "cube.exe";
+
+#if _WIN64
+        path += "\\Bin";
+#else
+        path += "\\Bin32";
+#endif
+#else // gcc
+        cube_exe = "./cube";
+        path += "/../examples/build";
+#endif
+        full_cmd = cube_exe;
+        full_cmd += " --c 100";
+
+        PrintBeginTable("Cube", 2);
+
+        PrintBeginTableRow();
+        PrintTableElement(full_cmd);
+        int test_result = RunTestInDirectory(path, cube_exe, full_cmd);
+        if (test_result == 0) {
+            PrintTableElement("SUCCESSFUL");
+        } else if (test_result == 1) {
+            PrintTableElement("Not Found");
+        } else {
+            PrintTableElement("FAILED!");
+            res = TEST_FAILED;
+        }
+        PrintEndTableRow();
+
+        full_cmd += " --validate";
+
+        PrintBeginTableRow();
+        PrintTableElement(full_cmd);
+        test_result = RunTestInDirectory(path, cube_exe, full_cmd);
+        if (test_result == 0) {
+            PrintTableElement("SUCCESSFUL");
+        } else if (test_result == 1) {
+            PrintTableElement("Not Found");
+        } else {
+            PrintTableElement("FAILED!");
+            res = TEST_FAILED;
+        }
+        PrintEndTableRow();
+
+        PrintEndTable();
+    } else {
+        PrintStandardText("No SDK found by VIA, skipping test section");
+    }
+    EndSection();
+
+    return res;
+}
+
+// Print information on any Vulkan commands we can (or can't) execute.
+ErrorResults PrintVulkanInfo(void) {
+    ErrorResults res = SUCCESSFUL;
+    bool created = false;
+    BeginSection("Vulkan API Calls");
+
+    res = PrintInstanceInfo();
+    if (res != SUCCESSFUL) {
+        goto out;
+    }
+    created = true;
+    res = PrintPhysDevInfo();
+    if (res != SUCCESSFUL) {
+        goto out;
+    }
+    res = PrintLogicalDeviceInfo();
+    if (res != SUCCESSFUL) {
+        goto out;
+    }
+
+out:
+    if (created) {
+        PrintCleanupInfo();
+    }
+
+    EndSection();
+
+    return res;
+}
diff --git a/vktrace/.gitignore b/vktrace/.gitignore
new file mode 100644
index 0000000..775651b
--- /dev/null
+++ b/vktrace/.gitignore
@@ -0,0 +1,25 @@
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
+Makefile
+__pycache__
+VKConfig.h
+_out64
+out32/*
+out64/*
+demos/Debug/*
+demos/tri.dir/Debug/*
+demos/tri/Debug/*
+demos/Win32/Debug/*
+demos/xcb_nvidia.dir/*
+libs/Win32/Debug/*
+*.pyc
+*.vcproj
+*.sln
+*.suo
+*.vcxproj
+*.sdf
+*.filters
+build
+dbuild
+src/vktrace_layer/codegen/*
diff --git a/vktrace/CMakeLists.txt b/vktrace/CMakeLists.txt
new file mode 100644
index 0000000..ddd6a22
--- /dev/null
+++ b/vktrace/CMakeLists.txt
@@ -0,0 +1,127 @@
+PROJECT(vktrace_project)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+    add_definitions(-DVK_USE_PLATFORM_WIN32_KHR -DWIN32_LEAN_AND_MEAN)
+    set(DisplayServer Win32)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
+    add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    if (BUILD_WSI_XCB_SUPPORT)
+        add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
+    endif()
+
+    if (BUILD_WSI_XLIB_SUPPORT)
+       add_definitions(-DVK_USE_PLATFORM_XLIB_KHR)
+    endif()
+
+    if (BUILD_WSI_WAYLAND_SUPPORT)
+       # TODO Add Wayland Support
+       # add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
+    endif()
+
+    if (BUILD_WSI_MIR_SUPPORT)
+       # TODO Add Mir Support
+       # add_definitions(-DVK_USE_PLATFORM_MIR_KHR)
+       # include_directories(${MIR_INCLUDE_DIR})
+    endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+    # Only vktrace is supported on macOS
+else()
+    message(FATAL_ERROR "Unsupported Platform!")
+endif()
+
+option(BUILD_VKTRACEVIEWER "Build VkTraceViewer" ON)
+
+if (BUILD_VKTRACEVIEWER)
+    # We need CMake version 3.0+ in order to "find_package(Qt5)":
+    cmake_minimum_required(VERSION 3.0)
+else ()
+    cmake_minimum_required(VERSION 2.8.11)
+endif()
+
+set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
+
+#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${SRC_DIR}/cmake/Modules/")
+#set(CMAKE_EXTERNAL_PATH "${SRC_DIR}/../../external")
+
+if (WIN32)
+    # TODO: s/CMAKE_PREFIX_PATH/CMAKE_EXTERNAL_WINDOWS_PATH/g
+#    set(CMAKE_PREFIX_PATH "${CMAKE_EXTERNAL_PATH}/windows")
+    set(WIN32_PTHREADS_PATH "${SRC_DIR}/thirdparty/pthreads.2")
+    set(WIN32_PTHREADS_INCLUDE_PATH "${WIN32_PTHREADS_PATH}/include")
+endif()
+
+set(PYTHON_EXECUTABLE ${PYTHON_CMD})
+find_package(PythonInterp)
+
+if (NOT PYTHONINTERP_FOUND)
+    message(FATAL_ERROR "Missing PythonInterp. Install python interpreter 2.7 (on linux use cmd: sudo apt-get install python2.7)")
+endif()
+
+#search for QT only if BUILD_VKTRACEVIEWER is ON
+if(BUILD_VKTRACEVIEWER)
+    find_package(Qt5 COMPONENTS Widgets Gui Core Svg)
+    if (NOT Qt5_FOUND)
+        if (WIN32)
+            message(WARNING "Qt5 dev libraries not found, vktraceviewer will not be built.\nTo enable build of vktraceviewer, set env var Qt5_Dir to\nC:\\Qt\\5.3\\msvc2013_64\\lib\\cmake\\Qt5 or C:\\Qt\\5.3\\msvc2013\\lib\\cmake\\Qt5")
+        else()
+            message(WARNING "Qt5 dev libraries not found, vktraceviewer will not be built.\nTo enable build of vktraceviewer, install package qt5-default.")
+        endif()
+    endif()
+endif()
+
+include_directories(
+	${CMAKE_CURRENT_SOURCE_DIR}/../include/vulkan
+)
+
+message("")
+message("cmake options:")
+message("  -DCMAKE_BUILD_TYPE='${CMAKE_BUILD_TYPE}': Build debug or release. (Debug|Release)")
+message("  -DCMAKE_VERBOSE='${CMAKE_VERBOSE}': Spew cmake project options. (On|Off)")
+message("  -DBUILD_X64='${BUILD_X64}': Build 32 or 64-bit. (On|Off)")
+message("")
+
+#
+#  Components to build
+#
+set(VKTRACE_VULKAN_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_subdirectory(src/vktrace_common)
+add_subdirectory(src/vktrace_trace)
+
+option(BUILD_VKTRACE_LAYER "Build vktrace_layer" ON)
+if(BUILD_VKTRACE_LAYER)
+    add_subdirectory(src/vktrace_layer)
+endif()
+option(BUILD_VKTRACE_REPLAY "Build vktrace_replay" ON)
+if(BUILD_VKTRACE_REPLAY)
+    add_subdirectory(src/vktrace_replay)
+endif()
+
+# Only build vktraceviewer if Qt5 is available
+if (Qt5_FOUND AND BUILD_VKTRACEVIEWER)
+    add_subdirectory(src/vktrace_viewer)
+endif()
+
+# use macro from stackoverflow (link below) to get all the extensions that are on the current system
+# http://stackoverflow.com/questions/7787823/cmake-how-to-get-the-name-of-all-subdirectories-of-a-directory
+MACRO(SUBDIRLIST result curdir)
+  FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
+  SET(dirlist "")
+  FOREACH(child ${children})
+    IF(IS_DIRECTORY ${curdir}/${child})
+        LIST(APPEND dirlist ${child})
+    ENDIF()
+  ENDFOREACH()
+  SET(${result} ${dirlist})
+ENDMACRO()
+
+# now generate the list and add each of the subdirectories
+SUBDIRLIST(SUBDIRS ${SRC_DIR}/vktrace_extensions)
+message("Adding extensions: '${SUBDIRS}'")
+FOREACH(subdir ${SUBDIRS})
+    add_subdirectory(${SRC_DIR}/vktrace_extensions/${subdir})
+ENDFOREACH()
+
+
+
diff --git a/vktrace/README.md b/vktrace/README.md
new file mode 100644
index 0000000..d66f87f
--- /dev/null
+++ b/vktrace/README.md
@@ -0,0 +1,108 @@
+Vktrace Trace and Replay Tool
+=============================
+
+Vktrace is a Vulkan API tracer for graphics applications.
+
+##Using Vktrace on Linux###
+Vktrace builds two binaries with associated Vulkan libraries: a tracer with Vulkan
+tracing library and a replayer. The tracing library is a Vulkan layer library.
+
+###Running Vktrace tracer as standalone server on Linux###
+The Vktrace tracer program can run as a server.  Then the app/game to be traced
+is launched separately with the Vktrace tracer library preloaded. To run
+Vktrace as a server one should omit the "-p" option.
+```
+cd <vktrace build directory>
+./vktrace <options>
+Example to trace spinning cube demo.
+export VK_ICD_FILENAMES=/home/jon/LoaderAndValidationLayers/main_icd.json
+export LD_LIBRARY_PATH=/home/jon/LoaderAndValidationLayers/dbuild/loader
+./vktrace -o vktrace_cube.vktrace
+```
+
+In a separate terminal run your app, the cube demo in this example:
+```
+cd /home/jon/LoaderAndValidationLayers/dbuild/demos
+export LD_LIBRARY_PATH=/home/jon/LoaderAndValidationLayers/dbuild/loader
+VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_vktrace VK_DEVICE_LAYERS=VK_LAYER_LUNARG_vktrace ./cube
+```
+
+Trace file is written into "vktrace_cube<number>.vktrace".
+As the app is rerun, the Vktrace tracer server will increment the output file
+number for each successive run of the app.
+
+One can also set VKTRACE_LIB_IPADDR to a remote system IP address. Then
+the tracer inserted into an app will send the trace packets to the remote
+system rather than local system. In this case, the remote system should be
+running the trace server.
+
+###Running Vktrace tracer and launch app/game from tracer on Linux###
+The Vktrace tracer program launches the app/game you desire and then traces it.
+To launch app/game from Vktrace tracer one must use the "-p" option.
+```
+cd <vktrace build dir>
+./vktrace -p <path to app to launch>  <more options>
+```
+Example to trace the spinning cube demo from sample implementation
+```
+export VK_ICD_FILENAMES=/home/jon/LoaderAndValidationLayers/main_icd.json
+export LD_LIBRARY_PATH=/home/jon/LoaderAndValidationLayers/dbuild/loader
+./vktrace -p /home/jon/LoaderAndValidationLayers/dbuild/demos/cube -o vktrace_cube.vktrace -w /home/jon/LoaderAndValidationLayers/dbuild/demos
+```
+Trace file is in "vktrace_cube.vktrace".
+
+###Running replayer on Linux###
+The Vktrace replayer takes  a trace file  and will launch an Vulkan session based
+on trace file.
+```
+cd <vktrace build dir>
+export LD_LIBRARY_PATH=<path to libvulkan.so>
+./vkreplay <options> -t trace_filename
+```
+Example to replay trace file captured above
+```
+export VK_ICD_FILENAMES=/home/jon/LoaderAndValidationLayers/main_icd.json
+export LD_LIBRARY_PATH=/home/jon/LoaderAndValidationLayers/dbuild:/home/jon/LoaderAndValidationLayers/dbuild/loader
+./vkreplay -t vktrace_cube.vktrace
+```
+
+##Using Vktrace on Windows##
+Vktrace builds two binaries with associated Vulkan libraries: a tracer with Vulkan
+tracing library and a replayer. The tracing library is a Vulkan layer library.
+
+
+###Running Vktrace tracer and launch app/game from tracer on Windows###
+The Vktrace tracer program launches the app/game you desire and then traces it.
+To launch app/game from Vktrace tracer one must use the "-p" option.
+Also, you may need to copy the Vulkan.dll library into the directory of Vktrace,
+and of the app/game (while we continue to put Windows support into place).
+```
+cd <vktrace build dir>
+./vktrace -p <path-to-app-to-launch> -w <working-dir-path-of-app>  <more options>
+```
+Example to trace the spinning cube demo (Note: see other files for how to configure your ICD):
+```
+cd C:\\Users\developer\\Vktrace\\_out64\\Debug
+
+vktrace -p C:\\Users\developer\\LoaderAndValidationLayers\\_out64\\demos\\cube.exe
+        -w C:\\Users\developer\\LoaderAndValidationLayers\\_out64\\demos
+        -o vktrace_cube.vktrace
+```
+Trace file is in "vktrace_cube.vktrace".
+
+###Running replayer on Windows###
+The Vktrace replayer takes  a trace file  and will launch an Vulkan session based
+on trace file.
+```
+cd <vktrace build dir>
+vkreplay <options> -t trace_filename
+```
+Example to replay trace file captured above
+```
+cd C:\\Users\developer\\Vktrace\\_out64\\Debug
+vkreplay -t vktrace_cube.vktrace
+```
+##Building Vktrace##
+Vktrace is built as part of top level VulkanTools build. Follow the
+build directions for the top level VulkanTools project build in BUILDVT.md. Vktrace binaries and
+libraries will be placed in <build_dir>.
diff --git a/vktrace/TODO.md b/vktrace/TODO.md
new file mode 100644
index 0000000..dda62f4
--- /dev/null
+++ b/vktrace/TODO.md
@@ -0,0 +1,76 @@
+Here is a list of all supported features in VkTrace, followed by a TODO list of features that we'd like to add soon in the development process. We've also listed the features that we'd "like to have" in the future, but don't have a short-term plan to implement. 
+
+Feel Free to vote things up in the lists, attempt to implement them yourself, or add to the list!
+
+As you complete an item, please copy / paste it into the SUPPORTED FEATURES section.
+
+**SUPPORTED FEATURES IN DEBUGGER**
+* Generating & loading traces
+* Replay traces within the UI w/ pause, continue, stop ability
+  * Auto-pause on Validation Layer Messages (info, warnings, and/or errors), controlled by settings
+  * Single-step the replay
+  * Timeline pointer gets updated in real-time of replayed API call
+  * Run the replay in a separate thread from the UI
+  * Pop-out replay window to be floating so it can replay at larger dimensions
+* Timeline shows CPU time of each API call
+  * A separate timeline is shown for each thread referenced in the trace file
+  * Tooltips display the API call index and entrypoint name and parameters
+  * Click call will cause API Call Tree to highlight call
+* API entrypoints names & parameters displayed in UI
+* Tracing and replay standard output gets directed to Output window
+* Plugin-based UI allows for extensibility to other APIs
+* Search API Call Tree
+  * Search result navigation
+* API Call Tree Enhancements:
+  * Draw call navigation buttons
+  * Draw calls are shown in bold font
+  * "Run to here" context menu option to control where Replayer pauses
+* Group API Calls by:
+  * Frame boundary
+  * Thread Id
+* Export API Calls as Text file
+* Settings dialog
+
+**TODO LIST IN DEBUGGER**
+* Hide / show columns on API Call Tree
+* State dependency graph at selected API Call
+* Group API Calls by:
+  * API-specific debug groups
+  * Command Buffer Submission
+  * Render vs State calls
+* Saving 'session' data:
+  * Recently loaded traces
+* Capture state from replay
+* Rewind the replay
+* Custom viewers of each state type
+* Per API entrypoint call stacks
+* Collect and display machine information
+* 64-bit build supports 32-bit trace files
+* Timeline enhancements:
+  * Pan & Zoom
+* Optimize trace file loading by memory-mapping the file
+
+**SUPPORTED FEATURES IN TRACING/REPLAYING COMMAND LINE TOOLS AND LIBRARIES**
+* Command line Tracer app (vktrace) which launches game/app with tracing library(ies) inserted and writes trace packets to a file
+* Command line Tracer server which collects tracing packets over a socket connection and writes them to a file
+* Vulkan tracer library supports multithreaded Vulkan apps
+* Command line Replayer app (vkreplay) replays a Vulkan trace file with Window display on Linux
+
+**TODO LIST IN TRACING/REPLAYING COMMAND LINE TOOLS AND LIBRARIES**
+* Optimize replay speed by using hash maps for opaque handles
+* Handle XGL persistently CPU mapped buffers during tracing, rather then relying on updating data at unmap time
+* Optimize Replayer speed by memory-mapping the file and/or reading file in a separate thread
+* Looping in Replayer over arbitrary frames or calls
+* Looping in Replayer with state restoration at beginning of loop
+* Replayer window display of Vulkan on Windows OS
+* Command line tool to display trace file in human readable format
+* Command line tool for editing trace files in human readable format
+* Replayer supports multithreading
+* 64-bit build supports 32-bit trace files
+* XGL tracing and replay cross platform support with differing GPUs
+
+**LIKE TO HAVE FUTURE FEATURE IDEAS**
+* Export trace file into *.cpp/h files that compile into a runnable application
+* Editing, adding, removal of API calls
+* Shader editing
+* Hyperlink API Call Tree to state-specific windows
diff --git a/vktrace/src/LICENSE b/vktrace/src/LICENSE
new file mode 100644
index 0000000..1d79dd8
--- /dev/null
+++ b/vktrace/src/LICENSE
@@ -0,0 +1,17 @@
+Copyright (C) 2014-2016 Valve Corporation
+Copyright (C) 2014-2016 LunarG, Inc.
+
+All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
diff --git a/vktrace/src/build_options.cmake b/vktrace/src/build_options.cmake
new file mode 100644
index 0000000..bde0c04
--- /dev/null
+++ b/vktrace/src/build_options.cmake
@@ -0,0 +1,515 @@
+#
+# cmake -DCMAKE_BUILD_TYPE=Debug ..
+#
+#   http://www.cmake.org/Wiki/CMake_FAQ
+#   http://www.cmake.org/Wiki/CMake_Useful_Variables
+#   http://clang.llvm.org/docs/LanguageExtensions.html
+#
+#
+cmake_minimum_required(VERSION 2.8)
+
+set(BUILD_X64 "" CACHE STRING "whether to perform 64-bit build: ON or OFF overrides default detection")
+
+option(CMAKE_VERBOSE "Verbose CMake" FALSE)
+if (CMAKE_VERBOSE)
+    SET(CMAKE_VERBOSE_MAKEFILE ON)
+endif()
+
+# With gcc48: http://indico.cern.ch/getFile.py/access?contribId=1&resId=0&materialId=slides&confId=230762
+
+option(WITH_HARDENING "Enable hardening: Compile-time protection against static sized buffer overflows" OFF)
+
+# Unless user specifies BUILD_X64 explicitly, assume native target
+if (BUILD_X64 STREQUAL "")
+  if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+    set(BUILD_X64 "TRUE")
+  else()
+    set(BUILD_X64 "FALSE")
+  endif()
+endif()
+
+# Generate bitness suffix to use, but make sure to include the existing suffix (.exe) 
+# for platforms that need it (ie, Windows)
+if (BUILD_X64)
+    set(CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
+    set(CMAKE_SHARED_LIBRARY_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}")
+else()
+    # Don't add the 32 for Windows because it goes in a different location.
+    if (MSVC)
+        set(CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
+        set(CMAKE_SHARED_LIBRARY_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}")
+    else()
+        set(CMAKE_EXECUTABLE_SUFFIX "32${CMAKE_EXECUTABLE_SUFFIX}")
+        set(CMAKE_SHARED_LIBRARY_SUFFIX "32${CMAKE_SHARED_LIBRARY_SUFFIX}")
+    endif()
+endif()
+
+# Default to release build
+if (NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE Release)
+endif()
+
+# Make sure we're using 64-bit versions of stat, fopen, etc.
+# Large File Support extensions:
+#   http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html#Feature-Test-Macros
+add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES)
+
+# support for inttypes.h macros
+add_definitions(-D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS)
+
+if(MSVC)
+    set(CMAKE_CXX_FLAGS_LIST "/W3 /D_CRT_SECURE_NO_WARNINGS=1 /DWIN32 /D_WIN32")
+    set(CMAKE_CXX_FLAGS_RELEASE_LIST "/O2 /DNDEBUG")
+    set(CMAKE_CXX_FLAGS_DEBUG_LIST "/Od /D_DEBUG")
+else()
+    set(CMAKE_CXX_FLAGS_LIST "-g -Wall -Wextra")
+    set(CMAKE_CXX_FLAGS_RELEASE_LIST "-g -O2 -DNDEBUG")
+    set(CMAKE_CXX_FLAGS_DEBUG_LIST "-g -O0 -D_DEBUG")
+endif()
+
+
+if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+
+  # clang doesn't print colored diagnostics when invoked from Ninja
+  if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
+      add_definitions ("-fcolor-diagnostics")
+  endif()
+
+  if (CLANG_EVERYTHING)
+      set(CMAKE_CXX_FLAGS_LIST ${CMAKE_CXX_FLAGS_LIST}
+          # "-pedantic"             # Warn on language extensions
+          "-Weverything"            # Enable all warnings
+          "-fdiagnostics-show-category=name"
+          "-Wno-unused-macros"
+          "-Wno-padded"
+          "-Wno-variadic-macros"
+          "-Wno-missing-variable-declarations"
+          "-Wno-missing-prototypes"
+          "-Wno-sign-conversion"
+          "-Wno-conversion"
+          "-Wno-cast-align"
+          "-Wno-exit-time-destructors"
+          "-Wno-documentation-deprecated-sync"
+          "-Wno-documentation-unknown-command"
+
+          # TODO: Would be great to start enabling some of these warnings...
+          "-Wno-undefined-reinterpret-cast"
+          "-Wno-incompatible-pointer-types-discards-qualifiers"
+          "-Wno-float-equal"
+          "-Wno-unreachable-code"
+          "-Wno-weak-vtables"
+          "-Wno-extra-semi"
+          "-Wno-disabled-macro-expansion"
+          "-Wno-format-nonliteral"
+          "-Wno-packed"
+          "-Wno-c++11-long-long"
+          "-Wno-c++11-extensions"
+          "-Wno-gnu-anonymous-struct"
+          "-Wno-gnu-zero-variadic-macro-arguments"
+          "-Wno-nested-anon-types"
+          "-Wno-gnu-redeclared-enum"
+
+          "-Wno-pedantic"
+          "-Wno-header-hygiene"
+          "-Wno-covered-switch-default"
+          "-Wno-duplicate-enum"
+          "-Wno-switch-enum"
+          "-Wno-extra-tokens"
+
+          # Added because SDL2 headers have a ton of Doxygen warnings currently.
+          "-Wno-documentation"
+
+          )
+  endif()
+
+endif()
+
+
+if ((NOT MSVC) AND (NOT BUILD_X64) AND (CMAKE_SIZEOF_VOID_P EQUAL 8))
+    set(CMAKE_CXX_FLAGS_LIST "${CMAKE_CXX_FLAGS_LIST} -m32")
+    set(CMAKE_EXE_LINK_FLAGS_LIST "${CMAKE_EXE_LINK_FLAGS_LIST} -m32")
+    set(CMAKE_SHARED_LINK_FLAGS_LIST "${CMAKE_SHARED_LINK_FLAGS_LIST} -m32")
+
+    set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF)
+    set(CMAKE_SYSTEM_LIBRARY_PATH /lib32 /usr/lib32 /usr/lib/i386-linux-gnu /usr/local/lib32)
+    set(CMAKE_IGNORE_PATH /lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib64 /usr/local/lib)
+endif()
+
+function(add_compiler_flag flag)
+    set(CMAKE_C_FLAGS    "${CMAKE_C_FLAGS}   ${flag}" PARENT_SCOPE)
+    set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
+endfunction()
+
+function(add_c_compiler_flag flag)
+    set(CMAKE_C_FLAGS    "${CMAKE_C_FLAGS}   ${flag}" PARENT_SCOPE)
+endfunction()
+
+function(add_cpp_compiler_flag flag)
+    set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
+endfunction()
+
+function(add_compiler_flag_debug flag)
+    set(CMAKE_C_FLAGS_DEBUG    "${CMAKE_C_FLAGS_DEBUG}   ${flag}" PARENT_SCOPE)
+    set(CMAKE_CXX_FLAGS_DEBUG  "${CMAKE_CXX_FLAGS_DEBUG} ${flag}" PARENT_SCOPE)
+endfunction()
+
+function(add_compiler_flag_release flag)
+    set(CMAKE_C_FLAGS_RELEASE    "${CMAKE_C_FLAGS_RELEASE}   ${flag}" PARENT_SCOPE)
+    set(CMAKE_CXX_FLAGS_RELEASE  "${CMAKE_CXX_FLAGS_RELEASE} ${flag}" PARENT_SCOPE)
+endfunction()
+
+
+function(add_linker_flag flag)
+    set(CMAKE_EXE_LINKER_FLAGS   "${CMAKE_EXE_LINKER_FLAGS} ${flag}" PARENT_SCOPE)
+endfunction()
+
+function(add_shared_linker_flag flag)
+    set(CMAKE_SHARED_LINKER_FLAGS   "${CMAKE_SHARED_LINKER_FLAGS} ${flag}" PARENT_SCOPE)
+endfunction()
+
+#
+# To show the include files as you're building, do this:
+#    add_compiler_flag("-H")
+# For Visual Studio, it's /showIncludes I believe...
+#
+
+# stack-protector-strong: http://gcc.gnu.org/ml/gcc-patches/2012-06/msg00974.html
+## -fstack-protector-strong
+# Compile with the option "-fstack-usage" and a file .su will be generated with stack
+# information for each function.
+## -fstack-usage
+
+# For more info on -fno-strict-aliasing: "Just Say No to strict aliasing optimizations in C": http://nothings.org/
+# The Linux kernel is compiled with -fno-strict-aliasing: https://lkml.org/lkml/2003/2/26/158 or http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html
+
+### TODO: see if sse is generated with these instructions and clang:
+## -march=corei7 -msse -mfpmath=sse
+
+if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+   if ( NOT BUILD_X64 )
+      # Fix startup crash in dlopen_notify_callback (called indirectly from our dlopen() function) when tracing glxspheres on my AMD dev box (x86 release only)
+      # Also fixes tracing Q3 Arena using release tracer
+      # Clang is generating sse2 code even when it shouldn't be:
+      #  http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-March/020310.html
+      set(MARCH_STR "-march=i586")
+   endif()
+endif()
+
+if(MSVC)
+    set(CMAKE_CXX_FLAGS_LIST
+        ${CMAKE_CXX_FLAGS_LIST}
+        "/EHsc" # Need exceptions
+    )
+else()
+    set(CMAKE_CXX_FLAGS_LIST
+        ${CMAKE_CXX_FLAGS_LIST}
+        "-fno-omit-frame-pointer"
+        ${MARCH_STR}
+        # "-msse2 -mfpmath=sse" # To build with SSE instruction sets
+        "-Wno-unused-parameter -Wno-unused-function"
+        "-fno-strict-aliasing" # DO NOT remove this, we have lots of code that will fail in obscure ways otherwise because it was developed with MSVC first.
+        "-fno-math-errno"
+    	  "-fvisibility=hidden"
+        # "-fno-exceptions" # Exceptions are enabled by default for c++ files, disabled for c files.
+    )
+endif()
+
+if (CMAKE_COMPILER_IS_GNUCC)
+    execute_process(COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
+    string(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION})
+    list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR)
+    list(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR)
+    # message(STATUS "Detected GCC v ${GCC_MAJOR} . ${GCC_MINOR}")
+endif()
+
+if (GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
+    set(CMAKE_CXX_FLAGS_LIST ${CMAKE_CXX_FLAGS_LIST}
+        "-Wno-unused-local-typedefs"
+    )
+endif()
+
+if (MSVC)
+else()
+    if (WITH_HARDENING)
+        # http://gcc.gnu.org/ml/gcc-patches/2004-09/msg02055.html
+        add_definitions(-D_FORTIFY_SOURCE=2 -fpic)
+        if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+            # During program load, several ELF memory sections need to be written to by the
+            # linker, but can be turned read-only before turning over control to the
+            # program. This prevents some GOT (and .dtors) overwrite attacks, but at least
+            # the part of the GOT used by the dynamic linker (.got.plt) is still vulnerable.
+            add_definitions(-pie -z now -z relro)
+        endif()
+    endif()
+endif()
+
+if (NOT MSVC)
+    if(APPLE)
+        set(CMAKE_EXE_LINK_FLAGS_LIST "-Wl,-undefined,error")
+    else()
+        set(CMAKE_EXE_LINK_FLAGS_LIST "-Wl,--no-undefined")
+    endif()
+endif()
+
+# Compiler flags
+string(REPLACE ";" " " CMAKE_C_FLAGS              "${CMAKE_CXX_FLAGS_LIST}")
+string(REPLACE ";" " " CMAKE_C_FLAGS_RELEASE      "${CMAKE_CXX_FLAGS_RELEASE_LIST}")
+string(REPLACE ";" " " CMAKE_C_FLAGS_DEBUG        "${CMAKE_CXX_FLAGS_DEBUG_LIST}")
+
+string(REPLACE ";" " " CMAKE_CXX_FLAGS            "${CMAKE_CXX_FLAGS_LIST}")
+string(REPLACE ";" " " CMAKE_CXX_FLAGS_RELEASE    "${CMAKE_CXX_FLAGS_RELEASE_LIST}")
+string(REPLACE ";" " " CMAKE_CXX_FLAGS_DEBUG      "${CMAKE_CXX_FLAGS_DEBUG_LIST}")
+
+# Linker flags (exe)
+string(REPLACE ";" " " CMAKE_EXE_LINKER_FLAGS     "${CMAKE_EXE_LINK_FLAGS_LIST}")
+# Linker flags (shared)
+string(REPLACE ";" " " CMAKE_SHARED_LINKER_FLAGS  "${CMAKE_SHARED_LINK_FLAGS_LIST}")
+
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/../../)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/../../)
+
+
+function(build_options_finalize)
+    if (CMAKE_VERBOSE)
+        message("  CMAKE_PROJECT_NAME: ${CMAKE_PROJECT_NAME}")
+        message("  PROJECT_NAME: ${PROJECT_NAME}")
+        message("  BUILD_X64: ${BUILD_X64}")
+        message("  BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+        message("  CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}")
+        message("  PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
+        message("  CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
+        message("  PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}")
+        message("  CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}")
+        message("  CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
+        message("  CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
+        message("  CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}")
+        message("  EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}")
+        message("  SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}")
+        message("  SHARED_LIBRARY_C_FLAGS: ${CMAKE_SHARED_LIBRARY_C_FLAGS}")
+        message("  SHARED_LIBRARY_CXX_FLAGS: ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}")
+        message("  SHARED_LIBRARY_LINK_CXX_FLAGS: ${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS}")
+        message("  SHARED_LIBRARY_LINK_C_FLAGS: ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}")
+        message("  CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
+        message("  CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}")
+        message("  CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}")
+        message("  CMAKE_EXECUTABLE_SUFFIX: ${CMAKE_EXECUTABLE_SUFFIX}")
+        message("")
+    endif()
+endfunction()
+
+function(require_pthreads)
+    find_package(Threads)
+    if (NOT CMAKE_USE_PTHREADS_INIT AND NOT WIN32_PTHREADS_INCLUDE_PATH)
+        message(FATAL_ERROR "pthread not found")
+    endif()
+
+    if (MSVC)
+        include_directories("${WIN32_PTHREADS_INCLUDE_PATH}")
+        if (BUILD_X64)
+            set(PTHREAD_SRC_LIB "${WIN32_PTHREADS_PATH}/lib/x64/pthreadVC2.lib" PARENT_SCOPE)
+            set(PTHREAD_SRC_DLL "${WIN32_PTHREADS_PATH}/dll/x64/pthreadVC2.dll" PARENT_SCOPE)
+        else()
+            set(PTHREAD_SRC_LIB "${WIN32_PTHREADS_PATH}/lib/x86/pthreadVC2.lib" PARENT_SCOPE)
+            set(PTHREAD_SRC_DLL "${WIN32_PTHREADS_PATH}/dll/x86/pthreadVC2.dll" PARENT_SCOPE)
+        endif()
+
+    else()
+        # Export the variable to the parent scope so the linker knows where to find the library.
+        set(CMAKE_THREAD_LIBS_INIT ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(require_libjpegturbo)
+    find_library(LibJpegTurbo_LIBRARY
+        NAMES libturbojpeg.so libturbojpeg.so.0 libturbojpeg.dylib
+        PATHS /opt/libjpeg-turbo/lib 
+    )
+
+    # On platforms that find this, the include files will have also been installed to the system
+    # so we don't need extra include dirs.
+    if (LibJpegTurbo_LIBRARY)
+        set(LibJpegTurbo_INCLUDE "" PARENT_SCOPE)
+    else()
+        if (BUILD_X64)
+            set(BITS_STRING "x64")
+        else()
+            set(BITS_STRING "x86")
+        endif()
+        set(LibJpegTurbo_INCLUDE "${CMAKE_PREFIX_PATH}/libjpeg-turbo-2.1.3/include" PARENT_SCOPE)
+        set(LibJpegTurbo_LIBRARY "${CMAKE_PREFIX_PATH}/libjpeg-turbo-2.1.3/lib_${BITS_STRING}/turbojpeg.lib" PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(require_sdl2)
+    if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+        include(FindPkgConfig)
+        pkg_search_module(PC_SDL2 REQUIRED sdl2)
+
+        find_path(SDL2_INCLUDE SDL.h
+            DOC "SDL2 Include Path"
+	    HINTS ${PC_SDL2_INCLUDEDIR} ${PC_SDL2_INCLUDE_DIRS} )
+
+        find_library(SDL2_LIBRARY SDL2
+            DOC "SDL2 Library"
+	    HINTS ${PC_SDL2_LIBDIR} ${PC_SDL2_LIBRARY_DIRS} )
+    elseif (MSVC)
+        set(SDL2Root "${CMAKE_EXTERNAL_PATH}/SDL")
+
+        set(SDL2_INCLUDE "${SDL2Root}/include" CACHE PATH "SDL2 Include Path")
+        set(SDL2_LIBRARY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/SDL2.lib" CACHE FILEPATH "SDL2 Library")
+
+        # Only want to include this once.
+        # This has to go into properties because it needs to persist across the entire cmake run.
+        get_property(SDL_PROJECT_ALREADY_INCLUDED 
+            GLOBAL 
+            PROPERTY SDL_PROJECT_INCLUDED
+            )
+
+        if (NOT SDL_PROJECT_ALREADY_INCLUDED)
+            INCLUDE_EXTERNAL_MSPROJECT(SDL "${SDL2Root}/VisualC/SDL/SDL_VS2013.vcxproj")
+            set_property(GLOBAL
+                PROPERTY SDL_PROJECT_INCLUDED "TRUE"
+                )
+            message("Including SDL_VS2013.vcxproj for you!")
+        endif()
+
+    else()
+        message(FATAL_ERROR "Need to deal with SDL on non-Windows platforms")
+    endif()
+endfunction()
+
+function(require_m)
+    if (MSVC)
+	set(M_LIBRARY "winmm.lib" PARENT_SCOPE)
+    else()
+	set(M_LIBRARY "m" PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(require_gl)
+    if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+	include(FindPkgConfig)
+	pkg_search_module(PC_GL QUIET gl)
+
+	find_path(GL_INCLUDE GL/gl.h
+	    DOC "OpenGL Include Path"
+	    HINTS ${PC_GL_INCLUDEDIR} ${PC_GL_INCLUDE_DIRS} )
+
+	find_library(GL_LIBRARY GL
+	    DOC "OpenGL Library"
+	    HINTS ${PC_GL_LIBDIR} ${PC_GL_LIBRARY_DIRS} )
+    elseif (MSVC)
+	set(GL_INCLUDE ""	      CACHE PATH "OpenGL Include Path")
+	set(GL_LIBRARY "opengl32.lib" CACHE FILEPATH "OpenGL Library")
+    else()
+	set(GL_INCLUDE ""   CACHE PATH "OpenGL Include Path")
+	set(GL_LIBRARY "GL" CACHE FILEPATH "OpenGL Library")
+    endif()
+endfunction()
+
+function(require_glu)
+    if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+	include(FindPkgConfig)
+	pkg_search_module(PC_GLU QUIET glu)
+
+	find_path(GLU_INCLUDE GL/glu.h
+	    DOC "GLU Include Path"
+	    HINTS ${PC_GLU_INCLUDEDIR} ${PC_GLU_INCLUDE_DIRS} )
+
+	find_library(GLU_LIBRARY GLU
+	    DOC "GLU Library"
+	    HINTS ${PC_GLU_LIBDIR} ${PC_GLU_LIBRARY_DIRS} )
+    elseif (MSVC)
+	set(GLU_INCLUDE ""	    CACHE PATH "GLU Include Path")
+	set(GLU_LIBRARY "glu32.lib" CACHE FILEPATH "GLU Library")
+    else()
+	set(GLU_INCLUDE ""    CACHE PATH "GLU Include Path")
+	set(GLU_LIBRARY "GLU" CACHE FILEPATH "GLU Library")
+    endif()
+endfunction()
+
+function(request_backtrace)
+    if (NOT MSVC)
+        set( LibBackTrace_INCLUDE "${SRC_DIR}/libbacktrace" PARENT_SCOPE )
+        set( LibBackTrace_LIBRARY "backtracevogl" PARENT_SCOPE )
+    else()
+        set( LibBackTrace_INCLUDE "" PARENT_SCOPE )
+        set( LibBackTrace_LIBRARY "" PARENT_SCOPE )
+    endif()
+endfunction()
+
+# What compiler toolchain are we building on?
+if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+    add_compiler_flag("-DCOMPILER_GCC=1")
+    add_compiler_flag("-DCOMPILER_GCCLIKE=1")
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "mingw")
+    add_compiler_flag("-DCOMPILER_MINGW=1")
+    add_compiler_flag("-DCOMPILER_GCCLIKE=1")
+elseif (MSVC)
+    add_compiler_flag("-DCOMPILER_MSVC=1")
+elseif (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+    add_compiler_flag("-DCOMPILER_CLANG=1")
+    add_compiler_flag("-DCOMPILER_GCCLIKE=1")
+else()
+    message("Compiler is ${CMAKE_C_COMPILER_ID}")
+    message(FATAL_ERROR "Compiler unset, build will fail--stopping at CMake time.")
+endif()
+
+# Platform specific library defines.
+if (WIN32)
+    set( LIBRT "" )
+    set( LIBDL "" )
+
+elseif (UNIX)
+    set( LIBRT rt )
+    set( LIBDL dl )
+else()
+    message(FATAL_ERROR "Need to determine what the library name for 'rt' is for non-windows, non-unix platforms (or if it's even needed).")
+endif()
+
+# What OS will we be running on?
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+    add_compiler_flag("-DPLATFORM_OSX=1")
+    add_compiler_flag("-DPLATFORM_POSIX=1")
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+    add_compiler_flag("-DPLATFORM_LINUX=1")
+    add_compiler_flag("-DPLATFORM_POSIX=1")
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+    add_compiler_flag("-DPLATFORM_WINDOWS=1")
+else()
+    message(FATAL_ERROR "Platform unset, build will fail--stopping at CMake time.")
+endif()
+
+# What bittedness are we building?
+if (BUILD_X64)
+    add_compiler_flag("-DPLATFORM_64BIT=1")
+else()
+    add_compiler_flag("-DPLATFORM_32BIT=1")
+endif()
+
+# Compiler flags for windows.
+if (MSVC)
+    # Multithreaded compilation is a big time saver.
+    add_compiler_flag("/MP")
+
+    # In debug, we use the DLL debug runtime.
+    add_compiler_flag_debug("/MDd")
+
+    # And in release we use the DLL release runtime
+    add_compiler_flag_release("/MD")
+
+    # x64 doesn't ever support /ZI, only /Zi.
+    if (BUILD_X64)
+      add_compiler_flag("/Zi")
+    else()
+
+      # In debug, get debug information suitable for Edit and Continue
+      add_compiler_flag_debug("/ZI")
+
+      # In release, still generate debug information (because not having it is dumb)
+      add_compiler_flag_release("/Zi")
+    endif()
+
+    # And tell the linker to always generate the file for us.
+    add_linker_flag("/DEBUG")
+endif()
diff --git a/vktrace/src/vktrace_common/CMakeLists.txt b/vktrace/src/vktrace_common/CMakeLists.txt
new file mode 100644
index 0000000..0c7afa0
--- /dev/null
+++ b/vktrace/src/vktrace_common/CMakeLists.txt
@@ -0,0 +1,57 @@
+project(vktrace_common)
+cmake_minimum_required(VERSION 2.8)
+
+include(${SRC_DIR}/build_options.cmake)
+
+include_directories(
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/thirdparty
+)
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+    require_pthreads()
+endif()
+
+set(SRC_LIST
+    ${SRC_LIST}
+    vktrace_filelike.c
+    vktrace_interconnect.c
+    vktrace_platform.c
+    vktrace_process.c
+    vktrace_settings.c
+    vktrace_tracelog.c
+    vktrace_trace_packet_utils.c
+    vktrace_pageguard_memorycopy.cpp
+)
+
+set (CXX_SRC_LIST
+     vktrace_pageguard_memorycopy.cpp
+)
+
+set_source_files_properties( ${SRC_LIST} PROPERTIES LANGUAGE C)
+set_source_files_properties( ${CXX_SRC_LIST} PROPERTIES LANGUAGE CXX)
+
+file( GLOB_RECURSE HDRS *.[h|inl] )
+
+if (NOT MSVC)
+    add_c_compiler_flag("-fPIC")
+    add_cpp_compiler_flag("-fPIC -std=c++11")
+endif()
+
+add_library(${PROJECT_NAME} STATIC ${SRC_LIST} ${CXX_SRC_LIST} ${HDRS})
+
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+target_link_libraries(${PROJECT_NAME}
+    Rpcrt4.lib
+)
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+target_link_Libraries(${PROJECT_NAME}
+    dl
+    pthread
+)
+endif (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+
+build_options_finalize()
+
+set_target_properties(vktrace_common PROPERTIES LINKER_LANGUAGE C)
diff --git a/vktrace/src/vktrace_common/vktrace_common.h b/vktrace/src/vktrace_common/vktrace_common.h
new file mode 100644
index 0000000..a77bf02
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_common.h
@@ -0,0 +1,62 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "vktrace_platform.h"
+
+#include "vktrace_memory.h"
+#include "vktrace_tracelog.h"
+
+#ifndef STRINGIFY
+#define STRINGIFY(x) #x
+#endif
+
+#if defined(WIN32)
+
+#define VKTRACER_EXPORT __declspec(dllexport)
+#define VKTRACER_STDCALL __stdcall
+#define VKTRACER_CDECL __cdecl
+#define VKTRACER_EXIT void __cdecl
+#define VKTRACER_ENTRY void
+#define VKTRACER_LEAVE void
+
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+
+#define VKTRACER_EXPORT __attribute__ ((visibility ("default")))
+#define VKTRACER_STDCALL
+#define VKTRACER_CDECL
+#define VKTRACER_EXIT void
+#define VKTRACER_ENTRY void __attribute__ ((constructor))
+#define VKTRACER_LEAVE void __attribute__ ((destructor))
+
+#endif
+
+#define ROUNDUP_TO_2(_len)  ((((_len)+ 1)>>1)<<1)
+#define ROUNDUP_TO_4(_len)  ((((_len)+ 3)>>2)<<2)
+#define ROUNDUP_TO_8(_len)  ((((_len)+ 7)>>3)<<3)
+#define ROUNDUP_TO_16(_len) ((((_len)+15)>>4)<<4)
diff --git a/vktrace/src/vktrace_common/vktrace_filelike.c b/vktrace/src/vktrace_common/vktrace_filelike.c
new file mode 100644
index 0000000..9391837
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_filelike.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#include "vktrace_filelike.h"
+#include "vktrace_common.h"
+#include "vktrace_interconnect.h"
+#include <assert.h>
+#include <stdlib.h>
+
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+Checkpoint* vktrace_Checkpoint_create(const char* _str)
+{
+    Checkpoint* pCheckpoint = VKTRACE_NEW(Checkpoint);
+    pCheckpoint->mToken = _str;
+    pCheckpoint->mTokenLength = strlen(_str) + 1;
+    return pCheckpoint;
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_Checkpoint_write(Checkpoint* pCheckpoint, FileLike* _out)
+{
+    vktrace_FileLike_Write(_out, pCheckpoint->mToken, pCheckpoint->mTokenLength);
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_Checkpoint_read(Checkpoint* pCheckpoint, FileLike* _in)
+{
+    if (pCheckpoint->mTokenLength < 64) {
+        char buffer[64];
+        vktrace_FileLike_Read(_in, buffer, pCheckpoint->mTokenLength);
+        if (strcmp(buffer, pCheckpoint->mToken) != 0) {
+            return FALSE;
+        }
+    } else {
+        char* buffer = VKTRACE_NEW_ARRAY(char, pCheckpoint->mTokenLength);
+        vktrace_FileLike_Read(_in, buffer, pCheckpoint->mTokenLength);
+        if (strcmp(buffer, pCheckpoint->mToken) != 0) {
+            VKTRACE_DELETE(buffer);
+            return FALSE;
+        }
+        VKTRACE_DELETE(buffer);
+    }
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+FileLike* vktrace_FileLike_create_file(FILE* fp)
+{
+    FileLike* pFile = NULL;
+    if (fp != NULL)
+    {
+        pFile = VKTRACE_NEW(FileLike);
+        pFile->mMode = File;
+        pFile->mFile = fp;
+        pFile->mMessageStream = NULL;
+    }
+    return pFile;
+}
+
+// ------------------------------------------------------------------------------------------------
+FileLike* vktrace_FileLike_create_msg(MessageStream* _msgStream)
+{
+    FileLike* pFile = NULL;
+    if (_msgStream != NULL)
+    {
+        pFile = VKTRACE_NEW(FileLike);
+        pFile->mMode = Socket;
+        pFile->mFile = NULL;
+        pFile->mMessageStream = _msgStream;
+    }
+    return pFile;
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t vktrace_FileLike_Read(FileLike* pFileLike, void* _bytes, size_t _len)
+{
+    size_t minSize = 0;
+    size_t bytesInStream = 0;
+    if (vktrace_FileLike_ReadRaw(pFileLike, &bytesInStream, sizeof(bytesInStream)) == FALSE)
+        return 0;
+
+    minSize = (_len < bytesInStream) ? _len: bytesInStream;
+    if (bytesInStream > 0) {
+        assert(_len >= bytesInStream);
+        if (vktrace_FileLike_ReadRaw(pFileLike, _bytes, minSize) == FALSE)
+            return 0;
+    }
+
+    return minSize;
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_FileLike_ReadRaw(FileLike* pFileLike, void* _bytes, size_t _len)
+{
+    BOOL result = TRUE;
+    assert((pFileLike->mFile != 0) ^ (pFileLike->mMessageStream != 0));
+
+    switch(pFileLike->mMode) {
+    case File:
+        {
+            if (1 != fread(_bytes, _len, 1, pFileLike->mFile))
+            {
+                if (ferror(pFileLike->mFile) != 0)
+                {
+                    perror("fread error");
+                }
+                else if (feof(pFileLike->mFile) != 0)
+                {
+                    vktrace_LogVerbose("Reached end of file.");
+                }
+                result = FALSE;
+            } 
+            break;
+        }
+    case Socket:
+        {
+            result = vktrace_MessageStream_BlockingRecv(pFileLike->mMessageStream, _bytes, _len);
+            break;
+        }
+
+        default: 
+            assert(!"Invalid mode in FileLike_ReadRaw");
+            result = FALSE;
+    }
+    return result;
+}
+
+void vktrace_FileLike_Write(FileLike* pFileLike, const void* _bytes, size_t _len)
+{
+    vktrace_FileLike_WriteRaw(pFileLike, &_len, sizeof(_len));
+    if (_len) {
+        vktrace_FileLike_WriteRaw(pFileLike, _bytes, _len);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_FileLike_WriteRaw(FileLike* pFile, const void* _bytes, size_t _len)
+{
+    BOOL result = TRUE;
+    assert((pFile->mFile != 0) ^ (pFile->mMessageStream != 0));
+    switch (pFile->mMode)
+    {
+        case File:
+            if (1 != fwrite(_bytes, _len, 1, pFile->mFile))
+            {
+                result = FALSE;
+            }
+            break;
+        case Socket:
+            result = vktrace_MessageStream_Send(pFile->mMessageStream, _bytes, _len);
+            break;
+        default:
+            assert(!"Invalid mode in FileLike_WriteRaw");
+            result = FALSE;
+            break;
+    }
+    return result;
+}
diff --git a/vktrace/src/vktrace_common/vktrace_filelike.h b/vktrace/src/vktrace_common/vktrace_filelike.h
new file mode 100644
index 0000000..7926e14
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_filelike.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#pragma once
+
+#ifdef WIN32
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+#pragma comment (lib, "Ws2_32.lib")
+#endif
+
+#include "vktrace_common.h"
+#include "vktrace_interconnect.h"
+
+typedef struct MessageStream MessageStream;
+
+struct FileLike;
+typedef struct FileLike FileLike;
+typedef struct FileLike
+{
+    enum { File, Socket } mMode;
+    FILE* mFile;
+    MessageStream* mMessageStream;
+} FileLike;
+
+// For creating checkpoints (consistency checks) in the various streams we're interacting with.
+typedef struct Checkpoint
+{
+    const char* mToken;
+    size_t mTokenLength;
+} Checkpoint;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Checkpoint* vktrace_Checkpoint_create(const char* _str);
+void vktrace_Checkpoint_write(Checkpoint* pCheckpoint, FileLike* _out);
+BOOL vktrace_Checkpoint_read(Checkpoint* pCheckpoint, FileLike* _in);
+
+// An interface for interacting with sockets, files, and memory streams with a file-like interface.
+// This is a simple file-like interface--it doesn't support rewinding or anything fancy, just fifo 
+// reads and writes.
+
+// create a filelike interface for file streaming
+FileLike* vktrace_FileLike_create_file(FILE* fp);
+
+// create a filelike interface for network streaming
+FileLike* vktrace_FileLike_create_msg(MessageStream* _msgStream);
+
+// read a size and then a buffer of that size
+size_t vktrace_FileLike_Read(FileLike* pFileLike, void* _bytes, size_t _len);
+
+// Normally, Read expects the size to live in the stream prefixing the data to be read.
+// With ReadRaw, no size is expected first, and the bytes are directly read.
+BOOL vktrace_FileLike_ReadRaw(FileLike* pFileLike, void* _bytes, size_t _len);
+
+// write _len and then the buffer of size _len
+void vktrace_FileLike_Write(FileLike* pFileLike, const void* _bytes, size_t _len);
+
+// Normally, Write outputs the _len to the stream first--with WriteRaw the bytes are simply written, 
+// no size parameter first.
+BOOL vktrace_FileLike_WriteRaw(FileLike* pFile, const void* _bytes, size_t _len);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/vktrace/src/vktrace_common/vktrace_interconnect.c b/vktrace/src/vktrace_common/vktrace_interconnect.c
new file mode 100644
index 0000000..3a80a18
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_interconnect.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#include "vktrace_interconnect.h"
+#include "vktrace_common.h"
+
+#include "vktrace_filelike.h"
+
+#if defined(ANDROID)
+#include <sys/un.h>
+#endif
+
+const size_t kSendBufferSize = 1024 * 1024;
+
+MessageStream* gMessageStream = NULL;
+static VKTRACE_CRITICAL_SECTION gSendLock;
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// private functions
+BOOL vktrace_MessageStream_SetupSocket(MessageStream* pStream);
+BOOL vktrace_MessageStream_SetupHostSocket(MessageStream* pStream);
+BOOL vktrace_MessageStream_SetupClientSocket(MessageStream* pStream);
+BOOL vktrace_MessageStream_Handshake(MessageStream* pStream);
+BOOL vktrace_MessageStream_ReallySend(MessageStream* pStream, const void* _bytes, size_t _size, BOOL _optional);
+void vktrace_MessageStream_FlushSendBuffer(MessageStream* pStream, BOOL _optional);
+
+// public functions
+MessageStream* vktrace_MessageStream_create_port_string(BOOL _isHost, const char* _address, const char* _port)
+{
+    MessageStream* pStream;
+    // make sure the strings are shorter than the destination buffer we have to store them!
+    assert(strlen(_address) + 1 <= 64);
+    assert(strlen(_port) + 1 <= 8);
+
+    pStream = VKTRACE_NEW(MessageStream);
+    memcpy(pStream->mAddress, _address, strlen(_address) + 1);
+    memcpy(pStream->mPort, _port, strlen(_port) + 1);
+
+    pStream->mErrorNum = 0;
+    memset(pStream->mSmallBuffer, 0, 64);
+    pStream->mHost = _isHost;
+    pStream->mHostAddressInfo = NULL;
+    pStream->mNextPacketId = 0;
+    pStream->mSocket = INVALID_SOCKET;
+    pStream->mSendBuffer = NULL;
+
+    if (vktrace_MessageStream_SetupSocket(pStream) == FALSE)
+    {
+        VKTRACE_DELETE(pStream);
+        pStream = NULL;
+    }
+
+    return pStream;
+}
+
+MessageStream* vktrace_MessageStream_create(BOOL _isHost, const char* _address, unsigned int _port)
+{
+    char portBuf[32];
+    memset(portBuf, 0, 32 * sizeof(char));
+    sprintf(portBuf, "%u", _port);
+    return vktrace_MessageStream_create_port_string(_isHost, _address, portBuf);
+}
+
+void vktrace_MessageStream_destroy(MessageStream** ppStream)
+{
+    if ((*ppStream)->mSendBuffer != NULL) {
+        // Try to get our data out.
+        vktrace_MessageStream_FlushSendBuffer(*ppStream, TRUE);
+        vktrace_SimpleBuffer_destroy(&(*ppStream)->mSendBuffer);
+    }
+
+    if ((*ppStream)->mHostAddressInfo != NULL)
+    {
+        freeaddrinfo((*ppStream)->mHostAddressInfo);
+        (*ppStream)->mHostAddressInfo = NULL;
+    }
+
+    vktrace_LogDebug("Destroyed socket connection.");
+#if defined(WIN32)
+    WSACleanup();
+#endif
+    VKTRACE_DELETE(*ppStream);
+    (*ppStream) = NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// private function implementations
+BOOL vktrace_MessageStream_SetupSocket(MessageStream* pStream)
+{
+    BOOL result = TRUE;
+#if defined(WIN32)
+    WSADATA wsaData;
+
+    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
+        result = FALSE;
+    }
+    else
+#endif
+    {
+        if (pStream->mHost) {
+            result = vktrace_MessageStream_SetupHostSocket(pStream);
+        } else {
+            result = vktrace_MessageStream_SetupClientSocket(pStream);
+        }
+    }
+    return result;
+}
+
+BOOL vktrace_MessageStream_SetupHostSocket(MessageStream* pStream)
+{
+    int hr = 0;
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    int yes = 1;
+#endif
+    struct addrinfo hostAddrInfo = { 0 };
+    SOCKET listenSocket;
+
+    vktrace_create_critical_section(&gSendLock);
+    hostAddrInfo.ai_family = AF_INET;
+    hostAddrInfo.ai_socktype = SOCK_STREAM;
+    hostAddrInfo.ai_protocol = IPPROTO_TCP;
+    hostAddrInfo.ai_flags = AI_PASSIVE;
+
+    hr = getaddrinfo(NULL, pStream->mPort, &hostAddrInfo, &pStream->mHostAddressInfo);
+    if (hr != 0) {
+        vktrace_LogError("Host: Failed getaddrinfo.");
+        return FALSE;
+    }
+
+    listenSocket = socket(pStream->mHostAddressInfo->ai_family, pStream->mHostAddressInfo->ai_socktype, pStream->mHostAddressInfo->ai_protocol);
+    if (listenSocket == INVALID_SOCKET) {
+        // TODO: Figure out errors
+        vktrace_LogError("Host: Failed creating a listen socket.");
+        freeaddrinfo(pStream->mHostAddressInfo);
+        pStream->mHostAddressInfo = NULL;
+        return FALSE;
+    }
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+#endif
+    hr = bind(listenSocket, pStream->mHostAddressInfo->ai_addr, (int)pStream->mHostAddressInfo->ai_addrlen);
+    if (hr == SOCKET_ERROR) {
+        vktrace_LogError("Host: Failed binding socket err=%d.", VKTRACE_WSAGetLastError());
+        freeaddrinfo(pStream->mHostAddressInfo);
+        pStream->mHostAddressInfo = NULL;
+        closesocket(listenSocket);
+        return FALSE;
+    }
+
+    // Done with this.
+    freeaddrinfo(pStream->mHostAddressInfo);
+    pStream->mHostAddressInfo = NULL;
+
+    hr = listen(listenSocket, 1);
+    if (hr == SOCKET_ERROR) {
+        vktrace_LogError("Host: Failed listening on socket err=%d.");
+        closesocket(listenSocket);
+        return FALSE;
+    }
+
+    // Fo reals.
+    vktrace_LogVerbose("Listening for connections on port %s.", pStream->mPort);
+    pStream->mSocket = accept(listenSocket, NULL, NULL);
+    closesocket(listenSocket);
+
+    if (pStream->mSocket == INVALID_SOCKET) {
+        vktrace_LogError("Host: Failed accepting socket connection.");
+        return FALSE;
+    }
+
+    vktrace_LogVerbose("Connected on port %s.", pStream->mPort);
+    if (vktrace_MessageStream_Handshake(pStream))
+    {
+        // TODO: The SendBuffer can cause big delays in sending messages back to the client.
+        // We haven't verified if this improves performance in real applications,
+        // so disable it for now.
+        //pStream->mSendBuffer = vktrace_SimpleBuffer_create(kSendBufferSize);
+        pStream->mSendBuffer = NULL;
+    }
+    else
+    {
+        vktrace_LogError("vktrace_MessageStream_SetupHostSocket failed handshake.");
+    }
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_SetupClientSocket(MessageStream* pStream)
+{
+    int hr = 0;
+    unsigned int attempt = 0;
+    BOOL bConnected = FALSE;
+    struct addrinfo hostAddrInfo = { 0 },
+        *currentAttempt = NULL;
+    vktrace_create_critical_section(&gSendLock);
+
+#if defined(ANDROID)
+
+    struct sockaddr_un addr;
+    socklen_t namelen;
+
+    // Copy the string such that a null character precedes it, i.e. "0\vktrace"
+    memset(&addr, 0, sizeof(addr));
+    strcpy(addr.sun_path + 1, pStream->mPort);
+    addr.sun_family = AF_UNIX;
+    namelen = sizeof(addr.sun_family) + strlen(pStream->mPort) + 1;
+
+    pStream->mSocket = socket(AF_UNIX, SOCK_STREAM, 0);
+    hr = connect(pStream->mSocket, (struct sockaddr *) &addr, namelen);
+
+    if (hr == SOCKET_ERROR)
+    {
+        vktrace_LogError("Client: Failed connect to abstract socket.");
+        closesocket(pStream->mSocket);
+        pStream->mSocket = INVALID_SOCKET;
+    }
+
+#else
+
+    hostAddrInfo.ai_family = AF_UNSPEC;
+    hostAddrInfo.ai_socktype = SOCK_STREAM;
+    hostAddrInfo.ai_protocol = IPPROTO_TCP;
+
+    hr = getaddrinfo(pStream->mAddress, pStream->mPort, &hostAddrInfo, &pStream->mHostAddressInfo);
+    if (hr != 0) {
+        vktrace_LogError("Client: Failed getaddrinfo result=%d.", hr);
+        return FALSE;
+    }
+
+    // make several attempts to connect before bailing out
+    for (attempt = 0; attempt < 10 && !bConnected; attempt++)
+    {
+        for (currentAttempt = pStream->mHostAddressInfo; currentAttempt != NULL; currentAttempt = currentAttempt->ai_next)
+        {
+            pStream->mSocket = socket(currentAttempt->ai_family, currentAttempt->ai_socktype, currentAttempt->ai_protocol);
+
+            hr = connect(pStream->mSocket, currentAttempt->ai_addr, (int)currentAttempt->ai_addrlen);
+            if (hr == SOCKET_ERROR)
+            {
+                vktrace_LogVerbose("Client: Failed connect. Possibly non-fatal.");
+                closesocket(pStream->mSocket);
+                pStream->mSocket = INVALID_SOCKET;
+                continue;
+            }
+
+            bConnected = TRUE;
+            break;
+        }
+
+        if (!bConnected)
+        {
+            Sleep(1);
+            vktrace_LogVerbose("Client: Connect attempt %u on port %s failed, trying again.", attempt, pStream->mPort);
+        }
+        else
+        {
+            vktrace_LogVerbose("Client: Connected to port %s successfully.", pStream->mPort);
+        }
+    }
+
+    freeaddrinfo(pStream->mHostAddressInfo);
+    pStream->mHostAddressInfo = NULL;
+
+#endif
+
+    if (pStream->mSocket == INVALID_SOCKET) {
+        vktrace_LogError("Client: Couldn't find any connections.");
+        return FALSE;
+    }
+
+    if (!vktrace_MessageStream_Handshake(pStream))
+    {
+        vktrace_LogError("Client: Failed handshake with host.");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_Handshake(MessageStream* pStream)
+{
+    BOOL result = TRUE;
+    FileLike* fileLike = vktrace_FileLike_create_msg(pStream);
+    Checkpoint* syn = vktrace_Checkpoint_create("It's a trap!");
+    Checkpoint* ack = vktrace_Checkpoint_create(" - Admiral Ackbar");
+
+    if (pStream->mHost) {
+        vktrace_Checkpoint_write(syn, fileLike);
+        result = vktrace_Checkpoint_read(ack, fileLike);
+    } else {
+        if (vktrace_Checkpoint_read(syn, fileLike))
+        {
+            vktrace_Checkpoint_write(ack, fileLike);
+        }
+        else
+        {
+            result = FALSE;
+        }
+    }
+
+    // Turn on non-blocking modes for sockets now.
+    if (result)
+    {
+#if defined(WIN32)
+        u_long asyncMode = 1;
+        ioctlsocket(pStream->mSocket, FIONBIO, &asyncMode);
+#else
+        fcntl(pStream->mSocket, F_SETFL, O_NONBLOCK);
+#endif
+    }
+
+    VKTRACE_DELETE(syn);
+    VKTRACE_DELETE(ack);
+    VKTRACE_DELETE(fileLike);
+
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_MessageStream_FlushSendBuffer(MessageStream* pStream, BOOL _optional)
+{
+    size_t bufferedByteSize = 0;
+    const void* bufferBytes = vktrace_SimpleBuffer_GetBytes(pStream->mSendBuffer, &bufferedByteSize);
+    if (bufferedByteSize > 0) {
+        // TODO use return value from ReallySend
+        vktrace_MessageStream_ReallySend(pStream, bufferBytes, bufferedByteSize, _optional);
+        vktrace_SimpleBuffer_EmptyBuffer(pStream->mSendBuffer);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_BufferedSend(MessageStream* pStream, const void* _bytes, size_t _size, BOOL _optional)
+{
+    BOOL result = TRUE;
+    if (pStream->mSendBuffer == NULL) {
+        result = vktrace_MessageStream_ReallySend(pStream, _bytes, _size, _optional);
+    }
+    else
+    {
+        if (!vktrace_SimpleBuffer_WouldOverflow(pStream->mSendBuffer, _size)) {
+            result = vktrace_SimpleBuffer_AddBytes(pStream->mSendBuffer, _bytes, _size);
+        } else {
+            // Time to flush the cache.
+            vktrace_MessageStream_FlushSendBuffer(pStream, FALSE);
+
+            // Check to see if the packet is larger than the send buffer 
+            if (vktrace_SimpleBuffer_WouldOverflow(pStream->mSendBuffer, _size)) { 
+                result = vktrace_MessageStream_ReallySend(pStream, _bytes, _size, _optional); 
+            } else { 
+                result = vktrace_SimpleBuffer_AddBytes(pStream->mSendBuffer, _bytes, _size);
+            }
+        }
+    }
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_Send(MessageStream* pStream, const void* _bytes, size_t _len)
+{
+    return vktrace_MessageStream_BufferedSend(pStream, _bytes, _len, FALSE);
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_ReallySend(MessageStream* pStream, const void* _bytes, size_t _size, BOOL _optional)
+{
+    size_t bytesSent = 0;
+    assert(_size > 0);
+
+    vktrace_enter_critical_section(&gSendLock);
+    do {
+        int sentThisTime = send(pStream->mSocket, (const char*)_bytes + bytesSent, (int)_size - (int)bytesSent, 0);
+        if (sentThisTime == SOCKET_ERROR) {
+            int socketError = VKTRACE_WSAGetLastError();
+            if (socketError == WSAEWOULDBLOCK) {
+                // Try again. Don't sleep, because that nukes performance from orbit.
+                continue;
+            }
+
+            if (!_optional) {
+                vktrace_leave_critical_section(&gSendLock);
+                return FALSE;
+            } 
+        }
+        if (sentThisTime == 0) {
+            if (!_optional) {
+                vktrace_leave_critical_section(&gSendLock);
+                return FALSE;
+            }
+            vktrace_LogDebug("Send on socket 0 bytes, totalbytes sent so far %u.", bytesSent);
+            break;
+        }
+
+        bytesSent += sentThisTime;
+
+    } while (bytesSent < _size);
+    vktrace_leave_critical_section(&gSendLock);
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_Recv(MessageStream* pStream, void* _out, size_t _len)
+{
+    unsigned int totalDataRead = 0;
+    do {
+        int dataRead = recv(pStream->mSocket, ((char*)_out) + totalDataRead, (int)_len - totalDataRead, 0);
+        if (dataRead == SOCKET_ERROR) {
+            pStream->mErrorNum = VKTRACE_WSAGetLastError();
+            if (pStream->mErrorNum == WSAEWOULDBLOCK || pStream->mErrorNum == EAGAIN) {
+                if (totalDataRead == 0) {
+                    return FALSE;
+                } else {
+                    // I don't do partial reads--once I start receiving I wait for everything.
+                    vktrace_LogDebug("Sleep on partial socket recv (%u bytes / %u), error num %d.", totalDataRead, _len, pStream->mErrorNum);
+                    Sleep(1);
+                }
+                // I've split these into two blocks because one of them is expected and the other isn't.
+            } else if (pStream->mErrorNum == WSAECONNRESET) {
+                // The remote client disconnected, probably not an issue.
+                vktrace_LogDebug("Connection was reset by client.");
+                return FALSE;
+            } else {
+                // Some other wonky network error--place a breakpoint here.
+                vktrace_LogError("Unexpected error (%d) while receiving message stream.", pStream->mErrorNum);
+                return FALSE;
+            }
+        } else {
+            totalDataRead += dataRead;
+        }
+    } while (totalDataRead < _len);
+
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_MessageStream_BlockingRecv(MessageStream* pStream, void* _outBuffer, size_t _len)
+{
+    while (!vktrace_MessageStream_Recv(pStream, _outBuffer, _len)) {
+        if (pStream->mErrorNum == WSAECONNRESET)
+        {
+            return FALSE;
+        }
+        Sleep(1);
+    }
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+SimpleBuffer* vktrace_SimpleBuffer_create(size_t _bufferSize)
+{
+    SimpleBuffer* pBuffer = VKTRACE_NEW(SimpleBuffer);
+    pBuffer->mBuffer = (unsigned char*)vktrace_malloc(_bufferSize);
+    if (pBuffer->mBuffer == NULL)
+    {
+        VKTRACE_DELETE(pBuffer);
+        return NULL;
+    }
+
+    pBuffer->mEnd = 0;
+    pBuffer->mSize = _bufferSize;
+
+    return pBuffer;
+}
+
+void vktrace_SimpleBuffer_destroy(SimpleBuffer** ppBuffer)
+{
+    vktrace_free((*ppBuffer)->mBuffer);
+    VKTRACE_DELETE(*ppBuffer);
+}
+
+BOOL vktrace_SimpleBuffer_AddBytes(SimpleBuffer* pBuffer, const void* _bytes, size_t _size)
+{
+    if (vktrace_SimpleBuffer_WouldOverflow(pBuffer, _size))
+    { 
+        return FALSE;
+    }
+
+    memcpy((unsigned char*)pBuffer->mBuffer + pBuffer->mEnd, _bytes, _size);
+    pBuffer->mEnd += _size;
+
+    return TRUE;
+}
+
+void vktrace_SimpleBuffer_EmptyBuffer(SimpleBuffer* pBuffer)
+{
+    pBuffer->mEnd = 0;
+}
+
+BOOL vktrace_SimpleBuffer_WouldOverflow(SimpleBuffer* pBuffer, size_t _requestedSize)
+{
+    return pBuffer->mEnd + _requestedSize > pBuffer->mSize;
+}
+
+const void* vktrace_SimpleBuffer_GetBytes(SimpleBuffer* pBuffer, size_t* _outByteCount)
+{
+    (*_outByteCount) = pBuffer->mEnd; 
+    return pBuffer->mBuffer; 
+}
+
+//// ------------------------------------------------------------------------------------------------
+//void RemoteCommand::Read(FileLike* _fileLike)
+//{
+//    unsigned int myCommand = 0;
+//    _fileLike->Read(&myCommand);
+//    mRemoteCommandType = (EnumRemoteCommand)myCommand;
+//}
+//
+//// ------------------------------------------------------------------------------------------------
+//void RemoteCommand::Write(FileLike* _fileLike) const
+//{
+//    _fileLike->Write((unsigned int)mRemoteCommandType);
+//}
diff --git a/vktrace/src/vktrace_common/vktrace_interconnect.h b/vktrace/src/vktrace_common/vktrace_interconnect.h
new file mode 100644
index 0000000..fc9ad3d
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_interconnect.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#pragma once
+
+#include <errno.h>
+#include "vktrace_common.h"
+
+#if defined(PLATFORM_POSIX)
+    #include <arpa/inet.h>
+    #include <netdb.h>
+    #include <netinet/in.h>
+    #include <netinet/tcp.h>
+    #include <sys/socket.h>
+    #define SOCKET int
+    #define INVALID_SOCKET 0
+    #define SOCKET_ERROR -1
+    #define closesocket close
+    #define VKTRACE_WSAGetLastError() errno
+    #define WSAEWOULDBLOCK EWOULDBLOCK
+    #define WSAEAGAIN EAGAIN
+    #define WSAECONNRESET ECONNRESET
+#elif defined(WIN32)
+    #include <WinSock2.h>
+    #include <WS2tcpip.h>
+    #pragma comment (lib, "Ws2_32.lib")
+    #define VKTRACE_WSAGetLastError() WSAGetLastError()
+#endif
+
+static const unsigned int VKTRACE_BASE_PORT = 34199;
+struct SSerializeDataPacket;
+
+struct SimpleBuffer;
+
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+typedef struct MessageStream
+{
+    SOCKET mSocket;
+    struct addrinfo* mHostAddressInfo;
+    size_t mNextPacketId;
+    struct SimpleBuffer* mSendBuffer;
+
+    // Used if someone asks for a receive of a small string.
+    char mSmallBuffer[64];
+
+    char mAddress[64];
+
+    char mPort[8];
+
+    BOOL mHost;
+    int mErrorNum;
+} MessageStream;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+MessageStream* vktrace_MessageStream_create_port_string(BOOL _isHost, const char* _address, const char* _port);
+MessageStream* vktrace_MessageStream_create(BOOL _isHost, const char* _address, unsigned int _port);
+void vktrace_MessageStream_destroy(MessageStream** ppStream);
+BOOL vktrace_MessageStream_BufferedSend(MessageStream* pStream, const void* _bytes, size_t _size, BOOL _optional);
+BOOL vktrace_MessageStream_Send(MessageStream* pStream, const void* _bytes, size_t _len);
+
+BOOL vktrace_MessageStream_Recv(MessageStream* pStream, void* _out, size_t _len);
+BOOL vktrace_MessageStream_BlockingRecv(MessageStream* pStream, void* _outBuffer, size_t _len);
+
+extern MessageStream* gMessageStream;
+#ifdef __cplusplus
+}
+#endif
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
+typedef struct SimpleBuffer
+{
+    void* mBuffer;
+    size_t mEnd;
+    size_t mSize;
+} SimpleBuffer;
+
+SimpleBuffer* vktrace_SimpleBuffer_create(size_t _bufferSize);
+void vktrace_SimpleBuffer_destroy(SimpleBuffer** ppBuffer);
+BOOL vktrace_SimpleBuffer_AddBytes(SimpleBuffer* pBuffer, const void* _bytes, size_t _size);
+void vktrace_SimpleBuffer_EmptyBuffer(SimpleBuffer* pBuffer);
+BOOL vktrace_SimpleBuffer_WouldOverflow(SimpleBuffer* pBuffer, size_t _requestedSize);
+const void* vktrace_SimpleBuffer_GetBytes(SimpleBuffer* pBuffer, size_t* _outByteCount);
diff --git a/vktrace/src/vktrace_common/vktrace_memory.h b/vktrace/src/vktrace_common/vktrace_memory.h
new file mode 100644
index 0000000..29c3f75
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_memory.h
@@ -0,0 +1,154 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#pragma once
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define VKTRACE_NEW(type) (type*)vktrace_malloc(sizeof(type))
+#define VKTRACE_NEW_ARRAY(type, count) (type*)vktrace_malloc(sizeof(type)*count)
+#define VKTRACE_DELETE(ptr) vktrace_free(ptr);
+#define VKTRACE_REALLOC(ptr, size) vktrace_realloc(ptr, size);
+
+static void* vktrace_malloc(size_t size)
+{
+    void* pMemory;
+    if (size == 0)
+        return NULL;
+
+    pMemory = malloc(size);
+
+    return pMemory;
+}
+
+static void vktrace_free(void* ptr)
+{
+    free(ptr);
+    ptr = NULL;
+}
+
+static void * vktrace_realloc(void *ptr,size_t size)
+{
+    void *pMemory;
+    if (size == 0)
+        return NULL;
+
+    pMemory = realloc(ptr, size);
+    return pMemory;
+}
+
+static char* vktrace_allocate_and_copy(const char* _src)
+{
+    if (_src == NULL)
+    {
+        return NULL;
+    }
+    else
+    {
+        size_t bufferSize = 1 + strlen(_src);
+
+        char* retVal = VKTRACE_NEW_ARRAY(char, bufferSize);
+#ifdef WIN32
+        strcpy_s(retVal, bufferSize, _src);
+#else // linux
+        strncpy(retVal, _src, bufferSize);
+#endif
+
+        return retVal;
+    }
+}
+
+static char* vktrace_allocate_and_copy_n(const char* _src, int _count)
+{
+    size_t bufferSize = 1 + _count;
+
+    char* retVal = VKTRACE_NEW_ARRAY(char, bufferSize);
+
+#ifdef WIN32
+    strncpy_s(retVal, bufferSize, _src, _count);
+#else // linux
+    strncpy(retVal, _src, _count);
+    retVal[_count] = '\0';
+#endif
+
+    return retVal;
+}
+
+static char* vktrace_copy_and_append(const char* pBaseString, const char* pSeparator, const char* pAppendString)
+{
+    size_t baseSize = (pBaseString != NULL) ? strlen(pBaseString) : 0;
+    size_t separatorSize = ((pAppendString != NULL) && strlen(pAppendString) && (pSeparator != NULL)) ?
+                           strlen(pSeparator) : 0;
+    size_t appendSize = (pAppendString != NULL) ? strlen(pAppendString) : 0;
+    size_t bufferSize = baseSize + separatorSize + appendSize + 1;
+    char* retVal = VKTRACE_NEW_ARRAY(char, bufferSize);
+    if (retVal != NULL)
+    {
+#ifdef WIN32
+        strncpy_s(retVal, bufferSize, pBaseString, baseSize);
+        strncpy_s(&retVal[baseSize], bufferSize-baseSize, pSeparator, separatorSize);
+        strncpy_s(&retVal[baseSize+separatorSize], bufferSize-baseSize-separatorSize, pAppendString, appendSize);
+#else // linux
+        strncpy(retVal, pBaseString, baseSize);
+        strncpy(&retVal[baseSize], pSeparator, separatorSize);
+        strncpy(&retVal[baseSize+separatorSize], pAppendString, appendSize);
+#endif
+    }
+    retVal[bufferSize-1] = '\0';
+    return retVal;
+}
+
+static char* vktrace_copy_and_append_args(const char* pBaseString, const char* pSeparator, const char* pAppendFormat, va_list args)
+{
+    size_t baseSize = (pBaseString != NULL) ? strlen(pBaseString) : 0;
+    size_t separatorSize = (pSeparator != NULL) ? strlen(pSeparator) : 0;
+    size_t appendSize = 0;
+    size_t bufferSize = 0;
+    char* retVal = NULL;
+
+#if defined(WIN32)
+    appendSize = _vscprintf(pAppendFormat, args);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    va_list argcopy;
+    va_copy(argcopy, args);
+    appendSize = vsnprintf(NULL, 0, pAppendFormat, argcopy);
+    va_end(argcopy);
+#endif
+
+    bufferSize = baseSize + separatorSize + appendSize + 1;
+    retVal = VKTRACE_NEW_ARRAY(char, bufferSize);
+    if (retVal != NULL)
+    {
+#ifdef WIN32
+        strncpy_s(retVal, bufferSize, pBaseString, baseSize);
+        strncpy_s(&retVal[baseSize], bufferSize-baseSize, pSeparator, separatorSize);
+        _vsnprintf_s(&retVal[baseSize+separatorSize], bufferSize-baseSize-separatorSize, appendSize, pAppendFormat, args);
+#else // linux
+        strncpy(retVal, pBaseString, baseSize);
+        strncpy(&retVal[baseSize], pSeparator, separatorSize);
+        vsnprintf(&retVal[baseSize+separatorSize], appendSize, pAppendFormat, args);
+#endif
+    }
+    return retVal;
+}
+
diff --git a/vktrace/src/vktrace_common/vktrace_multiplatform.h b/vktrace/src/vktrace_common/vktrace_multiplatform.h
new file mode 100644
index 0000000..2665438
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_multiplatform.h
@@ -0,0 +1,119 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: David Pinedo<david@lunarg.com>
+ **************************************************************************/
+#pragma once
+
+#include "vktrace_trace_packet_identifiers.h"
+#include "vktrace_filelike.h"
+#include "vktrace_memory.h"
+#include "vktrace_process.h"
+#include <stdbool.h>
+#include "vulkan/vk_icd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// Define types needed for cross-platform vkreplay.
+// Unfortunately, some of these are duplicated from vulkan.h
+// and platform-specific header files. Haven't figured out how
+// to avoid this.
+#if !defined(VK_USE_PLATFORM_XCB_KHR)
+typedef VkFlags VkXcbSurfaceCreateFlagsKHR;
+typedef struct xcb_connection_t xcb_connection_t;
+typedef uint32_t xcb_window_t;
+typedef uint32_t xcb_visualid_t;
+typedef struct VkXcbSurfaceCreateInfoKHR {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkXcbSurfaceCreateFlagsKHR    flags;
+    xcb_connection_t*             connection;
+    xcb_window_t                  window;
+} VkXcbSurfaceCreateInfoKHR;
+typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id);
+typedef struct {
+    VkIcdSurfaceBase base;
+    xcb_connection_t *connection;
+    xcb_window_t window;
+} VkIcdSurfaceXcb;
+#endif
+
+#if !defined(VK_USE_PLATFORM_XLIB_KHR)
+typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
+struct _XDisplay;
+typedef struct _XDisplay Display;
+typedef uint32_t CARD32;
+typedef CARD32 XID;
+typedef XID Window;
+typedef CARD32 VisualID;
+typedef struct VkXlibSurfaceCreateInfoKHR {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkXlibSurfaceCreateFlagsKHR    flags;
+    Display*                       dpy;
+    Window                         window;
+} VkXlibSurfaceCreateInfoKHR;
+typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID);
+typedef struct {
+    VkIcdSurfaceBase base;
+    Display *dpy;
+    Window window;
+} VkIcdSurfaceXlib;
+#endif
+
+#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
+typedef VkFlags VkAndroidSurfaceCreateFlagsKHR;
+typedef uint32_t* ANativeWindow;
+typedef struct VkAndroidSurfaceCreateInfoKHR {
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkAndroidSurfaceCreateFlagsKHR    flags;
+    ANativeWindow*                    window;
+} VkAndroidSurfaceCreateInfoKHR;
+typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+typedef struct {
+  ANativeWindow* window;
+} VkIcdSurfaceAndroid;
+#endif
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+typedef void* HINSTANCE;
+typedef void* HWND;
+typedef void* HANDLE;
+typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
+typedef struct VkWin32SurfaceCreateInfoKHR {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkWin32SurfaceCreateFlagsKHR   flags;
+    HINSTANCE                      hinstance;
+    HWND                           window;
+} VkWin32SurfaceCreateInfoKHR;
+typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.cpp b/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.cpp
new file mode 100644
index 0000000..06c9edc
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.cpp
@@ -0,0 +1,484 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "vktrace_pageguard_memorycopy.h"
+
+#define OPTIMIZATION_FUNCTION_IMPLEMENTATION
+
+
+static const size_t SIZE_LIMIT_TO_USE_OPTIMIZATION = 1*1024*1024; //turn off optimization of memcpy if size < this limit.
+                                                                  //for multithread memcopy, there is system cost on multiple threads include switch control from different threads,
+                                                                  //synchronization and communication like semaphore wait and post and other process which system don't need to handle
+                                                                  //in single thread memcpy, if these cost is greater than benefit of using multithread,we should directly call memcpy.
+                                                                  //here set the value with 1M base on roughly estimation of the cost.
+
+
+bool vktrace_sem_create(vktrace_sem_id *sem_id, uint32_t initvalue)
+{
+    bool sem_create_ok = false;
+#if defined(USE_PAGEGUARD_SPEEDUP)
+#if defined(WIN32)
+    static const uint32_t maxValue=0x40000; // Posix doesn't have this value in its sem_create interface, but windows have it. here we also don't need this value, so give it a value that's has no limit for this case.
+    HANDLE sid = CreateSemaphore(NULL, initvalue, maxValue, NULL);
+    if (sid != INVALID_HANDLE_VALUE)
+    {
+        *sem_id = sid;
+        sem_create_ok = true;
+    }
+#else
+    sem_t *sem = new sem_t;
+    if(sem != nullptr)
+    {
+        if (sem_init(sem, 0, initvalue) == 0)
+        {
+            sem_create_ok = true;
+            *sem_id = sem;
+        }
+    }
+#endif
+#endif // USE_PAGEGUARD_SPEEDUP
+    return sem_create_ok;
+}
+
+void vktrace_sem_delete(vktrace_sem_id sid)
+{
+#if defined(USE_PAGEGUARD_SPEEDUP)
+#if defined(WIN32)
+    CloseHandle(sid);
+#else
+    sem_close(sid);
+    delete (sem_t *)sid;
+#endif
+#endif // USE_PAGEGUARD_SPEEDUP
+}
+
+void vktrace_sem_wait(vktrace_sem_id sid)
+{
+#if defined(USE_PAGEGUARD_SPEEDUP)
+#if defined(WIN32)
+    WaitForSingleObject(sid, INFINITE);
+#else
+    sem_wait(sid);
+#endif
+#endif // USE_PAGEGUARD_SPEEDUP
+}
+
+void vktrace_sem_post(vktrace_sem_id sid)
+{
+#if defined(USE_PAGEGUARD_SPEEDUP)
+#if defined(WIN32)
+    ReleaseSemaphore(sid, 1, NULL);
+#else
+    sem_post(sid);
+#endif
+#endif // USE_PAGEGUARD_SPEEDUP
+}
+
+#if defined(PAGEGUARD_MEMCPY_USE_PPL_LIB)
+
+#if defined(WIN32)
+#define PARALLEL_INVOKE_NUM   10 //this is the maximum task number that  parallel_invoke can use, how many threads are actually used to finish these task depand on system concurrency algorithm.
+extern "C" void *vktrace_pageguard_memcpy(void * destination, const void * source, size_t size)
+{
+    void *pRet=NULL;
+        if (size < SIZE_LIMIT_TO_USE_OPTIMIZATION)
+        {
+            pRet = memcpy(destination, source, (size_t)size);
+        }
+        else
+        {
+            pRet = destination;
+            void* ptr_parallel[PARALLEL_INVOKE_NUM];
+            void* pBuffer_parallel[PARALLEL_INVOKE_NUM];
+            size_t size_parallel[PARALLEL_INVOKE_NUM];
+
+            int stepsize = size / PARALLEL_INVOKE_NUM;
+            for (int i = 0; i < PARALLEL_INVOKE_NUM; i++)
+            {
+                ptr_parallel[i] = (char *)destination + i*stepsize;
+                pBuffer_parallel[i] = (char *)source + i*stepsize;
+                if ((i + 1) == PARALLEL_INVOKE_NUM)
+                {
+                    size_parallel[i] = size - (PARALLEL_INVOKE_NUM-1)*stepsize;
+                }
+                else
+                {
+                   size_parallel[i] = stepsize;
+                }
+            }
+            parallel_invoke(
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[9], pBuffer_parallel[9], (size_t)size_parallel[9]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[8], pBuffer_parallel[8], (size_t)size_parallel[8]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[7], pBuffer_parallel[7], (size_t)size_parallel[7]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[6], pBuffer_parallel[6], (size_t)size_parallel[6]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[5], pBuffer_parallel[5], (size_t)size_parallel[5]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[4], pBuffer_parallel[4], (size_t)size_parallel[4]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[3], pBuffer_parallel[3], (size_t)size_parallel[3]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[2], pBuffer_parallel[2], (size_t)size_parallel[2]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[1], pBuffer_parallel[1], (size_t)size_parallel[1]); },
+                [&ptr_parallel, &pBuffer_parallel, &size_parallel] { memcpy(ptr_parallel[0], pBuffer_parallel[0], (size_t)size_parallel[0]); }
+            );
+        }
+    return pRet;
+}
+#else//defined(PAGEGUARD_MEMCPY_USE_PPL_LIB), Linux
+extern "C" void *vktrace_pageguard_memcpy(void * destination, const void * source, size_t size)
+{
+    return memcpy(destination, source, (size_t)size);
+}
+#endif
+
+#else //!defined(PAGEGUARD_MEMCPY_USE_PPL_LIB), use cross-platform memcpy multithread which exclude PPL
+
+
+typedef void(*vktrace_pageguard_ptr_task_unit_function)(void *pTaskUnitParaInput);
+
+typedef struct
+{
+    void * src, *dest;
+    size_t size;
+} vktrace_pageguard_task_unit_parameters;
+
+typedef struct
+{
+    int index;
+    vktrace_pageguard_task_unit_parameters *ptask_units;
+    int amount;
+    vktrace_sem_id sem_id_access;
+} vktrace_pageguard_task_queue;
+
+typedef struct
+{
+    int index;
+    vktrace_pageguard_thread_id thread_id;
+    vktrace_sem_id sem_id_task_start;
+    vktrace_sem_id sem_id_task_end;
+    //vktrace_pageguard_task_unit_parameters *ptask_para;
+} vktrace_pageguard_task_control_block;
+
+
+
+#if defined(WIN32)
+  typedef uint32_t(*vktrace_pageguard_thread_function_ptr)(void *parameters);
+#else
+  typedef void *(*vktrace_pageguard_thread_function_ptr)(void *parameters);
+#endif
+bool vktrace_pageguard_create_thread(vktrace_pageguard_thread_id *ptid, vktrace_pageguard_thread_function_ptr pfunc, vktrace_pageguard_task_control_block *ptaskpara)
+{
+    bool create_thread_ok = false;
+#if defined(WIN32)
+    DWORD dwThreadID;
+    HANDLE thread_handle;
+    thread_handle = CreateThread(NULL, 0,
+        (LPTHREAD_START_ROUTINE)pfunc, ptaskpara,
+        0, &dwThreadID);
+
+    if (thread_handle != INVALID_HANDLE_VALUE)
+    {
+        *ptid = thread_handle;
+        create_thread_ok = true;
+    }
+
+#else
+    pthread_t thread;
+    int state = PTHREAD_CANCEL_ENABLE, oldtype;
+    state = PTHREAD_CANCEL_ASYNCHRONOUS;
+    pthread_setcanceltype(state, &oldtype);
+    if (pthread_create(&thread, NULL, pfunc, (void *)ptaskpara) == 0)
+    {
+        *ptid = thread;
+        create_thread_ok = true;
+    }
+    pthread_setcanceltype(oldtype, &state);
+#endif
+    return create_thread_ok;
+}
+
+void vktrace_pageguard_delete_thread(vktrace_pageguard_thread_id tid)
+{
+#if defined(WIN32)
+    DWORD  dwExitCode=0;
+    TerminateThread((HANDLE)tid, dwExitCode);
+#else
+    pthread_cancel((pthread_t)tid);
+    pthread_join((pthread_t)tid, NULL);
+#endif
+}
+
+int vktrace_pageguard_get_cpu_core_count()
+{
+    int iret = 4;
+#if defined(WIN32)
+    SYSTEM_INFO sSysInfo;
+    GetSystemInfo(&sSysInfo);
+    iret = sSysInfo.dwNumberOfProcessors;
+#else
+    iret=sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+    return iret;
+}
+
+vktrace_pageguard_task_queue *vktrace_pageguard_get_task_queue()
+{
+    static vktrace_pageguard_task_queue *pvktrace_pageguard_task_queue_multi_thread_memcpy = nullptr;
+    if (!pvktrace_pageguard_task_queue_multi_thread_memcpy)
+    {
+        pvktrace_pageguard_task_queue_multi_thread_memcpy = new vktrace_pageguard_task_queue;
+        memset( reinterpret_cast<void *>(pvktrace_pageguard_task_queue_multi_thread_memcpy), 0, sizeof(vktrace_pageguard_task_queue));
+        vktrace_sem_create(&pvktrace_pageguard_task_queue_multi_thread_memcpy->sem_id_access, 1);
+    }
+    return pvktrace_pageguard_task_queue_multi_thread_memcpy;
+}
+
+vktrace_pageguard_task_unit_parameters *vktrace_pageguard_get_task_unit_parameters()
+{
+    vktrace_pageguard_task_unit_parameters *pret = nullptr;
+    vktrace_pageguard_task_queue *ptaskqueue = vktrace_pageguard_get_task_queue();
+    vktrace_sem_wait(ptaskqueue->sem_id_access);
+    if (ptaskqueue->index<ptaskqueue->amount)
+    {
+        pret = &ptaskqueue->ptask_units[ptaskqueue->index];
+        ptaskqueue->index++;
+    }
+    vktrace_sem_post(ptaskqueue->sem_id_access);
+    return pret;
+}
+
+vktrace_pageguard_task_control_block *vktrace_pageguard_get_task_control_block()
+{
+    static vktrace_pageguard_task_control_block *ptask_control_block = nullptr;
+    if (!ptask_control_block)
+    {
+        int thread_number = vktrace_pageguard_get_cpu_core_count();
+        ptask_control_block = reinterpret_cast<vktrace_pageguard_task_control_block *>(new uint8_t[thread_number*sizeof(vktrace_pageguard_task_control_block)]);
+        memset((void *)ptask_control_block, 0, thread_number*sizeof(vktrace_pageguard_task_control_block));
+    }
+    return ptask_control_block;
+}
+
+uint32_t vktrace_pageguard_thread_function(void *ptcbpara)
+{
+    vktrace_pageguard_task_control_block * ptasktcb = reinterpret_cast<vktrace_pageguard_task_control_block *>(ptcbpara);
+    vktrace_pageguard_task_unit_parameters *parameters;
+    bool stop_loop;
+    while (1)
+    {
+        vktrace_sem_wait(ptasktcb->sem_id_task_start);
+        stop_loop = false;
+        while (!stop_loop)
+        {
+            parameters = vktrace_pageguard_get_task_unit_parameters();
+            if (parameters != nullptr)
+            {
+                memcpy(parameters->dest, parameters->src, parameters->size);
+            }
+            else
+            {
+                stop_loop = true;
+            }
+        }
+        vktrace_sem_post(ptasktcb->sem_id_task_end);
+    }
+}
+
+bool vktrace_pageguard_init_multi_threads_memcpy_custom(vktrace_pageguard_thread_function_ptr pfunc)
+{
+    bool init_multi_threads_memcpy_custom_ok = false, success_sem_start = false, success_sem_end = false, success_thread = false;
+    vktrace_pageguard_task_control_block *ptcb = vktrace_pageguard_get_task_control_block();
+    int thread_number = vktrace_pageguard_get_cpu_core_count();
+    for (int i = 0; i < thread_number; i++)
+    {
+        success_sem_start = vktrace_sem_create(&ptcb[i].sem_id_task_start,0);
+        success_sem_end = vktrace_sem_create(&ptcb[i].sem_id_task_end,0);
+        ptcb[i].index = i;
+        success_thread = vktrace_pageguard_create_thread(&ptcb[i].thread_id, (vktrace_pageguard_thread_function_ptr)pfunc, &ptcb[i]);
+        if (success_sem_start&&success_sem_end &&success_thread)
+        {
+            init_multi_threads_memcpy_custom_ok = true;
+        }
+        else
+        {
+            init_multi_threads_memcpy_custom_ok = false;
+            break;
+        }
+    }
+    return init_multi_threads_memcpy_custom_ok;
+}
+
+static vktrace_sem_id glocal_sem_id;
+static bool glocal_sem_id_create_success= vktrace_sem_create(&glocal_sem_id, 1);
+
+int vktrace_pageguard_ref_count(bool release)
+{
+  static int ref_count = 0;
+  int curr_ref_count;
+  if (!release)
+  {
+      vktrace_sem_wait(glocal_sem_id);
+      curr_ref_count = ref_count;
+      ++ref_count;
+      vktrace_sem_post(glocal_sem_id);
+      return curr_ref_count;
+  }
+  else
+  {
+     vktrace_sem_wait(glocal_sem_id);
+     --ref_count;
+     curr_ref_count = ref_count;
+     vktrace_sem_post(glocal_sem_id);
+     return curr_ref_count;
+  }
+}
+
+extern "C" BOOL vktrace_pageguard_init_multi_threads_memcpy()
+{
+    int refnum = vktrace_pageguard_ref_count(false);
+    BOOL init_multi_threads_memcpy_ok = TRUE;
+    vktrace_pageguard_thread_function_ptr pfunc = (vktrace_pageguard_thread_function_ptr)vktrace_pageguard_thread_function;
+    if (!refnum)
+    {
+        init_multi_threads_memcpy_ok = vktrace_pageguard_init_multi_threads_memcpy_custom(pfunc);
+    }
+    return init_multi_threads_memcpy_ok;
+}
+
+void vktrace_pageguard_delete_task_control_block()
+{
+    delete[] vktrace_pageguard_get_task_control_block();
+}
+
+void vktrace_pageguard_delete_task_queue()
+{
+    delete vktrace_pageguard_get_task_queue();
+}
+
+extern "C" void vktrace_pageguard_done_multi_threads_memcpy()
+{
+    int refnum = vktrace_pageguard_ref_count(true);
+    if (!refnum)
+    {
+        vktrace_pageguard_task_control_block *task_control_block = vktrace_pageguard_get_task_control_block();
+        if (task_control_block != nullptr)
+        {
+            int thread_number = vktrace_pageguard_get_cpu_core_count();
+
+            for (int i = 0; i < thread_number; i++)
+            {
+                vktrace_pageguard_delete_thread(task_control_block[i].thread_id);
+                vktrace_sem_delete(task_control_block[i].sem_id_task_start);
+                vktrace_sem_delete(task_control_block[i].sem_id_task_end);
+            }
+            vktrace_pageguard_delete_task_control_block();
+            vktrace_sem_delete(vktrace_pageguard_get_task_queue()->sem_id_access);
+            vktrace_pageguard_delete_task_queue();
+        }
+    }
+}
+
+//must keep units until vktrace_pageguard_multi_threads_memcpy_run finished
+void vktrace_pageguard_set_task_queue(vktrace_pageguard_task_unit_parameters *units, int unitamount)
+{
+    vktrace_sem_wait(glocal_sem_id);
+    vktrace_pageguard_task_queue *pqueue = vktrace_pageguard_get_task_queue();
+    pqueue->amount = unitamount;
+    pqueue->ptask_units = units;
+    pqueue->index = 0;
+}
+
+void vktrace_pageguard_clear_task_queue()
+{
+    vktrace_pageguard_task_queue *pqueue = vktrace_pageguard_get_task_queue();
+    pqueue->amount = 0;
+    pqueue->ptask_units = nullptr;
+    pqueue->index = 0;
+    vktrace_sem_post(glocal_sem_id);
+}
+
+//The steps for using multithreading copy:
+//<1>init_multi_threads_memcpy
+//   it should be put at beginning of the app
+
+//<2>vktrace_pageguard_set_task_queue
+//<3>vktrace_pageguard_multi_threads_memcpy_run
+
+//<4>done_multi_threads_memcpy()
+//   it should be putted at end of the app
+void vktrace_pageguard_multi_threads_memcpy_run()
+{
+    vktrace_pageguard_task_control_block *ptcb = vktrace_pageguard_get_task_control_block();
+    int thread_number = vktrace_pageguard_get_cpu_core_count();
+
+    for (int i = 0; i < thread_number; i++)
+    {
+        vktrace_sem_post(ptcb[i].sem_id_task_start);
+    }
+
+    for (int i = 0; i < thread_number; i++)
+    {
+        vktrace_sem_wait(ptcb[i].sem_id_task_end);
+    }
+
+}
+
+void vktrace_pageguard_memcpy_multithread(void *dest, const void *src, size_t n)
+{
+    static const size_t PAGEGUARD_MEMCPY_MULTITHREAD_UNIT_SIZE = 0x10000;
+    int thread_number = vktrace_pageguard_get_cpu_core_count();
+
+    //taskunitamount should be >=thread_number, but should not >= a value which make the unit too small and the cost of switch thread > memcpy that unit, on the other side, too small is also not best if consider last task will determine the memcpy speed.
+    int taskunitamount = n / PAGEGUARD_MEMCPY_MULTITHREAD_UNIT_SIZE;
+    if (taskunitamount < thread_number)
+    {
+        taskunitamount = thread_number;
+    }
+    size_t size_per_unit = n / taskunitamount, size_left = n%taskunitamount, size;
+    vktrace_pageguard_task_unit_parameters *units = reinterpret_cast<vktrace_pageguard_task_unit_parameters *>(new uint8_t[taskunitamount*sizeof(vktrace_pageguard_task_unit_parameters)]);
+    assert(units);
+    for (int i = 0; i < taskunitamount; i++)
+    {
+        size = size_per_unit;
+        if((i + 1) == taskunitamount)
+        {
+            size += size_left;
+        }
+        units[i].src = (void *)((uint8_t *)src + i*size_per_unit);
+        units[i].dest = (void *)((uint8_t *)dest + i*size_per_unit);
+        units[i].size = size;
+    }
+    vktrace_pageguard_set_task_queue(units, taskunitamount);
+    vktrace_pageguard_multi_threads_memcpy_run();
+    delete[] units;
+    vktrace_pageguard_clear_task_queue();
+}
+
+extern "C" void *vktrace_pageguard_memcpy(void * destination, const void * source, size_t size)
+{
+    void *pRet = NULL;
+    if (size < SIZE_LIMIT_TO_USE_OPTIMIZATION)
+    {
+        pRet = memcpy(destination, source, (size_t)size);
+    }
+    else
+    {
+        pRet = destination;
+        vktrace_pageguard_memcpy_multithread(destination, source, (size_t)size);
+    }
+    return pRet;
+}
+#endif
diff --git a/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.h b/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.h
new file mode 100644
index 0000000..2276e74
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_pageguard_memorycopy.h
@@ -0,0 +1,77 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include <stdio.h>
+#include <stdlib.h>
+
+//use PPL parallel_invoke call(on windows for now, but PPL also has a PPLx for Linux), or use cross-platform memcpy multithread which exclude PPL
+//#define PAGEGUARD_MEMCPY_USE_PPL_LIB
+#include "vktrace_platform.h"
+
+// Pageguard is only used on Windows, but we use lots of the pageguard code
+// to implement support for persistently mapped buffers on Linux, so we set
+// USE_PAGEGUARD_SPEEDUP.
+//
+// The pmb implementation on Windows involves setting a page guard on a pmb
+// and an exception handler getting called when the pmb is modified by the
+// user program. On Linux, instead of using an exception handler, the
+// file /proc/<pid>/pagemap is read to determine what pages have been
+// modified in the pmb.
+
+#define USE_PAGEGUARD_SPEEDUP
+#if defined(WIN32)
+    #if defined(PAGEGUARD_MEMCPY_USE_PPL_LIB)
+        #include <ppl.h>
+        using namespace concurrency;
+    #endif
+#else
+    #include <semaphore.h>
+    #include <pthread.h>
+#endif
+
+
+typedef struct __PageGuardChangedBlockInfo
+{
+    uint32_t offset;
+    uint32_t length;
+    uint32_t reserve0;
+    uint32_t reserve1;
+} PageGuardChangedBlockInfo, *pPageGuardChangedBlockInfo;
+
+#if defined(WIN32)
+typedef HANDLE vktrace_pageguard_thread_id;
+typedef HANDLE vktrace_sem_id;
+#else
+typedef pthread_t vktrace_pageguard_thread_id;
+typedef sem_t* vktrace_sem_id;
+#endif
+
+#ifdef __cplusplus
+bool vktrace_sem_create(vktrace_sem_id *sem_id, uint32_t initvalue);
+void vktrace_sem_delete(vktrace_sem_id sid);
+void vktrace_sem_wait(vktrace_sem_id sid);
+void vktrace_sem_post(vktrace_sem_id sid);
+void vktrace_pageguard_memcpy_multithread(void *dest, const void *src, size_t n);
+extern "C" void *vktrace_pageguard_memcpy(void * destination, const void * source, size_t size);
+#else
+void *vktrace_pageguard_memcpy(void * destination, const void * source, size_t size);
+#endif
+
+
+
+#define PAGEGUARD_SPECIAL_FORMAT_PACKET_FOR_VKFLUSHMAPPEDMEMORYRANGES 0X00000001
diff --git a/vktrace/src/vktrace_common/vktrace_platform.c b/vktrace/src/vktrace_common/vktrace_platform.c
new file mode 100644
index 0000000..3823014
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_platform.c
@@ -0,0 +1,484 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#include "vktrace_platform.h"
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+#include "vktrace_common.h"
+#include <pthread.h>
+#endif
+
+#if defined(PLATFORM_OSX)
+#include <libproc.h>
+#endif
+
+vktrace_process_id vktrace_get_pid()
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    return getpid();
+#elif defined(WIN32)
+    return GetCurrentProcessId();
+#endif
+}
+
+char* vktrace_platform_get_current_executable_directory()
+{
+    char* exePath = (char*)vktrace_malloc(_MAX_PATH);
+#if defined(WIN32)
+    DWORD s = GetModuleFileName(NULL, exePath, MAX_PATH);
+#elif defined(PLATFORM_LINUX)
+    ssize_t s = readlink("/proc/self/exe", exePath, _MAX_PATH);
+    if (s >= 0)
+    {
+        exePath[s] = '\0';
+    }
+    else
+    {
+        exePath[0] = '\0';
+    }
+#elif defined(PLATFORM_OSX)
+    ssize_t s = proc_pidpath(getpid(), exePath, _MAX_PATH);
+    if (s >= 0)
+    {
+        exePath[s] = '\0';
+    }
+    else
+    {
+        exePath[0] = '\0';
+    }
+#endif
+
+    while (s > 0)
+    {
+        if (exePath[s] == '/' || exePath[s] == '\\')
+        {
+            // NULL this location and break so that the shortened string can be returned.
+            exePath[s] = '\0';
+            break;
+        }
+
+        --s;
+    }
+
+    if (s <= 0)
+    {
+        assert(!"Unexpected path returned in vktrace_platform_get_current_executable_directory");
+        vktrace_free(exePath);
+        exePath = NULL;
+    }
+
+    return exePath;
+}
+
+BOOL vktrace_is_loaded_into_vktrace()
+{
+    char exePath[_MAX_PATH];
+
+#if defined(WIN32)
+    char* substr = ((sizeof(void*) == 4)? "vktrace32.exe" : "vktrace.exe");
+    GetModuleFileName(NULL, exePath, MAX_PATH);
+#elif defined(PLATFORM_LINUX)
+    char* substr = ((sizeof(void*) == 4)? "vktrace32" : "vktrace");
+    ssize_t s = readlink("/proc/self/exe", exePath, _MAX_PATH);
+    if (s >= 0)
+    {
+        exePath[s] = '\0';
+    }
+    else
+    {
+        exePath[0] = '\0';
+    }
+#elif defined(PLATFORM_OSX)
+    char* substr = ((sizeof(void*) == 4)? "vktrace32" : "vktrace");
+    ssize_t s = proc_pidpath(getpid(), exePath, _MAX_PATH);
+    if (s >= 0)
+    {
+        exePath[s] = '\0';
+    }
+    else
+    {
+        exePath[0] = '\0';
+    }
+#endif
+    return (strstr(exePath, substr) != NULL);
+}
+
+BOOL vktrace_platform_get_next_lib_sym(void * *ppFunc, const char * name)
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    if ((*ppFunc = dlsym(RTLD_NEXT, name)) == NULL) {
+         vktrace_LogError("dlsym: failed to find symbol %s %s", name, dlerror());
+         return FALSE;
+    }
+#elif defined(WIN32)
+    vktrace_LogError("unimplemented");
+    assert(0);
+    return FALSE;
+#endif
+   return TRUE;
+}
+
+vktrace_thread_id vktrace_platform_get_thread_id()
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    //return (vktrace_thread_id)syscall(SYS_gettid);
+    return (vktrace_thread_id)pthread_self();
+#elif defined(WIN32)
+    return GetCurrentThreadId();
+#endif
+}
+
+char *vktrace_get_global_var(const char *name)
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    return getenv(name);
+#else
+    // TODO: add code for reading from Windows registry
+    // For now we just return the result from getenv
+    return getenv(name);
+#endif
+}
+
+void vktrace_set_global_var(const char *name, const char *val)
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    setenv(name, val, 1);
+#else
+    // TODO add code for writing to Windows registry
+    // For now we just do _putenv_s
+    _putenv_s(name, val);
+#endif
+}
+
+size_t vktrace_platform_rand_s(uint32_t* out_array, size_t out_array_length)
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    static __thread unsigned int s_seed = 0;
+    size_t i = 0;
+
+    if (s_seed == 0)
+    {
+        // Try to seed rand_r() with /dev/urandom.
+        size_t nbytes = 0;
+        int fd = open("/dev/urandom", O_RDONLY);
+        if (fd != -1)
+        {
+            nbytes = read(fd, &s_seed, sizeof(s_seed));
+            close(fd);
+        }
+
+        // If that didn't work, fallback to time and thread id.
+        if (nbytes != sizeof(s_seed))
+        {
+            struct timeval time;
+            gettimeofday(&time, NULL);
+            s_seed = vktrace_platform_get_thread_id() ^ ((time.tv_sec * 1000) + (time.tv_usec / 1000));
+        }
+    }
+
+    for (i = 0; i < out_array_length; ++i)
+    {
+        out_array[i] = rand_r(&s_seed);
+    }
+
+    return out_array_length;
+#elif defined(WIN32)
+    //VKTRACE_ASSUME(sizeof(uint32_t) == sizeof(unsigned int));
+
+    size_t ret_values = 0;
+    for (ret_values = 0; ret_values < out_array_length; ++ret_values)
+    {
+        if (FAILED(rand_s(&out_array[ret_values])))
+            return ret_values;
+    }
+
+    return ret_values;
+#endif
+}
+
+void * vktrace_platform_open_library(const char* libPath)
+{
+#if defined(WIN32)
+    return LoadLibrary(libPath);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    return dlopen(libPath, RTLD_LAZY);
+#endif
+}
+
+void * vktrace_platform_get_library_entrypoint(void * libHandle, const char *name)
+{
+    // Get func ptr to library entrypoint. We don't log an error if
+    // we don't find the entrypoint, because cross-platform support
+    // causes vkreplay to query the address of all api entrypoints,
+    // even the wsi-specific ones.
+#ifdef WIN32
+    FARPROC proc = GetProcAddress((HMODULE)libHandle, name);
+#else
+    void * proc = dlsym(libHandle, name);
+#endif
+    return proc;
+}
+
+void vktrace_platform_close_library(void* pLibrary)
+{
+#if defined(WIN32)
+    FreeLibrary((HMODULE)pLibrary);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    dlclose(pLibrary);
+#endif
+}
+
+void vktrace_platform_full_path(const char* partPath, unsigned long bytes, char* buffer)
+{
+    assert(buffer != NULL);
+#if defined(WIN32)
+    GetFullPathName(partPath, bytes, buffer, NULL);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    char *ptr = realpath(partPath, buffer);
+    (void) ptr;
+#endif
+}
+
+char* vktrace_platform_extract_path(char* _path)
+{
+    // These functions actually work on const strings, but the C decl version exposed by the macro 
+    // takes non-const TCHAR*.
+    char* pDir;
+    size_t newLen;
+    char* pathSepBack = strrchr(_path, '\\');
+    char* pathSepFor = strrchr(_path, '/');
+    char* lastPathSep = pathSepBack > pathSepFor ? pathSepBack : pathSepFor;
+
+    if (lastPathSep == NULL)
+    {
+        return vktrace_allocate_and_copy(".\\");
+    }
+
+    pDir = VKTRACE_NEW_ARRAY(char, strlen(_path) + 1);
+    newLen = strlen(_path) - strlen(lastPathSep);
+    strncpy(pDir, _path, newLen);
+    pDir[newLen] = '\0';
+    return pDir;
+}
+
+// The following linux paths are based on:
+// standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
+char* vktrace_platform_get_settings_path()
+{
+#if defined(__linux__)
+    char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
+    if (xdgConfigHome != NULL && strlen(xdgConfigHome) > 0)
+    {
+        return vktrace_copy_and_append(xdgConfigHome, VKTRACE_PATH_SEPARATOR, "vktrace");
+    }
+    else
+    {
+        return vktrace_copy_and_append(getenv("HOME"), VKTRACE_PATH_SEPARATOR, ".config/vktrace");
+    }
+#elif defined(WIN32)
+    DWORD reqLength = GetEnvironmentVariable("localappdata", NULL, 0);
+    TCHAR* localAppData = VKTRACE_NEW_ARRAY(TCHAR, reqLength);
+    GetEnvironmentVariable("localappdata", localAppData, reqLength);
+    TCHAR* localVktraceData = vktrace_copy_and_append(localAppData, VKTRACE_PATH_SEPARATOR, "vktrace");
+    VKTRACE_DELETE(localAppData);
+    return localVktraceData;
+#else
+    assert(!"not implemented");
+#endif
+}
+
+char* vktrace_platform_get_data_path()
+{
+#if defined(__linux__)
+    char* xdgDataHome = getenv("XDG_DATA_HOME");
+    if (xdgDataHome != NULL && strlen(xdgDataHome) > 0)
+    {
+        return vktrace_copy_and_append(xdgDataHome, VKTRACE_PATH_SEPARATOR, "vktrace");
+    }
+    else
+    {
+        return vktrace_copy_and_append(getenv("HOME"), VKTRACE_PATH_SEPARATOR, ".local/share/vktrace");
+    }
+#elif defined(WIN32)
+    DWORD reqLength = GetEnvironmentVariable("localappdata", NULL, 0);
+    TCHAR* localAppData = VKTRACE_NEW_ARRAY(TCHAR, reqLength);
+    GetEnvironmentVariable("localappdata", localAppData, reqLength);
+    TCHAR* localVktraceData = vktrace_copy_and_append(localAppData, VKTRACE_PATH_SEPARATOR, "vktrace");
+    VKTRACE_DELETE(localAppData);
+    return localVktraceData;
+#else
+    assert(!"not implemented");
+#endif
+}
+
+
+vktrace_thread vktrace_platform_create_thread(VKTRACE_THREAD_ROUTINE_RETURN_TYPE(*start_routine)(LPVOID), void* args)
+{
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    vktrace_thread thread = 0;
+    if(pthread_create(&thread, NULL, (void *(*) (void*)) start_routine, args) != 0)
+    {
+        vktrace_LogError("Failed to create thread");
+    }
+    return thread;
+#elif defined(WIN32)
+    return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, args, 0, NULL);
+#endif
+}
+
+void vktrace_platform_resume_thread(vktrace_thread* pThread)
+{
+    assert(pThread != NULL);
+#if defined(PLATFORM_LINUX)
+    assert(!"Add code to resume threads on Linux");
+#elif defined(PLATFORM_OSX)
+    assert(!"Add code to resume threads on macOS");
+#elif defined(WIN32)
+    if (*pThread != NULL)
+        ResumeThread(*pThread);
+#endif
+}
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+int64_t vktrace_linux_sync_wait_for_thread(vktrace_thread* pThread)
+{
+    void *retval;
+    assert(pThread != NULL);
+    if (pthread_join(*pThread, &retval) != 0)
+    {
+        vktrace_LogError("Error occurred while waiting for thread to end.");
+    }
+    return retval ? *((int64_t*)retval): 0;
+}
+#endif
+
+void vktrace_platform_delete_thread(vktrace_thread* pThread)
+{
+    assert(pThread != NULL);
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    // Don't have to do anything!
+#elif defined(WIN32)
+    CloseHandle(*pThread);
+    *pThread = NULL;
+#endif
+}
+
+#if defined(WIN32)
+void vktrace_platform_thread_once(void *ctl, BOOL (CALLBACK * func) (PINIT_ONCE, PVOID, PVOID *))
+{
+    assert(func != NULL);
+    assert(ctl != NULL);
+    InitOnceExecuteOnce((PINIT_ONCE) ctl, (PINIT_ONCE_FN) func, NULL, NULL);
+}
+#else
+void vktrace_platform_thread_once(void *ctl, void (* func) (void))
+{
+    assert(func != NULL);
+    assert(ctl != NULL);
+    pthread_once((pthread_once_t *) ctl, func);
+}
+#endif
+
+void vktrace_create_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection)
+{
+#if defined(WIN32)
+    InitializeCriticalSection(pCriticalSection);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    pthread_mutex_init(pCriticalSection, NULL);
+#endif
+}
+
+void vktrace_enter_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection)
+{
+#if defined(WIN32)
+    EnterCriticalSection(pCriticalSection);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    pthread_mutex_lock(pCriticalSection);
+#endif
+}
+
+void vktrace_leave_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection)
+{
+#if defined(WIN32)
+    LeaveCriticalSection(pCriticalSection);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    pthread_mutex_unlock(pCriticalSection);
+#endif
+}
+
+void vktrace_delete_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection)
+{
+#if defined(WIN32)
+    DeleteCriticalSection(pCriticalSection);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    pthread_mutex_destroy(pCriticalSection);
+#endif
+}
+
+BOOL vktrace_platform_remote_load_library(vktrace_process_handle pProcessHandle, const char* dllPath, vktrace_thread* pTracingThread, char ** ldPreload)
+{
+    if (dllPath == NULL)
+        return TRUE;
+#if defined(WIN32)
+    SIZE_T bytesWritten = 0;
+    void* targetProcessMem = NULL;
+    vktrace_thread thread = NULL;
+    size_t byteCount = sizeof(char) * (strlen(dllPath) + 1);
+    targetProcessMem = VirtualAllocEx(pProcessHandle, 0, byteCount, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+    if (!targetProcessMem)
+    {
+        vktrace_LogError("Failed to inject ourselves into target process--couldn't allocate process memory.");
+        return FALSE;
+    }
+
+    if (!WriteProcessMemory(pProcessHandle, targetProcessMem, dllPath, byteCount, &bytesWritten))
+    {
+        vktrace_LogError("Failed to inject ourselves into target process--couldn't write inception DLL name into process.");
+        return FALSE;
+    }
+
+    thread = CreateRemoteThread(pProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, targetProcessMem, 0, NULL);
+    if (thread == NULL)
+    {
+        vktrace_LogError("Failed to inject ourselves into target process--couldn't spawn thread.");
+        return FALSE;
+    }
+    assert(pTracingThread != NULL);
+    *pTracingThread = thread;
+
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    char *tmp;
+    if (ldPreload == NULL)
+        return TRUE;
+    if (*ldPreload == NULL)
+    {
+        tmp = vktrace_copy_and_append("LD_PRELOAD", "=", dllPath);
+    }
+    else
+    {
+        tmp = vktrace_copy_and_append(*ldPreload, " ", dllPath);
+        VKTRACE_DELETE((void*)*ldPreload);
+    }
+    *ldPreload = tmp;
+#endif
+
+    return TRUE;
+}
diff --git a/vktrace/src/vktrace_common/vktrace_platform.h b/vktrace/src/vktrace_common/vktrace_platform.h
new file mode 100644
index 0000000..cd24277
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_platform.h
@@ -0,0 +1,180 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: David Pinedo <david@lunarg.com>
+ **************************************************************************/
+#pragma once
+
+
+#if defined(PLATFORM_LINUX)
+#define _GNU_SOURCE 1
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/prctl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include "wintypes.h"
+#include <sys/mman.h>
+#define APIENTRY
+#define Sleep(n) usleep(n * 1000)
+#define VKTRACE_WINAPI
+typedef pthread_t vktrace_thread;
+typedef pid_t vktrace_process_handle;
+typedef pid_t vktrace_thread_id;
+typedef pid_t vktrace_process_id;
+typedef unsigned int VKTRACE_THREAD_ROUTINE_RETURN_TYPE;
+typedef pthread_mutex_t VKTRACE_CRITICAL_SECTION;
+#define VKTRACE_NULL_THREAD 0
+#define _MAX_PATH PATH_MAX
+#define VKTRACE_PATH_SEPARATOR "/"
+#define VKTRACE_LIST_SEPARATOR ":"
+#define VKTRACE_THREAD_LOCAL __thread
+
+#elif defined(WIN32)
+#define _CRT_RAND_S
+// The following line is needed to use the C++ std::min() or std::max():
+#define NOMINMAX
+#include <Windows.h>
+#include <tchar.h>
+#define VKTRACE_WINAPI WINAPI
+typedef HANDLE vktrace_thread;
+typedef HANDLE vktrace_process_handle;
+typedef DWORD vktrace_thread_id;
+typedef DWORD vktrace_process_id;
+typedef DWORD VKTRACE_THREAD_ROUTINE_RETURN_TYPE;
+typedef CRITICAL_SECTION VKTRACE_CRITICAL_SECTION;
+#define VKTRACE_NULL_THREAD NULL
+#define VKTRACE_PATH_SEPARATOR "\\"
+#define VKTRACE_LIST_SEPARATOR ";"
+#define VKTRACE_THREAD_LOCAL __declspec(thread)
+#if !defined(__cplusplus)
+#define inline _inline
+#endif
+#elif defined(PLATFORM_OSX)
+
+#define _GNU_SOURCE 1
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+//#include <sys/prctl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include "wintypes.h"
+#define APIENTRY
+#define Sleep(n) usleep(n * 1000)
+#define VKTRACE_WINAPI
+typedef pthread_t vktrace_thread;
+typedef pid_t vktrace_process_handle;
+typedef pid_t vktrace_thread_id;
+typedef pid_t vktrace_process_id;
+typedef unsigned int VKTRACE_THREAD_ROUTINE_RETURN_TYPE;
+typedef pthread_mutex_t VKTRACE_CRITICAL_SECTION;
+#define VKTRACE_NULL_THREAD 0
+#define _MAX_PATH PATH_MAX
+#define VKTRACE_PATH_SEPARATOR "/"
+#define VKTRACE_LIST_SEPARATOR ":"
+#define VKTRACE_THREAD_LOCAL __thread
+
+#endif
+
+#if defined(WIN32)
+#include "vktrace_common.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// return the process ID of current process
+vktrace_process_id vktrace_get_pid();
+
+// Get the path of the currently running executable.
+// The string returned must be freed by the caller.
+char* vktrace_platform_get_current_executable_directory();
+
+// Determine if the current process is vktrace[32|64]
+BOOL vktrace_is_loaded_into_vktrace();
+
+// Get the thread id for this thread.
+vktrace_thread_id vktrace_platform_get_thread_id();
+
+// Get the Registry or Environment variable
+char *vktrace_get_global_var(const char *);
+
+// Set the Registry or Environment variable
+void vktrace_set_global_var(const char *, const char *);
+
+// Provides out_array_length uint32s of random data from a secure service
+size_t vktrace_platform_rand_s(uint32_t* out_array, size_t byteCount);
+
+// Alternatives to loading libraries, getting proc addresses, etc
+void * vktrace_platform_open_library(const char* libPath);
+void * vktrace_platform_get_library_entrypoint(void * libHandle, const char *name);
+void vktrace_platform_close_library(void* plibrary);
+BOOL vktrace_platform_get_next_lib_sym(void * *ppFunc, const char * name);
+
+// Returns the partial path appended to the current directory to provide a full path.
+// Note the resulting string may not point to an existing file.
+void vktrace_platform_full_path(const char* partPath, unsigned long bytes, char* buffer);
+
+// returns a newly allocated string which contains just the directory structure of the supplied file path.
+char* vktrace_platform_extract_path(char* _path);
+
+// returns platform specific path for settings / configuration files
+char* vktrace_platform_get_settings_path();
+
+// returns platform specific path for all data files
+char* vktrace_platform_get_data_path();
+
+vktrace_thread vktrace_platform_create_thread(VKTRACE_THREAD_ROUTINE_RETURN_TYPE(*start_routine)(LPVOID), void* args);
+void vktrace_platform_resume_thread(vktrace_thread* pThread);
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+int64_t vktrace_linux_sync_wait_for_thread(vktrace_thread* pThread);
+#endif
+void vktrace_platform_delete_thread(vktrace_thread* pThread);
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+void vktrace_platform_thread_once(void *ctl, void (* func) (void));
+#elif defined(WIN32)
+void vktrace_platform_thread_once(void *ctl, BOOL (CALLBACK * func) (_Inout_ PINIT_ONCE initOnce, _Inout_opt_ PVOID param, _Out_opt_ PVOID *lpContext));
+#endif
+
+void vktrace_create_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection);
+void vktrace_enter_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection);
+void vktrace_leave_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection);
+void vktrace_delete_critical_section(VKTRACE_CRITICAL_SECTION* pCriticalSection);
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+#define VKTRACE_LIBRARY_NAME(projname) (sizeof(void*) == 4)? "lib"#projname"32.so" : "lib"#projname".so"
+#endif
+#if defined(WIN32)
+#define VKTRACE_LIBRARY_NAME(projname) (sizeof(void*) == 4)? #projname"32.dll" : #projname".dll"
+#endif
+
+BOOL vktrace_platform_remote_load_library(vktrace_process_handle pProcessHandle, const char* dllPath, vktrace_thread* pTracingThread, char **ldPreload);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/vktrace/src/vktrace_common/vktrace_process.c b/vktrace/src/vktrace_common/vktrace_process.c
new file mode 100644
index 0000000..6f458e4
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_process.c
@@ -0,0 +1,144 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: David Pinedo <david@lunarg.com>
+ **************************************************************************/
+#include "vktrace_process.h"
+
+
+BOOL vktrace_process_spawn(vktrace_process_info* pInfo)
+{
+    assert(pInfo != NULL);
+
+#if defined(WIN32)
+    {
+    unsigned long processCreateFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
+    char fullExePath[_MAX_PATH];
+    PROCESS_INFORMATION processInformation;
+    STARTUPINFO si = { 0 };
+    si.cb = sizeof(si);
+
+    memset(&processInformation, 0, sizeof(PROCESS_INFORMATION));
+    memset(fullExePath, 0, sizeof(char)*_MAX_PATH);
+    fullExePath[0] = 0;
+
+    SetLastError(0);
+    if (0==SearchPath(NULL, pInfo->exeName, ".exe", ARRAYSIZE(fullExePath), fullExePath, NULL))
+    {
+        vktrace_LogVerbose("Failed to spawn '%s'.", pInfo->exeName);
+        return FALSE;
+    }
+
+    if (!CreateProcess(fullExePath, pInfo->fullProcessCmdLine, NULL, NULL, TRUE,
+        processCreateFlags, NULL, pInfo->workingDirectory,
+        &si, &processInformation))
+    {
+        vktrace_LogVerbose("Failed to spawn '%s'.", fullExePath);
+        return FALSE;
+    }
+
+    pInfo->hProcess = processInformation.hProcess;
+    pInfo->hThread = processInformation.hThread;
+    pInfo->processId = processInformation.dwProcessId;
+    // TODO : Do we need to do anything with processInformation.dwThreadId?
+    }
+#elif defined(PLATFORM_LINUX)
+    pInfo->processId = fork();
+    if (pInfo->processId == -1)
+    {
+        vktrace_LogError("Failed to spawn process.");
+        return FALSE;
+    }
+    else if (pInfo->processId == 0)
+    {
+        // Inside new process
+        char *args[128];
+        const char delim[] = " \t";
+        unsigned int idx;
+
+        // Change process name so the the tracer DLLs will behave as expected when loaded.
+        // NOTE: Must be 15 characters or less.
+        const char * tmpProcName = "vktraceChildProcess";
+        prctl(PR_SET_NAME, (unsigned long)tmpProcName, 0, 0, 0);
+
+        // Change working directory
+        if (chdir(pInfo->workingDirectory) == -1)
+        {
+            vktrace_LogError("Failed to set working directory.");
+        }
+
+        args[0] = pInfo->exeName;
+        args[127] = NULL;
+        idx = 1;
+        args[idx] = strtok(pInfo->processArgs, delim);
+        while ( args[idx] != NULL && idx < 128)
+        {
+            idx++;
+            args[idx] = strtok(NULL, delim);
+        }
+        vktrace_LogDebug("exec process=%s argc=%u\n", pInfo->exeName, idx);
+#if 0  //uncoment to print out list of env vars
+        char *env = environ[0];
+        idx = 0;
+        while (env && strlen(env)) {
+            if (strstr(env, "VK") || strstr(env, "LD"))
+                vktrace_LogDebug("env[%d] = %s", idx++, env);
+            else
+                idx++;
+            env = environ[idx];
+        }
+#endif
+        if (execv(pInfo->exeName, args) < 0)
+        {
+            vktrace_LogError("Failed to spawn process.");
+            exit(1);
+        }
+    }
+#endif
+
+    return TRUE;
+}
+
+void vktrace_process_info_delete(vktrace_process_info* pInfo)
+{
+    if (pInfo->pCaptureThreads != NULL)
+    {
+        vktrace_platform_delete_thread(&(pInfo->pCaptureThreads[0].recordingThread));
+        VKTRACE_DELETE(pInfo->pCaptureThreads);
+    }
+
+#ifdef WIN32
+    vktrace_platform_delete_thread(&(pInfo->watchdogThread));
+#endif
+
+    if (pInfo->pTraceFile != NULL)
+    {
+        vktrace_LogDebug("Closing trace file: '%s'", pInfo->traceFilename);
+        fclose(pInfo->pTraceFile);
+    }
+
+    VKTRACE_DELETE(pInfo->traceFilename);
+    VKTRACE_DELETE(pInfo->workingDirectory);
+    VKTRACE_DELETE(pInfo->processArgs);
+    VKTRACE_DELETE(pInfo->fullProcessCmdLine);
+    VKTRACE_DELETE(pInfo->exeName);
+
+    vktrace_delete_critical_section(&(pInfo->traceFileCriticalSection));
+}
diff --git a/vktrace/src/vktrace_common/vktrace_process.h b/vktrace/src/vktrace_common/vktrace_process.h
new file mode 100644
index 0000000..fb15715
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_process.h
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#pragma once
+
+#include "vktrace_platform.h"
+#include "vktrace_trace_packet_identifiers.h"
+
+typedef struct vktrace_process_capture_trace_thread_info vktrace_process_capture_trace_thread_info;
+
+typedef struct vktrace_process_info
+{
+    char* exeName;
+    char* processArgs;
+    char* fullProcessCmdLine;
+    char* workingDirectory;
+    char* traceFilename;
+    FILE* pTraceFile;
+
+    // vktrace's thread id
+    vktrace_thread_id parentThreadId;
+
+    VKTRACE_CRITICAL_SECTION traceFileCriticalSection;
+
+    volatile BOOL serverRequestsTermination;
+
+    vktrace_process_capture_trace_thread_info* pCaptureThreads;
+
+    // process id, handle, and main thread
+    vktrace_process_id processId;
+    vktrace_process_handle hProcess;
+    vktrace_thread hThread;
+    vktrace_thread watchdogThread;
+} vktrace_process_info;
+
+
+typedef struct vktrace_process_tracer_dll
+{
+    char * dllPath;
+    BOOL bLoaded;
+    VKTRACE_TRACER_ID tid;
+} vktrace_process_tracer_dll;
+
+struct vktrace_process_capture_trace_thread_info
+{
+    vktrace_thread recordingThread;
+    vktrace_process_info* pProcessInfo;
+    VKTRACE_TRACER_ID tracerId;
+};
+
+BOOL vktrace_process_spawn(vktrace_process_info* pInfo);
+void vktrace_process_info_delete(vktrace_process_info* pInfo);
diff --git a/vktrace/src/vktrace_common/vktrace_settings.c b/vktrace/src/vktrace_common/vktrace_settings.c
new file mode 100644
index 0000000..61a41fb
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_settings.c
@@ -0,0 +1,788 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+
+#include "vktrace_settings.h"
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingInfo_print(const vktrace_SettingInfo* pSetting)
+{
+    if (pSetting->bPrintInHelp)
+    {
+        char * pStrParams;
+        char tmpStr[100];
+        if (pSetting->type == VKTRACE_SETTING_STRING)
+        {
+            pStrParams = "<string>";
+        } else if (pSetting->type == VKTRACE_SETTING_BOOL) {
+            pStrParams = "<BOOL>";
+        } else if (pSetting->type == VKTRACE_SETTING_UINT) {
+            pStrParams = "<uint>";
+        } else if (pSetting->type == VKTRACE_SETTING_INT) {
+            pStrParams = "<int>";
+        } else {
+            pStrParams = "< ??? >";
+        }
+#if defined(WIN32)
+        _snprintf_s(tmpStr, sizeof(tmpStr), _TRUNCATE, "-%s,--%s %s",
+                    pSetting->pShortName, pSetting->pLongName, pStrParams);
+# else
+        snprintf(tmpStr, sizeof(tmpStr), "-%s, --%s %s",
+                 pSetting->pShortName, pSetting->pLongName, pStrParams);
+#endif
+        printf("    %-33s  %s\n", tmpStr, pSetting->pDesc);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_print(const vktrace_SettingGroup* pSettingGroup)
+{
+    unsigned int i;
+    printf("%s available options:\n", pSettingGroup->pName);
+
+    for (i = 0; i < pSettingGroup->numSettings; i++)
+    {
+        vktrace_SettingInfo_print(&(pSettingGroup->pSettings[i]));
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+BOOL vktrace_SettingInfo_parse_value(vktrace_SettingInfo* pSetting, const char* arg)
+{
+    switch(pSetting->type)
+    {
+    case VKTRACE_SETTING_STRING:
+        {
+            vktrace_free(*pSetting->Data.ppChar);
+            *pSetting->Data.ppChar = vktrace_allocate_and_copy(arg);
+        }
+        break;
+    case VKTRACE_SETTING_BOOL:
+        {
+            BOOL bTrue = FALSE;
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+            bTrue = (strncasecmp(arg, "true", 4) == 0);
+#elif defined(PLATFORM_WINDOWS)
+            bTrue = (_strnicmp(arg, "true", 4) == 0);
+#endif
+            *pSetting->Data.pBool = bTrue;
+        }
+        break;
+    case VKTRACE_SETTING_UINT:
+        {
+            if (sscanf(arg, "%u", pSetting->Data.pUint) != 1)
+            {
+                vktrace_LogWarning("Invalid unsigned int setting: '%s'. Resetting to default value instead.", arg);
+                *(pSetting->Data.pUint) = *(pSetting->Default.pUint);
+            }
+        }
+        break;
+    case VKTRACE_SETTING_INT:
+        {
+            if (sscanf(arg, "%d", pSetting->Data.pInt) != 1)
+            {
+                vktrace_LogWarning("Invalid int setting: '%s'. Resetting to default value instead.", arg);
+                *(pSetting->Data.pInt) = *(pSetting->Default.pInt);
+            }
+        }
+        break;
+    default:
+        vktrace_LogError("Unhandled setting type (%d).", pSetting->type);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------------
+char* vktrace_SettingInfo_stringify_value(vktrace_SettingInfo* pSetting)
+{
+    switch(pSetting->type)
+    {
+    case VKTRACE_SETTING_STRING:
+        {
+            return vktrace_allocate_and_copy(*pSetting->Data.ppChar);
+        }
+        break;
+    case VKTRACE_SETTING_BOOL:
+        {
+            return (*pSetting->Data.pBool ? vktrace_allocate_and_copy("TRUE") : vktrace_allocate_and_copy("FALSE"));
+        }
+        break;
+    case VKTRACE_SETTING_UINT:
+        {
+            char value[100] = {0};
+            sprintf(value, "%u", *pSetting->Data.pUint);
+            return vktrace_allocate_and_copy(value);
+        }
+        break;
+    case VKTRACE_SETTING_INT:
+        {
+            char value[100] = {0};
+            sprintf(value, "%d", *pSetting->Data.pInt);
+            return vktrace_allocate_and_copy(value);
+        }
+        break;
+    default:
+        assert(!"Unhandled setting type");
+        break;
+    }
+    return vktrace_allocate_and_copy("<unhandled setting type>");
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_reset_defaults(vktrace_SettingGroup* pSettingGroup)
+{
+    if (pSettingGroup != NULL)
+    {
+        unsigned int u;
+        for (u = 0; u < pSettingGroup->numSettings; u++)
+        {
+            vktrace_SettingInfo_reset_default(&pSettingGroup->pSettings[u]);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingInfo_reset_default(vktrace_SettingInfo* pSetting)
+{
+    assert(pSetting != NULL);
+    switch(pSetting->type)
+    {
+    case VKTRACE_SETTING_STRING:
+        if (*pSetting->Data.ppChar != NULL)
+        {
+            vktrace_free(*pSetting->Data.ppChar);
+        }
+
+        if (pSetting->Default.ppChar == NULL)
+        {
+            *pSetting->Data.ppChar = NULL;
+        }
+        else
+        {
+            *pSetting->Data.ppChar = vktrace_allocate_and_copy(*pSetting->Default.ppChar);
+        }
+        break;
+    case VKTRACE_SETTING_BOOL:
+        *pSetting->Data.pBool = *pSetting->Default.pBool;
+        break;
+    case VKTRACE_SETTING_UINT:
+        *pSetting->Data.pUint = *pSetting->Default.pUint;
+        break;
+    case VKTRACE_SETTING_INT:
+        *pSetting->Data.pInt = *pSetting->Default.pInt;
+        break;
+    default:
+        assert(!"Unhandled VKTRACE_SETTING_TYPE");
+        break;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_merge(vktrace_SettingGroup* pSrc, vktrace_SettingGroup** ppDestGroups, unsigned int* pNumDestGroups)
+{
+    unsigned int g;
+    vktrace_SettingGroup* pDestGroup = NULL;
+    assert(pSrc != NULL);
+    assert(ppDestGroups != NULL);
+    assert(pNumDestGroups != NULL);
+
+    for (g = 0; g < *pNumDestGroups; g++)
+    {
+        if (strcmp(pSrc->pName, (*ppDestGroups)[g].pName) == 0)
+        {
+            // group exists, store the pointer
+            pDestGroup = &(*ppDestGroups)[g];
+            break;
+        }
+    }
+
+    if (pDestGroup == NULL)
+    {
+        // need to replicate pSrc into ppDestGroups
+        pDestGroup = vktrace_SettingGroup_Create(vktrace_allocate_and_copy(pSrc->pName), ppDestGroups, pNumDestGroups);
+        assert(pDestGroup != NULL);
+    }
+
+    if (pDestGroup != NULL)
+    {
+        // now add all the settings!
+        unsigned int srcIndex;
+        for (srcIndex = 0; srcIndex < pSrc->numSettings; srcIndex++)
+        {
+            // search for pre-existing setting in the dest group
+            unsigned int destIndex;
+            BOOL bFound = FALSE;
+            for (destIndex = 0; destIndex < pDestGroup->numSettings; destIndex++)
+            {
+                if (strcmp(pDestGroup->pSettings[destIndex].pLongName, pSrc->pSettings[srcIndex].pLongName) == 0)
+                {
+                    bFound = TRUE;
+                    break;
+                }
+            }
+
+            if (bFound == FALSE)
+            {
+                vktrace_SettingGroup_Add_Info(&pSrc->pSettings[srcIndex], pDestGroup);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_Add_Info(vktrace_SettingInfo* pSrcInfo, vktrace_SettingGroup* pDestGroup)
+{
+    assert(pSrcInfo != NULL);
+    assert(pDestGroup != NULL);
+    if (pDestGroup != NULL)
+    {
+        // create a SettingInfo to store the copied information
+        vktrace_SettingInfo info;
+        vktrace_SettingInfo* pTmp;
+        memset(&info, 0, sizeof(vktrace_SettingInfo));
+
+        // copy necessary buffers so that deletion works correctly
+        info.pShortName = vktrace_allocate_and_copy(pSrcInfo->pShortName);
+        info.pLongName = vktrace_allocate_and_copy(pSrcInfo->pLongName);
+        info.type = VKTRACE_SETTING_STRING;
+        info.Data.ppChar = vktrace_malloc(sizeof(char**));
+        *info.Data.ppChar = vktrace_SettingInfo_stringify_value(pSrcInfo);
+        info.pDesc = NULL;
+
+        // add it to the current group
+        pTmp = pDestGroup->pSettings;
+        pDestGroup->numSettings += 1;
+        pDestGroup->pSettings = VKTRACE_NEW_ARRAY(vktrace_SettingInfo, pDestGroup->numSettings);
+        if (pDestGroup->pSettings == NULL)
+        {
+            // failed to allocate new info array
+            // restore original
+            pDestGroup->numSettings -= 1;
+            pDestGroup->pSettings = pTmp;
+        }
+        else
+        {
+            if (pTmp != NULL)
+            {
+                memcpy(pDestGroup->pSettings, pTmp, pDestGroup->numSettings * sizeof(vktrace_SettingInfo));
+            }
+
+            pDestGroup->pSettings[pDestGroup->numSettings - 1] = info;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+vktrace_SettingGroup* vktrace_SettingGroup_Create(const char* pGroupName, vktrace_SettingGroup** ppSettingGroups, unsigned int* pNumSettingGroups)
+{
+    vktrace_SettingGroup* pNewGroup = NULL;
+    vktrace_SettingGroup* pTmp = *ppSettingGroups;
+    unsigned int lastIndex = *pNumSettingGroups;
+
+    (*pNumSettingGroups) += 1;
+
+    *ppSettingGroups = VKTRACE_NEW_ARRAY(vktrace_SettingGroup, *pNumSettingGroups);
+    if (*ppSettingGroups == NULL)
+    {
+        // out of memory!
+        // Don't create the new group, and restore the list to it's original state
+        (*pNumSettingGroups) -= 1;
+        *ppSettingGroups = pTmp;
+    }
+    else
+    {
+        // copy old settings to new ones
+        memcpy(*ppSettingGroups, pTmp, lastIndex * sizeof(vktrace_SettingGroup));
+
+        // clean up old array
+        VKTRACE_DELETE(pTmp);
+
+        // initialize new group
+        memset(&(*ppSettingGroups)[lastIndex], 0, sizeof(vktrace_SettingGroup));
+
+        // name the new group
+        pNewGroup = &(*ppSettingGroups)[lastIndex];
+        pNewGroup->pName = pGroupName;
+    }
+
+    return pNewGroup;
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_update(vktrace_SettingGroup* pSrc, vktrace_SettingGroup* pDestGroups, unsigned int numDestGroups)
+{
+    unsigned int i;
+    vktrace_SettingGroup* pGroup;
+    for (i = 0; i < numDestGroups; i++)
+    {
+        pGroup = &pDestGroups[i];
+        if (strcmp(pSrc->pName, pGroup->pName) == 0)
+        {
+            vktrace_SettingGroup_Apply_Overrides(pGroup, pSrc, 1);
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+int vktrace_SettingGroup_Load_from_file(FILE* pFile, vktrace_SettingGroup** ppSettingGroups, unsigned int* pNumSettingGroups)
+{
+    int retVal = 0;
+    char* line = VKTRACE_NEW_ARRAY(char, 1024);
+
+    assert(pFile != NULL);
+    assert(ppSettingGroups != NULL);
+    assert(pNumSettingGroups != NULL);
+    *pNumSettingGroups = 0;
+
+    if (line == NULL)
+    {
+        vktrace_LogError("Out of memory while reading settings file.");
+        retVal = -1;
+    }
+    else
+    {
+        vktrace_SettingGroup* pCurGroup = NULL;
+        while (feof(pFile) == 0 && ferror(pFile) == 0)
+        {
+            char* lineStart;
+            char* pOpenBracket;
+            char* pCloseBracket;
+            line = fgets(line, 1024, pFile);
+            if (line == NULL)
+            {
+                break;
+            }
+
+            // if line ends with a newline, then replace it with a NULL
+            if (line[strlen(line)-1] == '\n')
+            {
+                line[strlen(line)-1] = '\0';
+            }
+
+            // remove any leading whitespace
+            lineStart = line;
+            while (*lineStart == ' ') { ++lineStart; }
+
+            // skip empty lines
+            if (strlen(lineStart) == 0)
+            {
+                continue;
+            }
+
+            // if the line starts with "#" or "//", then consider it a comment and ignore it.
+            // if the first 'word' is only "-- " then the remainder of the line is for application arguments
+            // else first 'word' in line should be a long setting name and the rest of line is value for setting
+            if (lineStart[0] == '#' || (lineStart[0] == '/' && lineStart[1] == '/'))
+            {
+                // its a comment, continue to next loop iteration
+                continue;
+            }
+
+            pOpenBracket = strchr(lineStart, '[');
+            pCloseBracket = strchr(lineStart, ']');
+            if (pOpenBracket != NULL && pCloseBracket != NULL)
+            {
+                // a group was found!
+                unsigned int i;
+                char* pGroupName = vktrace_allocate_and_copy_n(pOpenBracket + 1,
+                                                           (int) (pCloseBracket - pOpenBracket - 1));
+
+                // Check to see if we already have this group
+                pCurGroup = NULL;
+                for (i = 0; i < *pNumSettingGroups; i++)
+                {
+                    if (strcmp((*ppSettingGroups)[i].pName, pGroupName) == 0)
+                    {
+                        // we already have this group!
+                        pCurGroup = &(*ppSettingGroups)[i];
+                        break;
+                    }
+                }
+
+                if (pCurGroup == NULL)
+                {
+                    // Need to grow our list of groups!
+                    pCurGroup = vktrace_SettingGroup_Create(pGroupName, ppSettingGroups, pNumSettingGroups);
+                }
+            }
+            else
+            {
+                char* pTokName = strtok(lineStart, "=");
+                char* pTokValue = strtok(NULL, "=");
+                if (pTokName != NULL && pTokValue != NULL)
+                {
+                    // A setting name and value were found!
+                    char* pValueStart = pTokValue;
+                    char* pTmpEndName = pTokName;
+
+                    assert(pCurGroup != NULL);
+                    if (pCurGroup != NULL)
+                    {
+                        // create a SettingInfo to store this information
+                        vktrace_SettingInfo info;
+                        vktrace_SettingInfo* pTmp;
+                        memset(&info, 0, sizeof(vktrace_SettingInfo));
+
+                        // trim trailing whitespace by turning it into a null char
+                        while (*pTmpEndName != '\0')
+                        {
+                            if (*pTmpEndName == ' ')
+                            {
+                                *pTmpEndName = '\0';
+                                break;
+                            }
+                            else
+                            {
+                                ++pTmpEndName;
+                            }
+                        }
+
+                        info.pLongName = vktrace_allocate_and_copy(pTokName);
+                        info.type = VKTRACE_SETTING_STRING;
+
+                        // remove leading whitespace from value
+                        while (*pValueStart == ' ') { ++pValueStart; }
+                        info.Data.ppChar = vktrace_malloc(sizeof(char**));
+                        *info.Data.ppChar = vktrace_allocate_and_copy(pValueStart);
+
+                        // add it to the current group
+                        pTmp = pCurGroup->pSettings;
+                        pCurGroup->numSettings += 1;
+                        pCurGroup->pSettings = VKTRACE_NEW_ARRAY(vktrace_SettingInfo, pCurGroup->numSettings);
+                        if (pCurGroup->pSettings == NULL)
+                        {
+                            // failed to allocate new info array
+                            // restore original
+                            pCurGroup->numSettings -= 1;
+                            pCurGroup->pSettings = pTmp;
+                        }
+                        else
+                        {
+                            if (pTmp != NULL)
+                            {
+                                memcpy(pCurGroup->pSettings, pTmp, pCurGroup->numSettings * sizeof(vktrace_SettingInfo));
+                            }
+
+                            pCurGroup->pSettings[pCurGroup->numSettings - 1] = info;
+                        }
+                    }
+                }
+                else
+                {
+                    vktrace_LogWarning("Could not parse a line in settings file: '%s'.", line);
+                }
+            }
+        }
+    }
+
+    VKTRACE_DELETE(line);
+
+    return retVal;
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_Delete_Loaded(vktrace_SettingGroup** ppSettingGroups, unsigned int* pNumSettingGroups)
+{
+    unsigned int g;
+    unsigned int s;
+    assert(ppSettingGroups != NULL);
+    assert(*ppSettingGroups != NULL);
+    assert(pNumSettingGroups != NULL);
+
+    for (g = 0; g < *pNumSettingGroups; g++)
+    {
+        vktrace_SettingGroup* pGroup = &(*ppSettingGroups)[g];
+        vktrace_free((void*)pGroup->pName);
+        pGroup->pName = NULL;
+
+        for (s = 0; s < pGroup->numSettings; s++)
+        {
+            vktrace_free((void*)pGroup->pSettings[s].pShortName);
+            pGroup->pSettings[s].pShortName = NULL;
+
+            vktrace_free((void*)pGroup->pSettings[s].pLongName);
+            pGroup->pSettings[s].pLongName = NULL;
+
+            vktrace_free(*pGroup->pSettings[s].Data.ppChar);
+            vktrace_free(pGroup->pSettings[s].Data.ppChar);
+            pGroup->pSettings[s].Data.ppChar = NULL;
+
+            vktrace_free((void*)pGroup->pSettings[s].pDesc);
+            pGroup->pSettings[s].pDesc = NULL;
+
+        }
+
+        vktrace_free((void*)pGroup->pSettings);
+        pGroup->pSettings = NULL;
+    }
+
+    vktrace_free(*ppSettingGroups);
+    *pNumSettingGroups = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_Apply_Overrides(vktrace_SettingGroup* pSettingGroup, vktrace_SettingGroup* pOverrideGroups, unsigned int numOverrideGroups)
+{
+    unsigned int overrideGroupIndex;
+    assert(pSettingGroup != NULL);
+    assert(pOverrideGroups != NULL);
+
+    // only override matching group (based on name)
+    for (overrideGroupIndex = 0; overrideGroupIndex < numOverrideGroups; overrideGroupIndex++)
+    {
+        if (strcmp(pSettingGroup->pName, pOverrideGroups[overrideGroupIndex].pName) == 0)
+        {
+            unsigned int overrideSettingIndex;
+            vktrace_SettingGroup* pOverride = &pOverrideGroups[overrideGroupIndex];
+
+            for (overrideSettingIndex = 0; overrideSettingIndex < pOverride->numSettings; overrideSettingIndex++)
+            {
+                unsigned int baseSettingIndex;
+                vktrace_SettingInfo* pOverrideSetting = &pOverride->pSettings[overrideSettingIndex];
+
+                // override matching settings based on long name
+                for (baseSettingIndex = 0; baseSettingIndex < pSettingGroup->numSettings; baseSettingIndex++)
+                {
+                    if (strcmp(pSettingGroup->pSettings[baseSettingIndex].pLongName, pOverrideSetting->pLongName) == 0)
+                    {
+                        char* pTmp = vktrace_SettingInfo_stringify_value(pOverrideSetting);
+                        if (vktrace_SettingInfo_parse_value(&pSettingGroup->pSettings[baseSettingIndex], pTmp) == FALSE)
+                        {
+                            vktrace_LogWarning("Failed to parse override value.");
+                        }
+                        vktrace_free(pTmp);
+                        break;
+                    }
+                }
+            }
+            break;
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+BOOL vktrace_SettingGroup_save(vktrace_SettingGroup* pSettingGroup, unsigned int numSettingGroups, FILE* pSettingsFile)
+{
+    BOOL retVal = TRUE;
+
+    if (pSettingGroup == NULL)
+    {
+        vktrace_LogError("Cannot save a null group of settings.");
+        retVal = FALSE;
+    }
+
+    if (pSettingsFile == NULL)
+    {
+        vktrace_LogError("Cannot save an unnamed settings file.");
+        retVal = FALSE;
+    }
+
+    if (retVal == TRUE)
+    {
+        unsigned int g;
+        unsigned int index;
+
+        for (g = 0; g < numSettingGroups; g++)
+        {
+            // group name
+            fputs("[", pSettingsFile);
+            fputs(pSettingGroup[g].pName, pSettingsFile);
+            fputs("]\n", pSettingsFile);
+
+            // settings
+            for (index = 0; index < pSettingGroup[g].numSettings; index++)
+            {
+                char* value = NULL;
+                fputs("   ", pSettingsFile);
+                fputs(pSettingGroup[g].pSettings[index].pLongName, pSettingsFile);
+                fputs(" = ", pSettingsFile);
+                value = vktrace_SettingInfo_stringify_value(&pSettingGroup[g].pSettings[index]);
+                if (value != NULL)
+                {
+                    fputs(value, pSettingsFile);
+                    vktrace_free(value);
+                }
+                else
+                {
+                    fputs("", pSettingsFile);
+                }
+                fputs("\n", pSettingsFile);
+            }
+
+            fputs("\n", pSettingsFile);
+        }
+    }
+
+    return retVal;
+}
+
+//-----------------------------------------------------------------------------
+int vktrace_SettingGroup_init_from_cmdline(vktrace_SettingGroup* pSettingGroup, int argc, char* argv[], char** ppOut_remaining_args)
+{
+    int i = 0;
+
+    if (pSettingGroup != NULL)
+    {
+        vktrace_SettingInfo* pSettings = pSettingGroup->pSettings;
+        unsigned int num_settings = pSettingGroup->numSettings;
+
+        // update settings based on command line options
+        for (i = 1; i < argc; )
+        {
+            unsigned int settingIndex;
+            int consumed = 0;
+            char* curArg = argv[i];
+
+            // if the arg is only "--" then all following args are for the application;
+            // if the arg starts with "-" then it is referring to a short name;
+            // if the arg starts with "--" then it is referring to a long name.
+            if (strcmp("--", curArg) == 0 && ppOut_remaining_args != NULL)
+            {
+                // all remaining args are for the application
+
+                // increment past the current arg
+                i += 1;
+                consumed++;
+                for (; i < argc; i++)
+                {
+                    if (*ppOut_remaining_args == NULL || strlen(*ppOut_remaining_args) == 0)
+                    {
+                        *ppOut_remaining_args = vktrace_allocate_and_copy(argv[i]);
+                    }
+                    else
+                    {
+                        *ppOut_remaining_args = vktrace_copy_and_append(*ppOut_remaining_args, " ", argv[i]);
+                    }
+                    consumed++;
+                }
+            }
+            else
+            {
+                for (settingIndex = 0; settingIndex < num_settings; settingIndex++)
+                {
+                    const char* pSettingName = NULL;
+                    curArg = argv[i];
+                    if (strncmp("--", curArg, 2) == 0)
+                    {
+                        // long option name
+                        pSettingName = pSettings[settingIndex].pLongName;
+                        curArg += 2;
+                    }
+                    else if (strncmp("-", curArg, 1) == 0)
+                    {
+                        // short option name
+                        pSettingName = pSettings[settingIndex].pShortName;
+                        curArg += 1;
+                    }
+
+                    if (pSettingName != NULL && strcmp(curArg, pSettingName) == 0)
+                    {
+                        if (i+1 < argc &&
+                            vktrace_SettingInfo_parse_value(&pSettings[settingIndex], argv[i+1]))
+                        {
+                            consumed += 2;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            if (consumed == 0)
+            {
+                vktrace_SettingGroup_print(pSettingGroup);
+                vktrace_SettingGroup_delete(pSettingGroup);
+                return -1;
+            }
+
+            i += consumed;
+        }
+    }
+
+    return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+int vktrace_SettingGroup_init(vktrace_SettingGroup* pSettingGroup, FILE* pSettingsFile, int argc, char* argv[], const char** ppOut_remaining_args)
+{
+    if (pSettingGroup == NULL)
+    {
+        assert(!"No need to call vktrace_SettingGroup_init if the application has no settings");
+        return 0;
+    }
+
+    if (argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0))
+    {
+        vktrace_SettingGroup_print(pSettingGroup);
+        return -1;
+    }
+
+    // Initially, set all options to their defaults
+    vktrace_SettingGroup_reset_defaults(pSettingGroup);
+
+    // Secondly set options based on settings file
+    if (pSettingsFile != NULL)
+    {
+        vktrace_SettingGroup* pGroups = NULL;
+        unsigned int numGroups = 0;
+        if (vktrace_SettingGroup_Load_from_file(pSettingsFile, &pGroups, &numGroups) == -1)
+        {
+            vktrace_SettingGroup_print(pSettingGroup);
+            return -1;
+        }
+
+        vktrace_SettingGroup_Apply_Overrides(pSettingGroup, pGroups, numGroups);
+
+        vktrace_SettingGroup_Delete_Loaded(&pGroups, &numGroups);
+    }
+
+    // Thirdly set options based on cmd line args
+    if (vktrace_SettingGroup_init_from_cmdline(pSettingGroup, argc, argv, (char **)ppOut_remaining_args) == -1)
+    {
+        return -1;
+    }
+
+    return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void vktrace_SettingGroup_delete(vktrace_SettingGroup* pSettingGroup)
+{
+    if (pSettingGroup != NULL)
+    {
+        unsigned int i;
+
+        // need to delete all strings
+        for (i = 0; i < pSettingGroup->numSettings; i++)
+        {
+            if (pSettingGroup->pSettings[i].type == VKTRACE_SETTING_STRING)
+            {
+                if (*(pSettingGroup->pSettings[i].Data.ppChar) != NULL)
+                {
+                    vktrace_free(*pSettingGroup->pSettings[i].Data.ppChar);
+                    *pSettingGroup->pSettings[i].Data.ppChar = NULL;
+                }
+            }
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_common/vktrace_settings.h b/vktrace/src/vktrace_common/vktrace_settings.h
new file mode 100644
index 0000000..7b7a719
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_settings.h
@@ -0,0 +1,99 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#pragma once
+
+#include "vktrace_common.h"
+
+typedef enum VKTRACE_SETTING_TYPE
+{
+    VKTRACE_SETTING_STRING,
+    VKTRACE_SETTING_BOOL,
+    VKTRACE_SETTING_UINT,
+    VKTRACE_SETTING_INT
+} VKTRACE_SETTING_TYPE;
+
+// ------------------------------------------------------------------------------------------------
+typedef struct vktrace_SettingInfo
+{
+    const char* pShortName;
+    const char* pLongName;
+    VKTRACE_SETTING_TYPE type;
+    union Data
+    {
+        void* pVoid;
+        char** ppChar;
+        BOOL* pBool;
+        unsigned int* pUint;
+        int* pInt;
+    } Data;
+    union Default
+    {
+        void* pVoid;
+        char** ppChar;
+        BOOL* pBool;
+        unsigned int* pUint;
+        int* pInt;
+    } Default;
+    BOOL bPrintInHelp;
+    const char* pDesc;
+} vktrace_SettingInfo;
+
+typedef struct vktrace_SettingGroup
+{
+    const char* pName;
+    unsigned int numSettings;
+    vktrace_SettingInfo* pSettings;
+} vktrace_SettingGroup;
+
+int vktrace_SettingGroup_init(vktrace_SettingGroup* pSettingGroup, FILE *pSettingsFile, int argc, char* argv[], const char** ppOut_remaining_args);
+BOOL vktrace_SettingGroup_save(vktrace_SettingGroup* pSettingGroup, unsigned int numSettingGroups, FILE* pSettingsFile);
+void vktrace_SettingGroup_delete(vktrace_SettingGroup* pSettingGroup);
+void vktrace_SettingGroup_reset_defaults(vktrace_SettingGroup* pSettingGroup);
+
+// Adds pSrc group to ppDestGroups if the named group is not already there,
+// or adds missing settings from pSrc into the existing group in ppDestGroups.
+// pNumDestGroups is updated if pSrc is added to ppDestGroups.
+void vktrace_SettingGroup_merge(vktrace_SettingGroup* pSrc, vktrace_SettingGroup** ppDestGroups, unsigned int* pNumDestGroups);
+
+// Updates DestGroups with values from Src
+void vktrace_SettingGroup_update(vktrace_SettingGroup* pSrc, vktrace_SettingGroup* pDestGroups, unsigned int numDestGroups);
+
+// Creates a new named group at the end of the ppSettingGroups array, and updates pNumSettingGroups.
+vktrace_SettingGroup* vktrace_SettingGroup_Create(const char* pGroupName, vktrace_SettingGroup** ppSettingGroups, unsigned int* pNumSettingGroups);
+
+// Adds a STRING settingInfo to pDestGroup which holds a copy of pSrcInfo, but with a stringified value.
+// The conversion to string is necessary for memory management purposes.
+void vktrace_SettingGroup_Add_Info(vktrace_SettingInfo* pSrcInfo, vktrace_SettingGroup* pDestGroup);
+
+int vktrace_SettingGroup_Load_from_file(FILE* pFile, vktrace_SettingGroup** ppSettingGroups, unsigned int* pNumSettingGroups);
+void vktrace_SettingGroup_Delete_Loaded(vktrace_SettingGroup** ppSettingGroups, unsigned int* pNumSettingGroups);
+
+void vktrace_SettingGroup_Apply_Overrides(vktrace_SettingGroup* pSettingGroup, vktrace_SettingGroup* pOverrideGroups, unsigned int numOverrideGroups);
+
+int vktrace_SettingGroup_init_from_cmdline(vktrace_SettingGroup* pSettingGroup, int argc, char* argv[], char** ppOut_remaining_args);
+
+void vktrace_SettingGroup_print(const vktrace_SettingGroup* pSettingGroup);
+void vktrace_SettingInfo_print(const vktrace_SettingInfo* pSetting);
+
+char* vktrace_SettingInfo_stringify_value(vktrace_SettingInfo* pSetting);
+BOOL vktrace_SettingInfo_parse_value(vktrace_SettingInfo* pSetting, const char* arg);
+void vktrace_SettingInfo_reset_default(vktrace_SettingInfo* pSetting);
diff --git a/vktrace/src/vktrace_common/vktrace_trace_packet_identifiers.h b/vktrace/src/vktrace_common/vktrace_trace_packet_identifiers.h
new file mode 100644
index 0000000..c057d1b
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_trace_packet_identifiers.h
@@ -0,0 +1,166 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#pragma once
+
+#include "vktrace_common.h"
+
+#define VKTRACE_TRACE_FILE_VERSION_2 0x0002
+#define VKTRACE_TRACE_FILE_VERSION_3 0x0003
+#define VKTRACE_TRACE_FILE_VERSION_4 0x0004
+#define VKTRACE_TRACE_FILE_VERSION_5 0x0005
+#define VKTRACE_TRACE_FILE_VERSION VKTRACE_TRACE_FILE_VERSION_5
+#define VKTRACE_TRACE_FILE_VERSION_MINIMUM_COMPATIBLE VKTRACE_TRACE_FILE_VERSION_5
+
+#define VKTRACE_MAX_TRACER_ID_ARRAY_SIZE 14
+
+typedef enum VKTRACE_TRACER_ID
+{
+    VKTRACE_TID_RESERVED = 0,
+    VKTRACE_TID_GL_FPS,
+    VKTRACE_TID_VULKAN
+    // Max enum must be less than VKTRACE_MAX_TRACER_ID_ARRAY_SIZE
+} VKTRACE_TRACER_ID;
+
+typedef struct VKTRACE_TRACER_REPLAYER_INFO
+{
+    VKTRACE_TRACER_ID tracerId;
+    BOOL needsReplayer;
+    const char* const replayerLibraryName;
+    const char* const debuggerLibraryname;
+} VKTRACE_TRACER_REPLAYER_INFO;
+
+// The index here should match the value of the VKTRACE_TRACER_ID
+static const VKTRACE_TRACER_REPLAYER_INFO gs_tracerReplayerInfo[VKTRACE_MAX_TRACER_ID_ARRAY_SIZE] = {
+    {VKTRACE_TID_RESERVED, FALSE, "", ""},
+    {VKTRACE_TID_GL_FPS, FALSE, "", ""},
+    {VKTRACE_TID_VULKAN, TRUE, VKTRACE_LIBRARY_NAME(vulkan_replay), VKTRACE_LIBRARY_NAME(vktraceviewer_vk)},
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+    {VKTRACE_TID_RESERVED, FALSE, "", ""}, // this can be updated as new tracers are added
+};
+
+typedef enum _VKTRACE_TRACE_PACKET_ID
+{
+    VKTRACE_TPI_MESSAGE,
+    VKTRACE_TPI_MARKER_CHECKPOINT,
+    VKTRACE_TPI_MARKER_API_BOUNDARY,
+    VKTRACE_TPI_MARKER_API_GROUP_BEGIN,
+    VKTRACE_TPI_MARKER_API_GROUP_END,
+    VKTRACE_TPI_MARKER_TERMINATE_PROCESS,
+    VKTRACE_TPI_BEGIN_API_HERE // this enum should always be the last in the list. Feel free to insert new ID above this one.
+} VKTRACE_TRACE_PACKET_ID;
+
+typedef struct {
+    uint8_t id;
+    uint8_t is_64_bit;
+} vktrace_tracer_info;
+
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+
+typedef struct {
+    uint16_t trace_file_version;
+    uint32_t uuid[4];
+    uint64_t first_packet_offset;   // will be size of header including size of tracer_id_array and state_snapshot_path/binary
+    uint8_t tracer_count;           // number of tracers referenced in this trace file
+    vktrace_tracer_info tracer_id_array[VKTRACE_MAX_TRACER_ID_ARRAY_SIZE]; // array of tracer_ids and values which are referenced in the trace file
+    uint64_t trace_start_time;
+} vktrace_trace_file_header;
+
+typedef struct {
+    uint64_t size; // total size, including extra data, needed to get to the next packet_header
+    uint64_t global_packet_index;
+    uint8_t tracer_id; // TODO: need to uniquely identify tracers in a way that is known by the replayer
+    uint16_t packet_id; // VKTRACE_TRACE_PACKET_ID (or one of the api-specific IDs)
+    uint32_t thread_id;
+    uint64_t vktrace_begin_time; // start of measuring vktrace's overhead related to this packet
+    uint64_t entrypoint_begin_time;
+    uint64_t entrypoint_end_time;
+    uint64_t vktrace_end_time; // end of measuring vktrace's overhead related to this packet
+    uint64_t next_buffers_offset; // used for tracking the addition of buffers to the trace packet
+    uintptr_t pBody; // points to the body of the packet
+} vktrace_trace_packet_header;
+
+#else
+
+// vktrace_trace_file_header and vktrace_trace_packet_header are written and
+// read by both host and target CPU, which can be different architectures.
+// Specifically for Android, x86 and arm32 align 64-bit integers differently,
+// so we must require 8 byte alignment.
+// These changes are guarded by a 64-bit check above for now.
+
+#if defined(_WIN32) || defined(_WIN64)
+#define ALIGN8 __declspec(align(8))
+#else
+#define ALIGN8 __attribute__((aligned(8)))
+#endif
+
+typedef struct {
+    uint16_t trace_file_version;
+    uint32_t uuid[4];
+    ALIGN8 uint64_t first_packet_offset;   // will be size of header including size of tracer_id_array and state_snapshot_path/binary
+    uint8_t tracer_count;           // number of tracers referenced in this trace file
+    vktrace_tracer_info tracer_id_array[VKTRACE_MAX_TRACER_ID_ARRAY_SIZE]; // array of tracer_ids and values which are referenced in the trace file
+    ALIGN8 uint64_t trace_start_time;
+} vktrace_trace_file_header;
+
+typedef struct {
+    ALIGN8 uint64_t size; // total size, including extra data, needed to get to the next packet_header
+    ALIGN8 uint64_t global_packet_index;
+    uint8_t tracer_id; // TODO: need to uniquely identify tracers in a way that is known by the replayer
+    uint16_t packet_id; // VKTRACE_TRACE_PACKET_ID (or one of the api-specific IDs)
+    uint32_t thread_id;
+    ALIGN8 uint64_t vktrace_begin_time; // start of measuring vktrace's overhead related to this packet
+    ALIGN8 uint64_t entrypoint_begin_time;
+    ALIGN8 uint64_t entrypoint_end_time;
+    ALIGN8 uint64_t vktrace_end_time; // end of measuring vktrace's overhead related to this packet
+    ALIGN8 uint64_t next_buffers_offset; // used for tracking the addition of buffers to the trace packet
+    uintptr_t pBody; // points to the body of the packet
+} vktrace_trace_packet_header;
+
+#endif // 64-bit
+
+typedef struct {
+    vktrace_trace_packet_header* pHeader;
+    VktraceLogLevel type;
+    uint32_t length;
+    char* message;
+} vktrace_trace_packet_message;
+
+typedef struct {
+    vktrace_trace_packet_header* pHeader;
+    unsigned int length;
+    char* label;
+} vktrace_trace_packet_marker_checkpoint;
+
+typedef vktrace_trace_packet_marker_checkpoint vktrace_trace_packet_marker_api_boundary;
+typedef vktrace_trace_packet_marker_checkpoint vktrace_trace_packet_marker_api_group_begin;
+typedef vktrace_trace_packet_marker_checkpoint vktrace_trace_packet_marker_api_group_end;
+
+typedef VKTRACE_TRACER_ID (VKTRACER_CDECL *funcptr_VKTRACE_GetTracerId)();
diff --git a/vktrace/src/vktrace_common/vktrace_trace_packet_utils.c b/vktrace/src/vktrace_common/vktrace_trace_packet_utils.c
new file mode 100644
index 0000000..1ba1246
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_trace_packet_utils.c
@@ -0,0 +1,293 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_interconnect.h"
+#include "vktrace_filelike.h"
+#include "vktrace_pageguard_memorycopy.h"
+
+#ifdef WIN32
+#include <rpc.h>
+#pragma comment (lib, "Rpcrt4.lib")
+#endif
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+#include <fcntl.h>
+#include <time.h>
+#endif
+
+#if defined(PLATFORM_OSX)
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
+#include "vktrace_pageguard_memorycopy.h"
+
+static uint64_t g_packet_index = 0;
+
+void vktrace_gen_uuid(uint32_t* pUuid)
+{
+    uint32_t buf[] = { 0xABCDEF, 0x12345678, 0xFFFECABC, 0xABCDDEF0 };
+    vktrace_platform_rand_s(buf, sizeof(buf)/sizeof(uint32_t));
+
+    pUuid[0] = buf[0];
+    pUuid[1] = buf[1];
+    pUuid[2] = buf[2];
+    pUuid[3] = buf[3];
+}
+
+#if defined(PLATFORM_LINUX)
+uint64_t vktrace_get_time()
+{
+    struct timespec time;
+    clock_gettime(CLOCK_MONOTONIC, &time);
+    return ((uint64_t)time.tv_sec * 1000000000) + time.tv_nsec;
+}
+#elif defined(PLATFORM_OSX)
+uint64_t vktrace_get_time()
+{
+    clock_serv_t cclock;
+    mach_timespec_t mts;
+    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+    clock_get_time(cclock, &mts);
+    mach_port_deallocate(mach_task_self(), cclock);
+
+    return ((uint64_t)mts.tv_sec * 1000000000) + mts.tv_nsec;
+}
+#elif defined(PLATFORM_WINDOWS)
+uint64_t vktrace_get_time()
+{
+    // Should really avoid using RDTSC here since for RDTSC to be
+    // accurate, the process needs to stay on the same CPU and the CPU
+    // needs to stay at the same clock rate, which isn't always the case
+    // with today's power managed CPUs.
+    // But if all that is OK, the following one-liner could be used instead
+    // of the rest of this function.
+    //
+    // return __rdtsc();
+    //
+    LARGE_INTEGER count;
+    static LARGE_INTEGER start, freq;
+    if (0 == start.QuadPart) {
+        QueryPerformanceFrequency(&freq);
+        QueryPerformanceCounter(&start);
+    }
+    QueryPerformanceCounter(&count);
+    // Using a relative (from start) count here postpones overflow as we convert to ns.
+    return (uint64_t)(((count.QuadPart - start.QuadPart) * 1000000000) / freq.QuadPart);
+}
+#else
+uint64_t vktrace_get_time()
+{
+    return 0;
+}
+#endif
+
+//=============================================================================
+// trace file header
+
+vktrace_trace_file_header* vktrace_create_trace_file_header()
+{
+    vktrace_trace_file_header* pHeader = VKTRACE_NEW(vktrace_trace_file_header);
+    memset(pHeader, 0, sizeof(vktrace_trace_file_header));
+    pHeader->trace_file_version = VKTRACE_TRACE_FILE_VERSION;
+    vktrace_gen_uuid(pHeader->uuid);
+    pHeader->trace_start_time = vktrace_get_time();
+
+    return pHeader;
+}
+
+void vktrace_delete_trace_file_header(vktrace_trace_file_header** ppHeader)
+{
+    vktrace_free(*ppHeader);
+    *ppHeader = NULL;
+}
+
+//=============================================================================
+// Methods for creating, populating, and writing trace packets
+
+vktrace_trace_packet_header* vktrace_create_trace_packet(uint8_t tracer_id, uint16_t packet_id, uint64_t packet_size, uint64_t additional_buffers_size)
+{
+    // Always allocate at least enough space for the packet header
+    uint64_t total_packet_size = sizeof(vktrace_trace_packet_header) + packet_size + additional_buffers_size;
+    void* pMemory = vktrace_malloc((size_t)total_packet_size);
+    memset(pMemory, 0, (size_t)total_packet_size);
+
+    vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)pMemory;
+    pHeader->size = total_packet_size;
+    pHeader->global_packet_index = g_packet_index++;
+    pHeader->tracer_id = tracer_id;
+    pHeader->thread_id = vktrace_platform_get_thread_id();
+    pHeader->packet_id = packet_id;
+    if (pHeader->vktrace_begin_time == 0)
+        pHeader->vktrace_begin_time = vktrace_get_time();
+    pHeader->entrypoint_begin_time = pHeader->vktrace_begin_time;
+    pHeader->entrypoint_end_time = 0;
+    pHeader->vktrace_end_time = 0;
+    pHeader->next_buffers_offset = sizeof(vktrace_trace_packet_header) + packet_size; // initial offset is from start of header to after the packet body
+    if (total_packet_size > sizeof(vktrace_trace_packet_header))
+    {
+        pHeader->pBody = (uintptr_t)(((char*)pMemory) + sizeof(vktrace_trace_packet_header));
+    }
+    return pHeader;
+}
+
+void vktrace_delete_trace_packet(vktrace_trace_packet_header** ppHeader)
+{
+    if (ppHeader == NULL)
+        return;
+    if (*ppHeader == NULL)
+        return;
+
+    VKTRACE_DELETE(*ppHeader);
+    *ppHeader = NULL;
+}
+
+void* vktrace_trace_packet_get_new_buffer_address(vktrace_trace_packet_header* pHeader, uint64_t byteCount)
+{
+    void* pBufferStart;
+    assert(byteCount > 0);
+    assert((byteCount&0x3) == 0);  // All buffer sizes should be multiple of 4 so structs in packet are kept aligned
+    assert(pHeader->size >= pHeader->next_buffers_offset + byteCount);
+    if (pHeader->size < pHeader->next_buffers_offset + byteCount || byteCount == 0)
+    {
+        // not enough memory left in packet to hold buffer
+        // or request is for 0 bytes
+        return NULL;
+    }
+
+    pBufferStart = (void*)((char*)pHeader + pHeader->next_buffers_offset);
+    pHeader->next_buffers_offset += byteCount;
+    return pBufferStart;
+}
+
+void vktrace_add_buffer_to_trace_packet(vktrace_trace_packet_header* pHeader, void** ptr_address, uint64_t size, const void* pBuffer)
+{
+
+    // Make sure we have valid pointers and sizes. All pointers and sizes must be 4 byte aligned.
+    assert(ptr_address != NULL);
+    assert((size&0x3) == 0);
+
+    if (pBuffer == NULL || size == 0)
+    {
+        *ptr_address = NULL;
+    }
+    else
+    {
+        // set ptr to the location of the added buffer
+        *ptr_address = vktrace_trace_packet_get_new_buffer_address(pHeader, size);
+
+        // address of buffer in packet adding must be 4 byte aligned
+        assert(((uint64_t)*ptr_address&0x3) == 0);
+
+        // copy buffer to the location
+        vktrace_pageguard_memcpy(*ptr_address, pBuffer, (size_t)size);
+    }
+}
+
+void vktrace_finalize_buffer_address(vktrace_trace_packet_header* pHeader, void** ptr_address)
+{
+    assert(ptr_address != NULL);
+
+    if (*ptr_address != NULL)
+    {
+        // turn ptr into an offset from the packet body
+        uint64_t offset = (uint64_t)*ptr_address - (uint64_t) (pHeader->pBody);
+        *ptr_address = (void*)offset;
+    }
+}
+
+void vktrace_set_packet_entrypoint_end_time(vktrace_trace_packet_header* pHeader)
+{
+    pHeader->entrypoint_end_time = vktrace_get_time();
+}
+
+void vktrace_finalize_trace_packet(vktrace_trace_packet_header* pHeader)
+{
+    if (pHeader->entrypoint_end_time == 0)
+    {
+        vktrace_set_packet_entrypoint_end_time(pHeader);
+    }
+    pHeader->vktrace_end_time = vktrace_get_time();
+}
+
+void vktrace_write_trace_packet(const vktrace_trace_packet_header* pHeader, FileLike* pFile)
+{
+    BOOL res = vktrace_FileLike_WriteRaw(pFile, pHeader, (size_t)pHeader->size);
+    if (!res && pHeader->packet_id != VKTRACE_TPI_MARKER_TERMINATE_PROCESS)
+    {
+        // We don't retry on failure because vktrace_FileLike_WriteRaw already retried and gave up.
+        vktrace_LogWarning("Failed to write trace packet.");
+        exit(1);
+    }
+}
+
+//=============================================================================
+// Methods for Reading and interpretting trace packets
+
+vktrace_trace_packet_header* vktrace_read_trace_packet(FileLike* pFile)
+{
+    // read size
+    // allocate space
+    // offset to after size
+    // read the rest of the packet
+    uint64_t total_packet_size = 0;
+
+    if (vktrace_FileLike_ReadRaw(pFile, &total_packet_size, sizeof(uint64_t)) == FALSE)
+    {
+        return NULL;
+    }
+
+    // allocate space
+    vktrace_trace_packet_header *pHeader = (vktrace_trace_packet_header*)vktrace_malloc((size_t)total_packet_size);
+
+    if (pHeader != NULL)
+    {
+        pHeader->size = total_packet_size;
+        if (vktrace_FileLike_ReadRaw(pFile, (char*)pHeader + sizeof(uint64_t), (size_t)total_packet_size - sizeof(uint64_t)) == FALSE)
+        {
+            vktrace_LogError("Failed to read trace packet with size of %u.", total_packet_size);
+            return NULL;
+        }
+
+        pHeader->pBody = (uintptr_t)pHeader + sizeof(vktrace_trace_packet_header);
+    }
+    else {
+        vktrace_LogError("Malloc failed in vktrace_read_trace_packet of size %u.", total_packet_size);
+    }
+
+    return pHeader;
+}
+
+void* vktrace_trace_packet_interpret_buffer_pointer(vktrace_trace_packet_header* pHeader, intptr_t ptr_variable)
+{
+    // the pointer variable actually contains a byte offset from the packet body to the start of the buffer.
+    uint64_t offset = ptr_variable;
+    void* buffer_location;
+
+    // if the offset is 0, then we know the pointer to the buffer was NULL, so no buffer exists and we return NULL.
+    if (offset == 0)
+        return NULL;
+
+    buffer_location = (char*)(pHeader->pBody) + offset;
+    return buffer_location;
+}
diff --git a/vktrace/src/vktrace_common/vktrace_trace_packet_utils.h b/vktrace/src/vktrace_common/vktrace_trace_packet_utils.h
new file mode 100644
index 0000000..c21ace0
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_trace_packet_utils.h
@@ -0,0 +1,157 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#pragma once
+
+#include "vktrace_multiplatform.h"
+#include "vktrace_trace_packet_identifiers.h"
+#include "vktrace_filelike.h"
+#include "vktrace_memory.h"
+#include "vktrace_process.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// pUuid is expected to be an array of 4 unsigned ints
+void vktrace_gen_uuid(uint32_t* pUuid);
+
+uint64_t vktrace_get_time();
+
+//=============================================================================
+// trace file header
+
+// there is a file header at the start of every trace file
+vktrace_trace_file_header* vktrace_create_trace_file_header();
+
+// deletes the trace file header and sets pointer to NULL
+void vktrace_delete_trace_file_header(vktrace_trace_file_header** ppHeader);
+
+static FILE* vktrace_write_trace_file_header(vktrace_process_info* pProcInfo)
+{
+    FILE* tracefp = NULL;
+    vktrace_trace_file_header* pHeader = NULL;
+    size_t items_written = 0;
+    assert(pProcInfo != NULL);
+
+    // open trace file
+    tracefp = fopen(pProcInfo->traceFilename, "wb");
+    if (tracefp == NULL)
+    {
+        vktrace_LogError("Cannot open trace file for writing %s.", pProcInfo->traceFilename);
+        return tracefp;
+    }
+    else
+    {
+        vktrace_LogDebug("Creating trace file: '%s'", pProcInfo->traceFilename);
+    }
+
+    // populate header information
+    pHeader = vktrace_create_trace_file_header();
+    pHeader->first_packet_offset = sizeof(vktrace_trace_file_header);
+    pHeader->tracer_count = 1;
+
+
+    pHeader->tracer_id_array[0].id = pProcInfo->pCaptureThreads[0].tracerId;
+    pHeader->tracer_id_array[0].is_64_bit = (sizeof(intptr_t) == 8) ? 1 : 0;
+
+    // create critical section
+    vktrace_create_critical_section(&pProcInfo->traceFileCriticalSection);
+
+    // write header into file
+    vktrace_enter_critical_section(&pProcInfo->traceFileCriticalSection);
+    items_written = fwrite(pHeader, sizeof(vktrace_trace_file_header), 1, tracefp);
+    vktrace_leave_critical_section(&pProcInfo->traceFileCriticalSection);
+    if (items_written != 1)
+    {
+        vktrace_LogError("Failed to write trace file header.");
+        vktrace_delete_critical_section(&pProcInfo->traceFileCriticalSection);
+        fclose(tracefp);
+        return NULL;
+    }
+    vktrace_delete_trace_file_header(&pHeader);
+    return tracefp;
+};
+
+
+//=============================================================================
+// trace packets
+// There is a trace_packet_header before every trace_packet_body.
+// Additional buffers will come after the trace_packet_body.
+
+//=============================================================================
+// Methods for creating, populating, and writing trace packets
+
+// \param packet_size should include the total bytes for the specific type of packet, and any additional buffers needed by the packet.
+//        The size of the header will be added automatically within the function.
+vktrace_trace_packet_header* vktrace_create_trace_packet(uint8_t tracer_id, uint16_t packet_id, uint64_t packet_size, uint64_t additional_buffers_size);
+
+// deletes a trace packet and sets pointer to NULL
+void vktrace_delete_trace_packet(vktrace_trace_packet_header** ppHeader);
+
+// gets the next address available to write a buffer into the packet
+void* vktrace_trace_packet_get_new_buffer_address(vktrace_trace_packet_header* pHeader, uint64_t byteCount);
+
+// copies buffer data into a trace packet at the specified offset (from the end of the header).
+// it is up to the caller to ensure that buffers do not overlap.
+void vktrace_add_buffer_to_trace_packet(vktrace_trace_packet_header* pHeader, void** ptr_address, uint64_t size, const void* pBuffer);
+
+// converts buffer pointers into byte offset so that pointer can be interpretted after being read into memory
+void vktrace_finalize_buffer_address(vktrace_trace_packet_header* pHeader, void** ptr_address);
+
+// sets entrypoint end time
+void vktrace_set_packet_entrypoint_end_time(vktrace_trace_packet_header* pHeader);
+
+//void initialize_trace_packet_header(vktrace_trace_packet_header* pHeader, uint8_t tracer_id, uint16_t packet_id, uint64_t total_packet_size);
+void vktrace_finalize_trace_packet(vktrace_trace_packet_header* pHeader);
+
+// Write the trace packet to the filelike thing.
+// This has no knowledge of the details of the packet other than its size.
+void vktrace_write_trace_packet(const vktrace_trace_packet_header* pHeader, FileLike* pFile);
+
+//=============================================================================
+// Methods for Reading and interpretting trace packets
+
+// Reads in the trace packet header, the body of the packet, and additional buffers
+vktrace_trace_packet_header* vktrace_read_trace_packet(FileLike* pFile);
+
+// converts a pointer variable that is currently byte offset into a pointer to the actual offset location
+void* vktrace_trace_packet_interpret_buffer_pointer(vktrace_trace_packet_header* pHeader, intptr_t ptr_variable);
+
+//=============================================================================
+// trace packet message
+// Interpretting a trace_packet_message should be done only when:
+// 1) a trace_packet is first created and most of the contents are empty.
+// 2) immediately after the packet was read from memory
+// All other conversions of the trace packet body from the header should 
+// be performed using a C-style cast.
+static vktrace_trace_packet_message* vktrace_interpret_body_as_trace_packet_message(vktrace_trace_packet_header* pHeader)
+{
+    vktrace_trace_packet_message* pPacket = (vktrace_trace_packet_message*)pHeader->pBody;
+    // update pointers
+    pPacket->pHeader = pHeader;
+    pPacket->message = (char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->message);
+    return pPacket;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/vktrace/src/vktrace_common/vktrace_tracelog.c b/vktrace/src/vktrace_common/vktrace_tracelog.c
new file mode 100644
index 0000000..cea1a24
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_tracelog.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#include "vktrace_platform.h"
+
+#include "vktrace_tracelog.h"
+#include "vktrace_trace_packet_utils.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+// filelike thing that is used for outputting trace messages
+static FileLike* g_pFileOut = NULL;
+
+VKTRACE_TRACER_ID g_tracelog_tracer_id = VKTRACE_TID_RESERVED;
+
+void vktrace_trace_set_trace_file(FileLike* pFileLike)
+{
+    g_pFileOut = pFileLike;
+}
+
+// set initial value to 0 but once we read the trace file version
+// we will update this and use for version checks
+static uint32_t g_trace_version_num = 0;
+
+void vktrace_set_trace_version(uint32_t version)
+{
+    g_trace_version_num = version;
+}
+
+BOOL vktrace_check_min_version(uint32_t version)
+{
+    return ((g_trace_version_num) >= (version) ? true : false);
+}
+
+FileLike* vktrace_trace_get_trace_file()
+{
+    return g_pFileOut;
+}
+
+void vktrace_tracelog_set_tracer_id(uint8_t tracerId)
+{
+    g_tracelog_tracer_id = (VKTRACE_TRACER_ID)tracerId;
+}
+
+VKTRACE_REPORT_CALLBACK_FUNCTION s_reportFunc;
+VktraceLogLevel s_logLevel = VKTRACE_LOG_ERROR;
+
+const char* vktrace_LogLevelToString(VktraceLogLevel level)
+{
+    switch(level)
+    {
+    case VKTRACE_LOG_NONE: return "Quiet";
+    case VKTRACE_LOG_DEBUG: return "Debug";
+    case VKTRACE_LOG_ERROR: return "Errors";
+    case VKTRACE_LOG_WARNING: return "Warnings";
+    case VKTRACE_LOG_VERBOSE: return "Info";
+    default:
+        return "Unknown";
+    }
+}
+
+const char* vktrace_LogLevelToShortString(VktraceLogLevel level)
+{
+    switch(level)
+    {
+    case VKTRACE_LOG_NONE: return "Quiet";
+    case VKTRACE_LOG_DEBUG: return "Debug";
+    case VKTRACE_LOG_ERROR: return "Errors";
+    case VKTRACE_LOG_WARNING: return "Warnings";
+    case VKTRACE_LOG_VERBOSE: return "Info";
+    default:
+        return "Unknown";
+    }
+}
+
+
+// For use by both tools and libraries.
+void vktrace_LogSetCallback(VKTRACE_REPORT_CALLBACK_FUNCTION pCallback)
+{
+    s_reportFunc = pCallback;
+}
+
+void vktrace_LogSetLevel(VktraceLogLevel level)
+{
+    vktrace_LogDebug("Log Level = %u (%s)", level, vktrace_LogLevelToString(level));
+    s_logLevel = level;
+}
+
+BOOL vktrace_LogIsLogging(VktraceLogLevel level)
+{
+    return (level <= s_logLevel) ? TRUE : FALSE;
+}
+
+void LogGuts(VktraceLogLevel level, const char* fmt, va_list args)
+{
+#if defined(WIN32)
+        int requiredLength = _vscprintf(fmt, args) + 1;
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+        int requiredLength;
+        va_list argcopy;
+        va_copy(argcopy, args);
+        requiredLength = vsnprintf(NULL, 0, fmt, argcopy) + 1;
+        va_end(argcopy);
+#endif
+    static VKTRACE_THREAD_LOCAL BOOL logging = FALSE;
+
+    // Don't recursively log problems found during logging
+    if (logging)
+    {
+        return;
+    }
+    logging = TRUE;
+
+    char* message = (char*)vktrace_malloc(requiredLength);
+#if defined(WIN32)
+    _vsnprintf_s(message, requiredLength, requiredLength - 1, fmt, args);
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    vsnprintf(message, requiredLength, fmt, args);
+#endif
+
+    if (s_reportFunc != NULL)
+    {
+        s_reportFunc(level, message);
+    }
+    else
+    {
+#ifdef ANDROID
+        #include <android/log.h>
+        __android_log_print(ANDROID_LOG_INFO, "vktrace", "%s: %s\n", vktrace_LogLevelToString(level), message);
+#else
+        printf("%s: %s\n", vktrace_LogLevelToString(level), message);
+#endif
+    }
+
+    vktrace_free(message);
+    logging = FALSE;
+}
+
+void vktrace_LogAlways(const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    LogGuts(VKTRACE_LOG_VERBOSE, format, args);
+    va_end(args);
+}
+
+void vktrace_LogDebug(const char* format, ...)
+{
+#if defined(_DEBUG)
+	if (vktrace_LogIsLogging(VKTRACE_LOG_DEBUG))
+	{
+		va_list args;
+	    va_start(args, format);
+	    LogGuts(VKTRACE_LOG_DEBUG, format, args);
+	    va_end(args);
+    }
+#endif
+}
+
+void vktrace_LogError(const char* format, ...)
+{
+    if (vktrace_LogIsLogging(VKTRACE_LOG_ERROR))
+    {
+        va_list args;
+        va_start(args, format);
+        LogGuts(VKTRACE_LOG_ERROR, format, args);
+        va_end(args);
+    }
+}
+
+void vktrace_LogWarning(const char* format, ...)
+{
+    if (vktrace_LogIsLogging(VKTRACE_LOG_WARNING))
+    {
+        va_list args;
+        va_start(args, format);
+        LogGuts(VKTRACE_LOG_WARNING, format, args);
+        va_end(args);
+    }
+}
+
+void vktrace_LogVerbose(const char* format, ...)
+{
+    if (vktrace_LogIsLogging(VKTRACE_LOG_VERBOSE))
+    {
+        va_list args;
+        va_start(args, format);
+        LogGuts(VKTRACE_LOG_VERBOSE, format, args);
+        va_end(args);
+    }
+}
diff --git a/vktrace/src/vktrace_common/vktrace_tracelog.h b/vktrace/src/vktrace_common/vktrace_tracelog.h
new file mode 100644
index 0000000..a84776f
--- /dev/null
+++ b/vktrace/src/vktrace_common/vktrace_tracelog.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+typedef struct FileLike FileLike;
+
+typedef enum {
+    VKTRACE_LOG_NONE = 0,
+    VKTRACE_LOG_ERROR,
+    VKTRACE_LOG_WARNING,
+    VKTRACE_LOG_VERBOSE,
+    VKTRACE_LOG_DEBUG
+} VktraceLogLevel;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char* vktrace_LogLevelToString(VktraceLogLevel level);
+const char* vktrace_LogLevelToShortString(VktraceLogLevel level);
+
+void vktrace_trace_set_trace_file(FileLike* pFileLike);
+FileLike* vktrace_trace_get_trace_file();
+void vktrace_tracelog_set_tracer_id(uint8_t tracerId);
+
+void vktrace_set_trace_version(uint32_t version);
+BOOL vktrace_check_min_version(uint32_t version);
+
+// Logging is done by reporting the messages back to a callback.
+// Plugins should register a callback from the parent tool;
+// Tools should register their own callback so that they can output messages as desired.
+typedef void (*VKTRACE_REPORT_CALLBACK_FUNCTION)(VktraceLogLevel level, const char* pMsg);
+extern VKTRACE_REPORT_CALLBACK_FUNCTION s_reportFunc;
+extern VktraceLogLevel s_logLevel;
+
+void vktrace_LogSetCallback(VKTRACE_REPORT_CALLBACK_FUNCTION pCallback);
+void vktrace_LogSetLevel(VktraceLogLevel level);
+
+// Allows checking if a level is being logged so that string-related functions
+// can be skipped if they will not reported.
+BOOL vktrace_LogIsLogging(VktraceLogLevel level);
+
+// Always log the message, no matter what the ReportingLevel is.
+void vktrace_LogAlways(const char* format, ...);
+
+// Log debug information that is primarily helpful for Vktrace developers
+// and will only appear in _DEBUG builds.
+// This will also always be logged, no matter what the ReportingLevel is.
+void vktrace_LogDebug(const char* format, ...);
+
+// Log an error message.
+void vktrace_LogError(const char* format, ...);
+
+// Log a warning.
+void vktrace_LogWarning(const char* format, ...);
+
+// Log any misc information that might help a user understand what is going on.
+void vktrace_LogVerbose(const char* format, ...);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/vktrace/src/vktrace_common/wintypes.h b/vktrace/src/vktrace_common/wintypes.h
new file mode 100644
index 0000000..d4cbd52
--- /dev/null
+++ b/vktrace/src/vktrace_common/wintypes.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+#pragma once
+
+#if  defined(__linux__) || defined(__APPLE__)
+#include <stdint.h>
+#include <stddef.h>
+typedef void * LPVOID;
+typedef void * PVOID;
+typedef void VOID;
+typedef char CHAR;
+typedef char TCHAR;
+typedef long LONG;
+typedef unsigned long ULONG;
+typedef int BOOL;
+typedef size_t SIZE_T;
+typedef unsigned long DWORD;
+typedef unsigned char BYTE;
+typedef unsigned char *PBYTE;
+typedef unsigned short USHORT;
+typedef unsigned char UCHAR;
+typedef unsigned short WORD;
+typedef DWORD * DWORD_PTR;
+typedef DWORD *PDWORD;
+typedef DWORD_PTR *PDWORD_PTR;
+typedef int32_t INT32;
+typedef int64_t LONG64;
+typedef uint64_t ULONG64;
+typedef const char * PCSTR;
+typedef const wchar_t * PCWSTR;
+#ifndef MAX_PATH
+#include <limits.h>
+#ifndef PATH_MAX
+#define MAX_PATH 4096
+#else
+#define MAX_PATH PATH_MAX
+#endif
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#elif WIN32
+#include <windows.h>
+#endif
+
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/CMakeLists.txt b/vktrace/src/vktrace_extensions/vktracevulkan/CMakeLists.txt
new file mode 100644
index 0000000..57a076c
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/CMakeLists.txt
@@ -0,0 +1,75 @@
+PROJECT(vktracevulkan)
+cmake_minimum_required(VERSION 2.8)
+
+#include(FindPkgConfig)
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+    set(PYTHON_CMD "python3")
+else()
+    set(PYTHON_CMD "py")
+endif()
+
+
+# if Vktrace is being built as part of a vulkan driver build, then use that target instead of the locally-commited binary.
+#if (TARGET vulkan)
+#    message(STATUS "Using external Vulkan header and library.")
+#    set(VKTRACE_VULKAN_LIB vulkan)
+    set(VKTRACE_VULKAN_DRIVER_DIR ${CMAKE_SOURCE_DIR})
+    set(VKTRACE_VULKAN_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include/vulkan)
+    set(VKTRACE_VULKAN_HEADER ${CMAKE_SOURCE_DIR}/include/vulkan/vulkan.h)
+    #set(VKTRACE_VULKAN_LUNARG_DEBUG_MARKER_HEADER ${VKTRACE_VULKAN_INCLUDE_DIR}/vk_lunarg_debug_marker.h)
+#else()
+    # Use a locally committed vulkan header and binary
+#    message(STATUS "Using Vktrace-supplied Vulkan header and library.")
+#    set(VKTRACE_VULKAN_DRIVER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vulkan)
+#    set(VKTRACE_VULKAN_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vulkan/include)
+#    set(VKTRACE_VULKAN_HEADER ${VKTRACE_VULKAN_INCLUDE_DIR}/vulkan/vulkan.h)
+#    set(VKTRACE_VULKAN_DEBUG_REPORT_LUNARG_HEADER ${VKTRACE_VULKAN_INCLUDE_DIR}/vk_debug_report_lunarg.h)
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+   if (CMAKE_GENERATOR MATCHES "^Visual Studio.*")
+      set(VKTRACE_VULKAN_LIB
+         ${CMAKE_BINARY_DIR}/loader/${CMAKE_CFG_INTDIR}/${API_LOWERCASE}-1.lib
+         )
+   else()
+      set(VKTRACE_VULKAN_LIB
+         ${CMAKE_BINARY_DIR}/loader/${API_LOWERCASE}-1.lib
+         )
+   endif()
+endif()
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
+    ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+    set(VKTRACE_VULKAN_LIB
+        ${CMAKE_BINARY_DIR}/loader/lib${API_LOWERCASE}.so
+    )
+
+endif()
+
+message(STATUS "VKTRACE_VULKAN_LIB = " ${VKTRACE_VULKAN_LIB})
+#message(STATUS "VKTRACE_VULKAN_DRIVER_DIR = " ${VKTRACE_VULKAN_DRIVER_DIR})
+#message(STATUS "VKTRACE_VULKAN_HEADER = " ${VKTRACE_VULKAN_HEADER})
+
+# Run a codegen script to generate utilities that are vulkan-specific, dependent on the vulkan header files, and may be shared by the tracer, replayer, or debugger.
+# Generally, these are likely to be things that SHOULD be provided by the vulkan SDK.
+set(VKTRACE_VULKAN_CODEGEN_UTILS "vulkan/codegen_utils")
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${VKTRACE_VULKAN_CODEGEN_UTILS})
+
+# generate files for vulkan.h
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SCRIPTS_DIR}/lvl_genvk.py -registry ${SCRIPTS_DIR}/vk.xml -o ${CMAKE_CURRENT_SOURCE_DIR}/${VKTRACE_VULKAN_CODEGEN_UTILS} vk_struct_size_helper.h)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SCRIPTS_DIR}/lvl_genvk.py -registry ${SCRIPTS_DIR}/vk.xml -o ${CMAKE_CURRENT_SOURCE_DIR}/${VKTRACE_VULKAN_CODEGEN_UTILS} vk_struct_size_helper.c)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SCRIPTS_DIR}/lvl_genvk.py -registry ${SCRIPTS_DIR}/vk.xml -o ${CMAKE_CURRENT_SOURCE_DIR}/${VKTRACE_VULKAN_CODEGEN_UTILS} vk_enum_string_helper.h)
+
+# Run a codegen script to generate vktrace-specific vulkan utils
+set(CODEGEN_VKTRACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/codegen_vktrace_utils")
+file(MAKE_DIRECTORY ${CODEGEN_VKTRACE_DIR})
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-packet-id VK_VERSION_1_0 OUTPUT_FILE ${CODEGEN_VKTRACE_DIR}/vktrace_vk_packet_id.h)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-core-trace-packets VK_VERSION_1_0 OUTPUT_FILE ${CODEGEN_VKTRACE_DIR}/vktrace_vk_vk_packets.h)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-ext-trace-packets vk_lunarg_debug_marker OUTPUT_FILE ${CODEGEN_VKTRACE_DIR}/vktrace_vk_vk_lunarg_debug_marker_packets.h)
+
+# Directories which actually contain vulkan-specific vktrace plugins.
+add_subdirectory(vkreplay/)
+# Only build vktraceviewer when Qt5 is available
+if (Qt5_FOUND AND BUILD_VKTRACEVIEWER)
+    add_subdirectory(vktraceviewer/)
+endif()
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/CMakeLists.txt b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/CMakeLists.txt
new file mode 100644
index 0000000..9e6691c
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/CMakeLists.txt
@@ -0,0 +1,79 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(vulkan_replay)
+
+include("${SRC_DIR}/build_options.cmake")
+
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/codegen)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-replay-vk-funcs     VK_VERSION_1_0 OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vkreplay_vk_func_ptrs.h)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-replay-c            VK_VERSION_1_0 OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vkreplay_vk_replay_gen.cpp)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-replay-obj-mapper-h VK_VERSION_1_0 OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vkreplay_vk_objmapper.h)
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+set(OS_REPLAYER_LIBS
+    xcb
+)
+endif()
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows" OR
+    ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+set(OS_REPLAYER_LIBS  )
+endif()
+
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+
+set(SRC_LIST
+    ${SRC_LIST}
+    vkreplay.cpp
+    vkreplay_settings.cpp
+    vkreplay_vkreplay.cpp
+    vkreplay_vkdisplay.cpp
+    codegen/vkreplay_vk_replay_gen.cpp
+)
+
+set (HDR_LIST
+    vkreplay.h
+    vkreplay_settings.h
+    vkreplay_vkdisplay.h
+    vkreplay_vkreplay.h
+    codegen/vkreplay_vk_func_ptrs.h
+    codegen/vkreplay_vk_objmapper.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/../vulkan/codegen_utils/vk_enum_string_helper.h
+    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_packet_id.h
+    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_vk_packets.h
+
+)
+
+include_directories(
+    codegen
+    ${SRC_DIR}/vktrace_replay
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/thirdparty
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CODEGEN_VKTRACE_DIR}
+    ${VKTRACE_VULKAN_INCLUDE_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/../vulkan/codegen_utils
+)
+# needed for vktraceviewer_vk library which is shared
+if (NOT MSVC)
+    add_compiler_flag("-fPIC")
+endif()
+
+add_definitions(-DAPI_LOWERCASE="${API_LOWERCASE}")
+
+add_library(${PROJECT_NAME} STATIC ${SRC_LIST} ${HDR_LIST})
+
+if(WIN32)
+    add_dependencies(${PROJECT_NAME} "${API_LOWERCASE}-${MAJOR}")
+else()
+    add_dependencies(${PROJECT_NAME} "${API_LOWERCASE}")
+endif()
+
+target_link_libraries(${PROJECT_NAME}
+    ${OS_REPLAYER_LIBS}
+    ${VKTRACE_VULKAN_LIB}
+    vktrace_common
+)
+
+build_options_finalize()
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.cpp
new file mode 100644
index 0000000..6c4d391
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.cpp
@@ -0,0 +1,199 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation, Inc.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ **************************************************************************/
+#include <inttypes.h>
+#include "vkreplay.h"
+#include "vkreplay_vkreplay.h"
+#include "vktrace_vk_packet_id.h"
+#include "vktrace_tracelog.h"
+
+static vkreplayer_settings s_defaultVkReplaySettings = { NULL, 1, -1, -1, NULL, NULL };
+
+vkReplay* g_pReplayer = NULL;
+VKTRACE_CRITICAL_SECTION g_handlerLock;
+PFN_vkDebugReportCallbackEXT g_fpDbgMsgCallback;
+vktrace_replay::VKTRACE_DBG_MSG_CALLBACK_FUNCTION g_fpVktraceCallback = NULL;
+
+static VKAPI_ATTR VkBool32 VKAPI_CALL vkErrorHandler(
+                                VkFlags             msgFlags,
+                                VkDebugReportObjectTypeEXT     objType,
+                                uint64_t            srcObjectHandle,
+                                size_t              location,
+                                int32_t             msgCode,
+                                const char*         pLayerPrefix,
+                                const char*         pMsg,
+                                void*               pUserData)
+{
+    VkBool32 bail = false;
+
+    vktrace_enter_critical_section(&g_handlerLock);
+    if ((msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) == VK_DEBUG_REPORT_ERROR_BIT_EXT)
+    {
+        vktrace_LogError("MsgFlags %d with object %#" PRIxLEAST64 ", location %u returned msgCode %d and msg %s",
+                     msgFlags, srcObjectHandle, location, msgCode, (char *) pMsg);
+        g_pReplayer->push_validation_msg(msgFlags, objType, srcObjectHandle, location, msgCode, pLayerPrefix, pMsg, pUserData);
+        if (g_fpVktraceCallback != NULL)
+        {
+            g_fpVktraceCallback(vktrace_replay::VKTRACE_DBG_MSG_ERROR, pMsg);
+        }
+        /* TODO: bailing out of the call chain due to this error should allow
+         * the app to continue in some fashion.
+         * Is that needed here?
+         */
+        bail = true;
+    }
+    else if ((msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) == VK_DEBUG_REPORT_WARNING_BIT_EXT ||
+             (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) == VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
+    {
+        if (g_fpVktraceCallback != NULL)
+        {
+            g_fpVktraceCallback(vktrace_replay::VKTRACE_DBG_MSG_WARNING, pMsg);
+        }
+    }
+    else
+    {
+        if (g_fpVktraceCallback != NULL)
+        {
+            g_fpVktraceCallback(vktrace_replay::VKTRACE_DBG_MSG_INFO, pMsg);
+        }
+    }
+    vktrace_leave_critical_section(&g_handlerLock);
+
+    return bail;
+}
+
+void VkReplaySetLogCallback(VKTRACE_REPORT_CALLBACK_FUNCTION pCallback)
+{
+}
+
+void VkReplaySetLogLevel(VktraceLogLevel level)
+{
+}
+
+void VkReplayRegisterDbgMsgCallback(vktrace_replay::VKTRACE_DBG_MSG_CALLBACK_FUNCTION pCallback)
+{
+    g_fpVktraceCallback = pCallback;
+}
+
+vktrace_SettingGroup* VKTRACER_CDECL VkReplayGetSettings()
+{
+    static BOOL bFirstTime = TRUE;
+    if (bFirstTime == TRUE)
+    {
+        vktrace_SettingGroup_reset_defaults(&g_vkReplaySettingGroup);
+        bFirstTime = FALSE;
+    }
+
+    return &g_vkReplaySettingGroup;
+}
+
+void VKTRACER_CDECL VkReplayUpdateFromSettings(vktrace_SettingGroup* pSettingGroups, unsigned int numSettingGroups)
+{
+    vktrace_SettingGroup_Apply_Overrides(&g_vkReplaySettingGroup, pSettingGroups, numSettingGroups);
+}
+
+int VKTRACER_CDECL VkReplayInitialize(vktrace_replay::ReplayDisplay* pDisplay, vkreplayer_settings *pReplaySettings)
+{
+    try
+    {
+        if (pReplaySettings == NULL)
+        {
+            g_pReplayer = new vkReplay(&s_defaultVkReplaySettings);
+        }
+        else
+        {
+            g_pReplayer = new vkReplay(pReplaySettings);
+        }
+    }
+    catch (int e)
+    {
+        vktrace_LogError("Failed to create vkReplay, probably out of memory. Error %d", e);
+        return -1;
+    }
+
+    vktrace_create_critical_section(&g_handlerLock);
+    g_fpDbgMsgCallback = vkErrorHandler;
+    int result = g_pReplayer->init(*pDisplay);
+    return result;
+}
+
+void VKTRACER_CDECL VkReplayDeinitialize()
+{
+    if (g_pReplayer != NULL)
+    {
+        delete g_pReplayer;
+        g_pReplayer = NULL;
+    }
+    vktrace_delete_critical_section(&g_handlerLock);
+}
+
+vktrace_trace_packet_header* VKTRACER_CDECL VkReplayInterpret(vktrace_trace_packet_header* pPacket)
+{
+    // Attempt to interpret the packet as a Vulkan packet
+    vktrace_trace_packet_header* pInterpretedHeader = interpret_trace_packet_vk(pPacket);
+    if (pInterpretedHeader == NULL)
+    {
+        vktrace_LogError("Unrecognized Vulkan packet_id: %u", pPacket->packet_id);
+    }
+
+    return pInterpretedHeader;
+}
+
+vktrace_replay::VKTRACE_REPLAY_RESULT VKTRACER_CDECL VkReplayReplay(vktrace_trace_packet_header* pPacket)
+{
+    vktrace_replay::VKTRACE_REPLAY_RESULT result = vktrace_replay::VKTRACE_REPLAY_ERROR;
+    if (g_pReplayer != NULL)
+    {
+        result = g_pReplayer->replay(pPacket);
+
+        if (result == vktrace_replay::VKTRACE_REPLAY_SUCCESS)
+            result = g_pReplayer->pop_validation_msgs();
+    }
+    return result;
+}
+
+int VKTRACER_CDECL VkReplayDump()
+{
+    if (g_pReplayer != NULL)
+    {
+        g_pReplayer->dump_validation_data();
+        return 0;
+    }
+    return -1;
+}
+
+int VKTRACER_CDECL VkReplayGetFrameNumber()
+{
+    if (g_pReplayer != NULL)
+    {
+        return g_pReplayer->get_frame_number();
+    }
+    return -1;
+}
+
+void VKTRACER_CDECL VkReplayResetFrameNumber()
+{
+    if (g_pReplayer != NULL)
+    {
+        g_pReplayer->reset_frame_number();
+    }
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.h b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.h
new file mode 100644
index 0000000..aa49311
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay.h
@@ -0,0 +1,41 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation, Inc.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+#pragma once
+#include "vkreplay_window.h"
+#include "vkreplay_factory.h"
+#include "vkreplay_settings.h"
+
+
+extern void VkReplaySetLogCallback(VKTRACE_REPORT_CALLBACK_FUNCTION pCallback);
+extern void VkReplaySetLogLevel(VktraceLogLevel level);
+extern void VkReplayRegisterDbgMsgCallback(vktrace_replay::VKTRACE_DBG_MSG_CALLBACK_FUNCTION pCallback);
+extern vktrace_SettingGroup* VKTRACER_CDECL VkReplayGetSettings();
+extern void VKTRACER_CDECL VkReplayUpdateFromSettings(vktrace_SettingGroup* pSettingGroups, unsigned int numSettingGroups);
+extern int VKTRACER_CDECL VkReplayInitialize(vktrace_replay::ReplayDisplay* pDisplay, vkreplayer_settings *pReplaySettings);
+extern void VKTRACER_CDECL VkReplayDeinitialize();
+extern vktrace_trace_packet_header* VKTRACER_CDECL VkReplayInterpret(vktrace_trace_packet_header* pPacket);
+extern vktrace_replay::VKTRACE_REPLAY_RESULT VKTRACER_CDECL VkReplayReplay(vktrace_trace_packet_header* pPacket);
+extern int VKTRACER_CDECL VkReplayDump();
+extern int VKTRACER_CDECL VkReplayGetFrameNumber();
+extern void VKTRACER_CDECL VkReplayResetFrameNumber();
+
+extern PFN_vkDebugReportCallbackEXT g_fpDbgMsgCallback;
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.cpp
new file mode 100644
index 0000000..b59b777
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.cpp
@@ -0,0 +1,58 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation, Inc.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ **************************************************************************/
+#include "vulkan/vk_layer.h"
+
+#include "vkreplay_settings.h"
+// declared as extern in header
+vkreplayer_settings g_vkReplaySettings;
+
+static vkreplayer_settings s_defaultVkReplaySettings = { NULL, 1, -1, -1, NULL, NULL };
+
+vktrace_SettingInfo g_vk_settings_info[] =
+{
+    { "o", "Open", VKTRACE_SETTING_STRING, { &g_vkReplaySettings.pTraceFilePath }, { &s_defaultVkReplaySettings.pTraceFilePath }, TRUE, "The trace file to open and replay." },
+    { "t", "TraceFile", VKTRACE_SETTING_STRING, { &g_vkReplaySettings.pTraceFilePath }, { &s_defaultVkReplaySettings.pTraceFilePath }, TRUE, "(Deprecated, use -o or --Open instead) The trace file to replay." },
+    { "l", "NumLoops", VKTRACE_SETTING_UINT, { &g_vkReplaySettings.numLoops }, { &s_defaultVkReplaySettings.numLoops }, TRUE, "The number of times to replay the trace file or loop range." },
+    { "lsf", "LoopStartFrame", VKTRACE_SETTING_INT, { &g_vkReplaySettings.loopStartFrame }, { &s_defaultVkReplaySettings.loopStartFrame }, TRUE, "The start frame number of the loop range." },
+    { "lef", "LoopEndFrame", VKTRACE_SETTING_INT, { &g_vkReplaySettings.loopEndFrame }, { &s_defaultVkReplaySettings.loopEndFrame }, TRUE, "The end frame number of the loop range." },
+    { "s", "Screenshot", VKTRACE_SETTING_STRING, { &g_vkReplaySettings.screenshotList }, { &s_defaultVkReplaySettings.screenshotList }, TRUE, "Comma separated list of frames to take a take snapshots of" },
+};
+
+vktrace_SettingGroup g_vkReplaySettingGroup =
+{
+    "vkreplay_vk",
+    sizeof(g_vk_settings_info) / sizeof(g_vk_settings_info[0]),
+    &g_vk_settings_info[0]
+};
+
+void apply_layerSettings_overrides()
+{
+#if 0
+    setLayerOptionEnum("DrawStateReportFlags", g_vkReplaySettings.drawStateReportFlags);
+    setLayerOptionEnum("DrawStateDebugAction", g_vkReplaySettings.drawStateDebugAction);
+    setLayerOptionEnum("MemTrackerReportFlags", g_vkReplaySettings.memTrackerReportFlags);
+    setLayerOptionEnum("MemTrackerDebugAction", g_vkReplaySettings.memTrackerDebugAction);
+    setLayerOptionEnum("ObjectTrackerReportFlags", g_vkReplaySettings.objectTrackerReportFlags);
+    setLayerOptionEnum("ObjectTrackerDebugAction", g_vkReplaySettings.objectTrackerDebugAction);
+#endif
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.h b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.h
new file mode 100644
index 0000000..003d6c0
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_settings.h
@@ -0,0 +1,39 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation, Inc.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ **************************************************************************/
+#ifndef VKREPLAY__VK_SETTINGS_H
+#define VKREPLAY__VK_SETTINGS_H
+
+extern "C"
+{
+#include "vktrace_settings.h"
+#include "vkreplay_main.h"
+}
+
+#include <vulkan/vulkan.h>
+
+extern vkreplayer_settings g_vkReplaySettings;
+extern vktrace_SettingGroup g_vkReplaySettingGroup;
+
+void apply_layerSettings_overrides();
+
+#endif // VKREPLAY__VK_SETTINGS_H
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.cpp
new file mode 100644
index 0000000..ac139de
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.cpp
@@ -0,0 +1,371 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Mark Lobodzinski <mark@lunarg.com>
+ */
+
+#include "vkreplay_vkreplay.h"
+#include "vk_icd.h"
+#define APP_NAME "vkreplay_vk"
+#define IDI_ICON 101
+
+vkDisplay::vkDisplay()
+    : m_initedVK(false),
+    m_windowWidth(0),
+    m_windowHeight(0),
+    m_frameNumber(0)
+{
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+    memset(&m_surface, 0, sizeof(VkIcdSurfaceAndroid));
+    m_window = 0;
+#else
+    memset(&m_surface, 0, sizeof(VkIcdSurfaceXcb));
+    m_pXcbConnection = NULL;
+    m_pXcbScreen = NULL;
+    m_XcbWindow = 0;
+#endif
+#elif defined(WIN32)
+    memset(&m_surface, 0, sizeof(VkIcdSurfaceWin32));
+    m_windowHandle = NULL;
+    m_connection = NULL;
+#endif
+}
+
+vkDisplay::~vkDisplay()
+{
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    if (m_XcbWindow != 0)
+    {
+        xcb_destroy_window(m_pXcbConnection, m_XcbWindow);
+    }
+    if (m_pXcbConnection != NULL)
+    {
+        xcb_disconnect(m_pXcbConnection);
+    }
+#endif
+}
+
+VkResult vkDisplay::init_vk(unsigned int gpu_idx)
+{
+#if 0
+    VkApplicationInfo appInfo = {};
+    appInfo.pApplicationName = APP_NAME;
+    appInfo.pEngineName = "";
+    appInfo.apiVersion = VK_API_VERSION;
+    VkResult res = vkInitAndEnumerateGpus(&appInfo, NULL, VK_MAX_PHYSICAL_GPUS, &m_gpuCount, m_gpus);
+    if ( res == VK_SUCCESS ) {
+        // retrieve the GPU information for all GPUs
+        for( uint32_t gpu = 0; gpu < m_gpuCount; gpu++)
+        {
+            size_t gpuInfoSize = sizeof(m_gpuProps[0]);
+
+            // get the GPU physical properties:
+            res = vkGetGpuInfo( m_gpus[gpu], VK_INFO_TYPE_PHYSICAL_GPU_PROPERTIES, &gpuInfoSize, &m_gpuProps[gpu]);
+            if (res != VK_SUCCESS)
+                vktrace_LogWarning("Failed to retrieve properties for gpu[%d] result %d", gpu, res);
+        }
+        res = VK_SUCCESS;
+    } else if ((gpu_idx + 1) > m_gpuCount) {
+        vktrace_LogError("vkInitAndEnumerate number of gpus does not include requested index: num %d, requested %d", m_gpuCount, gpu_idx);
+        return -1;
+    } else {
+        vktrace_LogError("vkInitAndEnumerate failed");
+        return res;
+    }
+    // TODO add multi-gpu support always use gpu[gpu_idx] for now
+    // get all extensions supported by this device gpu[gpu_idx]
+    // first check if extensions are available and save a list of them
+    bool foundWSIExt = false;
+    for( int ext = 0; ext < sizeof( extensions ) / sizeof( extensions[0] ); ext++)
+    {
+        res = vkGetExtensionSupport( m_gpus[gpu_idx], extensions[ext] );
+        if (res == VK_SUCCESS) {
+            m_extensions.push_back((char *) extensions[ext]);
+            if (!strcmp(extensions[ext], "VK_WSI_WINDOWS"))
+                foundWSIExt = true;
+        }
+    }
+    if (!foundWSIExt) {
+        vktrace_LogError("VK_WSI_WINDOWS extension not supported by gpu[%d]", gpu_idx);
+        return VK_ERROR_INCOMPATIBLE_DEVICE;
+    }
+    // TODO generalize this: use one universal queue for now
+    VkDeviceQueueCreateInfo dqci = {};
+    dqci.queueCount = 1;
+    dqci.queueType = VK_QUEUE_UNIVERSAL;
+    std::vector<float> queue_priorities (dqci.queueCount, 0.0);
+    dqci.pQueuePriorities = queue_priorities.data();
+    // create the device enabling validation level 4
+    const char * const * extensionNames = &m_extensions[0];
+    VkDeviceCreateInfo info = {};
+    info.queueCreateInfoCount = 1;
+    info.pQueueCreateInfos = &dqci;
+    info.enabledExtensionCount = static_cast <uint32_t> (m_extensions.size());
+    info.ppEnabledExtensionNames = extensionNames;
+    info.flags = VK_DEVICE_CREATE_VALIDATION;
+    info.maxValidationLevel = VK_VALIDATION_LEVEL_4;
+    bool32_t vkTrue = VK_TRUE;
+    res = vkDbgSetGlobalOption( VK_DBG_OPTION_BREAK_ON_ERROR, sizeof( vkTrue ), &vkTrue );
+    if (res != VK_SUCCESS)
+        vktrace_LogWarning("Could not set debug option break on error");
+    res = vkCreateDevice( m_gpus[0], &info, &m_dev[gpu_idx]);
+    return res;
+#else
+    return VK_ERROR_INITIALIZATION_FAILED;
+#endif
+}
+
+int vkDisplay::init(const unsigned int gpu_idx)
+{
+    //m_gpuIdx = gpu_idx;
+#if 0
+    VkResult result = init_vk(gpu_idx);
+    if (result != VK_SUCCESS) {
+        vktrace_LogError("could not init vulkan library");
+        return -1;
+    } else {
+        m_initedVK = true;
+    }
+#endif
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    const xcb_setup_t *setup;
+    xcb_screen_iterator_t iter;
+    int scr;
+    m_pXcbConnection = xcb_connect(NULL, &scr);
+    setup = xcb_get_setup(m_pXcbConnection);
+    iter = xcb_setup_roots_iterator(setup);
+    while (scr-- > 0)
+        xcb_screen_next(&iter);
+    m_pXcbScreen = iter.data;
+#endif
+    set_pause_status(false);
+    set_quit_status(false);
+    return 0;
+}
+
+#if defined(WIN32)
+LRESULT WINAPI WindowProcVk( HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
+{
+    switch(msg)
+    {
+        case WM_CLOSE:
+            DestroyWindow( window);
+            // fall-thru
+        case WM_DESTROY:
+            PostQuitMessage(0) ;
+            return 0L ;
+        default:
+            return DefWindowProc( window, msg, wp, lp ) ;
+    }
+}
+#endif
+
+int vkDisplay::set_window(vktrace_window_handle hWindow, unsigned int width, unsigned int height)
+{
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+    m_window = hWindow;
+    m_surface.window = hWindow;
+#else
+    m_XcbWindow = hWindow;
+#endif
+#elif defined(WIN32)
+    m_windowHandle = hWindow;
+#endif
+    m_windowWidth = width;
+    m_windowHeight = height;
+    return 0;
+}
+
+int vkDisplay::create_window(const unsigned int width, const unsigned int height)
+{
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+    return 0;
+#else
+
+    uint32_t value_mask, value_list[32];
+    m_XcbWindow = xcb_generate_id(m_pXcbConnection);
+
+    value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+    value_list[0] = m_pXcbScreen->black_pixel;
+    value_list[1] = XCB_EVENT_MASK_KEY_RELEASE |
+                    XCB_EVENT_MASK_EXPOSURE;
+
+    xcb_create_window(m_pXcbConnection,
+            XCB_COPY_FROM_PARENT,
+            m_XcbWindow, m_pXcbScreen->root,
+            0, 0, width, height, 0,
+            XCB_WINDOW_CLASS_INPUT_OUTPUT,
+            m_pXcbScreen->root_visual,
+            value_mask, value_list);
+
+    // Magic code that will send notification when window is destroyed
+    xcb_intern_atom_cookie_t cookie =
+        xcb_intern_atom(m_pXcbConnection, 1, 12, "WM_PROTOCOLS");
+    xcb_intern_atom_reply_t *reply =
+        xcb_intern_atom_reply(m_pXcbConnection, cookie, 0);
+
+    xcb_intern_atom_cookie_t cookie2 =
+        xcb_intern_atom(m_pXcbConnection, 0, 16, "WM_DELETE_WINDOW");
+    this->atom_wm_delete_window =
+        xcb_intern_atom_reply(m_pXcbConnection, cookie2, 0);
+
+    xcb_change_property(m_pXcbConnection, XCB_PROP_MODE_REPLACE, m_XcbWindow,
+                        (*reply).atom, 4, 32, 1,
+                        &(*this->atom_wm_delete_window).atom);
+    free(reply);
+
+    xcb_map_window(m_pXcbConnection, m_XcbWindow);
+    xcb_flush(m_pXcbConnection);
+    // TODO : Not sure of best place to put this, but I have all the info I need here so just setting it all here for now
+    //m_XcbPlatformHandle.connection = m_pXcbConnection;
+    //m_XcbPlatformHandle.root = m_pXcbScreen->root;
+    m_surface.base.platform = VK_ICD_WSI_PLATFORM_XCB;
+    m_surface.connection = m_pXcbConnection;
+    m_surface.window = m_XcbWindow;
+    return 0;
+#endif
+#elif defined(WIN32)
+    // Register Window class
+    WNDCLASSEX wcex = {};
+	m_connection = GetModuleHandle(0);
+    wcex.cbSize = sizeof( WNDCLASSEX);
+    wcex.style = CS_HREDRAW | CS_VREDRAW;
+    wcex.lpfnWndProc = WindowProcVk;
+    wcex.cbClsExtra = 0;
+    wcex.cbWndExtra = 0;
+    wcex.hInstance = m_connection;
+    wcex.hIcon = LoadIcon(wcex.hInstance, MAKEINTRESOURCE( IDI_ICON));
+    wcex.hCursor = LoadCursor( NULL, IDC_ARROW);
+    wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1);
+    wcex.lpszMenuName = NULL;
+    wcex.lpszClassName = APP_NAME;
+    wcex.hIconSm = LoadIcon( wcex.hInstance, MAKEINTRESOURCE( IDI_ICON));
+    if( !RegisterClassEx( &wcex))
+    {
+        vktrace_LogError("Failed to register windows class");
+        return -1;
+    }
+
+    // create the window
+    RECT wr = {0,0,width,height};
+    AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
+    m_windowHandle = CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
+                                  0, 0, wr.right-wr.left, wr.bottom-wr.top,
+                                  NULL, NULL, wcex.hInstance, NULL);
+
+    if (m_windowHandle)
+    {
+        ShowWindow( m_windowHandle, SW_SHOWDEFAULT);
+        m_windowWidth = width;
+        m_windowHeight = height;
+    } else {
+        vktrace_LogError("Failed to create window");
+        return -1;
+    }
+    // TODO : Not sure of best place to put this, but I have all the info I need here so just setting it all here for now
+    m_surface.base.platform = VK_ICD_WSI_PLATFORM_WIN32;
+    m_surface.hinstance = wcex.hInstance;
+    m_surface.hwnd = m_windowHandle;
+    return 0;
+#endif
+}
+
+void vkDisplay::resize_window(const unsigned int width, const unsigned int height)
+{
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+    m_windowWidth = width;
+    m_windowHeight = height;
+#else
+    if (width != m_windowWidth || height != m_windowHeight)
+    {
+        uint32_t values[2];
+        values[0] = width;
+        values[1] = height;
+        xcb_configure_window(m_pXcbConnection, m_XcbWindow, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
+        xcb_flush(m_pXcbConnection);
+        m_windowWidth = width;
+        m_windowHeight = height;
+    }
+#endif
+#elif defined(WIN32)
+    if (width != m_windowWidth || height != m_windowHeight)
+    {
+        RECT wr = {0, 0, width, height};
+        AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
+        SetWindowPos(get_window_handle(), HWND_TOP, 0, 0, wr.right-wr.left, wr.bottom-wr.top, SWP_NOMOVE);
+        m_windowWidth = width;
+        m_windowHeight = height;
+    }
+#endif
+}
+
+void vkDisplay::process_event()
+{
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+// TODO
+#else
+    xcb_connection_t *xcb_conn = this->get_connection_handle();
+    xcb_generic_event_t *event = xcb_poll_for_event(xcb_conn);
+    xcb_flush(xcb_conn);
+    uint8_t event_code = 0;
+
+    while (event) {
+        event_code = event->response_type & 0x7f;
+        switch (event_code) {
+        case XCB_EXPOSE:
+            // TODO: Resize window
+            break;
+        case XCB_CLIENT_MESSAGE:
+            if ((*(xcb_client_message_event_t *)event).data.data32[0] ==
+                (*this->atom_wm_delete_window).atom) {
+                this->set_quit_status(true);
+            }
+            break;
+        case XCB_KEY_RELEASE: {
+            const xcb_key_release_event_t *key =
+                (const xcb_key_release_event_t *)event;
+
+            switch (key->detail) {
+            case 0x9: // Escape
+                this->set_quit_status(true);
+                break;
+            case 0x41:
+                this->set_pause_status(!(this->get_pause_status()));
+                break;
+            }
+        } break;
+        case XCB_CONFIGURE_NOTIFY: {
+            // TODO resize here too
+        } break;
+        }
+        free(event);
+        event = xcb_poll_for_event(xcb_conn);
+    }
+#endif
+#elif defined(WIN32)
+// TODO: handle win events
+#endif
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.h b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.h
new file mode 100644
index 0000000..0c58e22
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkdisplay.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Mark Lobodzinski <mark@lunarg.com>
+ */
+
+#pragma once
+
+#include "vkreplay_vkreplay.h"
+#include "vk_icd.h"
+
+class vkDisplay: public vktrace_replay::ReplayDisplayImp {
+friend class vkReplay;
+public:
+    vkDisplay();
+    ~vkDisplay();
+    int init(const unsigned int gpu_idx);
+    int set_window(vktrace_window_handle hWindow, unsigned int width, unsigned int height);
+    int create_window(const unsigned int width, const unsigned int height);
+    void resize_window(const unsigned int width, const unsigned int height);
+    void process_event();
+    bool get_pause_status() { return m_pause; }
+    void set_pause_status(bool pause) { m_pause = pause; }
+    bool get_quit_status() { return m_quit; }
+    void set_quit_status(bool quit) { m_quit = quit; }
+    VkSurfaceKHR get_surface() { return (VkSurfaceKHR) &m_surface; };
+    // VK_DEVICE get_device() { return m_dev[m_gpuIdx];}
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+    ANativeWindow* get_window_handle() { return m_window; }
+#else
+    xcb_window_t get_window_handle() { return m_XcbWindow; }
+    xcb_connection_t* get_connection_handle() { return m_pXcbConnection; }
+    xcb_screen_t* get_screen_handle() { return m_pXcbScreen; }
+#endif
+#elif defined(WIN32)
+    HWND get_window_handle() { return m_windowHandle; }
+    HINSTANCE get_connection_handle() { return m_connection; }
+#endif
+private:
+    VkResult init_vk(const unsigned int gpu_idx);
+    bool m_initedVK;
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+    VkIcdSurfaceAndroid m_surface;
+    ANativeWindow* m_window;
+#else
+    VkIcdSurfaceXcb m_surface;
+    xcb_connection_t *m_pXcbConnection;
+    xcb_screen_t *m_pXcbScreen;
+    xcb_window_t m_XcbWindow;
+    xcb_intern_atom_reply_t *atom_wm_delete_window;
+    //VkPlatformHandleXcbKHR m_XcbPlatformHandle;
+#endif
+#elif defined(WIN32)
+    VkIcdSurfaceWin32 m_surface;
+    HWND m_windowHandle;
+    HINSTANCE m_connection;
+#endif
+    unsigned int m_windowWidth;
+    unsigned int m_windowHeight;
+    unsigned int m_frameNumber;
+    std::vector<VkExtent2D> imageExtents;
+    std::vector<VkImage> imageHandles;
+    std::vector<VkDeviceMemory> imageMemory;
+    std::vector<VkDevice> imageDevice;
+#if 0
+    VK_DEVICE m_dev[VK_MAX_PHYSICAL_GPUS];
+    uint32_t m_gpuCount;
+    unsigned int m_gpuIdx;
+    VK_PHYSICAL_GPU m_gpus[VK_MAX_PHYSICAL_GPUS];
+    VK_PHYSICAL_GPU_PROPERTIES m_gpuProps[VK_MAX_PHYSICAL_GPUS];
+#endif
+    std::vector<char *>m_extensions;
+    bool m_pause = false;
+    bool m_quit = false;
+};
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.cpp
new file mode 100644
index 0000000..49ba223
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.cpp
@@ -0,0 +1,3452 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Mark Lobodzinski <mark@lunarg.com>
+ * Author: Tobin Ehlis <tobin@lunarg.com>
+ */
+
+#include "vulkan/vulkan.h"
+#include "vkreplay_vkreplay.h"
+#include "vkreplay.h"
+#include "vkreplay_settings.h"
+
+#include <algorithm>
+
+#include "vktrace_vk_vk_packets.h"
+#include "vk_enum_string_helper.h"
+
+using namespace std;
+#include "vktrace_pageguard_memorycopy.h"
+
+vkreplayer_settings *g_pReplaySettings;
+
+vkReplay::vkReplay(vkreplayer_settings *pReplaySettings)
+{
+    g_pReplaySettings = pReplaySettings;
+    m_display = new vkDisplay();
+    m_pDSDump = NULL;
+    m_pCBDump = NULL;
+//    m_pVktraceSnapshotPrint = NULL;
+    m_objMapper.m_adjustForGPU = false;
+
+    m_frameNumber = 0;
+}
+
+vkReplay::~vkReplay()
+{
+    delete m_display;
+    vktrace_platform_close_library(m_vkFuncs.m_libHandle);
+}
+
+int vkReplay::init(vktrace_replay::ReplayDisplay & disp)
+{
+    int err;
+#if defined(PLATFORM_LINUX)
+    void * handle = dlopen("lib" API_LOWERCASE ".so", RTLD_LAZY);
+#else
+    HMODULE handle = LoadLibrary(API_LOWERCASE "-1.dll");
+#endif
+
+    if (handle == NULL) {
+        vktrace_LogError("Failed to open vulkan library.");
+        return -1;
+    }
+    m_vkFuncs.init_funcs(handle);
+    disp.set_implementation(m_display);
+    if ((err = m_display->init(disp.get_gpu())) != 0) {
+        vktrace_LogError("Failed to init vulkan display.");
+        return err;
+    }
+    if (disp.get_window_handle() == 0)
+    {
+        if ((err = m_display->create_window(disp.get_width(), disp.get_height())) != 0) {
+            vktrace_LogError("Failed to create Window");
+            return err;
+        }
+    }
+    else
+    {
+        if ((err = m_display->set_window(disp.get_window_handle(), disp.get_width(), disp.get_height())) != 0)
+        {
+            vktrace_LogError("Failed to set Window");
+            return err;
+        }
+    }
+    return 0;
+}
+
+vktrace_replay::VKTRACE_REPLAY_RESULT vkReplay::handle_replay_errors(const char* entrypointName, const VkResult resCall, const VkResult resTrace, const vktrace_replay::VKTRACE_REPLAY_RESULT resIn)
+{
+    vktrace_replay::VKTRACE_REPLAY_RESULT res = resIn;
+    if (resCall == VK_ERROR_DEVICE_LOST)
+    {
+        vktrace_LogError("API call %s returned VK_ERROR_DEVICE_LOST. vkreplay cannot continue, exiting.", entrypointName);
+        exit(1);
+    }
+    if (resCall != resTrace) {
+        vktrace_LogError("Return value %s from API call (%s) does not match return value from trace file %s.",
+                string_VkResult((VkResult)resCall), entrypointName, string_VkResult((VkResult)resTrace));
+        res = vktrace_replay::VKTRACE_REPLAY_BAD_RETURN;
+    }
+    if (resCall != VK_SUCCESS  && resCall != VK_NOT_READY) {
+        vktrace_LogWarning("API call (%s) returned failed result %s", entrypointName, string_VkResult(resCall));
+    }
+    return res;
+}
+void vkReplay::push_validation_msg(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObjectHandle, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, const void* pUserData)
+{
+    struct ValidationMsg msgObj;
+    msgObj.msgFlags = msgFlags;
+    msgObj.objType = objType;
+    msgObj.srcObjectHandle = srcObjectHandle;
+    msgObj.location = location;
+    strncpy(msgObj.layerPrefix, pLayerPrefix, 256);
+    msgObj.layerPrefix[255] = '\0';
+    msgObj.msgCode = msgCode;
+    strncpy(msgObj.msg, pMsg, 256);
+    msgObj.msg[255] = '\0';
+    msgObj.pUserData = (void *) pUserData;
+    m_validationMsgs.push_back(msgObj);
+}
+
+vktrace_replay::VKTRACE_REPLAY_RESULT vkReplay::pop_validation_msgs()
+{
+    if (m_validationMsgs.size() == 0)
+        return vktrace_replay::VKTRACE_REPLAY_SUCCESS;
+    m_validationMsgs.clear();
+    return vktrace_replay::VKTRACE_REPLAY_VALIDATION_ERROR;
+}
+
+int vkReplay::dump_validation_data()
+{
+    if  (m_pDSDump && m_pCBDump)
+    {
+        m_pDSDump((char *) "pipeline_dump.dot");
+        m_pCBDump((char *) "cb_dump.dot");
+    }
+//    if (m_pVktraceSnapshotPrint != NULL)
+//    {
+//        m_pVktraceSnapshotPrint();
+//    }
+   return 0;
+}
+
+VkResult vkReplay::manually_replay_vkCreateInstance(packet_vkCreateInstance* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkInstanceCreateInfo *pCreateInfo;
+    char **ppEnabledLayerNames = NULL, **saved_ppLayers;
+    if (!m_display->m_initedVK)
+    {
+        VkInstance inst;
+
+        const char strScreenShot[] = "VK_LAYER_LUNARG_screenshot";
+        pCreateInfo = (VkInstanceCreateInfo *) pPacket->pCreateInfo;
+        if (g_pReplaySettings->screenshotList != NULL) {
+            // enable screenshot layer if it is available and not already in list
+            bool found_ss = false;
+            for (uint32_t i = 0; i < pCreateInfo->enabledLayerCount; i++) {
+                if (!strcmp(pCreateInfo->ppEnabledLayerNames[i], strScreenShot)) {
+                    found_ss = true;
+                    break;
+                }
+            }
+            if (!found_ss) {
+                uint32_t count;
+
+                // query to find if ScreenShot layer is available
+                m_vkFuncs.real_vkEnumerateInstanceLayerProperties(&count, NULL);
+                VkLayerProperties *props = (VkLayerProperties *) vktrace_malloc(count * sizeof (VkLayerProperties));
+                if (props && count > 0)
+                    m_vkFuncs.real_vkEnumerateInstanceLayerProperties(&count, props);
+                for (uint32_t i = 0; i < count; i++) {
+                    if (!strcmp(props[i].layerName, strScreenShot)) {
+                        found_ss = true;
+                        break;
+                    }
+                }
+                if (found_ss) {
+                    // screenshot layer is available so enable it
+                    ppEnabledLayerNames = (char **) vktrace_malloc((pCreateInfo->enabledLayerCount + 1) * sizeof (char *));
+                    for (uint32_t i = 0; i < pCreateInfo->enabledLayerCount && ppEnabledLayerNames; i++) {
+                        ppEnabledLayerNames[i] = (char *) pCreateInfo->ppEnabledLayerNames[i];
+                    }
+                    ppEnabledLayerNames[pCreateInfo->enabledLayerCount] = (char *) vktrace_malloc(strlen(strScreenShot) + 1);
+                    strcpy(ppEnabledLayerNames[pCreateInfo->enabledLayerCount++], strScreenShot);
+                    saved_ppLayers = (char **) pCreateInfo->ppEnabledLayerNames;
+                    pCreateInfo->ppEnabledLayerNames = ppEnabledLayerNames;
+                }
+                vktrace_free(props);
+            }
+        }
+
+        char **saved_ppExtensions = (char **)pCreateInfo->ppEnabledExtensionNames;
+        int savedExtensionCount = pCreateInfo->enabledExtensionCount;
+        vector<const char *> extension_names;
+        vector<string> outlist;
+
+#if defined(PLATFORM_LINUX)
+#if !defined(ANDROID)
+        extension_names.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
+        outlist.push_back("VK_KHR_win32_surface");
+#else
+        extension_names.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
+        outlist.push_back("VK_KHR_win32_surface");
+        outlist.push_back("VK_KHR_xlib_surface");
+        outlist.push_back("VK_KHR_xcb_surface");
+        outlist.push_back("VK_KHR_wayland_surface");
+        outlist.push_back("VK_KHR_mir_surface");
+#endif //ANDROID
+#else
+        extension_names.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
+        outlist.push_back("VK_KHR_xlib_surface");
+        outlist.push_back("VK_KHR_xcb_surface");
+        outlist.push_back("VK_KHR_wayland_surface");
+        outlist.push_back("VK_KHR_mir_surface");
+#endif
+
+        for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
+            if ( find(outlist.begin(), outlist.end(), pCreateInfo->ppEnabledExtensionNames[i]) == outlist.end() ) {
+                extension_names.push_back(pCreateInfo->ppEnabledExtensionNames[i]);
+            }
+        }
+        pCreateInfo->ppEnabledExtensionNames = extension_names.data();
+        pCreateInfo->enabledExtensionCount = (uint32_t)extension_names.size();
+
+        replayResult = m_vkFuncs.real_vkCreateInstance(pPacket->pCreateInfo, NULL, &inst);
+
+        pCreateInfo->ppEnabledExtensionNames = saved_ppExtensions;
+        pCreateInfo->enabledExtensionCount = savedExtensionCount;
+
+        if (ppEnabledLayerNames) {
+            // restore the packets CreateInfo struct
+            vktrace_free(ppEnabledLayerNames[pCreateInfo->enabledLayerCount - 1]);
+            vktrace_free(ppEnabledLayerNames);
+            pCreateInfo->ppEnabledLayerNames = saved_ppLayers;
+        }
+
+        if (replayResult == VK_SUCCESS) {
+            m_objMapper.add_to_instances_map(*(pPacket->pInstance), inst);
+        }
+    }
+    return replayResult;
+}
+
+bool vkReplay::getQueueFamilyIdx(VkPhysicalDevice tracePhysicalDevice,
+                                 VkPhysicalDevice replayPhysicalDevice,
+                                 uint32_t traceIdx,
+                                 uint32_t* pReplayIdx)
+{
+    if  (traceIdx == VK_QUEUE_FAMILY_IGNORED)
+    {
+        *pReplayIdx = VK_QUEUE_FAMILY_IGNORED;
+        return true;
+    }
+
+    if (traceQueueFamilyProperties.find(tracePhysicalDevice) == traceQueueFamilyProperties.end() ||
+        replayQueueFamilyProperties.find(replayPhysicalDevice) == replayQueueFamilyProperties.end())
+    {
+        goto fail;
+    }
+
+    if (min(traceQueueFamilyProperties[tracePhysicalDevice].count, replayQueueFamilyProperties[replayPhysicalDevice].count) == 0)
+    {
+        goto fail;
+    }
+
+    if (replayQueueFamilyProperties[replayPhysicalDevice].count == 1)
+    {
+        *pReplayIdx = 0;
+        return true;
+    }
+
+    for (uint32_t i = 0; i < min(traceQueueFamilyProperties[tracePhysicalDevice].count, replayQueueFamilyProperties[replayPhysicalDevice].count); i++)
+    {
+        if (traceQueueFamilyProperties[tracePhysicalDevice].queueFamilyProperties[traceIdx].queueFlags == replayQueueFamilyProperties[replayPhysicalDevice].queueFamilyProperties[i].queueFlags)
+        {
+            *pReplayIdx = i;
+            return true;
+        }
+    }
+
+    // Didn't find an exact match, search for a superset
+    for (uint32_t i = 0; i < min(traceQueueFamilyProperties[tracePhysicalDevice].count, replayQueueFamilyProperties[replayPhysicalDevice].count); i++)
+    {
+        if (traceQueueFamilyProperties[tracePhysicalDevice].queueFamilyProperties[traceIdx].queueFlags ==
+            (traceQueueFamilyProperties[tracePhysicalDevice].queueFamilyProperties[traceIdx].queueFlags & replayQueueFamilyProperties[replayPhysicalDevice].queueFamilyProperties[i].queueFlags))
+        {
+            *pReplayIdx = i;
+            return true;
+        }
+    }
+
+fail:
+    vktrace_LogError("Cannot determine queue family index - has vkGetPhysicalDeviceQueueFamilyProperties been called?");
+    // Didn't find a match
+    return false;
+}
+
+bool vkReplay::getQueueFamilyIdx(VkDevice traceDevice,
+                                 VkDevice replayDevice,
+                                 uint32_t traceIdx,
+                                 uint32_t* pReplayIdx)
+{
+    VkPhysicalDevice tracePhysicalDevice;
+    VkPhysicalDevice replayPhysicalDevice;
+
+    if (tracePhysicalDevices.find(traceDevice) == tracePhysicalDevices.end() ||
+        replayPhysicalDevices.find(replayDevice) == replayPhysicalDevices.end())
+    {
+        vktrace_LogWarning("Cannot determine queue family index - has vkGetPhysicalDeviceQueueFamilyProperties been called?");
+        return false;
+    }
+
+    tracePhysicalDevice = tracePhysicalDevices[traceDevice];
+    replayPhysicalDevice = replayPhysicalDevices[replayDevice];
+
+    return getQueueFamilyIdx(tracePhysicalDevice,
+                             replayPhysicalDevice,
+                             traceIdx,
+                             pReplayIdx);
+}
+
+VkResult vkReplay::manually_replay_vkCreateDevice(packet_vkCreateDevice* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    if (!m_display->m_initedVK)
+    {
+        VkDevice device;
+        VkPhysicalDevice remappedPhysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+        VkDeviceCreateInfo *pCreateInfo;
+        char **ppEnabledLayerNames = NULL, **saved_ppLayers;
+        if (remappedPhysicalDevice == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCreateDevice() due to invalid remapped VkPhysicalDevice.");
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+        const char strScreenShot[] = "VK_LAYER_LUNARG_screenshot";
+        //char *strScreenShotEnv = vktrace_get_global_var("_VK_SCREENSHOT");
+
+        pCreateInfo = (VkDeviceCreateInfo *) pPacket->pCreateInfo;
+        if (g_pReplaySettings->screenshotList != NULL) {
+            // enable screenshot layer if it is available and not already in list
+            bool found_ss = false;
+            for (uint32_t i = 0; i < pCreateInfo->enabledLayerCount; i++) {
+                if (!strcmp(pCreateInfo->ppEnabledLayerNames[i], strScreenShot)) {
+                    found_ss = true;
+                    break;
+                }
+            }
+            if (!found_ss) {
+                uint32_t count;
+
+                // query to find if ScreenShot layer is available
+                m_vkFuncs.real_vkEnumerateDeviceLayerProperties(remappedPhysicalDevice, &count, NULL);
+                VkLayerProperties *props = (VkLayerProperties *) vktrace_malloc(count * sizeof (VkLayerProperties));
+                if (props && count > 0)
+                    m_vkFuncs.real_vkEnumerateDeviceLayerProperties(remappedPhysicalDevice, &count, props);
+                for (uint32_t i = 0; i < count; i++) {
+                    if (!strcmp(props[i].layerName, strScreenShot)) {
+                        found_ss = true;
+                        break;
+                    }
+                }
+                if (found_ss) {
+                    // screenshot layer is available so enable it
+                    ppEnabledLayerNames = (char **) vktrace_malloc((pCreateInfo->enabledLayerCount+1) * sizeof(char *));
+                    for (uint32_t i = 0; i < pCreateInfo->enabledLayerCount && ppEnabledLayerNames; i++)
+                    {
+                        ppEnabledLayerNames[i] = (char *) pCreateInfo->ppEnabledLayerNames[i];
+                    }
+                    ppEnabledLayerNames[pCreateInfo->enabledLayerCount] = (char *) vktrace_malloc(strlen(strScreenShot) + 1);
+                    strcpy(ppEnabledLayerNames[pCreateInfo->enabledLayerCount++], strScreenShot);
+                    saved_ppLayers = (char **) pCreateInfo->ppEnabledLayerNames;
+                    pCreateInfo->ppEnabledLayerNames = ppEnabledLayerNames;
+                }
+                vktrace_free(props);
+            }
+        }
+
+        // Convert all instances of queueFamilyIndex in structure
+        for (uint32_t i = 0; i < pPacket->pCreateInfo->queueCreateInfoCount; i++) {
+            uint32_t replayIdx;
+            if (pPacket->pCreateInfo->pQueueCreateInfos &&
+                getQueueFamilyIdx(pPacket->physicalDevice,
+                                  remappedPhysicalDevice,
+                                  pPacket->pCreateInfo->pQueueCreateInfos->queueFamilyIndex,
+                                  &replayIdx))
+            {
+                *((uint32_t *)&pPacket->pCreateInfo->pQueueCreateInfos->queueFamilyIndex) = replayIdx;
+            }
+            else
+            {
+                vktrace_LogError("vkCreateDevice failed, bad queueFamilyIndex");
+                return VK_ERROR_VALIDATION_FAILED_EXT;
+            }
+        }
+
+        replayResult = m_vkFuncs.real_vkCreateDevice(remappedPhysicalDevice, pPacket->pCreateInfo, NULL, &device);
+        if (ppEnabledLayerNames)
+        {
+            // restore the packets CreateInfo struct
+            vktrace_free(ppEnabledLayerNames[pCreateInfo->enabledLayerCount-1]);
+            vktrace_free(ppEnabledLayerNames);
+            pCreateInfo->ppEnabledLayerNames = saved_ppLayers;
+        }
+        if (replayResult == VK_SUCCESS)
+        {
+            m_objMapper.add_to_devices_map(*(pPacket->pDevice), device);
+            tracePhysicalDevices[*(pPacket->pDevice)] = pPacket->physicalDevice;
+            replayPhysicalDevices[device] = remappedPhysicalDevice;
+        }
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateBuffer(packet_vkCreateBuffer* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    bufferObj local_bufferObj;
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    // Convert queueFamilyIndices
+    if (pPacket->pCreateInfo)
+    {
+        for (uint32_t i = 0; i < pPacket->pCreateInfo->queueFamilyIndexCount; i++)
+        {
+            uint32_t replayIdx;
+            if (pPacket->pCreateInfo->pQueueFamilyIndices &&
+                getQueueFamilyIdx(pPacket->device,
+                                  remappedDevice,
+                                  pPacket->pCreateInfo->pQueueFamilyIndices[i],
+                                  &replayIdx))
+            {
+                *((uint32_t*)&pPacket->pCreateInfo->pQueueFamilyIndices[i]) = replayIdx;
+            } else {
+                vktrace_LogError("vkCreateBuffer failed, bad queueFamilyIndex");
+               return VK_ERROR_VALIDATION_FAILED_EXT;
+            }
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkCreateBuffer(remappedDevice, pPacket->pCreateInfo, NULL, &local_bufferObj.replayBuffer);
+    if (replayResult == VK_SUCCESS)
+    {
+        traceBufferToDevice[*pPacket->pBuffer] = pPacket->device;
+        replayBufferToDevice[local_bufferObj.replayBuffer] = remappedDevice;
+        m_objMapper.add_to_buffers_map(*(pPacket->pBuffer), local_bufferObj);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateImage(packet_vkCreateImage* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    imageObj local_imageObj;
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    // Convert queueFamilyIndices
+    if (pPacket->pCreateInfo)
+    {
+        for (uint32_t i = 0; i < pPacket->pCreateInfo->queueFamilyIndexCount; i++)
+        {
+            uint32_t replayIdx;
+            if (pPacket->pCreateInfo->pQueueFamilyIndices &&
+                getQueueFamilyIdx(pPacket->device,
+                                  remappedDevice,
+                                  pPacket->pCreateInfo->pQueueFamilyIndices[i],
+                                  &replayIdx))
+            {
+                *((uint32_t*)&pPacket->pCreateInfo->pQueueFamilyIndices[i]) = replayIdx;
+            } else {
+                vktrace_LogError("vkCreateImage failed, bad queueFamilyIndex");
+               return VK_ERROR_VALIDATION_FAILED_EXT;
+            }
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkCreateImage(remappedDevice, pPacket->pCreateInfo, NULL, &local_imageObj.replayImage);
+    if (replayResult == VK_SUCCESS)
+    {
+        traceImageToDevice[*pPacket->pImage] = pPacket->device;
+        replayImageToDevice[local_imageObj.replayImage] = remappedDevice;
+        m_objMapper.add_to_images_map(*(pPacket->pImage), local_imageObj);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateCommandPool(packet_vkCreateCommandPool* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkCommandPool local_pCommandPool;
+    VkDevice remappeddevice = m_objMapper.remap_devices(pPacket->device);
+    if (pPacket->device != VK_NULL_HANDLE && remappeddevice == VK_NULL_HANDLE)
+    {
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    // No need to remap pAllocator
+
+    // Convert queueFamilyIndex
+    if (pPacket->pCreateInfo)
+    {
+        uint32_t replayIdx;
+        if (getQueueFamilyIdx(pPacket->device,
+                              remappeddevice,
+                              pPacket->pCreateInfo->queueFamilyIndex,
+                              &replayIdx))
+        {
+            *((uint32_t*)&pPacket->pCreateInfo->queueFamilyIndex) = replayIdx;
+        } else {
+            vktrace_LogError("vkCreateCommandPool failed, bad queueFamilyIndex");
+           return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkCreateCommandPool(remappeddevice, pPacket->pCreateInfo, pPacket->pAllocator, &local_pCommandPool);
+    if (replayResult == VK_SUCCESS)
+    {
+        m_objMapper.add_to_commandpools_map(*(pPacket->pCommandPool), local_pCommandPool);
+    }
+    return replayResult;
+}
+
+
+VkResult vkReplay::manually_replay_vkEnumeratePhysicalDevices(packet_vkEnumeratePhysicalDevices* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    if (!m_display->m_initedVK)
+    {
+        uint32_t deviceCount = *(pPacket->pPhysicalDeviceCount);
+        VkPhysicalDevice *pDevices = pPacket->pPhysicalDevices;
+
+        VkInstance remappedInstance = m_objMapper.remap_instances(pPacket->instance);
+        if (remappedInstance == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkEnumeratePhysicalDevices() due to invalid remapped VkInstance.");
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+        if (pPacket->pPhysicalDevices != NULL)
+            pDevices = VKTRACE_NEW_ARRAY(VkPhysicalDevice, deviceCount);
+        replayResult = m_vkFuncs.real_vkEnumeratePhysicalDevices(remappedInstance, &deviceCount, pDevices);
+
+        //TODO handle different number of physical devices in trace versus replay
+        if (deviceCount != *(pPacket->pPhysicalDeviceCount))
+        {
+            vktrace_LogWarning("Number of physical devices mismatched in replay %u versus trace %u.", deviceCount, *(pPacket->pPhysicalDeviceCount));
+        }
+        else if (deviceCount == 0)
+        {
+             vktrace_LogError("vkEnumeratePhysicalDevices number of gpus is zero.");
+        }
+        else if (pDevices != NULL)
+        {
+            vktrace_LogVerbose("Enumerated %d physical devices in the system.", deviceCount);
+        }
+        // TODO handle enumeration results in a different order from trace to replay
+        for (uint32_t i = 0; i < deviceCount; i++)
+        {
+            if (pDevices != NULL)
+            {
+                m_objMapper.add_to_physicaldevices_map(pPacket->pPhysicalDevices[i], pDevices[i]);
+            }
+        }
+        VKTRACE_DELETE(pDevices);
+    }
+    return replayResult;
+}
+
+// TODO138 : Some of these functions have been renamed/changed in v138, need to scrub them and update as appropriate
+//VkResult vkReplay::manually_replay_vkGetPhysicalDeviceInfo(packet_vkGetPhysicalDeviceInfo* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    if (!m_display->m_initedVK)
+//    {
+//        VkPhysicalDevice remappedPhysicalDevice = m_objMapper.remap(pPacket->physicalDevice);
+//        if (remappedPhysicalDevice == VK_NULL_HANDLE)
+//            return VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//        switch (pPacket->infoType) {
+//        case VK_PHYSICAL_DEVICE_INFO_TYPE_PROPERTIES:
+//        {
+//            VkPhysicalDeviceProperties deviceProps;
+//            size_t dataSize = sizeof(VkPhysicalDeviceProperties);
+//            replayResult = m_vkFuncs.real_vkGetPhysicalDeviceInfo(remappedPhysicalDevice, pPacket->infoType, &dataSize,
+//                            (pPacket->pData == NULL) ? NULL : &deviceProps);
+//            if (pPacket->pData != NULL)
+//            {
+//                vktrace_LogVerbose("Replay Physical Device Properties");
+//                vktrace_LogVerbose("Vendor ID %x, Device ID %x, name %s", deviceProps.vendorId, deviceProps.deviceId, deviceProps.deviceName);
+//                vktrace_LogVerbose("API version %u, Driver version %u, gpu Type %u", deviceProps.apiVersion, deviceProps.driverVersion, deviceProps.deviceType);
+//                vktrace_LogVerbose("Max Descriptor Sets: %u", deviceProps.maxDescriptorSets);
+//                vktrace_LogVerbose("Max Bound Descriptor Sets: %u", deviceProps.maxBoundDescriptorSets);
+//                vktrace_LogVerbose("Max Thread Group Size: %u", deviceProps.maxThreadGroupSize);
+//                vktrace_LogVerbose("Max Color Attachments: %u", deviceProps.maxColorAttachments);
+//                vktrace_LogVerbose("Max Inline Memory Update Size: %llu", deviceProps.maxInlineMemoryUpdateSize);
+//            }
+//            break;
+//        }
+//        case VK_PHYSICAL_DEVICE_INFO_TYPE_PERFORMANCE:
+//        {
+//            VkPhysicalDevicePerformance devicePerfs;
+//            size_t dataSize = sizeof(VkPhysicalDevicePerformance);
+//            replayResult = m_vkFuncs.real_vkGetPhysicalDeviceInfo(remappedPhysicalDevice, pPacket->infoType, &dataSize,
+//                            (pPacket->pData == NULL) ? NULL : &devicePerfs);
+//            if (pPacket->pData != NULL)
+//            {
+//                vktrace_LogVerbose("Replay Physical Device Performance");
+//                vktrace_LogVerbose("Max device clock %f, max shader ALUs/clock %f, max texel fetches/clock %f", devicePerfs.maxDeviceClock, devicePerfs.aluPerClock, devicePerfs.texPerClock);
+//                vktrace_LogVerbose("Max primitives/clock %f, Max pixels/clock %f",devicePerfs.primsPerClock, devicePerfs.pixelsPerClock);
+//            }
+//            break;
+//        }
+//        case VK_PHYSICAL_DEVICE_INFO_TYPE_QUEUE_PROPERTIES:
+//        {
+//            VkPhysicalDeviceQueueProperties *pGpuQueue, *pQ;
+//            size_t dataSize = sizeof(VkPhysicalDeviceQueueProperties);
+//            size_t numQueues = 1;
+//            assert(pPacket->pDataSize);
+//            if ((*(pPacket->pDataSize) % dataSize) != 0)
+//                vktrace_LogWarning("vkGetPhysicalDeviceInfo() for QUEUE_PROPERTIES not an integral data size assuming 1");
+//            else
+//                numQueues = *(pPacket->pDataSize) / dataSize;
+//            dataSize = numQueues * dataSize;
+//            pQ = static_cast < VkPhysicalDeviceQueueProperties *> (vktrace_malloc(dataSize));
+//            pGpuQueue = pQ;
+//            replayResult = m_vkFuncs.real_vkGetPhysicalDeviceInfo(remappedPhysicalDevice, pPacket->infoType, &dataSize,
+//                            (pPacket->pData == NULL) ? NULL : pGpuQueue);
+//            if (pPacket->pData != NULL)
+//            {
+//                for (unsigned int i = 0; i < numQueues; i++)
+//                {
+//                    vktrace_LogVerbose("Replay Physical Device Queue Property for index %d, flags %u.", i, pGpuQueue->queueFlags);
+//                    vktrace_LogVerbose("Max available count %u, max atomic counters %u, supports timestamps %u.",pGpuQueue->queueCount, pGpuQueue->maxAtomicCounters, pGpuQueue->supportsTimestamps);
+//                    pGpuQueue++;
+//                }
+//            }
+//            vktrace_free(pQ);
+//            break;
+//        }
+//        default:
+//        {
+//            size_t size = 0;
+//            void* pData = NULL;
+//            if (pPacket->pData != NULL && pPacket->pDataSize != NULL)
+//            {
+//                size = *pPacket->pDataSize;
+//                pData = vktrace_malloc(*pPacket->pDataSize);
+//            }
+//            replayResult = m_vkFuncs.real_vkGetPhysicalDeviceInfo(remappedPhysicalDevice, pPacket->infoType, &size, pData);
+//            if (replayResult == VK_SUCCESS)
+//            {
+///*                // TODO : We could pull this out into its own case of switch, and also may want to perform some
+////                //   validation between the trace values and replay values
+//                else*/ if (size != *pPacket->pDataSize && pData != NULL)
+//                {
+//                    vktrace_LogWarning("vkGetPhysicalDeviceInfo returned a differing data size: replay (%d bytes) vs trace (%d bytes)", size, *pPacket->pDataSize);
+//                }
+//                else if (pData != NULL && memcmp(pData, pPacket->pData, size) != 0)
+//                {
+//                    vktrace_LogWarning("vkGetPhysicalDeviceInfo returned differing data contents than the trace file contained.");
+//                }
+//            }
+//            vktrace_free(pData);
+//            break;
+//        }
+//        };
+//    }
+//    return replayResult;
+//}
+
+//VkResult vkReplay::manually_replay_vkGetGlobalExtensionInfo(packet_vkGetGlobalExtensionInfo* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    if (!m_display->m_initedVK) {
+//        replayResult = m_vkFuncs.real_vkGetGlobalExtensionInfo(pPacket->infoType, pPacket->extensionIndex, pPacket->pDataSize, pPacket->pData);
+//// TODO: Confirm that replay'd properties match with traced properties to ensure compatibility.
+////        if (replayResult == VK_SUCCESS) {
+////            for (unsigned int ext = 0; ext < sizeof(g_extensions) / sizeof(g_extensions[0]); ext++)
+////            {
+////                if (!strncmp(g_extensions[ext], pPacket->pExtName, strlen(g_extensions[ext]))) {
+////                    bool extInList = false;
+////                    for (unsigned int j = 0; j < m_display->m_extensions.size(); ++j) {
+////                        if (!strncmp(m_display->m_extensions[j], g_extensions[ext], strlen(g_extensions[ext])))
+////                            extInList = true;
+////                        break;
+////                    }
+////                    if (!extInList)
+////                        m_display->m_extensions.push_back((char *) g_extensions[ext]);
+////                    break;
+////                }
+////            }
+////        }
+//    }
+//    return replayResult;
+//}
+
+//VkResult vkReplay::manually_replay_vkGetPhysicalDeviceExtensionInfo(packet_vkGetPhysicalDeviceExtensionInfo* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    if (!m_display->m_initedVK) {
+//        VkPhysicalDevice remappedPhysicalDevice = m_objMapper.remap(pPacket->physicalDevice);
+//        if (remappedPhysicalDevice == VK_NULL_HANDLE)
+//            return VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//        replayResult = m_vkFuncs.real_vkGetPhysicalDeviceExtensionInfo(remappedPhysicalDevice, pPacket->infoType, pPacket->extensionIndex, pPacket->pDataSize, pPacket->pData);
+//// TODO: Confirm that replay'd properties match with traced properties to ensure compatibility.
+////        if (replayResult == VK_SUCCESS) {
+////            for (unsigned int ext = 0; ext < sizeof(g_extensions) / sizeof(g_extensions[0]); ext++)
+////            {
+////                if (!strncmp(g_extensions[ext], pPacket->pExtName, strlen(g_extensions[ext]))) {
+////                    bool extInList = false;
+////                    for (unsigned int j = 0; j < m_display->m_extensions.size(); ++j) {
+////                        if (!strncmp(m_display->m_extensions[j], g_extensions[ext], strlen(g_extensions[ext])))
+////                            extInList = true;
+////                        break;
+////                    }
+////                    if (!extInList)
+////                        m_display->m_extensions.push_back((char *) g_extensions[ext]);
+////                    break;
+////                }
+////            }
+////        }
+//    }
+//    return replayResult;
+//}
+
+//VkResult vkReplay::manually_replay_vkGetSwapchainInfoWSI(packet_vkGetSwapchainInfoWSI* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    size_t dataSize = *pPacket->pDataSize;
+//    void* pData = vktrace_malloc(dataSize);
+//    VkSwapchainWSI remappedSwapchain = m_objMapper.remap_swapchainwsis(pPacket->swapchain);
+//    if (remappedSwapchain == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkGetSwapchainInfoWSI() due to invalid remapped VkSwapchainWSI.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//    replayResult = m_vkFuncs.real_vkGetSwapchainInfoWSI(remappedSwapchain, pPacket->infoType, &dataSize, pData);
+//    if (replayResult == VK_SUCCESS)
+//    {
+//        if (dataSize != *pPacket->pDataSize)
+//        {
+//            vktrace_LogWarning("SwapchainInfo dataSize differs between trace (%d bytes) and replay (%d bytes)", *pPacket->pDataSize, dataSize);
+//        }
+//        if (pPacket->infoType == VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI)
+//        {
+//            VkSwapchainImageInfoWSI* pImageInfoReplay = (VkSwapchainImageInfoWSI*)pData;
+//            VkSwapchainImageInfoWSI* pImageInfoTrace = (VkSwapchainImageInfoWSI*)pPacket->pData;
+//            size_t imageCountReplay = dataSize / sizeof(VkSwapchainImageInfoWSI);
+//            size_t imageCountTrace = *pPacket->pDataSize / sizeof(VkSwapchainImageInfoWSI);
+//            for (size_t i = 0; i < imageCountReplay && i < imageCountTrace; i++)
+//            {
+//                imageObj imgObj;
+//                imgObj.replayImage = pImageInfoReplay[i].image;
+//                m_objMapper.add_to_map(&pImageInfoTrace[i].image, &imgObj);
+//
+//                gpuMemObj memObj;
+//                memObj.replayGpuMem = pImageInfoReplay[i].memory;
+//                m_objMapper.add_to_map(&pImageInfoTrace[i].memory, &memObj);
+//            }
+//        }
+//    }
+//    vktrace_free(pData);
+//    return replayResult;
+//}
+
+VkResult vkReplay::manually_replay_vkQueueSubmit(packet_vkQueueSubmit* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkQueue remappedQueue = m_objMapper.remap_queues(pPacket->queue);
+    if (remappedQueue == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped VkQueue.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkFence remappedFence = m_objMapper.remap_fences(pPacket->fence);
+    if (pPacket->fence != VK_NULL_HANDLE && remappedFence == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped VkPhysicalDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSubmitInfo *remappedSubmits = NULL;
+    remappedSubmits = VKTRACE_NEW_ARRAY( VkSubmitInfo, pPacket->submitCount);
+    VkCommandBuffer *pRemappedBuffers = NULL;
+    VkSemaphore *pRemappedWaitSems = NULL, *pRemappedSignalSems = NULL;
+    for (uint32_t submit_idx = 0; submit_idx < pPacket->submitCount; submit_idx++) {
+        const VkSubmitInfo *submit = &pPacket->pSubmits[submit_idx];
+        VkSubmitInfo *remappedSubmit = &remappedSubmits[submit_idx];
+        memset(remappedSubmit, 0, sizeof(VkSubmitInfo));
+        remappedSubmit->sType = submit->sType;
+        remappedSubmit->pNext = submit->pNext;
+        remappedSubmit->pWaitDstStageMask = submit->pWaitDstStageMask;
+        // Remap Semaphores & CommandBuffers for this submit
+        uint32_t i = 0;
+        if (submit->pCommandBuffers != NULL) {
+            pRemappedBuffers = VKTRACE_NEW_ARRAY( VkCommandBuffer, submit->commandBufferCount);
+            remappedSubmit->pCommandBuffers = pRemappedBuffers;
+            remappedSubmit->commandBufferCount = submit->commandBufferCount;
+            for (i = 0; i < submit->commandBufferCount; i++) {
+                *(pRemappedBuffers + i) = m_objMapper.remap_commandbuffers(*(submit->pCommandBuffers + i));
+                if (*(pRemappedBuffers + i) == VK_NULL_HANDLE) {
+                    vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped VkCommandBuffer.");
+                    VKTRACE_DELETE(remappedSubmits);
+                    VKTRACE_DELETE(pRemappedBuffers);
+                    return replayResult;
+                }
+            }
+        }
+        if (submit->pWaitSemaphores != NULL) {
+            pRemappedWaitSems = VKTRACE_NEW_ARRAY(VkSemaphore, submit->waitSemaphoreCount);
+            remappedSubmit->pWaitSemaphores = pRemappedWaitSems;
+            remappedSubmit->waitSemaphoreCount = submit->waitSemaphoreCount;
+            for (i = 0; i < submit->waitSemaphoreCount; i++) {
+                (*(pRemappedWaitSems + i)) = m_objMapper.remap_semaphores((*(submit->pWaitSemaphores + i)));
+                if (*(pRemappedWaitSems + i) == VK_NULL_HANDLE) {
+                    vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped wait VkSemaphore.");
+                    VKTRACE_DELETE(remappedSubmits);
+                    VKTRACE_DELETE(pRemappedBuffers);
+                    VKTRACE_DELETE(pRemappedWaitSems);
+                    return replayResult;
+                }
+            }
+        }
+        if (submit->pSignalSemaphores != NULL) {
+            pRemappedSignalSems = VKTRACE_NEW_ARRAY(VkSemaphore, submit->signalSemaphoreCount);
+            remappedSubmit->pSignalSemaphores = pRemappedSignalSems;
+            remappedSubmit->signalSemaphoreCount = submit->signalSemaphoreCount;
+            for (i = 0; i < submit->signalSemaphoreCount; i++) {
+                (*(pRemappedSignalSems + i)) = m_objMapper.remap_semaphores((*(submit->pSignalSemaphores + i)));
+                if (*(pRemappedSignalSems + i) == VK_NULL_HANDLE) {
+                    vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped signal VkSemaphore.");
+                    VKTRACE_DELETE(remappedSubmits);
+                    VKTRACE_DELETE(pRemappedBuffers);
+                    VKTRACE_DELETE(pRemappedWaitSems);
+                    VKTRACE_DELETE(pRemappedSignalSems);
+                    return replayResult;
+                }
+            }
+        }
+    }
+    replayResult = m_vkFuncs.real_vkQueueSubmit(remappedQueue,
+                                                pPacket->submitCount,
+                                                remappedSubmits,
+                                                remappedFence);
+    VKTRACE_DELETE(pRemappedBuffers);
+    VKTRACE_DELETE(pRemappedWaitSems);
+    VKTRACE_DELETE(pRemappedSignalSems);
+    VKTRACE_DELETE(remappedSubmits);
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkQueueBindSparse(packet_vkQueueBindSparse* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkQueue remappedQueue = m_objMapper.remap_queues(pPacket->queue);
+    if (remappedQueue == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkQueue.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkFence remappedFence = m_objMapper.remap_fences(pPacket->fence);
+    if (pPacket->fence != VK_NULL_HANDLE && remappedFence == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkPhysicalDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkBindSparseInfo* remappedBindSparseInfos = VKTRACE_NEW_ARRAY(VkBindSparseInfo, pPacket->bindInfoCount);
+    VkSparseImageMemoryBind *pRemappedImageMemories = NULL;
+    VkSparseMemoryBind *pRemappedBufferMemories = NULL;
+    VkSparseMemoryBind *pRemappedImageOpaqueMemories = NULL;
+    VkSemaphore *pRemappedWaitSems = NULL;
+    VkSemaphore *pRemappedSignalSems = NULL;
+    VkSparseImageMemoryBindInfo* sIMBinf = NULL;
+    VkSparseBufferMemoryBindInfo* sBMBinf = NULL;
+    VkSparseImageOpaqueMemoryBindInfo* sIMOBinf = NULL;
+
+    memcpy((void*)remappedBindSparseInfos, (void*)(pPacket->pBindInfo), sizeof(VkBindSparseInfo)*pPacket->bindInfoCount);
+
+    for (uint32_t bindInfo_idx = 0; bindInfo_idx < pPacket->bindInfoCount; bindInfo_idx++) {
+        if (remappedBindSparseInfos[bindInfo_idx].pBufferBinds)
+        {
+            sBMBinf = VKTRACE_NEW_ARRAY(VkSparseBufferMemoryBindInfo, remappedBindSparseInfos[bindInfo_idx].bufferBindCount);
+            remappedBindSparseInfos[bindInfo_idx].pBufferBinds = (const VkSparseBufferMemoryBindInfo*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)remappedBindSparseInfos[bindInfo_idx].pBufferBinds));
+            memcpy((void*)sBMBinf, (void*)remappedBindSparseInfos[bindInfo_idx].pBufferBinds, sizeof(VkSparseBufferMemoryBindInfo)*remappedBindSparseInfos[bindInfo_idx].bufferBindCount);
+
+            sBMBinf->buffer = m_objMapper.remap_buffers(sBMBinf->buffer);
+
+            if (sBMBinf->buffer == VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkBuffer.");
+                goto FAILURE;
+            }
+
+            if (sBMBinf->bindCount > 0 && sBMBinf->pBinds)
+            {
+                pRemappedBufferMemories = (VkSparseMemoryBind*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)remappedBindSparseInfos[bindInfo_idx].pBufferBinds->pBinds));
+            }
+
+            for (uint32_t bindCountIdx = 0; bindCountIdx < sBMBinf->bindCount; bindCountIdx++)
+            {
+                gpuMemObj local_mem = m_objMapper.m_devicememorys.find(pRemappedBufferMemories[bindCountIdx].memory)->second;
+                VkDeviceMemory replay_mem = m_objMapper.remap_devicememorys(pRemappedBufferMemories[bindCountIdx].memory);
+
+                if (replay_mem == VK_NULL_HANDLE || local_mem.pGpuMem == NULL)
+                {
+                    vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkDeviceMemory.");
+                    goto FAILURE;
+                }
+                pRemappedBufferMemories[bindCountIdx].memory = replay_mem;
+            }
+            sBMBinf->pBinds = pRemappedBufferMemories;
+            remappedBindSparseInfos[bindInfo_idx].pBufferBinds = sBMBinf;
+        }
+
+        if (remappedBindSparseInfos[bindInfo_idx].pImageBinds)
+        {
+            sIMBinf = VKTRACE_NEW_ARRAY(VkSparseImageMemoryBindInfo, remappedBindSparseInfos[bindInfo_idx].imageBindCount);
+            remappedBindSparseInfos[bindInfo_idx].pImageBinds = (const VkSparseImageMemoryBindInfo*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)remappedBindSparseInfos[bindInfo_idx].pImageBinds));
+            memcpy((void*)sIMBinf, (void*)remappedBindSparseInfos[bindInfo_idx].pImageBinds, sizeof(VkSparseImageMemoryBindInfo)*remappedBindSparseInfos[bindInfo_idx].imageBindCount);
+
+            sIMBinf->image = m_objMapper.remap_images(sIMBinf->image);
+
+            if(sIMBinf->image == VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkImage.");
+                goto FAILURE;
+            }
+
+            if (sIMBinf->bindCount > 0 && sIMBinf->pBinds)
+            {
+                pRemappedImageMemories = (VkSparseImageMemoryBind*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)remappedBindSparseInfos[bindInfo_idx].pImageBinds->pBinds));
+            }
+            for (uint32_t bindCountIdx = 0; bindCountIdx < sIMBinf->bindCount; bindCountIdx++)
+            {
+                gpuMemObj local_mem = m_objMapper.m_devicememorys.find(pRemappedImageMemories[bindCountIdx].memory)->second;
+                VkDeviceMemory replay_mem = m_objMapper.remap_devicememorys(pRemappedImageMemories[bindCountIdx].memory);
+
+                if (replay_mem == VK_NULL_HANDLE || local_mem.pGpuMem == NULL)
+                {
+                    vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkDeviceMemory.");
+                    goto FAILURE;
+                }
+                pRemappedImageMemories[bindCountIdx].memory = replay_mem;
+            }
+            sIMBinf->pBinds = pRemappedImageMemories;
+            remappedBindSparseInfos[bindInfo_idx].pImageBinds = sIMBinf;
+        }
+
+        if (remappedBindSparseInfos[bindInfo_idx].pImageOpaqueBinds)
+        {
+            sIMOBinf = VKTRACE_NEW_ARRAY(VkSparseImageOpaqueMemoryBindInfo, remappedBindSparseInfos[bindInfo_idx].imageOpaqueBindCount);
+            remappedBindSparseInfos[bindInfo_idx].pImageOpaqueBinds = (const VkSparseImageOpaqueMemoryBindInfo*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)remappedBindSparseInfos[bindInfo_idx].pImageOpaqueBinds));
+            memcpy((void*)sIMOBinf, (void*)remappedBindSparseInfos[bindInfo_idx].pImageOpaqueBinds, sizeof(VkSparseImageOpaqueMemoryBindInfo)*remappedBindSparseInfos[bindInfo_idx].imageOpaqueBindCount);
+
+            sIMOBinf->image = m_objMapper.remap_images(sIMOBinf->image);
+
+            if (sIMOBinf->image == VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkImage.");
+                goto FAILURE;
+            }
+
+            if (sIMOBinf->bindCount > 0 && sIMOBinf->pBinds)
+            {
+                pRemappedImageOpaqueMemories = (VkSparseMemoryBind*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)remappedBindSparseInfos[bindInfo_idx].pImageOpaqueBinds->pBinds));
+            }
+            for (uint32_t bindCountIdx = 0; bindCountIdx < sIMOBinf->bindCount; bindCountIdx++)
+            {
+                gpuMemObj local_mem = m_objMapper.m_devicememorys.find(pRemappedImageOpaqueMemories[bindCountIdx].memory)->second;
+                VkDeviceMemory replay_mem = m_objMapper.remap_devicememorys(pRemappedImageOpaqueMemories[bindCountIdx].memory);
+
+                if (replay_mem == VK_NULL_HANDLE || local_mem.pGpuMem == NULL)
+                {
+                    vktrace_LogError("Skipping vkQueueBindSparse() due to invalid remapped VkDeviceMemory.");
+                    goto FAILURE;
+                }
+                pRemappedImageOpaqueMemories[bindCountIdx].memory = replay_mem;
+            }
+            sIMOBinf->pBinds = pRemappedImageOpaqueMemories;
+            remappedBindSparseInfos[bindInfo_idx].pImageOpaqueBinds = sIMOBinf;
+        }
+
+        if (remappedBindSparseInfos[bindInfo_idx].pWaitSemaphores != NULL) {
+            pRemappedWaitSems = VKTRACE_NEW_ARRAY(VkSemaphore, remappedBindSparseInfos[bindInfo_idx].waitSemaphoreCount);
+            remappedBindSparseInfos[bindInfo_idx].pWaitSemaphores = pRemappedWaitSems;
+            for (uint32_t i = 0; i < remappedBindSparseInfos[bindInfo_idx].waitSemaphoreCount; i++) {
+                (*(pRemappedWaitSems + i)) = m_objMapper.remap_semaphores((*(remappedBindSparseInfos[bindInfo_idx].pWaitSemaphores + i)));
+                if (*(pRemappedWaitSems + i) == VK_NULL_HANDLE) {
+                    vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped wait VkSemaphore.");
+                    goto FAILURE;
+                }
+            }
+        }
+        if (remappedBindSparseInfos[bindInfo_idx].pSignalSemaphores != NULL) {
+            pRemappedSignalSems = VKTRACE_NEW_ARRAY(VkSemaphore, remappedBindSparseInfos[bindInfo_idx].signalSemaphoreCount);
+            remappedBindSparseInfos[bindInfo_idx].pSignalSemaphores = pRemappedSignalSems;
+            for (uint32_t i = 0; i < remappedBindSparseInfos[bindInfo_idx].signalSemaphoreCount; i++) {
+                (*(pRemappedSignalSems + i)) = m_objMapper.remap_semaphores((*(remappedBindSparseInfos[bindInfo_idx].pSignalSemaphores + i)));
+                if (*(pRemappedSignalSems + i) == VK_NULL_HANDLE) {
+                    vktrace_LogError("Skipping vkQueueSubmit() due to invalid remapped signal VkSemaphore.");
+                    goto FAILURE;
+                }
+            }
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkQueueBindSparse(remappedQueue,
+        pPacket->bindInfoCount,
+        remappedBindSparseInfos,
+        remappedFence);
+
+FAILURE:
+    VKTRACE_DELETE(remappedBindSparseInfos);
+    VKTRACE_DELETE(sIMBinf);
+    VKTRACE_DELETE(sBMBinf);
+    VKTRACE_DELETE(sIMOBinf);
+    VKTRACE_DELETE(pRemappedSignalSems);
+    VKTRACE_DELETE(pRemappedWaitSems);
+    return replayResult;
+}
+
+
+//VkResult vkReplay::manually_replay_vkGetObjectInfo(packet_vkGetObjectInfo* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+//    if (remappedDevice == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkGetObjectInfo() due to invalid remapped VkDevice.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    VkObject remappedObject = m_objMapper.remap(pPacket->object, pPacket->objType);
+//    if (remappedObject == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkGetObjectInfo() due to invalid remapped VkObject.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    size_t size = 0;
+//    void* pData = NULL;
+//    if (pPacket->pData != NULL && pPacket->pDataSize != NULL)
+//    {
+//        size = *pPacket->pDataSize;
+//        pData = vktrace_malloc(*pPacket->pDataSize);
+//        memcpy(pData, pPacket->pData, *pPacket->pDataSize);
+//    }
+//    // TODO only search for object once rather than at remap() and init_objMemXXX()
+//    replayResult = m_vkFuncs.real_vkGetObjectInfo(remappedDevice, pPacket->objType, remappedObject, pPacket->infoType, &size, pData);
+//    if (replayResult == VK_SUCCESS)
+//    {
+//        if (size != *pPacket->pDataSize && pData != NULL)
+//        {
+//            vktrace_LogWarning("vkGetObjectInfo returned a differing data size: replay (%d bytes) vs trace (%d bytes).", size, *pPacket->pDataSize);
+//        }
+//        else if (pData != NULL)
+//        {
+//            switch (pPacket->infoType)
+//            {
+//                case VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS:
+//                {
+//                    VkMemoryRequirements *traceReqs = (VkMemoryRequirements *) pPacket->pData;
+//                    VkMemoryRequirements *replayReqs = (VkMemoryRequirements *) pData;
+//                    size_t num = size / sizeof(VkMemoryRequirements);
+//                    for (unsigned int i = 0; i < num; i++)
+//                    {
+//                        if (traceReqs->size != replayReqs->size)
+//                            vktrace_LogWarning("vkGetObjectInfo(INFO_TYPE_MEMORY_REQUIREMENTS) mismatch: trace size %u, replay size %u.", traceReqs->size, replayReqs->size);
+//                        if (traceReqs->alignment != replayReqs->alignment)
+//                            vktrace_LogWarning("vkGetObjectInfo(INFO_TYPE_MEMORY_REQUIREMENTS) mismatch: trace alignment %u, replay aligmnent %u.", traceReqs->alignment, replayReqs->alignment);
+//                        if (traceReqs->granularity != replayReqs->granularity)
+//                            vktrace_LogWarning("vkGetObjectInfo(INFO_TYPE_MEMORY_REQUIREMENTS) mismatch: trace granularity %u, replay granularity %u.", traceReqs->granularity, replayReqs->granularity);
+//                        if (traceReqs->memPropsAllowed != replayReqs->memPropsAllowed)
+//                            vktrace_LogWarning("vkGetObjectInfo(INFO_TYPE_MEMORY_REQUIREMENTS) mismatch: trace memPropsAllowed %u, replay memPropsAllowed %u.", traceReqs->memPropsAllowed, replayReqs->memPropsAllowed);
+//                        if (traceReqs->memPropsRequired != replayReqs->memPropsRequired)
+//                            vktrace_LogWarning("vkGetObjectInfo(INFO_TYPE_MEMORY_REQUIREMENTS) mismatch: trace memPropsRequired %u, replay memPropsRequired %u.", traceReqs->memPropsRequired, replayReqs->memPropsRequired);
+//                        traceReqs++;
+//                        replayReqs++;
+//                    }
+//                    if (m_objMapper.m_adjustForGPU)
+//                        m_objMapper.init_objMemReqs(pPacket->object, replayReqs - num, num);
+//                    break;
+//                }
+//                default:
+//                    if (memcmp(pData, pPacket->pData, size) != 0)
+//                        vktrace_LogWarning("vkGetObjectInfo() mismatch on *pData: between trace and replay *pDataSize %u.", size);
+//            }
+//        }
+//    }
+//    vktrace_free(pData);
+//    return replayResult;
+//}
+
+//VkResult vkReplay::manually_replay_vkGetImageSubresourceInfo(packet_vkGetImageSubresourceInfo* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+//    if (remappedDevice == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkGetImageSubresourceInfo() due to invalid remapped VkDevice.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    VkImage remappedImage = m_objMapper.remap(pPacket->image);
+//    if (remappedImage == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkGetImageSubresourceInfo() due to invalid remapped VkImage.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    size_t size = 0;
+//    void* pData = NULL;
+//    if (pPacket->pData != NULL && pPacket->pDataSize != NULL)
+//    {
+//        size = *pPacket->pDataSize;
+//        pData = vktrace_malloc(*pPacket->pDataSize);
+//    }
+//    replayResult = m_vkFuncs.real_vkGetImageSubresourceInfo(remappedDevice, remappedImage, pPacket->pSubresource, pPacket->infoType, &size, pData);
+//    if (replayResult == VK_SUCCESS)
+//    {
+//        if (size != *pPacket->pDataSize && pData != NULL)
+//        {
+//            vktrace_LogWarning("vkGetImageSubresourceInfo returned a differing data size: replay (%d bytes) vs trace (%d bytes).", size, *pPacket->pDataSize);
+//        }
+//        else if (pData != NULL && memcmp(pData, pPacket->pData, size) != 0)
+//        {
+//            vktrace_LogWarning("vkGetImageSubresourceInfo returned differing data contents than the trace file contained.");
+//        }
+//    }
+//    vktrace_free(pData);
+//    return replayResult;
+//}
+
+void vkReplay::manually_replay_vkUpdateDescriptorSets(packet_vkUpdateDescriptorSets* pPacket)
+{
+    // We have to remap handles internal to the structures so save the handles prior to remap and then restore
+    // Rather than doing a deep memcpy of the entire struct and fixing any intermediate pointers, do save and restores via STL queue
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkDevice.");
+        return;
+    }
+
+    VkWriteDescriptorSet* pRemappedWrites = VKTRACE_NEW_ARRAY(VkWriteDescriptorSet, pPacket->descriptorWriteCount);
+    memcpy(pRemappedWrites, pPacket->pDescriptorWrites, pPacket->descriptorWriteCount * sizeof(VkWriteDescriptorSet));
+
+    VkCopyDescriptorSet* pRemappedCopies = VKTRACE_NEW_ARRAY(VkCopyDescriptorSet, pPacket->descriptorCopyCount);
+    memcpy(pRemappedCopies, pPacket->pDescriptorCopies, pPacket->descriptorCopyCount * sizeof(VkCopyDescriptorSet));
+
+    bool errorBadRemap = false;
+
+    for (uint32_t i = 0; i < pPacket->descriptorWriteCount && !errorBadRemap; i++)
+    {
+        pRemappedWrites[i].dstSet = m_objMapper.remap_descriptorsets(pPacket->pDescriptorWrites[i].dstSet);
+        if (pRemappedWrites[i].dstSet == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped write VkDescriptorSet.");
+            errorBadRemap = true;
+            break;
+        }
+
+        switch (pPacket->pDescriptorWrites[i].descriptorType) {
+        case VK_DESCRIPTOR_TYPE_SAMPLER:
+            pRemappedWrites[i].pImageInfo = VKTRACE_NEW_ARRAY(VkDescriptorImageInfo, pPacket->pDescriptorWrites[i].descriptorCount);
+            memcpy((void*)pRemappedWrites[i].pImageInfo, pPacket->pDescriptorWrites[i].pImageInfo, pPacket->pDescriptorWrites[i].descriptorCount * sizeof(VkDescriptorImageInfo));
+            for (uint32_t j = 0; j < pPacket->pDescriptorWrites[i].descriptorCount; j++)
+            {
+                if (pPacket->pDescriptorWrites[i].pImageInfo[j].sampler != VK_NULL_HANDLE)
+                {
+                    const_cast<VkDescriptorImageInfo*>(pRemappedWrites[i].pImageInfo)[j].sampler = m_objMapper.remap_samplers(pPacket->pDescriptorWrites[i].pImageInfo[j].sampler);
+                    if (pRemappedWrites[i].pImageInfo[j].sampler == VK_NULL_HANDLE)
+                    {
+                        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkSampler.");
+                        errorBadRemap = true;
+                        break;
+                    }
+                }
+            }
+            break;
+        case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+        case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+        case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+            pRemappedWrites[i].pImageInfo = VKTRACE_NEW_ARRAY(VkDescriptorImageInfo, pPacket->pDescriptorWrites[i].descriptorCount);
+            memcpy((void*)pRemappedWrites[i].pImageInfo, pPacket->pDescriptorWrites[i].pImageInfo, pPacket->pDescriptorWrites[i].descriptorCount * sizeof(VkDescriptorImageInfo));
+            for (uint32_t j = 0; j < pPacket->pDescriptorWrites[i].descriptorCount; j++)
+            {
+                if (pPacket->pDescriptorWrites[i].pImageInfo[j].imageView != VK_NULL_HANDLE)
+                {
+                    const_cast<VkDescriptorImageInfo*>(pRemappedWrites[i].pImageInfo)[j].imageView = m_objMapper.remap_imageviews(pPacket->pDescriptorWrites[i].pImageInfo[j].imageView);
+                    if (pRemappedWrites[i].pImageInfo[j].imageView == VK_NULL_HANDLE)
+                    {
+                        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkImageView.");
+                        errorBadRemap = true;
+                        break;
+                    }
+                }
+            }
+            break;
+        case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+            pRemappedWrites[i].pImageInfo = VKTRACE_NEW_ARRAY(VkDescriptorImageInfo, pPacket->pDescriptorWrites[i].descriptorCount);
+            memcpy((void*)pRemappedWrites[i].pImageInfo, pPacket->pDescriptorWrites[i].pImageInfo, pPacket->pDescriptorWrites[i].descriptorCount * sizeof(VkDescriptorImageInfo));
+            for (uint32_t j = 0; j < pPacket->pDescriptorWrites[i].descriptorCount; j++)
+            {
+                if (pPacket->pDescriptorWrites[i].pImageInfo[j].sampler != VK_NULL_HANDLE)
+                {
+                    const_cast<VkDescriptorImageInfo*>(pRemappedWrites[i].pImageInfo)[j].sampler = m_objMapper.remap_samplers(pPacket->pDescriptorWrites[i].pImageInfo[j].sampler);
+                    if (pRemappedWrites[i].pImageInfo[j].sampler == VK_NULL_HANDLE)
+                    {
+                        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkSampler.");
+                        errorBadRemap = true;
+                        break;
+                    }
+                }
+                if (pPacket->pDescriptorWrites[i].pImageInfo[j].imageView != VK_NULL_HANDLE)
+                {
+                    const_cast<VkDescriptorImageInfo*>(pRemappedWrites[i].pImageInfo)[j].imageView = m_objMapper.remap_imageviews(pPacket->pDescriptorWrites[i].pImageInfo[j].imageView);
+                    if (pRemappedWrites[i].pImageInfo[j].imageView == VK_NULL_HANDLE)
+                    {
+                        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkImageView.");
+                        errorBadRemap = true;
+                        break;
+                    }
+                }
+            }
+            break;
+        case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+        case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+            pRemappedWrites[i].pTexelBufferView = VKTRACE_NEW_ARRAY(VkBufferView, pPacket->pDescriptorWrites[i].descriptorCount);
+            memcpy((void*)pRemappedWrites[i].pTexelBufferView, pPacket->pDescriptorWrites[i].pTexelBufferView, pPacket->pDescriptorWrites[i].descriptorCount * sizeof(VkBufferView));
+            for (uint32_t j = 0; j < pPacket->pDescriptorWrites[i].descriptorCount; j++)
+            {
+                if (pPacket->pDescriptorWrites[i].pTexelBufferView[j] != VK_NULL_HANDLE)
+                {
+                    const_cast<VkBufferView*>(pRemappedWrites[i].pTexelBufferView)[j] = m_objMapper.remap_bufferviews(pPacket->pDescriptorWrites[i].pTexelBufferView[j]);
+                    if (pRemappedWrites[i].pTexelBufferView[j] == VK_NULL_HANDLE)
+                    {
+                        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkBufferView.");
+                        errorBadRemap = true;
+                        break;
+                    }
+                }
+            }
+            break;
+        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+            pRemappedWrites[i].pBufferInfo = VKTRACE_NEW_ARRAY(VkDescriptorBufferInfo, pPacket->pDescriptorWrites[i].descriptorCount);
+            memcpy((void*)pRemappedWrites[i].pBufferInfo, pPacket->pDescriptorWrites[i].pBufferInfo, pPacket->pDescriptorWrites[i].descriptorCount * sizeof(VkDescriptorBufferInfo));
+            for (uint32_t j = 0; j < pPacket->pDescriptorWrites[i].descriptorCount; j++)
+            {
+                if (pPacket->pDescriptorWrites[i].pBufferInfo[j].buffer != VK_NULL_HANDLE)
+                {
+                    const_cast<VkDescriptorBufferInfo*>(pRemappedWrites[i].pBufferInfo)[j].buffer = m_objMapper.remap_buffers(pPacket->pDescriptorWrites[i].pBufferInfo[j].buffer);
+                    if (pRemappedWrites[i].pBufferInfo[j].buffer == VK_NULL_HANDLE)
+                    {
+                        vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped VkBufferView.");
+                        errorBadRemap = true;
+                        break;
+                    }
+                }
+            }
+            /* Nothing to do, already copied the constant values into the new descriptor info */
+        default:
+            break;
+        }
+    }
+
+    for (uint32_t i = 0; i < pPacket->descriptorCopyCount && !errorBadRemap; i++)
+    {
+        pRemappedCopies[i].dstSet = m_objMapper.remap_descriptorsets(pPacket->pDescriptorCopies[i].dstSet);
+        if (pRemappedCopies[i].dstSet == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped destination VkDescriptorSet.");
+            errorBadRemap = true;
+            break;
+        }
+
+        pRemappedCopies[i].srcSet = m_objMapper.remap_descriptorsets(pPacket->pDescriptorCopies[i].srcSet);
+        if (pRemappedCopies[i].srcSet == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkUpdateDescriptorSets() due to invalid remapped source VkDescriptorSet.");
+            errorBadRemap = true;
+            break;
+        }
+    }
+
+    if (!errorBadRemap)
+    {
+        // If an error occurred, don't call the real function, but skip ahead so that memory is cleaned up!
+
+        m_vkFuncs.real_vkUpdateDescriptorSets(remappedDevice, pPacket->descriptorWriteCount, pRemappedWrites, pPacket->descriptorCopyCount, pRemappedCopies);
+    }
+
+    for (uint32_t d = 0; d < pPacket->descriptorWriteCount; d++)
+    {
+        if (pRemappedWrites[d].pImageInfo != NULL) {
+            VKTRACE_DELETE((void*)pRemappedWrites[d].pImageInfo);
+            pRemappedWrites[d].pImageInfo = NULL;
+        }
+        if (pRemappedWrites[d].pBufferInfo != NULL) {
+            VKTRACE_DELETE((void*)pRemappedWrites[d].pBufferInfo);
+            pRemappedWrites[d].pImageInfo = NULL;
+        }
+        if (pRemappedWrites[d].pTexelBufferView != NULL) {
+            VKTRACE_DELETE((void*)pRemappedWrites[d].pTexelBufferView);
+            pRemappedWrites[d].pTexelBufferView = NULL;
+        }
+    }
+    VKTRACE_DELETE(pRemappedWrites);
+    VKTRACE_DELETE(pRemappedCopies);
+}
+
+VkResult vkReplay::manually_replay_vkCreateDescriptorSetLayout(packet_vkCreateDescriptorSetLayout* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateDescriptorSetLayout() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkDescriptorSetLayoutCreateInfo *pInfo = (VkDescriptorSetLayoutCreateInfo*) pPacket->pCreateInfo;
+    if (pInfo != NULL)
+    {
+        if (pInfo->pBindings != NULL)
+        {
+            for (unsigned int i = 0; i < pInfo->bindingCount; i++)
+            {
+                VkDescriptorSetLayoutBinding *pBindings = (VkDescriptorSetLayoutBinding *) &pInfo->pBindings[i];
+                if (pBindings->pImmutableSamplers != NULL)
+                {
+                    for (unsigned int j = 0; j < pBindings->descriptorCount; j++)
+                    {
+                        VkSampler* pSampler = (VkSampler*)&pBindings->pImmutableSamplers[j];
+                        *pSampler = m_objMapper.remap_samplers(pBindings->pImmutableSamplers[j]);
+                        if (*pSampler == VK_NULL_HANDLE)
+                        {
+                            vktrace_LogError("Skipping vkCreateDescriptorSetLayout() due to invalid remapped VkSampler.");
+                            return VK_ERROR_VALIDATION_FAILED_EXT;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    VkDescriptorSetLayout setLayout;
+    replayResult = m_vkFuncs.real_vkCreateDescriptorSetLayout(remappedDevice, pPacket->pCreateInfo, NULL, &setLayout);
+    if (replayResult == VK_SUCCESS)
+    {
+        m_objMapper.add_to_descriptorsetlayouts_map(*(pPacket->pSetLayout), setLayout);
+    }
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkDestroyDescriptorSetLayout(packet_vkDestroyDescriptorSetLayout* pPacket)
+{
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE) {
+        vktrace_LogError("Skipping vkDestroyDescriptorSetLayout() due to invalid remapped VkDevice.");
+        return;
+    }
+
+    m_vkFuncs.real_vkDestroyDescriptorSetLayout(remappedDevice, pPacket->descriptorSetLayout, NULL);
+    m_objMapper.rm_from_descriptorsetlayouts_map(pPacket->descriptorSetLayout);
+}
+
+VkResult vkReplay::manually_replay_vkAllocateDescriptorSets(packet_vkAllocateDescriptorSets* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkAllocateDescriptorSets() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkDescriptorPool remappedPool = m_objMapper.remap_descriptorpools(pPacket->pAllocateInfo->descriptorPool);
+    if (remappedPool == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkAllocateDescriptorSets() due to invalid remapped VkDescriptorPool.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkDescriptorSetLayout* pRemappedSetLayouts = VKTRACE_NEW_ARRAY(VkDescriptorSetLayout, pPacket->pAllocateInfo->descriptorSetCount);
+
+    VkDescriptorSetAllocateInfo allocateInfo;
+    allocateInfo.pNext = NULL;
+    allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    allocateInfo.descriptorPool = remappedPool;
+    allocateInfo.descriptorSetCount = pPacket->pAllocateInfo->descriptorSetCount;
+    allocateInfo.pSetLayouts = pRemappedSetLayouts;
+
+    for (uint32_t i = 0; i < allocateInfo.descriptorSetCount; i++)
+    {
+        pRemappedSetLayouts[i] = m_objMapper.remap_descriptorsetlayouts(pPacket->pAllocateInfo->pSetLayouts[i]);
+        if (pRemappedSetLayouts[i] == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkAllocateDescriptorSets() due to invalid remapped VkDescriptorSetLayout.");
+            VKTRACE_DELETE(pRemappedSetLayouts);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+    }
+
+    VkDescriptorSet* pDescriptorSets = NULL;
+    replayResult = m_vkFuncs.real_vkAllocateDescriptorSets(
+                       remappedDevice,
+                       pPacket->pAllocateInfo,
+                       pDescriptorSets);
+    if(replayResult == VK_SUCCESS)
+    {
+        for(uint32_t i = 0; i < pPacket->pAllocateInfo->descriptorSetCount; ++i)
+        {
+           m_objMapper.add_to_descriptorsets_map(pPacket->pDescriptorSets[i], pDescriptorSets[i]);
+        }
+    }
+
+    VKTRACE_DELETE(pRemappedSetLayouts);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkFreeDescriptorSets(packet_vkFreeDescriptorSets* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkFreeDescriptorSets() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkDescriptorPool remappedDescriptorPool;
+    remappedDescriptorPool = m_objMapper.remap_descriptorpools(pPacket->descriptorPool);
+    if (remappedDescriptorPool == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkFreeDescriptorSets() due to invalid remapped VkDescriptorPool.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkDescriptorSet* localDSs = VKTRACE_NEW_ARRAY(VkDescriptorSet, pPacket->descriptorSetCount);
+    uint32_t i;
+    for (i = 0; i < pPacket->descriptorSetCount; ++i) {
+        localDSs[i] = m_objMapper.remap_descriptorsets(pPacket->pDescriptorSets[i]);
+        if (localDSs[i] == VK_NULL_HANDLE && pPacket->pDescriptorSets[i] != VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkFreeDescriptorSets() due to invalid remapped VkDescriptorSet.");
+            VKTRACE_DELETE(localDSs);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkFreeDescriptorSets(remappedDevice, remappedDescriptorPool, pPacket->descriptorSetCount, localDSs);
+    if(replayResult == VK_SUCCESS)
+    {
+        for (i = 0; i < pPacket->descriptorSetCount; ++i) {
+           m_objMapper.rm_from_descriptorsets_map(pPacket->pDescriptorSets[i]);
+        }
+    }
+    VKTRACE_DELETE(localDSs);
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkCmdBindDescriptorSets(packet_vkCmdBindDescriptorSets* pPacket)
+{
+    VkCommandBuffer remappedCommandBuffer = m_objMapper.remap_commandbuffers(pPacket->commandBuffer);
+    if (remappedCommandBuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdBindDescriptorSets() due to invalid remapped VkCommandBuffer.");
+        return;
+    }
+
+    VkPipelineLayout remappedLayout = m_objMapper.remap_pipelinelayouts(pPacket->layout);
+    if (remappedLayout == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdBindDescriptorSets() due to invalid remapped VkPipelineLayout.");
+        return;
+    }
+
+    VkDescriptorSet* pRemappedSets = (VkDescriptorSet *) vktrace_malloc(sizeof(VkDescriptorSet) * pPacket->descriptorSetCount);
+    if (pRemappedSets == NULL)
+    {
+        vktrace_LogError("Replay of CmdBindDescriptorSets out of memory.");
+        return;
+    }
+
+    for (uint32_t idx = 0; idx < pPacket->descriptorSetCount && pPacket->pDescriptorSets != NULL; idx++)
+    {
+        pRemappedSets[idx] = m_objMapper.remap_descriptorsets(pPacket->pDescriptorSets[idx]);
+        if (pRemappedSets[idx] == VK_NULL_HANDLE && pPacket->pDescriptorSets[idx] != VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCmdBindDescriptorSets() due to invalid remapped VkDescriptorSet.");
+            vktrace_free(pRemappedSets);
+            return;
+        }
+    }
+
+    m_vkFuncs.real_vkCmdBindDescriptorSets(remappedCommandBuffer, pPacket->pipelineBindPoint, remappedLayout, pPacket->firstSet, pPacket->descriptorSetCount, pRemappedSets, pPacket->dynamicOffsetCount, pPacket->pDynamicOffsets);
+    vktrace_free(pRemappedSets);
+    return;
+}
+
+void vkReplay::manually_replay_vkCmdBindVertexBuffers(packet_vkCmdBindVertexBuffers* pPacket)
+{
+    VkCommandBuffer remappedCommandBuffer = m_objMapper.remap_commandbuffers(pPacket->commandBuffer);
+    if (remappedCommandBuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdBindVertexBuffers() due to invalid remapped VkCommandBuffer.");
+        return;
+    }
+
+    VkBuffer *pSaveBuff = VKTRACE_NEW_ARRAY(VkBuffer, pPacket->bindingCount);
+    if (pSaveBuff == NULL && pPacket->bindingCount > 0)
+    {
+        vktrace_LogError("Replay of CmdBindVertexBuffers out of memory.");
+        return;
+    }
+    uint32_t i = 0;
+    if (pPacket->pBuffers != NULL) {
+        for (i = 0; i < pPacket->bindingCount; i++)
+        {
+            VkBuffer *pBuff = (VkBuffer*) &(pPacket->pBuffers[i]);
+            pSaveBuff[i] = pPacket->pBuffers[i];
+            *pBuff = m_objMapper.remap_buffers(pPacket->pBuffers[i]);
+            if (*pBuff == VK_NULL_HANDLE && pPacket->pBuffers[i] != VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkCmdBindVertexBuffers() due to invalid remapped VkBuffer.");
+                VKTRACE_DELETE(pSaveBuff);
+                return;
+            }
+        }
+    }
+    m_vkFuncs.real_vkCmdBindVertexBuffers(remappedCommandBuffer, pPacket->firstBinding, pPacket->bindingCount, pPacket->pBuffers, pPacket->pOffsets);
+    for (uint32_t k = 0; k < i; k++)
+    {
+        VkBuffer *pBuff = (VkBuffer*) &(pPacket->pBuffers[k]);
+        *pBuff = pSaveBuff[k];
+    }
+    VKTRACE_DELETE(pSaveBuff);
+    return;
+}
+
+//VkResult vkReplay::manually_replay_vkCreateGraphicsPipeline(packet_vkCreateGraphicsPipeline* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+//    if (remappedDevice == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkCreateGraphicsPipeline() due to invalid remapped VkDevice.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    // remap shaders from each stage
+//    VkPipelineShaderStageCreateInfo* pRemappedStages = VKTRACE_NEW_ARRAY(VkPipelineShaderStageCreateInfo, pPacket->pCreateInfo->stageCount);
+//    memcpy(pRemappedStages, pPacket->pCreateInfo->pStages, sizeof(VkPipelineShaderStageCreateInfo) * pPacket->pCreateInfo->stageCount);
+//    for (uint32_t i = 0; i < pPacket->pCreateInfo->stageCount; i++)
+//    {
+//        pRemappedStages[i].shader = m_objMapper.remap(pRemappedStages[i].shader);
+//        if (pRemappedStages[i].shader == VK_NULL_HANDLE)
+//        {
+//            vktrace_LogError("Skipping vkCreateGraphicsPipeline() due to invalid remapped VkShader.");
+//            return VK_ERROR_VALIDATION_FAILED_EXT;
+//        }
+//    }
+//
+//    VkGraphicsPipelineCreateInfo createInfo = {
+//        .sType = pPacket->pCreateInfo->sType,
+//        .pNext = pPacket->pCreateInfo->pNext,
+//        .stageCount = pPacket->pCreateInfo->stageCount,
+//        .pStages = pRemappedStages,
+//        .pVertexInputState = pPacket->pCreateInfo->pVertexInputState,
+//        .pIaState = pPacket->pCreateInfo->pIaState,
+//        .pTessState = pPacket->pCreateInfo->pTessState,
+//        .pVpState = pPacket->pCreateInfo->pVpState,
+//        .pRsState = pPacket->pCreateInfo->pRsState,
+//        .pMsState = pPacket->pCreateInfo->pMsState,
+//        .pDsState = pPacket->pCreateInfo->pDsState,
+//        .pCbState = pPacket->pCreateInfo->pCbState,
+//        .flags = pPacket->pCreateInfo->flags,
+//        .layout = m_objMapper.remap(pPacket->pCreateInfo->layout)
+//    };
+//
+//    VkPipeline pipeline;
+//    replayResult = m_vkFuncs.real_vkCreateGraphicsPipeline(remappedDevice, &createInfo, &pipeline);
+//    if (replayResult == VK_SUCCESS)
+//    {
+//        m_objMapper.add_to_map(pPacket->pPipeline, &pipeline);
+//    }
+//
+//    VKTRACE_DELETE(pRemappedStages);
+//
+//    return replayResult;
+//}
+
+VkResult vkReplay::manually_replay_vkGetPipelineCacheData(packet_vkGetPipelineCacheData* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    size_t dataSize;
+    VkDevice remappeddevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappeddevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPipelineCacheData() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkPipelineCache remappedpipelineCache = m_objMapper.remap_pipelinecaches(pPacket->pipelineCache);
+    if (pPacket->pipelineCache != VK_NULL_HANDLE && remappedpipelineCache == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPipelineCacheData() due to invalid remapped VkPipelineCache.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    // Since the returned data size may not be equal to size of the buffer in the trace packet allocate a local buffer as needed
+    replayResult = m_vkFuncs.real_vkGetPipelineCacheData(remappeddevice, remappedpipelineCache, &dataSize, NULL);
+    if (replayResult != VK_SUCCESS)
+        return replayResult;
+    if (pPacket->pData) {
+        uint8_t *pData = VKTRACE_NEW_ARRAY(uint8_t, dataSize);
+        replayResult = m_vkFuncs.real_vkGetPipelineCacheData(remappeddevice, remappedpipelineCache, pPacket->pDataSize, pData);
+        VKTRACE_DELETE(pData);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateComputePipelines(packet_vkCreateComputePipelines* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkDevice remappeddevice = m_objMapper.remap_devices(pPacket->device);
+    uint32_t i;
+
+    if (pPacket->device != VK_NULL_HANDLE && remappeddevice == VK_NULL_HANDLE)
+    {
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkPipelineCache pipelineCache;
+    pipelineCache = m_objMapper.remap_pipelinecaches(pPacket->pipelineCache);
+
+    VkComputePipelineCreateInfo* pLocalCIs = VKTRACE_NEW_ARRAY(VkComputePipelineCreateInfo, pPacket->createInfoCount);
+    memcpy((void*)pLocalCIs, (void*)(pPacket->pCreateInfos), sizeof(VkComputePipelineCreateInfo)*pPacket->createInfoCount);
+
+    // Fix up stage sub-elements
+    for (i = 0; i<pPacket->createInfoCount; i++)
+    {
+        pLocalCIs[i].stage.module = m_objMapper.remap_shadermodules(pLocalCIs[i].stage.module);
+
+        if (pLocalCIs[i].stage.pName)
+            pLocalCIs[i].stage.pName = (const char*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)pLocalCIs[i].stage.pName));
+
+        if (pLocalCIs[i].stage.pSpecializationInfo)
+        {
+            VkSpecializationInfo* si = VKTRACE_NEW(VkSpecializationInfo);
+            pLocalCIs[i].stage.pSpecializationInfo = (const VkSpecializationInfo*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)pLocalCIs[i].stage.pSpecializationInfo));
+            memcpy((void*)si, (void*)(pLocalCIs[i].stage.pSpecializationInfo), sizeof(VkSpecializationInfo));
+
+            if (si->mapEntryCount > 0 && si->pMapEntries)
+                si->pMapEntries = (const VkSpecializationMapEntry*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)pLocalCIs[i].stage.pSpecializationInfo->pMapEntries));
+            if (si->dataSize > 0 && si->pData)
+                si->pData = (const void*)(vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)si->pData));
+            pLocalCIs[i].stage.pSpecializationInfo = si;
+        }
+
+        pLocalCIs[i].layout = m_objMapper.remap_pipelinelayouts(pLocalCIs[i].layout);
+        pLocalCIs[i].basePipelineHandle = m_objMapper.remap_pipelines(pLocalCIs[i].basePipelineHandle);
+    }
+
+    VkPipeline *local_pPipelines = VKTRACE_NEW_ARRAY(VkPipeline, pPacket->createInfoCount);
+
+    replayResult = m_vkFuncs.real_vkCreateComputePipelines(remappeddevice, pipelineCache, pPacket->createInfoCount, pLocalCIs, NULL, local_pPipelines);
+
+    if (replayResult == VK_SUCCESS)
+    {
+        for (i = 0; i < pPacket->createInfoCount; i++) {
+            m_objMapper.add_to_pipelines_map(pPacket->pPipelines[i], local_pPipelines[i]);
+        }
+    }
+
+    for (i = 0; i<pPacket->createInfoCount; i++)
+        if (pLocalCIs[i].stage.pSpecializationInfo)
+            VKTRACE_DELETE((void *)pLocalCIs[i].stage.pSpecializationInfo);
+    VKTRACE_DELETE(pLocalCIs);
+    VKTRACE_DELETE(local_pPipelines);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateGraphicsPipelines(packet_vkCreateGraphicsPipelines* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateGraphicsPipelines() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    // remap shaders from each stage
+    VkPipelineShaderStageCreateInfo* pRemappedStages = VKTRACE_NEW_ARRAY(VkPipelineShaderStageCreateInfo, pPacket->pCreateInfos->stageCount);
+    memcpy(pRemappedStages, pPacket->pCreateInfos->pStages, sizeof(VkPipelineShaderStageCreateInfo) * pPacket->pCreateInfos->stageCount);
+
+    VkGraphicsPipelineCreateInfo* pLocalCIs = VKTRACE_NEW_ARRAY(VkGraphicsPipelineCreateInfo, pPacket->createInfoCount);
+    uint32_t i, j;
+    for (i=0; i<pPacket->createInfoCount; i++) {
+        memcpy((void*)&(pLocalCIs[i]), (void*)&(pPacket->pCreateInfos[i]), sizeof(VkGraphicsPipelineCreateInfo));
+        for (j=0; j < pPacket->pCreateInfos[i].stageCount; j++)
+        {
+            pRemappedStages[j].module = m_objMapper.remap_shadermodules(pRemappedStages[j].module);
+            if (pRemappedStages[j].module == VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkCreateGraphicsPipelines() due to invalid remapped VkShaderModule.");
+                VKTRACE_DELETE(pRemappedStages);
+                VKTRACE_DELETE(pLocalCIs);
+                return VK_ERROR_VALIDATION_FAILED_EXT;
+            }
+        }
+        VkPipelineShaderStageCreateInfo** ppSSCI = (VkPipelineShaderStageCreateInfo**)&(pLocalCIs[i].pStages);
+        *ppSSCI = pRemappedStages;
+
+        pLocalCIs[i].layout = m_objMapper.remap_pipelinelayouts(pPacket->pCreateInfos[i].layout);
+        if (pLocalCIs[i].layout == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCreateGraphicsPipelines() due to invalid remapped VkPipelineLayout.");
+            VKTRACE_DELETE(pRemappedStages);
+            VKTRACE_DELETE(pLocalCIs);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+
+        pLocalCIs[i].renderPass = m_objMapper.remap_renderpasss(pPacket->pCreateInfos[i].renderPass);
+        if (pLocalCIs[i].renderPass == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCreateGraphicsPipelines() due to invalid remapped VkRenderPass.");
+            VKTRACE_DELETE(pRemappedStages);
+            VKTRACE_DELETE(pLocalCIs);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+
+        pLocalCIs[i].basePipelineHandle = m_objMapper.remap_pipelines(pPacket->pCreateInfos[i].basePipelineHandle);
+        if (pLocalCIs[i].basePipelineHandle == VK_NULL_HANDLE && pPacket->pCreateInfos[i].basePipelineHandle != VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCreateGraphicsPipelines() due to invalid remapped VkPipeline.");
+            VKTRACE_DELETE(pRemappedStages);
+            VKTRACE_DELETE(pLocalCIs);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+
+        ((VkPipelineViewportStateCreateInfo*)pLocalCIs[i].pViewportState)->pViewports =
+                (VkViewport*)vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)pPacket->pCreateInfos[i].pViewportState->pViewports);
+        ((VkPipelineViewportStateCreateInfo*)pLocalCIs[i].pViewportState)->pScissors =
+                (VkRect2D*)vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)pPacket->pCreateInfos[i].pViewportState->pScissors);
+
+        ((VkPipelineMultisampleStateCreateInfo*)pLocalCIs[i].pMultisampleState)->pSampleMask =
+                (VkSampleMask*)vktrace_trace_packet_interpret_buffer_pointer(pPacket->header, (intptr_t)pPacket->pCreateInfos[i].pMultisampleState->pSampleMask);
+    }
+
+    VkPipelineCache remappedPipelineCache;
+    remappedPipelineCache = m_objMapper.remap_pipelinecaches(pPacket->pipelineCache);
+    if (remappedPipelineCache == VK_NULL_HANDLE && pPacket->pipelineCache != VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateGraphicsPipelines() due to invalid remapped VkPipelineCache.");
+        VKTRACE_DELETE(pRemappedStages);
+        VKTRACE_DELETE(pLocalCIs);
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    uint32_t createInfoCount = pPacket->createInfoCount;
+    VkPipeline *local_pPipelines = VKTRACE_NEW_ARRAY(VkPipeline, pPacket->createInfoCount);
+
+    replayResult = m_vkFuncs.real_vkCreateGraphicsPipelines(remappedDevice, remappedPipelineCache, createInfoCount, pLocalCIs, NULL, local_pPipelines);
+
+    if (replayResult == VK_SUCCESS)
+    {
+        for (i = 0; i < pPacket->createInfoCount; i++) {
+            m_objMapper.add_to_pipelines_map(pPacket->pPipelines[i], local_pPipelines[i]);
+        }
+    }
+
+    VKTRACE_DELETE(pRemappedStages);
+    VKTRACE_DELETE(pLocalCIs);
+    VKTRACE_DELETE(local_pPipelines);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreatePipelineLayout(packet_vkCreatePipelineLayout* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+
+    // array to store the original trace-time layouts, so that we can remap them inside the packet and then
+    // restore them after replaying the API call.
+    VkDescriptorSetLayout* pSaveLayouts = NULL;
+    if (pPacket->pCreateInfo->setLayoutCount > 0) {
+        pSaveLayouts = (VkDescriptorSetLayout*) vktrace_malloc(sizeof(VkDescriptorSetLayout) * pPacket->pCreateInfo->setLayoutCount);
+        if (!pSaveLayouts) {
+            vktrace_LogError("Replay of CreatePipelineLayout out of memory.");
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+    }
+    uint32_t i = 0;
+    for (i = 0; (i < pPacket->pCreateInfo->setLayoutCount) && (pPacket->pCreateInfo->pSetLayouts != NULL); i++) {
+        VkDescriptorSetLayout* pSL = (VkDescriptorSetLayout*) &(pPacket->pCreateInfo->pSetLayouts[i]);
+        pSaveLayouts[i] = pPacket->pCreateInfo->pSetLayouts[i];
+        *pSL = m_objMapper.remap_descriptorsetlayouts(pPacket->pCreateInfo->pSetLayouts[i]);
+    }
+    VkPipelineLayout localPipelineLayout;
+    replayResult = m_vkFuncs.real_vkCreatePipelineLayout(remappedDevice, pPacket->pCreateInfo, NULL, &localPipelineLayout);
+    if (replayResult == VK_SUCCESS)
+    {
+        m_objMapper.add_to_pipelinelayouts_map(*(pPacket->pPipelineLayout), localPipelineLayout);
+    }
+    // restore packet to contain the original Set Layouts before being remapped.
+    for (uint32_t k = 0; k < i; k++) {
+        VkDescriptorSetLayout* pSL = (VkDescriptorSetLayout*) &(pPacket->pCreateInfo->pSetLayouts[k]);
+        *pSL = pSaveLayouts[k];
+    }
+    if (pSaveLayouts != NULL) {
+        vktrace_free(pSaveLayouts);
+    }
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkCmdWaitEvents(packet_vkCmdWaitEvents* pPacket)
+{
+    VkDevice traceDevice;
+    VkDevice replayDevice;
+    uint32_t srcReplayIdx, dstReplayIdx;
+    VkCommandBuffer remappedCommandBuffer = m_objMapper.remap_commandbuffers(pPacket->commandBuffer);
+    if (remappedCommandBuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdWaitEvents() due to invalid remapped VkCommandBuffer.");
+        return;
+    }
+
+    VkEvent* saveEvent = VKTRACE_NEW_ARRAY(VkEvent, pPacket->eventCount);
+    uint32_t idx = 0;
+    uint32_t numRemapBuf = 0;
+    uint32_t numRemapImg = 0;
+    for (idx = 0; idx < pPacket->eventCount; idx++)
+    {
+        VkEvent *pEvent = (VkEvent *) &(pPacket->pEvents[idx]);
+        saveEvent[idx] = pPacket->pEvents[idx];
+        *pEvent = m_objMapper.remap_events(pPacket->pEvents[idx]);
+        if (*pEvent == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCmdWaitEvents() due to invalid remapped VkEvent.");
+            VKTRACE_DELETE(saveEvent);
+            return;
+        }
+    }
+
+    VkBuffer* saveBuf = VKTRACE_NEW_ARRAY(VkBuffer, pPacket->bufferMemoryBarrierCount);
+    for (idx = 0; idx < pPacket->bufferMemoryBarrierCount; idx++)
+    {
+        VkBufferMemoryBarrier *pNextBuf = (VkBufferMemoryBarrier *)& (pPacket->pBufferMemoryBarriers[idx]);
+        saveBuf[numRemapBuf++] = pNextBuf->buffer;
+        traceDevice = traceBufferToDevice[pNextBuf->buffer];
+        pNextBuf->buffer = m_objMapper.remap_buffers(pNextBuf->buffer);
+        if (pNextBuf->buffer == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCmdWaitEvents() due to invalid remapped VkBuffer.");
+            VKTRACE_DELETE(saveEvent);
+            VKTRACE_DELETE(saveBuf);
+            return;
+        }
+        replayDevice = replayBufferToDevice[pNextBuf->buffer];
+        if (getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pBufferMemoryBarriers[idx].srcQueueFamilyIndex,
+                              &srcReplayIdx) &&
+            getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pBufferMemoryBarriers[idx].dstQueueFamilyIndex,
+                              &dstReplayIdx))
+        {
+            *((uint32_t *)&pPacket->pBufferMemoryBarriers[idx].srcQueueFamilyIndex) = srcReplayIdx;
+            *((uint32_t *)&pPacket->pBufferMemoryBarriers[idx].srcQueueFamilyIndex) = dstReplayIdx;
+        } else {
+            vktrace_LogError("vkCmdWaitEvents failed, bad srcQueueFamilyIndex");
+            VKTRACE_DELETE(saveEvent);
+            VKTRACE_DELETE(saveBuf);
+            return;
+        }
+    }
+    VkImage* saveImg = VKTRACE_NEW_ARRAY(VkImage, pPacket->imageMemoryBarrierCount);
+    for (idx = 0; idx < pPacket->imageMemoryBarrierCount; idx++)
+    {
+        VkImageMemoryBarrier *pNextImg = (VkImageMemoryBarrier *) &(pPacket->pImageMemoryBarriers[idx]);
+        saveImg[numRemapImg++] = pNextImg->image;
+        traceDevice = traceImageToDevice[pNextImg->image];
+        pNextImg->image = m_objMapper.remap_images(pNextImg->image);
+        if (pNextImg->image == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCmdWaitEvents() due to invalid remapped VkImage.");
+            VKTRACE_DELETE(saveEvent);
+            VKTRACE_DELETE(saveBuf);
+            VKTRACE_DELETE(saveImg);
+            return;
+        }
+        replayDevice = replayImageToDevice[pNextImg->image];
+        if (getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pImageMemoryBarriers[idx].srcQueueFamilyIndex,
+                              &srcReplayIdx) &&
+            getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pImageMemoryBarriers[idx].dstQueueFamilyIndex,
+                              &dstReplayIdx))
+        {
+            *((uint32_t *)&pPacket->pImageMemoryBarriers[idx].srcQueueFamilyIndex) = srcReplayIdx;
+            *((uint32_t *)&pPacket->pImageMemoryBarriers[idx].srcQueueFamilyIndex) = dstReplayIdx;
+        } else {
+            vktrace_LogError("vkCmdWaitEvents failed, bad srcQueueFamilyIndex");
+            VKTRACE_DELETE(saveEvent);
+            VKTRACE_DELETE(saveBuf);
+            VKTRACE_DELETE(saveImg);
+            return;
+        }
+    }
+    m_vkFuncs.real_vkCmdWaitEvents(remappedCommandBuffer, pPacket->eventCount, pPacket->pEvents, pPacket->srcStageMask, pPacket->dstStageMask, pPacket->memoryBarrierCount, pPacket->pMemoryBarriers, pPacket->bufferMemoryBarrierCount, pPacket->pBufferMemoryBarriers, pPacket->imageMemoryBarrierCount, pPacket->pImageMemoryBarriers);
+
+    for (idx = 0; idx < pPacket->bufferMemoryBarrierCount; idx++) {
+        VkBufferMemoryBarrier *pNextBuf = (VkBufferMemoryBarrier *) &(pPacket->pBufferMemoryBarriers[idx]);
+        pNextBuf->buffer = saveBuf[idx];
+    }
+    for (idx = 0; idx < pPacket->memoryBarrierCount; idx++) {
+        VkImageMemoryBarrier *pNextImg = (VkImageMemoryBarrier *) &(pPacket->pImageMemoryBarriers[idx]);
+        pNextImg->image = saveImg[idx];
+    }
+    for (idx = 0; idx < pPacket->eventCount; idx++) {
+        VkEvent *pEvent = (VkEvent *) &(pPacket->pEvents[idx]);
+        *pEvent = saveEvent[idx];
+    }
+    VKTRACE_DELETE(saveEvent);
+    VKTRACE_DELETE(saveBuf);
+    VKTRACE_DELETE(saveImg);
+    return;
+}
+
+void vkReplay::manually_replay_vkCmdPipelineBarrier(packet_vkCmdPipelineBarrier* pPacket)
+{
+    VkDevice traceDevice;
+    VkDevice replayDevice;
+    uint32_t srcReplayIdx, dstReplayIdx;
+    VkCommandBuffer remappedCommandBuffer = m_objMapper.remap_commandbuffers(pPacket->commandBuffer);
+    if (remappedCommandBuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdPipelineBarrier() due to invalid remapped VkCommandBuffer.");
+        return;
+    }
+
+    uint32_t idx = 0;
+    uint32_t numRemapBuf = 0;
+    uint32_t numRemapImg = 0;
+    VkBuffer* saveBuf = VKTRACE_NEW_ARRAY(VkBuffer, pPacket->bufferMemoryBarrierCount);
+    VkImage* saveImg = VKTRACE_NEW_ARRAY(VkImage, pPacket->imageMemoryBarrierCount);
+    for (idx = 0; idx < pPacket->bufferMemoryBarrierCount; idx++)
+    {
+        VkBufferMemoryBarrier *pNextBuf = (VkBufferMemoryBarrier *) &(pPacket->pBufferMemoryBarriers[idx]);
+        saveBuf[numRemapBuf++] = pNextBuf->buffer;
+        traceDevice = traceBufferToDevice[pNextBuf->buffer];
+        pNextBuf->buffer = m_objMapper.remap_buffers(pNextBuf->buffer);
+        if (pNextBuf->buffer == VK_NULL_HANDLE && saveBuf[numRemapBuf - 1] != VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCmdPipelineBarrier() due to invalid remapped VkBuffer.");
+            VKTRACE_DELETE(saveBuf);
+            VKTRACE_DELETE(saveImg);
+            return;
+        }
+        replayDevice = replayBufferToDevice[pNextBuf->buffer];
+        if (getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pBufferMemoryBarriers[idx].srcQueueFamilyIndex,
+                              &srcReplayIdx) &&
+            getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pBufferMemoryBarriers[idx].dstQueueFamilyIndex,
+                              &dstReplayIdx))
+        {
+            *((uint32_t *)&pPacket->pBufferMemoryBarriers[idx].srcQueueFamilyIndex) = srcReplayIdx;
+            *((uint32_t *)&pPacket->pBufferMemoryBarriers[idx].srcQueueFamilyIndex) = dstReplayIdx;
+        } else {
+            vktrace_LogError("vkCmdPipelineBarrier failed, bad srcQueueFamilyIndex");
+            VKTRACE_DELETE(saveBuf);
+            VKTRACE_DELETE(saveImg);
+            return;
+        }
+    }
+    for (idx = 0; idx < pPacket->imageMemoryBarrierCount; idx++)
+    {
+        VkImageMemoryBarrier *pNextImg = (VkImageMemoryBarrier *) &(pPacket->pImageMemoryBarriers[idx]);
+        saveImg[numRemapImg++] = pNextImg->image;
+        traceDevice = traceImageToDevice[pNextImg->image];
+        if (traceDevice == NULL)
+            vktrace_LogError("DEBUG: traceDevice is NULL");
+        pNextImg->image = m_objMapper.remap_images(pNextImg->image);
+        if (pNextImg->image == VK_NULL_HANDLE && saveImg[numRemapImg - 1] != VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkCmdPipelineBarrier() due to invalid remapped VkImage.");
+            VKTRACE_DELETE(saveBuf);
+            VKTRACE_DELETE(saveImg);
+            return;
+        }
+        replayDevice = replayImageToDevice[pNextImg->image];
+        if (getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pImageMemoryBarriers[idx].srcQueueFamilyIndex,
+                              &srcReplayIdx) &&
+            getQueueFamilyIdx(traceDevice,
+                              replayDevice,
+                              pPacket->pImageMemoryBarriers[idx].dstQueueFamilyIndex,
+                              &dstReplayIdx))
+        {
+            *((uint32_t *)&pPacket->pImageMemoryBarriers[idx].srcQueueFamilyIndex) = srcReplayIdx;
+            *((uint32_t *)&pPacket->pImageMemoryBarriers[idx].srcQueueFamilyIndex) = dstReplayIdx;
+        } else {
+            vktrace_LogError("vkPipelineBarrier failed, bad srcQueueFamilyIndex");
+            VKTRACE_DELETE(saveBuf);
+            VKTRACE_DELETE(saveImg);
+            return;
+        }
+    }
+    m_vkFuncs.real_vkCmdPipelineBarrier(remappedCommandBuffer, pPacket->srcStageMask, pPacket->dstStageMask, pPacket->dependencyFlags, pPacket->memoryBarrierCount, pPacket->pMemoryBarriers, pPacket->bufferMemoryBarrierCount, pPacket->pBufferMemoryBarriers, pPacket->imageMemoryBarrierCount, pPacket->pImageMemoryBarriers);
+
+    for (idx = 0; idx < pPacket->bufferMemoryBarrierCount; idx++) {
+        VkBufferMemoryBarrier *pNextBuf = (VkBufferMemoryBarrier *) &(pPacket->pBufferMemoryBarriers[idx]);
+        pNextBuf->buffer = saveBuf[idx];
+    }
+    for (idx = 0; idx < pPacket->imageMemoryBarrierCount; idx++) {
+        VkImageMemoryBarrier *pNextImg = (VkImageMemoryBarrier *) &(pPacket->pImageMemoryBarriers[idx]);
+        pNextImg->image = saveImg[idx];
+    }
+    VKTRACE_DELETE(saveBuf);
+    VKTRACE_DELETE(saveImg);
+    return;
+}
+
+VkResult vkReplay::manually_replay_vkCreateFramebuffer(packet_vkCreateFramebuffer* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateFramebuffer() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkFramebufferCreateInfo *pInfo = (VkFramebufferCreateInfo *) pPacket->pCreateInfo;
+    VkImageView *pAttachments, *pSavedAttachments = (VkImageView*)pInfo->pAttachments;
+    bool allocatedAttachments = false;
+    if (pSavedAttachments != NULL)
+    {
+        allocatedAttachments = true;
+        pAttachments = VKTRACE_NEW_ARRAY(VkImageView, pInfo->attachmentCount);
+        memcpy(pAttachments, pSavedAttachments, sizeof(VkImageView) * pInfo->attachmentCount);
+        for (uint32_t i = 0; i < pInfo->attachmentCount; i++)
+        {
+            pAttachments[i] = m_objMapper.remap_imageviews(pInfo->pAttachments[i]);
+            if (pAttachments[i] == VK_NULL_HANDLE && pInfo->pAttachments[i] != VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkCreateFramebuffer() due to invalid remapped VkImageView.");
+                VKTRACE_DELETE(pAttachments);
+                return VK_ERROR_VALIDATION_FAILED_EXT;
+            }
+        }
+        pInfo->pAttachments = pAttachments;
+    }
+    VkRenderPass savedRP = pPacket->pCreateInfo->renderPass;
+    pInfo->renderPass = m_objMapper.remap_renderpasss(pPacket->pCreateInfo->renderPass);
+    if (pInfo->renderPass == VK_NULL_HANDLE && pPacket->pCreateInfo->renderPass != VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateFramebuffer() due to invalid remapped VkRenderPass.");
+        if (allocatedAttachments)
+        {
+            VKTRACE_DELETE(pAttachments);
+        }
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkFramebuffer local_framebuffer;
+    replayResult = m_vkFuncs.real_vkCreateFramebuffer(remappedDevice, pPacket->pCreateInfo, NULL, &local_framebuffer);
+    pInfo->pAttachments = pSavedAttachments;
+    pInfo->renderPass = savedRP;
+    if (replayResult == VK_SUCCESS)
+    {
+        m_objMapper.add_to_framebuffers_map(*(pPacket->pFramebuffer), local_framebuffer);
+    }
+    if (allocatedAttachments)
+    {
+        VKTRACE_DELETE((void*)pAttachments);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateRenderPass(packet_vkCreateRenderPass* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateRenderPass() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkRenderPass local_renderpass;
+    replayResult = m_vkFuncs.real_vkCreateRenderPass(remappedDevice, pPacket->pCreateInfo, NULL, &local_renderpass);
+    if (replayResult == VK_SUCCESS)
+    {
+        m_objMapper.add_to_renderpasss_map(*(pPacket->pRenderPass), local_renderpass);
+    }
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkCmdBeginRenderPass(packet_vkCmdBeginRenderPass* pPacket)
+{
+    VkCommandBuffer remappedCommandBuffer = m_objMapper.remap_commandbuffers(pPacket->commandBuffer);
+    if (remappedCommandBuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdBeginRenderPass() due to invalid remapped VkCommandBuffer.");
+        return;
+    }
+    VkRenderPassBeginInfo local_renderPassBeginInfo;
+    memcpy((void*)&local_renderPassBeginInfo, (void*)pPacket->pRenderPassBegin, sizeof(VkRenderPassBeginInfo));
+    local_renderPassBeginInfo.pClearValues = (const VkClearValue*)pPacket->pRenderPassBegin->pClearValues;
+    local_renderPassBeginInfo.framebuffer = m_objMapper.remap_framebuffers(pPacket->pRenderPassBegin->framebuffer);
+    if (local_renderPassBeginInfo.framebuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdBeginRenderPass() due to invalid remapped VkFramebuffer.");
+        return;
+    }
+    local_renderPassBeginInfo.renderPass = m_objMapper.remap_renderpasss(pPacket->pRenderPassBegin->renderPass);
+    if (local_renderPassBeginInfo.renderPass == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCmdBeginRenderPass() due to invalid remapped VkRenderPass.");
+        return;
+    }
+    m_vkFuncs.real_vkCmdBeginRenderPass(remappedCommandBuffer, &local_renderPassBeginInfo, pPacket->contents);
+    return;
+}
+
+VkResult vkReplay::manually_replay_vkBeginCommandBuffer(packet_vkBeginCommandBuffer* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkCommandBuffer remappedCommandBuffer = m_objMapper.remap_commandbuffers(pPacket->commandBuffer);
+    if (remappedCommandBuffer == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkBeginCommandBuffer() due to invalid remapped VkCommandBuffer.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkCommandBufferBeginInfo* pInfo = (VkCommandBufferBeginInfo*)pPacket->pBeginInfo;
+    VkCommandBufferInheritanceInfo *pHinfo = (VkCommandBufferInheritanceInfo *) ((pInfo) ? pInfo->pInheritanceInfo : NULL);
+    // Save the original RP & FB, then overwrite packet with remapped values
+    VkRenderPass savedRP, *pRP;
+    VkFramebuffer savedFB, *pFB;
+    if (pInfo != NULL && pHinfo != NULL)
+    {
+        savedRP = pHinfo->renderPass;
+        savedFB = pHinfo->framebuffer;
+        pRP = &(pHinfo->renderPass);
+        pFB = &(pHinfo->framebuffer);
+        *pRP = m_objMapper.remap_renderpasss(savedRP);
+        *pFB = m_objMapper.remap_framebuffers(savedFB);
+    }
+    replayResult = m_vkFuncs.real_vkBeginCommandBuffer(remappedCommandBuffer, pPacket->pBeginInfo);
+    if (pInfo != NULL && pHinfo != NULL) {
+        pHinfo->renderPass = savedRP;
+        pHinfo->framebuffer = savedFB;
+    }
+    return replayResult;
+}
+
+// TODO138 : Can we kill this?
+//VkResult vkReplay::manually_replay_vkStorePipeline(packet_vkStorePipeline* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+//    if (remappedDevice == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkStorePipeline() due to invalid remapped VkDevice.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    VkPipeline remappedPipeline = m_objMapper.remap(pPacket->pipeline);
+//    if (remappedPipeline == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkStorePipeline() due to invalid remapped VkPipeline.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    size_t size = 0;
+//    void* pData = NULL;
+//    if (pPacket->pData != NULL && pPacket->pDataSize != NULL)
+//    {
+//        size = *pPacket->pDataSize;
+//        pData = vktrace_malloc(*pPacket->pDataSize);
+//    }
+//    replayResult = m_vkFuncs.real_vkStorePipeline(remappedDevice, remappedPipeline, &size, pData);
+//    if (replayResult == VK_SUCCESS)
+//    {
+//        if (size != *pPacket->pDataSize && pData != NULL)
+//        {
+//            vktrace_LogWarning("vkStorePipeline returned a differing data size: replay (%d bytes) vs trace (%d bytes).", size, *pPacket->pDataSize);
+//        }
+//        else if (pData != NULL && memcmp(pData, pPacket->pData, size) != 0)
+//        {
+//            vktrace_LogWarning("vkStorePipeline returned differing data contents than the trace file contained.");
+//        }
+//    }
+//    vktrace_free(pData);
+//    return replayResult;
+//}
+
+// TODO138 : This needs to be broken out into separate functions for each non-disp object
+//VkResult vkReplay::manually_replay_vkDestroy<Object>(packet_vkDestroyObject* pPacket)
+//{
+//    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+//
+//    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+//    if (remappedDevice == VK_NULL_HANDLE)
+//    {
+//        vktrace_LogError("Skipping vkDestroy() due to invalid remapped VkDevice.");
+//        return VK_ERROR_VALIDATION_FAILED_EXT;
+//    }
+//
+//    uint64_t remapHandle = m_objMapper.remap_<OBJECT_TYPE_HERE>(pPacket->object, pPacket->objType);
+//    <VkObject> object;
+//    object.handle = remapHandle;
+//    if (object != 0)
+//        replayResult = m_vkFuncs.real_vkDestroy<Object>(remappedDevice, object);
+//    if (replayResult == VK_SUCCESS)
+//        m_objMapper.rm_from_<OBJECT_TYPE_HERE>_map(pPacket->object.handle);
+//    return replayResult;
+//}
+
+VkResult vkReplay::manually_replay_vkWaitForFences(packet_vkWaitForFences* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    uint32_t i;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkWaitForFences() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkFence *pFence = VKTRACE_NEW_ARRAY(VkFence, pPacket->fenceCount);
+    for (i = 0; i < pPacket->fenceCount; i++)
+    {
+        (*(pFence + i)) = m_objMapper.remap_fences((*(pPacket->pFences + i)));
+        if (*(pFence + i) == VK_NULL_HANDLE)
+        {
+            vktrace_LogError("Skipping vkWaitForFences() due to invalid remapped VkFence.");
+            VKTRACE_DELETE(pFence);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+    }
+    if (pPacket->result == VK_SUCCESS)
+    {
+        replayResult = m_vkFuncs.real_vkWaitForFences(remappedDevice, pPacket->fenceCount, pFence, pPacket->waitAll, UINT64_MAX);// mean as long as possible
+    }
+    else
+    {
+        if (pPacket->result == VK_TIMEOUT)
+        {
+            replayResult = m_vkFuncs.real_vkWaitForFences(remappedDevice, pPacket->fenceCount, pFence, pPacket->waitAll, 0);
+        }
+        else
+        {
+            replayResult = m_vkFuncs.real_vkWaitForFences(remappedDevice, pPacket->fenceCount, pFence, pPacket->waitAll, pPacket->timeout);
+        }
+    }
+    VKTRACE_DELETE(pFence);
+    return replayResult;
+}
+
+bool vkReplay::getMemoryTypeIdx(VkDevice traceDevice,
+                                VkDevice replayDevice,
+                                uint32_t traceIdx,
+                                uint32_t* pReplayIdx)
+{
+    VkPhysicalDevice tracePhysicalDevice;
+    VkPhysicalDevice replayPhysicalDevice;
+    bool foundMatch = false;
+    uint32_t i,j;
+
+    if (tracePhysicalDevices.find(traceDevice) == tracePhysicalDevices.end() ||
+        replayPhysicalDevices.find(replayDevice) == replayPhysicalDevices.end())
+    {
+        goto fail;
+    }
+
+    tracePhysicalDevice = tracePhysicalDevices[traceDevice];
+    replayPhysicalDevice = replayPhysicalDevices[replayDevice];
+
+    if (min(traceMemoryProperties[tracePhysicalDevice].memoryTypeCount, replayMemoryProperties[replayPhysicalDevice].memoryTypeCount) == 0)
+    {
+        goto fail;
+    }
+
+    for (i = 0; i < min(traceMemoryProperties[tracePhysicalDevice].memoryTypeCount, replayMemoryProperties[replayPhysicalDevice].memoryTypeCount); i++)
+    {
+        if (traceMemoryProperties[tracePhysicalDevice].memoryTypes[traceIdx].propertyFlags == replayMemoryProperties[replayPhysicalDevice].memoryTypes[i].propertyFlags)
+        {
+            *pReplayIdx = i;
+            foundMatch = true;
+            break;
+        }
+    }
+
+    if (!foundMatch)
+    {
+        // Didn't find an exact match, search for a superset
+        for (i = 0; i < min(traceMemoryProperties[tracePhysicalDevice].memoryTypeCount, replayMemoryProperties[replayPhysicalDevice].memoryTypeCount); i++)
+        {
+            if (traceMemoryProperties[tracePhysicalDevice].memoryTypes[traceIdx].propertyFlags ==
+                (traceMemoryProperties[tracePhysicalDevice].memoryTypes[traceIdx].propertyFlags & replayMemoryProperties[replayPhysicalDevice].memoryTypes[i].propertyFlags))
+            {
+                *pReplayIdx = i;
+                foundMatch = true;
+                break;
+            }
+        }
+    }
+
+    if (!foundMatch)
+    {
+        // Didn't find a superset, search for mem type with both HOST_VISIBLE and HOST_COHERENT set
+        for (i = 0; i < replayMemoryProperties[replayPhysicalDevice].memoryTypeCount; i++)
+        {
+            if ((VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) & replayMemoryProperties[replayPhysicalDevice].memoryTypes[i].propertyFlags)
+            {
+                *pReplayIdx = i;
+                foundMatch = true;
+                break;
+            }
+        }
+    }
+
+    if (foundMatch)
+    {
+        // Check to see if there are other replayMemoryProperties identical to the one that matched.
+        // If there are, print a warning and use the index from the trace file.
+        for (j = i+1; j < replayMemoryProperties[replayPhysicalDevice].memoryTypeCount; j++)
+        {
+            if (replayMemoryProperties[replayPhysicalDevice].memoryTypes[i].propertyFlags == replayMemoryProperties[replayPhysicalDevice].memoryTypes[j].propertyFlags)
+            {
+                vktrace_LogWarning("memoryTypes propertyFlags identical in two or more entries, using idx %d from trace", traceIdx);
+                *pReplayIdx = traceIdx;
+                return true;
+            }
+         }
+        return true;
+     }
+
+fail:
+    // Didn't find a match
+    vktrace_LogError("Cannot determine memory type during vkAllocateMemory - vkGetPhysicalDeviceMemoryProperties should be called before vkAllocateMemory.");
+    return false;
+}
+
+
+VkResult vkReplay::manually_replay_vkAllocateMemory(packet_vkAllocateMemory* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    gpuMemObj local_mem;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkAllocateMemory() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    if (pPacket->pAllocateInfo->pNext)
+    {
+		VkDedicatedAllocationMemoryAllocateInfoNV* x = (VkDedicatedAllocationMemoryAllocateInfoNV*)(pPacket->pAllocateInfo->pNext);
+
+		if (x->sType == VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV)
+       {
+            x->image = m_objMapper.remap_images(x->image);
+            x->buffer = m_objMapper.remap_buffers(x->buffer);
+       }
+    }
+
+    if (!m_objMapper.m_adjustForGPU)
+    {
+        uint32_t replayIdx;
+        if (getMemoryTypeIdx(pPacket->device, remappedDevice, pPacket->pAllocateInfo->memoryTypeIndex, &replayIdx))
+        {
+            *((uint32_t*)&pPacket->pAllocateInfo->memoryTypeIndex) = replayIdx;
+            replayResult = m_vkFuncs.real_vkAllocateMemory(remappedDevice, pPacket->pAllocateInfo, NULL, &local_mem.replayGpuMem);
+        } else {
+            vktrace_LogError("vkAllocateMemory() failed, couldn't find memory type for memoryTypeIndex");
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+    }
+    if (replayResult == VK_SUCCESS || m_objMapper.m_adjustForGPU)
+    {
+        local_mem.pGpuMem = new (gpuMemory);
+        if (local_mem.pGpuMem)
+            local_mem.pGpuMem->setAllocInfo(pPacket->pAllocateInfo, m_objMapper.m_adjustForGPU);
+        m_objMapper.add_to_devicememorys_map(*(pPacket->pMemory), local_mem);
+    }
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkFreeMemory(packet_vkFreeMemory* pPacket)
+{
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkFreeMemory() due to invalid remapped VkDevice.");
+        return;
+    }
+
+    gpuMemObj local_mem;
+    local_mem = m_objMapper.m_devicememorys.find(pPacket->memory)->second;
+    // TODO how/when to free pendingAlloc that did not use and existing gpuMemObj
+    m_vkFuncs.real_vkFreeMemory(remappedDevice, local_mem.replayGpuMem, NULL);
+    delete local_mem.pGpuMem;
+    m_objMapper.rm_from_devicememorys_map(pPacket->memory);
+}
+
+VkResult vkReplay::manually_replay_vkMapMemory(packet_vkMapMemory* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkMapMemory() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    gpuMemObj local_mem = m_objMapper.m_devicememorys.find(pPacket->memory)->second;
+    void* pData;
+    if (!local_mem.pGpuMem->isPendingAlloc())
+    {
+        replayResult = m_vkFuncs.real_vkMapMemory(remappedDevice, local_mem.replayGpuMem, pPacket->offset, pPacket->size, pPacket->flags, &pData);
+        if (replayResult == VK_SUCCESS)
+        {
+            if (local_mem.pGpuMem)
+            {
+                local_mem.pGpuMem->setMemoryMapRange(pData, (size_t)pPacket->size, (size_t)pPacket->offset, false);
+            }
+        }
+    }
+    else
+    {
+        if (local_mem.pGpuMem)
+        {
+            local_mem.pGpuMem->setMemoryMapRange(NULL, (size_t)pPacket->size, (size_t)pPacket->offset, true);
+        }
+    }
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkUnmapMemory(packet_vkUnmapMemory* pPacket)
+{
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE) {
+        vktrace_LogError("Skipping vkUnmapMemory() due to invalid remapped VkDevice.");
+        return;
+    }
+
+    gpuMemObj local_mem = m_objMapper.m_devicememorys.find(pPacket->memory)->second;
+    if (!local_mem.pGpuMem->isPendingAlloc())
+    {
+        if (local_mem.pGpuMem)
+        {
+            if (pPacket->pData)
+                local_mem.pGpuMem->copyMappingData(pPacket->pData, true, 0, 0);  // copies data from packet into memory buffer
+        }
+        m_vkFuncs.real_vkUnmapMemory(remappedDevice, local_mem.replayGpuMem);
+    }
+    else
+    {
+        if (local_mem.pGpuMem)
+        {
+            unsigned char *pBuf = (unsigned char *) vktrace_malloc(local_mem.pGpuMem->getMemoryMapSize());
+            if (!pBuf)
+            {
+                vktrace_LogError("vkUnmapMemory() malloc failed.");
+            }
+            local_mem.pGpuMem->setMemoryDataAddr(pBuf);
+            local_mem.pGpuMem->copyMappingData(pPacket->pData, true, 0, 0);
+        }
+    }
+}
+
+BOOL isvkFlushMappedMemoryRangesSpecial(PBYTE pOPTPackageData)
+{
+    BOOL bRet = FALSE;
+    PageGuardChangedBlockInfo *pChangedInfoArray = (PageGuardChangedBlockInfo *)pOPTPackageData;
+    if (((uint64_t)pChangedInfoArray[0].reserve0) & PAGEGUARD_SPECIAL_FORMAT_PACKET_FOR_VKFLUSHMAPPEDMEMORYRANGES) // TODO need think about 32bit
+    {
+        bRet = TRUE;
+    }
+    return bRet;
+}
+
+//after OPT speed up, the format of this packet will be different with before, the packet now only include changed block(page).
+//
+VkResult vkReplay::manually_replay_vkFlushMappedMemoryRanges(packet_vkFlushMappedMemoryRanges* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkFlushMappedMemoryRanges() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkMappedMemoryRange* localRanges = VKTRACE_NEW_ARRAY(VkMappedMemoryRange, pPacket->memoryRangeCount);
+    memcpy(localRanges, pPacket->pMemoryRanges, sizeof(VkMappedMemoryRange) * (pPacket->memoryRangeCount));
+
+    gpuMemObj* pLocalMems = VKTRACE_NEW_ARRAY(gpuMemObj, pPacket->memoryRangeCount);
+    for (uint32_t i = 0; i < pPacket->memoryRangeCount; i++)
+    {
+        pLocalMems[i] = m_objMapper.m_devicememorys.find(pPacket->pMemoryRanges[i].memory)->second;
+        localRanges[i].memory = m_objMapper.remap_devicememorys(pPacket->pMemoryRanges[i].memory);
+        if (localRanges[i].memory == VK_NULL_HANDLE || pLocalMems[i].pGpuMem == NULL)
+        {
+            vktrace_LogError("Skipping vkFlushMappedMemoryRanges() due to invalid remapped VkDeviceMemory.");
+            VKTRACE_DELETE(localRanges);
+            VKTRACE_DELETE(pLocalMems);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+
+        if (!pLocalMems[i].pGpuMem->isPendingAlloc())
+        {
+            if (pPacket->pMemoryRanges[i].size != 0)
+            {
+#ifdef USE_PAGEGUARD_SPEEDUP
+                if(vktrace_check_min_version(VKTRACE_TRACE_FILE_VERSION_5))
+                    pLocalMems[i].pGpuMem->copyMappingDataPageGuard(pPacket->ppData[i]);
+                else
+                    pLocalMems[i].pGpuMem->copyMappingData(pPacket->ppData[i], false, (size_t)pPacket->pMemoryRanges[i].size, (size_t)pPacket->pMemoryRanges[i].offset);
+#else
+                pLocalMems[i].pGpuMem->copyMappingData(pPacket->ppData[i], false, (size_t)pPacket->pMemoryRanges[i].size, (size_t)pPacket->pMemoryRanges[i].offset);
+#endif
+            }
+        }
+        else
+        {
+            unsigned char *pBuf = (unsigned char *) vktrace_malloc(pLocalMems[i].pGpuMem->getMemoryMapSize());
+            if (!pBuf)
+            {
+                vktrace_LogError("vkFlushMappedMemoryRanges() malloc failed.");
+            }
+            pLocalMems[i].pGpuMem->setMemoryDataAddr(pBuf);
+#ifdef USE_PAGEGUARD_SPEEDUP
+            if(vktrace_check_min_version(VKTRACE_TRACE_FILE_VERSION_5))
+                pLocalMems[i].pGpuMem->copyMappingDataPageGuard(pPacket->ppData[i]);
+            else
+                pLocalMems[i].pGpuMem->copyMappingData(pPacket->ppData[i], false, (size_t)pPacket->pMemoryRanges[i].size, (size_t)pPacket->pMemoryRanges[i].offset);
+#else
+            pLocalMems[i].pGpuMem->copyMappingData(pPacket->ppData[i], false, (size_t)pPacket->pMemoryRanges[i].size, (size_t)pPacket->pMemoryRanges[i].offset);
+#endif
+        }
+    }
+
+#ifdef USE_PAGEGUARD_SPEEDUP
+    replayResult = pPacket->result;//if this is a OPT refresh-all packet, we need avoid to call real api and return original return to avoid error message;
+    if (!vktrace_check_min_version(VKTRACE_TRACE_FILE_VERSION_5) || !isvkFlushMappedMemoryRangesSpecial((PBYTE)pPacket->ppData[0]))
+#endif
+    {
+        replayResult = m_vkFuncs.real_vkFlushMappedMemoryRanges(remappedDevice, pPacket->memoryRangeCount, localRanges);
+    }
+
+    VKTRACE_DELETE(localRanges);
+    VKTRACE_DELETE(pLocalMems);
+
+    return replayResult;
+}
+
+//InvalidateMappedMemory Ranges and flushMappedMemoryRanges are similar but keep it seperate until
+//functionality tested fully
+VkResult vkReplay::manually_replay_vkInvalidateMappedMemoryRanges(packet_vkInvalidateMappedMemoryRanges* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkInvalidateMappedMemoryRanges() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkMappedMemoryRange* localRanges = VKTRACE_NEW_ARRAY(VkMappedMemoryRange, pPacket->memoryRangeCount);
+    memcpy(localRanges, pPacket->pMemoryRanges, sizeof(VkMappedMemoryRange) * (pPacket->memoryRangeCount));
+
+    gpuMemObj* pLocalMems = VKTRACE_NEW_ARRAY(gpuMemObj, pPacket->memoryRangeCount);
+    for (uint32_t i = 0; i < pPacket->memoryRangeCount; i++)
+    {
+        pLocalMems[i] = m_objMapper.m_devicememorys.find(pPacket->pMemoryRanges[i].memory)->second;
+        localRanges[i].memory = m_objMapper.remap_devicememorys(pPacket->pMemoryRanges[i].memory);
+        if (localRanges[i].memory == VK_NULL_HANDLE || pLocalMems[i].pGpuMem == NULL)
+        {
+            vktrace_LogError("Skipping vkInvalidsateMappedMemoryRanges() due to invalid remapped VkDeviceMemory.");
+            VKTRACE_DELETE(localRanges);
+            VKTRACE_DELETE(pLocalMems);
+            return VK_ERROR_VALIDATION_FAILED_EXT;
+        }
+
+        if (!pLocalMems[i].pGpuMem->isPendingAlloc())
+        {
+            if (pPacket->pMemoryRanges[i].size != 0)
+            {
+                pLocalMems[i].pGpuMem->copyMappingData(pPacket->ppData[i], false, (size_t)pPacket->pMemoryRanges[i].size, (size_t)pPacket->pMemoryRanges[i].offset);
+            }
+        }
+        else
+        {
+            unsigned char *pBuf = (unsigned char *) vktrace_malloc(pLocalMems[i].pGpuMem->getMemoryMapSize());
+            if (!pBuf)
+            {
+                vktrace_LogError("vkInvalidateMappedMemoryRanges() malloc failed.");
+            }
+            pLocalMems[i].pGpuMem->setMemoryDataAddr(pBuf);
+            pLocalMems[i].pGpuMem->copyMappingData(pPacket->ppData[i], false, (size_t)pPacket->pMemoryRanges[i].size, (size_t)pPacket->pMemoryRanges[i].offset);
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkInvalidateMappedMemoryRanges(remappedDevice, pPacket->memoryRangeCount, localRanges);
+
+    VKTRACE_DELETE(localRanges);
+    VKTRACE_DELETE(pLocalMems);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkGetPhysicalDeviceSurfaceSupportKHR(packet_vkGetPhysicalDeviceSurfaceSupportKHR* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfaceSupportKHR() due to invalid remapped VkPhysicalDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSurfaceKHR remappedSurfaceKHR = m_objMapper.remap_surfacekhrs(pPacket->surface);
+    if (remappedSurfaceKHR == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfaceSupportKHR() due to invalid remapped VkSurfaceKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    replayResult = m_vkFuncs.real_vkGetPhysicalDeviceSurfaceSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex, remappedSurfaceKHR, pPacket->pSupported);
+
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkGetPhysicalDeviceMemoryProperties(packet_vkGetPhysicalDeviceMemoryProperties* pPacket)
+{
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceMemoryProperties() due to invalid remapped VkPhysicalDevice.");
+        return;
+    }
+
+    traceMemoryProperties[pPacket->physicalDevice] = *(pPacket->pMemoryProperties);
+    m_vkFuncs.real_vkGetPhysicalDeviceMemoryProperties(remappedphysicalDevice, pPacket->pMemoryProperties);
+    replayMemoryProperties[remappedphysicalDevice] = *(pPacket->pMemoryProperties);
+    return;
+}
+
+void vkReplay::manually_replay_vkGetPhysicalDeviceQueueFamilyProperties(packet_vkGetPhysicalDeviceQueueFamilyProperties* pPacket)
+{
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (pPacket->physicalDevice != VK_NULL_HANDLE && remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceQueueFamilyProperties() due to invalid remapped VkPhysicalDevice.");
+        return;
+    }
+
+    // If we haven't previously allocated queueFamilyProperties for the trace physical device, allocate it.
+    // If we previously allocated queueFamilyProperities for the trace physical device and the size of this
+    // query is larger than what we saved last time, then free the last properties map and allocate a new map.
+    if (traceQueueFamilyProperties.find(pPacket->physicalDevice) == traceQueueFamilyProperties.end() ||
+        *pPacket->pQueueFamilyPropertyCount > traceQueueFamilyProperties[pPacket->physicalDevice].count)
+    {
+        if (traceQueueFamilyProperties.find(pPacket->physicalDevice) != traceQueueFamilyProperties.end())
+        {
+            free(traceQueueFamilyProperties[pPacket->physicalDevice].queueFamilyProperties);
+            traceQueueFamilyProperties.erase(pPacket->physicalDevice);
+        }
+        if (pPacket->pQueueFamilyProperties)
+        {
+            traceQueueFamilyProperties[pPacket->physicalDevice].queueFamilyProperties =
+                (VkQueueFamilyProperties*)malloc(*pPacket->pQueueFamilyPropertyCount * sizeof(VkQueueFamilyProperties));
+            memcpy(traceQueueFamilyProperties[pPacket->physicalDevice].queueFamilyProperties,
+                   pPacket->pQueueFamilyProperties,
+                   *pPacket->pQueueFamilyPropertyCount * sizeof(VkQueueFamilyProperties));
+            traceQueueFamilyProperties[pPacket->physicalDevice].count = *pPacket->pQueueFamilyPropertyCount;
+        }
+    }
+
+    m_vkFuncs.real_vkGetPhysicalDeviceQueueFamilyProperties(remappedphysicalDevice, pPacket->pQueueFamilyPropertyCount, pPacket->pQueueFamilyProperties);
+
+    // If we haven't previously allocated queueFamilyProperties for the replay physical device, allocate it.
+    // If we previously allocated queueFamilyProperities for the replay physical device and the size of this
+    // query is larger than what we saved last time, then free the last properties map and allocate a new map.
+    if (replayQueueFamilyProperties.find(remappedphysicalDevice) == replayQueueFamilyProperties.end() ||
+        *pPacket->pQueueFamilyPropertyCount > replayQueueFamilyProperties[remappedphysicalDevice].count)
+    {
+        if (replayQueueFamilyProperties.find(remappedphysicalDevice) != replayQueueFamilyProperties.end())
+        {
+            free(replayQueueFamilyProperties[remappedphysicalDevice].queueFamilyProperties);
+            replayQueueFamilyProperties.erase(remappedphysicalDevice);
+        }
+        if (pPacket->pQueueFamilyProperties)
+        {
+            replayQueueFamilyProperties[remappedphysicalDevice].queueFamilyProperties =
+                (VkQueueFamilyProperties*)malloc(*pPacket->pQueueFamilyPropertyCount * sizeof(VkQueueFamilyProperties));
+            memcpy(replayQueueFamilyProperties[remappedphysicalDevice].queueFamilyProperties,
+                   pPacket->pQueueFamilyProperties,
+                   *pPacket->pQueueFamilyPropertyCount * sizeof(VkQueueFamilyProperties));
+            replayQueueFamilyProperties[remappedphysicalDevice].count = *pPacket->pQueueFamilyPropertyCount;
+        }
+    }
+
+
+    return;
+}
+
+VkResult vkReplay::manually_replay_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(packet_vkGetPhysicalDeviceSurfaceCapabilitiesKHR* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfaceCapabilitiesKHR() due to invalid remapped VkPhysicalDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSurfaceKHR remappedSurfaceKHR = m_objMapper.remap_surfacekhrs(pPacket->surface);
+    if (remappedSurfaceKHR == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfaceCapabilitiesKHR() due to invalid remapped VkSurfaceKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    m_display->resize_window(pPacket->pSurfaceCapabilities->currentExtent.width, pPacket->pSurfaceCapabilities->currentExtent.height);
+
+    replayResult = m_vkFuncs.real_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(remappedphysicalDevice, remappedSurfaceKHR, pPacket->pSurfaceCapabilities);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkGetPhysicalDeviceSurfaceFormatsKHR(packet_vkGetPhysicalDeviceSurfaceFormatsKHR* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfaceFormatsKHR() due to invalid remapped VkPhysicalDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSurfaceKHR remappedSurfaceKHR = m_objMapper.remap_surfacekhrs(pPacket->surface);
+    if (remappedSurfaceKHR == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfaceFormatsKHR() due to invalid remapped VkSurfaceKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    replayResult = m_vkFuncs.real_vkGetPhysicalDeviceSurfaceFormatsKHR(remappedphysicalDevice, remappedSurfaceKHR, pPacket->pSurfaceFormatCount, pPacket->pSurfaceFormats);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkGetPhysicalDeviceSurfacePresentModesKHR(packet_vkGetPhysicalDeviceSurfacePresentModesKHR* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfacePresentModesKHR() due to invalid remapped VkPhysicalDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSurfaceKHR remappedSurfaceKHR = m_objMapper.remap_surfacekhrs(pPacket->surface);
+    if (remappedSurfaceKHR == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetPhysicalDeviceSurfacePresentModesKHR() due to invalid remapped VkSurfaceKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    replayResult = m_vkFuncs.real_vkGetPhysicalDeviceSurfacePresentModesKHR(remappedphysicalDevice, remappedSurfaceKHR, pPacket->pPresentModeCount, pPacket->pPresentModes);
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateSwapchainKHR(packet_vkCreateSwapchainKHR* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkSwapchainKHR local_pSwapchain;
+    VkSwapchainKHR save_oldSwapchain, *pSC;
+    VkSurfaceKHR save_surface;
+    pSC = (VkSwapchainKHR *) &pPacket->pCreateInfo->oldSwapchain;
+    VkDevice remappeddevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappeddevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateSwapchainKHR() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    save_oldSwapchain = pPacket->pCreateInfo->oldSwapchain;
+    (*pSC) = m_objMapper.remap_swapchainkhrs(save_oldSwapchain);
+    if ((*pSC) == VK_NULL_HANDLE && save_oldSwapchain != VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateSwapchainKHR() due to invalid remapped VkSwapchainKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    save_surface = pPacket->pCreateInfo->surface;
+    VkSurfaceKHR *pSurf = (VkSurfaceKHR *) &(pPacket->pCreateInfo->surface);
+    *pSurf = m_objMapper.remap_surfacekhrs(*pSurf);
+    if (*pSurf == VK_NULL_HANDLE && pPacket->pCreateInfo->surface != VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateSwapchainKHR() due to invalid remapped VkSurfaceKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    m_display->resize_window(pPacket->pCreateInfo->imageExtent.width, pPacket->pCreateInfo->imageExtent.height);
+
+    // Convert queueFamilyIndices
+    if (pPacket->pCreateInfo)
+    {
+        for (uint32_t i = 0; i < pPacket->pCreateInfo->queueFamilyIndexCount; i++)
+        {
+            uint32_t replayIdx;
+            if (pPacket->pCreateInfo->pQueueFamilyIndices &&
+                getQueueFamilyIdx(pPacket->device,
+                                  remappeddevice,
+                                  pPacket->pCreateInfo->pQueueFamilyIndices[i],
+                                  &replayIdx))
+                {
+                    *((uint32_t*)&pPacket->pCreateInfo->pQueueFamilyIndices[i]) = replayIdx;
+                }
+            else {
+                vktrace_LogError("vkSwapchainCreateInfoKHR, bad queueFamilyIndex");
+                return VK_ERROR_VALIDATION_FAILED_EXT;
+            }
+        }
+    }
+
+    // Get the list of VkFormats that are supported:
+    VkPhysicalDevice remappedPhysicalDevice = replayPhysicalDevices[remappeddevice];
+    uint32_t formatCount;
+    VkResult res;
+    // Note that pPacket->pCreateInfo->surface has been remapped above
+    res = vkGetPhysicalDeviceSurfaceFormatsKHR(remappedPhysicalDevice, pPacket->pCreateInfo->surface,
+                                               &formatCount, NULL);
+    assert(!res);
+    VkSurfaceFormatKHR *surfFormats =
+        (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
+    assert(surfFormats);
+    res = vkGetPhysicalDeviceSurfaceFormatsKHR(remappedPhysicalDevice, pPacket->pCreateInfo->surface,
+                                               &formatCount, surfFormats);
+    assert(!res);
+    // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
+    // the surface has no preferred format.  Otherwise, at least one
+    // supported format will be returned.
+    if (!(formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) {
+        bool found = false;
+        for (uint32_t i = 0; i < formatCount; i++) {
+            if (pPacket->pCreateInfo->imageFormat == surfFormats[i].format) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            vktrace_LogWarning("Format %d is not supported for presentable images, using format %d",
+                               pPacket->pCreateInfo->imageFormat, surfFormats[0].format);
+            VkFormat *pFormat = (VkFormat *) &(pPacket->pCreateInfo->imageFormat);
+            *pFormat = surfFormats[0].format;
+        }
+    }
+    free(surfFormats);
+
+    replayResult = m_vkFuncs.real_vkCreateSwapchainKHR(remappeddevice, pPacket->pCreateInfo, pPacket->pAllocator, &local_pSwapchain);
+    if (replayResult == VK_SUCCESS)
+    {
+        m_objMapper.add_to_swapchainkhrs_map(*(pPacket->pSwapchain), local_pSwapchain);
+    }
+
+    (*pSC) = save_oldSwapchain;
+    *pSurf = save_surface;
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkGetSwapchainImagesKHR(packet_vkGetSwapchainImagesKHR* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkDevice remappeddevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappeddevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkGetSwapchainImagesKHR() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSwapchainKHR remappedswapchain;
+    remappedswapchain = m_objMapper.remap_swapchainkhrs(pPacket->swapchain);
+    if (remappedswapchain == VK_NULL_HANDLE && pPacket->swapchain != VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateSwapchainKHR() due to invalid remapped VkSwapchainKHR.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkImage packetImage[128] = {0};
+    uint32_t numImages = 0;
+    if (pPacket->pSwapchainImages != NULL) {
+        // Need to store the images and then add to map after we get actual image handles back
+        VkImage* pPacketImages = (VkImage*)pPacket->pSwapchainImages;
+        numImages = *(pPacket->pSwapchainImageCount);
+        for (uint32_t i = 0; i < numImages; i++) {
+            packetImage[i] = pPacketImages[i];
+            traceImageToDevice[packetImage[i]] = pPacket->device;
+        }
+    }
+
+    replayResult = m_vkFuncs.real_vkGetSwapchainImagesKHR(remappeddevice, remappedswapchain, pPacket->pSwapchainImageCount, pPacket->pSwapchainImages);
+    if (replayResult == VK_SUCCESS)
+    {
+        if (numImages != 0) {
+            VkImage* pReplayImages = (VkImage*)pPacket->pSwapchainImages;
+            for (uint32_t i = 0; i < numImages; i++) {
+                imageObj local_imageObj;
+                local_imageObj.replayImage = pReplayImages[i];
+                m_objMapper.add_to_images_map(packetImage[i], local_imageObj);
+                replayImageToDevice[pReplayImages[i]] = remappeddevice;
+            }
+        }
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkQueuePresentKHR(packet_vkQueuePresentKHR* pPacket)
+{
+    VkResult replayResult = VK_SUCCESS;
+    VkQueue remappedQueue = m_objMapper.remap_queues(pPacket->queue);
+    if (remappedQueue == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkQueuePresentKHR() due to invalid remapped VkQueue.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkSemaphore localSemaphores[5];
+    VkSwapchainKHR localSwapchains[5];
+    VkResult localResults[5];
+    VkSemaphore *pRemappedWaitSems = localSemaphores;
+    VkSwapchainKHR *pRemappedSwapchains = localSwapchains;
+    VkResult *pResults = localResults;
+    VkPresentInfoKHR present;
+    uint32_t i;
+    uint32_t remappedImageIndex = UINT32_MAX;
+
+    if (pPacket->pPresentInfo->swapchainCount > 5) {
+        pRemappedSwapchains = VKTRACE_NEW_ARRAY(VkSwapchainKHR, pPacket->pPresentInfo->swapchainCount);
+    }
+
+    if (pPacket->pPresentInfo->swapchainCount > 5 && pPacket->pPresentInfo->pResults != NULL) {
+        pResults = VKTRACE_NEW_ARRAY(VkResult, pPacket->pPresentInfo->swapchainCount);
+    }
+
+    if (pPacket->pPresentInfo->waitSemaphoreCount > 5) {
+        pRemappedWaitSems = VKTRACE_NEW_ARRAY(VkSemaphore, pPacket->pPresentInfo->waitSemaphoreCount);
+    }
+
+    if (pRemappedSwapchains == NULL || pRemappedWaitSems == NULL || pResults == NULL)
+    {
+        replayResult = VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+
+    if (replayResult == VK_SUCCESS) {
+        for (i=0; i<pPacket->pPresentInfo->swapchainCount; i++) {
+            pRemappedSwapchains[i] = m_objMapper.remap_swapchainkhrs(pPacket->pPresentInfo->pSwapchains[i]);
+            if (pRemappedSwapchains[i] == VK_NULL_HANDLE)
+            {
+                vktrace_LogError("Skipping vkQueuePresentKHR() due to invalid remapped VkSwapchainKHR.");
+                replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+                goto out;
+            }
+        }
+
+        assert(pPacket->pPresentInfo->swapchainCount == 1 && "Multiple swapchain images not supported yet");
+
+        if(pPacket->pPresentInfo->pImageIndices)
+        {
+            auto imageIndice = *pPacket->pPresentInfo->pImageIndices;
+            remappedImageIndex = m_objMapper.remap_pImageIndex(imageIndice);
+        }
+
+        if (remappedImageIndex == UINT32_MAX)
+        {
+            vktrace_LogError("Skipping vkQueuePresentKHR() due to invalid remapped pImageIndices.");
+            replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+            goto out;
+        }
+
+        present.sType = pPacket->pPresentInfo->sType;
+        present.pNext = pPacket->pPresentInfo->pNext;
+        present.swapchainCount = pPacket->pPresentInfo->swapchainCount;
+        present.pSwapchains = pRemappedSwapchains;
+        present.pImageIndices = &remappedImageIndex;
+        present.waitSemaphoreCount = pPacket->pPresentInfo->waitSemaphoreCount;
+        present.pWaitSemaphores = NULL;
+        if (present.waitSemaphoreCount != 0) {
+            present.pWaitSemaphores = pRemappedWaitSems;
+            for (i = 0; i < pPacket->pPresentInfo->waitSemaphoreCount; i++) {
+                (*(pRemappedWaitSems + i)) = m_objMapper.remap_semaphores((*(pPacket->pPresentInfo->pWaitSemaphores + i)));
+                if (*(pRemappedWaitSems + i) == VK_NULL_HANDLE)
+                {
+                    vktrace_LogError("Skipping vkQueuePresentKHR() due to invalid remapped wait VkSemaphore.");
+                    replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+                    goto out;
+                }
+            }
+        }
+        present.pResults = NULL;
+    }
+
+    if (replayResult == VK_SUCCESS) {
+        // If the application requested per-swapchain results, set up to get the results from the replay.
+        if (pPacket->pPresentInfo->pResults != NULL) {
+            present.pResults = pResults;
+        }
+
+        replayResult = m_vkFuncs.real_vkQueuePresentKHR(remappedQueue, &present);
+
+        m_frameNumber++;
+
+        // Compare the results from the trace file with those just received from the replay.  Report any differences.
+        if (present.pResults != NULL) {
+            for (i=0; i<pPacket->pPresentInfo->swapchainCount; i++) {
+                if (present.pResults[i] != pPacket->pPresentInfo->pResults[i]) {
+                    vktrace_LogError("Return value %s from API call (VkQueuePresentKHR) does not match return value from trace file %s for swapchain %d.",
+                                     string_VkResult(present.pResults[i]), string_VkResult(pPacket->pPresentInfo->pResults[i]), i);
+                }
+            }
+        }
+    }
+
+out:
+
+    if (pRemappedWaitSems != NULL && pRemappedWaitSems != localSemaphores) {
+        VKTRACE_DELETE(pRemappedWaitSems);
+    }
+    if (pResults != NULL && pResults != localResults) {
+        VKTRACE_DELETE(pResults);
+    }
+    if (pRemappedSwapchains != NULL && pRemappedSwapchains != localSwapchains) {
+        VKTRACE_DELETE(pRemappedSwapchains);
+    }
+
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateXcbSurfaceKHR(packet_vkCreateXcbSurfaceKHR* pPacket)
+{
+    VkResult replayResult = VK_SUCCESS;
+    VkSurfaceKHR local_pSurface = VK_NULL_HANDLE;
+    VkInstance remappedInstance = m_objMapper.remap_instances(pPacket->instance);
+    if (remappedInstance == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateXcbSurfaceKHR() due to invalid remapped VkInstance.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    VkXcbSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.connection = pSurf->connection;
+    createInfo.window = pSurf->window;
+    replayResult = m_vkFuncs.real_vkCreateXcbSurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#elif defined WIN32
+    VkIcdSurfaceWin32 *pSurf = (VkIcdSurfaceWin32 *) m_display->get_surface();
+    VkWin32SurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.hinstance = pSurf->hinstance;
+    createInfo.hwnd = pSurf->hwnd;
+    replayResult = m_vkFuncs.real_vkCreateWin32SurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#else
+    vktrace_LogError("manually_replay_vkCreateXcbSurfaceKHR not implemented on this vkreplay platform");
+    replayResult = VK_ERROR_FEATURE_NOT_PRESENT;
+#endif
+
+    if (replayResult == VK_SUCCESS) {
+        m_objMapper.add_to_surfacekhrs_map(*(pPacket->pSurface), local_pSurface);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateXlibSurfaceKHR(packet_vkCreateXlibSurfaceKHR* pPacket)
+{
+    VkResult replayResult = VK_SUCCESS;
+    VkSurfaceKHR local_pSurface = VK_NULL_HANDLE;
+    VkInstance remappedinstance = m_objMapper.remap_instances(pPacket->instance);
+
+    if (pPacket->instance != VK_NULL_HANDLE && remappedinstance == VK_NULL_HANDLE) {
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+#if defined PLATFORM_LINUX && defined VK_USE_PLATFORM_XLIB_KHR
+    VkIcdSurfaceXlib *pSurf = (VkIcdSurfaceXlib *) m_display->get_surface();
+    VkXlibSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.dpy = pSurf->dpy;
+    createInfo.window = pSurf->window;
+    replayResult = m_vkFuncs.real_vkCreateXlibSurfaceKHR(remappedinstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#elif defined PLATFORM_LINUX && defined VK_USE_PLATFORM_XCB_KHR
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    VkXcbSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.connection = pSurf->connection;
+    createInfo.window = pSurf->window;
+    replayResult = m_vkFuncs.real_vkCreateXcbSurfaceKHR(remappedinstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#elif defined PLATFORM_LINUX && defined VK_USE_PLATFORM_ANDROID_KHR
+    // TODO
+#elif defined PLATFORM_LINUX
+#error manually_replay_vkCreateXlibSurfaceKHR on PLATFORM_LINUX requires one of VK_USE_PLATFORM_XLIB_KHR or VK_USE_PLATFORM_XCB_KHR or VK_USE_PLATFORM_ANDROID_KHR
+#elif defined WIN32
+    VkIcdSurfaceWin32 *pSurf = (VkIcdSurfaceWin32 *) m_display->get_surface();
+    VkWin32SurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.hinstance = pSurf->hinstance;
+    createInfo.hwnd = pSurf->hwnd;
+    replayResult = m_vkFuncs.real_vkCreateWin32SurfaceKHR(remappedinstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#else
+    vktrace_LogError("manually_replay_vkCreateXlibSurfaceKHR not implemented on this playback platform");
+    replayResult = VK_ERROR_FEATURE_NOT_PRESENT;
+#endif
+    if (replayResult == VK_SUCCESS) {
+        m_objMapper.add_to_surfacekhrs_map(*(pPacket->pSurface), local_pSurface);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateWin32SurfaceKHR(packet_vkCreateWin32SurfaceKHR* pPacket)
+{
+
+    VkResult replayResult = VK_SUCCESS;
+    VkSurfaceKHR local_pSurface = VK_NULL_HANDLE;
+    VkInstance remappedInstance = m_objMapper.remap_instances(pPacket->instance);
+    if (remappedInstance == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateWin32SurfaceKHR() due to invalid remapped VkInstance.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+#if defined WIN32
+    VkIcdSurfaceWin32 *pSurf = (VkIcdSurfaceWin32 *) m_display->get_surface();
+    VkWin32SurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.hinstance = pSurf->hinstance;
+    createInfo.hwnd = pSurf->hwnd;
+    replayResult = m_vkFuncs.real_vkCreateWin32SurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#elif defined(PLATFORM_LINUX) && !defined(ANDROID)
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    VkXcbSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.connection = pSurf->connection;
+    createInfo.window = pSurf->window;
+    replayResult = m_vkFuncs.real_vkCreateXcbSurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#else
+    vktrace_LogError("manually_replay_vkCreateWin32SurfaceKHR not implemented on this playback platform");
+    replayResult = VK_ERROR_FEATURE_NOT_PRESENT;
+#endif
+    if (replayResult == VK_SUCCESS) {
+        m_objMapper.add_to_surfacekhrs_map(*(pPacket->pSurface), local_pSurface);
+    }
+    return replayResult;
+}
+
+VkResult vkReplay::manually_replay_vkCreateAndroidSurfaceKHR(packet_vkCreateAndroidSurfaceKHR* pPacket)
+{
+    VkResult replayResult = VK_SUCCESS;
+    VkSurfaceKHR local_pSurface = VK_NULL_HANDLE;
+    VkInstance remappedInstance = m_objMapper.remap_instances(pPacket->instance);
+    if (remappedInstance == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateAndroidSurfaceKHR() due to invalid remapped VkInstance.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+#if defined WIN32
+    VkIcdSurfaceWin32 *pSurf = (VkIcdSurfaceWin32 *) m_display->get_surface();
+    VkWin32SurfaceCreateInfoKHR createInfo;
+    createInfo.sType = pPacket->pCreateInfo->sType;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.hinstance = pSurf->hinstance;
+    createInfo.hwnd = pSurf->hwnd;
+    replayResult = m_vkFuncs.real_vkCreateWin32SurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#elif defined(PLATFORM_LINUX)
+#if !defined(ANDROID)
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    VkXcbSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = pPacket->pCreateInfo->sType;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.connection = pSurf->connection;
+    createInfo.window = pSurf->window;
+    replayResult = m_vkFuncs.real_vkCreateXcbSurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#else
+    VkIcdSurfaceAndroid *pSurf = (VkIcdSurfaceAndroid *) m_display->get_surface();
+    VkAndroidSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = pPacket->pCreateInfo->sType;
+    createInfo.pNext = pPacket->pCreateInfo->pNext;
+    createInfo.flags = pPacket->pCreateInfo->flags;
+    createInfo.window = pSurf->window;
+    replayResult = m_vkFuncs.real_vkCreateAndroidSurfaceKHR(remappedInstance, &createInfo, pPacket->pAllocator, &local_pSurface);
+#endif // ANDROID
+#else
+    vktrace_LogError("manually_replay_vkCreateAndroidSurfaceKHR not implemented on this playback platform");
+    replayResult = VK_ERROR_FEATURE_NOT_PRESENT;
+#endif
+    if (replayResult == VK_SUCCESS) {
+        m_objMapper.add_to_surfacekhrs_map(*(pPacket->pSurface), local_pSurface);
+    }
+    return replayResult;
+}
+
+VkResult  vkReplay::manually_replay_vkCreateDebugReportCallbackEXT(packet_vkCreateDebugReportCallbackEXT* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkDebugReportCallbackEXT local_msgCallback;
+    VkInstance remappedInstance = m_objMapper.remap_instances(pPacket->instance);
+    if (remappedInstance == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkCreateDebugReportCallbackEXT() due to invalid remapped VkInstance.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    if (!g_fpDbgMsgCallback || !m_vkFuncs.real_vkCreateDebugReportCallbackEXT) {
+        // just eat this call as we don't have local call back function defined
+        return VK_SUCCESS;
+    } else
+    {
+        VkDebugReportCallbackCreateInfoEXT dbgCreateInfo;
+        memset(&dbgCreateInfo, 0, sizeof(dbgCreateInfo));
+        dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
+        dbgCreateInfo.flags = pPacket->pCreateInfo->flags;
+        dbgCreateInfo.pfnCallback = g_fpDbgMsgCallback;
+        dbgCreateInfo.pUserData = NULL;
+        replayResult = m_vkFuncs.real_vkCreateDebugReportCallbackEXT(remappedInstance, &dbgCreateInfo, NULL, &local_msgCallback);
+        if (replayResult == VK_SUCCESS)
+        {
+                m_objMapper.add_to_debugreportcallbackexts_map(*(pPacket->pCallback), local_msgCallback);
+        }
+    }
+    return replayResult;
+}
+
+void vkReplay::manually_replay_vkDestroyDebugReportCallbackEXT(packet_vkDestroyDebugReportCallbackEXT* pPacket)
+{
+    VkInstance remappedInstance = m_objMapper.remap_instances(pPacket->instance);
+    if (remappedInstance == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkDestroyDebugReportCallbackEXT() due to invalid remapped VkInstance.");
+        return;
+    }
+
+    VkDebugReportCallbackEXT remappedMsgCallback;
+    remappedMsgCallback = m_objMapper.remap_debugreportcallbackexts(pPacket->callback);
+    if (remappedMsgCallback == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkDestroyDebugReportCallbackEXT() due to invalid remapped VkDebugReportCallbackEXT.");
+        return;
+    }
+
+    if (!g_fpDbgMsgCallback)
+    {
+        // just eat this call as we don't have local call back function defined
+        return;
+    } else
+    {
+        m_vkFuncs.real_vkDestroyDebugReportCallbackEXT(remappedInstance, remappedMsgCallback, NULL);
+    }
+}
+
+VkResult vkReplay::manually_replay_vkAllocateCommandBuffers(packet_vkAllocateCommandBuffers* pPacket)
+{
+    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;
+    VkDevice remappedDevice = m_objMapper.remap_devices(pPacket->device);
+    if (remappedDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkAllocateCommandBuffers() due to invalid remapped VkDevice.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    VkCommandBuffer *local_pCommandBuffers = new VkCommandBuffer[pPacket->pAllocateInfo->commandBufferCount];
+    VkCommandPool local_CommandPool;
+    local_CommandPool = pPacket->pAllocateInfo->commandPool;
+    ((VkCommandBufferAllocateInfo *) pPacket->pAllocateInfo)->commandPool = m_objMapper.remap_commandpools(pPacket->pAllocateInfo->commandPool);
+    if (pPacket->pAllocateInfo->commandPool == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Skipping vkAllocateCommandBuffers() due to invalid remapped VkCommandPool.");
+        return VK_ERROR_VALIDATION_FAILED_EXT;
+    }
+
+    replayResult = m_vkFuncs.real_vkAllocateCommandBuffers(remappedDevice, pPacket->pAllocateInfo, local_pCommandBuffers);
+    ((VkCommandBufferAllocateInfo *) pPacket->pAllocateInfo)->commandPool = local_CommandPool;
+
+    if (replayResult == VK_SUCCESS)
+    {
+        for (uint32_t i = 0; i < pPacket->pAllocateInfo->commandBufferCount; i++) {
+            m_objMapper.add_to_commandbuffers_map(pPacket->pCommandBuffers[i], local_pCommandBuffers[i]);
+        }
+    }
+    delete[] local_pCommandBuffers;
+    return replayResult;
+}
+
+VkBool32 vkReplay::manually_replay_vkGetPhysicalDeviceXcbPresentationSupportKHR(packet_vkGetPhysicalDeviceXcbPresentationSupportKHR* pPacket)
+{
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Error detected in vkGetPhysicalDeviceXcbPresentationSupportKHR() due to invalid remapped VkPhysicalDevice.");
+        return VK_FALSE;
+    }
+
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    m_display->get_window_handle();
+    return (m_vkFuncs.real_vkGetPhysicalDeviceXcbPresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex, pSurf->connection, m_display->get_screen_handle()->root_visual));
+#elif defined WIN32
+    return (m_vkFuncs.real_vkGetPhysicalDeviceWin32PresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex));
+#else
+    vktrace_LogError("manually_replay_vkGetPhysicalDeviceXcbPresentationSupportKHR not implemented on this playback platform");
+    return VK_FALSE;
+#endif
+}
+
+VkBool32 vkReplay::manually_replay_vkGetPhysicalDeviceXlibPresentationSupportKHR(packet_vkGetPhysicalDeviceXlibPresentationSupportKHR* pPacket)
+{
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        vktrace_LogError("Error detected in vkGetPhysicalDeviceXcbPresentationSupportKHR() due to invalid remapped VkPhysicalDevice.");
+        return VK_FALSE;
+    }
+
+#if defined PLATFORM_LINUX && defined VK_USE_PLATFORM_XLIB_KHR
+    VkIcdSurfaceXlib *pSurf = (VkIcdSurfaceXlib *) m_display->get_surface();
+    m_display->get_window_handle();
+    return (m_vkFuncs.real_vkGetPhysicalDeviceXlibPresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex, pSurf->dpy, m_display->get_screen_handle()->root_visual));
+#elif defined PLATFORM_LINUX && defined VK_USE_PLATFORM_XCB_KHR
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    m_display->get_window_handle();
+    return (m_vkFuncs.real_vkGetPhysicalDeviceXcbPresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex, pSurf->connection, m_display->get_screen_handle()->root_visual));
+#elif defined PLATFORM_LINUX && defined VK_USE_PLATFORM_ANDROID_KHR
+    // This is not defined for Android
+    return VK_TRUE;
+#elif defined PLATFORM_LINUX
+#error manually_replay_vkGetPhysicalDeviceXlibPresentationSupportKHR on PLATFORM_LINUX requires one of VK_USE_PLATFORM_XLIB_KHR or VK_USE_PLATFORM_XCB_KHR or VK_USE_PLATFORM_ANDROID_KHR
+#elif defined WIN32
+    return (m_vkFuncs.real_vkGetPhysicalDeviceWin32PresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex));
+#else
+    vktrace_LogError("manually_replay_vkGetPhysicalDeviceXlibPresentationSupportKHR not implemented on this playback platform");
+    return VK_FALSE;
+#endif
+}
+
+VkBool32 vkReplay::manually_replay_vkGetPhysicalDeviceWin32PresentationSupportKHR(packet_vkGetPhysicalDeviceWin32PresentationSupportKHR* pPacket)
+{
+    VkPhysicalDevice remappedphysicalDevice = m_objMapper.remap_physicaldevices(pPacket->physicalDevice);
+    if (pPacket->physicalDevice != VK_NULL_HANDLE && remappedphysicalDevice == VK_NULL_HANDLE)
+    {
+        return VK_FALSE;
+    }
+
+#if defined WIN32
+    return (m_vkFuncs.real_vkGetPhysicalDeviceWin32PresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex));
+#elif defined(PLATFORM_LINUX) && !defined(ANDROID)
+    VkIcdSurfaceXcb *pSurf = (VkIcdSurfaceXcb *) m_display->get_surface();
+    m_display->get_window_handle();
+    return (m_vkFuncs.real_vkGetPhysicalDeviceXcbPresentationSupportKHR(remappedphysicalDevice, pPacket->queueFamilyIndex, pSurf->connection, m_display->get_screen_handle()->root_visual));
+#else
+    vktrace_LogError("manually_replay_vkGetPhysicalDeviceWin32PresentationSupportKHR not implemented on this playback platform");
+    return VK_FALSE;
+#endif
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.h b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.h
new file mode 100644
index 0000000..fdb79f4
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vkreplay/vkreplay_vkreplay.h
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Mark Lobodzinski <mark@lunarg.com>
+ * Author: Tobin Ehlis <tobin@lunarg.com>
+ */
+
+#pragma once
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#if defined(PLATFORM_LINUX)
+#if defined(ANDROID)
+#include <android_native_app_glue.h>
+#else
+#include <xcb/xcb.h>
+#endif // ANDROID
+#endif
+#include "vktrace_multiplatform.h"
+#include "vkreplay_window.h"
+#include "vkreplay_factory.h"
+#include "vktrace_trace_packet_identifiers.h"
+#include <unordered_map>
+
+extern "C" {
+#include "vktrace_vk_vk_packets.h"
+
+// TODO138 : Need to add packets files for new wsi headers
+}
+
+#include "vulkan/vulkan.h"
+
+#include "vkreplay_vkdisplay.h"
+#include "vkreplay_vk_func_ptrs.h"
+#include "vkreplay_vk_objmapper.h"
+
+#define CHECK_RETURN_VALUE(entrypoint) returnValue = handle_replay_errors(#entrypoint, replayResult, pPacket->result, returnValue);
+
+class vkReplay {
+public:
+    ~vkReplay();
+    vkReplay(vkreplayer_settings *pReplaySettings);
+
+    int init(vktrace_replay::ReplayDisplay & disp);
+    vkDisplay * get_display() {return m_display;}
+    vktrace_replay::VKTRACE_REPLAY_RESULT replay(vktrace_trace_packet_header *packet);
+    vktrace_replay::VKTRACE_REPLAY_RESULT handle_replay_errors(const char* entrypointName, const VkResult resCall, const VkResult resTrace, const vktrace_replay::VKTRACE_REPLAY_RESULT resIn);
+
+    void push_validation_msg(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObjectHandle, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, const void* pUserData);
+    vktrace_replay::VKTRACE_REPLAY_RESULT pop_validation_msgs();
+    int dump_validation_data();
+    int get_frame_number() { return m_frameNumber; }
+    void reset_frame_number() { m_frameNumber = 0; }
+private:
+    struct vkFuncs m_vkFuncs;
+    vkReplayObjMapper m_objMapper;
+    void (*m_pDSDump)(char*);
+    void (*m_pCBDump)(char*);
+    //VKTRACESNAPSHOT_PRINT_OBJECTS m_pVktraceSnapshotPrint;
+    vkDisplay *m_display;
+
+    int m_frameNumber;
+
+    struct ValidationMsg {
+        VkFlags msgFlags;
+        VkDebugReportObjectTypeEXT objType;
+        uint64_t srcObjectHandle;
+        size_t location;
+        int32_t msgCode;
+        char layerPrefix[256];
+        char msg[256];
+        void* pUserData;
+    };
+
+    VkDebugReportCallbackEXT m_dbgMsgCallbackObj;
+
+    std::vector<struct ValidationMsg> m_validationMsgs;
+    std::vector<int> m_screenshotFrames;
+    VkResult manually_replay_vkCreateInstance(packet_vkCreateInstance* pPacket);
+    VkResult manually_replay_vkCreateDevice(packet_vkCreateDevice* pPacket);
+    VkResult manually_replay_vkCreateBuffer(packet_vkCreateBuffer* pPacket);
+    VkResult manually_replay_vkCreateImage(packet_vkCreateImage* pPacket);
+    VkResult manually_replay_vkCreateCommandPool(packet_vkCreateCommandPool* pPacket);
+    VkResult manually_replay_vkEnumeratePhysicalDevices(packet_vkEnumeratePhysicalDevices* pPacket);
+    // TODO138 : Many new functions in API now that we need to assess if manual code needed
+    //VkResult manually_replay_vkGetPhysicalDeviceInfo(packet_vkGetPhysicalDeviceInfo* pPacket);
+    //VkResult manually_replay_vkGetGlobalExtensionInfo(packet_vkGetGlobalExtensionInfo* pPacket);
+    //VkResult manually_replay_vkGetPhysicalDeviceExtensionInfo(packet_vkGetPhysicalDeviceExtensionInfo* pPacket);
+    VkResult manually_replay_vkQueueSubmit(packet_vkQueueSubmit* pPacket);
+    VkResult manually_replay_vkQueueBindSparse(packet_vkQueueBindSparse* pPacket);
+    //VkResult manually_replay_vkGetObjectInfo(packet_vkGetObjectInfo* pPacket);
+    //VkResult manually_replay_vkGetImageSubresourceInfo(packet_vkGetImageSubresourceInfo* pPacket);
+    void manually_replay_vkUpdateDescriptorSets(packet_vkUpdateDescriptorSets* pPacket);
+    VkResult manually_replay_vkCreateDescriptorSetLayout(packet_vkCreateDescriptorSetLayout* pPacket);
+    void manually_replay_vkDestroyDescriptorSetLayout(packet_vkDestroyDescriptorSetLayout* pPacket);
+    VkResult manually_replay_vkAllocateDescriptorSets(packet_vkAllocateDescriptorSets* pPacket);
+    VkResult manually_replay_vkFreeDescriptorSets(packet_vkFreeDescriptorSets* pPacket);
+    void manually_replay_vkCmdBindDescriptorSets(packet_vkCmdBindDescriptorSets* pPacket);
+    void manually_replay_vkCmdBindVertexBuffers(packet_vkCmdBindVertexBuffers* pPacket);
+    VkResult manually_replay_vkGetPipelineCacheData(packet_vkGetPipelineCacheData* pPacket);
+    VkResult manually_replay_vkCreateGraphicsPipelines(packet_vkCreateGraphicsPipelines* pPacket);
+    VkResult manually_replay_vkCreateComputePipelines(packet_vkCreateComputePipelines* pPacket);
+    VkResult manually_replay_vkCreatePipelineLayout(packet_vkCreatePipelineLayout* pPacket);
+    void manually_replay_vkCmdWaitEvents(packet_vkCmdWaitEvents* pPacket);
+    void manually_replay_vkCmdPipelineBarrier(packet_vkCmdPipelineBarrier* pPacket);
+    VkResult manually_replay_vkCreateFramebuffer(packet_vkCreateFramebuffer* pPacket);
+    VkResult manually_replay_vkCreateRenderPass(packet_vkCreateRenderPass* pPacket);
+    void manually_replay_vkCmdBeginRenderPass(packet_vkCmdBeginRenderPass* pPacket);
+    VkResult manually_replay_vkBeginCommandBuffer(packet_vkBeginCommandBuffer* pPacket);
+    VkResult manually_replay_vkAllocateCommandBuffers(packet_vkAllocateCommandBuffers* pPacket);
+    VkResult manually_replay_vkWaitForFences(packet_vkWaitForFences* pPacket);
+    VkResult manually_replay_vkAllocateMemory(packet_vkAllocateMemory* pPacket);
+    void manually_replay_vkFreeMemory(packet_vkFreeMemory* pPacket);
+    VkResult manually_replay_vkMapMemory(packet_vkMapMemory* pPacket);
+    void manually_replay_vkUnmapMemory(packet_vkUnmapMemory* pPacket);
+    VkResult manually_replay_vkFlushMappedMemoryRanges(packet_vkFlushMappedMemoryRanges* pPacket);
+    VkResult manually_replay_vkInvalidateMappedMemoryRanges(packet_vkInvalidateMappedMemoryRanges* pPacket);
+    void manually_replay_vkGetPhysicalDeviceMemoryProperties(packet_vkGetPhysicalDeviceMemoryProperties* pPacket);
+    void manually_replay_vkGetPhysicalDeviceQueueFamilyProperties(packet_vkGetPhysicalDeviceQueueFamilyProperties* pPacket);
+    VkResult manually_replay_vkGetPhysicalDeviceSurfaceSupportKHR(packet_vkGetPhysicalDeviceSurfaceSupportKHR* pPacket);
+    VkResult manually_replay_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(packet_vkGetPhysicalDeviceSurfaceCapabilitiesKHR* pPacket);
+    VkResult manually_replay_vkGetPhysicalDeviceSurfaceFormatsKHR(packet_vkGetPhysicalDeviceSurfaceFormatsKHR* pPacket);
+    VkResult manually_replay_vkGetPhysicalDeviceSurfacePresentModesKHR(packet_vkGetPhysicalDeviceSurfacePresentModesKHR* pPacket);
+    VkResult manually_replay_vkCreateSwapchainKHR(packet_vkCreateSwapchainKHR* pPacket);
+    VkResult manually_replay_vkGetSwapchainImagesKHR(packet_vkGetSwapchainImagesKHR* pPacket);
+    VkResult manually_replay_vkQueuePresentKHR(packet_vkQueuePresentKHR* pPacket);
+    VkResult manually_replay_vkCreateXcbSurfaceKHR(packet_vkCreateXcbSurfaceKHR* pPacket);
+    VkBool32 manually_replay_vkGetPhysicalDeviceXcbPresentationSupportKHR(packet_vkGetPhysicalDeviceXcbPresentationSupportKHR* pPacket);
+    VkResult manually_replay_vkCreateXlibSurfaceKHR(packet_vkCreateXlibSurfaceKHR* pPacket);
+    VkBool32 manually_replay_vkGetPhysicalDeviceXlibPresentationSupportKHR(packet_vkGetPhysicalDeviceXlibPresentationSupportKHR* pPacket);
+    VkResult manually_replay_vkCreateWin32SurfaceKHR(packet_vkCreateWin32SurfaceKHR* pPacket);
+    VkBool32 manually_replay_vkGetPhysicalDeviceWin32PresentationSupportKHR(packet_vkGetPhysicalDeviceWin32PresentationSupportKHR* pPacket);
+    VkResult manually_replay_vkCreateAndroidSurfaceKHR(packet_vkCreateAndroidSurfaceKHR* pPacket);
+    VkResult manually_replay_vkCreateDebugReportCallbackEXT(packet_vkCreateDebugReportCallbackEXT* pPacket);
+    void manually_replay_vkDestroyDebugReportCallbackEXT(packet_vkDestroyDebugReportCallbackEXT* pPacket);
+
+    void process_screenshot_list(const char *list)
+    {
+        std::string spec(list), word;
+        size_t start = 0, comma = 0;
+
+        while (start < spec.size()) {
+            comma = spec.find(',', start);
+
+            if (comma == std::string::npos)
+                word = std::string(spec, start);
+            else
+                word = std::string(spec, start, comma - start);
+
+            m_screenshotFrames.push_back(atoi(word.c_str()));
+            if (comma == std::string::npos)
+                break;
+
+            start = comma + 1;
+
+        }
+    }
+
+    struct QueueFamilyProperties {
+        uint32_t count;
+        VkQueueFamilyProperties* queueFamilyProperties;
+    };
+
+    // Map VkPhysicalDevice to QueueFamilyPropeties (and ultimately queue indices)
+    std::unordered_map<VkPhysicalDevice, struct QueueFamilyProperties> traceQueueFamilyProperties;
+    std::unordered_map<VkPhysicalDevice, struct QueueFamilyProperties> replayQueueFamilyProperties;
+
+    // Map VkDevice to a VkPhysicalDevice
+    std::unordered_map<VkDevice, VkPhysicalDevice> tracePhysicalDevices;
+    std::unordered_map<VkDevice, VkPhysicalDevice> replayPhysicalDevices;
+
+    // Map VkBuffer to VkDevice, so we can search for the VkDevice used to create a buffer
+    std::unordered_map<VkBuffer, VkDevice> traceBufferToDevice;
+    std::unordered_map<VkBuffer, VkDevice> replayBufferToDevice;
+
+    // Map VkImage to VkDevice, so we can search for the VkDevice used to create an image
+    std::unordered_map<VkImage, VkDevice> traceImageToDevice;
+    std::unordered_map<VkImage, VkDevice> replayImageToDevice;
+
+    // Map VkPhysicalDevice to VkPhysicalDeviceMemoryProperites
+    std::unordered_map<VkPhysicalDevice, VkPhysicalDeviceMemoryProperties> traceMemoryProperties;
+    std::unordered_map<VkPhysicalDevice, VkPhysicalDeviceMemoryProperties> replayMemoryProperties;
+
+    bool getMemoryTypeIdx(VkDevice traceDevice,
+                          VkDevice replayDevice,
+                          uint32_t traceIdx,
+                          uint32_t* pReplayIdx);
+
+    bool getQueueFamilyIdx(VkPhysicalDevice tracePhysicalDevice,
+                           VkPhysicalDevice replayPhysicalDevice,
+                           uint32_t traceIdx,
+                           uint32_t* pReplayIdx);
+    bool getQueueFamilyIdx(VkDevice traceDevice,
+        VkDevice replayDevice,
+        uint32_t traceIdx,
+        uint32_t* pReplayIdx);
+
+};
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/CMakeLists.txt b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/CMakeLists.txt
new file mode 100644
index 0000000..0825ad9
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/CMakeLists.txt
@@ -0,0 +1,83 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(vktraceviewer_vk)
+
+include("${SRC_DIR}/build_options.cmake")
+
+find_package(Qt5 COMPONENTS Widgets Gui Core Svg QUIET)
+
+if(NOT Qt5_FOUND)
+# After Qt5.6 is installed, you may need to add the following to the cmake command line:
+# -DCMAKE_PREFIX_PATH=C:\\Qt\\5.6\\msvc2015_64\\
+message(WARNING "WARNING: vktraceviewer_vk will be excluded because Qt5 was not found.")
+else()
+
+set(SRC_LIST
+    ${SRC_LIST}
+    vktraceviewer_vk.cpp
+    vktraceviewer_vk_settings.cpp
+    vktraceviewer_vk_qcontroller.cpp
+    vktraceviewer_vk_qfile_model.cpp
+    vktraceviewer_vk_qgroupframesproxymodel.cpp
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_QReplayWorker.cpp
+    ${SRC_DIR}/vktrace_replay/vkreplay_factory.cpp
+)
+
+# This should only contain headers that define a QOBJECT
+# Typically that means just headers for UI objects
+set(UI_HEADER_LIST
+    vktraceviewer_vk_qcontroller.h
+    vktraceviewer_vk_qfile_model.h
+    vktraceviewer_vk_qgroupframesproxymodel.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_qgroupthreadsproxymodel.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_qimageviewer.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_qsvgviewer.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_QTraceFileModel.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_QReplayWidget.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_QReplayWorker.h
+)
+
+set(HDR_LIST
+    vktraceviewer_vk_settings.h
+    vktraceviewer_vk_qgroupframesproxymodel.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_qgroupthreadsproxymodel.h
+    ${SRC_DIR}/vktrace_viewer/vktraceviewer_controller.h
+    ${SRC_DIR}/vktrace_replay/vkreplay_factory.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/../vulkan/codegen_utils/vk_enum_string_helper.h
+    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_packet_id.h
+    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_vk_packets.h
+)
+
+include_directories(
+    ${CODEGEN_VKTRACE_DIR}
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/vktrace_viewer
+    ${SRC_DIR}/vktrace_replay
+    ${SRC_DIR}/thirdparty
+    ${CMAKE_CURRENT_SOURCE_DIR}/../vkreplay
+    ${CMAKE_CURRENT_SOURCE_DIR}/../vulkan/codegen_utils
+    ${VKTRACE_VULKAN_DIR}/${CODEGEN_VKTRACE_DIR}
+    ${VKTRACE_VULKAN_INCLUDE_DIR}
+    ${Qt5Widgets_INCLUDE_DIRS}
+)
+
+QT5_WRAP_CPP(QT_GEN_HEADER_MOC_LIST ${UI_HEADER_LIST})
+
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/../../../../")
+
+add_library(${PROJECT_NAME} STATIC ${SRC_LIST} ${HDR_LIST}
+    ${QT_GEN_HEADER_MOC_LIST}
+)
+
+target_link_libraries(${PROJECT_NAME} 
+    Qt5::Widgets
+    Qt5::Core
+    Qt5::Svg
+    ${VKTRACE_VULKAN_LIB}
+    vktrace_common
+    vulkan_replay
+)
+
+build_options_finalize()
+
+endif(NOT Qt5_FOUND)
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk.cpp
new file mode 100644
index 0000000..19fa6ae
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk.cpp
@@ -0,0 +1,42 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_vk_qcontroller.h"
+#include "vktraceviewer_controller.h"
+
+extern "C"
+{
+VKTRACER_EXPORT vktraceviewer_QController* VKTRACER_CDECL vtvCreateQController()
+{
+    vktraceviewer_vk_QController* pController = new vktraceviewer_vk_QController();
+
+    return (vktraceviewer_QController*) pController;
+}
+
+VKTRACER_EXPORT void VKTRACER_CDECL vtvDeleteQController(vktraceviewer_QController** ppController)
+{
+    if (ppController != NULL && *ppController != NULL)
+    {
+        delete *ppController;
+        *ppController = NULL;
+    }
+}
+
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qcontroller.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qcontroller.cpp
new file mode 100644
index 0000000..1bcd7c2
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qcontroller.cpp
@@ -0,0 +1,365 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+**************************************************************************/
+#include "vktraceviewer_vk_settings.h"
+#include "vktraceviewer_vk_qcontroller.h"
+
+extern "C" {
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_packet_id.h"
+}
+
+#include <assert.h>
+#include <QFileInfo>
+#include <QWidget>
+#include <QToolButton>
+#include <QCoreApplication>
+#include <QProcess>
+
+#include "vktraceviewer_view.h"
+#include "vkreplay_seq.h"
+
+static vktraceviewer_vk_QController* s_pController;
+
+void controllerLoggingCallback(VktraceLogLevel level, const char* pMessage)
+{
+    if (s_pController != NULL)
+    {
+        s_pController->OnOutputMessage(level, pMessage);
+    }
+}
+
+vktraceviewer_vk_QController::vktraceviewer_vk_QController()
+    : m_pView(NULL),
+      m_pTraceFileInfo(NULL),
+      m_pDrawStateDiagram(NULL),
+      m_pCommandBuffersDiagram(NULL),
+      m_pReplayWidget(NULL),
+      m_pTraceFileModel(NULL)
+{
+    s_pController = this;
+    vktrace_LogSetCallback(controllerLoggingCallback);
+    vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+    initialize_default_settings();
+    vktrace_SettingGroup_reset_defaults(&g_vkTraceViewerSettingGroup);
+}
+
+vktraceviewer_vk_QController::~vktraceviewer_vk_QController()
+{
+}
+
+vktrace_trace_packet_header* vktraceviewer_vk_QController::InterpretTracePacket(vktrace_trace_packet_header* pHeader)
+{
+    // Attempt to interpret the packet as a Vulkan packet
+    vktrace_trace_packet_header* pInterpretedHeader = interpret_trace_packet_vk(pHeader);
+    if (pInterpretedHeader == NULL)
+    {
+        vktrace_LogError("Unrecognized Vulkan packet id: %u.", pHeader->packet_id);
+    }
+    else if (pInterpretedHeader->packet_id == VKTRACE_TPI_VK_vkApiVersion)
+    {
+        packet_vkApiVersion* pPacket = (packet_vkApiVersion*)pInterpretedHeader->pBody;
+        if (pPacket->version != VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION))
+        {
+            vktrace_LogError("Trace file is from Vulkan version 0x%x (%u.%u.%u), but the VkTraceViewer plugin only supports version 0x%x (%u.%u.%u).", pPacket->version, VK_VERSION_MAJOR(pPacket->version), VK_VERSION_MINOR(pPacket->version), VK_VERSION_PATCH(pPacket->version), VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), 1, 0, VK_HEADER_VERSION);
+            pInterpretedHeader = NULL;
+        }
+    }
+
+    return pInterpretedHeader;
+}
+
+bool vktraceviewer_vk_QController::LoadTraceFile(vktraceviewer_trace_file_info* pTraceFileInfo, vktraceviewer_view* pView)
+{
+    assert(pTraceFileInfo != NULL);
+    assert(pView != NULL);
+    setView(pView);
+    m_pTraceFileInfo = pTraceFileInfo;
+
+    assert(m_pReplayWidget == NULL);
+    m_pReplayWidget = new vktraceviewer_QReplayWidget(&m_replayWorker);
+    if (m_pReplayWidget != NULL)
+    {
+        // load available replayers
+        if (!m_replayWorker.load_replayers(pTraceFileInfo, m_pReplayWidget->GetReplayWindow(),
+            g_vkTraceViewerSettings.replay_window_width,
+            g_vkTraceViewerSettings.replay_window_height,
+            g_vkTraceViewerSettings.separate_replay_window))
+        {
+            emit OutputMessage(VKTRACE_LOG_ERROR, "Failed to load necessary replayers.");
+            delete m_pReplayWidget;
+            m_pReplayWidget = NULL;
+        }
+        else
+        {
+            m_pView->add_custom_state_viewer(m_pReplayWidget, "Replayer", true);
+            m_pReplayWidget->setEnabled(true);
+            connect(m_pReplayWidget, SIGNAL(ReplayStarted()), this, SLOT(onReplayStarted()));
+            connect(m_pReplayWidget, SIGNAL(ReplayPaused(uint64_t)), this, SLOT(onReplayPaused(uint64_t)));
+            connect(m_pReplayWidget, SIGNAL(ReplayContinued()), this, SLOT(onReplayContinued()));
+            connect(m_pReplayWidget, SIGNAL(ReplayStopped(uint64_t)), this, SLOT(onReplayStopped(uint64_t)));
+            connect(m_pReplayWidget, SIGNAL(ReplayFinished(uint64_t)), this, SLOT(onReplayFinished(uint64_t)));
+            connect(m_pReplayWidget, SIGNAL(ReplayProgressUpdate(uint64_t)), this, SLOT(onReplayProgressUpdate(uint64_t)));
+
+            connect(m_pReplayWidget, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)), this, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)));
+            connect(m_pReplayWidget, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)), this, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)));
+        }
+    }
+
+    assert(m_pTraceFileModel == NULL);
+    m_pTraceFileModel = new vktraceviewer_vk_QFileModel(NULL, pTraceFileInfo);
+    updateCallTreeBasedOnSettings();
+
+    deleteStateDumps();
+
+    return true;
+}
+
+const char* vktraceviewer_vk_QController::GetPacketIdString(uint16_t packetId)
+{
+    return vktrace_vk_packet_id_name((VKTRACE_TRACE_PACKET_ID_VK)packetId);
+}
+
+void vktraceviewer_vk_QController::updateCallTreeBasedOnSettings()
+{
+    if (m_pTraceFileModel == NULL)
+    {
+        return;
+    }
+
+    if (g_vkTraceViewerSettings.groupByFrame)
+    {
+        if (m_groupByFramesProxy.sourceModel() != m_pTraceFileModel)
+        {
+            m_groupByFramesProxy.setSourceModel(m_pTraceFileModel);
+        }
+        m_pView->set_calltree_model(m_pTraceFileModel, &m_groupByFramesProxy);
+    }
+    else if (g_vkTraceViewerSettings.groupByThread)
+    {
+        if (m_groupByThreadsProxy.sourceModel() != m_pTraceFileModel)
+        {
+            m_groupByThreadsProxy.setSourceModel(m_pTraceFileModel);
+        }
+        m_pView->set_calltree_model(m_pTraceFileModel, &m_groupByThreadsProxy);
+    }
+    else
+    {
+        m_pView->set_calltree_model(m_pTraceFileModel, NULL);
+    }
+}
+
+void vktraceviewer_vk_QController::deleteStateDumps() const
+{
+    QFile::remove("pipeline_dump.dot");
+    QFile::remove("pipeline_dump.svg");
+    QFile::remove("cb_dump.dot");
+    QFile::remove("cb_dump.svg");
+}
+
+void vktraceviewer_vk_QController::setStateWidgetsEnabled(bool bEnabled)
+{
+    if(m_pDrawStateDiagram != NULL)
+    {
+        m_pView->enable_custom_state_viewer(m_pDrawStateDiagram, bEnabled);
+    }
+
+    if(m_pCommandBuffersDiagram != NULL)
+    {
+        m_pView->enable_custom_state_viewer(m_pCommandBuffersDiagram, bEnabled);
+    }
+}
+
+void vktraceviewer_vk_QController::onReplayStarted()
+{
+    emit OutputMessage(VKTRACE_LOG_VERBOSE, "Replay Started");
+    deleteStateDumps();
+    setStateWidgetsEnabled(false);
+    m_pView->on_replay_state_changed(true);
+}
+
+void vktraceviewer_vk_QController::onReplayPaused(uint64_t packetIndex)
+{
+    emit OutputMessage(VKTRACE_LOG_VERBOSE, packetIndex, "Replay Paused");
+    m_pView->on_replay_state_changed(false);
+
+    // When paused, the replay will 'continue' from the last packet,
+    // so select that call to indicate to the user where the pause occured.
+    m_pView->select_call_at_packet_index(packetIndex);
+
+    // Dump state data from the replayer
+    vktrace_replay::vktrace_trace_packet_replay_library* pVkReplayer = m_replayWorker.getReplayer(VKTRACE_TID_VULKAN);
+    if (pVkReplayer != NULL)
+    {
+        int err;
+        err = pVkReplayer->Dump();
+        if (err)
+        {
+            emit OutputMessage(VKTRACE_LOG_WARNING, packetIndex, "Replayer couldn't output state data.");
+        }
+    }
+
+    // Now try to load known state data.
+
+    // Convert dot files to svg format
+#if defined(PLATFORM_LINUX)
+    if (QFile::exists("/usr/bin/dot"))
+    {
+        QProcess process;
+        process.start("/usr/bin/dot pipeline_dump.dot -Tsvg -o pipeline_dump.svg");
+        process.waitForFinished(-1);
+        process.start("/usr/bin/dot cb_dump.dot -Tsvg -o cb_dump.svg");
+        process.waitForFinished(-1);
+    }
+    else
+    {
+        emit OutputMessage(VKTRACE_LOG_ERROR, packetIndex, "DOT not found, unable to generate state diagrams.");
+    }
+#else
+    emit OutputMessage(VKTRACE_LOG_ERROR, packetIndex, "DOT not found, unable to generate state diagrams.");
+#endif
+
+    if (QFile::exists("pipeline_dump.svg"))
+    {
+        if (m_pDrawStateDiagram == NULL)
+        {
+            m_pDrawStateDiagram = new vktraceviewer_qsvgviewer();
+            m_pView->add_custom_state_viewer(m_pDrawStateDiagram, tr("Draw State"), false);
+            m_pView->enable_custom_state_viewer(m_pDrawStateDiagram, false);
+        }
+
+        if (m_pDrawStateDiagram != NULL && m_pDrawStateDiagram->load(tr("pipeline_dump.svg")))
+        {
+            m_pView->enable_custom_state_viewer(m_pDrawStateDiagram, true);
+        }
+
+    }
+
+    if (QFile::exists("cb_dump.svg"))
+    {
+        if (m_pCommandBuffersDiagram == NULL)
+        {
+            m_pCommandBuffersDiagram = new vktraceviewer_qsvgviewer();
+            m_pView->add_custom_state_viewer(m_pCommandBuffersDiagram, tr("Command Buffers"), false);
+            m_pView->enable_custom_state_viewer(m_pCommandBuffersDiagram, false);
+        }
+
+        if (m_pCommandBuffersDiagram != NULL && m_pCommandBuffersDiagram->load(tr("cb_dump.svg")))
+        {
+            m_pView->enable_custom_state_viewer(m_pCommandBuffersDiagram, true);
+        }
+    }
+}
+
+void vktraceviewer_vk_QController::onReplayContinued()
+{
+    emit OutputMessage(VKTRACE_LOG_VERBOSE, "Replay Continued");
+    deleteStateDumps();
+    setStateWidgetsEnabled(false);
+    m_pView->on_replay_state_changed(true);
+}
+
+void vktraceviewer_vk_QController::onReplayStopped(uint64_t packetIndex)
+{
+    emit OutputMessage(VKTRACE_LOG_VERBOSE, packetIndex, "Replay Stopped");
+    m_pView->on_replay_state_changed(false);
+    setStateWidgetsEnabled(false);
+
+    // Stopping the replay means that it will 'play' or 'step' from the beginning,
+    // so select the first packet index to indicate to the user what stopping replay does.
+    m_pView->select_call_at_packet_index(0);
+}
+
+void vktraceviewer_vk_QController::onReplayProgressUpdate(uint64_t packetArrayIndex)
+{
+    m_pView->highlight_timeline_item(packetArrayIndex, true, true);
+}
+
+void vktraceviewer_vk_QController::onReplayFinished(uint64_t packetIndex)
+{
+    emit OutputMessage(VKTRACE_LOG_VERBOSE, packetIndex, "Replay Finished");
+    m_pView->on_replay_state_changed(false);
+    setStateWidgetsEnabled(false);
+
+    // The replay has completed, so highlight the final packet index.
+    m_pView->select_call_at_packet_index(packetIndex);
+}
+
+void vktraceviewer_vk_QController::OnOutputMessage(VktraceLogLevel level, const QString& msg)
+{
+    emit OutputMessage(level, msg);
+}
+
+vktrace_SettingGroup* vktraceviewer_vk_QController::GetSettings()
+{
+    return &g_vkTraceViewerSettingGroup;
+}
+
+void vktraceviewer_vk_QController::UpdateFromSettings(vktrace_SettingGroup *pGroups, unsigned int numGroups)
+{
+    vktrace_SettingGroup_Apply_Overrides(&g_vkTraceViewerSettingGroup, pGroups, numGroups);
+
+    m_replayWorker.setPrintReplayMessages(g_vkTraceViewerSettings.printReplayInfoMsgs,
+        g_vkTraceViewerSettings.printReplayWarningMsgs,
+        g_vkTraceViewerSettings.printReplayErrorMsgs);
+
+    m_replayWorker.setPauseOnReplayMessages(g_vkTraceViewerSettings.pauseOnReplayInfo,
+        g_vkTraceViewerSettings.pauseOnReplayWarning,
+        g_vkTraceViewerSettings.pauseOnReplayError);
+
+    m_replayWorker.onSettingsUpdated(pGroups, numGroups);
+
+    updateCallTreeBasedOnSettings();
+}
+
+void vktraceviewer_vk_QController::UnloadTraceFile(void)
+{
+    if (m_pView != NULL)
+    {
+        m_pView->set_calltree_model(NULL, NULL);
+        m_pView = NULL;
+    }
+
+    if (m_pTraceFileModel != NULL)
+    {
+        delete m_pTraceFileModel;
+        m_pTraceFileModel = NULL;
+    }
+
+    if (m_pReplayWidget != NULL)
+    {
+        delete m_pReplayWidget;
+        m_pReplayWidget = NULL;
+    }
+
+    if (m_pDrawStateDiagram != NULL)
+    {
+        delete m_pDrawStateDiagram;
+        m_pDrawStateDiagram = NULL;
+    }
+
+    if (m_pCommandBuffersDiagram != NULL)
+    {
+        delete m_pCommandBuffersDiagram;
+        m_pCommandBuffersDiagram = NULL;
+    }
+
+    m_replayWorker.unloadReplayers();
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qcontroller.h b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qcontroller.h
new file mode 100644
index 0000000..c0a30d5
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qcontroller.h
@@ -0,0 +1,87 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Valve Software. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *************************************************************************/
+#ifndef VKTRACEVIEWER_VK_QCONTROLLER_H
+#define VKTRACEVIEWER_VK_QCONTROLLER_H
+
+#include "vktrace_trace_packet_identifiers.h"
+#include "vktraceviewer_vk_qgroupframesproxymodel.h"
+#include "vktraceviewer_qgroupthreadsproxymodel.h"
+#include "vktraceviewer_qsvgviewer.h"
+#include "vktraceviewer_QReplayWidget.h"
+#include "vktraceviewer_QReplayWorker.h"
+#include "vktraceviewer_vk_qfile_model.h"
+#include "vktraceviewer_controller.h"
+#include <QLabel>
+#include <QScrollArea>
+
+
+class vktraceviewer_vk_QController : public vktraceviewer_QController
+{
+    Q_OBJECT
+public:
+    vktraceviewer_vk_QController();
+    virtual ~vktraceviewer_vk_QController();
+
+    virtual vktrace_trace_packet_header* InterpretTracePacket(vktrace_trace_packet_header* pHeader);
+    virtual bool LoadTraceFile(vktraceviewer_trace_file_info* pTraceFileInfo, vktraceviewer_view* pView);
+    virtual void UnloadTraceFile(void);
+
+    void setView(vktraceviewer_view* pView)
+    {
+        m_pView = pView;
+        m_replayWorker.setView(pView);
+    }
+
+    virtual vktrace_SettingGroup* GetSettings();
+    virtual void UpdateFromSettings(vktrace_SettingGroup *pGroups, unsigned int numGroups);
+
+    virtual const char* GetPacketIdString(uint16_t packetId);
+
+public slots:
+    void OnOutputMessage(VktraceLogLevel level, const QString& msg);
+
+signals:
+    // Inherited from glvdebug_QController
+    void OutputMessage(VktraceLogLevel level, const QString& message);
+    void OutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& message);
+
+protected slots:
+    void onReplayStarted();
+    void onReplayPaused(uint64_t packetIndex);
+    void onReplayContinued();
+    void onReplayStopped(uint64_t packetIndex);
+    void onReplayFinished(uint64_t packetIndex);
+    void onReplayProgressUpdate(uint64_t packetArrayIndex);
+
+private:
+    vktraceviewer_view* m_pView;
+    vktraceviewer_trace_file_info* m_pTraceFileInfo;
+    vktraceviewer_QReplayWorker m_replayWorker;
+    vktraceviewer_qsvgviewer* m_pDrawStateDiagram;
+    vktraceviewer_qsvgviewer* m_pCommandBuffersDiagram;
+    vktraceviewer_QReplayWidget* m_pReplayWidget;
+    vktraceviewer_vk_QFileModel* m_pTraceFileModel;
+    vktraceviewer_vk_QGroupFramesProxyModel m_groupByFramesProxy;
+    vktraceviewer_QGroupThreadsProxyModel m_groupByThreadsProxy;
+
+    void setStateWidgetsEnabled(bool bEnabled);
+    void updateCallTreeBasedOnSettings();
+    void deleteStateDumps() const;
+};
+
+#endif // VKTRACEVIEWER_VK_QCONTROLLER_H
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qfile_model.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qfile_model.cpp
new file mode 100644
index 0000000..2761416
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qfile_model.cpp
@@ -0,0 +1,95 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include "vktraceviewer_vk_qfile_model.h"
+extern "C" {
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_packet_id.h"
+}
+
+vktraceviewer_vk_QFileModel::vktraceviewer_vk_QFileModel(QObject* parent, vktraceviewer_trace_file_info* pTraceFileInfo)
+        : vktraceviewer_QTraceFileModel(parent, pTraceFileInfo)
+{
+}
+
+vktraceviewer_vk_QFileModel::~vktraceviewer_vk_QFileModel()
+{
+}
+
+QString vktraceviewer_vk_QFileModel::get_packet_string(const vktrace_trace_packet_header* pHeader) const
+{
+    if (pHeader->packet_id < VKTRACE_TPI_BEGIN_API_HERE)
+    {
+        return vktraceviewer_QTraceFileModel::get_packet_string(pHeader);
+    }
+    else
+    {
+        QString packetString = vktrace_stringify_vk_packet_id((const enum VKTRACE_TRACE_PACKET_ID_VK) pHeader->packet_id, pHeader);
+        return packetString;
+    }
+}
+
+QString vktraceviewer_vk_QFileModel::get_packet_string_multiline(const vktrace_trace_packet_header* pHeader) const
+{
+    if (pHeader->packet_id < VKTRACE_TPI_BEGIN_API_HERE)
+    {
+        return vktraceviewer_QTraceFileModel::get_packet_string_multiline(pHeader);
+    }
+    else
+    {
+        QString packetString = vktrace_stringify_vk_packet_id((const enum VKTRACE_TRACE_PACKET_ID_VK) pHeader->packet_id, pHeader);
+        return packetString;
+    }
+}
+
+bool vktraceviewer_vk_QFileModel::isDrawCall(const VKTRACE_TRACE_PACKET_ID packetId) const
+{
+    // TODO : Update this based on latest API updates
+    bool isDraw = false;
+    switch((VKTRACE_TRACE_PACKET_ID_VK)packetId)
+    {
+        case VKTRACE_TPI_VK_vkCmdDraw:
+        case VKTRACE_TPI_VK_vkCmdDrawIndexed:
+        case VKTRACE_TPI_VK_vkCmdDrawIndirect:
+        case VKTRACE_TPI_VK_vkCmdDrawIndexedIndirect:
+        case VKTRACE_TPI_VK_vkCmdDispatch:
+        case VKTRACE_TPI_VK_vkCmdDispatchIndirect:
+        case VKTRACE_TPI_VK_vkCmdCopyBuffer:
+        case VKTRACE_TPI_VK_vkCmdCopyImage:
+        case VKTRACE_TPI_VK_vkCmdCopyBufferToImage:
+        case VKTRACE_TPI_VK_vkCmdCopyImageToBuffer:
+        case VKTRACE_TPI_VK_vkCmdUpdateBuffer:
+        case VKTRACE_TPI_VK_vkCmdFillBuffer:
+        case VKTRACE_TPI_VK_vkCmdClearColorImage:
+        case VKTRACE_TPI_VK_vkCmdClearDepthStencilImage:
+        case VKTRACE_TPI_VK_vkCmdClearAttachments:
+        case VKTRACE_TPI_VK_vkCmdResolveImage:
+        {
+            isDraw = true;
+            break;
+        }
+        default:
+        {
+            isDraw = false;
+        }
+    }
+    return isDraw;
+}
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qfile_model.h b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qfile_model.h
new file mode 100644
index 0000000..47b4992
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qfile_model.h
@@ -0,0 +1,40 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Lunarg, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_VK_QFILE_MODEL_H_
+#define VKTRACEVIEWER_VK_QFILE_MODEL_H_
+
+#include "vktrace_trace_packet_identifiers.h"
+#include "vktraceviewer_QTraceFileModel.h"
+#include <QObject>
+
+class vktraceviewer_vk_QFileModel : public vktraceviewer_QTraceFileModel
+{
+    Q_OBJECT
+public:
+    vktraceviewer_vk_QFileModel(QObject * parent, vktraceviewer_trace_file_info *);
+    virtual ~vktraceviewer_vk_QFileModel();
+
+    virtual QString get_packet_string(const vktrace_trace_packet_header* pHeader) const;
+    virtual QString get_packet_string_multiline(const vktrace_trace_packet_header* pHeader) const;
+
+    virtual bool isDrawCall(const VKTRACE_TRACE_PACKET_ID packetId) const;
+
+};
+
+#endif //VKTRACEVIEWER_VK_QFILE_MODEL_H_
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qgroupframesproxymodel.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qgroupframesproxymodel.cpp
new file mode 100644
index 0000000..cb72e36
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qgroupframesproxymodel.cpp
@@ -0,0 +1,57 @@
+/**************************************************************************
+ *
+ * Copyright 2016 Valve Corporation
+ * Copyright (C) 2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+#include "vktraceviewer_vk_qgroupframesproxymodel.h"
+
+extern "C" {
+#include "vktrace_vk_packet_id.h"
+}
+
+void vktraceviewer_vk_QGroupFramesProxyModel::buildGroups()
+    {
+        m_mapSourceRowToProxyGroupRow.clear();
+        m_frameList.clear();
+        m_curFrameCount = 0;
+
+        if (sourceModel() != NULL)
+        {
+            FrameInfo* pCurFrame = addNewFrame();
+            m_mapSourceRowToProxyGroupRow.reserve(sourceModel()->rowCount());
+            for (int srcRow = 0; srcRow < sourceModel()->rowCount(); srcRow++)
+            {
+                int proxyRow = pCurFrame->mapChildRowToSourceRow.count();
+
+                // map source row to it's corresponding row in the proxy group.
+                m_mapSourceRowToProxyGroupRow.append(proxyRow);
+
+                // add this src row to the current proxy group.
+                pCurFrame->mapChildRowToSourceRow.append(srcRow);
+
+                // Should a new frame be started based on the API call in the previous row?
+                // If source data is a frame boundary make a new frame
+                QModelIndex tmpIndex = sourceModel()->index(srcRow, 0);
+                assert(tmpIndex.isValid());
+                vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)tmpIndex.internalPointer();
+                if (pHeader != NULL && pHeader->tracer_id == VKTRACE_TID_VULKAN && pHeader->packet_id == VKTRACE_TPI_VK_vkQueuePresentKHR)
+                {
+                    pCurFrame = addNewFrame();
+                }
+            } // end for each source row
+        }
+    }
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qgroupframesproxymodel.h b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qgroupframesproxymodel.h
new file mode 100644
index 0000000..c8c7ce9
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_qgroupframesproxymodel.h
@@ -0,0 +1,327 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_VK_QGROUPFRAMESPROXYMODEL_H
+#define VKTRACEVIEWER_VK_QGROUPFRAMESPROXYMODEL_H
+
+
+#include "vktraceviewer_QTraceFileModel.h"
+#include <QAbstractProxyModel>
+#include <QStandardItem>
+
+#include <QDebug>
+
+struct FrameInfo
+{
+    int frameIndex;
+    QPersistentModelIndex modelIndex;
+    QList<int> mapChildRowToSourceRow;
+};
+
+class vktraceviewer_vk_QGroupFramesProxyModel : public QAbstractProxyModel
+{
+    Q_OBJECT
+public:
+    vktraceviewer_vk_QGroupFramesProxyModel(QObject *parent = 0)
+        : QAbstractProxyModel(parent),
+          m_curFrameCount(0)
+    {
+        buildGroups();
+    }
+
+    virtual ~vktraceviewer_vk_QGroupFramesProxyModel()
+    {
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual void setSourceModel(QAbstractItemModel *sourceModel)
+    {
+        if (sourceModel != NULL && !sourceModel->inherits("vktraceviewer_QTraceFileModel"))
+        {
+            assert(!"Setting QGroupFramesProxyModel to have a sourceModel that doesn't inherit from QTraceFileModel.");
+            sourceModel = NULL;
+        }
+
+        QAbstractProxyModel::setSourceModel(sourceModel);
+        buildGroups();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual int rowCount(const QModelIndex &parent) const
+    {
+        if (!parent.isValid())
+        {
+            return m_frameList.count();
+        }
+        else if (isFrame(parent))
+        {
+            // this is a frame.
+            // A frame knows how many children it has!
+            return m_frameList[parent.row()].mapChildRowToSourceRow.count();
+        }
+        else
+        {
+            // ask the source
+            return sourceModel()->rowCount(mapToSource(parent));
+        }
+
+        return 0;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual bool hasChildren(const QModelIndex &parent) const
+    {
+        if (!parent.isValid())
+        {
+            return true;
+        }
+        else if (isFrame(parent))
+        {
+            return m_frameList[parent.row()].mapChildRowToSourceRow.count() > 0;
+        }
+        return false;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual QVariant data( const QModelIndex &index, int role ) const
+    {
+        if (!index.isValid())
+        {
+            return QVariant();
+        }
+
+        if (!isFrame(index))
+        {
+            return mapToSource(index).data(role);
+        }
+
+        if (role == Qt::DisplayRole)
+        {
+            if (index.column() == 0)
+            {
+                return QVariant(QString("Frame %1").arg(m_frameList[index.row()].frameIndex));
+            }
+            else
+            {
+                return QVariant(QString(""));
+            }
+        }
+
+        return QVariant();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual Qt::ItemFlags flags(const QModelIndex &index) const
+    {
+        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual int columnCount(const QModelIndex &parent) const
+    {
+        return sourceModel()->columnCount();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
+    {
+        return sourceModel()->headerData(section, orientation, role);
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
+    {
+        if (!parent.isValid())
+        {
+            // if parent is not valid, then this row and column is referencing Frame data
+            if (row < m_frameList.count())
+            {
+                return createIndex(row, column, (FrameInfo*)&m_frameList[row]);
+            }
+
+            return QModelIndex();
+        }
+        else if (isFrame(parent))
+        {
+            // the parent is a frame, so this row and column reference a source cell
+            const FrameInfo* pFrame = (const FrameInfo*)&m_frameList[parent.row()];
+            assert(pFrame->frameIndex == parent.row());
+            return createIndex(row, column, pFrame->frameIndex);
+        }
+
+        return QModelIndex();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex parent(const QModelIndex &child) const
+    {
+        if (!child.isValid())
+            return QModelIndex();
+
+        if (isFrame(child))
+        {
+            // frames don't have a parent (ie, they are at the root level)
+            return QModelIndex();
+        }
+        else
+        {
+            // The child is a proxy of the source model,
+            // so the parent is its frame's modelIndex.
+            quintptr frameIndex = child.internalId();
+            const FrameInfo* pFrame = (const FrameInfo*)&m_frameList[frameIndex];
+            return pFrame->modelIndex;
+        }
+    }
+
+    //---------------------------------------------------------------------------------------------
+    // sibling(..) needed to be implemented here because the inherited implementation looks at the
+    // sourceModel to get the new index, which results in bugs because it returns a source model
+    // sibling of a proxy model index. The new implementation keeps everything in proxy model space.
+    QModelIndex sibling(int row, int column, const QModelIndex &idx) const
+    {
+        return index(row, column, parent(idx));
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex mapToSource(const QModelIndex &proxyIndex) const
+    {
+        if (!proxyIndex.isValid())
+            return QModelIndex();
+
+        if (isFrame(proxyIndex))
+        {
+            // frames can't get mapped to the source
+            return QModelIndex();
+        }
+        else
+        {
+            quintptr frameIndex = proxyIndex.internalId();
+            const FrameInfo* pFrame = (const FrameInfo*)&m_frameList[frameIndex];
+            assert(pFrame->frameIndex == (int)frameIndex);
+            if (proxyIndex.row() < pFrame->mapChildRowToSourceRow.count())
+            {
+                int srcRow = pFrame->mapChildRowToSourceRow[proxyIndex.row()];
+                int srcCol = proxyIndex.column();
+
+                // by using a default srcParent, we'll only get top-level indices.
+                // ie, we won't support hierarchical sourceModels.
+                return sourceModel()->index(srcRow, srcCol, QModelIndex());
+            }
+            else
+            {
+                // this unexpected case has happened when scrolling quickly.
+                // UPDATE: I think I fixed this issue, it was due to calling .next() too many times on an iterator.
+                return QModelIndex();
+            }
+        }
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
+    {
+        if (!sourceIndex.isValid())
+            return QModelIndex();
+
+        int srcRow = sourceIndex.row();
+        int proxyRow = m_mapSourceRowToProxyGroupRow[srcRow];
+
+        // figure out which frame has the srcRow as a child
+        const FrameInfo* pProxyGroup = NULL;
+        QListIterator<FrameInfo> frameIter(m_frameList);
+        while (frameIter.hasNext())
+        {
+            const FrameInfo* pFrame = &frameIter.next();
+            if (pFrame->mapChildRowToSourceRow.contains(srcRow))
+            {
+                pProxyGroup = pFrame;
+                break;
+            }
+        }
+
+        return createIndex(proxyRow, sourceIndex.column(), pProxyGroup->frameIndex);
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
+    {
+        QModelIndexList results = sourceModel()->match(start, role, value, hits, flags);
+
+        for (int i = 0; i < results.count(); i++)
+        {
+            results[i] = mapFromSource(results[i]);
+        }
+
+        return results;
+    }
+
+    //---------------------------------------------------------------------------------------------
+private:
+    QList<FrameInfo> m_frameList;
+    QList<int> m_mapSourceRowToProxyGroupRow;
+    int m_curFrameCount;
+
+    //---------------------------------------------------------------------------------------------
+    bool isFrame(const QModelIndex &proxyIndex) const
+    {
+        // API Calls use the frame number as the index's internalId
+        int id = (int)proxyIndex.internalId();
+        if (id >= 0 && id < m_frameList.count())
+        {
+            // this is an api call
+            return false;
+        }
+
+        // do some validation on the modelIndex
+        FrameInfo* pFI = (FrameInfo*)proxyIndex.internalPointer();
+        if (pFI != NULL &&
+            pFI->frameIndex == proxyIndex.row() &&
+            proxyIndex.row() < m_frameList.count())
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    FrameInfo* addNewFrame()
+    {
+        // create frame info
+        FrameInfo info;
+        m_frameList.append(info);
+        FrameInfo* pFrame = &m_frameList[m_curFrameCount];
+
+        pFrame->frameIndex = m_curFrameCount;
+
+        // create proxy model index for frame node
+        pFrame->modelIndex = createIndex(m_curFrameCount, 0, pFrame);
+
+        // increment frame count
+        m_curFrameCount++;
+
+        return pFrame;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    void buildGroups();
+
+};
+
+#endif // VKTRACEVIEWER_VK_QGROUPFRAMESPROXYMODEL_H
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_settings.cpp b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_settings.cpp
new file mode 100644
index 0000000..48ee550
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_settings.cpp
@@ -0,0 +1,63 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include "vktraceviewer_vk_settings.h"
+
+// declared as extern in header
+vktraceviewer_vk_settings g_vkTraceViewerSettings;
+static vktraceviewer_vk_settings s_defaultVkSettings;
+
+vktrace_SettingInfo g_vk_settings[] =
+{
+    { "ri", "PrintReplayInfoMsgs", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.printReplayInfoMsgs, &s_defaultVkSettings.printReplayInfoMsgs, TRUE, "Print info messages reported when replaying trace file."},
+    { "rw", "PrintReplayWarningMsgs", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.printReplayWarningMsgs, &s_defaultVkSettings.printReplayWarningMsgs, TRUE, "Print warning messages reported when replaying trace file."},
+    { "re", "PrintReplayErrorMsgs", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.printReplayErrorMsgs, &s_defaultVkSettings.printReplayErrorMsgs, TRUE, "Print error messages reported when replaying trace file."},
+    { "pi", "PauseOnReplayInfo", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.pauseOnReplayInfo, &s_defaultVkSettings.pauseOnReplayInfo, TRUE, "Pause replay if an info message is reported."},
+    { "pw", "PauseOnReplayWarning", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.pauseOnReplayWarning, &s_defaultVkSettings.pauseOnReplayWarning, TRUE, "Pause replay if a warning message is reported."},
+    { "pe", "PauseOnReplayError", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.pauseOnReplayError, &s_defaultVkSettings.pauseOnReplayError, TRUE, "Pause replay if an error message is reported."},
+    { "gf", "GroupByFrame", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.groupByFrame, &s_defaultVkSettings.groupByFrame, TRUE, "Group API calls by frame."},
+    { "gt", "GroupByThread", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.groupByThread, &s_defaultVkSettings.groupByThread, TRUE, "Group API calls by the CPU thread Id on which they executed."},
+    { "rw", "ReplayWindowWidth", VKTRACE_SETTING_INT, &g_vkTraceViewerSettings.replay_window_width, &s_defaultVkSettings.replay_window_width, TRUE, "Width of replay window on startup."},
+    { "rh", "ReplayWindowHeight", VKTRACE_SETTING_INT, &g_vkTraceViewerSettings.replay_window_height, &s_defaultVkSettings.replay_window_height, TRUE, "Height of replay window on startup."},
+    { "sr", "SeparateReplayWindow", VKTRACE_SETTING_BOOL, &g_vkTraceViewerSettings.separate_replay_window, &s_defaultVkSettings.separate_replay_window, TRUE, "Use a separate replay window."},
+};
+
+vktrace_SettingGroup g_vkTraceViewerSettingGroup =
+{
+    "vktraceviewer_vk",
+    sizeof(g_vk_settings) / sizeof(g_vk_settings[0]),
+    &g_vk_settings[0]
+};
+
+void initialize_default_settings()
+{
+    s_defaultVkSettings.printReplayInfoMsgs = FALSE;
+    s_defaultVkSettings.printReplayWarningMsgs = TRUE;
+    s_defaultVkSettings.printReplayErrorMsgs = TRUE;
+    s_defaultVkSettings.pauseOnReplayInfo = FALSE;
+    s_defaultVkSettings.pauseOnReplayWarning = FALSE;
+    s_defaultVkSettings.pauseOnReplayError = TRUE;
+    s_defaultVkSettings.groupByFrame = FALSE;
+    s_defaultVkSettings.groupByThread = FALSE;
+    s_defaultVkSettings.replay_window_width = 1024;
+    s_defaultVkSettings.replay_window_height = 768;
+    s_defaultVkSettings.separate_replay_window = FALSE;
+};
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_settings.h b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_settings.h
new file mode 100644
index 0000000..b46681f
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vktraceviewer/vktraceviewer_vk_settings.h
@@ -0,0 +1,49 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_VK_SETTINGS_H
+#define VKTRACEVIEWER_VK_SETTINGS_H
+
+extern "C" {
+#include "vktrace_settings.h"
+}
+
+typedef struct vktraceviewer_vk_settings
+{
+    BOOL printReplayInfoMsgs;
+    BOOL printReplayWarningMsgs;
+    BOOL printReplayErrorMsgs;
+    BOOL pauseOnReplayInfo;
+    BOOL pauseOnReplayWarning;
+    BOOL pauseOnReplayError;
+    BOOL groupByFrame;
+    BOOL groupByThread;
+    int replay_window_width;
+    int replay_window_height;
+    BOOL separate_replay_window;
+} vktraceviewer_vk_settings;
+
+extern vktraceviewer_vk_settings g_vkTraceViewerSettings;
+extern vktrace_SettingGroup g_vkTraceViewerSettingGroup;
+
+void initialize_default_settings();
+
+#endif // VKTRACEVIEWER_VK_SETTINGS_H
+
diff --git a/vktrace/src/vktrace_extensions/vktracevulkan/vulkan/__init__.py b/vktrace/src/vktrace_extensions/vktracevulkan/vulkan/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vktrace/src/vktrace_extensions/vktracevulkan/vulkan/__init__.py
diff --git a/vktrace/src/vktrace_layer/CMakeLists.txt b/vktrace/src/vktrace_layer/CMakeLists.txt
new file mode 100644
index 0000000..b7ccaba
--- /dev/null
+++ b/vktrace/src/vktrace_layer/CMakeLists.txt
@@ -0,0 +1,151 @@
+cmake_minimum_required(VERSION 2.8)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+   if (BUILD_WSI_XCB_SUPPORT)
+       set(ENV{VULKAN_WSI} "Xcb")
+   elseif (BUILD_WSI_XLIB_SUPPORT)
+       set(ENV{VULKAN_WSI} "Xlib")
+   elseif (BUILD_WSI_WAYLAND_SUPPORT)
+       set(ENV{VULKAN_WSI} "Wayland")
+   else()
+       # Mir WSI Case
+       set(ENV{VULKAN_WSI} "Mir")
+   endif()
+endif()
+
+project(VkLayer_vktrace_layer)
+
+include("${SRC_DIR}/build_options.cmake")
+
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/codegen)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-trace-h      vk_version_1_0 OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vktrace_vk_vk.h)
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-trace-c      vk_version_1_0 OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vktrace_vk_vk.cpp)
+#execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-ext-trace-h vk_lunarg_debug_marker OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vktrace_vk_vk_lunarg_debug_marker.h)
+#execute_process(COMMAND ${PYTHON_EXECUTABLE} ${VKTRACE_VULKAN_DIR}/vktrace_generate.py AllPlatforms vktrace-ext-trace-c vk_lunarg_debug_marker OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/codegen/vktrace_vk_vk_lunarg_debug_marker.cpp)
+
+if (WIN32)
+    # Put VkLayer_vktrace_layer.dll in the same directory as vktrace.exe
+    # so that vktrace.exe can find VkLayer_vktrace_layer.dll.
+    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/../../../layersvt/)
+endif()
+
+set (CODEGEN_UTILS_DIR  ${CMAKE_CURRENT_SOURCE_DIR}/../vktrace_extensions/vktracevulkan/vulkan/codegen_utils)
+set(CODEGEN_VKTRACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../vktrace_extensions/vktracevulkan/codegen_vktrace_utils")
+
+set(SRC_LIST
+    ${SRC_LIST}
+    vktrace_lib.c
+    vktrace_lib_pagestatusarray.cpp
+    vktrace_lib_pageguardmappedmemory.cpp
+    vktrace_lib_pageguardcapture.cpp
+    vktrace_lib_pageguard.cpp
+    vktrace_lib_trace.cpp
+    vktrace_vk_exts.cpp
+    codegen/vktrace_vk_vk.cpp
+    ${CODEGEN_UTILS_DIR}/vk_struct_size_helper.c
+)
+#    codegen/vktrace_vk_vk_lunarg_debug_marker.cpp
+#    ${CODEGEN_UTILS_DIR}/vk_lunarg_debug_marker_struct_size_helper.c
+
+set_source_files_properties( ${SRC_LIST} PROPERTIES LANGUAGE CXX)
+
+set (HDR_LIST
+    vktrace_lib_helpers.h
+    vktrace_lib_pagestatusarray.h
+    vktrace_lib_pageguardmappedmemory.h
+    vktrace_lib_pageguardcapture.h
+    vktrace_lib_pageguard.h
+    vktrace_vk_exts.h
+    vk_dispatch_table_helper.h
+    codegen/vktrace_vk_vk.h
+    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_packet_id.h
+    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_vk_packets.h
+    ${CODEGEN_UTILS_DIR}/vk_struct_size_helper.h
+)
+
+# def file - needed for Windows 32-bit so that vk functions names aren't mangled
+if (WIN32)
+    if (NOT(CMAKE_GENERATOR MATCHES "Win64"))
+        set (HDR_LIST ${HDR_LIST} VkLayer_vktrace_layer.def)
+    endif()
+endif()
+
+
+#    codegen/vktrace_vk_vk_lunarg_debug_marker.h
+#    ${CODEGEN_VKTRACE_DIR}/vktrace_vk_vk_lunarg_debug_marker_packets.h
+#    ${CODEGEN_UTILS_DIR}/vk_lunarg_debug_marker_struct_size_helper.h
+
+macro(run_vk_xml_generate dependency output)
+    add_custom_command(OUTPUT ${output}
+        COMMAND ${PYTHON_CMD} ${SCRIPTS_DIR}/lvl_genvk.py -registry ${SCRIPTS_DIR}/vk.xml ${output}
+        DEPENDS ${SCRIPTS_DIR}/vk.xml ${SCRIPTS_DIR}/generator.py ${SCRIPTS_DIR}/${dependency} ${SCRIPTS_DIR}/lvl_genvk.py ${SCRIPTS_DIR}/reg.py
+    )
+endmacro()
+
+run_vk_xml_generate(dispatch_table_generator.py vk_dispatch_table_helper.h)
+add_custom_target(generate_vktrace_layer_helpers DEPENDS
+    vk_dispatch_table_helper.h)
+
+include_directories(
+    codegen
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/vktrace_trace
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CODEGEN_VKTRACE_DIR}
+    ${VKTRACE_VULKAN_INCLUDE_DIR}
+    ${CODEGEN_UTILS_DIR}
+    ${CMAKE_CURRENT_BINARY_DIR}/../../../layersvt
+)
+# copy/link layer json file into build/layersvt directory
+if (NOT WIN32)
+    # extra setup for out-of-tree builds
+    if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
+        add_custom_target(vktrace_layer-json ALL
+            COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/linux/VkLayer_vktrace_layer.json ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}../layersvt/
+            VERBATIM
+            )
+    endif()
+else()
+    if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
+        FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/windows/VkLayer_vktrace_layer.json src_json)
+        if (CMAKE_GENERATOR MATCHES "^Visual Studio.*")
+            FILE(TO_NATIVE_PATH ${PROJECT_BINARY_DIR}/../../../layersvt/$<CONFIGURATION>/VkLayer_vktrace_layer.json dst_json)
+        else()
+            FILE(TO_NATIVE_PATH ${PROJECT_BINARY_DIR}/../../../layersvt/VkLayer_vktrace_layer.json dst_json)
+        endif()
+
+        add_custom_target(vktrace_layer-json ALL
+            COMMAND copy ${src_json} ${dst_json}
+            VERBATIM
+            )
+        add_dependencies(vktrace_layer-json VkLayer_vktrace_layer)
+    endif()
+endif()
+
+add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${HDR_LIST})
+
+add_dependencies(${PROJECT_NAME} generate_vktrace_layer_helpers)
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    set(OS_TRACER_LIBS
+        -shared
+        -ldl
+    )
+endif()
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+    set(OS_TRACER_LIBS)
+endif()
+
+target_link_libraries(${PROJECT_NAME}
+    vktrace_common
+    ${VKTRACE_VULKAN_LIB}
+    ${OS_TRACER_LIBS}
+)
+
+build_options_finalize()
+
+set_target_properties(VkLayer_vktrace_layer PROPERTIES LINKER_LANGUAGE C)
diff --git a/vktrace/src/vktrace_layer/VkLayer_vktrace_layer.def b/vktrace/src/vktrace_layer/VkLayer_vktrace_layer.def
new file mode 100644
index 0000000..0c2d702
--- /dev/null
+++ b/vktrace/src/vktrace_layer/VkLayer_vktrace_layer.def
@@ -0,0 +1,181 @@
+;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+; Vulkan

+;

+; Copyright (c) 2015-2016 The Khronos Group Inc.

+; Copyright (c) 2015-2016 Valve Corporation

+; Copyright (c) 2015-2016 LunarG, Inc.

+;

+; Licensed under the Apache License, Version 2.0 (the "License");

+; you may not use this file except in compliance with the License.

+; You may obtain a copy of the License at

+;

+;     http://www.apache.org/licenses/LICENSE-2.0

+;

+; Unless required by applicable law or agreed to in writing, software

+; distributed under the License is distributed on an "AS IS" BASIS,

+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+; See the License for the specific language governing permissions and

+; limitations under the License.

+;

+;  Author: David Pinedo <david@lunarg.com>

+;;;;  End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

+

+

+LIBRARY VkLayer_vktrace_layer

+EXPORTS

+__HOOKED_vkAcquireNextImageKHR

+__HOOKED_vkAllocateCommandBuffers

+__HOOKED_vkAllocateDescriptorSets

+__HOOKED_vkAllocateMemory

+__HOOKED_vkBeginCommandBuffer

+__HOOKED_vkBindBufferMemory

+__HOOKED_vkBindImageMemory

+__HOOKED_vkCmdBeginQuery

+__HOOKED_vkCmdBeginRenderPass

+__HOOKED_vkCmdBindDescriptorSets

+__HOOKED_vkCmdBindIndexBuffer

+__HOOKED_vkCmdBindPipeline

+__HOOKED_vkCmdBindVertexBuffers

+__HOOKED_vkCmdBlitImage

+__HOOKED_vkCmdClearAttachments

+__HOOKED_vkCmdClearColorImage

+__HOOKED_vkCmdClearDepthStencilImage

+__HOOKED_vkCmdCopyBuffer

+__HOOKED_vkCmdCopyBufferToImage

+__HOOKED_vkCmdCopyImage

+__HOOKED_vkCmdCopyImageToBuffer

+__HOOKED_vkCmdCopyQueryPoolResults

+__HOOKED_vkCmdDispatch

+__HOOKED_vkCmdDispatchIndirect

+__HOOKED_vkCmdDraw

+__HOOKED_vkCmdDrawIndexed

+__HOOKED_vkCmdDrawIndexedIndirect

+__HOOKED_vkCmdDrawIndirect

+__HOOKED_vkCmdEndQuery

+__HOOKED_vkCmdEndRenderPass

+__HOOKED_vkCmdExecuteCommands

+__HOOKED_vkCmdFillBuffer

+__HOOKED_vkCmdNextSubpass

+__HOOKED_vkCmdPipelineBarrier

+__HOOKED_vkCmdPushConstants

+__HOOKED_vkCmdResetEvent

+__HOOKED_vkCmdResetQueryPool

+__HOOKED_vkCmdResolveImage

+__HOOKED_vkCmdSetBlendConstants

+__HOOKED_vkCmdSetDepthBias

+__HOOKED_vkCmdSetDepthBounds

+__HOOKED_vkCmdSetEvent

+__HOOKED_vkCmdSetLineWidth

+__HOOKED_vkCmdSetScissor

+__HOOKED_vkCmdSetStencilCompareMask

+__HOOKED_vkCmdSetStencilReference

+__HOOKED_vkCmdSetStencilWriteMask

+__HOOKED_vkCmdSetViewport

+__HOOKED_vkCmdUpdateBuffer

+__HOOKED_vkCmdWaitEvents

+__HOOKED_vkCmdWriteTimestamp

+__HOOKED_vkCreateBuffer

+__HOOKED_vkCreateBufferView

+__HOOKED_vkCreateCommandPool

+__HOOKED_vkCreateComputePipelines

+__HOOKED_vkCreateDebugReportCallbackEXT

+__HOOKED_vkCreateDescriptorPool

+__HOOKED_vkCreateDescriptorSetLayout

+__HOOKED_vkCreateDevice

+__HOOKED_vkCreateEvent

+__HOOKED_vkCreateFence

+__HOOKED_vkCreateFramebuffer

+__HOOKED_vkCreateGraphicsPipelines

+__HOOKED_vkCreateImage

+__HOOKED_vkCreateImageView

+__HOOKED_vkCreateInstance

+__HOOKED_vkCreatePipelineCache

+__HOOKED_vkCreatePipelineLayout

+__HOOKED_vkCreateQueryPool

+__HOOKED_vkCreateRenderPass

+__HOOKED_vkCreateSampler

+__HOOKED_vkCreateSemaphore

+__HOOKED_vkCreateShaderModule

+__HOOKED_vkCreateSwapchainKHR

+__HOOKED_vkCreateWin32SurfaceKHR

+__HOOKED_vkDebugReportMessageEXT

+__HOOKED_vkDestroyBuffer

+__HOOKED_vkDestroyBufferView

+__HOOKED_vkDestroyCommandPool

+__HOOKED_vkDestroyDebugReportCallbackEXT

+__HOOKED_vkDestroyDescriptorPool

+__HOOKED_vkDestroyDescriptorSetLayout

+__HOOKED_vkDestroyDevice

+__HOOKED_vkDestroyEvent

+__HOOKED_vkDestroyFence

+__HOOKED_vkDestroyFramebuffer

+__HOOKED_vkDestroyImage

+__HOOKED_vkDestroyImageView

+__HOOKED_vkDestroyInstance

+__HOOKED_vkDestroyPipeline

+__HOOKED_vkDestroyPipelineCache

+__HOOKED_vkDestroyPipelineLayout

+__HOOKED_vkDestroyQueryPool

+__HOOKED_vkDestroyRenderPass

+__HOOKED_vkDestroySampler

+__HOOKED_vkDestroySemaphore

+__HOOKED_vkDestroyShaderModule

+__HOOKED_vkDestroySurfaceKHR

+__HOOKED_vkDestroySwapchainKHR

+__HOOKED_vkDeviceWaitIdle

+__HOOKED_vkEndCommandBuffer

+vkEnumerateDeviceExtensionProperties

+__HOOKED_vkEnumerateDeviceExtensionProperties

+vkEnumerateDeviceLayerProperties

+__HOOKED_vkEnumerateDeviceLayerProperties

+vkEnumerateInstanceLayerProperties

+vkEnumerateInstanceExtensionProperties

+__HOOKED_vkEnumeratePhysicalDevices

+__HOOKED_vkFlushMappedMemoryRanges

+__HOOKED_vkFreeCommandBuffers

+__HOOKED_vkFreeDescriptorSets

+__HOOKED_vkFreeMemory

+__HOOKED_vkGetBufferMemoryRequirements

+__HOOKED_vkGetDeviceMemoryCommitment

+VK_LAYER_LUNARG_vktraceGetDeviceProcAddr

+__HOOKED_vkGetDeviceProcAddr

+__HOOKED_vkGetDeviceQueue

+__HOOKED_vkGetEventStatus

+__HOOKED_vkGetFenceStatus

+__HOOKED_vkGetImageMemoryRequirements

+__HOOKED_vkGetImageSparseMemoryRequirements

+__HOOKED_vkGetImageSubresourceLayout

+VK_LAYER_LUNARG_vktraceGetInstanceProcAddr

+__HOOKED_vkGetInstanceProcAddr

+__HOOKED_vkGetPhysicalDeviceFeatures

+__HOOKED_vkGetPhysicalDeviceFormatProperties

+__HOOKED_vkGetPhysicalDeviceImageFormatProperties

+__HOOKED_vkGetPhysicalDeviceMemoryProperties

+__HOOKED_vkGetPhysicalDeviceProperties

+__HOOKED_vkGetPhysicalDeviceQueueFamilyProperties

+__HOOKED_vkGetPhysicalDeviceSparseImageFormatProperties

+__HOOKED_vkGetPhysicalDeviceSurfaceCapabilitiesKHR

+__HOOKED_vkGetPhysicalDeviceSurfaceFormatsKHR

+__HOOKED_vkGetPhysicalDeviceSurfacePresentModesKHR

+__HOOKED_vkGetPhysicalDeviceSurfaceSupportKHR

+__HOOKED_vkGetPhysicalDeviceWin32PresentationSupportKHR

+__HOOKED_vkGetPipelineCacheData

+__HOOKED_vkGetQueryPoolResults

+__HOOKED_vkGetRenderAreaGranularity

+__HOOKED_vkGetSwapchainImagesKHR

+__HOOKED_vkInvalidateMappedMemoryRanges

+__HOOKED_vkMapMemory

+__HOOKED_vkMergePipelineCaches

+__HOOKED_vkQueueBindSparse

+__HOOKED_vkQueuePresentKHR

+__HOOKED_vkQueueSubmit

+__HOOKED_vkQueueWaitIdle

+__HOOKED_vkResetCommandBuffer

+__HOOKED_vkResetCommandPool

+__HOOKED_vkResetDescriptorPool

+__HOOKED_vkResetEvent

+__HOOKED_vkResetFences

+__HOOKED_vkSetEvent

+__HOOKED_vkUnmapMemory

+__HOOKED_vkUpdateDescriptorSets

+__HOOKED_vkWaitForFences

diff --git a/vktrace/src/vktrace_layer/linux/VkLayer_vktrace_layer.json b/vktrace/src/vktrace_layer/linux/VkLayer_vktrace_layer.json
new file mode 100644
index 0000000..a187b32
--- /dev/null
+++ b/vktrace/src/vktrace_layer/linux/VkLayer_vktrace_layer.json
@@ -0,0 +1,15 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_vktrace",
+        "type": "GLOBAL",
+        "library_path": "../vktrace/libVkLayer_vktrace_layer.so",
+        "api_version": "1.0.38",
+        "implementation_version": "1",
+        "description": "Vktrace tracing library",
+        "functions" : {
+          "vkGetInstanceProcAddr" : "VK_LAYER_LUNARG_vktraceGetInstanceProcAddr",
+          "vkGetDeviceProcAddr" : "VK_LAYER_LUNARG_vktraceGetDeviceProcAddr"
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_lib.c b/vktrace/src/vktrace_layer/vktrace_lib.c
new file mode 100644
index 0000000..3921603
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#include "vktrace_common.h"
+#include "vktrace_filelike.h"
+#include "vktrace_interconnect.h"
+#include "vktrace_vk_vk.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Environment variables
+// These are needed because Windows may not have getenv available.
+// See the Windows man page for getenv to find out why.
+
+#if defined(_WIN32)
+static inline char *vktrace_layer_getenv(const char *name)
+{
+    char *retVal;
+    DWORD valSize;
+    valSize = GetEnvironmentVariableA(name, NULL, 0); 
+    // valSize DOES include the null terminator, so for any set variable
+    // will always be at least 1. If it's 0, the variable wasn't set.
+    if (valSize == 0)
+        return NULL;
+    retVal = (char *)malloc(valSize);
+    GetEnvironmentVariableA(name, retVal, valSize);
+    return retVal;
+}
+
+static inline void vktrace_layer_free_getenv(const char *val)
+{
+    free((void *)val);
+}
+#else
+static inline char *vktrace_layer_getenv(const char *name)
+{
+    return getenv(name);
+}
+
+static inline void vktrace_layer_free_getenv(const char *val) { }
+#endif
+
+VKTRACER_LEAVE _Unload(void);
+
+#ifdef PLATFORM_LINUX
+static void vktrace_sighandler(int signum, siginfo_t *info, void *ptr)
+{
+   vktrace_LogVerbose("vktrace_lib library handle signal %d.", signum);
+    _Unload();
+    kill(0, signum);
+}
+#endif
+
+VKTRACER_EXIT TrapExit(void)
+{
+    vktrace_LogVerbose("vktrace_lib TrapExit.");
+}
+
+void loggingCallback(VktraceLogLevel level, const char* pMessage)
+{
+    switch(level)
+    {
+    case VKTRACE_LOG_DEBUG: printf("vktrace debug: %s\n", pMessage); break;
+    case VKTRACE_LOG_ERROR: printf("vktrace error: %s\n", pMessage); break;
+    case VKTRACE_LOG_WARNING: printf("vktrace warning: %s\n", pMessage); break;
+    case VKTRACE_LOG_VERBOSE: printf("vktrace info: %s\n", pMessage); break;
+    default:
+        printf("%s\n", pMessage); break;
+    }
+    fflush(stdout);
+
+    if (vktrace_trace_get_trace_file() != NULL)
+    {
+        uint32_t requiredLength = (uint32_t) ROUNDUP_TO_4(strlen(pMessage) + 1);
+        vktrace_trace_packet_header* pHeader = vktrace_create_trace_packet(VKTRACE_TID_VULKAN, VKTRACE_TPI_MESSAGE, sizeof(vktrace_trace_packet_message), requiredLength);
+        vktrace_trace_packet_message* pPacket = vktrace_interpret_body_as_trace_packet_message(pHeader);
+        pPacket->type = level;
+        pPacket->length = requiredLength;
+
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&pPacket->message, requiredLength, pMessage);
+        vktrace_finalize_buffer_address(pHeader, (void**)&pPacket->message);
+        vktrace_set_packet_entrypoint_end_time(pHeader);
+        vktrace_finalize_trace_packet(pHeader);
+
+        vktrace_write_trace_packet(pHeader, vktrace_trace_get_trace_file());
+        vktrace_delete_trace_packet(&pHeader);
+    }
+
+#if defined(WIN32)
+#if _DEBUG
+    OutputDebugString(pMessage);
+#endif
+#endif
+
+#if defined(ANDROID)
+#include <android/log.h>
+    switch(level)
+    {
+    case VKTRACE_LOG_DEBUG:   __android_log_print(ANDROID_LOG_DEBUG,   "vktrace", "%s", pMessage); break;
+    case VKTRACE_LOG_ERROR:   __android_log_print(ANDROID_LOG_ERROR,   "vktrace", "%s", pMessage); break;
+    case VKTRACE_LOG_WARNING: __android_log_print(ANDROID_LOG_WARNING, "vktrace", "%s", pMessage); break;
+    case VKTRACE_LOG_VERBOSE: __android_log_print(ANDROID_LOG_INFO,    "vktrace", "%s", pMessage); break;
+    default:
+        __android_log_print(ANDROID_LOG_INFO, "vktrace", "%s", pMessage); break;
+    }
+#endif
+}
+
+extern
+VKTRACER_ENTRY _Load(void)
+{
+    // only do the hooking and networking if the tracer is NOT loaded by vktrace
+    if (vktrace_is_loaded_into_vktrace() == FALSE)
+    {
+        char *verbosity;
+        vktrace_LogSetCallback(loggingCallback);
+        verbosity = vktrace_layer_getenv("_VK_TRACE_VERBOSITY");
+        if (verbosity && !strcmp(verbosity, "quiet"))
+            vktrace_LogSetLevel(VKTRACE_LOG_NONE);
+        else if (verbosity && !strcmp(verbosity, "warnings"))
+            vktrace_LogSetLevel(VKTRACE_LOG_WARNING);
+        else if (verbosity && !strcmp(verbosity, "full"))
+            vktrace_LogSetLevel(VKTRACE_LOG_VERBOSE);
+#ifdef _DEBUG
+        else if (verbosity && !strcmp(verbosity, "debug"))
+            vktrace_LogSetLevel(VKTRACE_LOG_DEBUG);
+#endif
+        else
+            // Either verbosity=="errors", or it wasn't specified
+            vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+
+        vktrace_layer_free_getenv(verbosity);
+
+        vktrace_LogVerbose("vktrace_lib library loaded into PID %d", vktrace_get_pid());
+        atexit(TrapExit);
+
+        // If you need to debug startup, build with this set to true, then attach and change it to false.
+    #ifdef _DEBUG
+        {
+            bool debugStartup = false;
+        while (debugStartup);
+        }
+    #endif
+#ifdef PLATFORM_LINUX
+        struct sigaction act;
+        memset(&act, 0 , sizeof(act));
+        act.sa_sigaction = vktrace_sighandler;
+        act.sa_flags = SA_SIGINFO | SA_RESETHAND;
+        sigaction(SIGINT, &act, NULL);
+        sigaction(SIGTERM, &act, NULL);
+        sigaction(SIGABRT, &act, NULL);
+#endif
+    }
+}
+
+VKTRACER_LEAVE _Unload(void)
+{
+    // only do the hooking and networking if the tracer is NOT loaded by vktrace
+    if (vktrace_is_loaded_into_vktrace() == FALSE)
+    {
+        if (vktrace_trace_get_trace_file() != NULL) {
+            vktrace_trace_packet_header* pHeader = vktrace_create_trace_packet(VKTRACE_TID_VULKAN, VKTRACE_TPI_MARKER_TERMINATE_PROCESS, 0, 0);
+            vktrace_finalize_trace_packet(pHeader);
+            vktrace_write_trace_packet(pHeader, vktrace_trace_get_trace_file());
+            vktrace_delete_trace_packet(&pHeader);
+            vktrace_free(vktrace_trace_get_trace_file());
+            vktrace_trace_set_trace_file(NULL);
+        }
+        if (gMessageStream != NULL)
+        {
+            vktrace_MessageStream_destroy(&gMessageStream);
+        }
+        vktrace_LogVerbose("vktrace_lib library unloaded from PID %d", vktrace_get_pid());
+    }
+}
+
+#if defined(WIN32)
+BOOL APIENTRY DllMain( HMODULE hModule,
+                       DWORD  ul_reason_for_call,
+                       LPVOID lpReserved
+                     )
+{
+    hModule;
+    lpReserved;
+
+    switch (ul_reason_for_call)
+    {
+    case DLL_PROCESS_ATTACH:
+    {
+        _Load();
+        break;
+    }
+    case DLL_PROCESS_DETACH:
+    {
+        _Unload();
+        break;
+    }
+    default:
+        break;
+    }
+    return TRUE;
+}
+#endif
+#ifdef __cplusplus
+}
+#endif
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_helpers.h b/vktrace/src/vktrace_layer/vktrace_lib_helpers.h
new file mode 100644
index 0000000..988cfac
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_helpers.h
@@ -0,0 +1,429 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Tobin Ehlis <tobin@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+#pragma once
+#include <unordered_map>
+#include "vktrace_vk_vk.h"
+#include "vulkan/vk_layer.h"
+#include "vktrace_platform.h"
+
+#include "vk_struct_size_helper.h"
+
+// Support for shadowing CPU mapped memory
+//TODO better handling of multiple range rather than fixed array
+typedef struct _VKAllocInfo {
+    VkDeviceSize   totalSize;
+    VkDeviceSize   rangeSize;
+    VkDeviceSize   rangeOffset;
+    BOOL           didFlush;
+    VkDeviceMemory handle;
+    uint8_t        *pData;
+    BOOL           valid;
+} VKAllocInfo;
+
+typedef struct _VKMemInfo {
+    unsigned int numEntrys;
+    VKAllocInfo *pEntrys;
+    VKAllocInfo *pLastMapped;
+    unsigned int capacity;
+} VKMemInfo;
+
+typedef struct _layer_device_data {
+    VkLayerDispatchTable devTable;
+    bool KHRDeviceSwapchainEnabled;
+} layer_device_data;
+typedef struct _layer_instance_data {
+    VkLayerInstanceDispatchTable instTable;
+    bool LunargDebugReportEnabled;
+    bool KHRSurfaceEnabled;
+    bool KHRXcbSurfaceEnabled;
+    bool KHRXlibSurfaceEnabled;
+    bool KHRWaylandSurfaceEnabled;
+    bool KHRMirSurfaceEnabled;
+    bool KHRWin32SurfaceEnabled;
+    bool KHRAndroidSurfaceEnabled;
+} layer_instance_data;
+
+// defined in manually written file: vktrace_lib_trace.c
+extern VKMemInfo g_memInfo;
+extern VKTRACE_CRITICAL_SECTION g_memInfoLock;
+extern std::unordered_map<void *, layer_device_data *> g_deviceDataMap;
+extern std::unordered_map<void *, layer_instance_data *> g_instanceDataMap;
+
+typedef void *dispatch_key;
+inline dispatch_key get_dispatch_key(const void* object)
+{
+    return (dispatch_key) *(VkLayerDispatchTable **) object;
+}
+
+layer_instance_data *mid(void *object);
+layer_device_data *mdd(void* object);
+
+static void init_mem_info_entrys(VKAllocInfo *ptr, const unsigned int num)
+{
+    unsigned int i;
+    for (i = 0; i < num; i++)
+    {
+        VKAllocInfo *entry = ptr + i;
+        entry->pData = NULL;
+        entry->totalSize = 0;
+        entry->rangeSize = 0;
+        entry->rangeOffset = 0;
+        entry->didFlush = FALSE;
+        memset(&entry->handle, 0, sizeof(VkDeviceMemory));
+        entry->valid = FALSE;
+    }
+}
+
+// caller must hold the g_memInfoLock
+static void init_mem_info()
+{
+    g_memInfo.numEntrys = 0;
+    g_memInfo.capacity = 4096;
+    g_memInfo.pLastMapped = NULL;
+
+    g_memInfo.pEntrys = VKTRACE_NEW_ARRAY(VKAllocInfo, g_memInfo.capacity);
+
+    if (g_memInfo.pEntrys == NULL)
+        vktrace_LogError("init_mem_info()  malloc failed.");
+    else
+        init_mem_info_entrys(g_memInfo.pEntrys, g_memInfo.capacity);
+}
+
+// caller must hold the g_memInfoLock
+static void delete_mem_info()
+{
+    VKTRACE_DELETE(g_memInfo.pEntrys);
+    g_memInfo.pEntrys = NULL;
+    g_memInfo.numEntrys = 0;
+    g_memInfo.capacity = 0;
+    g_memInfo.pLastMapped = NULL;
+}
+
+// caller must hold the g_memInfoLock
+static VKAllocInfo * get_mem_info_entry()
+{
+    unsigned int i;
+    VKAllocInfo *entry;
+    if (g_memInfo.numEntrys > g_memInfo.capacity)
+    {
+        vktrace_LogError("get_mem_info_entry() bad internal state numEntrys %u.", g_memInfo.numEntrys);
+        return NULL;
+    }
+
+    entry = g_memInfo.pEntrys;
+    for (i = 0; i < g_memInfo.numEntrys; i++)
+    {
+        if ((entry + i)->valid == FALSE)
+            return entry + i;
+    }
+    if (g_memInfo.numEntrys == g_memInfo.capacity)
+    {  // grow the array 2x
+        g_memInfo.capacity *= 2;
+        g_memInfo.pEntrys = (VKAllocInfo *) VKTRACE_REALLOC(g_memInfo.pEntrys, g_memInfo.capacity * sizeof(VKAllocInfo));
+        if (g_memInfo.pEntrys == NULL)
+            vktrace_LogError("get_mem_info_entry() realloc failed.");
+        vktrace_LogDebug("realloc memInfo from %u to %u", g_memInfo.capacity /2, g_memInfo.capacity);
+        //init the newly added entrys
+        init_mem_info_entrys(g_memInfo.pEntrys + g_memInfo.capacity / 2, g_memInfo.capacity / 2);
+    }
+
+    assert(g_memInfo.numEntrys < g_memInfo.capacity);
+    entry = g_memInfo.pEntrys + g_memInfo.numEntrys;
+    g_memInfo.numEntrys++;
+    assert(entry->valid == FALSE);
+    return entry;
+}
+
+// caller must hold the g_memInfoLock
+static VKAllocInfo * find_mem_info_entry(const VkDeviceMemory handle)
+{
+    VKAllocInfo *entry;
+    unsigned int i;
+    entry = g_memInfo.pEntrys;
+    if (g_memInfo.pLastMapped && g_memInfo.pLastMapped->handle == handle && g_memInfo.pLastMapped->valid)
+    {
+        return g_memInfo.pLastMapped;
+    }
+    for (i = 0; i < g_memInfo.numEntrys; i++)
+    {
+        if ((entry + i)->valid && (handle == (entry + i)->handle))
+        {
+            return entry + i;
+        }
+    }
+
+    return NULL;
+}
+
+static VKAllocInfo * find_mem_info_entry_lock(const VkDeviceMemory handle)
+{
+    VKAllocInfo *res;
+    vktrace_enter_critical_section(&g_memInfoLock);
+    res = find_mem_info_entry(handle);
+    vktrace_leave_critical_section(&g_memInfoLock);
+    return res;
+}
+
+static void add_new_handle_to_mem_info(const VkDeviceMemory handle, VkDeviceSize size, void *pData)
+{
+    VKAllocInfo *entry;
+
+    vktrace_enter_critical_section(&g_memInfoLock);
+    if (g_memInfo.capacity == 0)
+        init_mem_info();
+
+    entry = get_mem_info_entry();
+    if (entry)
+    {
+        entry->valid = TRUE;
+        entry->handle = handle;
+        entry->totalSize = size;
+        entry->rangeSize = 0;
+        entry->rangeOffset = 0;
+        entry->didFlush = FALSE;
+        entry->pData = (uint8_t *) pData;   // NOTE: VKFreeMemory will free this mem, so no malloc()
+    }
+    vktrace_leave_critical_section(&g_memInfoLock);
+}
+
+static void add_data_to_mem_info(const VkDeviceMemory handle, VkDeviceSize rangeSize, VkDeviceSize rangeOffset, void *pData)
+{
+    VKAllocInfo *entry;
+
+    vktrace_enter_critical_section(&g_memInfoLock);
+    entry = find_mem_info_entry(handle);
+    if (entry)
+    {
+        entry->pData = (uint8_t *)pData;
+        if (rangeSize == VK_WHOLE_SIZE)
+            entry->rangeSize = entry->totalSize - rangeOffset;
+        else
+            entry->rangeSize = rangeSize;
+        entry->rangeOffset = rangeOffset;
+        assert(entry->totalSize >= entry->rangeSize + rangeOffset);
+    }
+    g_memInfo.pLastMapped = entry;
+    vktrace_leave_critical_section(&g_memInfoLock);
+}
+
+static void rm_handle_from_mem_info(const VkDeviceMemory handle)
+{
+    VKAllocInfo *entry;
+
+    vktrace_enter_critical_section(&g_memInfoLock);
+    entry = find_mem_info_entry(handle);
+    if (entry)
+    {
+        entry->valid = FALSE;
+        entry->pData = NULL;
+        entry->totalSize = 0;
+        entry->rangeSize = 0;
+        entry->rangeOffset = 0;
+        entry->didFlush = FALSE;
+        memset(&entry->handle, 0, sizeof(VkDeviceMemory));
+
+        if (entry == g_memInfo.pLastMapped)
+            g_memInfo.pLastMapped = NULL;
+        // adjust numEntrys to be last valid entry in list
+        do {
+            entry =  g_memInfo.pEntrys + g_memInfo.numEntrys - 1;
+            if (entry->valid == FALSE)
+                g_memInfo.numEntrys--;
+        } while ((entry->valid == FALSE) && (g_memInfo.numEntrys > 0));
+        if (g_memInfo.numEntrys == 0)
+            delete_mem_info();
+    }
+    vktrace_leave_critical_section(&g_memInfoLock);
+}
+
+static void add_alloc_memory_to_trace_packet(vktrace_trace_packet_header* pHeader, void** ppOut, const void* pIn)
+{
+    while (pIn)
+    {
+        switch (((VkApplicationInfo *)pIn)->sType)
+        {
+        case VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV:
+            vktrace_add_buffer_to_trace_packet(pHeader, ppOut, sizeof(VkDedicatedAllocationMemoryAllocateInfoNV), pIn);
+            vktrace_finalize_buffer_address(pHeader, ppOut);
+            break;
+        case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV:
+            vktrace_add_buffer_to_trace_packet(pHeader, ppOut, sizeof(VkExportMemoryAllocateInfoNV), pIn);
+            vktrace_finalize_buffer_address(pHeader, ppOut);
+            break;
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+        case VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV:
+            vktrace_add_buffer_to_trace_packet(pHeader, ppOut, sizeof(VkExportMemoryWin32HandleInfoNV), pIn);
+            vktrace_finalize_buffer_address(pHeader, ppOut);
+            break;
+
+        case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV:
+            vktrace_add_buffer_to_trace_packet(pHeader, ppOut, sizeof(VkImportMemoryWin32HandleInfoNV), pIn);
+            vktrace_finalize_buffer_address(pHeader, ppOut);
+            break;
+#endif
+        default:
+            vktrace_LogError("vkAllocateMemory: unrecognize pAllocate pNext list structure");
+            break;
+        }
+        pIn = ((VkApplicationInfo *)pIn)->pNext;
+    }
+}
+
+static void add_VkPipelineShaderStageCreateInfo_to_trace_packet(vktrace_trace_packet_header* pHeader, VkPipelineShaderStageCreateInfo* packetShader, const VkPipelineShaderStageCreateInfo* paramShader)
+{
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&packetShader->pName, ROUNDUP_TO_4(strlen(paramShader->pName) + 1), paramShader->pName);
+    vktrace_finalize_buffer_address(pHeader, (void**)&packetShader->pName);
+
+    // Specialization info
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&packetShader->pSpecializationInfo, sizeof(VkSpecializationInfo), paramShader->pSpecializationInfo);
+    if (packetShader->pSpecializationInfo != NULL)
+    {
+        if (paramShader->pSpecializationInfo != NULL) {
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&packetShader->pSpecializationInfo->pMapEntries, sizeof(VkSpecializationMapEntry) * paramShader->pSpecializationInfo->mapEntryCount, paramShader->pSpecializationInfo->pMapEntries);
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&packetShader->pSpecializationInfo->pData, paramShader->pSpecializationInfo->dataSize, paramShader->pSpecializationInfo->pData);
+            vktrace_finalize_buffer_address(pHeader, (void**)&packetShader->pSpecializationInfo->pMapEntries);
+            vktrace_finalize_buffer_address(pHeader, (void**)&packetShader->pSpecializationInfo->pData);
+        }
+    }
+    vktrace_finalize_buffer_address(pHeader, (void**)&packetShader->pSpecializationInfo);
+}
+
+static void add_create_ds_layout_to_trace_packet(vktrace_trace_packet_header* pHeader, const VkDescriptorSetLayoutCreateInfo** ppOut, const VkDescriptorSetLayoutCreateInfo* pIn)
+{
+    uint32_t i;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)(ppOut), sizeof(VkDescriptorSetLayoutCreateInfo), pIn);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppOut)->pBindings), sizeof(VkDescriptorSetLayoutBinding) * pIn->bindingCount, pIn->pBindings);
+    for (i = 0; i < pIn->bindingCount; i++) {
+        if (pIn->pBindings[i].pImmutableSamplers != NULL &&
+                (pIn->pBindings[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
+                 pIn->pBindings[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)) {
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppOut)->pBindings[i].pImmutableSamplers),
+                                               sizeof(VkSampler) * pIn->pBindings[i].descriptorCount,
+                                               pIn->pBindings[i].pImmutableSamplers);
+            vktrace_finalize_buffer_address(pHeader, (void**)&((*ppOut)->pBindings[i].pImmutableSamplers));
+        }
+    }
+    vktrace_finalize_buffer_address(pHeader, (void**)&((*ppOut)->pBindings));
+    vktrace_finalize_buffer_address(pHeader, (void**)(ppOut));
+    return;
+}
+
+static void add_VkGraphicsPipelineCreateInfos_to_trace_packet(vktrace_trace_packet_header* pHeader, VkGraphicsPipelineCreateInfo* pPacket, const VkGraphicsPipelineCreateInfo* pParam, uint32_t count)
+{
+    if (pParam != NULL)
+    {
+        uint32_t i;
+        uint32_t j;
+
+        for (i = 0; i < count; i++) {
+            // shader stages array
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pStages), sizeof(VkPipelineShaderStageCreateInfo) * pParam[i].stageCount, pParam[i].pStages);
+            for (j = 0; j < pParam[i].stageCount; j++)
+            {
+                add_VkPipelineShaderStageCreateInfo_to_trace_packet(pHeader, (VkPipelineShaderStageCreateInfo*)&pPacket->pStages[j], &pParam->pStages[j]);
+            }
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pStages));
+
+            // Vertex Input State
+            if (pParam[i].pVertexInputState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pVertexInputState), sizeof(VkPipelineVertexInputStateCreateInfo), pParam[i].pVertexInputState);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pVertexInputState->pVertexBindingDescriptions), pParam[i].pVertexInputState->vertexBindingDescriptionCount * sizeof(VkVertexInputBindingDescription), pParam[i].pVertexInputState->pVertexBindingDescriptions);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pVertexInputState->pVertexBindingDescriptions));
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pVertexInputState->pVertexAttributeDescriptions), pParam[i].pVertexInputState->vertexAttributeDescriptionCount * sizeof(VkVertexInputAttributeDescription), pParam[i].pVertexInputState->pVertexAttributeDescriptions);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pVertexInputState->pVertexAttributeDescriptions));
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pVertexInputState));
+            }
+            // Input Assembly State
+            if (pParam[i].pInputAssemblyState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pInputAssemblyState), sizeof(VkPipelineInputAssemblyStateCreateInfo), pParam[i].pInputAssemblyState);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pInputAssemblyState));
+            }
+            // Tesselation State
+            if (pParam[i].pTessellationState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pTessellationState), sizeof(VkPipelineTessellationStateCreateInfo), pParam[i].pTessellationState);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pTessellationState));
+            }
+            // Viewport State
+            if (pParam[i].pViewportState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pViewportState), sizeof(VkPipelineViewportStateCreateInfo), pParam[i].pViewportState);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pViewportState->pViewports), sizeof(VkViewport) * pParam[i].pViewportState->viewportCount,
+                                                   pParam[i].pViewportState->pViewports);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pViewportState->pViewports));
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pViewportState->pScissors), sizeof(VkRect2D) * pParam[i].pViewportState->scissorCount,
+                                                   pParam[i].pViewportState->pScissors);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pViewportState->pScissors));
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pViewportState));
+            }
+
+            // Raster State
+            if (pParam[i].pRasterizationState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pRasterizationState), sizeof(VkPipelineRasterizationStateCreateInfo), pParam[i].pRasterizationState);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pRasterizationState));
+            }
+            // MultiSample State
+            if (pParam[i].pMultisampleState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMultisampleState), sizeof(VkPipelineMultisampleStateCreateInfo), pParam[i].pMultisampleState);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMultisampleState->pSampleMask), sizeof(VkSampleMask), pParam[i].pMultisampleState->pSampleMask);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMultisampleState->pSampleMask));
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMultisampleState));
+            }
+
+            // DepthStencil State
+            if (pParam[i].pDepthStencilState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDepthStencilState), sizeof(VkPipelineDepthStencilStateCreateInfo), pParam[i].pDepthStencilState);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDepthStencilState));
+            }
+
+            // ColorBlend State
+            if (pParam[i].pColorBlendState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pColorBlendState), sizeof(VkPipelineColorBlendStateCreateInfo), pParam[i].pColorBlendState);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pColorBlendState->pAttachments), pParam[i].pColorBlendState->attachmentCount * sizeof(VkPipelineColorBlendAttachmentState), pParam[i].pColorBlendState->pAttachments);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pColorBlendState->pAttachments));
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pColorBlendState));
+            }
+
+            // DynamicState
+            if (pParam[i].pDynamicState) {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDynamicState), sizeof(VkPipelineDynamicStateCreateInfo), pParam[i].pDynamicState);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDynamicState->pDynamicStates), pParam[i].pDynamicState->dynamicStateCount * sizeof(VkDynamicState), pParam[i].pDynamicState->pDynamicStates);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDynamicState->pDynamicStates));
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDynamicState));
+            }
+        }
+    }
+    return;
+}
+
+static void add_VkComputePipelineCreateInfos_to_trace_packet(vktrace_trace_packet_header* pHeader, VkComputePipelineCreateInfo* pPacket, const VkComputePipelineCreateInfo* pParam, uint32_t count)
+{
+    if (pParam != NULL)
+    {
+        uint32_t i;
+
+        for (i = 0; i < count; i++) {
+            // shader stage
+            add_VkPipelineShaderStageCreateInfo_to_trace_packet(pHeader, (VkPipelineShaderStageCreateInfo*)&pPacket->stage, &pParam[i].stage);
+        }
+    }
+    return;
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pageguard.cpp b/vktrace/src/vktrace_layer/vktrace_lib_pageguard.cpp
new file mode 100644
index 0000000..c7ca2f4
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pageguard.cpp
@@ -0,0 +1,427 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+* Copyright (C) 2015-2016 LunarG, Inc.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "vktrace_pageguard_memorycopy.h"
+#include "vktrace_lib_pagestatusarray.h"
+#include "vktrace_lib_pageguardmappedmemory.h"
+#include "vktrace_lib_pageguardcapture.h"
+#include "vktrace_lib_pageguard.h"
+
+#if !defined(ANDROID)
+static const bool PAGEGUARD_PAGEGUARD_ENABLE_DEFAULT = true;
+#else
+static const bool PAGEGUARD_PAGEGUARD_ENABLE_DEFAULT = false;
+#endif
+
+static const VkDeviceSize PAGEGUARD_TARGET_RANGE_SIZE_DEFAULT = 2; //cover all reasonal mapped memory size, the mapped memory size may be less than 1 page, so processing for mapped memory size<1 page is already added,
+                                                                   //other value: 32 * 1024 * 1024 (32M),  64M, this is the size which cause DOOM4 capture very slow.
+static const VkDeviceSize  PAGEGUARD_PAGEGUARD_TARGET_RANGE_SIZE_MIN = 1; // already tested: 2,2M,4M,32M,64M, because commonly page size is 4k, so only range size=2 can cover small size mapped memory.
+
+#if defined(WIN32)
+static vktrace_sem_id ref_amount_sem_id;// TODO if vktrace implement cross platform lib or dll load or unload function, this sem can be putted in those functions, but now we leave it to process quit.
+static bool ref_amount_sem_id_create_success = vktrace_sem_create(&ref_amount_sem_id, 1);
+#endif
+static vktrace_sem_id map_lock_sem_id;
+static bool map_lock_sem_id_create_success = vktrace_sem_create(&map_lock_sem_id, 1);
+
+void pageguardEnter()
+{
+    vktrace_sem_wait(map_lock_sem_id);
+}
+void pageguardExit()
+{
+    vktrace_sem_post(map_lock_sem_id);
+}
+
+
+VkDeviceSize& ref_target_range_size()
+{
+    static VkDeviceSize OPTTargetRangeSize = PAGEGUARD_TARGET_RANGE_SIZE_DEFAULT;
+    return OPTTargetRangeSize;
+}
+
+void set_pageguard_target_range_size(VkDeviceSize newrangesize)
+{
+    VkDeviceSize& refTargetRangeSize = ref_target_range_size();
+
+    refTargetRangeSize = newrangesize;
+}
+
+#if defined(WIN32)
+LONG WINAPI PageGuardExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);
+#endif
+
+PVOID OPTHandler = nullptr; //use to remove page guard handler
+uint32_t OPTHandlerRefAmount = 0; //for persistent map and multi-threading environment, map and unmap maybe overlap, we need to make sure remove handler after all persistent map has been unmapped.
+
+//return if enable pageguard;
+//if enable page guard, then check if need to update target range size, page guard only work for those persistent mapped memory which >= target range size. 
+bool getPageGuardEnableFlag()
+{
+    static bool EnablePageGuard = PAGEGUARD_PAGEGUARD_ENABLE_DEFAULT;
+    static bool FirstTimeRun = true;
+    if (FirstTimeRun)
+    {
+        FirstTimeRun = false;
+        const char *env_pageguard = vktrace_get_global_var(PAGEGUARD_PAGEGUARD_ENABLE_ENV);
+        if (env_pageguard)
+        {
+            int envvalue;
+            if (sscanf(env_pageguard, "%d", &envvalue) == 1)
+            {
+                if (envvalue)
+                {
+                    EnablePageGuard = true;
+                    const char *env_target_range_size = vktrace_get_global_var(PAGEGUARD_PAGEGUARD_TARGET_RANGE_SIZE_ENV);
+                    if (env_target_range_size)
+                    {
+                        VkDeviceSize rangesize;
+                        if (sscanf(env_target_range_size, "%" PRIx64, &rangesize) == 1)
+                        {
+                            if (rangesize > PAGEGUARD_PAGEGUARD_TARGET_RANGE_SIZE_MIN)
+                            {
+                                set_pageguard_target_range_size(rangesize);
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    EnablePageGuard = false;
+                }
+            }
+        }
+    }
+    return EnablePageGuard;
+}
+
+bool getEnableReadPMBFlag()
+{
+    static bool EnableReadPMB;
+    static bool FirstTimeRun = true;
+    if (FirstTimeRun)
+    {
+        EnableReadPMB = (vktrace_get_global_var(PAGEGUARD_PAGEGUARD_ENABLE_READ_PMB_ENV) != NULL);
+        FirstTimeRun = false;
+    }
+    return EnableReadPMB;
+}
+
+#if defined(WIN32)
+void setPageGuardExceptionHandler()
+{
+    vktrace_sem_wait(ref_amount_sem_id);
+    if (!OPTHandler)
+    {
+        OPTHandler = AddVectoredExceptionHandler(1, PageGuardExceptionHandler);
+        OPTHandlerRefAmount = 1;
+    }
+    else
+    {
+        OPTHandlerRefAmount++;
+    }
+    vktrace_sem_post(ref_amount_sem_id);
+}
+
+void removePageGuardExceptionHandler()
+{
+    vktrace_sem_wait(ref_amount_sem_id);
+    if (OPTHandler)
+    {
+        if (OPTHandlerRefAmount)
+        {
+            OPTHandlerRefAmount--;
+        }
+        if (!OPTHandlerRefAmount)
+        {
+            RemoveVectoredExceptionHandler(OPTHandler);
+            OPTHandler = nullptr;
+        }
+    }
+    vktrace_sem_post(ref_amount_sem_id);
+}
+#endif
+
+size_t pageguardGetAdjustedSize(size_t size)
+{
+    size_t pagesize = pageguardGetSystemPageSize();
+    if (size % pagesize)
+    {
+        size = size - (size % pagesize) + pagesize;
+    }
+    return size;
+}
+
+#if defined(PLATFORM_LINUX)
+// Keep a map of memory allocations and sizes.
+// We need the size when we want to free the memory on Linux.
+static std::unordered_map<void *, size_t> allocateMemoryMap;
+#endif
+
+// Page guard only works for virtual memory. Real device memory
+// sometimes doesn't have a page concept, so we can't use page guard
+// to track it (or check dirty bits in /proc/<pid>/pagemap).
+// So we allocate virtual memory to return to the app and we
+// keep it sync'ed it with real device memory.
+void* pageguardAllocateMemory(size_t size)
+{
+    void* pMemory = nullptr;
+    if (size != 0)
+    {
+#if defined(WIN32)
+        pMemory = (PBYTE)VirtualAlloc(nullptr, pageguardGetAdjustedSize(size),
+                                      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+#else
+        pMemory = mmap(NULL, pageguardGetAdjustedSize(size),
+                       PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+        if (pMemory != nullptr)
+            allocateMemoryMap[pMemory] = pageguardGetAdjustedSize(size);
+#endif
+    }
+    if (pMemory == nullptr)
+        vktrace_LogError("pageguardAllocateMemory(%d) memory allocation failed", size);
+    return pMemory;
+}
+
+void pageguardFreeMemory(void* pMemory)
+{
+    if (pMemory)
+    {
+#if defined(WIN32)
+        VirtualFree(pMemory, 0, MEM_RELEASE);
+#else
+        munmap(pMemory, allocateMemoryMap[pMemory]);
+        allocateMemoryMap.erase(pMemory);
+#endif
+    }
+}
+
+DWORD pageguardGetSystemPageSize()
+{
+#if defined(PLATFORM_LINUX)
+    return getpagesize();
+#elif defined(WIN32)
+    SYSTEM_INFO sSysInfo;
+    GetSystemInfo(&sSysInfo);
+    return sSysInfo.dwPageSize;
+#endif
+}
+
+void setFlagTovkFlushMappedMemoryRangesSpecial(PBYTE pOPTPackageData)
+{
+    PageGuardChangedBlockInfo *pChangedInfoArray = (PageGuardChangedBlockInfo *)pOPTPackageData;
+    pChangedInfoArray[0].reserve0 = pChangedInfoArray[0].reserve0 | PAGEGUARD_SPECIAL_FORMAT_PACKET_FOR_VKFLUSHMAPPEDMEMORYRANGES;
+}
+
+PageGuardCapture& getPageGuardControlInstance()
+{
+    static PageGuardCapture OPTControl;
+    return OPTControl;
+}
+
+void flushTargetChangedMappedMemory(LPPageGuardMappedMemory TargetMappedMemory, vkFlushMappedMemoryRangesFunc pFunc, VkMappedMemoryRange*  pMemoryRanges)
+{
+    bool newMemoryRangesInside = (pMemoryRanges == nullptr);
+    if (newMemoryRangesInside)
+    {
+        pMemoryRanges = new VkMappedMemoryRange[1];
+        assert(pMemoryRanges);
+    }
+    pMemoryRanges[0].memory = TargetMappedMemory->getMappedMemory();
+    pMemoryRanges[0].offset = TargetMappedMemory->getMappedOffset();
+    pMemoryRanges[0].pNext = nullptr;
+    pMemoryRanges[0].size = TargetMappedMemory->getMappedSize();
+    pMemoryRanges[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+    (*pFunc)(TargetMappedMemory->getMappedDevice(), 1, pMemoryRanges);
+    if (newMemoryRangesInside)
+    {
+        delete[] pMemoryRanges;
+    }
+}
+
+void flushAllChangedMappedMemory(vkFlushMappedMemoryRangesFunc pFunc)
+{
+    LPPageGuardMappedMemory pMappedMemoryTemp;
+    uint64_t amount = getPageGuardControlInstance().getMapMemory().size();
+    if (amount)
+    {
+        int i = 0;
+        VkMappedMemoryRange*  pMemoryRanges = new VkMappedMemoryRange[1];//amount
+        for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin(); it != getPageGuardControlInstance().getMapMemory().end(); it++)
+        {
+            pMappedMemoryTemp = &(it->second);
+            flushTargetChangedMappedMemory(pMappedMemoryTemp, pFunc, pMemoryRanges);
+            i++;
+        }
+        delete[] pMemoryRanges;
+    }
+}
+
+void resetAllReadFlagAndPageGuard()
+{
+    LPPageGuardMappedMemory pMappedMemoryTemp;
+    uint64_t amount = getPageGuardControlInstance().getMapMemory().size();
+    if (amount)
+    {
+        int i = 0;
+        for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin(); it != getPageGuardControlInstance().getMapMemory().end(); it++)
+        {
+            pMappedMemoryTemp = &(it->second);
+            pMappedMemoryTemp->resetMemoryObjectAllReadFlagAndPageGuard();
+            i++;
+        }
+    }
+}
+
+#ifdef WIN32
+LONG WINAPI PageGuardExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
+{
+    LONG resultCode = EXCEPTION_CONTINUE_SEARCH;
+    pageguardEnter();
+    if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
+    {
+        VkDeviceSize OffsetOfAddr;
+        PBYTE pBlock;
+        VkDeviceSize BlockSize;
+        PBYTE addr = reinterpret_cast<PBYTE>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
+        bool bWrite = ExceptionInfo->ExceptionRecord->ExceptionInformation[0];
+
+        LPPageGuardMappedMemory pMappedMem = getPageGuardControlInstance().findMappedMemoryObject(addr, &OffsetOfAddr, &pBlock, &BlockSize);
+        if (pMappedMem)
+        {
+            uint64_t index = pMappedMem->getIndexOfChangedBlockByAddr(addr);
+            if (!getEnableReadPMBFlag() || bWrite)
+            {
+                pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_CHANGED);
+            }
+            else
+            {
+
+#ifndef PAGEGUARD_ADD_PAGEGUARD_ON_REAL_MAPPED_MEMORY
+                vktrace_pageguard_memcpy(pBlock, pMappedMem->getRealMappedDataPointer() + OffsetOfAddr - OffsetOfAddr % BlockSize, pMappedMem->getMappedBlockSize(index));
+                pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_READ);
+
+#else
+                pMappedMem->setMappedBlockChanged(index, true);
+#endif
+            }
+            resultCode = EXCEPTION_CONTINUE_EXECUTION;
+        }
+    }
+    pageguardExit();
+    return resultCode;
+}
+#endif
+
+//The function source code is modified from __HOOKED_vkFlushMappedMemoryRanges
+//for coherent map, need this function to dump data so simulate target application write data when playback.
+VkResult vkFlushMappedMemoryRangesWithoutAPICall(
+    VkDevice device,
+    uint32_t memoryRangeCount,
+    const VkMappedMemoryRange* pMemoryRanges)
+{
+    VkResult result = VK_SUCCESS;
+    vktrace_trace_packet_header* pHeader;
+    size_t rangesSize = 0;
+    size_t dataSize = 0;
+    uint32_t iter;
+    packet_vkFlushMappedMemoryRanges* pPacket = nullptr;
+
+#ifdef USE_PAGEGUARD_SPEEDUP
+    PBYTE *ppPackageData = new PBYTE[memoryRangeCount];
+    getPageGuardControlInstance().vkFlushMappedMemoryRangesPageGuardHandle(device, memoryRangeCount, pMemoryRanges, ppPackageData);//the packet is not needed if no any change on data of all ranges
+#endif
+
+    // find out how much memory is in the ranges
+    for (iter = 0; iter < memoryRangeCount; iter++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[iter];
+        rangesSize += vk_size_vkmappedmemoryrange(pRange);
+        dataSize += (size_t)pRange->size;
+    }
+#ifdef USE_PAGEGUARD_SPEEDUP
+    dataSize = getPageGuardControlInstance().getALLChangedPackageSizeInMappedMemory(device, memoryRangeCount, pMemoryRanges, ppPackageData);
+#endif
+    CREATE_TRACE_PACKET(vkFlushMappedMemoryRanges, rangesSize + sizeof(void*)*memoryRangeCount + dataSize);
+    pPacket = interpret_body_as_vkFlushMappedMemoryRanges(pHeader);
+
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMemoryRanges), rangesSize, pMemoryRanges);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMemoryRanges));
+
+    // insert into packet the data that was written by CPU between the vkMapMemory call and here
+    // create a temporary local ppData array and add it to the packet (to reserve the space for the array)
+    void** ppTmpData = (void **)malloc(memoryRangeCount * sizeof(void*));
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData), sizeof(void*)*memoryRangeCount, ppTmpData);
+    free(ppTmpData);
+
+    // now the actual memory
+    vktrace_enter_critical_section(&g_memInfoLock);
+    for (iter = 0; iter < memoryRangeCount; iter++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[iter];
+        VKAllocInfo* pEntry = find_mem_info_entry(pRange->memory);
+
+        if (pEntry != nullptr)
+        {
+            assert(pEntry->handle == pRange->memory);
+            assert(pEntry->totalSize >= (pRange->size + pRange->offset));
+            assert(pEntry->totalSize >= pRange->size);
+#ifdef USE_PAGEGUARD_SPEEDUP
+            LPPageGuardMappedMemory pOPTMemoryTemp = getPageGuardControlInstance().findMappedMemoryObject(device, pRange);
+            VkDeviceSize OPTPackageSizeTemp = 0;
+            if (pOPTMemoryTemp)
+            {
+                PBYTE pOPTDataTemp = pOPTMemoryTemp->getChangedDataPackage(&OPTPackageSizeTemp);
+                setFlagTovkFlushMappedMemoryRangesSpecial(pOPTDataTemp);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData[iter]), OPTPackageSizeTemp, pOPTDataTemp);
+                pOPTMemoryTemp->clearChangedDataPackage();
+                pOPTMemoryTemp->resetMemoryObjectAllChangedFlagAndPageGuard();
+            }
+            else
+            {
+                PBYTE pOPTDataTemp = getPageGuardControlInstance().getChangedDataPackageOutOfMap(ppPackageData, iter, &OPTPackageSizeTemp);
+                setFlagTovkFlushMappedMemoryRangesSpecial(pOPTDataTemp);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData[iter]), OPTPackageSizeTemp, pOPTDataTemp);
+                getPageGuardControlInstance().clearChangedDataPackageOutOfMap(ppPackageData, iter);
+            }
+#else
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData[iter]), pRange->size, pEntry->pData + pRange->offset);
+#endif
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData[iter]));
+            pEntry->didFlush = true;
+        }
+        else
+        {
+            vktrace_LogError("Failed to copy app memory into trace packet (idx = %u) on vkFlushedMappedMemoryRanges", pHeader->global_packet_index);
+        }
+    }
+#ifdef USE_PAGEGUARD_SPEEDUP
+    delete[] ppPackageData;
+#endif
+    vktrace_leave_critical_section(&g_memInfoLock);
+
+    // now finalize the ppData array since it is done being updated
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData));
+
+    //result = mdd(device)->devTable.FlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket->device = device;
+    pPacket->memoryRangeCount = memoryRangeCount;
+    pPacket->result = result;
+
+    FINISH_TRACE_PACKET();
+    return result;
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pageguard.h b/vktrace/src/vktrace_layer/vktrace_lib_pageguard.h
new file mode 100644
index 0000000..8877517
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pageguard.h
@@ -0,0 +1,115 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+//  Optimization by using page-guard for speed up capture and capture persistent mapped memory
+//
+//  Background:
+//
+//     The speed is extremely slow when use vktrace to capture DOOM4. It took over half a day and 900G of trace for a capture from beginning to the game menu.
+//     The reason that caused such slow capture is DOOM updates a big mapped memory(over 67M) frequently, vktrace copies this memory block to harddrive when DOOM calls vkFlushmappedMemory to update it every time.
+//     Here we use page guard to record which page of big memory block has been changed and only save those changed pages, it make the capture time reduce to round 15 minutes, the trace file size is round 40G, 
+//     The Playback time for these trace file is round 7 minutes(on Win10/AMDFury/32GRam/I5 system).
+//
+//     The page guard method also has been tested ok for capture/playback SaschaWillems(July20) applications. Test system:  CPU: AMD FX-9590, GPU: R9 200 Series, RAM: 16 GB, Driver: Crimson Public Driver 16.7.3.
+//
+//  Page guard process:
+//
+//     1. The target app call map buffer and get a mapped memory pointer(for example pMemReal).
+//
+//     2. The capturer then allocates a duplicated memory of at least the same size(for example pMemCopy). Copies all data from pMemReal to pMemCopy
+//
+//     3. The capturer adds page guard to pMemCopy for every page.Then returns the pMemCopy(not pMemReal) to the target app.
+//
+//     4. When the target app uses pMemCopy to write, page guard will be triggered and page guard handler will records which page has been changed.
+//
+//     5. When the target app uses pMemCopy to read data, page guard will be triggered and page guard handler records which page the target app wants to read. And only copy that page from real mapped memory to copied memory so after page guard exception, target app will read right data.
+//
+//     6. When the target app calls CPU->GPU synchronization(vkFlushMappedMemoryRanges and submits queue), the capturer saves all changed pages and also copies back these changed pages to pMemReal from pMemCopy, resets all page guard which triggered by write.
+//
+//     7. When the target app calls GPU->CPU synchronization(vkInvalidateMappedMemoryRanges, also include vkQueueSubmit which is before that synchronization happen), clear all read array and resets all page guard which triggered by read.
+//
+//     8. When the target app calls to unmap the memory, the capturer saves all changed pages to HD and also copies back these changed pages to pMemReal from pMemCopy. Finally removes all page guards and frees pMemCopy.
+//
+//  Known limitations:
+//
+//     1. for a page which is in mapped memory, if target app first read it before write, then do GPU->CPU synchronization like vkFlushMappedMemoryRanges trigger flush copied mapped memory( pMemCopy ) to real mapped memory( pMemReal ), that page will not be recorded as changed page.
+//
+//     2. one page accessed by a thread and the access just happen when another thread already finish copying date to real mapped memory for that page but haven't reset page guard of that page, that page will not be recorded as changed page.
+
+#pragma once
+
+#include <stdbool.h>
+#include <unordered_map>
+#include "vulkan/vulkan.h"
+#include "vktrace_platform.h"
+#include "vktrace_common.h"
+
+#include "vktrace_interconnect.h"
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_exts.h"
+#include <stdio.h>
+
+#include "vktrace_pageguard_memorycopy.h"
+
+
+#define PAGEGUARD_PAGEGUARD_ENABLE_ENV "_VKTRACE_OPTIMIZE_PMB"
+#define PAGEGUARD_PAGEGUARD_TARGET_RANGE_SIZE_ENV "VKTRACE_PAGEGUARDTARGETSIZE"
+
+// VKTRACE_PAGEGUARD_ENABLE_READ_PMB env var enables read PMB support. Only
+// supported on Windows. Not yet tested.
+#define PAGEGUARD_PAGEGUARD_ENABLE_READ_PMB_ENV "VKTRACE_PAGEGUARD_ENABLE_READ_PMB"
+
+// Usefull macro for handling fatal errors during tracing
+#define VKTRACE_FATAL_ERROR(_m) \
+    do { \
+        vktrace_LogError(_m " Fatal error."); \
+        exit(1); \
+    } while (0)
+
+
+VkDeviceSize& ref_target_range_size();
+bool getPageGuardEnableFlag();
+bool getEnableReadPMBFlag();
+#if defined(WIN32)
+void setPageGuardExceptionHandler();
+void removePageGuardExceptionHandler();
+#endif
+size_t pageguardGetAdjustedSize(size_t size);
+void* pageguardAllocateMemory(size_t size);
+void pageguardFreeMemory(void* pMemory);
+DWORD pageguardGetSystemPageSize();
+
+void pageguardEnter();
+void pageguardExit();
+
+
+void setFlagTovkFlushMappedMemoryRangesSpecial(PBYTE pOPTPackageData);
+
+
+void flushAllChangedMappedMemory(vkFlushMappedMemoryRangesFunc pFunc);
+
+void flushTargetChangedMappedMemory(LPPageGuardMappedMemory TargetMappedMemory, vkFlushMappedMemoryRangesFunc pFunc, VkMappedMemoryRange*  pMemoryRanges);
+
+void resetAllReadFlagAndPageGuard();
+
+#if defined(WIN32)
+LONG WINAPI PageGuardExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);
+#endif
+
+VkResult vkFlushMappedMemoryRangesWithoutAPICall(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges);
+
+PageGuardCapture& getPageGuardControlInstance();
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.cpp b/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.cpp
new file mode 100644
index 0000000..8028cc7
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.cpp
@@ -0,0 +1,401 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+//OPT: Optimization by using page-guard for speed up capture
+//     The speed is extremely slow when use vktrace to capture DOOM4. It took over half a day and 900G of trace for a capture from beginning to the game menu.
+//     The reason that caused such slow capture is DOOM updates a big mapped memory(over 67M) frequently, vktrace copies this memory block to harddrive when DOOM calls vkFlushmappedMemory to update it every time.
+//     Here we use page guard to record which page of big memory block has been changed and only save those changed pages, it make the capture time reduce to round 15 minutes, the trace file size is round 40G, 
+//     The Playback time for these trace file is round 7 minutes(on Win10/AMDFury/32GRam/I5 system).
+
+#include "vktrace_pageguard_memorycopy.h"
+#include "vktrace_lib_pagestatusarray.h"
+#include "vktrace_lib_pageguardmappedmemory.h"
+#include "vktrace_lib_pageguardcapture.h"
+#include "vktrace_lib_pageguard.h"
+
+
+PageGuardCapture::PageGuardCapture()
+{
+    EmptyChangedInfoArray.offset = 0;
+    EmptyChangedInfoArray.length = 0;
+
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    // Open the /proc/self/clear_refs file. We'll write to that file
+    // when we want to clear all the page dirty bits in /proc/self/pagemap.
+    clearRefsFd = open("/proc/self/clear_refs", O_WRONLY);
+    if (clearRefsFd < 0)
+        VKTRACE_FATAL_ERROR("Open of /proc/self/clear_refs failed.");
+
+    // Clear the dirty bits, i.e. write a '4' to clear_refs. Some older
+    // kernels may require that '4' be written to it in order
+    // for /proc/self/pagemap to work as we we expect it to.
+    pageRefsDirtyClear();
+#endif
+
+}
+
+std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >& PageGuardCapture::getMapMemory()
+{
+    return MapMemory;
+}
+
+void PageGuardCapture::vkMapMemoryPageGuardHandle(
+    VkDevice device,
+    VkDeviceMemory memory,
+    VkDeviceSize offset,
+    VkDeviceSize size,
+    VkFlags flags,
+    void** ppData)
+{
+    PageGuardMappedMemory OPTmappedmem;
+    if (getPageGuardEnableFlag())
+    {
+#ifdef PAGEGUARD_TARGET_RANGE_SIZE_CONTROL
+        if (size >= ref_target_range_size())
+#endif
+        {
+            OPTmappedmem.vkMapMemoryPageGuardHandle(device, memory, offset, size, flags, ppData);
+            MapMemory[memory] = OPTmappedmem;
+        }
+    }
+    MapMemoryPtr[memory] = (PBYTE)(*ppData);
+    MapMemoryOffset[memory] = offset;
+    MapMemorySize[memory] = size;
+}
+
+void PageGuardCapture::vkUnmapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, void** MappedData, vkFlushMappedMemoryRangesFunc pFunc)
+{
+    LPPageGuardMappedMemory lpOPTMemoryTemp = findMappedMemoryObject(device, memory);
+    if (lpOPTMemoryTemp)
+    {
+        VkMappedMemoryRange memoryRange;
+        flushTargetChangedMappedMemory(lpOPTMemoryTemp, pFunc, &memoryRange);
+        lpOPTMemoryTemp->vkUnmapMemoryPageGuardHandle(device, memory, MappedData);
+        MapMemory.erase(memory);
+    }
+    MapMemoryPtr.erase(memory);
+    MapMemoryOffset.erase(memory);
+    MapMemorySize.erase(memory);
+}
+
+void* PageGuardCapture::getMappedMemoryPointer(VkDevice device, VkDeviceMemory memory)
+{
+    return MapMemoryPtr[memory];
+}
+
+VkDeviceSize PageGuardCapture::getMappedMemoryOffset(VkDevice device, VkDeviceMemory memory)
+{
+    return MapMemoryOffset[memory];
+}
+
+VkDeviceSize PageGuardCapture::getMappedMemorySize(VkDevice device, VkDeviceMemory memory)
+{
+    return MapMemorySize[memory];
+}
+
+//return: if it's target mapped memory and no change at all;
+//PBYTE *ppPackageDataforOutOfMap, must be an array include memoryRangeCount elements
+bool PageGuardCapture::vkFlushMappedMemoryRangesPageGuardHandle(
+    VkDevice device,
+    uint32_t memoryRangeCount,
+    const VkMappedMemoryRange* pMemoryRanges,
+    PBYTE *ppPackageDataforOutOfMap)
+{
+    bool handleSuccessfully = false, bChanged = false;
+    std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::const_iterator mappedmem_it;
+    for (uint32_t i = 0; i < memoryRangeCount; i++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[i];
+
+        ppPackageDataforOutOfMap[i] = nullptr;
+        LPPageGuardMappedMemory lpOPTMemoryTemp = findMappedMemoryObject(device, pRange->memory);
+
+        if (lpOPTMemoryTemp)
+        {
+            if (pRange->size == VK_WHOLE_SIZE)
+            {
+                pRange->size = lpOPTMemoryTemp->getMappedSize() - (pRange->offset - lpOPTMemoryTemp->MappedOffset);
+            }
+            if (lpOPTMemoryTemp->vkFlushMappedMemoryRangePageGuardHandle(device, pRange->memory, pRange->offset, pRange->size, nullptr, nullptr, nullptr))
+            {
+                bChanged = true;
+            }
+        }
+        else
+        {
+            bChanged = true;
+            VkDeviceSize RealRangeSize = pRange->size;
+            if (RealRangeSize == VK_WHOLE_SIZE)
+            {
+                RealRangeSize = MapMemorySize[pRange->memory] - (pRange->offset - MapMemoryOffset[pRange->memory]);
+            }
+            ppPackageDataforOutOfMap[i] = (PBYTE)pageguardAllocateMemory(RealRangeSize + 2 * sizeof(PageGuardChangedBlockInfo));
+            PageGuardChangedBlockInfo *pInfoTemp = (PageGuardChangedBlockInfo *)ppPackageDataforOutOfMap[i];
+            pInfoTemp[0].offset = 1;
+            pInfoTemp[0].length = (DWORD)RealRangeSize;
+            pInfoTemp[0].reserve0 = 0;
+            pInfoTemp[0].reserve1 = 0;
+            pInfoTemp[1].offset = pRange->offset - getMappedMemoryOffset(device, pRange->memory);
+            pInfoTemp[1].length = (DWORD)RealRangeSize;
+            pInfoTemp[1].reserve0 = 0;
+            pInfoTemp[1].reserve1 = 0;
+            PBYTE pDataInPackage = (PBYTE)(pInfoTemp + 2);
+            void* pDataMapped = getMappedMemoryPointer(device, pRange->memory);
+            vktrace_pageguard_memcpy(pDataInPackage, reinterpret_cast<PBYTE>(pDataMapped) + pInfoTemp[1].offset, RealRangeSize);
+        }
+    }
+    if (!bChanged)
+    {
+        handleSuccessfully = true;
+    }
+    return handleSuccessfully;
+}
+
+LPPageGuardMappedMemory PageGuardCapture::findMappedMemoryObject(VkDevice device, VkDeviceMemory memory)
+{
+    LPPageGuardMappedMemory pMappedMemoryObject = nullptr;
+    std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::const_iterator mappedmem_it;
+    mappedmem_it = MapMemory.find(memory);
+    if (mappedmem_it != MapMemory.end())
+    {
+        pMappedMemoryObject = ((PageGuardMappedMemory *)&(mappedmem_it->second));
+        if (pMappedMemoryObject->MappedDevice != device)
+        {
+            pMappedMemoryObject = nullptr;
+        }
+    }
+    return pMappedMemoryObject;
+}
+
+LPPageGuardMappedMemory PageGuardCapture::findMappedMemoryObject(PBYTE addr, VkDeviceSize *pOffsetOfAddr, PBYTE *ppBlock, VkDeviceSize *pBlockSize)
+{
+    LPPageGuardMappedMemory pMappedMemoryObject = nullptr;
+    LPPageGuardMappedMemory pMappedMemoryTemp;
+    PBYTE pBlock = nullptr;
+    VkDeviceSize OffsetOfAddr = 0, BlockSize = 0;
+
+    for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = MapMemory.begin(); it != MapMemory.end(); it++)
+    {
+        pMappedMemoryTemp = &(it->second);
+        if ((addr >= pMappedMemoryTemp->pMappedData) && (addr < (pMappedMemoryTemp->pMappedData + pMappedMemoryTemp->MappedSize)))
+        {
+            pMappedMemoryObject = pMappedMemoryTemp;
+
+            OffsetOfAddr = (VkDeviceSize)(addr - pMappedMemoryTemp->pMappedData);
+            BlockSize = pMappedMemoryTemp->PageGuardSize;
+            pBlock = addr - OffsetOfAddr % BlockSize;
+            if (ppBlock)
+            {
+                *ppBlock = pBlock;
+            }
+            if (pBlockSize)
+            {
+                *pBlockSize = BlockSize;
+            }
+            if (pOffsetOfAddr)
+            {
+                *pOffsetOfAddr = OffsetOfAddr;
+            }
+
+            return pMappedMemoryObject;
+        }
+    }
+    return NULL;
+}
+
+LPPageGuardMappedMemory PageGuardCapture::findMappedMemoryObject(VkDevice device, const VkMappedMemoryRange* pMemoryRange)
+{
+    LPPageGuardMappedMemory pMappedMemoryObject = findMappedMemoryObject(device, pMemoryRange->memory);
+    return pMappedMemoryObject;
+}
+
+//get size of all changed package in array of pMemoryRanges
+VkDeviceSize PageGuardCapture::getALLChangedPackageSizeInMappedMemory(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges, PBYTE *ppPackageDataforOutOfMap)
+{
+    VkDeviceSize allChangedPackageSize = 0, PackageSize = 0;
+    LPPageGuardMappedMemory pMappedMemoryTemp;
+    for (uint32_t i = 0; i < memoryRangeCount; i++)
+    {
+        pMappedMemoryTemp = findMappedMemoryObject(device, pMemoryRanges + i);
+        if (pMappedMemoryTemp)
+        {
+            pMappedMemoryTemp->getChangedDataPackage(&PackageSize);
+        }
+        else
+        {
+            PageGuardChangedBlockInfo *pInfoTemp = (PageGuardChangedBlockInfo *)ppPackageDataforOutOfMap[i];
+            PackageSize = pInfoTemp->length + 2 * sizeof(PageGuardChangedBlockInfo);
+        }
+        allChangedPackageSize += PackageSize;
+    }
+    return allChangedPackageSize;
+}
+
+//get ptr and size of OPTChangedDataPackage;
+PBYTE PageGuardCapture::getChangedDataPackageOutOfMap(PBYTE *ppPackageDataforOutOfMap, DWORD dwRangeIndex, VkDeviceSize  *pSize)
+{
+    PBYTE pDataPackage = (PBYTE)ppPackageDataforOutOfMap[dwRangeIndex];
+    PageGuardChangedBlockInfo *pInfo = (PageGuardChangedBlockInfo *)pDataPackage;
+    if (pSize)
+    {
+        *pSize = sizeof(PageGuardChangedBlockInfo) * 2 + pInfo->length;
+    }
+    return pDataPackage;
+}
+
+void PageGuardCapture::clearChangedDataPackageOutOfMap(PBYTE *ppPackageDataforOutOfMap, DWORD dwRangeIndex)
+{
+    pageguardFreeMemory(ppPackageDataforOutOfMap[dwRangeIndex]);
+    ppPackageDataforOutOfMap[dwRangeIndex] = nullptr;
+}
+
+bool PageGuardCapture::isHostWriteFlagSetInMemoryBarriers(uint32_t  memoryBarrierCount, const VkMemoryBarrier*  pMemoryBarriers)
+{
+    bool flagSet = false;
+    if ((memoryBarrierCount != 0) && (pMemoryBarriers))
+    {
+        for (uint32_t i = 0; i < memoryBarrierCount; i++)
+        {
+            if (pMemoryBarriers[i].srcAccessMask&VK_ACCESS_HOST_WRITE_BIT)
+            {
+                flagSet = true;
+            }
+        }
+    }
+    return flagSet;
+}
+
+bool PageGuardCapture::isHostWriteFlagSetInBufferMemoryBarrier(uint32_t  memoryBarrierCount, const VkBufferMemoryBarrier*  pMemoryBarriers)
+{
+    bool flagSet = false;
+    if ((memoryBarrierCount != 0) && (pMemoryBarriers))
+    {
+        for (uint32_t i = 0; i < memoryBarrierCount; i++)
+        {
+            if (pMemoryBarriers[i].srcAccessMask&VK_ACCESS_HOST_WRITE_BIT)
+            {
+                flagSet = true;
+            }
+        }
+    }
+    return flagSet;
+}
+
+bool PageGuardCapture::isHostWriteFlagSetInImageMemoryBarrier(uint32_t  memoryBarrierCount, const VkImageMemoryBarrier*  pMemoryBarriers)
+{
+    bool flagSet = false;
+    if ((memoryBarrierCount != 0) && (pMemoryBarriers))
+    {
+        for (uint32_t i = 0; i < memoryBarrierCount; i++)
+        {
+            if (pMemoryBarriers[i].srcAccessMask&VK_ACCESS_HOST_WRITE_BIT)
+            {
+                flagSet = true;
+            }
+        }
+    }
+    return flagSet;
+}
+
+bool PageGuardCapture::isHostWriteFlagSet(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags  dependencyFlags,
+    uint32_t   memoryBarrierCount, const VkMemoryBarrier*   pMemoryBarriers,
+    uint32_t  bufferMemoryBarrierCount, const VkBufferMemoryBarrier*  pBufferMemoryBarriers,
+    uint32_t  imageMemoryBarrierCount, const VkImageMemoryBarrier*  pImageMemoryBarriers)
+{
+    bool flagSet = false, bWrite = isHostWriteFlagSetInMemoryBarriers(memoryBarrierCount, pMemoryBarriers) ||
+        isHostWriteFlagSetInBufferMemoryBarrier(bufferMemoryBarrierCount, pBufferMemoryBarriers) ||
+        isHostWriteFlagSetInImageMemoryBarrier(imageMemoryBarrierCount, pImageMemoryBarriers);
+    if (bWrite || (srcStageMask&VK_PIPELINE_STAGE_HOST_BIT))
+    {
+        flagSet = true;
+    }
+    return flagSet;
+}
+
+bool PageGuardCapture::isReadyForHostReadInMemoryBarriers(uint32_t  memoryBarrierCount, const VkMemoryBarrier*  pMemoryBarriers)
+{
+    bool isReady = false;
+    if ((memoryBarrierCount != 0) && (pMemoryBarriers))
+    {
+        for (uint32_t i = 0; i < memoryBarrierCount; i++)
+        {
+            if (pMemoryBarriers[i].dstAccessMask&VK_ACCESS_HOST_READ_BIT)
+            {
+                isReady = true;
+            }
+        }
+    }
+    return isReady;
+}
+
+bool PageGuardCapture::isReadyForHostReadInBufferMemoryBarrier(uint32_t  memoryBarrierCount, const VkBufferMemoryBarrier*  pMemoryBarriers)
+{
+    bool isReady = false;
+    if ((memoryBarrierCount != 0) && (pMemoryBarriers))
+    {
+        for (uint32_t i = 0; i < memoryBarrierCount; i++)
+        {
+            if (pMemoryBarriers[i].dstAccessMask&VK_ACCESS_HOST_READ_BIT)
+            {
+                isReady = true;
+            }
+        }
+    }
+    return isReady;
+}
+
+bool PageGuardCapture::isReadyForHostReadInImageMemoryBarrier(uint32_t  memoryBarrierCount, const VkImageMemoryBarrier*  pMemoryBarriers)
+{
+    bool isReady = false;
+    if ((memoryBarrierCount != 0) && (pMemoryBarriers))
+    {
+        for (uint32_t i = 0; i < memoryBarrierCount; i++)
+        {
+            if ((pMemoryBarriers[i].dstAccessMask&VK_ACCESS_HOST_READ_BIT))
+            {
+                isReady = true;
+            }
+        }
+    }
+    return isReady;
+}
+
+bool PageGuardCapture::isReadyForHostRead(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags  dependencyFlags,
+    uint32_t   memoryBarrierCount, const VkMemoryBarrier*   pMemoryBarriers,
+    uint32_t  bufferMemoryBarrierCount, const VkBufferMemoryBarrier*  pBufferMemoryBarriers,
+    uint32_t  imageMemoryBarrierCount, const VkImageMemoryBarrier*  pImageMemoryBarriers)
+{
+    bool isReady = false, bRead = isReadyForHostReadInMemoryBarriers(memoryBarrierCount, pMemoryBarriers) ||
+        isReadyForHostReadInBufferMemoryBarrier(bufferMemoryBarrierCount, pBufferMemoryBarriers) ||
+        isReadyForHostReadInImageMemoryBarrier(imageMemoryBarrierCount, pImageMemoryBarriers);
+    if (bRead || (dstStageMask&VK_PIPELINE_STAGE_HOST_BIT))
+    {
+        isReady = true;
+    }
+    return isReady;
+}
+
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+void PageGuardCapture::pageRefsDirtyClear()
+{
+    char four='4';
+    if (clearRefsFd >=0)
+    {
+        lseek(clearRefsFd, 0, SEEK_SET);
+        if (1 != write(clearRefsFd, &four, 1))
+            VKTRACE_FATAL_ERROR("Write to /proc/self/clear_refs failed.");
+    }
+}
+#endif
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.h b/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.h
new file mode 100644
index 0000000..941a096
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pageguardcapture.h
@@ -0,0 +1,117 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#pragma once
+
+#include <stdbool.h>
+#include <unordered_map>
+#include "vulkan/vulkan.h"
+#include "vktrace_platform.h"
+#include "vktrace_common.h"
+
+#include "vktrace_interconnect.h"
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_exts.h"
+#include <stdio.h>
+
+#include "vktrace_pageguard_memorycopy.h"
+#include "vktrace_lib_pageguardmappedmemory.h"
+
+#define PAGEGUARD_TARGET_RANGE_SIZE_CONTROL
+
+//PAGEGUARD_ADD_PAGEGUARD_ON_REAL_MAPPED_MEMORY is a compile flag for add page guard on real mapped memory.
+//If comment this flag, pageguard will be added on a copy of mapped memory, with the flag, page guard will be added to
+//real mapped memory.
+//for some hareware, add to mapped memory not the copy of it may not be allowed, so turn on this flag just for you are already sure page guard can work on that hardware.
+//If add page guard to the copy of mapped memory, it's always allowed but need to do synchonization between the mapped memory and its copy.
+
+//#define PAGEGUARD_ADD_PAGEGUARD_ON_REAL_MAPPED_MEMORY
+
+typedef VkResult(*vkFlushMappedMemoryRangesFunc)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange*  pMemoryRanges);
+
+typedef class PageGuardCapture
+{
+private:
+    PageGuardChangedBlockInfo EmptyChangedInfoArray;
+    std::unordered_map< VkDeviceMemory, PageGuardMappedMemory > MapMemory;
+    std::unordered_map< VkDeviceMemory, PBYTE > MapMemoryPtr;
+    std::unordered_map< VkDeviceMemory, VkDeviceSize > MapMemorySize;
+    std::unordered_map< VkDeviceMemory, VkDeviceSize > MapMemoryOffset;
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    int clearRefsFd;
+#endif
+
+public:
+
+    PageGuardCapture();
+
+    std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >& getMapMemory();
+
+    void vkMapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkFlags flags, void** ppData);
+
+    void vkUnmapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, void** MappedData, vkFlushMappedMemoryRangesFunc pFunc);
+
+    void* getMappedMemoryPointer(VkDevice device, VkDeviceMemory memory);
+
+    VkDeviceSize getMappedMemoryOffset(VkDevice device, VkDeviceMemory memory);
+
+    VkDeviceSize getMappedMemorySize(VkDevice device, VkDeviceMemory memory);
+
+    /// return: if it's target mapped memory and no change at all;
+    /// PBYTE *ppPackageDataforOutOfMap, must be an array include memoryRangeCount elements
+    bool vkFlushMappedMemoryRangesPageGuardHandle(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges, PBYTE *ppPackageDataforOutOfMap);
+
+    LPPageGuardMappedMemory findMappedMemoryObject(VkDevice device, VkDeviceMemory memory);
+
+    LPPageGuardMappedMemory findMappedMemoryObject(PBYTE addr, VkDeviceSize *pOffsetOfAddr = nullptr, PBYTE *ppBlock = nullptr, VkDeviceSize *pBlockSize = nullptr);
+
+    LPPageGuardMappedMemory findMappedMemoryObject(VkDevice device, const VkMappedMemoryRange* pMemoryRange);
+
+    /// get size of all changed package in array of pMemoryRanges
+    VkDeviceSize getALLChangedPackageSizeInMappedMemory(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges, PBYTE *ppPackageDataforOutOfMap);
+
+    /// get ptr and size of OPTChangedDataPackage;
+    PBYTE getChangedDataPackageOutOfMap(PBYTE *ppPackageDataforOutOfMap, DWORD dwRangeIndex, VkDeviceSize  *pSize);
+
+    void clearChangedDataPackageOutOfMap(PBYTE *ppPackageDataforOutOfMap, DWORD dwRangeIndex);
+
+    bool isHostWriteFlagSetInMemoryBarriers(uint32_t  memoryBarrierCount, const VkMemoryBarrier*  pMemoryBarriers);
+
+    bool isHostWriteFlagSetInBufferMemoryBarrier(uint32_t  memoryBarrierCount, const VkBufferMemoryBarrier*  pMemoryBarriers);
+
+    bool isHostWriteFlagSetInImageMemoryBarrier(uint32_t  memoryBarrierCount, const VkImageMemoryBarrier*  pMemoryBarriers);
+
+    bool isHostWriteFlagSet(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags  dependencyFlags,
+        uint32_t   memoryBarrierCount, const VkMemoryBarrier*   pMemoryBarriers,
+        uint32_t  bufferMemoryBarrierCount, const VkBufferMemoryBarrier*  pBufferMemoryBarriers,
+        uint32_t  imageMemoryBarrierCount, const VkImageMemoryBarrier*  pImageMemoryBarriers);
+
+    bool isReadyForHostReadInMemoryBarriers(uint32_t  memoryBarrierCount, const VkMemoryBarrier*  pMemoryBarriers);
+
+    bool isReadyForHostReadInBufferMemoryBarrier(uint32_t  memoryBarrierCount, const VkBufferMemoryBarrier*  pMemoryBarriers);
+
+    bool isReadyForHostReadInImageMemoryBarrier(uint32_t  memoryBarrierCount, const VkImageMemoryBarrier*  pMemoryBarriers);
+
+    bool isReadyForHostRead(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags  dependencyFlags,
+        uint32_t   memoryBarrierCount, const VkMemoryBarrier*   pMemoryBarriers,
+        uint32_t  bufferMemoryBarrierCount, const VkBufferMemoryBarrier*  pBufferMemoryBarriers,
+        uint32_t  imageMemoryBarrierCount, const VkImageMemoryBarrier*  pImageMemoryBarriers);
+
+#if defined(PLATFORM_LINUX) && !defined(ANDROID)
+    void pageRefsDirtyClear();
+#endif
+
+} PageGuardCapture;
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.cpp b/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.cpp
new file mode 100644
index 0000000..eb9667e
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.cpp
@@ -0,0 +1,519 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+//OPT: Optimization by using page-guard for speed up capture
+//     The speed is extremely slow when use vktrace to capture DOOM4. It took over half a day and 900G of trace for a capture from beginning to the game menu.
+//     The reason that caused such slow capture is DOOM updates a big mapped memory(over 67M) frequently, vktrace copies this memory block to harddrive when DOOM calls vkFlushmappedMemory to update it every time.
+//     Here we use page guard to record which page of big memory block has been changed and only save those changed pages, it make the capture time reduce to round 15 minutes, the trace file size is round 40G, 
+//     The Playback time for these trace file is round 7 minutes(on Win10/AMDFury/32GRam/I5 system).
+
+#include "vktrace_pageguard_memorycopy.h"
+#include "vktrace_lib_pagestatusarray.h"
+#include "vktrace_lib_pageguardmappedmemory.h"
+#include "vktrace_lib_pageguardcapture.h"
+#include "vktrace_lib_pageguard.h"
+
+VkDevice& PageGuardMappedMemory::getMappedDevice()
+{
+    return MappedDevice;
+}
+
+VkDeviceMemory& PageGuardMappedMemory::getMappedMemory()
+{
+    return MappedMemory;
+}
+
+VkDeviceSize& PageGuardMappedMemory::getMappedOffset()
+{
+    return MappedOffset;
+}
+
+PBYTE& PageGuardMappedMemory::getRealMappedDataPointer()
+{
+    return pRealMappedData;
+}
+
+VkDeviceSize& PageGuardMappedMemory::getMappedSize()
+{
+    return MappedSize;
+}
+
+PageGuardMappedMemory::PageGuardMappedMemory()
+    :MappedDevice(nullptr),
+    MappedMemory((VkDeviceMemory)nullptr),
+    pMappedData(nullptr),
+    pRealMappedData(nullptr),
+    pChangedDataPackage(nullptr),
+    MappedSize(0),
+    PageGuardSize(pageguardGetSystemPageSize()),
+    pPageStatus(nullptr),
+    BlockConflictError(false),
+    PageSizeLeft(0),
+    PageGuardAmount(0)
+{
+}
+
+PageGuardMappedMemory::~PageGuardMappedMemory()
+{
+}
+
+bool PageGuardMappedMemory::isUseCopyForRealMappedMemory()
+{
+    bool useCopyForRealMappedMemory = false;
+    if (pRealMappedData)
+    {
+        useCopyForRealMappedMemory = true;
+    }
+    return useCopyForRealMappedMemory;
+}
+
+bool PageGuardMappedMemory::getChangedRangeByIndex(uint64_t index, PBYTE *pAddress, VkDeviceSize *pBlockSize)
+{
+    bool isValidResult = false;
+    if (index < PageGuardAmount)
+    {
+        isValidResult = true;
+        if ((index + 1) == PageGuardAmount)
+        {
+            if (pAddress)
+            {
+                *pAddress = pMappedData + index*PageGuardSize;
+            }
+            if (pBlockSize)
+            {
+                *pBlockSize = (SIZE_T)(PageSizeLeft ? PageSizeLeft : PageGuardSize);
+            }
+        }
+        else
+        {
+            if (pAddress)
+            {
+                *pAddress = pMappedData + index*PageGuardSize;
+            }
+            if (pBlockSize)
+            {
+                *pBlockSize = (SIZE_T)PageGuardSize;
+            }
+        }
+    }
+    return isValidResult;
+}
+
+//if return value ==-1, mean addr is out of page guard.
+uint64_t PageGuardMappedMemory::getIndexOfChangedBlockByAddr(PBYTE addr)
+{
+    int64_t addrOffset = addr - pMappedData;
+    uint64_t indexOfChangedBlockByAddr = -1;
+    if ((addrOffset >= 0) && ((VkDeviceSize)addrOffset < MappedSize))
+    {
+        indexOfChangedBlockByAddr = addrOffset / PageGuardSize;
+    }
+    return indexOfChangedBlockByAddr;
+}
+
+void PageGuardMappedMemory::setMappedBlockChanged(uint64_t index, bool changed, int which)
+{
+    if (index < PageGuardAmount)
+    {
+        switch (which)
+        {
+        case BLOCK_FLAG_ARRAY_CHANGED:
+            pPageStatus->setBlockChangedArray(index, changed);
+            break;
+
+        case BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT:
+            pPageStatus->setBlockChangedArraySnapshot(index, changed);
+            break;
+
+        case BLOCK_FLAG_ARRAY_READ_SNAPSHOT:
+            pPageStatus->setBlockReadArraySnapshot(index, changed);
+            break;
+
+        default://BLOCK_FLAG_ARRAY_READ
+            pPageStatus->setBlockReadArray(index, changed);
+            break;
+        }
+    }
+}
+
+bool PageGuardMappedMemory::isMappedBlockChanged(uint64_t index, int which)
+{
+    bool mappedBlockChanged = false;
+    if (index < PageGuardAmount)
+    {
+        switch (which)
+        {
+        case BLOCK_FLAG_ARRAY_CHANGED:
+            mappedBlockChanged = pPageStatus->getBlockChangedArray(index);
+            break;
+
+        case BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT:
+            mappedBlockChanged = pPageStatus->getBlockChangedArraySnapshot(index);
+            break;
+
+        case BLOCK_FLAG_ARRAY_READ_SNAPSHOT:
+            mappedBlockChanged = pPageStatus->getBlockReadArraySnapshot(index);
+            break;
+
+        case BLOCK_FLAG_ARRAY_READ:
+            mappedBlockChanged = pPageStatus->getBlockReadArray(index);
+            break;
+
+        default:
+            assert(false);
+            break;
+        }
+    }
+    return mappedBlockChanged;
+}
+
+uint64_t PageGuardMappedMemory::getMappedBlockSize(uint64_t index)
+{
+    uint64_t mappedBlockSize = PageGuardSize;
+    if ((index + 1) == PageGuardAmount)
+    {
+        if (PageSizeLeft)
+        {
+            mappedBlockSize = PageSizeLeft;
+        }
+    }
+    return mappedBlockSize;
+}
+
+uint64_t PageGuardMappedMemory::getMappedBlockOffset(uint64_t index)
+{
+    uint64_t mappedBlockOffset = 0;
+    if (index < PageGuardAmount)
+    {
+        mappedBlockOffset = index*PageGuardSize;
+    }
+    return mappedBlockOffset;
+}
+
+bool PageGuardMappedMemory::isNoMappedBlockChanged()
+{
+    bool noMappedBlockChanged = true;
+    for (uint64_t i = 0; i < PageGuardAmount; i++)
+    {
+        if (isMappedBlockChanged(i, BLOCK_FLAG_ARRAY_CHANGED))
+        {
+            noMappedBlockChanged = false;
+            break;
+        }
+    }
+    return noMappedBlockChanged;
+}
+
+void PageGuardMappedMemory::resetMemoryObjectAllChangedFlagAndPageGuard()
+{
+    for (uint64_t i = 0; i < PageGuardAmount; i++)
+    {
+        if (isMappedBlockChanged(i, BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT))
+        {
+            #if defined(WIN32)
+            DWORD oldProt;
+            VirtualProtect(pMappedData + i*PageGuardSize, (SIZE_T)getMappedBlockSize(i), PAGE_READWRITE | PAGE_GUARD, &oldProt);
+            #endif
+            setMappedBlockChanged(i, false, BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT);
+        }
+    }
+}
+
+void PageGuardMappedMemory::resetMemoryObjectAllReadFlagAndPageGuard()
+{
+    backupBlockReadArraySnapshot();
+    for (uint64_t i = 0; i < PageGuardAmount; i++)
+    {
+        if (isMappedBlockChanged(i, BLOCK_FLAG_ARRAY_READ_SNAPSHOT))
+        {
+            #if defined(WIN32)
+            DWORD oldProt;
+            VirtualProtect(pMappedData + i*PageGuardSize, (SIZE_T)getMappedBlockSize(i), PAGE_READWRITE | PAGE_GUARD, &oldProt);
+            #endif
+            setMappedBlockChanged(i, false, BLOCK_FLAG_ARRAY_READ_SNAPSHOT);
+        }
+    }
+}
+
+bool PageGuardMappedMemory::setAllPageGuardAndFlag(bool bSetPageGuard, bool bSetBlockChanged)
+{
+    bool setSuccessfully = true;
+    #if defined(WIN32)
+    DWORD dwMemSetting = bSetPageGuard ? (PAGE_READWRITE | PAGE_GUARD) : PAGE_READWRITE;
+    #endif
+
+    for (uint64_t i = 0; i < PageGuardAmount; i++)
+    {
+        #if defined(WIN32)
+        DWORD oldProt, dwErr;
+        if (!VirtualProtect(pMappedData + i*PageGuardSize, (SIZE_T)getMappedBlockSize(i), dwMemSetting, &oldProt))
+        {
+            dwErr = GetLastError();
+            setSuccessfully = false;
+        }
+        #endif
+        setMappedBlockChanged(i, bSetBlockChanged, BLOCK_FLAG_ARRAY_CHANGED);
+    }
+    return setSuccessfully;
+}
+
+bool PageGuardMappedMemory::vkMapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkFlags flags, void** ppData)
+{
+    bool handleSuccessfully = true;
+    MappedDevice = device;
+    MappedMemory = memory;
+    MappedOffset = offset;
+#ifndef PAGEGUARD_ADD_PAGEGUARD_ON_REAL_MAPPED_MEMORY
+    pRealMappedData = (PBYTE)*ppData;
+    pMappedData = (PBYTE)pageguardAllocateMemory(size);
+    vktrace_pageguard_memcpy(pMappedData, pRealMappedData, size);
+    *ppData = pMappedData;
+#else
+    pMappedData = (PBYTE)*ppData;
+#endif
+    MappedSize = size;
+
+#ifdef WIN32
+    setPageGuardExceptionHandler();
+#endif
+
+    PageSizeLeft = size % PageGuardSize;
+    PageGuardAmount = size / PageGuardSize;
+    if (PageSizeLeft != 0)
+    {
+        PageGuardAmount++;
+    }
+    pPageStatus = new PageStatusArray(PageGuardAmount);
+    assert(pPageStatus);
+    if (!setAllPageGuardAndFlag(true, false))
+    {
+        handleSuccessfully = false;
+    }
+    return handleSuccessfully;
+}
+
+void PageGuardMappedMemory::vkUnmapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, void** MappedData)
+{
+    if ((memory == MappedMemory) && (device == MappedDevice))
+    {
+        setAllPageGuardAndFlag(false, false);
+#ifdef WIN32
+        removePageGuardExceptionHandler();
+#endif
+        clearChangedDataPackage();
+#ifndef PAGEGUARD_ADD_PAGEGUARD_ON_REAL_MAPPED_MEMORY
+        if (MappedData == nullptr)
+        {
+            vktrace_pageguard_memcpy(pRealMappedData, pMappedData, MappedSize);
+            pageguardFreeMemory(pMappedData);
+        }
+        else
+        {
+            vktrace_pageguard_memcpy(pRealMappedData, pMappedData, MappedSize);
+            *MappedData = pMappedData;
+        }
+        pRealMappedData = nullptr;
+        pMappedData = nullptr;
+#else
+        pMappedData = nullptr;
+        if (MappedData != nullptr)
+        {
+            *MappedData = nullptr;
+        }
+#endif
+        delete pPageStatus;
+        pPageStatus = nullptr;
+        MappedMemory =(VkDeviceMemory)nullptr;
+        MappedSize = 0;
+    }
+}
+
+void PageGuardMappedMemory::backupBlockChangedArraySnapshot()
+{
+    pPageStatus->backupChangedArray();
+}
+
+void PageGuardMappedMemory::backupBlockReadArraySnapshot()
+{
+    pPageStatus->backupReadArray();
+}
+
+DWORD PageGuardMappedMemory::getChangedBlockAmount(int useWhich)
+{
+    DWORD dwAmount = 0;
+    for (uint64_t i = 0; i < PageGuardAmount; i++)
+    {
+        if (isMappedBlockChanged(i, useWhich))
+        {
+            dwAmount++;
+        }
+    }
+    return dwAmount;
+}
+
+//is RangeLimit cover or partly cover Range
+bool PageGuardMappedMemory::isRangeIncluded(VkDeviceSize RangeOffsetLimit, VkDeviceSize RangeSizeLimit, VkDeviceSize RangeOffset, VkDeviceSize RangeSize)
+{
+    bool rangeIncluded = false;
+    if ((RangeOffsetLimit <= RangeOffset) &&
+        ((RangeOffsetLimit + RangeSizeLimit)>RangeOffset))
+    {
+        rangeIncluded = true;
+    }
+    else
+    {
+        if ((RangeOffsetLimit<(RangeOffset + RangeSize)) &&
+            ((RangeOffsetLimit + RangeSizeLimit) >= (RangeOffset + RangeSize)))
+        {
+            rangeIncluded = true;
+        }
+
+    }
+    return rangeIncluded;
+}
+
+//for output,
+//if pData!=nullptr,the pData + Offset is head addr of an array of PageGuardChangedBlockInfo, the [0] is block amount, size (size for all changed blocks which amount is block amount),then block1 offset,block1 size...., 
+//               the block? offset is  this changed block offset to mapped memory head addr,the array followed by changed blocks data
+//
+//if pData==nullptr, only get size
+//DWORD *pdwSaveSize, the size of all changed blocks
+//DWORD *pInfoSize, the size of array of PageGuardChangedBlockInfo
+//VkDeviceSize RangeOffset, RangeSize, only consider the block which is in the range which start from RangeOffset and size is RangeSize, if RangeOffset<0, consider whole mapped memory
+//return the amount of changed blocks.
+DWORD PageGuardMappedMemory::getChangedBlockInfo(VkDeviceSize RangeOffset, VkDeviceSize RangeSize, DWORD *pdwSaveSize, DWORD *pInfoSize, PBYTE pData, DWORD DataOffset, int useWhich)
+{
+    DWORD dwAmount = getChangedBlockAmount(useWhich), dwIndex = 0, offset = 0;
+    DWORD infosize = sizeof(PageGuardChangedBlockInfo)*(dwAmount + 1), SaveSize = 0, CurrentBlockSize = 0;
+    PBYTE pChangedData;
+    PageGuardChangedBlockInfo *pChangedInfoArray = (PageGuardChangedBlockInfo *)(pData ? (pData + DataOffset) : nullptr);
+
+    if (pInfoSize)
+    {
+        *pInfoSize = infosize;
+    }
+    for (uint64_t i = 0; i < PageGuardAmount; i++)
+    {
+        CurrentBlockSize = getMappedBlockSize(i);
+        offset = getMappedBlockOffset(i);
+        if (isMappedBlockChanged(i, useWhich))
+        {
+            if (pChangedInfoArray)
+            {
+                pChangedInfoArray[dwIndex + 1].offset = offset;
+                pChangedInfoArray[dwIndex + 1].length = CurrentBlockSize;
+                pChangedInfoArray[dwIndex + 1].reserve0 = 0;
+                pChangedInfoArray[dwIndex + 1].reserve1 = 0;
+                pChangedData = pData + DataOffset + infosize + SaveSize;
+                vktrace_pageguard_memcpy(pChangedData, pMappedData + offset, CurrentBlockSize);
+            }
+            SaveSize += CurrentBlockSize;
+            dwIndex++;
+        }
+    }
+    if (pChangedInfoArray)
+    {
+        pChangedInfoArray[0].offset = dwAmount;
+        pChangedInfoArray[0].length = SaveSize;
+    }
+    if (pdwSaveSize)
+    {
+        *pdwSaveSize = SaveSize;
+    }
+    return dwAmount;
+}
+
+//return: if memory already changed;
+//        evenif no change to mmeory, it will still allocate memory for info array which only include one PageGuardChangedBlockInfo,its  offset and length are all 0;
+// VkDeviceSize       *pChangedSize, the size of all changed data block
+// VkDeviceSize       *pDataPackageSize, size for ChangedDataPackage
+// PBYTE              *ppChangedDataPackage, is an array of PageGuardChangedBlockInfo + all changed data, the [0] offset is blocks amount, length is size amount of these blocks(not include this info array),then block1 offset,block1 size....
+//                     allocate needed memory in the method, ppChangedDataPackage point to returned pointer, if ppChangedDataPackage==null, only calculate size
+bool PageGuardMappedMemory::vkFlushMappedMemoryRangePageGuardHandle(
+    VkDevice           device,
+    VkDeviceMemory     memory,
+    VkDeviceSize       offset,
+    VkDeviceSize       size,
+    VkDeviceSize       *pChangedSize,
+    VkDeviceSize       *pDataPackageSize,
+    PBYTE              *ppChangedDataPackage)
+{
+    bool handleSuccessfully = false;
+    DWORD dwSaveSize, InfoSize;
+
+    backupBlockChangedArraySnapshot();
+    getChangedBlockInfo(offset, size, &dwSaveSize, &InfoSize, nullptr, 0, BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT); //get the info size and size of changed blocks
+    if ((dwSaveSize != 0))
+    {
+        handleSuccessfully = true;
+    }
+    if (pChangedSize)
+    {
+        *pChangedSize = dwSaveSize;
+    }
+    if (pDataPackageSize)
+    {
+        *pDataPackageSize = dwSaveSize + InfoSize;
+    }
+    pChangedDataPackage = (PBYTE)pageguardAllocateMemory(dwSaveSize + InfoSize);
+    getChangedBlockInfo(offset, size, &dwSaveSize, &InfoSize, pChangedDataPackage, 0, BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT);
+
+    //if use copy of real mapped memory, need copy back to real mapped memory
+#ifndef PAGEGUARD_ADD_PAGEGUARD_ON_REAL_MAPPED_MEMORY
+    PageGuardChangedBlockInfo *pChangedInfoArray = (PageGuardChangedBlockInfo *)pChangedDataPackage;
+    if (pChangedInfoArray[0].length)
+    {
+        PBYTE pChangedData = (PBYTE)pChangedDataPackage + sizeof(PageGuardChangedBlockInfo)*(pChangedInfoArray[0].offset + 1);
+        DWORD CurrentOffset = 0;
+        for (DWORD i = 0; i < pChangedInfoArray[0].offset; i++)
+        {
+            vktrace_pageguard_memcpy(pRealMappedData + pChangedInfoArray[i + 1].offset, pChangedData + CurrentOffset, (size_t)pChangedInfoArray[i + 1].length);
+            CurrentOffset += pChangedInfoArray[i + 1].length;
+        }
+    }
+#endif
+
+    if (ppChangedDataPackage)
+    {
+        //regist the changed package
+        *ppChangedDataPackage = pChangedDataPackage;
+    }
+    return handleSuccessfully;
+}
+
+void PageGuardMappedMemory::clearChangedDataPackage()
+{
+    if (pChangedDataPackage)
+    {
+        pageguardFreeMemory(pChangedDataPackage);
+        pChangedDataPackage = nullptr;
+    }
+}
+
+//get ptr and size of OPTChangedDataPackage;
+PBYTE PageGuardMappedMemory::getChangedDataPackage(VkDeviceSize  *pSize)
+{
+    PBYTE pResultDataPackage = nullptr;
+    if (pChangedDataPackage)
+    {
+        pResultDataPackage = pChangedDataPackage;
+        PageGuardChangedBlockInfo *pChangedInfoArray = reinterpret_cast<PageGuardChangedBlockInfo *>(pChangedDataPackage);
+        if (pSize)
+        {
+            *pSize = sizeof(PageGuardChangedBlockInfo)*(pChangedInfoArray[0].offset + 1) + pChangedInfoArray[0].length;
+        }
+    }
+    return pResultDataPackage;
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.h b/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.h
new file mode 100644
index 0000000..edc95a4
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pageguardmappedmemory.h
@@ -0,0 +1,129 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#pragma once
+
+#include <stdbool.h>
+#include <unordered_map>
+#include "vulkan/vulkan.h"
+#include "vktrace_platform.h"
+#include "vktrace_common.h"
+
+#include "vktrace_interconnect.h"
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_exts.h"
+#include <stdio.h>
+
+#include "vktrace_pageguard_memorycopy.h"
+#include "vktrace_lib_pagestatusarray.h"
+
+typedef class PageGuardMappedMemory
+{
+    friend class PageGuardCapture;
+
+private:
+    VkDevice MappedDevice;
+    VkDeviceMemory MappedMemory;
+    VkDeviceSize MappedOffset;
+    PBYTE pMappedData; /// point to mapped memory in app process, if pRealMappedData==nullptr, pMappedData point to real mapped memory,
+    /// if pRealMappedData!=nullptr, pMappedData point to real mapped memory copy, the copy can be added page guard
+    PBYTE pRealMappedData; /// point to real mapped memory in app process
+    PBYTE pChangedDataPackage; /// if not nullptr, it point to a package which include changed info array and changed data block, allocated by this class
+    VkDeviceSize MappedSize; /// the size of range
+
+    VkDeviceSize PageGuardSize; /// size for one block
+
+protected:
+    PageStatusArray *pPageStatus;
+    bool BlockConflictError; /// record if any block has been read by host and also write by host
+    VkDeviceSize PageSizeLeft;
+    uint64_t PageGuardAmount;
+
+public:
+    PageGuardMappedMemory();
+    ~PageGuardMappedMemory();
+
+    VkDevice& getMappedDevice();
+
+    VkDeviceMemory& getMappedMemory();
+
+    VkDeviceSize& getMappedOffset();
+
+    PBYTE& getRealMappedDataPointer(); /// get pointer to real mapped memory in app process
+
+    VkDeviceSize& getMappedSize(); /// get the size of range
+
+    bool isUseCopyForRealMappedMemory();
+
+    /// get head addr and size for a block which is located by a given index
+    bool getChangedRangeByIndex(uint64_t index, PBYTE *paddr, VkDeviceSize *pBlockSize);
+
+    /// if return value <0, mean addr is out of page guard.
+    uint64_t getIndexOfChangedBlockByAddr(PBYTE addr);
+
+    void setMappedBlockChanged(uint64_t index, bool bChanged, int useWhich);
+
+    bool isMappedBlockChanged(uint64_t index, int useWhich);
+
+    uint64_t getMappedBlockSize(uint64_t index);
+
+    uint64_t getMappedBlockOffset(uint64_t index);
+
+    bool isNoMappedBlockChanged();
+
+    void resetMemoryObjectAllChangedFlagAndPageGuard();
+
+    void resetMemoryObjectAllReadFlagAndPageGuard();
+
+    bool setAllPageGuardAndFlag(bool bSetPageGuard, bool bSetBlockChanged);
+
+    bool vkMapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkFlags flags, void** ppData);
+
+    void vkUnmapMemoryPageGuardHandle(VkDevice device, VkDeviceMemory memory, void** MappedData);
+
+    void backupBlockChangedArraySnapshot();
+
+    void backupBlockReadArraySnapshot();
+
+    DWORD getChangedBlockAmount(int useWhich);
+
+    /// is RangeLimit cover or partly cover Range
+    bool isRangeIncluded(VkDeviceSize RangeOffsetLimit, VkDeviceSize RangeSizeLimit, VkDeviceSize RangeOffset, VkDeviceSize RangeSize);
+
+    /// for output,
+    /// if pData!=nullptr,the pData + Offset is head addr of an array of PageGuardChangedBlockInfo, the [0] is block amount, size (size for all changed blocks which amount is block amount),then block1 offset,block1 size...., 
+    ///               the block? offset is  this changed block offset to mapped memory head addr,the array followed by changed blocks data
+    ///
+    /// if pData==nullptr, only get size
+    /// DWORD *pdwSaveSize, the size of all changed blocks
+    /// DWORD *pInfoSize, the size of array of PageGuardChangedBlockInfo
+    /// VkDeviceSize RangeOffset, RangeSize, only consider the block which is in the range which start from RangeOffset and size is RangeSize, if RangeOffset<0, consider whole mapped memory
+    /// return the amount of changed blocks.
+    DWORD getChangedBlockInfo(VkDeviceSize RangeOffset, VkDeviceSize RangeSize, DWORD *pdwSaveSize, DWORD *pInfoSize, PBYTE pData, DWORD DataOffset, int useWhich = BLOCK_FLAG_ARRAY_CHANGED);
+
+    /// return: if memory already changed;
+    ///        evenif no change to mmeory, it will still allocate memory for info array which only include one PageGuardChangedBlockInfo,its  offset and length are all 0;
+    /// VkDeviceSize       *pChangedSize, the size of all changed data block
+    /// VkDeviceSize       *pDataPackageSize, size for ChangedDataPackage
+    /// PBYTE              *ppChangedDataPackage, is an array of PageGuardChangedBlockInfo + all changed data, the [0] offset is blocks amount, length is size amount of these blocks(not include this info array),then block1 offset,block1 size....
+    ///                     allocate needed memory in the method, ppChangedDataPackage point to returned pointer, if ppChangedDataPackage==null, only calculate size
+    bool vkFlushMappedMemoryRangePageGuardHandle(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkDeviceSize  *pChangedSize, VkDeviceSize  *pDataPackageSize, PBYTE   *ppChangedDataPackage);
+
+    void clearChangedDataPackage();
+
+    /// get ptr and size of OPTChangedDataPackage;
+    PBYTE getChangedDataPackage(VkDeviceSize  *pSize);
+} PageGuardMappedMemory, *LPPageGuardMappedMemory;
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.cpp b/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.cpp
new file mode 100644
index 0000000..687820f
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.cpp
@@ -0,0 +1,159 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+//OPT: Optimization by using page-guard for speed up capture
+//     The speed is extremely slow when use vktrace to capture DOOM4. It took over half a day and 900G of trace for a capture from beginning to the game menu.
+//     The reason that caused such slow capture is DOOM updates a big mapped memory(over 67M) frequently, vktrace copies this memory block to harddrive when DOOM calls vkFlushmappedMemory to update it every time.
+//     Here we use page guard to record which page of big memory block has been changed and only save those changed pages, it make the capture time reduce to round 15 minutes, the trace file size is round 40G, 
+//     The Playback time for these trace file is round 7 minutes(on Win10/AMDFury/32GRam/I5 system).
+#include <atomic>
+#include "vktrace_lib_pagestatusarray.h"
+
+const uint64_t PageStatusArray::PAGE_FLAG_AMOUNT_PER_BYTE = 1;
+const uint64_t PageStatusArray::PAGE_NUMBER_FROM_BIT_SHIFT = 0;
+
+PageStatusArray::PageStatusArray(uint64_t pageCount)
+{
+    ByteCount = (pageCount % PAGE_FLAG_AMOUNT_PER_BYTE) ? (pageCount / PAGE_FLAG_AMOUNT_PER_BYTE) + 1 : pageCount / PAGE_FLAG_AMOUNT_PER_BYTE;
+
+    pChangedArray[0] = new uint8_t[ByteCount];
+    assert(pChangedArray[0]);
+
+    pChangedArray[1] = new uint8_t[ByteCount];
+    assert(pChangedArray[1]);
+
+    pReadArray[0] = new uint8_t[ByteCount];
+    assert(pReadArray[0]);
+
+    pReadArray[1] = new uint8_t[ByteCount];
+    assert(pReadArray[1]);
+
+    activeChangesArray = pChangedArray[0];
+    capturedChangesArray = pChangedArray[1];
+    activeReadArray = pReadArray[0];
+    capturedReadArray = pReadArray[1];
+
+    clearAll();
+}
+
+PageStatusArray::~PageStatusArray()
+{
+    delete[] pChangedArray[0];
+    delete[] pChangedArray[1];
+    delete[] pReadArray[0];
+    delete[] pReadArray[1];
+}
+
+void PageStatusArray::toggleChangedArray()
+{
+    //TODO use atomic exchange
+    uint8_t *tempArray = activeChangesArray;
+    activeChangesArray = capturedChangesArray;
+    capturedChangesArray = tempArray;
+}
+
+void PageStatusArray::toggleReadArray()
+{
+    //TODO use atomic exchange
+    uint8_t *tempArray = activeReadArray;
+    activeReadArray = capturedReadArray;
+    capturedReadArray = tempArray;
+}
+
+bool PageStatusArray::getBlockChangedArray(uint64_t index)
+{
+    return activeChangesArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] & (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+}
+
+bool PageStatusArray::getBlockChangedArraySnapshot(uint64_t index)
+{
+    return capturedChangesArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] & (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+}
+
+bool PageStatusArray::getBlockReadArray(uint64_t index)
+{
+    return activeReadArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] & (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+}
+
+bool PageStatusArray::getBlockReadArraySnapshot(uint64_t index)
+{
+    return capturedReadArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] & (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+}
+
+void PageStatusArray::setBlockChangedArray(uint64_t index, bool changed)
+{
+    if (changed)
+    {
+        activeChangesArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] |= (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+    }
+    else
+    {
+        activeChangesArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] &= ~((uint8_t)(1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE)));
+    }
+}
+
+void PageStatusArray::setBlockChangedArraySnapshot(uint64_t index, bool changed)
+{
+    if (changed)
+    {
+        capturedChangesArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] |= (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+    }
+    else
+    {
+        capturedChangesArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] &= ~((uint8_t)(1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE)));
+    }
+}
+
+void PageStatusArray::setBlockReadArray(uint64_t index, bool changed)
+{
+    if (changed)
+    {
+        activeReadArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] |= (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+    }
+    else
+    {
+        activeReadArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] &= ~((uint8_t)(1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE)));
+    }
+}
+
+void PageStatusArray::setBlockReadArraySnapshot(uint64_t index, bool changed)
+{
+    if (changed)
+    {
+        capturedReadArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] |= (1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE));
+    }
+    else
+    {
+        capturedReadArray[index >> PAGE_NUMBER_FROM_BIT_SHIFT] &= ~((uint8_t)(1 << (index % PAGE_FLAG_AMOUNT_PER_BYTE)));
+    }
+}
+
+void PageStatusArray::backupChangedArray()
+{
+    toggleChangedArray();
+}
+
+void PageStatusArray::backupReadArray()
+{
+    toggleReadArray();
+}
+
+void PageStatusArray::clearAll()
+{
+    memset(activeChangesArray, 0, ByteCount);
+    memset(capturedChangesArray, 0, ByteCount);
+    memset(activeReadArray, 0, ByteCount);
+    memset(capturedReadArray, 0, ByteCount);
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.h b/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.h
new file mode 100644
index 0000000..f85788a
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_pagestatusarray.h
@@ -0,0 +1,67 @@
+/*
+* Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#pragma once
+
+#include <stdbool.h>
+#include <unordered_map>
+#include "vulkan/vulkan.h"
+#include "vktrace_platform.h"
+#include "vktrace_common.h"
+
+#include "vktrace_interconnect.h"
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_exts.h"
+#include <stdio.h>
+
+
+
+static const int BLOCK_FLAG_ARRAY_CHANGED = 0;
+static const int  BLOCK_FLAG_ARRAY_CHANGED_SNAPSHOT = 1;
+static const int  BLOCK_FLAG_ARRAY_READ = 2;
+static const int  BLOCK_FLAG_ARRAY_READ_SNAPSHOT = 3;
+
+typedef class PageStatusArray
+{
+public:
+    PageStatusArray(uint64_t pageCount);
+    ~PageStatusArray();
+
+    void toggleChangedArray();
+    void toggleReadArray();
+
+    bool getBlockChangedArray(uint64_t index);
+    bool getBlockChangedArraySnapshot(uint64_t index);
+    bool getBlockReadArray(uint64_t index);
+    bool getBlockReadArraySnapshot(uint64_t index);
+    void setBlockChangedArray(uint64_t index, bool changed);
+    void setBlockChangedArraySnapshot(uint64_t index, bool changed);
+    void setBlockReadArray(uint64_t index, bool changed);
+    void setBlockReadArraySnapshot(uint64_t index, bool changed);
+    void backupChangedArray();
+    void backupReadArray();
+    void clearAll();
+private:
+    const static uint64_t PAGE_FLAG_AMOUNT_PER_BYTE;
+    const static uint64_t PAGE_NUMBER_FROM_BIT_SHIFT;
+    uint64_t ByteCount;
+    uint8_t *activeChangesArray;
+    uint8_t *capturedChangesArray;
+    uint8_t *activeReadArray;
+    uint8_t *capturedReadArray;
+    uint8_t *pChangedArray[2]; /// include two array, one for page guard handler to record which block has been changed from vkMap.. or last time vkFlush..., the other one for flush data and reset pageguard
+    uint8_t *pReadArray[2]; /// include two array, one for page guard handler to record which block has been read by host from vkMap.. or last time vkinvalidate or vkpipelinebarrier with specific para..., the other one for reset page guard
+} PageStatusArray;
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp b/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp
new file mode 100644
index 0000000..f9d35d3
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp
@@ -0,0 +1,2669 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Tobin Ehlis <tobin@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: Mark Lobodzinski <mark@lunarg.com>
+ */
+#include <stdbool.h>
+#include <unordered_map>
+#include <vector>
+#include <list>
+#include "vktrace_vk_vk.h"
+#include "vulkan/vulkan.h"
+#include "vulkan/vk_layer.h"
+#include "vktrace_platform.h"
+#include "vk_dispatch_table_helper.h"
+#include "vktrace_common.h"
+#include "vktrace_lib_helpers.h"
+
+#include "vktrace_interconnect.h"
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_utils.h"
+#include "vktrace_vk_exts.h"
+#include <stdio.h>
+
+#include "vktrace_pageguard_memorycopy.h"
+#include "vktrace_lib_pagestatusarray.h"
+#include "vktrace_lib_pageguardmappedmemory.h"
+#include "vktrace_lib_pageguardcapture.h"
+#include "vktrace_lib_pageguard.h"
+
+// declared as extern in vktrace_lib_helpers.h
+VKTRACE_CRITICAL_SECTION g_memInfoLock;
+VKMemInfo g_memInfo = {0, NULL, NULL, 0};
+
+
+std::unordered_map<void *, layer_device_data *> g_deviceDataMap;
+std::unordered_map<void *, layer_instance_data *> g_instanceDataMap;
+
+
+layer_instance_data *mid(void *object)
+{
+    dispatch_key key = get_dispatch_key(object);
+    std::unordered_map<void *, layer_instance_data *>::const_iterator got;
+    got = g_instanceDataMap.find(key);
+    assert(got != g_instanceDataMap.end());
+    return got->second;
+}
+
+layer_device_data *mdd(void* object)
+{
+    dispatch_key key = get_dispatch_key(object);
+    std::unordered_map<void *, layer_device_data *>::const_iterator got;
+    got = g_deviceDataMap.find(key);
+    assert(got != g_deviceDataMap.end());
+    return got->second;
+}
+
+static layer_instance_data *initInstanceData(
+                                    VkInstance instance,
+                                    const PFN_vkGetInstanceProcAddr gpa,
+                                    std::unordered_map<void *, layer_instance_data *> &map)
+{
+    layer_instance_data *pTable;
+    assert(instance);
+    dispatch_key key = get_dispatch_key(instance);
+
+    std::unordered_map<void *, layer_instance_data *>::const_iterator it = map.find(key);
+    if (it == map.end())
+    {
+        pTable =  new layer_instance_data();
+        map[key] = pTable;
+    } else
+    {
+        return it->second;
+    }
+
+    // TODO: Convert to new init method
+    layer_init_instance_dispatch_table(instance, &pTable->instTable, gpa);
+
+    return pTable;
+}
+
+static layer_device_data *initDeviceData(
+        VkDevice device,
+        const PFN_vkGetDeviceProcAddr gpa,
+        std::unordered_map<void *, layer_device_data *> &map)
+{
+    layer_device_data *pTable;
+    dispatch_key key = get_dispatch_key(device);
+
+    std::unordered_map<void *, layer_device_data *>::const_iterator it = map.find(key);
+    if (it == map.end())
+    {
+        pTable =  new layer_device_data();
+        map[key] = pTable;
+    } else
+    {
+        return it->second;
+    }
+
+    layer_init_device_dispatch_table(device, &pTable->devTable, gpa);
+
+    return pTable;
+}
+
+/*
+ * This function will return the pNext pointer of any
+ * CreateInfo extensions that are not loader extensions.
+ * This is used to skip past the loader extensions prepended
+ * to the list during CreateInstance and CreateDevice.
+ */
+void *strip_create_extensions(const void *pNext)
+{
+    VkLayerInstanceCreateInfo *create_info = (VkLayerInstanceCreateInfo *) pNext;
+
+    while (create_info && (create_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO ||
+           create_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO)) {
+        create_info = (VkLayerInstanceCreateInfo *) create_info->pNext;
+    }
+
+    return create_info;
+}
+
+#if defined (PLATFORM_LINUX)
+
+static void mprotectWithErrorCheck(void *addr, size_t size, int prot)
+{
+    while (0 != mprotect(addr, size, prot))
+    {
+        if (errno == EAGAIN)
+            continue;
+
+        // Something bad happened, and it's fatal.
+        // Calling VKTRACE_FATAL_ERROR involves potentially doing a malloc,
+        // writing to the trace file, and writing to stdout -- operations that
+        // may not work from a signal handler.  But we're about to exit anyway.
+        VKTRACE_FATAL_ERROR("mprotect sys call failed.");
+    }
+}
+
+// When quering dirty pages from /proc/self/pagemap, there is a
+// small window between the reading of this file and when the dirty bits
+// are cleared in which some other thread might modify mapped memory.
+// We trap these memory writes by setting mapped memory pages to read-only and
+// setting the signal handler for SIGSEGV. The signal handler writes the
+// addresses that caused a SIGSEGV to a pipe, which are read and handled
+// by getMappedDirtyPagesLinux.
+
+// Note that we call mprotect(2) and write(2) in this signal handler.
+// Calling mprotect(2) in a signal hander is not POSIX compliant, but
+// is known to work on Linux. Calling write(2) in a signal hander is
+// allowed by POSIX.
+
+static int pipefd[2];
+
+static void segvHandler(int sig, siginfo_t *si, void *ununsed)
+{
+    size_t pageSize = getpagesize();
+    void *addr;
+
+    addr = si->si_addr;
+    if (sizeof(addr) != write(pipefd[1], &addr, sizeof(addr)))
+        VKTRACE_FATAL_ERROR("Write to pipe failed in PMB signal hander.");
+
+    // Change protection of this page to allow the write to proceed
+    mprotectWithErrorCheck((void *)((uint64_t)si->si_addr & ~(pageSize-1)), pageSize, PROT_READ|PROT_WRITE);
+}
+
+
+// This function is called when we need to update our list of mapped memory
+// that is dirty.  On Linux, we use the /proc/self/pagemap to detect which pages
+// changed. But we also use mprotect with a signal handler for the rare case
+// in which mapped memory is written to between when we read the dirty bits
+// from /proc/self/pagemap dirty and clear the dirty bits.
+
+void getMappedDirtyPagesLinux(void)
+{
+    LPPageGuardMappedMemory pMappedMem;
+    PBYTE addr, alignedAddrStart, alignedAddrEnd, previousAddr;
+    uint64_t nPages;
+    VkDeviceMemory mappedMemory;
+    off_t pmOffset;
+    int64_t index;
+    size_t pageSize = getpagesize();
+    size_t readLen;
+    VKAllocInfo *pEntry;
+    struct sigaction sigAction;
+    static int pmFd = -1;
+    static std::vector<uint64_t> pageEntries;
+
+    // If pageguard isn't enabled, we don't need to do anythhing
+    if (!getPageGuardEnableFlag())
+        return;
+
+    vktrace_enter_critical_section(&g_memInfoLock);
+
+    // Open pagefile, open the pipe, and set a SIGSEGV handler
+    if (pmFd == -1)
+    {
+        pmFd = open("/proc/self/pagemap", O_RDONLY);
+        if (pmFd < 0)
+            VKTRACE_FATAL_ERROR("Failed to open pagemap file.");
+
+        if (0 != pipe2(pipefd, O_NONBLOCK))
+            VKTRACE_FATAL_ERROR("Failed to create pipe.");
+
+        // Set the SIGSEGV signal handler
+        sigAction.sa_sigaction = segvHandler;
+        sigfillset(&sigAction.sa_mask);
+        sigAction.sa_flags = SA_SIGINFO;
+        if (0 != sigaction(SIGSEGV, &sigAction, NULL))
+            VKTRACE_FATAL_ERROR("sigaction sys call failed.");
+    }
+
+    // Iterate through all mapped memory allocations.
+    // Check dirty bits of each page in each mapped memory allocation.
+    for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
+         it != getPageGuardControlInstance().getMapMemory().end();
+         it++)
+    {
+        pMappedMem = &(it->second);
+        mappedMemory = pMappedMem->getMappedMemory();
+        pEntry=find_mem_info_entry(mappedMemory);
+        addr=pEntry->pData;
+        if (!addr)
+            continue;
+        alignedAddrStart = (PBYTE)((uint64_t)addr & ~(pageSize-1));
+        alignedAddrEnd = (PBYTE)(((uint64_t)addr + pEntry->rangeSize + pageSize - 1) & ~(pageSize-1));
+        nPages = (alignedAddrEnd - alignedAddrStart ) / pageSize;
+
+        // Make pages in this memory allocation non-writable so we get a SIGSEGV
+        // if some other thread writes to this memory while we are examining
+        // and clearing its dirty bit.
+        mprotectWithErrorCheck(alignedAddrStart, (size_t)(alignedAddrEnd - alignedAddrStart), PROT_READ);
+
+        // Read all the pagemap entries for this mapped memory allocation
+        if (pageEntries.size() < nPages)
+            pageEntries.resize(nPages);
+        pmOffset = (off_t)((uint64_t)alignedAddrStart/(uint64_t)pageSize) * 8;
+        lseek(pmFd, pmOffset, SEEK_SET);
+        readLen = read(pmFd, &pageEntries[0], 8*nPages);
+        assert(readLen==8*nPages);
+        if (readLen != 8*nPages)
+            VKTRACE_FATAL_ERROR("Failed to read from pagemap file.");
+
+        // Examine the dirty bits in each pagemap entry
+        addr = alignedAddrStart;
+        for (uint64_t i=0; i<nPages; i++)
+        {
+            if ((pageEntries[i]&((uint64_t)1<<55)) != 0)
+            {
+                index = pMappedMem->getIndexOfChangedBlockByAddr(addr);
+                if (index >= 0)
+                    pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_CHANGED);
+            }
+            addr += pageSize;
+        }
+    }
+
+    // Clear all dirty bits for this process
+#if !defined(ANDROID)
+    getPageGuardControlInstance().pageRefsDirtyClear();
+#endif
+
+    // Re-enable write permission for all mapped memory
+    for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
+         it != getPageGuardControlInstance().getMapMemory().end();
+         it++)
+    {
+        pMappedMem = &(it->second);
+        mappedMemory = pMappedMem->getMappedMemory();
+        pEntry=find_mem_info_entry(mappedMemory);
+        addr=pEntry->pData;
+        if (!addr)
+            continue;
+        alignedAddrStart = (PBYTE)((uint64_t)addr & ~(pageSize-1));
+        alignedAddrEnd = (PBYTE)(((uint64_t)addr + pEntry->rangeSize + pageSize - 1) & ~(pageSize-1));
+        mprotectWithErrorCheck(alignedAddrStart, (size_t)(alignedAddrEnd - alignedAddrStart), PROT_READ|PROT_WRITE);
+    }
+
+    // Read all the addresses that caused a segv and mark those pages dirty
+
+    previousAddr=0;
+    while ((sizeof(addr) == read(pipefd[0], &addr, sizeof(addr))))
+    {
+        // If we get consecutive identical addresses, the sig handler is getting
+        // called repeatedly with the same address (or an app is doing something crazy).
+        // To avoid infinitely calling the sighandler, generate an error and exit.
+
+        // We can't give an error here if we didn't find the address
+        // in mapped memory. The arrival of a signal might be delayed
+        // due to process scheduling, and thus might arrive after the
+        // mapped memory has been unmapped.
+        if (previousAddr == addr)
+        {
+            VKTRACE_FATAL_ERROR("SIGSEGV received on the same address.");
+        }
+        previousAddr = addr;
+
+        for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
+             it != getPageGuardControlInstance().getMapMemory().end();
+             it++)
+        {
+            pMappedMem = &(it->second);
+            index = pMappedMem->getIndexOfChangedBlockByAddr(addr);
+            if (index >= 0)
+            {
+                pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_CHANGED);
+                break;
+            }
+        }
+    }
+    vktrace_leave_critical_section(&g_memInfoLock);
+}
+#endif
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkAllocateMemory(
+    VkDevice device,
+    const VkMemoryAllocateInfo* pAllocateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkDeviceMemory* pMemory)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkAllocateMemory* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkAllocateMemory, get_struct_chain_size((void*)pAllocateInfo) + sizeof(VkAllocationCallbacks) + sizeof(VkDeviceMemory));
+    result = mdd(device)->devTable.AllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkAllocateMemory(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo), sizeof(VkMemoryAllocateInfo), pAllocateInfo);
+    add_alloc_memory_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo->pNext), pAllocateInfo->pNext);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMemory), sizeof(VkDeviceMemory), pMemory);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMemory));
+    FINISH_TRACE_PACKET();
+    // begin custom code
+    add_new_handle_to_mem_info(*pMemory, pAllocateInfo->allocationSize, NULL);
+    // end custom code
+    return result;
+}
+
+
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkMapMemory(
+    VkDevice device,
+    VkDeviceMemory memory,
+    VkDeviceSize offset,
+    VkDeviceSize size,
+    VkFlags flags,
+    void** ppData)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkMapMemory* pPacket = NULL;
+    VKAllocInfo *entry;
+    CREATE_TRACE_PACKET(vkMapMemory, sizeof(void*));
+    result = mdd(device)->devTable.MapMemory(device, memory, offset, size, flags, ppData);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    entry = find_mem_info_entry(memory);
+
+    // For vktrace usage, clamp the memory size to the total size less offset if VK_WHOLE_SIZE is specified.
+    if (size == VK_WHOLE_SIZE) {
+        size = entry->totalSize - offset;
+    }
+
+#ifdef USE_PAGEGUARD_SPEEDUP
+    pageguardEnter();
+    getPageGuardControlInstance().vkMapMemoryPageGuardHandle(device, memory, offset, size, flags, ppData);
+    pageguardExit();
+#endif
+    pPacket = interpret_body_as_vkMapMemory(pHeader);
+    pPacket->device = device;
+    pPacket->memory = memory;
+    pPacket->offset = offset;
+    pPacket->size = size;
+    pPacket->flags = flags;
+    if (ppData != NULL)
+    {
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData), sizeof(void*), *ppData);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData));
+        add_data_to_mem_info(memory, size, offset, *ppData);
+    }
+    pPacket->result = result;
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkUnmapMemory(
+    VkDevice device,
+    VkDeviceMemory memory)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkUnmapMemory* pPacket;
+    VKAllocInfo *entry;
+    size_t siz = 0;
+#ifdef USE_PAGEGUARD_SPEEDUP
+    void *PageGuardMappedData=NULL;
+    pageguardEnter();
+#if defined (PLATFORM_LINUX)
+    getMappedDirtyPagesLinux();
+#endif
+    getPageGuardControlInstance().vkUnmapMemoryPageGuardHandle(device, memory, &PageGuardMappedData, &vkFlushMappedMemoryRangesWithoutAPICall);
+#endif
+    uint64_t trace_begin_time = vktrace_get_time();
+
+    // insert into packet the data that was written by CPU between the vkMapMemory call and here
+    // Note must do this prior to the real vkUnMap() or else may get a FAULT
+    vktrace_enter_critical_section(&g_memInfoLock);
+    entry = find_mem_info_entry(memory);
+    if (entry && entry->pData != NULL)
+    {
+        if (!entry->didFlush)
+        {
+            // no FlushMapped Memory
+            siz = (size_t)entry->rangeSize;
+        }
+    }
+    CREATE_TRACE_PACKET(vkUnmapMemory, siz);
+    pHeader->vktrace_begin_time = trace_begin_time;
+    pPacket = interpret_body_as_vkUnmapMemory(pHeader);
+    if (siz)
+    {
+        assert(entry->handle == memory);
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->pData), siz, entry->pData);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pData));
+    }
+    entry->pData = NULL;
+    vktrace_leave_critical_section(&g_memInfoLock);
+    pHeader->entrypoint_begin_time = vktrace_get_time();
+    mdd(device)->devTable.UnmapMemory(device, memory);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket->device = device;
+    pPacket->memory = memory;
+    FINISH_TRACE_PACKET();
+#ifdef USE_PAGEGUARD_SPEEDUP
+    if (PageGuardMappedData != nullptr)
+    {
+        pageguardFreeMemory(PageGuardMappedData);
+    }
+    pageguardExit();
+#endif
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkFreeMemory(
+    VkDevice device,
+    VkDeviceMemory memory,
+    const VkAllocationCallbacks* pAllocator)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkFreeMemory* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkFreeMemory, sizeof(VkAllocationCallbacks));
+    mdd(device)->devTable.FreeMemory(device, memory, pAllocator);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkFreeMemory(pHeader);
+    pPacket->device = device;
+    pPacket->memory = memory;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    FINISH_TRACE_PACKET();
+    // begin custom code
+    rm_handle_from_mem_info(memory);
+    // end custom code
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkInvalidateMappedMemoryRanges(
+    VkDevice device,
+    uint32_t memoryRangeCount,
+    const VkMappedMemoryRange* pMemoryRanges)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    size_t rangesSize = 0;
+    size_t dataSize = 0;
+    uint32_t iter;
+    packet_vkInvalidateMappedMemoryRanges* pPacket = NULL;
+    uint64_t trace_begin_time = vktrace_get_time();
+
+#ifdef USE_PAGEGUARD_SPEEDUP
+    pageguardEnter();
+    resetAllReadFlagAndPageGuard();
+#endif
+
+    // find out how much memory is in the ranges
+    for (iter = 0; iter < memoryRangeCount; iter++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[iter];
+        rangesSize += vk_size_vkmappedmemoryrange(pRange);
+        dataSize += (size_t)pRange->size;
+    }
+
+    CREATE_TRACE_PACKET(vkInvalidateMappedMemoryRanges, rangesSize + sizeof(void*)*memoryRangeCount + dataSize);
+    pHeader->vktrace_begin_time = trace_begin_time;
+    pPacket = interpret_body_as_vkInvalidateMappedMemoryRanges(pHeader);
+
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->pMemoryRanges), rangesSize, pMemoryRanges);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMemoryRanges));
+
+    // insert into packet the data that was written by CPU between the vkMapMemory call and here
+    // create a temporary local ppData array and add it to the packet (to reserve the space for the array)
+    void** ppTmpData = (void **) malloc(memoryRangeCount * sizeof(void*));
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->ppData), sizeof(void*)*memoryRangeCount, ppTmpData);
+    free(ppTmpData);
+
+    // now the actual memory
+    vktrace_enter_critical_section(&g_memInfoLock);
+    for (iter = 0; iter < memoryRangeCount; iter++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[iter];
+        VKAllocInfo* pEntry = find_mem_info_entry(pRange->memory);
+
+        if (pEntry != NULL)
+        {
+            assert(pEntry->handle == pRange->memory);
+            assert(pEntry->totalSize >= (pRange->size + pRange->offset));
+            assert(pEntry->totalSize >= pRange->size);
+            assert(pRange->offset >= pEntry->rangeOffset && (pRange->offset + pRange->size) <= (pEntry->rangeOffset + pEntry->rangeSize));
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->ppData[iter]), pRange->size, pEntry->pData + pRange->offset);
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData[iter]));
+            pEntry->didFlush = TRUE;//Do we need didInvalidate?
+        }
+        else
+        {
+             vktrace_LogError("Failed to copy app memory into trace packet (idx = %u) on vkInvalidateMappedMemoryRanges", pHeader->global_packet_index);
+        }
+    }
+    vktrace_leave_critical_section(&g_memInfoLock);
+
+    // now finalize the ppData array since it is done being updated
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData));
+
+    pHeader->entrypoint_begin_time = vktrace_get_time();
+    result = mdd(device)->devTable.InvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket->device = device;
+    pPacket->memoryRangeCount = memoryRangeCount;
+    pPacket->result = result;
+    FINISH_TRACE_PACKET();
+#ifdef USE_PAGEGUARD_SPEEDUP
+    pageguardExit();
+#endif
+    return result;
+}
+
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkFlushMappedMemoryRanges(
+    VkDevice device,
+    uint32_t memoryRangeCount,
+    const VkMappedMemoryRange* pMemoryRanges)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    size_t rangesSize = 0;
+    size_t dataSize = 0;
+    uint32_t iter;
+    packet_vkFlushMappedMemoryRanges* pPacket = NULL;
+#ifdef USE_PAGEGUARD_SPEEDUP
+    pageguardEnter();
+#if defined (PLATFORM_LINUX)
+    getMappedDirtyPagesLinux();
+#endif
+    PBYTE *ppPackageData = new PBYTE[memoryRangeCount];
+    getPageGuardControlInstance().vkFlushMappedMemoryRangesPageGuardHandle(device, memoryRangeCount, pMemoryRanges, ppPackageData);//the packet is not needed if no any change on data of all ranges
+#endif
+
+    uint64_t trace_begin_time = vktrace_get_time();
+
+    // find out how much memory is in the ranges
+#ifndef USE_PAGEGUARD_SPEEDUP
+    for (iter = 0; iter < memoryRangeCount; iter++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[iter];
+        dataSize += ROUNDUP_TO_4(((size_t)(getPageGuardControlInstance().getMappedMemorySize(device, pRange->memory));
+    }
+#else
+    dataSize = ROUNDUP_TO_4(getPageGuardControlInstance().getALLChangedPackageSizeInMappedMemory(device, memoryRangeCount, pMemoryRanges, ppPackageData));
+#endif
+    rangesSize = sizeof(VkMappedMemoryRange) * memoryRangeCount;
+
+    CREATE_TRACE_PACKET(vkFlushMappedMemoryRanges, rangesSize + sizeof(void*)*memoryRangeCount + dataSize);
+    pHeader->vktrace_begin_time = trace_begin_time;
+    pPacket = interpret_body_as_vkFlushMappedMemoryRanges(pHeader);
+
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->pMemoryRanges), rangesSize, pMemoryRanges);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMemoryRanges));
+
+    // insert into packet the data that was written by CPU between the vkMapMemory call and here
+    // create a temporary local ppData array and add it to the packet (to reserve the space for the array)
+    void** ppTmpData = (void **) malloc(memoryRangeCount * sizeof(void*));
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->ppData), sizeof(void*)*memoryRangeCount, ppTmpData);
+    free(ppTmpData);
+
+    // now the actual memory
+    vktrace_enter_critical_section(&g_memInfoLock);
+    for (iter = 0; iter < memoryRangeCount; iter++)
+    {
+        VkMappedMemoryRange* pRange = (VkMappedMemoryRange*)&pMemoryRanges[iter];
+        VKAllocInfo* pEntry = find_mem_info_entry(pRange->memory);
+
+        if (pEntry != NULL)
+        {
+            VkDeviceSize rangeSize;
+            if (pRange->size == VK_WHOLE_SIZE)
+            {
+                LPPageGuardMappedMemory pOPTMemoryTemp = getPageGuardControlInstance().findMappedMemoryObject(device, pRange);
+                rangeSize = getPageGuardControlInstance().getMappedMemorySize(device, pRange->memory) - (pRange->offset - pOPTMemoryTemp->getMappedOffset());
+            }
+            else
+                rangeSize = pRange->size;
+            assert(pEntry->handle == pRange->memory);
+            assert(pEntry->totalSize >= (rangeSize + pRange->offset));
+            assert(pEntry->totalSize >= rangeSize);
+            assert(pRange->offset >= pEntry->rangeOffset && (pRange->offset + rangeSize) <= (pEntry->rangeOffset + pEntry->rangeSize));
+#ifdef USE_PAGEGUARD_SPEEDUP
+            LPPageGuardMappedMemory pOPTMemoryTemp = getPageGuardControlInstance().findMappedMemoryObject(device, pRange);
+            VkDeviceSize OPTPackageSizeTemp = 0;
+            if (pOPTMemoryTemp)
+            {
+                PBYTE pOPTDataTemp = pOPTMemoryTemp->getChangedDataPackage(&OPTPackageSizeTemp);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData[iter]), ROUNDUP_TO_4(OPTPackageSizeTemp), pOPTDataTemp);
+                pOPTMemoryTemp->clearChangedDataPackage();
+                pOPTMemoryTemp->resetMemoryObjectAllChangedFlagAndPageGuard();
+            }
+            else
+            {
+                PBYTE pOPTDataTemp = getPageGuardControlInstance().getChangedDataPackageOutOfMap(ppPackageData, iter, &OPTPackageSizeTemp);
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData[iter]), ROUNDUP_TO_4(OPTPackageSizeTemp), pOPTDataTemp);
+                getPageGuardControlInstance().clearChangedDataPackageOutOfMap(ppPackageData, iter);
+            }
+#else
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->ppData[iter]), ROUNDUP_TO_4(rangeSize), pEntry->pData + pRange->offset);
+#endif
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData[iter]));
+            pEntry->didFlush = TRUE;
+        }
+        else
+        {
+             vktrace_LogError("Failed to copy app memory into trace packet (idx = %u) on vkFlushedMappedMemoryRanges", pHeader->global_packet_index);
+        }
+    }
+#ifdef USE_PAGEGUARD_SPEEDUP
+    delete[] ppPackageData;
+#endif
+    vktrace_leave_critical_section(&g_memInfoLock);
+
+    // now finalize the ppData array since it is done being updated
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->ppData));
+
+    pHeader->entrypoint_begin_time = vktrace_get_time();
+    result = mdd(device)->devTable.FlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket->device = device;
+    pPacket->memoryRangeCount = memoryRangeCount;
+    pPacket->result = result;
+    FINISH_TRACE_PACKET();
+#ifdef USE_PAGEGUARD_SPEEDUP
+    pageguardExit();
+#endif
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkAllocateCommandBuffers(
+    VkDevice device,
+    const VkCommandBufferAllocateInfo* pAllocateInfo,
+    VkCommandBuffer* pCommandBuffers)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkAllocateCommandBuffers* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkAllocateCommandBuffers, get_struct_chain_size((void*)pAllocateInfo) + sizeof(VkCommandBuffer) * pAllocateInfo->commandBufferCount);
+    result = mdd(device)->devTable.AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkAllocateCommandBuffers(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo), sizeof(VkCommandBufferAllocateInfo), pAllocateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCommandBuffers), sizeof(VkCommandBuffer) * pAllocateInfo->commandBufferCount, pCommandBuffers);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCommandBuffers));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkBeginCommandBuffer(
+    VkCommandBuffer commandBuffer,
+    const VkCommandBufferBeginInfo* pBeginInfo)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkBeginCommandBuffer* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkBeginCommandBuffer, get_struct_chain_size((void*)pBeginInfo));
+    result = mdd(commandBuffer)->devTable.BeginCommandBuffer(commandBuffer, pBeginInfo);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkBeginCommandBuffer(pHeader);
+    pPacket->commandBuffer = commandBuffer;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBeginInfo), sizeof(VkCommandBufferBeginInfo), pBeginInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBeginInfo->pInheritanceInfo), sizeof(VkCommandBufferInheritanceInfo), pBeginInfo->pInheritanceInfo);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBeginInfo->pInheritanceInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBeginInfo));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateDescriptorPool(
+    VkDevice device,
+    const VkDescriptorPoolCreateInfo* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkDescriptorPool* pDescriptorPool)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateDescriptorPool* pPacket = NULL;
+    // begin custom code (needs to use get_struct_chain_size)
+    CREATE_TRACE_PACKET(vkCreateDescriptorPool,  get_struct_chain_size((void*)pCreateInfo) + sizeof(VkAllocationCallbacks) + sizeof(VkDescriptorPool));
+    // end custom code
+    result = mdd(device)->devTable.CreateDescriptorPool(device, pCreateInfo, pAllocator, pDescriptorPool);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreateDescriptorPool(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkDescriptorPoolCreateInfo), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pPoolSizes), pCreateInfo->poolSizeCount * sizeof(VkDescriptorPoolSize), pCreateInfo->pPoolSizes);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorPool), sizeof(VkDescriptorPool), pDescriptorPool);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pPoolSizes));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorPool));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VkLayerDeviceCreateInfo *get_chain_info(const VkDeviceCreateInfo *pCreateInfo, VkLayerFunction func)
+{
+    VkLayerDeviceCreateInfo *chain_info = (VkLayerDeviceCreateInfo *) pCreateInfo->pNext;
+    while (chain_info && !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
+           && chain_info->function == func)) {
+        chain_info = (VkLayerDeviceCreateInfo *) chain_info->pNext;
+    }
+    assert(chain_info != NULL);
+    return chain_info;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateDevice(
+    VkPhysicalDevice physicalDevice,
+    const VkDeviceCreateInfo* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkDevice* pDevice)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateDevice* pPacket = NULL;
+    uint32_t i;
+
+    VkLayerDeviceCreateInfo *chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+    assert(chain_info->u.pLayerInfo);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    assert(fpGetInstanceProcAddr);
+    PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+    assert(fpGetDeviceProcAddr);
+    PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice) fpGetInstanceProcAddr(NULL, "vkCreateDevice");
+    if (fpCreateDevice == NULL) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // Advance the link info for the next element on the chain
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+    result = fpCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
+    if (result != VK_SUCCESS) {
+        return result;
+    }
+
+    initDeviceData(*pDevice, fpGetDeviceProcAddr, g_deviceDataMap);
+    // Setup device dispatch table for extensions
+    ext_init_create_device(mdd(*pDevice), *pDevice, fpGetDeviceProcAddr, pCreateInfo->enabledExtensionCount, pCreateInfo->ppEnabledExtensionNames);
+
+    // remove the loader extended createInfo structure
+    VkDeviceCreateInfo localCreateInfo;
+    memcpy(&localCreateInfo, pCreateInfo, sizeof(localCreateInfo));
+    for (i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
+        char **ppName = (char **) &localCreateInfo.ppEnabledExtensionNames[i];
+        *ppName = (char *) pCreateInfo->ppEnabledExtensionNames[i];
+    }
+    for (i = 0; i < pCreateInfo->enabledLayerCount; i++) {
+        char **ppName = (char **) &localCreateInfo.ppEnabledLayerNames[i];
+        *ppName = (char *) pCreateInfo->ppEnabledLayerNames[i];
+    }
+    localCreateInfo.pNext = strip_create_extensions(pCreateInfo->pNext);
+
+    CREATE_TRACE_PACKET(vkCreateDevice, get_struct_chain_size((void*)&localCreateInfo) + sizeof(VkAllocationCallbacks) + sizeof(VkDevice));
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreateDevice(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    add_VkDeviceCreateInfo_to_packet(pHeader, (VkDeviceCreateInfo**) &(pPacket->pCreateInfo), &localCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDevice), sizeof(VkDevice), pDevice);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDevice));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateFramebuffer(
+    VkDevice device,
+    const VkFramebufferCreateInfo* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkFramebuffer* pFramebuffer)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateFramebuffer* pPacket = NULL;
+    // begin custom code
+    uint32_t attachmentCount = (pCreateInfo != NULL && pCreateInfo->pAttachments != NULL) ? pCreateInfo->attachmentCount : 0;
+    CREATE_TRACE_PACKET(vkCreateFramebuffer, get_struct_chain_size((void*)pCreateInfo) + sizeof(VkAllocationCallbacks) + sizeof(VkFramebuffer));
+    // end custom code
+    result = mdd(device)->devTable.CreateFramebuffer(device, pCreateInfo, pAllocator, pFramebuffer);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreateFramebuffer(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkFramebufferCreateInfo), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pAttachments), attachmentCount * sizeof(VkImageView), pCreateInfo->pAttachments);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pFramebuffer), sizeof(VkFramebuffer), pFramebuffer);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pAttachments));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pFramebuffer));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VkLayerInstanceCreateInfo *get_chain_info(const VkInstanceCreateInfo *pCreateInfo, VkLayerFunction func)
+{
+    VkLayerInstanceCreateInfo *chain_info = (VkLayerInstanceCreateInfo *) pCreateInfo->pNext;
+    while (chain_info && ((chain_info->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO)
+           || (chain_info->function != func))) {
+        chain_info = (VkLayerInstanceCreateInfo *) chain_info->pNext;
+    }
+    assert(chain_info != NULL);
+    return chain_info;
+}
+
+#if defined(USE_PAGEGUARD_SPEEDUP) && !defined(PAGEGUARD_MEMCPY_USE_PPL_LIB)
+extern "C" BOOL vktrace_pageguard_init_multi_threads_memcpy();
+extern "C" void vktrace_pageguard_done_multi_threads_memcpy();
+#endif
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateInstance(
+    const VkInstanceCreateInfo* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkInstance* pInstance)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateInstance* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint32_t i;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    SEND_ENTRYPOINT_ID(vkCreateInstance);
+    startTime = vktrace_get_time();
+
+#if defined(USE_PAGEGUARD_SPEEDUP) && !defined(PAGEGUARD_MEMCPY_USE_PPL_LIB)
+    vktrace_pageguard_init_multi_threads_memcpy();
+#endif
+
+    VkLayerInstanceCreateInfo *chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+    assert(chain_info->u.pLayerInfo);
+    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    assert(fpGetInstanceProcAddr);
+    PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance) fpGetInstanceProcAddr(NULL, "vkCreateInstance");
+    if (fpCreateInstance == NULL) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // Advance the link info for the next element on the chain
+    chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+    result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
+    if (result != VK_SUCCESS) {
+        return result;
+    }
+    endTime = vktrace_get_time();
+
+    initInstanceData(*pInstance, fpGetInstanceProcAddr, g_instanceDataMap);
+    ext_init_create_instance(mid(*pInstance), *pInstance, pCreateInfo->enabledExtensionCount, pCreateInfo->ppEnabledExtensionNames);
+
+    // remove the loader extended createInfo structure
+    VkInstanceCreateInfo localCreateInfo;
+    memcpy(&localCreateInfo, pCreateInfo, sizeof(localCreateInfo));
+
+    // Alloc space to copy pointers
+    if (localCreateInfo.enabledLayerCount > 0)
+        localCreateInfo.ppEnabledLayerNames = (const char* const*) malloc(localCreateInfo.enabledLayerCount * sizeof(char*));
+    if (localCreateInfo.enabledExtensionCount > 0)
+        localCreateInfo.ppEnabledExtensionNames = (const char* const*) malloc(localCreateInfo.enabledExtensionCount * sizeof(char*));
+
+    for (i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
+        char **ppName = (char **) &localCreateInfo.ppEnabledExtensionNames[i];
+        *ppName = (char *) pCreateInfo->ppEnabledExtensionNames[i];
+    }
+
+    // If app requests vktrace layer, don't record that in the trace
+    char **ppName = (char **) &localCreateInfo.ppEnabledLayerNames[0];
+    for (i = 0 ; i < pCreateInfo->enabledLayerCount; i++) {
+        if (strcmp("VK_LAYER_LUNARG_vktrace", pCreateInfo->ppEnabledLayerNames[i]) == 0) {
+            // Decrement the enabled layer count and skip copying the pointer
+            localCreateInfo.enabledLayerCount--;
+        } else {
+            // Copy pointer and increment write pointer for everything else
+            *ppName++ = (char *) pCreateInfo->ppEnabledLayerNames[i];
+        }
+    }
+
+    //localCreateInfo.pNext = strip_create_extensions(pCreateInfo->pNext);
+    // The pNext pointer isn't getting marshalled into the trace buffer properly anyway, so
+    // set it to NULL so that replay does not trip over it.
+    localCreateInfo.pNext = NULL;
+    CREATE_TRACE_PACKET(vkCreateInstance, sizeof(VkInstance) + get_struct_chain_size((void*)&localCreateInfo) + sizeof(VkAllocationCallbacks));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkCreateInstance(pHeader);
+
+    add_VkInstanceCreateInfo_to_packet(pHeader, (VkInstanceCreateInfo**)&(pPacket->pCreateInfo), (VkInstanceCreateInfo*) &localCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pInstance), sizeof(VkInstance), pInstance);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pInstance));
+    FINISH_TRACE_PACKET();
+
+    if (localCreateInfo.enabledLayerCount > 0)
+        free((void *)localCreateInfo.ppEnabledLayerNames);
+    if (localCreateInfo.enabledExtensionCount > 0)
+        free((void *)localCreateInfo.ppEnabledExtensionNames);
+
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkDestroyInstance(
+    VkInstance instance,
+    const VkAllocationCallbacks* pAllocator)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkDestroyInstance* pPacket = NULL;
+    dispatch_key key = get_dispatch_key(instance);
+    CREATE_TRACE_PACKET(vkDestroyInstance, sizeof(VkAllocationCallbacks));
+    mid(instance)->instTable.DestroyInstance(instance, pAllocator);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkDestroyInstance(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    FINISH_TRACE_PACKET();
+    g_instanceDataMap.erase(key);
+#if defined(USE_PAGEGUARD_SPEEDUP) && !defined(PAGEGUARD_MEMCPY_USE_PPL_LIB)
+	vktrace_pageguard_done_multi_threads_memcpy();
+#endif
+}
+
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateRenderPass(
+    VkDevice device,
+    const VkRenderPassCreateInfo* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkRenderPass* pRenderPass)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateRenderPass* pPacket = NULL;
+    // begin custom code (get_struct_chain_size)
+    uint32_t attachmentCount = (pCreateInfo != NULL && (pCreateInfo->pAttachments != NULL)) ? pCreateInfo->attachmentCount : 0;
+    uint32_t dependencyCount = (pCreateInfo != NULL && (pCreateInfo->pDependencies != NULL)) ? pCreateInfo->dependencyCount : 0;
+    uint32_t subpassCount = (pCreateInfo != NULL && (pCreateInfo->pSubpasses != NULL)) ? pCreateInfo->subpassCount : 0;
+    CREATE_TRACE_PACKET(vkCreateRenderPass, get_struct_chain_size((void*)pCreateInfo) + sizeof(VkAllocationCallbacks) + sizeof(VkRenderPass));
+    // end custom code
+    result = mdd(device)->devTable.CreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreateRenderPass(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkRenderPassCreateInfo), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pAttachments), attachmentCount * sizeof(VkAttachmentDescription), pCreateInfo->pAttachments);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pDependencies), dependencyCount * sizeof(VkSubpassDependency), pCreateInfo->pDependencies);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pSubpasses), subpassCount * sizeof(VkSubpassDescription), pCreateInfo->pSubpasses);
+    uint32_t i;
+    for (i=0; i < pPacket->pCreateInfo->subpassCount; i++) {
+        VkSubpassDescription *pSubpass = (VkSubpassDescription *) &pPacket->pCreateInfo->pSubpasses[i];
+        const VkSubpassDescription *pSp = &pCreateInfo->pSubpasses[i];
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSubpass->pInputAttachments), pSubpass->inputAttachmentCount * sizeof(VkAttachmentReference), pSp->pInputAttachments);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pSubpass->pInputAttachments));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSubpass->pColorAttachments), pSubpass->colorAttachmentCount * sizeof(VkAttachmentReference), pSp->pColorAttachments);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pSubpass->pColorAttachments));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSubpass->pResolveAttachments), pSubpass->colorAttachmentCount * sizeof(VkAttachmentReference), pSp->pResolveAttachments);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pSubpass->pResolveAttachments));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSubpass->pDepthStencilAttachment), 1 * sizeof(VkAttachmentReference), pSp->pDepthStencilAttachment);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pSubpass->pDepthStencilAttachment));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSubpass->pPreserveAttachments), pSubpass->preserveAttachmentCount * sizeof(VkAttachmentReference), pSp->pPreserveAttachments);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pSubpass->pPreserveAttachments));
+    }
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pRenderPass), sizeof(VkRenderPass), pRenderPass);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pAttachments));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pDependencies));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pSubpasses));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pRenderPass));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkEnumerateDeviceExtensionProperties(
+    VkPhysicalDevice physicalDevice,
+    const char* pLayerName,
+    uint32_t* pPropertyCount,
+    VkExtensionProperties* pProperties)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkEnumerateDeviceExtensionProperties* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    // Only call down chain if querying ICD rather than layer device extensions
+    if (pLayerName == NULL)
+        result = mid(physicalDevice)->instTable.EnumerateDeviceExtensionProperties(physicalDevice, NULL, pPropertyCount, pProperties);
+    else
+    {
+        *pPropertyCount = 0;
+        return VK_SUCCESS;
+    }
+    endTime = vktrace_get_time();
+    CREATE_TRACE_PACKET(vkEnumerateDeviceExtensionProperties, ((pLayerName != NULL) ? ROUNDUP_TO_4(strlen(pLayerName) + 1) : 0) + sizeof(uint32_t) + (*pPropertyCount * sizeof(VkExtensionProperties)));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkEnumerateDeviceExtensionProperties(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pLayerName), ((pLayerName != NULL) ? ROUNDUP_TO_4(strlen(pLayerName) + 1) : 0), pLayerName);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPropertyCount), sizeof(uint32_t), pPropertyCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pProperties), *pPropertyCount * sizeof(VkExtensionProperties), pProperties);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pLayerName));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPropertyCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pProperties));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkEnumerateDeviceLayerProperties(
+    VkPhysicalDevice physicalDevice,
+    uint32_t* pPropertyCount,
+    VkLayerProperties* pProperties)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkEnumerateDeviceLayerProperties* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    result = mid(physicalDevice)->instTable.EnumerateDeviceLayerProperties(physicalDevice, pPropertyCount, pProperties);
+    endTime = vktrace_get_time();
+    CREATE_TRACE_PACKET(vkEnumerateDeviceLayerProperties, sizeof(uint32_t) + (*pPropertyCount * sizeof(VkLayerProperties)));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkEnumerateDeviceLayerProperties(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPropertyCount), sizeof(uint32_t), pPropertyCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pProperties), *pPropertyCount * sizeof(VkLayerProperties), pProperties);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPropertyCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pProperties));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+// TODO : This should be pretty easy to fit into codegen. Don't need to make the call prior to creating packet
+//  Just need to account for "count" number of queue properties
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkGetPhysicalDeviceQueueFamilyProperties(
+    VkPhysicalDevice physicalDevice,
+    uint32_t* pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties* pQueueFamilyProperties)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkGetPhysicalDeviceQueueFamilyProperties* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    mid(physicalDevice)->instTable.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+    endTime = vktrace_get_time();
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceQueueFamilyProperties, sizeof(uint32_t) + *pQueueFamilyPropertyCount * sizeof(VkQueueFamilyProperties));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkGetPhysicalDeviceQueueFamilyProperties(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pQueueFamilyPropertyCount), sizeof(uint32_t), pQueueFamilyPropertyCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pQueueFamilyProperties), *pQueueFamilyPropertyCount * sizeof(VkQueueFamilyProperties), pQueueFamilyProperties);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pQueueFamilyPropertyCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pQueueFamilyProperties));
+    FINISH_TRACE_PACKET();
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkEnumeratePhysicalDevices(
+    VkInstance instance,
+    uint32_t* pPhysicalDeviceCount,
+    VkPhysicalDevice* pPhysicalDevices)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkEnumeratePhysicalDevices* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    //TODO make sure can handle being called twice with pPD == 0
+    SEND_ENTRYPOINT_ID(vkEnumeratePhysicalDevices);
+    startTime = vktrace_get_time();
+    result = mid(instance)->instTable.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
+    endTime = vktrace_get_time();
+    CREATE_TRACE_PACKET(vkEnumeratePhysicalDevices, sizeof(uint32_t) + ((pPhysicalDevices && pPhysicalDeviceCount) ? *pPhysicalDeviceCount * sizeof(VkPhysicalDevice) : 0));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkEnumeratePhysicalDevices(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPhysicalDeviceCount), sizeof(uint32_t), pPhysicalDeviceCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPhysicalDevices), *pPhysicalDeviceCount*sizeof(VkPhysicalDevice), pPhysicalDevices);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPhysicalDeviceCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPhysicalDevices));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetQueryPoolResults(
+    VkDevice device,
+    VkQueryPool queryPool,
+    uint32_t firstQuery,
+    uint32_t queryCount,
+    size_t dataSize,
+    void* pData,
+    VkDeviceSize stride,
+    VkQueryResultFlags flags)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkGetQueryPoolResults* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    result = mdd(device)->devTable.GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
+    endTime = vktrace_get_time();
+    CREATE_TRACE_PACKET(vkGetQueryPoolResults, dataSize);
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkGetQueryPoolResults(pHeader);
+    pPacket->device = device;
+    pPacket->queryPool = queryPool;
+    pPacket->firstQuery = firstQuery;
+    pPacket->queryCount = queryCount;
+    pPacket->dataSize = dataSize;
+    pPacket->stride = stride;
+    pPacket->flags = flags;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pData), dataSize, pData);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pData));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkAllocateDescriptorSets(
+    VkDevice device,
+    const VkDescriptorSetAllocateInfo* pAllocateInfo,
+    VkDescriptorSet* pDescriptorSets)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkAllocateDescriptorSets* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    SEND_ENTRYPOINT_ID(vkAllocateDescriptorSets);
+    startTime = vktrace_get_time();
+    result = mdd(device)->devTable.AllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets);
+    endTime = vktrace_get_time();
+    CREATE_TRACE_PACKET(vkAllocateDescriptorSets, vk_size_vkdescriptorsetallocateinfo(pAllocateInfo) + (pAllocateInfo->descriptorSetCount * sizeof(VkDescriptorSet)));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkAllocateDescriptorSets(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo), sizeof(VkDescriptorSetAllocateInfo), pAllocateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo->pSetLayouts), pPacket->pAllocateInfo->descriptorSetCount * sizeof(VkDescriptorSetLayout), pAllocateInfo->pSetLayouts);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorSets), pPacket->pAllocateInfo->descriptorSetCount * sizeof(VkDescriptorSet), pDescriptorSets);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocateInfo->pSetLayouts));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorSets));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocateInfo));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkUpdateDescriptorSets(
+VkDevice device,
+        uint32_t descriptorWriteCount,
+        const VkWriteDescriptorSet* pDescriptorWrites,
+        uint32_t descriptorCopyCount,
+        const VkCopyDescriptorSet* pDescriptorCopies);
+// Manually written because it needs to use get_struct_chain_size and allocate some extra pointers (why?)
+// Also since it needs to app the array of pointers and sub-buffers (see comments in function)
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkUpdateDescriptorSets(
+    VkDevice device,
+    uint32_t descriptorWriteCount,
+    const VkWriteDescriptorSet* pDescriptorWrites,
+    uint32_t descriptorCopyCount,
+    const VkCopyDescriptorSet* pDescriptorCopies )
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkUpdateDescriptorSets* pPacket = NULL;
+    // begin custom code
+    size_t arrayByteCount = 0;
+    size_t i;
+
+    for (i = 0; i < descriptorWriteCount; i++)
+    {
+        arrayByteCount += get_struct_chain_size(&pDescriptorWrites[i]);
+    }
+
+    for (i = 0; i < descriptorCopyCount; i++)
+    {
+        arrayByteCount += get_struct_chain_size(&pDescriptorCopies[i]);
+    }
+
+    CREATE_TRACE_PACKET(vkUpdateDescriptorSets, arrayByteCount);
+    // end custom code
+
+    mdd(device)->devTable.UpdateDescriptorSets(device, descriptorWriteCount, pDescriptorWrites, descriptorCopyCount, pDescriptorCopies);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkUpdateDescriptorSets(pHeader);
+    pPacket->device = device;
+    pPacket->descriptorWriteCount = descriptorWriteCount;
+    // begin custom code
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorWrites), descriptorWriteCount * sizeof(VkWriteDescriptorSet), pDescriptorWrites);
+    for (i = 0; i < descriptorWriteCount; i++)
+    {
+        switch (pPacket->pDescriptorWrites[i].descriptorType) {
+        case VK_DESCRIPTOR_TYPE_SAMPLER:
+        case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+        case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+        case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+        case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+            {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorWrites[i].pImageInfo),
+                                                   pDescriptorWrites[i].descriptorCount * sizeof(VkDescriptorImageInfo),
+                                                   pDescriptorWrites[i].pImageInfo);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorWrites[i].pImageInfo));
+            }
+            break;
+        case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+        case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+            {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorWrites[i].pTexelBufferView),
+                                                   pDescriptorWrites[i].descriptorCount * sizeof(VkBufferView),
+                                                   pDescriptorWrites[i].pTexelBufferView);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorWrites[i].pTexelBufferView));
+            }
+            break;
+        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+            {
+                vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorWrites[i].pBufferInfo),
+                                                   pDescriptorWrites[i].descriptorCount * sizeof(VkDescriptorBufferInfo),
+                                                   pDescriptorWrites[i].pBufferInfo);
+                vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorWrites[i].pBufferInfo));
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorWrites));
+
+    pPacket->descriptorCopyCount = descriptorCopyCount;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorCopies), descriptorCopyCount * sizeof(VkCopyDescriptorSet), pDescriptorCopies);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorCopies));
+    // end custom code
+    FINISH_TRACE_PACKET();
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkQueueSubmit(
+    VkQueue queue,
+    uint32_t submitCount,
+    const VkSubmitInfo* pSubmits,
+    VkFence fence)
+{
+#ifdef USE_PAGEGUARD_SPEEDUP
+    pageguardEnter();
+#if defined (PLATFORM_LINUX)
+    getMappedDirtyPagesLinux();
+#endif
+    flushAllChangedMappedMemory(&vkFlushMappedMemoryRangesWithoutAPICall);
+    resetAllReadFlagAndPageGuard();
+    pageguardExit();
+#endif
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkQueueSubmit* pPacket = NULL;
+    size_t arrayByteCount = 0;
+    uint32_t i = 0;
+    for (i=0; i<submitCount; ++i) {
+        arrayByteCount += vk_size_vksubmitinfo(&pSubmits[i]);
+    }
+    CREATE_TRACE_PACKET(vkQueueSubmit, arrayByteCount);
+    result = mdd(queue)->devTable.QueueSubmit(queue, submitCount, pSubmits, fence);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkQueueSubmit(pHeader);
+    pPacket->queue = queue;
+    pPacket->submitCount = submitCount;
+    pPacket->fence = fence;
+    pPacket->result = result;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSubmits), submitCount*sizeof(VkSubmitInfo), pSubmits);
+    for (i=0; i<submitCount; ++i) {
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSubmits[i].pCommandBuffers), pPacket->pSubmits[i].commandBufferCount * sizeof(VkCommandBuffer), pSubmits[i].pCommandBuffers);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSubmits[i].pCommandBuffers));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSubmits[i].pWaitSemaphores), pPacket->pSubmits[i].waitSemaphoreCount * sizeof(VkSemaphore), pSubmits[i].pWaitSemaphores);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSubmits[i].pWaitSemaphores));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSubmits[i].pSignalSemaphores), pPacket->pSubmits[i].signalSemaphoreCount * sizeof(VkSemaphore), pSubmits[i].pSignalSemaphores);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSubmits[i].pSignalSemaphores));
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSubmits[i].pWaitDstStageMask), pPacket->pSubmits[i].waitSemaphoreCount * sizeof(VkPipelineStageFlags), pSubmits[i].pWaitDstStageMask);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSubmits[i].pWaitDstStageMask));
+    }
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSubmits));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkQueueBindSparse(
+    VkQueue queue,
+    uint32_t bindInfoCount,
+    const VkBindSparseInfo* pBindInfo,
+    VkFence fence)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkQueueBindSparse* pPacket = NULL;
+    size_t arrayByteCount = 0;
+    uint32_t i = 0;
+
+    for (i = 0; i<bindInfoCount; ++i) {
+        arrayByteCount += vk_size_vkbindsparseinfo(&pBindInfo[i]);
+    }
+
+    CREATE_TRACE_PACKET(vkQueueBindSparse, arrayByteCount + 2 * sizeof(VkDeviceMemory));
+    result = mdd(queue)->devTable.QueueBindSparse(queue, bindInfoCount, pBindInfo, fence);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkQueueBindSparse(pHeader);
+    pPacket->queue = queue;
+    pPacket->bindInfoCount = bindInfoCount;
+    pPacket->fence = fence;
+    pPacket->result = result;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo), bindInfoCount * sizeof(VkBindSparseInfo), pBindInfo);
+
+    for (i = 0; i<bindInfoCount; ++i) {
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo[i].pBufferBinds), pPacket->pBindInfo[i].bufferBindCount * sizeof(VkSparseBufferMemoryBindInfo), pBindInfo[i].pBufferBinds);
+	for (uint32_t j = 0; j < pPacket->pBindInfo[i].bufferBindCount; j++) {
+            VkSparseBufferMemoryBindInfo *pSparseBufferMemoryBindInfo = (VkSparseBufferMemoryBindInfo *)&pPacket->pBindInfo[i].pBufferBinds[j];
+            const VkSparseBufferMemoryBindInfo *pSparseBufMemBndInf = &pBindInfo[i].pBufferBinds[j];
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSparseBufferMemoryBindInfo->pBinds), pSparseBufferMemoryBindInfo->bindCount * sizeof(VkSparseMemoryBind), pSparseBufMemBndInf->pBinds);
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pSparseBufferMemoryBindInfo->pBinds));
+        }
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBindInfo[i].pBufferBinds));
+
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo[i].pImageBinds), pPacket->pBindInfo[i].imageBindCount * sizeof(VkSparseImageMemoryBindInfo), pBindInfo[i].pImageOpaqueBinds);
+        for (uint32_t j = 0; j < pPacket->pBindInfo[i].imageBindCount; j++) {
+            VkSparseImageMemoryBindInfo *pSparseImageMemoryBindInfo = (VkSparseImageMemoryBindInfo *)&pPacket->pBindInfo[i].pImageBinds[j];
+            const VkSparseImageMemoryBindInfo *pSparseImgMemBndInf = &pBindInfo[i].pImageBinds[j];
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSparseImageMemoryBindInfo->pBinds), pSparseImageMemoryBindInfo->bindCount * sizeof(VkSparseImageMemoryBind), pSparseImgMemBndInf->pBinds);
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pSparseImageMemoryBindInfo->pBinds));
+        }
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBindInfo[i].pImageBinds));
+
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo[i].pImageOpaqueBinds), pPacket->pBindInfo[i].imageOpaqueBindCount * sizeof(VkSparseImageOpaqueMemoryBindInfo), pBindInfo[i].pImageOpaqueBinds);
+        for (uint32_t j = 0; j < pPacket->pBindInfo[i].imageOpaqueBindCount; j++) {
+            VkSparseImageOpaqueMemoryBindInfo *pSparseImageOpaqueMemoryBindInfo = (VkSparseImageOpaqueMemoryBindInfo *)&pPacket->pBindInfo[i].pImageOpaqueBinds[j];
+            const VkSparseImageOpaqueMemoryBindInfo *pSparseImgOpqMemBndInf = &pBindInfo[i].pImageOpaqueBinds[j];
+            vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pSparseImageOpaqueMemoryBindInfo->pBinds), pSparseImageOpaqueMemoryBindInfo->bindCount * sizeof(VkSparseMemoryBind), pSparseImgOpqMemBndInf->pBinds);
+            vktrace_finalize_buffer_address(pHeader, (void**)&(pSparseImageOpaqueMemoryBindInfo->pBinds));
+        }
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBindInfo[i].pImageOpaqueBinds));
+
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo[i].pWaitSemaphores), pPacket->pBindInfo[i].waitSemaphoreCount * sizeof(VkSemaphore), pBindInfo[i].pWaitSemaphores);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBindInfo[i].pWaitSemaphores));
+
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo[i].pSignalSemaphores), pPacket->pBindInfo[i].signalSemaphoreCount * sizeof(VkSemaphore), pBindInfo[i].pSignalSemaphores);
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBindInfo[i].pSignalSemaphores));
+    }
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBindInfo));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkCmdWaitEvents(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    eventCount,
+    const VkEvent*                              pEvents,
+    VkPipelineStageFlags                        srcStageMask,
+    VkPipelineStageFlags                        dstStageMask,
+    uint32_t                                    memoryBarrierCount,
+    const VkMemoryBarrier*                      pMemoryBarriers,
+    uint32_t                                    bufferMemoryBarrierCount,
+    const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
+    uint32_t                                    imageMemoryBarrierCount,
+    const VkImageMemoryBarrier*                 pImageMemoryBarriers)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkCmdWaitEvents* pPacket = NULL;
+    size_t customSize;
+    customSize = (eventCount * sizeof(VkEvent)) + (memoryBarrierCount * sizeof(VkMemoryBarrier)) +
+            (bufferMemoryBarrierCount * sizeof(VkBufferMemoryBarrier)) +
+            (imageMemoryBarrierCount * sizeof(VkImageMemoryBarrier));
+    CREATE_TRACE_PACKET(vkCmdWaitEvents, customSize);
+    mdd(commandBuffer)->devTable.CmdWaitEvents(commandBuffer, eventCount, pEvents, srcStageMask, dstStageMask,
+                                    memoryBarrierCount, pMemoryBarriers,
+                                    bufferMemoryBarrierCount, pBufferMemoryBarriers,
+                                    imageMemoryBarrierCount, pImageMemoryBarriers);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCmdWaitEvents(pHeader);
+    pPacket->commandBuffer = commandBuffer;
+    pPacket->eventCount = eventCount;
+    pPacket->srcStageMask = srcStageMask;
+    pPacket->dstStageMask = dstStageMask;
+    pPacket->memoryBarrierCount = memoryBarrierCount;
+    pPacket->bufferMemoryBarrierCount = bufferMemoryBarrierCount;
+    pPacket->imageMemoryBarrierCount = imageMemoryBarrierCount;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pEvents), eventCount * sizeof(VkEvent), pEvents);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pEvents));
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMemoryBarriers), memoryBarrierCount * sizeof(VkMemoryBarrier), pMemoryBarriers);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBufferMemoryBarriers), bufferMemoryBarrierCount * sizeof(VkBufferMemoryBarrier), pBufferMemoryBarriers);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pImageMemoryBarriers), imageMemoryBarrierCount * sizeof(VkImageMemoryBarrier), pImageMemoryBarriers);
+
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMemoryBarriers));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBufferMemoryBarriers));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pImageMemoryBarriers));
+    FINISH_TRACE_PACKET();
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkCmdPipelineBarrier(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineStageFlags                        srcStageMask,
+    VkPipelineStageFlags                        dstStageMask,
+    VkDependencyFlags                           dependencyFlags,
+    uint32_t                                    memoryBarrierCount,
+    const VkMemoryBarrier*                      pMemoryBarriers,
+    uint32_t                                    bufferMemoryBarrierCount,
+    const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
+    uint32_t                                    imageMemoryBarrierCount,
+    const VkImageMemoryBarrier*                 pImageMemoryBarriers)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkCmdPipelineBarrier* pPacket = NULL;
+    size_t customSize;
+    customSize = (memoryBarrierCount * sizeof(VkMemoryBarrier)) +
+            (bufferMemoryBarrierCount * sizeof(VkBufferMemoryBarrier)) +
+            (imageMemoryBarrierCount * sizeof(VkImageMemoryBarrier));
+    CREATE_TRACE_PACKET(vkCmdPipelineBarrier, customSize);
+    mdd(commandBuffer)->devTable.CmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCmdPipelineBarrier(pHeader);
+    pPacket->commandBuffer = commandBuffer;
+    pPacket->srcStageMask = srcStageMask;
+    pPacket->dstStageMask = dstStageMask;
+    pPacket->dependencyFlags = dependencyFlags;
+    pPacket->memoryBarrierCount = memoryBarrierCount;
+    pPacket->bufferMemoryBarrierCount = bufferMemoryBarrierCount;
+    pPacket->imageMemoryBarrierCount = imageMemoryBarrierCount;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMemoryBarriers), memoryBarrierCount * sizeof(VkMemoryBarrier), pMemoryBarriers);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBufferMemoryBarriers), bufferMemoryBarrierCount * sizeof(VkBufferMemoryBarrier), pBufferMemoryBarriers);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pImageMemoryBarriers), imageMemoryBarrierCount * sizeof(VkImageMemoryBarrier), pImageMemoryBarriers);
+
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pMemoryBarriers));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pBufferMemoryBarriers));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pImageMemoryBarriers));
+    FINISH_TRACE_PACKET();
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkCmdPushConstants(
+    VkCommandBuffer commandBuffer,
+    VkPipelineLayout layout,
+    VkShaderStageFlags stageFlags,
+    uint32_t offset,
+    uint32_t size,
+    const void* pValues)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkCmdPushConstants* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkCmdPushConstants, size);
+    mdd(commandBuffer)->devTable.CmdPushConstants(commandBuffer, layout, stageFlags, offset, size, pValues);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCmdPushConstants(pHeader);
+    pPacket->commandBuffer = commandBuffer;
+    pPacket->layout = layout;
+    pPacket->stageFlags = stageFlags;
+    pPacket->offset = offset;
+    pPacket->size = size;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pValues), size, pValues);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pValues));
+    FINISH_TRACE_PACKET();
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetPipelineCacheData(
+    VkDevice device,
+    VkPipelineCache pipelineCache,
+    size_t* pDataSize,
+    void* pData)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkGetPipelineCacheData* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    result = mdd(device)->devTable.GetPipelineCacheData(device, pipelineCache, pDataSize, pData);
+    endTime = vktrace_get_time();
+    assert(pDataSize);
+    CREATE_TRACE_PACKET(vkGetPipelineCacheData, sizeof(size_t) + ROUNDUP_TO_4(*pDataSize));
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkGetPipelineCacheData(pHeader);
+    pPacket->device = device;
+    pPacket->pipelineCache = pipelineCache;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDataSize), sizeof(size_t), pDataSize);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pData), ROUNDUP_TO_4(*pDataSize), pData);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDataSize));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pData));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateGraphicsPipelines(
+    VkDevice device,
+    VkPipelineCache pipelineCache,
+    uint32_t createInfoCount,
+    const VkGraphicsPipelineCreateInfo* pCreateInfos,
+    const VkAllocationCallbacks* pAllocator,
+    VkPipeline* pPipelines)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateGraphicsPipelines* pPacket = NULL;
+    size_t total_size = 0;
+    uint32_t i;
+    for (i = 0; i < createInfoCount; i++) {
+        total_size += get_struct_chain_size((void*)&pCreateInfos[i]);
+    }
+    CREATE_TRACE_PACKET(vkCreateGraphicsPipelines, total_size + sizeof(VkAllocationCallbacks) + createInfoCount*sizeof(VkPipeline));
+    result = mdd(device)->devTable.CreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreateGraphicsPipelines(pHeader);
+    pPacket->device = device;
+    pPacket->pipelineCache = pipelineCache;
+    pPacket->createInfoCount = createInfoCount;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfos), createInfoCount*sizeof(VkGraphicsPipelineCreateInfo), pCreateInfos);
+    add_VkGraphicsPipelineCreateInfos_to_trace_packet(pHeader, (VkGraphicsPipelineCreateInfo*)pPacket->pCreateInfos, pCreateInfos, createInfoCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPipelines), createInfoCount*sizeof(VkPipeline), pPipelines);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfos));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPipelines));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+uint64_t getVkComputePipelineCreateInfosAdditionalSize(uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos)
+{
+    uint64_t uiRet = 0;
+    VkPipelineShaderStageCreateInfo* packetShader;
+    for (uint32_t i = 0; i < createInfoCount; i++)
+    {
+        uiRet += sizeof(VkPipelineShaderStageCreateInfo);
+        packetShader = (VkPipelineShaderStageCreateInfo*)&pCreateInfos[i].stage;
+        uiRet += strlen(packetShader->pName) + 1;
+        uiRet += sizeof(VkSpecializationInfo);
+        if (packetShader->pSpecializationInfo != NULL)
+        {
+            uiRet += sizeof(VkSpecializationMapEntry) * packetShader->pSpecializationInfo->mapEntryCount;
+            uiRet += packetShader->pSpecializationInfo->dataSize;
+        }
+    }
+    return uiRet;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateComputePipelines(
+    VkDevice device,
+    VkPipelineCache pipelineCache,
+    uint32_t createInfoCount,
+    const VkComputePipelineCreateInfo* pCreateInfos,
+    const VkAllocationCallbacks* pAllocator,
+    VkPipeline* pPipelines)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateComputePipelines* pPacket = NULL;
+    /*uint32_t i;
+    size_t total_size;
+
+    total_size = createInfoCount*sizeof(VkComputePipelineCreateInfo) + sizeof(VkAllocationCallbacks) + createInfoCount*sizeof(VkPipeline);
+    for (i=0; i < createInfoCount; i++) {
+        total_size += ROUNDUP_TO_4(strlen(pCreateInfos[i].stage.pName)+1);
+        if (pCreateInfos[i].stage.pSpecializationInfo) {
+            total_size += sizeof(VkSpecializationInfo);
+            if (pCreateInfos[i].stage.pSpecializationInfo->mapEntryCount > 0 && pCreateInfos[i].stage.pSpecializationInfo->pMapEntries)
+                total_size += pCreateInfos[i].stage.pSpecializationInfo->mapEntryCount * sizeof(VkSpecializationMapEntry);
+            if (pCreateInfos[i].stage.pSpecializationInfo->dataSize > 0 && pCreateInfos[i].stage.pSpecializationInfo->pData)
+                total_size += pCreateInfos[i].stage.pSpecializationInfo->dataSize;
+        }
+    }
+    CREATE_TRACE_PACKET(vkCreateComputePipelines, total_size);*/
+    CREATE_TRACE_PACKET(vkCreateComputePipelines, createInfoCount*sizeof(VkComputePipelineCreateInfo) + getVkComputePipelineCreateInfosAdditionalSize( createInfoCount, pCreateInfos) + sizeof(VkAllocationCallbacks) + createInfoCount*sizeof(VkPipeline));
+
+    result = mdd(device)->devTable.CreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreateComputePipelines(pHeader);
+    pPacket->device = device;
+    pPacket->pipelineCache = pipelineCache;
+    pPacket->createInfoCount = createInfoCount;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfos), createInfoCount*sizeof(VkComputePipelineCreateInfo), pCreateInfos);
+    add_VkComputePipelineCreateInfos_to_trace_packet(pHeader, (VkComputePipelineCreateInfo*)pPacket->pCreateInfos, pCreateInfos, createInfoCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPipelines), createInfoCount*sizeof(VkPipeline), pPipelines);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfos));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPipelines));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreatePipelineCache(
+    VkDevice device,
+    const VkPipelineCacheCreateInfo* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkPipelineCache* pPipelineCache)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreatePipelineCache* pPacket = NULL;
+    // Need to round up the size when we create the packet because pCreateInfo->initialDataSize may not be a mult of 4
+    CREATE_TRACE_PACKET(vkCreatePipelineCache, ROUNDUP_TO_4(get_struct_chain_size((void*)pCreateInfo) + sizeof(VkAllocationCallbacks) + sizeof(VkPipelineCache)));
+    result = mdd(device)->devTable.CreatePipelineCache(device, pCreateInfo, pAllocator, pPipelineCache);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCreatePipelineCache(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkPipelineCacheCreateInfo), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pInitialData), ROUNDUP_TO_4(pPacket->pCreateInfo->initialDataSize), pCreateInfo->pInitialData);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPipelineCache), sizeof(VkPipelineCache), pPipelineCache);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pInitialData));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPipelineCache));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR void VKAPI_CALL __HOOKED_vkCmdBeginRenderPass(
+    VkCommandBuffer commandBuffer,
+    const VkRenderPassBeginInfo* pRenderPassBegin,
+    VkSubpassContents contents)
+{
+    vktrace_trace_packet_header* pHeader;
+    packet_vkCmdBeginRenderPass* pPacket = NULL;
+    size_t clearValueSize = sizeof(VkClearValue) * pRenderPassBegin->clearValueCount;
+    CREATE_TRACE_PACKET(vkCmdBeginRenderPass, sizeof(VkRenderPassBeginInfo) + clearValueSize);
+    mdd(commandBuffer)->devTable.CmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkCmdBeginRenderPass(pHeader);
+    pPacket->commandBuffer = commandBuffer;
+    pPacket->contents = contents;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pRenderPassBegin), sizeof(VkRenderPassBeginInfo), pRenderPassBegin);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pRenderPassBegin->pClearValues), clearValueSize, pRenderPassBegin->pClearValues);
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pRenderPassBegin->pClearValues));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pRenderPassBegin));
+    FINISH_TRACE_PACKET();
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkFreeDescriptorSets(
+    VkDevice device,
+    VkDescriptorPool descriptorPool,
+    uint32_t descriptorSetCount,
+    const VkDescriptorSet* pDescriptorSets)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkFreeDescriptorSets* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkFreeDescriptorSets, descriptorSetCount*sizeof(VkDescriptorSet));
+    result = mdd(device)->devTable.FreeDescriptorSets(device, descriptorPool, descriptorSetCount, pDescriptorSets);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkFreeDescriptorSets(pHeader);
+    pPacket->device = device;
+    pPacket->descriptorPool = descriptorPool;
+    pPacket->descriptorSetCount = descriptorSetCount;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorSets), descriptorSetCount*sizeof(VkDescriptorSet), pDescriptorSets);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDescriptorSets));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+    VkPhysicalDevice physicalDevice,
+    VkSurfaceKHR surface,
+    VkSurfaceCapabilitiesKHR* pSurfaceCapabilities)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkGetPhysicalDeviceSurfaceCapabilitiesKHR* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceSurfaceCapabilitiesKHR, sizeof(VkSurfaceCapabilitiesKHR));
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities);
+    pPacket = interpret_body_as_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->surface = surface;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurfaceCapabilities), sizeof(VkSurfaceCapabilitiesKHR), pSurfaceCapabilities);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurfaceCapabilities));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetPhysicalDeviceSurfaceFormatsKHR(
+    VkPhysicalDevice physicalDevice,
+    VkSurfaceKHR surface,
+    uint32_t* pSurfaceFormatCount,
+    VkSurfaceFormatKHR* pSurfaceFormats)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    size_t _dataSize;
+    packet_vkGetPhysicalDeviceSurfaceFormatsKHR* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats);
+    endTime = vktrace_get_time();
+    _dataSize = (pSurfaceFormatCount == NULL || pSurfaceFormats == NULL) ? 0 : (*pSurfaceFormatCount *sizeof(VkSurfaceFormatKHR));
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceSurfaceFormatsKHR, sizeof(uint32_t) + _dataSize);
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkGetPhysicalDeviceSurfaceFormatsKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->surface = surface;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurfaceFormatCount), sizeof(uint32_t), pSurfaceFormatCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurfaceFormats), _dataSize, pSurfaceFormats);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurfaceFormatCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurfaceFormats));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetPhysicalDeviceSurfacePresentModesKHR(
+    VkPhysicalDevice physicalDevice,
+    VkSurfaceKHR surface,
+    uint32_t* pPresentModeCount,
+    VkPresentModeKHR* pPresentModes)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    size_t _dataSize;
+    packet_vkGetPhysicalDeviceSurfacePresentModesKHR* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes);
+    endTime = vktrace_get_time();
+    _dataSize = (pPresentModeCount == NULL || pPresentModes == NULL) ? 0 : (*pPresentModeCount *sizeof(VkPresentModeKHR));
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceSurfacePresentModesKHR, sizeof(uint32_t) + _dataSize);
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkGetPhysicalDeviceSurfacePresentModesKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->surface = surface;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentModeCount), sizeof(uint32_t), pPresentModeCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentModes), _dataSize, pPresentModes);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentModeCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentModes));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateSwapchainKHR(
+    VkDevice device,
+    const VkSwapchainCreateInfoKHR* pCreateInfo,
+    const VkAllocationCallbacks* pAllocator,
+    VkSwapchainKHR* pSwapchain)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateSwapchainKHR* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkCreateSwapchainKHR, vk_size_vkswapchaincreateinfokhr(pCreateInfo) + sizeof(VkSwapchainKHR) + sizeof(VkAllocationCallbacks));
+    result = mdd(device)->devTable.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
+    pPacket = interpret_body_as_vkCreateSwapchainKHR(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkSwapchainCreateInfoKHR), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSwapchain), sizeof(VkSwapchainKHR), pSwapchain);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices), pCreateInfo->queueFamilyIndexCount * sizeof(uint32_t), pCreateInfo->pQueueFamilyIndices);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSwapchain));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetSwapchainImagesKHR(
+    VkDevice device,
+    VkSwapchainKHR swapchain,
+    uint32_t* pSwapchainImageCount,
+    VkImage* pSwapchainImages)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    size_t _dataSize;
+    packet_vkGetSwapchainImagesKHR* pPacket = NULL;
+    uint64_t startTime;
+    uint64_t endTime;
+    uint64_t vktraceStartTime = vktrace_get_time();
+    startTime = vktrace_get_time();
+    result = mdd(device)->devTable.GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
+    endTime = vktrace_get_time();
+    _dataSize = (pSwapchainImageCount == NULL || pSwapchainImages == NULL) ? 0 : (*pSwapchainImageCount *sizeof(VkImage));
+    CREATE_TRACE_PACKET(vkGetSwapchainImagesKHR, sizeof(uint32_t) + _dataSize);
+    pHeader->vktrace_begin_time = vktraceStartTime;
+    pHeader->entrypoint_begin_time = startTime;
+    pHeader->entrypoint_end_time = endTime;
+    pPacket = interpret_body_as_vkGetSwapchainImagesKHR(pHeader);
+    pPacket->device = device;
+    pPacket->swapchain = swapchain;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSwapchainImageCount), sizeof(uint32_t), pSwapchainImageCount);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSwapchainImages), _dataSize, pSwapchainImages);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSwapchainImageCount));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSwapchainImages));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkQueuePresentKHR(
+    VkQueue queue,
+    const VkPresentInfoKHR* pPresentInfo)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkQueuePresentKHR* pPacket = NULL;
+    size_t swapchainSize = pPresentInfo->swapchainCount * sizeof(VkSwapchainKHR);
+    size_t indexSize = pPresentInfo->swapchainCount * sizeof(uint32_t);
+    size_t semaSize = pPresentInfo->waitSemaphoreCount * sizeof(VkSemaphore);
+    size_t resultsSize = pPresentInfo->swapchainCount * sizeof(VkResult);
+    size_t totalSize = sizeof(VkPresentInfoKHR) + swapchainSize + indexSize + semaSize;
+    if (pPresentInfo->pResults != NULL) {
+        totalSize += resultsSize;
+    }
+    CREATE_TRACE_PACKET(vkQueuePresentKHR, totalSize);
+    result = mdd(queue)->devTable.QueuePresentKHR(queue, pPresentInfo);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkQueuePresentKHR(pHeader);
+    pPacket->queue = queue;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentInfo), sizeof(VkPresentInfoKHR), pPresentInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentInfo->pSwapchains), swapchainSize, pPresentInfo->pSwapchains);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentInfo->pImageIndices), indexSize, pPresentInfo->pImageIndices);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentInfo->pWaitSemaphores), semaSize, pPresentInfo->pWaitSemaphores);
+    if (pPresentInfo->pResults != NULL) {
+        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pPresentInfo->pResults), resultsSize, pPresentInfo->pResults);
+    }
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentInfo->pImageIndices));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentInfo->pSwapchains));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentInfo->pWaitSemaphores));
+    if (pPresentInfo->pResults != NULL) {
+        vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentInfo->pResults));
+    }
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pPresentInfo));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+/* TODO these can probably be moved into code gen */
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateWin32SurfaceKHR(
+    VkInstance                                  instance,
+    const VkWin32SurfaceCreateInfoKHR*          pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateWin32SurfaceKHR* pPacket = NULL;
+    // don't bother with copying the actual win32 hinstance, hwnd into the trace packet, vkreplay has to use it's own anyway
+    CREATE_TRACE_PACKET(vkCreateWin32SurfaceKHR, sizeof(VkSurfaceKHR) + sizeof(VkAllocationCallbacks) + sizeof(VkWin32SurfaceCreateInfoKHR));
+    result = mid(instance)->instTable.CreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+    pPacket = interpret_body_as_vkCreateWin32SurfaceKHR(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkWin32SurfaceCreateInfoKHR), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurface), sizeof(VkSurfaceKHR), pSurface);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurface));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkBool32 VKAPI_CALL __HOOKED_vkGetPhysicalDeviceWin32PresentationSupportKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    queueFamilyIndex)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkBool32 result;
+    packet_vkGetPhysicalDeviceWin32PresentationSupportKHR* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceWin32PresentationSupportKHR, 0);
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice, queueFamilyIndex);
+    pPacket = interpret_body_as_vkGetPhysicalDeviceWin32PresentationSupportKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->queueFamilyIndex = queueFamilyIndex;
+    pPacket->result = result;
+    FINISH_TRACE_PACKET();
+    return result;
+}
+#endif
+#ifdef VK_USE_PLATFORM_XCB_KHR
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateXcbSurfaceKHR(
+    VkInstance                                  instance,
+    const VkXcbSurfaceCreateInfoKHR*            pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateXcbSurfaceKHR* pPacket = NULL;
+    // don't bother with copying the actual xcb window and connection into the trace packet, vkreplay has to use it's own anyway
+    CREATE_TRACE_PACKET(vkCreateXcbSurfaceKHR, sizeof(VkSurfaceKHR) + sizeof(VkAllocationCallbacks) + sizeof(VkXcbSurfaceCreateInfoKHR));
+    result = mid(instance)->instTable.CreateXcbSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+    pPacket = interpret_body_as_vkCreateXcbSurfaceKHR(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkXcbSurfaceCreateInfoKHR), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurface), sizeof(VkSurfaceKHR), pSurface);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurface));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkBool32 VKAPI_CALL __HOOKED_vkGetPhysicalDeviceXcbPresentationSupportKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    queueFamilyIndex,
+    xcb_connection_t*                           connection,
+    xcb_visualid_t                              visual_id)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkBool32 result;
+    packet_vkGetPhysicalDeviceXcbPresentationSupportKHR* pPacket = NULL;
+    // don't bother with copying the actual xcb visual_id and connection into the trace packet, vkreplay has to use it's own anyway
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceXcbPresentationSupportKHR, 0);
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceXcbPresentationSupportKHR(physicalDevice, queueFamilyIndex, connection, visual_id);
+    pPacket = interpret_body_as_vkGetPhysicalDeviceXcbPresentationSupportKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->connection = connection;
+    pPacket->queueFamilyIndex = queueFamilyIndex;
+    pPacket->visual_id = visual_id;
+    pPacket->result = result;
+    FINISH_TRACE_PACKET();
+    return result;
+}
+#endif
+#ifdef VK_USE_PLATFORM_XLIB_KHR
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateXlibSurfaceKHR(
+    VkInstance                                  instance,
+    const VkXlibSurfaceCreateInfoKHR*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateXlibSurfaceKHR* pPacket = NULL;
+    // don't bother with copying the actual xlib window and connection into the trace packet, vkreplay has to use it's own anyway
+    CREATE_TRACE_PACKET(vkCreateXlibSurfaceKHR, sizeof(VkSurfaceKHR) + sizeof(VkAllocationCallbacks) + sizeof(VkXlibSurfaceCreateInfoKHR));
+    result = mid(instance)->instTable.CreateXlibSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+    pPacket = interpret_body_as_vkCreateXlibSurfaceKHR(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkXlibSurfaceCreateInfoKHR), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurface), sizeof(VkSurfaceKHR), pSurface);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurface));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+
+VKTRACER_EXPORT VKAPI_ATTR VkBool32 VKAPI_CALL __HOOKED_vkGetPhysicalDeviceXlibPresentationSupportKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    queueFamilyIndex,
+    Display*                                    dpy,
+    VisualID                                    visualID)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkBool32 result;
+    packet_vkGetPhysicalDeviceXlibPresentationSupportKHR* pPacket = NULL;
+    // don't bother with copying the actual xlib visual_id and connection into the trace packet, vkreplay has to use it's own anyway
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceXlibPresentationSupportKHR, 0);
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, dpy, visualID);
+    pPacket = interpret_body_as_vkGetPhysicalDeviceXlibPresentationSupportKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->dpy = dpy;
+    pPacket->queueFamilyIndex = queueFamilyIndex;
+    pPacket->visualID = visualID;
+    pPacket->result = result;
+    FINISH_TRACE_PACKET();
+    return result;
+}
+#endif
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkCreateAndroidSurfaceKHR(
+    VkInstance                                  instance,
+    const VkAndroidSurfaceCreateInfoKHR*        pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkCreateAndroidSurfaceKHR* pPacket = NULL;
+    // don't bother with copying the actual native window into the trace packet, vkreplay has to use it's own anyway
+    CREATE_TRACE_PACKET(vkCreateAndroidSurfaceKHR, sizeof(VkSurfaceKHR) + sizeof(VkAllocationCallbacks) + sizeof(VkAndroidSurfaceCreateInfoKHR));
+    result = mid(instance)->instTable.CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+    pPacket = interpret_body_as_vkCreateAndroidSurfaceKHR(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkAndroidSurfaceCreateInfoKHR), pCreateInfo);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurface), sizeof(VkSurfaceKHR), pSurface);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocator));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurface));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+#endif
+
+//TODO Wayland and Mir support
+
+/* TODO: Probably want to make this manual to get the result of the boolean and then check it on replay
+VKTRACER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL __HOOKED_vkGetPhysicalDeviceSurfaceSupportKHR(
+    VkPhysicalDevice physicalDevice,
+    uint32_t queueFamilyIndex,
+    const VkSurfaceDescriptionKHR* pSurfaceDescription,
+    VkBool32* pSupported)
+{
+    vktrace_trace_packet_header* pHeader;
+    VkResult result;
+    packet_vkGetPhysicalDeviceSurfaceSupportKHR* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkGetPhysicalDeviceSurfaceSupportKHR, sizeof(VkSurfaceDescriptionKHR) + sizeof(VkBool32));
+    result = mid(physicalDevice)->instTable.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, pSurfaceDescription, pSupported);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkGetPhysicalDeviceSurfaceSupportKHR(pHeader);
+    pPacket->physicalDevice = physicalDevice;
+    pPacket->queueFamilyIndex = queueFamilyIndex;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSurfaceDescription), sizeof(VkSurfaceDescriptionKHR), pSurfaceDescription);
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSupported), sizeof(VkBool32), pSupported);
+    pPacket->result = result;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSurfaceDescription));
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pSupported));
+    FINISH_TRACE_PACKET();
+    return result;
+}
+*/
+
+static inline PFN_vkVoidFunction layer_intercept_proc(const char *name)
+{
+    if (!name || name[0] != 'v' || name[1] != 'k')
+        return NULL;
+
+    name += 2;
+
+    if (!strcmp(name, "CreateDevice"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateDevice;
+    if (!strcmp(name, "DestroyDevice"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyDevice;
+    if (!strcmp(name, "GetDeviceQueue"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetDeviceQueue;
+    if (!strcmp(name, "QueueSubmit"))
+        return (PFN_vkVoidFunction) __HOOKED_vkQueueSubmit;
+    if (!strcmp(name, "QueueWaitIdle"))
+        return (PFN_vkVoidFunction) __HOOKED_vkQueueWaitIdle;
+    if (!strcmp(name, "DeviceWaitIdle"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDeviceWaitIdle;
+    if (!strcmp(name, "AllocateMemory"))
+        return (PFN_vkVoidFunction) __HOOKED_vkAllocateMemory;
+    if (!strcmp(name, "FreeMemory"))
+        return (PFN_vkVoidFunction) __HOOKED_vkFreeMemory;
+    if (!strcmp(name, "MapMemory"))
+        return (PFN_vkVoidFunction) __HOOKED_vkMapMemory;
+    if (!strcmp(name, "UnmapMemory"))
+        return (PFN_vkVoidFunction) __HOOKED_vkUnmapMemory;
+    if (!strcmp(name, "FlushMappedMemoryRanges"))
+        return (PFN_vkVoidFunction) __HOOKED_vkFlushMappedMemoryRanges;
+    if (!strcmp(name, "InvalidateMappedMemoryRanges"))
+        return (PFN_vkVoidFunction) __HOOKED_vkInvalidateMappedMemoryRanges;
+    if (!strcmp(name, "GetDeviceMemoryCommitment"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetDeviceMemoryCommitment;
+    if (!strcmp(name, "BindBufferMemory"))
+        return (PFN_vkVoidFunction) __HOOKED_vkBindBufferMemory;
+    if (!strcmp(name, "BindImageMemory"))
+        return (PFN_vkVoidFunction) __HOOKED_vkBindImageMemory;
+    if (!strcmp(name, "GetBufferMemoryRequirements"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetBufferMemoryRequirements;
+    if (!strcmp(name, "GetImageMemoryRequirements"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetImageMemoryRequirements;
+    if (!strcmp(name, "GetImageSparseMemoryRequirements"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetImageSparseMemoryRequirements;
+    if (!strcmp(name, "GetPhysicalDeviceSparseImageFormatProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceSparseImageFormatProperties;
+    if (!strcmp(name, "QueueBindSparse"))
+        return (PFN_vkVoidFunction) __HOOKED_vkQueueBindSparse;
+    if (!strcmp(name, "CreateFence"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateFence;
+    if (!strcmp(name, "DestroyFence"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyFence;
+    if (!strcmp(name, "ResetFences"))
+        return (PFN_vkVoidFunction) __HOOKED_vkResetFences;
+    if (!strcmp(name, "GetFenceStatus"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetFenceStatus;
+    if (!strcmp(name, "WaitForFences"))
+        return (PFN_vkVoidFunction) __HOOKED_vkWaitForFences;
+    if (!strcmp(name, "CreateSemaphore"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateSemaphore;
+    if (!strcmp(name, "DestroySemaphore"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroySemaphore;
+    if (!strcmp(name, "CreateEvent"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateEvent;
+    if (!strcmp(name, "DestroyEvent"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyEvent;
+    if (!strcmp(name, "GetEventStatus"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetEventStatus;
+    if (!strcmp(name, "SetEvent"))
+        return (PFN_vkVoidFunction) __HOOKED_vkSetEvent;
+    if (!strcmp(name, "ResetEvent"))
+        return (PFN_vkVoidFunction) __HOOKED_vkResetEvent;
+    if (!strcmp(name, "CreateQueryPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateQueryPool;
+    if (!strcmp(name, "DestroyQueryPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyQueryPool;
+    if (!strcmp(name, "GetQueryPoolResults"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetQueryPoolResults;
+    if (!strcmp(name, "CreateBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateBuffer;
+    if (!strcmp(name, "DestroyBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyBuffer;
+    if (!strcmp(name, "CreateBufferView"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateBufferView;
+    if (!strcmp(name, "DestroyBufferView"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyBufferView;
+    if (!strcmp(name, "CreateImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateImage;
+    if (!strcmp(name, "DestroyImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyImage;
+    if (!strcmp(name, "GetImageSubresourceLayout"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetImageSubresourceLayout;
+    if (!strcmp(name, "CreateImageView"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateImageView;
+    if (!strcmp(name, "DestroyImageView"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyImageView;
+    if (!strcmp(name, "CreateShaderModule"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateShaderModule;
+    if (!strcmp(name, "DestroyShaderModule"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyShaderModule;
+    if (!strcmp(name, "CreatePipelineCache"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreatePipelineCache;
+    if (!strcmp(name, "DestroyPipelineCache"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyPipelineCache;
+    if (!strcmp(name, "GetPipelineCacheData"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPipelineCacheData;
+    if (!strcmp(name, "MergePipelineCaches"))
+        return (PFN_vkVoidFunction) __HOOKED_vkMergePipelineCaches;
+    if (!strcmp(name, "CreateGraphicsPipelines"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateGraphicsPipelines;
+    if (!strcmp(name, "CreateComputePipelines"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateComputePipelines;
+    if (!strcmp(name, "DestroyPipeline"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyPipeline;
+    if (!strcmp(name, "CreatePipelineLayout"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreatePipelineLayout;
+    if (!strcmp(name, "DestroyPipelineLayout"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyPipelineLayout;
+    if (!strcmp(name, "CreateSampler"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateSampler;
+    if (!strcmp(name, "DestroySampler"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroySampler;
+    if (!strcmp(name, "CreateDescriptorSetLayout"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateDescriptorSetLayout;
+    if (!strcmp(name, "DestroyDescriptorSetLayout"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyDescriptorSetLayout;
+    if (!strcmp(name, "CreateDescriptorPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateDescriptorPool;
+    if (!strcmp(name, "DestroyDescriptorPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyDescriptorPool;
+    if (!strcmp(name, "ResetDescriptorPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkResetDescriptorPool;
+    if (!strcmp(name, "AllocateDescriptorSets"))
+        return (PFN_vkVoidFunction) __HOOKED_vkAllocateDescriptorSets;
+    if (!strcmp(name, "FreeDescriptorSets"))
+        return (PFN_vkVoidFunction) __HOOKED_vkFreeDescriptorSets;
+    if (!strcmp(name, "UpdateDescriptorSets"))
+        return (PFN_vkVoidFunction) __HOOKED_vkUpdateDescriptorSets;
+    if (!strcmp(name, "CreateCommandPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateCommandPool;
+    if (!strcmp(name, "DestroyCommandPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyCommandPool;
+    if (!strcmp(name, "ResetCommandPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkResetCommandPool;
+    if (!strcmp(name, "AllocateCommandBuffers"))
+        return (PFN_vkVoidFunction) __HOOKED_vkAllocateCommandBuffers;
+    if (!strcmp(name, "FreeCommandBuffers"))
+        return (PFN_vkVoidFunction) __HOOKED_vkFreeCommandBuffers;
+    if (!strcmp(name, "BeginCommandBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkBeginCommandBuffer;
+    if (!strcmp(name, "EndCommandBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkEndCommandBuffer;
+    if (!strcmp(name, "ResetCommandBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkResetCommandBuffer;
+    if (!strcmp(name, "CmdBindPipeline"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBindPipeline;
+    if (!strcmp(name, "CmdSetViewport"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetViewport;
+    if (!strcmp(name, "CmdSetScissor"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetScissor;
+    if (!strcmp(name, "CmdSetLineWidth"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetLineWidth;
+    if (!strcmp(name, "CmdSetDepthBias"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetDepthBias;
+    if (!strcmp(name, "CmdSetBlendConstants"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetBlendConstants;
+    if (!strcmp(name, "CmdSetDepthBounds"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetDepthBounds;
+    if (!strcmp(name, "CmdSetStencilCompareMask"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetStencilCompareMask;
+    if (!strcmp(name, "CmdSetStencilWriteMask"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetStencilWriteMask;
+    if (!strcmp(name, "CmdSetStencilReference"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetStencilReference;
+    if (!strcmp(name, "CmdBindDescriptorSets"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBindDescriptorSets;
+    if (!strcmp(name, "CmdBindIndexBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBindIndexBuffer;
+    if (!strcmp(name, "CmdBindVertexBuffers"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBindVertexBuffers;
+    if (!strcmp(name, "CmdDraw"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdDraw;
+    if (!strcmp(name, "CmdDrawIndexed"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdDrawIndexed;
+    if (!strcmp(name, "CmdDrawIndirect"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdDrawIndirect;
+    if (!strcmp(name, "CmdDrawIndexedIndirect"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdDrawIndexedIndirect;
+    if (!strcmp(name, "CmdDispatch"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdDispatch;
+    if (!strcmp(name, "CmdDispatchIndirect"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdDispatchIndirect;
+    if (!strcmp(name, "CmdCopyBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdCopyBuffer;
+    if (!strcmp(name, "CmdCopyImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdCopyImage;
+    if (!strcmp(name, "CmdBlitImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBlitImage;
+    if (!strcmp(name, "CmdCopyBufferToImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdCopyBufferToImage;
+    if (!strcmp(name, "CmdCopyImageToBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdCopyImageToBuffer;
+    if (!strcmp(name, "CmdUpdateBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdUpdateBuffer;
+    if (!strcmp(name, "CmdFillBuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdFillBuffer;
+    if (!strcmp(name, "CmdClearColorImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdClearColorImage;
+    if (!strcmp(name, "CmdClearDepthStencilImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdClearDepthStencilImage;
+    if (!strcmp(name, "CmdClearAttachments"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdClearAttachments;
+    if (!strcmp(name, "CmdResolveImage"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdResolveImage;
+    if (!strcmp(name, "CmdSetEvent"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdSetEvent;
+    if (!strcmp(name, "CmdResetEvent"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdResetEvent;
+    if (!strcmp(name, "CmdWaitEvents"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdWaitEvents;
+    if (!strcmp(name, "CmdPipelineBarrier"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdPipelineBarrier;
+    if (!strcmp(name, "CmdBeginQuery"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBeginQuery;
+    if (!strcmp(name, "CmdEndQuery"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdEndQuery;
+    if (!strcmp(name, "CmdResetQueryPool"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdResetQueryPool;
+    if (!strcmp(name, "CmdWriteTimestamp"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdWriteTimestamp;
+    if (!strcmp(name, "CmdCopyQueryPoolResults"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdCopyQueryPoolResults;
+    if (!strcmp(name, "CreateFramebuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateFramebuffer;
+    if (!strcmp(name, "DestroyFramebuffer"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyFramebuffer;
+    if (!strcmp(name, "CreateRenderPass"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateRenderPass;
+    if (!strcmp(name, "DestroyRenderPass"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyRenderPass;
+    if (!strcmp(name, "GetRenderAreaGranularity"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetRenderAreaGranularity;
+    if (!strcmp(name, "CmdBeginRenderPass"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdBeginRenderPass;
+    if (!strcmp(name, "CmdNextSubpass"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdNextSubpass;
+    if (!strcmp(name, "CmdPushConstants"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdPushConstants;
+    if (!strcmp(name, "CmdEndRenderPass"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdEndRenderPass;
+    if (!strcmp(name, "CmdExecuteCommands"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCmdExecuteCommands;
+
+    return NULL;
+}
+
+static inline PFN_vkVoidFunction layer_intercept_instance_proc(const char *name)
+{
+    if (!name || name[0] != 'v' || name[1] != 'k')
+        return NULL;
+
+    name += 2;
+    if (!strcmp(name, "CreateDevice"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateDevice;
+    if (!strcmp(name, "CreateInstance"))
+        return (PFN_vkVoidFunction) __HOOKED_vkCreateInstance;
+    if (!strcmp(name, "DestroyInstance"))
+        return (PFN_vkVoidFunction) __HOOKED_vkDestroyInstance;
+    if (!strcmp(name, "EnumeratePhysicalDevices"))
+        return (PFN_vkVoidFunction) __HOOKED_vkEnumeratePhysicalDevices;
+    if (!strcmp(name, "GetPhysicalDeviceFeatures"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceFeatures;
+    if (!strcmp(name, "GetPhysicalDeviceFormatProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceFormatProperties;
+    if (!strcmp(name, "GetPhysicalDeviceImageFormatProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceImageFormatProperties;
+    if (!strcmp(name, "GetPhysicalDeviceSparseImageFormatProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceSparseImageFormatProperties;
+    if (!strcmp(name, "GetPhysicalDeviceProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceProperties;
+    if (!strcmp(name, "GetPhysicalDeviceQueueFamilyProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceQueueFamilyProperties;
+    if (!strcmp(name, "GetPhysicalDeviceMemoryProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceMemoryProperties;
+    if (!strcmp(name, "EnumerateDeviceLayerProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkEnumerateDeviceLayerProperties;
+    if (!strcmp(name, "EnumerateDeviceExtensionProperties"))
+        return (PFN_vkVoidFunction) __HOOKED_vkEnumerateDeviceExtensionProperties;
+
+    return NULL;
+}
+
+/**
+ * Want trace packets created for GetDeviceProcAddr that is app initiated
+ * but not for loader initiated calls to GDPA. Thus need two versions of GDPA.
+ */
+VKTRACER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vktraceGetDeviceProcAddr(VkDevice device, const char* funcName)
+{
+    vktrace_trace_packet_header *pHeader;
+    PFN_vkVoidFunction addr;
+    packet_vkGetDeviceProcAddr* pPacket = NULL;
+    CREATE_TRACE_PACKET(vkGetDeviceProcAddr, ((funcName != NULL) ? ROUNDUP_TO_4(strlen(funcName) + 1) : 0));
+    addr = __HOOKED_vkGetDeviceProcAddr(device, funcName);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkGetDeviceProcAddr(pHeader);
+    pPacket->device = device;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pName), ((funcName != NULL) ? ROUNDUP_TO_4(strlen(funcName) + 1) : 0), funcName);
+    pPacket->result = addr;
+    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pName));
+    FINISH_TRACE_PACKET();
+    return addr;
+}
+
+/* GDPA with no trace packet creation */
+VKTRACER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL __HOOKED_vkGetDeviceProcAddr(VkDevice device, const char* funcName)
+{
+    if (!strcmp("vkGetDeviceProcAddr", funcName)) {
+        if (gMessageStream != NULL) {
+            return (PFN_vkVoidFunction) vktraceGetDeviceProcAddr;
+        } else {
+            return (PFN_vkVoidFunction) __HOOKED_vkGetDeviceProcAddr;
+        }
+    }
+
+    layer_device_data  *devData = mdd(device);
+    if (gMessageStream != NULL) {
+
+        PFN_vkVoidFunction addr;
+        addr = layer_intercept_proc(funcName);
+        if (addr)
+            return addr;
+
+        if (devData->KHRDeviceSwapchainEnabled)
+        {
+            if (!strcmp("vkCreateSwapchainKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateSwapchainKHR;
+            if (!strcmp("vkDestroySwapchainKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkDestroySwapchainKHR;
+            if (!strcmp("vkGetSwapchainImagesKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetSwapchainImagesKHR;
+            if (!strcmp("vkAcquireNextImageKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkAcquireNextImageKHR;
+            if (!strcmp("vkQueuePresentKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkQueuePresentKHR;
+        }
+    }
+
+    if (device == VK_NULL_HANDLE) {
+        return NULL;
+    }
+
+    VkLayerDispatchTable *pDisp =  &devData->devTable;
+    if (pDisp->GetDeviceProcAddr == NULL)
+        return NULL;
+    return pDisp->GetDeviceProcAddr(device, funcName);
+}
+
+/**
+ * Want trace packets created for GetInstanceProcAddr that is app initiated
+ * but not for loader initiated calls to GIPA. Thus need two versions of GIPA.
+ */
+VKTRACER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vktraceGetInstanceProcAddr(VkInstance instance, const char* funcName)
+{
+    vktrace_trace_packet_header* pHeader;
+    PFN_vkVoidFunction addr;
+    packet_vkGetInstanceProcAddr* pPacket = NULL;
+    //assert(strcmp("vkGetInstanceProcAddr", funcName));
+    CREATE_TRACE_PACKET(vkGetInstanceProcAddr, ((funcName != NULL) ? ROUNDUP_TO_4(strlen(funcName) + 1) : 0));
+    addr = __HOOKED_vkGetInstanceProcAddr(instance, funcName);
+    vktrace_set_packet_entrypoint_end_time(pHeader);
+    pPacket = interpret_body_as_vkGetInstanceProcAddr(pHeader);
+    pPacket->instance = instance;
+    vktrace_add_buffer_to_trace_packet(pHeader, (void**) &(pPacket->pName), ((funcName != NULL) ? ROUNDUP_TO_4(strlen(funcName) + 1) : 0), funcName);
+    pPacket->result = addr;
+    vktrace_finalize_buffer_address(pHeader, (void**) &(pPacket->pName));
+    FINISH_TRACE_PACKET();
+    return addr;
+}
+
+/* GIPA with no trace packet creation */
+VKTRACER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL __HOOKED_vkGetInstanceProcAddr(VkInstance instance, const char* funcName)
+{
+    PFN_vkVoidFunction addr;
+    layer_instance_data  *instData;
+
+    vktrace_platform_thread_once((void*) &gInitOnce, InitTracer);
+    if (!strcmp("vkGetInstanceProcAddr", funcName)) {
+        if (gMessageStream != NULL) {
+            return (PFN_vkVoidFunction) vktraceGetInstanceProcAddr;
+        } else {
+            return (PFN_vkVoidFunction) __HOOKED_vkGetInstanceProcAddr;
+        }
+    }
+
+    if (gMessageStream != NULL) {
+        addr = layer_intercept_instance_proc(funcName);
+        if (addr)
+            return addr;
+
+        if (instance == VK_NULL_HANDLE) {
+            return NULL;
+        }
+
+        instData = mid(instance);
+        if (instData->LunargDebugReportEnabled)
+        {
+            if (!strcmp("vkCreateDebugReportCallbackEXT", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateDebugReportCallbackEXT;
+            if (!strcmp("vkDestroyDebugReportCallbackEXT", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkDestroyDebugReportCallbackEXT;
+
+        }
+        if (instData->KHRSurfaceEnabled)
+        {
+            if (!strcmp("vkGetPhysicalDeviceSurfaceSupportKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceSurfaceSupportKHR;
+            if (!strcmp("vkDestroySurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkDestroySurfaceKHR;
+            if (!strcmp("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
+            if (!strcmp("vkGetPhysicalDeviceSurfaceFormatsKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceSurfaceFormatsKHR;
+            if (!strcmp("vkGetPhysicalDeviceSurfacePresentModesKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceSurfacePresentModesKHR;
+        }
+#ifdef VK_USE_PLATFORM_XLIB_KHR
+        if (instData->KHRXlibSurfaceEnabled)
+        {
+            if (!strcmp("vkCreateXlibSurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateXlibSurfaceKHR;
+            if (!strcmp("vkGetPhysicalDeviceXlibPresentationSupportKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceXlibPresentationSupportKHR;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_XCB_KHR
+        if (instData->KHRXcbSurfaceEnabled)
+        {
+            if (!strcmp("vkCreateXcbSurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateXcbSurfaceKHR;
+            if (!strcmp("vkGetPhysicalDeviceXcbPresentationSupportKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceXcbPresentationSupportKHR;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_WAYLAND_KHR
+        if (instData->KHRWaylandSurfaceEnabled)
+        {
+            if (!strcmp("vkCreateWaylandSurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateWaylandSurfaceKHR;
+            if (!strcmp("vkGetPhysicalDeviceWaylandPresentationSupportKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceWaylandPresentationSupportKHR;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_MIR_KHR
+        if (instData->KHRMirSurfaceEnabled)
+        {
+            if (!strcmp("vkCreateMirSurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateMirSurfaceKHR;
+            if (!strcmp("vkGetPhysicalDeviceMirPresentationSupportKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceMirPresentationSupportKHR;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+        if (instData->KHRWin32SurfaceEnabled)
+        {
+            if (!strcmp("vkCreateWin32SurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateWin32SurfaceKHR;
+            if (!strcmp("vkGetPhysicalDeviceWin32PresentationSupportKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkGetPhysicalDeviceWin32PresentationSupportKHR;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
+        if (instData->KHRAndroidSurfaceEnabled)
+        {
+            if (!strcmp("vkCreateAndroidSurfaceKHR", funcName))
+                return (PFN_vkVoidFunction) __HOOKED_vkCreateAndroidSurfaceKHR;
+        }
+#endif
+    } else {
+        if (instance == VK_NULL_HANDLE) {
+            return NULL;
+        }
+        instData = mid(instance);
+    }
+    VkLayerInstanceDispatchTable* pTable = &instData->instTable;
+    if (pTable->GetInstanceProcAddr == NULL)
+        return NULL;
+
+    return pTable->GetInstanceProcAddr(instance, funcName);
+}
+
+static const VkLayerProperties layerProps = {
+    "VK_LAYER_LUNARG_vktrace",
+    VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION),
+    1, "LunarG tracing layer",
+};
+
+template<typename T>
+VkResult EnumerateProperties(uint32_t src_count, const T *src_props, uint32_t *dst_count, T *dst_props) {
+    if (!dst_props || !src_props) {
+        *dst_count = src_count;
+        return VK_SUCCESS;
+    }
+
+    uint32_t copy_count = (*dst_count < src_count) ? *dst_count : src_count;
+    memcpy(dst_props, src_props, sizeof(T) * copy_count);
+    *dst_count = copy_count;
+
+    return (copy_count == src_count) ? VK_SUCCESS : VK_INCOMPLETE;
+}
+
+// LoaderLayerInterface V0
+// https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/blob/master/loader/LoaderAndLayerInterface.md
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t *pPropertyCount, VkLayerProperties *pProperties) {
+    return EnumerateProperties(1, &layerProps, pPropertyCount, pProperties);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) {
+    if (pLayerName && !strcmp(pLayerName, layerProps.layerName))
+        return EnumerateProperties(0, (VkExtensionProperties*)nullptr, pPropertyCount, pProperties);
+
+    return VK_ERROR_LAYER_NOT_PRESENT;
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkLayerProperties *pProperties) {
+    return EnumerateProperties(1, &layerProps, pPropertyCount, pProperties);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) {
+    if (pLayerName && !strcmp(pLayerName, layerProps.layerName))
+        return EnumerateProperties(0, (VkExtensionProperties*)nullptr, pPropertyCount, pProperties);
+
+    return VK_ERROR_LAYER_NOT_PRESENT;
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL VK_LAYER_LUNARG_vktraceGetInstanceProcAddr(VkInstance instance, const char* funcName) {
+    return __HOOKED_vkGetInstanceProcAddr(instance, funcName);
+}
+
+VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL VK_LAYER_LUNARG_vktraceGetDeviceProcAddr(VkDevice device, const char* funcName) {
+    return __HOOKED_vkGetDeviceProcAddr(device, funcName);
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_vk_exts.cpp b/vktrace/src/vktrace_layer/vktrace_vk_exts.cpp
new file mode 100644
index 0000000..d63fa89
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_vk_exts.cpp
@@ -0,0 +1,131 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ */
+#include "vktrace_lib_helpers.h"
+
+#include "vulkan/vk_layer.h"
+
+void ext_init_create_instance(
+        layer_instance_data             *instData,
+        VkInstance                      inst,
+        uint32_t                        extension_count,
+        const char*const*               ppEnabledExtensions)    // extension names to be enabled
+{
+    PFN_vkGetInstanceProcAddr gpa = instData->instTable.GetInstanceProcAddr;
+
+    instData->instTable.CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) gpa(inst, "vkCreateDebugReportCallbackEXT");
+    instData->instTable.DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) gpa(inst, "vkDestroyDebugReportCallbackEXT");
+    instData->instTable.DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT) gpa(inst, "vkDebugReportMessageEXT");
+    instData->instTable.GetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR) gpa(inst, "vkGetPhysicalDeviceSurfaceSupportKHR");
+    instData->instTable.DestroySurfaceKHR = (PFN_vkDestroySurfaceKHR) gpa(inst, "vkDestroySurfaceKHR");
+    instData->instTable.GetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR) gpa(inst, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
+    instData->instTable.GetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR) gpa(inst, "vkGetPhysicalDeviceSurfaceFormatsKHR");
+    instData->instTable.GetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR) gpa(inst, "vkGetPhysicalDeviceSurfacePresentModesKHR");
+#ifdef VK_USE_PLATFORM_XLIB_KHR
+    instData->instTable.CreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) gpa(inst, "vkCreateXlibSurfaceKHR");
+    instData->instTable.GetPhysicalDeviceXlibPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) gpa(inst, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
+#endif
+#ifdef VK_USE_PLATFORM_XCB_KHR
+    instData->instTable.CreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) gpa(inst, "vkCreateXcbSurfaceKHR");
+    instData->instTable.GetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) gpa(inst, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
+#endif
+#ifdef VK_USE_PLATFORM_MIR_KHR
+    instData->instTable.CreateMirSurfaceKHR = (PFN_vkCreateMirSurfaceKHR) gpa(inst, "vkCreateMirSurfaceKHR");
+    instData->instTable.GetPhysicalDeviceMirPresentationSupportKHR = (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) gpa(inst, "vkGetPhysicalDeviceMirPresentationSupportKHR");
+#endif
+#ifdef VK_USE_PLATFORM_WAYLAND_KHR
+    instData->instTable.CreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) gpa(inst, "vkCreateWaylandSurfaceKHR");
+    instData->instTable.GetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) gpa(inst, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
+#endif
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+    instData->instTable.CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) gpa(inst, "vkCreateWin32SurfaceKHR");
+    instData->instTable.GetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) gpa(inst, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
+#endif
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
+    instData->instTable.CreateAndroidSurfaceKHR = (PFN_vkCreateAndroidSurfaceKHR) gpa(inst, "vkCreateAndroidSurfaceKHR");
+#endif
+    instData->LunargDebugReportEnabled = false;
+    instData->KHRSurfaceEnabled = false;
+    instData->KHRXlibSurfaceEnabled = false;
+    instData->KHRXcbSurfaceEnabled = false;
+    instData->KHRWaylandSurfaceEnabled = false;
+    instData->KHRMirSurfaceEnabled = false;
+    instData->KHRWin32SurfaceEnabled = false;
+    instData->KHRAndroidSurfaceEnabled = false;
+    for (uint32_t i = 0; i < extension_count; i++) {
+        if (strcmp(ppEnabledExtensions[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
+            instData->LunargDebugReportEnabled = true;
+        }
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRSurfaceEnabled = true;
+        }
+#ifdef VK_USE_PLATFORM_XLIB_KHR
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_XLIB_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRXlibSurfaceEnabled = true;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_XCB_KHR
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_XCB_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRXcbSurfaceEnabled = true;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_MIR_KHR
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_MIR_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRMirSurfaceEnabled = true;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_WAYLAND_KHR
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRWaylandSurfaceEnabled = true;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_WIN32_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRWin32SurfaceEnabled = true;
+        }
+#endif
+#ifdef VK_USE_PLATFORM_ANDROID_KHR
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_ANDROID_SURFACE_EXTENSION_NAME) == 0) {
+            instData->KHRAndroidSurfaceEnabled = true;
+        }
+#endif
+    }
+}
+
+void ext_init_create_device(
+        layer_device_data               *devData,
+        VkDevice                        dev,
+        PFN_vkGetDeviceProcAddr         gpa,
+        uint32_t                        extension_count,
+        const char*const*               ppEnabledExtensions)  // extension names to be enabled
+{
+    devData->devTable.CreateSwapchainKHR = (PFN_vkCreateSwapchainKHR) gpa(dev, "vkCreateSwapchainKHR");
+    devData->devTable.DestroySwapchainKHR = (PFN_vkDestroySwapchainKHR) gpa(dev, "vkDestroySwapchainKHR");
+    devData->devTable.GetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR) gpa(dev, "vkGetSwapchainImagesKHR");
+    devData->devTable.AcquireNextImageKHR = (PFN_vkAcquireNextImageKHR) gpa(dev, "vkAcquireNextImageKHR");
+    devData->devTable.QueuePresentKHR = (PFN_vkQueuePresentKHR) gpa(dev, "vkQueuePresentKHR");
+    
+    devData->KHRDeviceSwapchainEnabled = false;
+    for (uint32_t i = 0; i < extension_count; i++) {
+        if (strcmp(ppEnabledExtensions[i], VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
+            devData->KHRDeviceSwapchainEnabled = true;
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_layer/vktrace_vk_exts.h b/vktrace/src/vktrace_layer/vktrace_vk_exts.h
new file mode 100644
index 0000000..b4a7500
--- /dev/null
+++ b/vktrace/src/vktrace_layer/vktrace_vk_exts.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ */
+#pragma once
+
+#include "vulkan/vk_layer.h"
+#include "vktrace_lib_helpers.h"
+
+void ext_init_create_instance(
+        layer_instance_data             *instData,
+        VkInstance                      inst,
+        uint32_t                        extension_count,
+        const char*const*               ppEnabledExtensions);
+
+void ext_init_create_device(
+        layer_device_data               *devData,
+        VkDevice                        dev,
+        PFN_vkGetDeviceProcAddr         gpa,
+        uint32_t                        extension_count,
+        const char*const*               ppEnabledExtensions);
diff --git a/vktrace/src/vktrace_layer/windows/VkLayer_vktrace_layer.json b/vktrace/src/vktrace_layer/windows/VkLayer_vktrace_layer.json
new file mode 100644
index 0000000..46f4381
--- /dev/null
+++ b/vktrace/src/vktrace_layer/windows/VkLayer_vktrace_layer.json
@@ -0,0 +1,15 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_LUNARG_vktrace",
+        "type": "GLOBAL",
+        "library_path": ".\\VkLayer_vktrace_layer.dll",
+        "api_version": "1.0.38",
+        "implementation_version": "1",
+        "description": "Vktrace tracing library",
+        "functions" : {
+          "vkGetInstanceProcAddr" : "__HOOKED_vkGetInstanceProcAddr",
+          "vkGetDeviceProcAddr" : "__HOOKED_vkGetDeviceProcAddr"
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_replay/CMakeLists.txt b/vktrace/src/vktrace_replay/CMakeLists.txt
new file mode 100644
index 0000000..454fa31
--- /dev/null
+++ b/vktrace/src/vktrace_replay/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 2.8)
+project(vkreplay)
+
+include("${SRC_DIR}/build_options.cmake")
+
+set(SRC_LIST
+    ${SRC_LIST}
+    vkreplay_factory.h
+    vkreplay_seq.h
+    vkreplay_window.h
+    vkreplay_main.cpp
+    vkreplay_seq.cpp
+    vkreplay_factory.cpp
+)
+
+include_directories(
+    ${SRC_DIR}/vktrace_replay
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/thirdparty
+    ${CMAKE_CURRENT_SOURCE_DIR}/../vktrace_extensions/vktracevulkan/vkreplay/
+)
+
+set (LIBRARIES vktrace_common vulkan_replay)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+if(WIN32)
+    add_dependencies(${PROJECT_NAME} "${API_LOWERCASE}-${MAJOR}")
+else()
+    add_dependencies(${PROJECT_NAME} "${API_LOWERCASE}")
+endif()
+
+target_link_libraries(${PROJECT_NAME}
+    ${LIBRARIES}
+)
+
+build_options_finalize()
diff --git a/vktrace/src/vktrace_replay/vkreplay_factory.cpp b/vktrace/src/vktrace_replay/vkreplay_factory.cpp
new file mode 100644
index 0000000..edf4586
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_factory.cpp
@@ -0,0 +1,147 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+
+#include "vkreplay_factory.h"
+#include "vktrace_trace_packet_identifiers.h"
+#include "vkreplay.h"
+
+namespace vktrace_replay {
+
+vktrace_trace_packet_replay_library* ReplayFactory::Create(uint8_t tracerId)
+{
+    vktrace_trace_packet_replay_library* pReplayer = NULL;
+    //void* pLibrary = NULL;
+
+    const VKTRACE_TRACER_REPLAYER_INFO* pReplayerInfo = &(gs_tracerReplayerInfo[tracerId]);
+
+
+    if (pReplayerInfo->tracerId != tracerId)
+    {
+        vktrace_LogError("Replayer info for TracerId (%d) failed consistency check.", tracerId);
+        assert(!"TracerId in VKTRACE_TRACER_REPLAYER_INFO does not match the requested tracerId. The array needs to be corrected.");
+    }
+
+    // Vulkan library is built into replayer executable
+    if (tracerId == VKTRACE_TID_VULKAN) {
+        pReplayer = VKTRACE_NEW(vktrace_trace_packet_replay_library);
+        if (pReplayer == NULL)
+        {
+            vktrace_LogError("Failed to allocate replayer library.");
+        }
+        else
+        {
+            pReplayer->pLibrary = NULL;
+
+            pReplayer->SetLogCallback = VkReplaySetLogCallback;
+            pReplayer->SetLogLevel = VkReplaySetLogLevel;
+
+            pReplayer->RegisterDbgMsgCallback = VkReplayRegisterDbgMsgCallback;
+            pReplayer->GetSettings = VkReplayGetSettings;
+            pReplayer->UpdateFromSettings = VkReplayUpdateFromSettings;
+            pReplayer->Initialize = VkReplayInitialize;
+            pReplayer->Deinitialize = VkReplayDeinitialize;
+            pReplayer->Interpret = VkReplayInterpret;
+            pReplayer->Replay = VkReplayReplay;
+            pReplayer->Dump = VkReplayDump;
+            pReplayer->GetFrameNumber = VkReplayGetFrameNumber;
+            pReplayer->ResetFrameNumber = VkReplayResetFrameNumber;
+        }
+
+    }
+    
+//    if (pReplayerInfo->needsReplayer == TRUE)
+//    {
+//        pLibrary = vktrace_platform_open_library(pReplayerInfo->replayerLibraryName);
+//        if (pLibrary == NULL)
+//        {
+//            vktrace_LogError("Failed to load replayer '%s.", pReplayerInfo->replayerLibraryName);
+//#if defined(PLATFORM_LINUX)
+//            char* error = dlerror();
+//            vktrace_LogError(error);
+//#endif
+//        }
+//    }
+//    else
+//    {
+//        vktrace_LogError("A replayer was requested for TracerId (%d), but it does not require a replayer.", tracerId);
+//        assert(!"Invalid TracerId supplied to ReplayFactory");
+//    }
+//
+//    if (pLibrary != NULL)
+//    {
+//        pReplayer = VKTRACE_NEW(vktrace_trace_packet_replay_library);
+//        if (pReplayer == NULL)
+//        {
+//            vktrace_LogError("Failed to allocate replayer library.");
+//            vktrace_platform_close_library(pLibrary);
+//        }
+//        else
+//        {
+//            pReplayer->pLibrary = pLibrary;
+//
+//            pReplayer->SetLogCallback = (funcptr_vkreplayer_setlogcallback)vktrace_platform_get_library_entrypoint(pLibrary, "SetLogCallback");
+//            pReplayer->SetLogLevel = (funcptr_vkreplayer_setloglevel)vktrace_platform_get_library_entrypoint(pLibrary, "SetLogLevel");
+//
+//            pReplayer->RegisterDbgMsgCallback = (funcptr_vkreplayer_registerdbgmsgcallback)vktrace_platform_get_library_entrypoint(pLibrary, "RegisterDbgMsgCallback");
+//            pReplayer->GetSettings = (funcptr_vkreplayer_getSettings)vktrace_platform_get_library_entrypoint(pLibrary, "GetSettings");
+//            pReplayer->UpdateFromSettings = (funcptr_vkreplayer_updatefromsettings)vktrace_platform_get_library_entrypoint(pLibrary, "UpdateFromSettings");
+//            pReplayer->Initialize = (funcptr_vkreplayer_initialize)vktrace_platform_get_library_entrypoint(pLibrary, "Initialize");
+//            pReplayer->Deinitialize = (funcptr_vkreplayer_deinitialize)vktrace_platform_get_library_entrypoint(pLibrary, "Deinitialize");
+//            pReplayer->Interpret = (funcptr_vkreplayer_interpret)vktrace_platform_get_library_entrypoint(pLibrary, "Interpret");
+//            pReplayer->Replay = (funcptr_vkreplayer_replay)vktrace_platform_get_library_entrypoint(pLibrary, "Replay");
+//            pReplayer->Dump = (funcptr_vkreplayer_dump)vktrace_platform_get_library_entrypoint(pLibrary, "Dump");
+//
+//            if (pReplayer->SetLogCallback == NULL ||
+//                pReplayer->SetLogLevel == NULL ||
+//                pReplayer->RegisterDbgMsgCallback == NULL ||
+//                pReplayer->GetSettings == NULL ||
+//                pReplayer->UpdateFromSettings == NULL ||
+//                pReplayer->Initialize == NULL ||
+//                pReplayer->Deinitialize == NULL ||
+//                pReplayer->Interpret == NULL ||
+//                pReplayer->Replay == NULL ||
+//                pReplayer->Dump == NULL)
+//            {
+//                VKTRACE_DELETE(pReplayer);
+//                vktrace_platform_close_library(pLibrary);
+//                pReplayer = NULL;
+//            }
+//        }
+//    }
+
+    return pReplayer;
+}
+
+void ReplayFactory::Destroy(vktrace_trace_packet_replay_library** ppReplayer)
+{
+    assert (ppReplayer != NULL);
+    assert (*ppReplayer != NULL);
+    if ((*ppReplayer)->pLibrary != NULL)
+    {
+        vktrace_platform_close_library((*ppReplayer)->pLibrary);
+    }
+    VKTRACE_DELETE(*ppReplayer);
+    *ppReplayer = NULL;
+}
+
+
+} // namespace vktrace_replay
diff --git a/vktrace/src/vktrace_replay/vkreplay_factory.h b/vktrace/src/vktrace_replay/vkreplay_factory.h
new file mode 100644
index 0000000..bd4d9f0
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_factory.h
@@ -0,0 +1,98 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#pragma once
+
+extern "C" {
+#include "vktrace_common.h"
+#include "vktrace_settings.h"
+#include "vktrace_trace_packet_identifiers.h"
+}
+#include "vkreplay_window.h"
+#include "vkreplay_main.h"
+
+namespace vktrace_replay {
+
+enum VKTRACE_REPLAY_RESULT
+{
+    VKTRACE_REPLAY_SUCCESS = 0,
+    VKTRACE_REPLAY_ERROR,          // internal error unrelated to the specific packet
+    VKTRACE_REPLAY_INVALID_ID,     // packet_id invalid
+    VKTRACE_REPLAY_BAD_RETURN,     // replay return value != trace return value
+    VKTRACE_REPLAY_CALL_ERROR,     // replaying call caused an error
+    VKTRACE_REPLAY_INVALID_PARAMS, // trace file parameters are invalid
+    VKTRACE_REPLAY_VALIDATION_ERROR // callback Msg error from validation layer
+};
+
+enum VKTRACE_DBG_MSG_TYPE
+{
+    VKTRACE_DBG_MSG_INFO = 0,
+    VKTRACE_DBG_MSG_WARNING,
+    VKTRACE_DBG_MSG_ERROR
+};
+
+// callback signature
+typedef void (*VKTRACE_DBG_MSG_CALLBACK_FUNCTION)(VKTRACE_DBG_MSG_TYPE msgType, const char* pMsg);
+
+// entrypoints that must be exposed by each replayer library
+extern "C"
+{
+// entrypoints
+
+typedef void (VKTRACER_CDECL *funcptr_vkreplayer_setloglevel)(VktraceLogLevel level);
+typedef void (VKTRACER_CDECL *funcptr_vkreplayer_setlogcallback)(VKTRACE_REPORT_CALLBACK_FUNCTION pCallback);
+
+typedef void (VKTRACER_CDECL *funcptr_vkreplayer_registerdbgmsgcallback)(VKTRACE_DBG_MSG_CALLBACK_FUNCTION pCallback);
+typedef vktrace_SettingGroup* (VKTRACER_CDECL *funcptr_vkreplayer_getSettings)();
+typedef void (VKTRACER_CDECL *funcptr_vkreplayer_updatefromsettings)(vktrace_SettingGroup* pSettingGroups, unsigned int numSettingGroups);
+typedef int (VKTRACER_CDECL *funcptr_vkreplayer_initialize)(vktrace_replay::ReplayDisplay* pDisplay, vkreplayer_settings* pReplaySettings);
+typedef void (VKTRACER_CDECL *funcptr_vkreplayer_deinitialize)();
+typedef vktrace_trace_packet_header* (VKTRACER_CDECL *funcptr_vkreplayer_interpret)(vktrace_trace_packet_header* pPacket);
+typedef vktrace_replay::VKTRACE_REPLAY_RESULT (VKTRACER_CDECL *funcptr_vkreplayer_replay)(vktrace_trace_packet_header* pPacket);
+typedef int (VKTRACER_CDECL *funcptr_vkreplayer_dump)();
+typedef int (VKTRACER_CDECL *funcptr_vkreplayer_getframenumber)();
+typedef void (VKTRACER_CDECL *funcptr_vkreplayer_resetframenumber)();
+}
+
+struct vktrace_trace_packet_replay_library
+{
+    void* pLibrary;
+    funcptr_vkreplayer_setloglevel SetLogLevel;
+    funcptr_vkreplayer_setlogcallback SetLogCallback;
+
+    funcptr_vkreplayer_registerdbgmsgcallback RegisterDbgMsgCallback;
+    funcptr_vkreplayer_getSettings GetSettings;
+    funcptr_vkreplayer_updatefromsettings UpdateFromSettings;
+    funcptr_vkreplayer_initialize Initialize;
+    funcptr_vkreplayer_deinitialize Deinitialize;
+    funcptr_vkreplayer_interpret Interpret;
+    funcptr_vkreplayer_replay Replay;
+    funcptr_vkreplayer_dump Dump;
+    funcptr_vkreplayer_getframenumber GetFrameNumber;
+    funcptr_vkreplayer_resetframenumber ResetFrameNumber;
+};
+
+class ReplayFactory {
+public:
+    vktrace_trace_packet_replay_library *Create(uint8_t tracerId);
+    void Destroy(vktrace_trace_packet_replay_library** ppReplayer);
+};
+} // namespace vktrace_replay
diff --git a/vktrace/src/vktrace_replay/vkreplay_main.cpp b/vktrace/src/vktrace_replay/vkreplay_main.cpp
new file mode 100644
index 0000000..b732a75
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_main.cpp
@@ -0,0 +1,587 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ * Author: David Pinedo <david@lunarg.com>
+ **************************************************************************/
+
+#include <stdio.h>
+#include <string>
+#if defined(ANDROID)
+#include <vector>
+#include <sstream>
+#include <android/log.h>
+#include <android_native_app_glue.h>
+#endif
+#include "vktrace_common.h"
+#include "vktrace_tracelog.h"
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_utils.h"
+#include "vkreplay_main.h"
+#include "vkreplay_factory.h"
+#include "vkreplay_seq.h"
+#include "vkreplay_window.h"
+
+vkreplayer_settings replaySettings = { NULL, 1, -1, -1, NULL, NULL };
+
+vktrace_SettingInfo g_settings_info[] =
+{
+    { "o", "Open", VKTRACE_SETTING_STRING, { &replaySettings.pTraceFilePath }, { &replaySettings.pTraceFilePath }, TRUE, "The trace file to open and replay." },
+    { "t", "TraceFile", VKTRACE_SETTING_STRING, { &replaySettings.pTraceFilePath }, { &replaySettings.pTraceFilePath }, TRUE, "The trace file to open and replay. (Deprecated)"},
+    { "l", "NumLoops", VKTRACE_SETTING_UINT, { &replaySettings.numLoops }, { &replaySettings.numLoops }, TRUE, "The number of times to replay the trace file or loop range." },
+    { "lsf", "LoopStartFrame", VKTRACE_SETTING_INT, { &replaySettings.loopStartFrame }, { &replaySettings.loopStartFrame }, TRUE, "The start frame number of the loop range." },
+    { "lef", "LoopEndFrame", VKTRACE_SETTING_INT, { &replaySettings.loopEndFrame }, { &replaySettings.loopEndFrame }, TRUE, "The end frame number of the loop range." },
+    { "s", "Screenshot", VKTRACE_SETTING_STRING, { &replaySettings.screenshotList }, { &replaySettings.screenshotList }, TRUE, "Comma separated list of frames to take a snapshot of."},
+#if _DEBUG
+    { "v", "Verbosity", VKTRACE_SETTING_STRING, { &replaySettings.verbosity }, { &replaySettings.verbosity }, TRUE, "Verbosity mode. Modes are \"quiet\", \"errors\", \"warnings\", \"full\", \"debug\"."},
+#else
+    { "v", "Verbosity", VKTRACE_SETTING_STRING, { &replaySettings.verbosity }, { &replaySettings.verbosity }, TRUE, "Verbosity mode. Modes are \"quiet\", \"errors\", \"warnings\", \"full\"."},
+#endif
+};
+
+vktrace_SettingGroup g_replaySettingGroup =
+{
+    "vkreplay",
+    sizeof(g_settings_info) / sizeof(g_settings_info[0]),
+    &g_settings_info[0]
+};
+
+namespace vktrace_replay {
+int main_loop(vktrace_replay::ReplayDisplay display, Sequencer &seq,
+              vktrace_trace_packet_replay_library *replayerArray[],
+              vkreplayer_settings settings) {
+    int err = 0;
+    vktrace_trace_packet_header *packet;
+    unsigned int res;
+    vktrace_trace_packet_replay_library *replayer = NULL;
+    vktrace_trace_packet_message* msgPacket;
+    struct seqBookmark startingPacket;
+
+    bool trace_running = true;
+    int prevFrameNumber = -1;
+
+    // record the location of looping start packet
+    seq.record_bookmark();
+    seq.get_bookmark(startingPacket);
+    while (settings.numLoops > 0)
+    {
+        while (trace_running) {
+            display.process_event();
+            if (display.get_quit_status()) {
+                goto out;
+            }
+            if (display.get_pause_status()) {
+                continue;
+            } else {
+                packet = seq.get_next_packet();
+                if (!packet)
+                    break;
+            }
+
+            switch (packet->packet_id) {
+                case VKTRACE_TPI_MESSAGE:
+                    msgPacket = vktrace_interpret_body_as_trace_packet_message(packet);
+                    vktrace_LogAlways("Packet %lu: Traced Message (%s): %s", packet->global_packet_index, vktrace_LogLevelToShortString(msgPacket->type), msgPacket->message);
+                    break;
+                case VKTRACE_TPI_MARKER_CHECKPOINT:
+                    break;
+                case VKTRACE_TPI_MARKER_API_BOUNDARY:
+                    break;
+                case VKTRACE_TPI_MARKER_API_GROUP_BEGIN:
+                    break;
+                case VKTRACE_TPI_MARKER_API_GROUP_END:
+                    break;
+                case VKTRACE_TPI_MARKER_TERMINATE_PROCESS:
+                    break;
+                //TODO processing code for all the above cases
+                default:
+                {
+                    if (packet->tracer_id >= VKTRACE_MAX_TRACER_ID_ARRAY_SIZE  || packet->tracer_id == VKTRACE_TID_RESERVED) {
+                        vktrace_LogError("Tracer_id from packet num packet %d invalid.", packet->packet_id);
+                        continue;
+                    }
+                    replayer = replayerArray[packet->tracer_id];
+                    if (replayer == NULL) {
+                        vktrace_LogWarning("Tracer_id %d has no valid replayer.", packet->tracer_id);
+                        continue;
+                    }
+                    if (packet->packet_id >= VKTRACE_TPI_BEGIN_API_HERE) {
+                        // replay the API packet
+                        res = replayer->Replay(replayer->Interpret(packet));
+                        if (res != VKTRACE_REPLAY_SUCCESS) {
+                           vktrace_LogError("Failed to replay packet_id %d, with global_packet_index %d.", packet->packet_id, packet->global_packet_index);
+                           static BOOL QuitOnAnyError=FALSE;
+                           if(QuitOnAnyError) {
+                              err = -1;
+                              goto out;
+                           }
+                        }
+
+                        // frame control logic
+                        int frameNumber = replayer->GetFrameNumber();
+                        if (prevFrameNumber != frameNumber) {
+                            prevFrameNumber = frameNumber;
+
+                            if (frameNumber == settings.loopStartFrame) {
+                                // record the location of looping start packet
+                                seq.record_bookmark();
+                                seq.get_bookmark(startingPacket);
+                            }
+
+                            if (frameNumber == settings.loopEndFrame) {
+                                trace_running = false;
+                            }
+                        }
+
+                    } else {
+                        vktrace_LogError("Bad packet type id=%d, index=%d.", packet->packet_id, packet->global_packet_index);
+                        err = -1;
+                        goto out;
+                    }
+                }
+            }
+        }
+        settings.numLoops--;
+        //if screenshot is enabled run it for one cycle only
+        //as all consecutive cycles must generate same screen
+        if (replaySettings.screenshotList != NULL) {
+            vktrace_free((char*)replaySettings.screenshotList);
+            replaySettings.screenshotList = NULL;
+        }
+        seq.set_bookmark(startingPacket);
+        trace_running = true;
+        if (replayer != NULL) {
+            replayer->ResetFrameNumber();
+        }
+    }
+
+out:
+    seq.clean_up();
+    if (replaySettings.screenshotList != NULL) {
+        vktrace_free((char*)replaySettings.screenshotList);
+        replaySettings.screenshotList = NULL;
+    }
+    return err;
+}
+} // namespace vktrace_replay
+
+using namespace vktrace_replay;
+
+void loggingCallback(VktraceLogLevel level, const char* pMessage)
+{
+    if (level == VKTRACE_LOG_NONE)
+        return;
+
+#if defined(ANDROID)
+    switch(level)
+    {
+    case VKTRACE_LOG_DEBUG: __android_log_print(ANDROID_LOG_DEBUG, "vkreplay", "%s", pMessage); break;
+    case VKTRACE_LOG_ERROR: __android_log_print(ANDROID_LOG_ERROR, "vkreplay", "%s", pMessage); break;
+    case VKTRACE_LOG_WARNING: __android_log_print(ANDROID_LOG_WARN, "vkreplay", "%s", pMessage); break;
+    case VKTRACE_LOG_VERBOSE: __android_log_print(ANDROID_LOG_VERBOSE, "vkreplay", "%s", pMessage); break;
+    default:
+        __android_log_print(ANDROID_LOG_INFO, "vkreplay", "%s", pMessage); break;
+    }
+#else
+    switch(level)
+    {
+    case VKTRACE_LOG_DEBUG: printf("vkreplay debug: %s\n", pMessage); break;
+    case VKTRACE_LOG_ERROR: printf("vkreplay error: %s\n", pMessage); break;
+    case VKTRACE_LOG_WARNING: printf("vkreplay warning: %s\n", pMessage); break;
+    case VKTRACE_LOG_VERBOSE: printf("vkreplay info: %s\n", pMessage); break;
+    default:
+        printf("%s\n", pMessage); break;
+    }
+    fflush(stdout);
+
+#if defined(_DEBUG)
+#if defined(WIN32)
+    OutputDebugString(pMessage);
+#endif
+#endif
+#endif // ANDROID
+}
+
+int vkreplay_main(int argc, char **argv, vktrace_window_handle window = 0)
+{
+    int err = 0;
+    vktrace_SettingGroup* pAllSettings = NULL;
+    unsigned int numAllSettings = 0;
+
+    // Default verbosity level
+    vktrace_LogSetCallback(loggingCallback);
+    vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+
+    // apply settings from cmd-line args
+    if (vktrace_SettingGroup_init_from_cmdline(&g_replaySettingGroup, argc, argv, &replaySettings.pTraceFilePath) != 0)
+    {
+        // invalid options specified
+        if (pAllSettings != NULL)
+        {
+            vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+        }
+        return -1;
+    }
+
+    // merge settings so that new settings will get written into the settings file
+    vktrace_SettingGroup_merge(&g_replaySettingGroup, &pAllSettings, &numAllSettings);
+
+    // Set verbosity level
+    if (replaySettings.verbosity == NULL || !strcmp(replaySettings.verbosity, "errors"))
+        replaySettings.verbosity = "errors";
+    else if (!strcmp(replaySettings.verbosity, "quiet"))
+        vktrace_LogSetLevel(VKTRACE_LOG_NONE);
+    else if (!strcmp(replaySettings.verbosity, "warnings"))
+        vktrace_LogSetLevel(VKTRACE_LOG_WARNING);
+    else if (!strcmp(replaySettings.verbosity, "full"))
+        vktrace_LogSetLevel(VKTRACE_LOG_VERBOSE);
+#if _DEBUG
+    else if (!strcmp(replaySettings.verbosity, "debug"))
+        vktrace_LogSetLevel(VKTRACE_LOG_DEBUG);
+#endif
+    else
+    {
+        vktrace_SettingGroup_print(&g_replaySettingGroup);
+        // invalid options specified
+        if (pAllSettings != NULL)
+        {
+            vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+        }
+        return -1;
+    }
+
+    // Set up environment for screenshot
+    if (replaySettings.screenshotList != NULL)
+    {
+        // Set env var that communicates list to ScreenShot layer
+        vktrace_set_global_var("_VK_SCREENSHOT", replaySettings.screenshotList);
+
+    }
+
+    // open trace file and read in header
+    char* pTraceFile = replaySettings.pTraceFilePath;
+    vktrace_trace_file_header fileHeader;
+    FILE *tracefp;
+
+    if (pTraceFile != NULL && strlen(pTraceFile) > 0)
+    {
+        tracefp = fopen(pTraceFile, "rb");
+        if (tracefp == NULL)
+        {
+            vktrace_LogError("Cannot open trace file: '%s'.", pTraceFile);
+            // invalid options specified
+            if (pAllSettings != NULL)
+            {
+                vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+            }
+            vktrace_free(pTraceFile);
+            return -1;
+        }
+    }
+    else
+    {
+        vktrace_LogError("No trace file specified.");
+        vktrace_SettingGroup_print(&g_replaySettingGroup);
+        if (pAllSettings != NULL)
+        {
+            vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+        }
+        return -1;
+    }
+
+    FileLike* traceFile = vktrace_FileLike_create_file(tracefp);
+    if (vktrace_FileLike_ReadRaw(traceFile, &fileHeader, sizeof(fileHeader)) == false)
+    {
+        vktrace_LogError("Unable to read header from file.");
+        if (pAllSettings != NULL)
+        {
+            vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+        }
+        fclose(tracefp);
+        vktrace_free(pTraceFile);
+        vktrace_free(traceFile);
+        return -1;
+    }
+
+    //set global version num
+    vktrace_set_trace_version(fileHeader.trace_file_version);
+
+    // Make sure trace file version is supported
+    if (fileHeader.trace_file_version < VKTRACE_TRACE_FILE_VERSION_MINIMUM_COMPATIBLE)
+    {
+        vktrace_LogError("Trace file version %u is older than minimum compatible version (%u).\nYou'll need to make a new trace file, or use an older replayer.", fileHeader.trace_file_version, VKTRACE_TRACE_FILE_VERSION_MINIMUM_COMPATIBLE);
+    }
+
+    // load any API specific driver libraries and init replayer objects
+    uint8_t tidApi = VKTRACE_TID_RESERVED;
+    vktrace_trace_packet_replay_library* replayer[VKTRACE_MAX_TRACER_ID_ARRAY_SIZE];
+    ReplayFactory makeReplayer;
+
+    // Create window. Initial size is 100x100. It will later get resized to the size
+    // used by the traced app. The resize will happen  during playback of swapchain functions.
+#if defined(ANDROID)
+    vktrace_replay::ReplayDisplay disp(window, 100, 100);
+#else
+    vktrace_replay::ReplayDisplay disp(100, 100, 0, false);
+#endif
+    //**********************************************************
+#if _DEBUG
+    static BOOL debugStartup = FALSE;//TRUE
+    while (debugStartup);
+#endif
+    //***********************************************************
+
+    for (int i = 0; i < VKTRACE_MAX_TRACER_ID_ARRAY_SIZE; i++)
+    {
+        replayer[i] = NULL;
+    }
+
+    for (int i = 0; i < fileHeader.tracer_count; i++)
+    {
+        uint8_t tracerId = fileHeader.tracer_id_array[i].id;
+        tidApi = tracerId;
+
+        const VKTRACE_TRACER_REPLAYER_INFO* pReplayerInfo = &(gs_tracerReplayerInfo[tracerId]);
+
+        if (pReplayerInfo->tracerId != tracerId)
+        {
+            vktrace_LogError("Replayer info for TracerId (%d) failed consistency check.", tracerId);
+            assert(!"TracerId in VKTRACE_TRACER_REPLAYER_INFO does not match the requested tracerId. The array needs to be corrected.");
+        }
+        else if (pReplayerInfo->needsReplayer == TRUE)
+        {
+            // Have our factory create the necessary replayer
+            replayer[tracerId] = makeReplayer.Create(tracerId);
+
+            if (replayer[tracerId] == NULL)
+            {
+                // replayer failed to be created
+                if (pAllSettings != NULL)
+                {
+                    vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+                }
+                fclose(tracefp);
+                vktrace_free(pTraceFile);
+                vktrace_free(traceFile);
+                return -1;
+            }
+
+            // merge the replayer's settings into the list of all settings so that we can output a comprehensive settings file later on.
+            vktrace_SettingGroup_merge(replayer[tracerId]->GetSettings(), &pAllSettings, &numAllSettings);
+
+            // update the replayer with the loaded settings
+            replayer[tracerId]->UpdateFromSettings(pAllSettings, numAllSettings);
+
+            // Initialize the replayer
+            err = replayer[tracerId]->Initialize(&disp, &replaySettings);
+            if (err) {
+                vktrace_LogError("Couldn't Initialize replayer for TracerId %d.", tracerId);
+                if (pAllSettings != NULL)
+                {
+                    vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+                }
+                fclose(tracefp);
+                vktrace_free(pTraceFile);
+                vktrace_free(traceFile);
+                return err;
+            }
+        }
+    }
+
+    if (tidApi == VKTRACE_TID_RESERVED) {
+        vktrace_LogError("No API specified in tracefile for replaying.");
+        if (pAllSettings != NULL)
+        {
+            vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+        }
+        fclose(tracefp);
+        vktrace_free(pTraceFile);
+        vktrace_free(traceFile);
+        return -1;
+    }
+
+    // main loop
+    Sequencer sequencer(traceFile);
+    err = vktrace_replay::main_loop(disp, sequencer, replayer, replaySettings);
+
+    for (int i = 0; i < VKTRACE_MAX_TRACER_ID_ARRAY_SIZE; i++)
+    {
+        if (replayer[i] != NULL)
+        {
+            replayer[i]->Deinitialize();
+            makeReplayer.Destroy(&replayer[i]);
+        }
+    }
+
+    if (pAllSettings != NULL) {
+        vktrace_SettingGroup_Delete_Loaded(&pAllSettings, &numAllSettings);
+    }
+
+    fclose(tracefp);
+    vktrace_free(pTraceFile);
+    vktrace_free(traceFile);
+
+    return err;
+}
+
+#if defined(ANDROID)
+static bool initialized = false;
+static bool active = false;
+
+// Convert Intents to argv
+// Ported from Hologram sample, only difference is flexible key
+std::vector<std::string> get_args(android_app &app, const char* intent_extra_data_key)
+{
+    std::vector<std::string> args;
+    JavaVM &vm = *app.activity->vm;
+    JNIEnv *p_env;
+    if (vm.AttachCurrentThread(&p_env, nullptr) != JNI_OK)
+        return args;
+
+    JNIEnv &env = *p_env;
+    jobject activity = app.activity->clazz;
+    jmethodID get_intent_method = env.GetMethodID(env.GetObjectClass(activity),
+            "getIntent", "()Landroid/content/Intent;");
+    jobject intent = env.CallObjectMethod(activity, get_intent_method);
+    jmethodID get_string_extra_method = env.GetMethodID(env.GetObjectClass(intent),
+            "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
+    jvalue get_string_extra_args;
+    get_string_extra_args.l = env.NewStringUTF(intent_extra_data_key);
+    jstring extra_str = static_cast<jstring>(env.CallObjectMethodA(intent,
+            get_string_extra_method, &get_string_extra_args));
+
+    std::string args_str;
+    if (extra_str) {
+        const char *extra_utf = env.GetStringUTFChars(extra_str, nullptr);
+        args_str = extra_utf;
+        env.ReleaseStringUTFChars(extra_str, extra_utf);
+        env.DeleteLocalRef(extra_str);
+    }
+
+    env.DeleteLocalRef(get_string_extra_args.l);
+    env.DeleteLocalRef(intent);
+    vm.DetachCurrentThread();
+
+    // split args_str
+    std::stringstream ss(args_str);
+    std::string arg;
+    while (std::getline(ss, arg, ' ')) {
+        if (!arg.empty())
+            args.push_back(arg);
+    }
+
+    return args;
+}
+
+static int32_t processInput(struct android_app* app, AInputEvent* event) {
+    return 0;
+}
+
+static void processCommand(struct android_app* app, int32_t cmd) {
+    switch(cmd) {
+        case APP_CMD_INIT_WINDOW: {
+            if (app->window) {
+                initialized = true;
+            }
+            break;
+        }
+        case APP_CMD_GAINED_FOCUS: {
+            active = true;
+            break;
+        }
+        case APP_CMD_LOST_FOCUS: {
+            active = false;
+            break;
+        }
+    }
+}
+
+// Start with carbon copy of main() and convert it to support Android, then diff them and move common code to helpers.
+void android_main(struct android_app *app)
+{
+    app_dummy();
+
+    const char* appTag = "vkreplay";
+
+    int vulkanSupport = InitVulkan();
+    if (vulkanSupport == 0) {
+        __android_log_print(ANDROID_LOG_ERROR, appTag, "No Vulkan support found");
+        return;
+    }
+
+    app->onAppCmd = processCommand;
+    app->onInputEvent = processInput;
+
+    while(1) {
+        int events;
+        struct android_poll_source* source;
+        while (ALooper_pollAll(active ? 0 : -1, NULL, &events, (void**)&source) >= 0) {
+            if (source) {
+                source->process(app, source);
+            }
+
+            if (app->destroyRequested != 0) {
+                // anything to clean up?
+                return;
+            }
+        }
+
+        if (initialized && active) {
+            // Parse Intents into argc, argv
+            // Use the following key to send arguments to gtest, i.e.
+            // --es args "-v\ debug\ -t\ /sdcard/cube0.vktrace"
+            const char key[] = "args";
+            std::vector<std::string> args = get_args(*app, key);
+
+            int argc = args.size() + 1;
+
+            char** argv = (char**) malloc(argc * sizeof(char*));
+            argv[0] = (char*)"vkreplay";
+            for (int i = 0; i < args.size(); i++)
+                argv[i + 1] = (char*) args[i].c_str();
+
+
+            __android_log_print(ANDROID_LOG_INFO, appTag, "argc = %i", argc);
+            for (int i = 0; i < argc; i++)
+                __android_log_print(ANDROID_LOG_INFO, appTag, "argv[%i] = %s", i, argv[i]);
+
+            // sleep to allow attaching debugger
+            //sleep(10);
+
+            // Call into common code
+            int err = vkreplay_main(argc, argv, app->window);
+            __android_log_print(ANDROID_LOG_DEBUG, appTag, "vkreplay_main returned %i", err);
+
+            ANativeActivity_finish(app->activity);
+            free(argv);
+
+            return;
+        }
+    }
+}
+
+#else // ANDROID
+
+extern "C"
+int main(int argc, char **argv)
+{
+    return vkreplay_main(argc, argv);
+}
+
+#endif
diff --git a/vktrace/src/vktrace_replay/vkreplay_main.h b/vktrace/src/vktrace_replay/vkreplay_main.h
new file mode 100644
index 0000000..a63e1af
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_main.h
@@ -0,0 +1,35 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Tony Barbour <tony@lunarg.com>
+ **************************************************************************/
+#ifndef VKREPLAY__MAIN_H
+#define VKREPLAY__MAIN_H
+
+typedef struct vkreplayer_settings
+{
+    char* pTraceFilePath;
+    unsigned int numLoops;
+    int loopStartFrame;
+    int loopEndFrame;
+    const char* screenshotList;
+    const char* verbosity;
+} vkreplayer_settings;
+
+#endif // VKREPLAY__MAIN_H
diff --git a/vktrace/src/vktrace_replay/vkreplay_seq.cpp b/vktrace/src/vktrace_replay/vkreplay_seq.cpp
new file mode 100644
index 0000000..106f019
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_seq.cpp
@@ -0,0 +1,52 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+#include "vkreplay_seq.h"
+
+extern "C" {
+#include "vktrace_trace_packet_utils.h"
+}
+
+namespace vktrace_replay {
+
+vktrace_trace_packet_header * Sequencer::get_next_packet()
+{
+    vktrace_free(m_lastPacket);
+    if (!m_pFile)
+        return (NULL);
+    m_lastPacket = vktrace_read_trace_packet(m_pFile);
+    return(m_lastPacket);
+}
+
+void Sequencer::get_bookmark(seqBookmark &bookmark) {
+    bookmark.file_offset = m_bookmark.file_offset;
+}
+
+
+void Sequencer::set_bookmark(const seqBookmark &bookmark) {
+    fseek(m_pFile->mFile, m_bookmark.file_offset, SEEK_SET);
+}
+
+void Sequencer::record_bookmark()
+{
+    m_bookmark.file_offset = ftell(m_pFile->mFile);
+}
+
+} /* namespace vktrace_replay */
diff --git a/vktrace/src/vktrace_replay/vkreplay_seq.h b/vktrace/src/vktrace_replay/vkreplay_seq.h
new file mode 100644
index 0000000..743ee6a
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_seq.h
@@ -0,0 +1,79 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+#pragma once
+
+extern "C" {
+#include "vktrace_filelike.h"
+#include "vktrace_trace_packet_identifiers.h"
+}
+
+/* Class to handle fetching and sequencing packets from a tracefile.
+ * Contains no knowledge of type of tracer needed to process packet.
+ * Requires low level file/stream reading/seeking support. */
+namespace vktrace_replay {
+
+
+struct seqBookmark
+{
+    unsigned int file_offset;
+};
+
+
+// replay Sequencer interface
+ class AbstractSequencer
+ {
+ public:
+    virtual ~AbstractSequencer() {}
+    virtual vktrace_trace_packet_header *get_next_packet() = 0;
+    virtual void get_bookmark(seqBookmark &bookmark) = 0;
+    virtual void set_bookmark(const seqBookmark &bookmark) = 0;
+ };
+
+class Sequencer: public AbstractSequencer
+{
+
+public:
+    Sequencer(FileLike* pFile) : m_lastPacket(NULL), m_pFile(pFile) {}
+    ~Sequencer() {
+        this->clean_up();
+    }
+
+    void clean_up() {
+        if (m_lastPacket){
+            free(m_lastPacket);
+            m_lastPacket = NULL;
+        }
+    }
+
+    vktrace_trace_packet_header *get_next_packet();
+    void get_bookmark(seqBookmark &bookmark);
+    void set_bookmark(const seqBookmark &bookmark);
+    void record_bookmark();
+
+private:
+    vktrace_trace_packet_header *m_lastPacket;
+    seqBookmark m_bookmark;
+    FileLike *m_pFile;
+};
+
+} /* namespace vktrace_replay */
+
+
diff --git a/vktrace/src/vktrace_replay/vkreplay_window.h b/vktrace/src/vktrace_replay/vkreplay_window.h
new file mode 100644
index 0000000..6ae91ae
--- /dev/null
+++ b/vktrace/src/vktrace_replay/vkreplay_window.h
@@ -0,0 +1,165 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+#pragma once
+
+extern "C"{
+#include "vktrace_platform.h"
+}
+
+#if defined(PLATFORM_LINUX) || defined(XCB_NVIDIA)
+#if defined(ANDROID)
+#include <android_native_app_glue.h>
+typedef ANativeWindow* vktrace_window_handle;
+#else
+#include <xcb/xcb.h>
+typedef xcb_window_t vktrace_window_handle;
+#endif
+#elif defined(WIN32)
+typedef HWND vktrace_window_handle;
+#endif
+
+/* classes to abstract the display and initialization of rendering API for presenting
+ * framebuffers for display into a window on the screen or else fullscreen.
+ * Uses Bridge design pattern.
+ */
+namespace vktrace_replay {
+
+class ReplayDisplayImp {
+public:
+    virtual ~ReplayDisplayImp() {}
+    virtual int init(const unsigned int gpu_idx) = 0;
+    virtual int set_window(vktrace_window_handle hWindow, unsigned int width, unsigned int height) = 0;
+    virtual int create_window(const unsigned int width, const unsigned int height) = 0;
+    virtual void process_event() = 0;
+    virtual bool get_pause_status() = 0;
+    virtual void set_pause_status(bool pause) = 0;
+    virtual bool get_quit_status() = 0;
+    virtual void set_quit_status(bool quit) = 0;
+};
+
+class ReplayDisplay {
+public:
+    ReplayDisplay()
+        : m_imp(NULL),
+        m_width(0),
+        m_height(0),
+        m_gpu(0),
+        m_fullscreen(false),
+        m_hWindow(0)
+    {
+
+    }
+
+    ReplayDisplay(const unsigned int width, const unsigned int height, const unsigned int gpu, const bool fullscreen) :
+        m_imp(NULL),
+        m_width(width),
+        m_height(height),
+        m_gpu(gpu),
+        m_fullscreen(fullscreen),
+        m_hWindow(0)
+    {
+    }
+
+    ReplayDisplay(vktrace_window_handle hWindow, unsigned int width, unsigned int height) :
+        m_imp(NULL),
+        m_width(width),
+        m_height(height),
+        m_gpu(0),
+        m_fullscreen(false),
+        m_hWindow(hWindow)
+    {
+    }
+
+    virtual ~ReplayDisplay()
+    {
+    }
+
+    void set_implementation(ReplayDisplayImp & disp)
+    {
+        m_imp = & disp;
+    }
+    void set_implementation(ReplayDisplayImp * disp)
+    {
+        m_imp = disp;
+    }
+    int init()
+    {
+        if (m_imp)
+            return m_imp->init(m_gpu);
+        else
+            return -1;
+    }
+    void process_event()
+    {
+        if(m_imp)
+            m_imp->process_event();
+    }
+    unsigned int get_gpu()
+    {
+        return m_gpu;
+    }
+    unsigned int get_width()
+    {
+        return m_width;
+    }
+    unsigned int get_height()
+    {
+        return m_height;
+    }
+    bool get_fullscreen()
+    {
+        return m_fullscreen;
+    }
+    vktrace_window_handle get_window_handle()
+    {
+        return m_hWindow;
+    }
+    bool get_pause_status() {
+        if (m_imp) {
+            return m_imp->get_pause_status();
+        }
+        return false;
+    }
+    void set_pause_status(bool pause) {
+        if (m_imp)
+            m_imp->set_pause_status(pause);
+    }
+    bool get_quit_status() {
+        if (m_imp) {
+            return m_imp->get_quit_status();
+        }
+        return false;
+    }
+    void set_quit_status(bool quit) {
+        if (m_imp)
+            m_imp->set_quit_status(quit);
+    }
+
+  private:
+    ReplayDisplayImp *m_imp;
+    unsigned int m_width;
+    unsigned int m_height;
+    unsigned int m_gpu;
+    bool m_fullscreen;
+    vktrace_window_handle m_hWindow;
+};
+
+}   // namespace vktrace_replay
diff --git a/vktrace/src/vktrace_trace/CMakeLists.txt b/vktrace/src/vktrace_trace/CMakeLists.txt
new file mode 100644
index 0000000..63ba576
--- /dev/null
+++ b/vktrace/src/vktrace_trace/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 2.8)
+project(vktrace)
+
+include("${SRC_DIR}/build_options.cmake")
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+set(SRC_LIST
+    ${SRC_LIST}
+    vktrace.cpp
+    vktrace_process.h
+    vktrace_process.cpp
+)
+
+include_directories(
+    ${SRC_DIR}
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/vktrace_trace
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_link_libraries(${PROJECT_NAME}
+    vktrace_common
+)
+
+build_options_finalize()
diff --git a/vktrace/src/vktrace_trace/vktrace.cpp b/vktrace/src/vktrace_trace/vktrace.cpp
new file mode 100644
index 0000000..9e9a48b
--- /dev/null
+++ b/vktrace/src/vktrace_trace/vktrace.cpp
@@ -0,0 +1,417 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ **************************************************************************/
+#include "vktrace.h"
+
+#include "vktrace_process.h"
+
+extern "C" {
+#include "vktrace_common.h"
+#include "vktrace_filelike.h"
+#include "vktrace_interconnect.h"
+#include "vktrace_trace_packet_identifiers.h"
+#include "vktrace_trace_packet_utils.h"
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+vktrace_settings g_settings;
+vktrace_settings g_default_settings;
+
+vktrace_SettingInfo g_settings_info[] =
+{
+    // common command options
+    { "p", "Program", VKTRACE_SETTING_STRING, { &g_settings.program }, { &g_default_settings.program }, TRUE, "The program to trace."},
+    { "a", "Arguments", VKTRACE_SETTING_STRING, { &g_settings.arguments }, { &g_default_settings.arguments }, TRUE, "Command line arguments to pass to trace program."},
+    { "w", "WorkingDir", VKTRACE_SETTING_STRING, { &g_settings.working_dir }, { &g_default_settings.working_dir }, TRUE, "The program's working directory."},
+    { "o", "OutputTrace", VKTRACE_SETTING_STRING, { &g_settings.output_trace }, { &g_default_settings.output_trace }, TRUE, "Path to the generated output trace file."},
+    { "s", "ScreenShot", VKTRACE_SETTING_STRING, { &g_settings.screenshotList }, { &g_default_settings.screenshotList }, TRUE, "Comma separated list of frames to take a snapshot of."},
+    { "ptm", "PrintTraceMessages", VKTRACE_SETTING_BOOL, { &g_settings.print_trace_messages }, { &g_default_settings.print_trace_messages }, TRUE, "Print trace messages to vktrace console."},
+    { "P", "PMB", VKTRACE_SETTING_BOOL, { &g_settings.enable_pmb }, { &g_default_settings.enable_pmb }, TRUE, "Optimize tracing of persistently mapped buffers, default is TRUE."},
+#if _DEBUG
+    { "v", "Verbosity", VKTRACE_SETTING_STRING, { &g_settings.verbosity }, { &g_default_settings.verbosity }, TRUE, "Verbosity mode. Modes are \"quiet\", \"errors\", \"warnings\", \"full\", \"debug\"."},
+#else
+    { "v", "Verbosity", VKTRACE_SETTING_STRING, { &g_settings.verbosity }, { &g_default_settings.verbosity }, TRUE, "Verbosity mode. Modes are \"quiet\", \"errors\", \"warnings\", \"full\"."},
+#endif
+
+    //{ "z", "pauze", VKTRACE_SETTING_BOOL, &g_settings.pause, &g_default_settings.pause, TRUE, "Wait for a key at startup (so a debugger can be attached)" },
+};
+
+vktrace_SettingGroup g_settingGroup =
+{
+    "vktrace",
+    sizeof(g_settings_info) / sizeof(g_settings_info[0]),
+    &g_settings_info[0]
+};
+
+// ------------------------------------------------------------------------------------------------
+#if defined(WIN32)
+uint64_t MessageLoop()
+{
+    MSG msg = { 0 };
+    bool quit = false;
+    while (!quit)
+    {
+        if (GetMessage(&msg, NULL, 0, 0) == FALSE)
+        {
+            quit = true;
+        }
+        else
+        {
+            quit = (msg.message == VKTRACE_WM_COMPLETE);
+        }
+    }
+    return msg.wParam;
+}
+#endif
+
+int PrepareTracers(vktrace_process_capture_trace_thread_info** ppTracerInfo)
+{
+    unsigned int num_tracers = 1;
+
+    assert(ppTracerInfo != NULL && *ppTracerInfo == NULL);
+    *ppTracerInfo = VKTRACE_NEW_ARRAY(vktrace_process_capture_trace_thread_info, num_tracers);
+    memset(*ppTracerInfo, 0, sizeof(vktrace_process_capture_trace_thread_info) * num_tracers);
+
+    // we only support Vulkan tracer
+    (*ppTracerInfo)[0].tracerId = VKTRACE_TID_VULKAN;
+
+    return num_tracers;
+}
+
+bool InjectTracersIntoProcess(vktrace_process_info* pInfo)
+{
+    bool bRecordingThreadsCreated = true;
+    vktrace_thread tracingThread;
+    if (vktrace_platform_remote_load_library(pInfo->hProcess, NULL, &tracingThread, NULL)) {
+        // prepare data for capture threads
+        pInfo->pCaptureThreads[0].pProcessInfo = pInfo;
+        pInfo->pCaptureThreads[0].recordingThread = VKTRACE_NULL_THREAD;
+
+        // create thread to record trace packets from the tracer
+        pInfo->pCaptureThreads[0].recordingThread = vktrace_platform_create_thread(Process_RunRecordTraceThread, &(pInfo->pCaptureThreads[0]));
+        if (pInfo->pCaptureThreads[0].recordingThread == VKTRACE_NULL_THREAD) {
+            vktrace_LogError("Failed to create trace recording thread.");
+            bRecordingThreadsCreated = false;
+        }
+
+    } else {
+        // failed to inject a DLL
+        bRecordingThreadsCreated = false;
+    }
+    return bRecordingThreadsCreated;
+}
+
+void loggingCallback(VktraceLogLevel level, const char* pMessage)
+{
+    if (level == VKTRACE_LOG_NONE)
+        return;
+
+    switch(level)
+    {
+    case VKTRACE_LOG_DEBUG: printf("vktrace debug: %s\n", pMessage); break;
+    case VKTRACE_LOG_ERROR: printf("vktrace error: %s\n", pMessage); break;
+    case VKTRACE_LOG_WARNING: printf("vktrace warning: %s\n", pMessage); break;
+    case VKTRACE_LOG_VERBOSE: printf("vktrace info: %s\n", pMessage); break;
+    default:
+        printf("%s\n", pMessage); break;
+    }
+    fflush(stdout);
+
+#if defined(WIN32)
+#if _DEBUG
+    OutputDebugString(pMessage);
+#endif
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+char* append_index_to_filename(const char* base, uint32_t index, const char* extension)
+{
+    char num[17];
+#ifdef PLATFORM_LINUX
+    snprintf(num, 17, "-%u", index);
+#elif defined(WIN32)
+    _snprintf_s(num, 17, _TRUNCATE, "-%u", index);
+#endif
+    return vktrace_copy_and_append(base, num, extension);
+}
+
+// ------------------------------------------------------------------------------------------------
+static uint32_t s_fileIndex = 0;
+char* find_available_filename(const char* originalFilename, bool bForceOverwrite)
+{
+    char* pOutputFilename = NULL;
+
+    if (bForceOverwrite)
+    {
+        if (s_fileIndex == 0)
+        {
+            pOutputFilename = vktrace_allocate_and_copy(g_settings.output_trace);
+        }
+        else
+        {
+            const char *pExtension = strrchr(g_settings.output_trace, '.');
+            char *basename = vktrace_allocate_and_copy_n(g_settings.output_trace, (int)((pExtension == NULL) ? strlen(g_settings.output_trace) : pExtension - g_settings.output_trace));
+            pOutputFilename = append_index_to_filename(basename, s_fileIndex, pExtension);
+            vktrace_free(basename);
+        }
+    }
+    else // don't overwrite
+    {
+        const char *pExtension = strrchr(g_settings.output_trace, '.');
+        char *basename = vktrace_allocate_and_copy_n(g_settings.output_trace, (int)((pExtension == NULL) ? strlen(g_settings.output_trace) : pExtension - g_settings.output_trace));
+        pOutputFilename = vktrace_allocate_and_copy(g_settings.output_trace);
+        FILE* pFile = NULL;
+        while ((pFile = fopen(pOutputFilename, "rb")) != NULL)
+        {
+            fclose(pFile);
+            ++s_fileIndex;
+
+            vktrace_free(pOutputFilename);
+            pOutputFilename = append_index_to_filename(basename, s_fileIndex, pExtension);
+        }
+        vktrace_free(basename);
+    }
+
+    // increment to the next available fileIndex to prep for the next trace file
+    ++s_fileIndex;
+    return pOutputFilename;
+}
+
+// ------------------------------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+    uint64_t exitval=0;
+    memset(&g_settings, 0, sizeof(vktrace_settings));
+
+    vktrace_LogSetCallback(loggingCallback);
+    vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+
+    // setup defaults
+    memset(&g_default_settings, 0, sizeof(vktrace_settings));
+    g_default_settings.output_trace = vktrace_allocate_and_copy("vktrace_out.vktrace");
+    g_default_settings.verbosity = "errors";
+    g_default_settings.screenshotList = NULL;
+    g_default_settings.enable_pmb = true;
+
+    if (vktrace_SettingGroup_init(&g_settingGroup, NULL, argc, argv, &g_settings.arguments) != 0)
+    {
+        // invalid cmd-line parameters
+        vktrace_SettingGroup_delete(&g_settingGroup);
+        vktrace_free(g_default_settings.output_trace);
+        return -1;
+    }
+    else
+    {
+        // Validate vktrace inputs
+        BOOL validArgs = TRUE;
+
+        if (g_settings.output_trace == NULL || strlen (g_settings.output_trace) == 0)
+        {
+            validArgs = FALSE;
+        }
+
+        if (strcmp(g_settings.verbosity, "quiet") == 0)
+            vktrace_LogSetLevel(VKTRACE_LOG_NONE);
+        else if (strcmp(g_settings.verbosity, "errors") == 0)
+            vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+        else if (strcmp(g_settings.verbosity, "warnings") == 0)
+            vktrace_LogSetLevel(VKTRACE_LOG_WARNING);
+        else if (strcmp(g_settings.verbosity, "full") == 0)
+            vktrace_LogSetLevel(VKTRACE_LOG_VERBOSE);
+#if _DEBUG
+        else if (strcmp(g_settings.verbosity, "debug") == 0)
+            vktrace_LogSetLevel(VKTRACE_LOG_DEBUG);
+#endif
+        else
+        {
+            vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+            validArgs = FALSE;
+        }
+        vktrace_set_global_var("_VK_TRACE_VERBOSITY", g_settings.verbosity);
+
+        if (validArgs == FALSE)
+        {
+            vktrace_SettingGroup_print(&g_settingGroup);
+            return -1;
+        }
+
+        if (g_settings.program == NULL || strlen(g_settings.program) == 0)
+        {
+            vktrace_LogWarning("No program (-p) parameter found: Will run vktrace as server.");
+            printf("Running vktrace as server...\n");
+            fflush(stdout);
+            g_settings.arguments = NULL;
+        }
+        else
+        {
+            if (g_settings.working_dir == NULL || strlen(g_settings.working_dir) == 0)
+            {
+                CHAR* buf = VKTRACE_NEW_ARRAY(CHAR, 4096);
+                vktrace_LogVerbose("No working directory (-w) parameter found: Assuming executable's path as working directory.");
+                vktrace_platform_full_path(g_settings.program, 4096, buf);
+                g_settings.working_dir = vktrace_platform_extract_path(buf);
+                VKTRACE_DELETE(buf);
+            }
+
+            vktrace_LogVerbose("Running vktrace as parent process will spawn child process: %s", g_settings.program);
+            if (g_settings.arguments != NULL && strlen(g_settings.arguments) > 0)
+            {
+                vktrace_LogVerbose("Args to be passed to child process: '%s'", g_settings.arguments);
+            }
+        }
+    }
+
+    if (g_settings.screenshotList)
+    {
+        // Export list to screenshot layer
+        vktrace_set_global_var("_VK_SCREENSHOT", g_settings.screenshotList);
+    }
+    else
+    {
+        vktrace_set_global_var("_VK_SCREENSHOT","");
+    }
+
+    vktrace_set_global_var("_VKTRACE_OPTIMIZE_PMB", g_settings.enable_pmb?"1":"0");
+
+    unsigned int serverIndex = 0;
+    do {
+        // Create and start the process or run in server mode
+
+        BOOL procStarted = TRUE;
+        vktrace_process_info procInfo;
+        memset(&procInfo, 0, sizeof(vktrace_process_info));
+        if (g_settings.program != NULL)
+        {
+            procInfo.exeName = vktrace_allocate_and_copy(g_settings.program);
+            procInfo.processArgs = vktrace_allocate_and_copy(g_settings.arguments);
+            procInfo.fullProcessCmdLine = vktrace_copy_and_append(g_settings.program, " ", g_settings.arguments);
+            procInfo.workingDirectory = vktrace_allocate_and_copy(g_settings.working_dir);
+            procInfo.traceFilename = vktrace_allocate_and_copy(g_settings.output_trace);
+        }
+        else
+        {
+            procInfo.traceFilename = find_available_filename(g_settings.output_trace, true);
+        }
+
+        procInfo.parentThreadId = vktrace_platform_get_thread_id();
+
+        // setup tracer, only Vulkan tracer suppported
+        PrepareTracers(&procInfo.pCaptureThreads);
+
+        if (g_settings.program != NULL)
+        {
+            char *instEnv = vktrace_get_global_var("VK_INSTANCE_LAYERS");
+            // Add ScreenShot layer if enabled
+            if (g_settings.screenshotList && (!instEnv || !strstr(instEnv, "VK_LAYER_LUNARG_screenshot")))
+            {
+                if (!instEnv || strlen(instEnv)  == 0)
+                    vktrace_set_global_var("VK_INSTANCE_LAYERS", "VK_LAYER_LUNARG_screenshot");
+                else
+                {
+                    char *newEnv = vktrace_copy_and_append(instEnv, VKTRACE_LIST_SEPARATOR, "VK_LAYER_LUNARG_screenshot");
+                    vktrace_set_global_var("VK_INSTANCE_LAYERS", newEnv);
+                }
+                instEnv = vktrace_get_global_var("VK_INSTANCE_LAYERS");
+            }
+            char *devEnv = vktrace_get_global_var("VK_DEVICE_LAYERS");
+            if (g_settings.screenshotList && (!devEnv || !strstr(devEnv, "VK_LAYER_LUNARG_screenshot")))
+            {
+                if (!devEnv || strlen(devEnv) == 0)
+                    vktrace_set_global_var("VK_DEVICE_LAYERS", "VK_LAYER_LUNARG_screenshot");
+                else
+                {
+                    char *newEnv = vktrace_copy_and_append(devEnv, VKTRACE_LIST_SEPARATOR, "VK_LAYER_LUNARG_screenshot");
+                    vktrace_set_global_var("VK_DEVICE_LAYERS", newEnv);
+                }
+                devEnv = vktrace_get_global_var("VK_DEVICE_LAYERS");
+            }
+            // Add vktrace_layer enable env var if needed
+            if (!instEnv || strlen(instEnv) == 0)
+            {
+                vktrace_set_global_var("VK_INSTANCE_LAYERS", "VK_LAYER_LUNARG_vktrace");
+            }
+            else if (instEnv != strstr(instEnv, "VK_LAYER_LUNARG_vktrace"))
+            {
+                char *newEnv = vktrace_copy_and_append("VK_LAYER_LUNARG_vktrace", VKTRACE_LIST_SEPARATOR, instEnv);
+                vktrace_set_global_var("VK_INSTANCE_LAYERS", newEnv);
+            }
+            if (!devEnv || strlen(devEnv) == 0)
+            {
+                vktrace_set_global_var("VK_DEVICE_LAYERS", "VK_LAYER_LUNARG_vktrace");
+            }
+            else if (devEnv != strstr(devEnv, "VK_LAYER_LUNARG_vktrace"))
+            {
+                char *newEnv = vktrace_copy_and_append("VK_LAYER_LUNARG_vktrace", VKTRACE_LIST_SEPARATOR, devEnv);
+                vktrace_set_global_var("VK_DEVICE_LAYERS", newEnv);
+            }
+            // call CreateProcess to launch the application
+            procStarted = vktrace_process_spawn(&procInfo);
+        }
+        if (procStarted == FALSE)
+        {
+            vktrace_LogError("Failed to set up remote process.");
+            exit(1);
+        }
+        else
+        {
+            if (InjectTracersIntoProcess(&procInfo) == FALSE)
+            {
+                vktrace_LogError("Failed to set up tracer communication threads.");
+                exit(1);
+            }
+
+            // create watchdog thread to monitor existence of remote process
+            if (g_settings.program != NULL)
+                procInfo.watchdogThread = vktrace_platform_create_thread(Process_RunWatchdogThread, &procInfo);
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+
+            // Sync wait for local threads and remote process to complete.
+            if (g_settings.program != NULL)
+            {
+                exitval=vktrace_linux_sync_wait_for_thread(&procInfo.watchdogThread);
+                if (exitval != 0)
+                    exit(exitval);
+            }
+
+            vktrace_linux_sync_wait_for_thread(&(procInfo.pCaptureThreads[0].recordingThread));
+
+#else
+            vktrace_platform_resume_thread(&procInfo.hThread);
+
+            // Now into the main message loop, listen for hotkeys to send over.
+            exitval = MessageLoop();
+#endif
+        }
+
+        vktrace_process_info_delete(&procInfo);
+        serverIndex++;
+    } while (g_settings.program == NULL);
+
+    vktrace_SettingGroup_delete(&g_settingGroup);
+    vktrace_free(g_default_settings.output_trace);
+
+    exit (exitval);
+}
+
diff --git a/vktrace/src/vktrace_trace/vktrace.h b/vktrace/src/vktrace_trace/vktrace.h
new file mode 100644
index 0000000..339cdc2
--- /dev/null
+++ b/vktrace/src/vktrace_trace/vktrace.h
@@ -0,0 +1,47 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ **************************************************************************/
+#pragma once
+
+extern "C" {
+#include "vktrace_settings.h"
+}
+
+
+#if defined(WIN32)
+#define VKTRACE_WM_COMPLETE (WM_USER + 0)
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+// globals
+//----------------------------------------------------------------------------------------------------------------------
+typedef struct vktrace_settings
+{
+    const char* program;
+    const char* arguments;
+    const char* working_dir;
+    char* output_trace;
+    BOOL print_trace_messages;
+    const char* screenshotList;
+    BOOL enable_pmb;
+    const char *verbosity;
+} vktrace_settings;
+
+extern vktrace_settings g_settings;
diff --git a/vktrace/src/vktrace_trace/vktrace_process.cpp b/vktrace/src/vktrace_trace/vktrace_process.cpp
new file mode 100644
index 0000000..fc7a0d8
--- /dev/null
+++ b/vktrace/src/vktrace_trace/vktrace_process.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#include <string>
+#include "vktrace_process.h"
+#include "vktrace.h"
+
+#if defined(PLATFORM_LINUX)
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+#if defined(PLATFORM_OSX)
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+extern "C" {
+#include "vktrace_filelike.h"
+#include "vktrace_interconnect.h"
+#include "vktrace_trace_packet_utils.h"
+}
+
+const unsigned long kWatchDogPollTime = 250;
+
+#if defined(WIN32)
+void SafeCloseHandle(HANDLE& _handle)
+{
+    if (_handle) {
+        CloseHandle(_handle);
+        _handle = NULL;
+    }
+}
+#endif
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+// Needs to be static because Process_RunWatchdogThread passes the address of rval to pthread_exit
+static int rval;
+#endif
+
+// ------------------------------------------------------------------------------------------------
+VKTRACE_THREAD_ROUTINE_RETURN_TYPE Process_RunWatchdogThread(LPVOID _procInfoPtr)
+{
+    vktrace_process_info* pProcInfo = (vktrace_process_info*)_procInfoPtr;
+
+#if defined(WIN32)
+
+    DWORD rv;
+    while (WaitForSingleObject(pProcInfo->hProcess, kWatchDogPollTime) == WAIT_TIMEOUT)
+    {
+        if (pProcInfo->serverRequestsTermination)
+        {
+            vktrace_LogVerbose("Vktrace has requested exit.");
+            return 0;
+        }
+    }
+
+    vktrace_LogVerbose("Child process has terminated.");
+    GetExitCodeProcess(pProcInfo->hProcess,  &rv);
+    PostThreadMessage(pProcInfo->parentThreadId, VKTRACE_WM_COMPLETE, rv, 0);
+    pProcInfo->serverRequestsTermination = TRUE;
+    return 0;
+    
+#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
+    int status = 0;
+    int options = 0;
+
+    // Check to see if process exists
+    rval=waitpid(pProcInfo->processId, &status, WNOHANG);
+    if (rval == pProcInfo->processId)
+    {
+        vktrace_LogVerbose("Child process was terminated.");
+        rval=1;
+        pthread_exit(&rval);
+    }
+
+    rval=1;
+    while (waitpid(pProcInfo->processId, &status, options) != -1)
+    {
+        if (WIFEXITED(status))
+        {
+            vktrace_LogVerbose("Child process exited.");
+            rval = WEXITSTATUS(status);
+            break;
+        }
+        else if (WCOREDUMP(status))
+        {
+            vktrace_LogError("Child process crashed.");
+            break;
+        }
+        else if (WIFSIGNALED(status))
+        {
+            vktrace_LogVerbose("Child process was signaled.");
+        }
+        else if (WIFSTOPPED(status))
+        {
+            vktrace_LogVerbose("Child process was stopped.");
+        }
+        else if (WIFCONTINUED(status))
+            vktrace_LogVerbose("Child process was continued.");
+    }
+    pthread_exit(&rval);
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+VKTRACE_THREAD_ROUTINE_RETURN_TYPE Process_RunRecordTraceThread(LPVOID _threadInfo)
+{
+    vktrace_process_capture_trace_thread_info* pInfo = (vktrace_process_capture_trace_thread_info*)_threadInfo;
+
+    MessageStream* pMessageStream = vktrace_MessageStream_create(TRUE, "", VKTRACE_BASE_PORT + pInfo->tracerId);
+    if (pMessageStream == NULL)
+    {
+        vktrace_LogError("Thread_CaptureTrace() cannot create message stream.");
+        return 1;
+    }
+
+    // create trace file
+    pInfo->pProcessInfo->pTraceFile = vktrace_write_trace_file_header(pInfo->pProcessInfo);
+
+    if (pInfo->pProcessInfo->pTraceFile == NULL) {
+        // writing trace file generated an error, no sense in continuing.
+        vktrace_LogError("Error cannot create trace file and write header.");
+        vktrace_process_info_delete(pInfo->pProcessInfo);
+        return 1;
+    }
+
+    FileLike* fileLikeSocket = vktrace_FileLike_create_msg(pMessageStream);
+    unsigned int total_packet_count = 0;
+    vktrace_trace_packet_header* pHeader = NULL;
+    size_t bytes_written;
+
+    while (pInfo->pProcessInfo->serverRequestsTermination == FALSE)
+    {
+        // get a packet
+        //vktrace_LogDebug("Waiting for a packet...");
+
+        // read entire packet in
+        pHeader = vktrace_read_trace_packet(fileLikeSocket);
+        ++total_packet_count;
+        if (pHeader == NULL)
+        {
+            if (pMessageStream->mErrorNum == WSAECONNRESET)
+            {
+                vktrace_LogError("Network Connection Reset");
+            }
+            else
+            {
+                vktrace_LogError("Network Connection Failed");
+            }
+            break;
+        }
+
+        //vktrace_LogDebug("Received packet id: %hu", pHeader->packet_id);
+        
+        if (pHeader->pBody == (uintptr_t) NULL)
+        {
+            vktrace_LogWarning("Received empty packet body for id: %hu", pHeader->packet_id);
+        }
+        else
+        {
+            // handle special case packets
+            if (pHeader->packet_id == VKTRACE_TPI_MESSAGE)
+            {
+                if (g_settings.print_trace_messages == TRUE)
+                {
+                    vktrace_trace_packet_message* pPacket = vktrace_interpret_body_as_trace_packet_message(pHeader);
+                    vktrace_LogAlways("Packet %lu: Traced Message (%s): %s", pHeader->global_packet_index, vktrace_LogLevelToShortString(pPacket->type), pPacket->message);
+                    vktrace_finalize_buffer_address(pHeader, (void **) &(pPacket->message));
+                }
+            }
+
+            if (pHeader->packet_id == VKTRACE_TPI_MARKER_TERMINATE_PROCESS)
+            {
+                pInfo->pProcessInfo->serverRequestsTermination = true;
+                vktrace_delete_trace_packet(&pHeader);
+                vktrace_LogVerbose("Thread_CaptureTrace is exiting.");
+                break;
+            }
+
+            if (pInfo->pProcessInfo->pTraceFile != NULL)
+            {
+                vktrace_enter_critical_section(&pInfo->pProcessInfo->traceFileCriticalSection);
+                bytes_written = fwrite(pHeader, 1, (size_t)pHeader->size, pInfo->pProcessInfo->pTraceFile);
+                fflush(pInfo->pProcessInfo->pTraceFile);
+                vktrace_leave_critical_section(&pInfo->pProcessInfo->traceFileCriticalSection);
+                if (bytes_written != pHeader->size)
+                {
+                    vktrace_LogError("Failed to write the packet for packet_id = %hu", pHeader->packet_id);
+                }
+            }
+        }
+
+        // clean up
+        vktrace_delete_trace_packet(&pHeader);
+    }
+
+#if defined(WIN32)
+    PostThreadMessage(pInfo->pProcessInfo->parentThreadId, VKTRACE_WM_COMPLETE, 0, 0);
+#endif
+
+    VKTRACE_DELETE(fileLikeSocket);
+    vktrace_MessageStream_destroy(&pMessageStream);
+
+    return 0;
+}
diff --git a/vktrace/src/vktrace_trace/vktrace_process.h b/vktrace/src/vktrace_trace/vktrace_process.h
new file mode 100644
index 0000000..bc7a176
--- /dev/null
+++ b/vktrace/src/vktrace_trace/vktrace_process.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014-2016 Valve Corporation. All rights reserved.
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */
+
+#pragma once
+
+extern "C" {
+#include "vktrace_common.h"
+#include "vktrace_process.h"
+#include "vktrace_interconnect.h"
+}
+
+VKTRACE_THREAD_ROUTINE_RETURN_TYPE Process_RunRecordTraceThread(LPVOID);
+
+VKTRACE_THREAD_ROUTINE_RETURN_TYPE Process_RunWatchdogThread(LPVOID);
diff --git a/vktrace/src/vktrace_viewer/CMakeLists.txt b/vktrace/src/vktrace_viewer/CMakeLists.txt
new file mode 100644
index 0000000..dfe2a48
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/CMakeLists.txt
@@ -0,0 +1,190 @@
+project(vktraceviewer)
+cmake_minimum_required(VERSION 3.0)
+
+include("${SRC_DIR}/build_options.cmake")
+
+set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "${SRC_DIR}/cmake/Modules/")
+
+# we want cmake to link the Qt libs into the binary
+# This policy was introduced in 2.8.11, so on newer versions, use the OLD policy to maintain consistent behavior
+if (POLICY CMP0020)
+   cmake_policy(SET CMP0020 OLD)
+endif()
+
+find_package(Qt5 COMPONENTS Widgets Gui Core Svg QUIET)
+
+if(NOT Qt5_FOUND)
+# After Qt5.6 is installed, you may need to add the following to the cmake command line:
+# -DCMAKE_PREFIX_PATH=C:\\Qt\\5.6\\msvc2015_64\\
+message(WARNING "WARNING: vktraceviewer will be excluded because Qt5 was not found.")
+else()
+
+find_package(Threads REQUIRED)
+find_package(X11 REQUIRED)
+
+require_pthreads()
+
+include_directories(
+    ${SRC_DIR}
+    ${SRC_DIR}/vktrace_common
+    ${SRC_DIR}/vktrace_replay
+    ${SRC_DIR}/vktrace_extensions/vktracevulkan/vkreplay
+    ${SRC_DIR}/vktrace_extensions/vktracevulkan/vktraceviewer
+    ${SRC_DIR}/vktrace_viewer
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${Qt5Widgets_INCLUDE_DIRS}
+)
+
+set(SRC_LIST
+    main.cpp
+    vktraceviewer.cpp
+    vktraceviewer_settings.cpp
+    vktraceviewer_output.cpp
+    vktraceviewer_trace_file_utils.cpp
+    vktraceviewer_qgeneratetracedialog.cpp
+    vktraceviewer_qsettingsdialog.cpp
+    vktraceviewer_qtimelineview.cpp
+    vktraceviewer_qtracefileloader.cpp
+    vktraceviewer_QReplayWorker.cpp
+    vktraceviewer_controller_factory.cpp
+    ${SRC_DIR}/vktrace_replay/vkreplay_seq.cpp
+    ${SRC_DIR}/vktrace_replay/vkreplay_factory.cpp
+   )
+
+# This should only contain headers that define a QOBJECT
+# Typically that means just headers for UI objects
+set(UI_HEADER_LIST
+    vktraceviewer.h
+    vktraceviewer_qgeneratetracedialog.h
+    vktraceviewer_qsettingsdialog.h
+    vktraceviewer_qtimelineview.h
+    vktraceviewer_qimageviewer.h
+    vktraceviewer_qsvgviewer.h
+    vktraceviewer_QReplayWidget.h
+    vktraceviewer_QReplayWorker.h
+    vktraceviewer_QTraceFileModel.h
+    vktraceviewer_qtracefileloader.h
+   )
+
+# These is for all headers
+set(HEADER_LIST
+    vktraceviewer.h
+    vktraceviewer_settings.h
+    vktraceviewer_output.h
+    vktraceviewer_controller.h
+    vktraceviewer_controller_factory.h
+    vktraceviewer_qgeneratetracedialog.h
+    vktraceviewer_qsettingsdialog.h
+    vktraceviewer_qtimelineview.h
+    vktraceviewer_qimageviewer.h
+    vktraceviewer_qsvgviewer.h
+    vktraceviewer_QReplayWidget.h
+    vktraceviewer_QReplayWorker.h
+    vktraceviewer_qtracefileloader.h
+    vktraceviewer_QTraceFileModel.h
+    vktraceviewer_trace_file_utils.h
+    vktraceviewer_view.h
+   )
+
+set(FORM_LIST
+    vktraceviewer.ui
+   )
+
+set(RESOURCE_LIST
+   )
+
+QT5_WRAP_CPP(QT_GEN_HEADER_MOC_LIST ${UI_HEADER_LIST})
+QT5_WRAP_UI(QT_GEN_FORM_HEADER_LIST ${FORM_LIST})
+QT5_ADD_RESOURCES(QT_GEN_RESOURCE_RCC_LIST ${RESOURCE_LIST})
+
+if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+    add_compiler_flag("-Wno-global-constructors -Wno-used-but-marked-unused")
+endif()
+
+# Platform specific compile flags.
+if (NOT MSVC)
+    add_compiler_flag("-fPIC")
+endif()
+
+add_executable(${PROJECT_NAME} ${SRC_LIST} ${HEADER_LIST}
+    ${QT_GEN_HEADER_MOC_LIST}
+    ${QT_GEN_FORM_HEADER_LIST}
+    ${QT_GEN_RESOURCE_RCC_LIST}
+   )
+
+if (TARGET SDL)
+    add_dependencies(${PROJECT_NAME} SDL)
+endif ()
+
+add_dependencies(${PROJECT_NAME} vktraceviewer_vk)
+
+target_link_libraries(${PROJECT_NAME}
+    Qt5::Widgets
+    Qt5::Core
+    Qt5::Svg
+    vktrace_common
+    vulkan_replay
+    vktraceviewer_vk
+    ${CMAKE_DL_LIBS}
+    ${X11_X11_LIB}
+)
+
+if (MSVC)
+  # copy the debug and release dlls for Qt5Widgets, Qt5Core, and Qt5Gui
+  set (COPY_DEST ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR})
+
+  get_property(qt5_qmake_executable TARGET ${Qt5Core_QMAKE_EXECUTABLE} PROPERTY IMPORT_LOCATION)
+  execute_process(COMMAND ${qt5_qmake_executable} -query QT_INSTALL_PLUGINS OUTPUT_VARIABLE QT_INSTALL_PLUGINS OUTPUT_STRIP_TRAILING_WHITESPACE)
+  execute_process(COMMAND ${qt5_qmake_executable} -query QT_INSTALL_BINS OUTPUT_VARIABLE QT_INSTALL_BINS OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  # There are also several other files that need to be copied or created
+  FILE(WRITE ${CMAKE_CURRENT_LIST_DIR}/qt.conf "[Paths]\nPlugins=.")
+  if(EXISTS "${QT_INSTALL_BINS}/icudt54.dll")
+    add_custom_target(copy_deps_qt5 ALL
+                      ${CMAKE_COMMAND} -E make_directory "${COPY_DEST}/platforms"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_LIST_DIR}/qt.conf" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Widgets.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Widgetsd.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Core.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Cored.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Gui.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Guid.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Svgd.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Svg.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libGLESv2d.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libGLESv2.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libEGLd.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libEGL.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_PLUGINS}/platforms/qwindows.dll" "${COPY_DEST}/platforms"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_PLUGINS}/platforms/qwindowsd.dll"  "${COPY_DEST}/platforms"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/icudt54.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/icuin54.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/icuuc54.dll" "${COPY_DEST}"
+                      COMMENT "Copying vktraceviewer Qt5 dependencies to ${COPY_DEST}"
+                      VERBATIM)
+  else()
+    add_custom_target(copy_deps_qt5 ALL
+                      ${CMAKE_COMMAND} -E make_directory "${COPY_DEST}/platforms"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_LIST_DIR}/qt.conf" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Widgets.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Widgetsd.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Core.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Cored.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Gui.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Guid.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Svgd.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/Qt5Svg.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libGLESv2d.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libGLESv2.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libEGLd.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_BINS}/libEGL.dll" "${COPY_DEST}"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_PLUGINS}/platforms/qwindows.dll" "${COPY_DEST}/platforms"
+                      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${QT_INSTALL_PLUGINS}/platforms/qwindowsd.dll"  "${COPY_DEST}/platforms"
+                      COMMENT "Copying vktraceviewer Qt5 dependencies to ${COPY_DEST}"
+                      VERBATIM)
+  endif()
+
+endif()
+
+build_options_finalize()
+endif(NOT Qt5_FOUND)
diff --git a/vktrace/src/vktrace_viewer/main.cpp b/vktrace/src/vktrace_viewer/main.cpp
new file mode 100644
index 0000000..f756163
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/main.cpp
@@ -0,0 +1,51 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include <QApplication>
+
+#include "vktraceviewer.h"
+#include "vktraceviewer_settings.h"
+
+int main(int argc, char *argv[])
+{
+    // Initialize QApplication before initializing settings
+    QApplication a(argc, argv);
+
+    // initialize settings
+    if (vktraceviewer_initialize_settings(argc, argv) == false)
+    {
+        return -1;
+    }
+
+    vktraceviewer w;
+    w.show();
+
+    if (g_settings.trace_file_to_open != NULL && strlen(g_settings.trace_file_to_open) > 0)
+    {
+        w.open_trace_file_threaded(QString(g_settings.trace_file_to_open));
+    }
+
+    int result = a.exec();
+
+    vktrace_SettingGroup_Delete_Loaded(&g_pAllSettings, &g_numAllSettings);
+
+    return result;
+}
diff --git a/vktrace/src/vktrace_viewer/qt.conf b/vktrace/src/vktrace_viewer/qt.conf
new file mode 100644
index 0000000..3b74f3a
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/qt.conf
@@ -0,0 +1,2 @@
+[Paths]

+Plugins=.
\ No newline at end of file
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer.cpp b/vktrace/src/vktrace_viewer/vktraceviewer.cpp
new file mode 100644
index 0000000..1e63198
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer.cpp
@@ -0,0 +1,1292 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include <assert.h>
+#include <QDebug>
+#include <QFileDialog>
+#include <QMoveEvent>
+#include <QPalette>
+#include <QProcess>
+#include <QToolButton>
+#include <QStandardPaths>
+#include <QMessageBox>
+#include <QCoreApplication>
+#include <QGraphicsBlurEffect>
+#include <QAbstractProxyModel>
+#include <qwindow.h>
+
+#include "ui_vktraceviewer.h"
+#include "vktraceviewer.h"
+#include "vktraceviewer_settings.h"
+#include "vktraceviewer_output.h"
+
+#include "vktraceviewer_controller_factory.h"
+#include "vktraceviewer_qgeneratetracedialog.h"
+#include "vktraceviewer_qtracefileloader.h"
+
+#include "vkreplay_main.h"
+//----------------------------------------------------------------------------------------------------------------------
+// globals
+//----------------------------------------------------------------------------------------------------------------------
+static QString g_PROJECT_NAME = "VkTrace Viewer";
+
+//-----------------------------------------------------------------------------
+void loggingCallback(VktraceLogLevel level, const char* pMessage)
+{
+    switch(level)
+    {
+    case VKTRACE_LOG_ERROR:
+        gs_OUTPUT.error(-1, QString(pMessage));
+        break;
+    case VKTRACE_LOG_WARNING:
+        gs_OUTPUT.warning(-1, QString(pMessage));
+        break;
+    case VKTRACE_LOG_DEBUG:
+    case VKTRACE_LOG_VERBOSE:
+    default:
+        gs_OUTPUT.message(-1, QString(pMessage));
+        break;
+    }
+
+#if defined(WIN32)
+#if _DEBUG
+    OutputDebugString(pMessage);
+#endif
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+vktraceviewer::vktraceviewer(QWidget *parent)
+    : QMainWindow(parent),
+      ui(new Ui::vktraceviewer),
+      m_settingsDialog(this),
+      m_pTraceFileModel(NULL),
+      m_pProxyModel(NULL),
+      m_pController(NULL),
+      m_pGenerateTraceButton(NULL),
+      m_pTimeline(NULL),
+      m_pGenerateTraceDialog(NULL),
+      m_bDelayUpdateUIForContext(false),
+      m_bGeneratingTrace(false)
+{
+    ui->setupUi(this);
+    qRegisterMetaType<uint64_t>("uint64_t");
+    qRegisterMetaType<VktraceLogLevel>("VktraceLogLevel");
+
+    m_pTraceStatsTab = new QWidget();
+    m_pTraceStatsTab->setObjectName(QStringLiteral("m_pTraceStatsTab"));
+    m_pTraceStatsTabLayout = new QGridLayout(m_pTraceStatsTab);
+    m_pTraceStatsTabLayout->setSpacing(6);
+    m_pTraceStatsTabLayout->setContentsMargins(11,11,11,11);
+    m_pTraceStatsTabLayout->setObjectName(QStringLiteral("m_pTraceStatsTabLayout"));
+    m_pTraceStatsTabText = new QTextBrowser(m_pTraceStatsTab);
+    m_pTraceStatsTabText->setObjectName(QStringLiteral("m_pTraceStatsTabText"));
+    m_pTraceStatsTabText->setLineWrapMode(QTextEdit::NoWrap);
+    m_pTraceStatsTabLayout->addWidget(m_pTraceStatsTabText, 0, 0, 1, 1);
+
+    QFont font("monospace", 10);
+    m_pTraceStatsTabText->setFont(font);
+    ui->outputTextBrowser->setFont(font);
+
+    // Hide unused, default tab
+    ui->stateTabWidget->removeTab(0);
+
+    memset(&m_traceFileInfo, 0, sizeof(vktraceviewer_trace_file_info));
+
+    m_settingsDialog.resize(g_settings.settings_dialog_width, g_settings.settings_dialog_height);
+    connect(&m_settingsDialog, SIGNAL(SaveSettings(vktrace_SettingGroup*, unsigned int)), this, SLOT(on_settingsSaved(vktrace_SettingGroup*, unsigned int)));
+    connect(&m_settingsDialog, SIGNAL(Resized(unsigned int, unsigned int)), this, SLOT(on_settingsDialogResized(unsigned int, unsigned int)));
+
+    this->move(g_settings.window_position_left, g_settings.window_position_top);
+    this->resize(g_settings.window_size_width, g_settings.window_size_height);
+
+    connect(ui->outputTextBrowser, SIGNAL(anchorClicked(const QUrl&)), this, SLOT(on_hyperlinkClicked(const QUrl&)));
+
+    // setup Output Window
+    vktraceviewer_output_init(ui->outputTextBrowser);
+    vktrace_LogSetCallback(loggingCallback);
+    vktrace_LogSetLevel(VKTRACE_LOG_ERROR);
+    vktrace_LogAlways("Welcome to VkTraceViewer!");
+
+    // cache the original background color of the search text box
+    m_searchTextboxBackgroundColor = ui->searchTextBox->palette().base().color();
+    
+    // add buttons to toolbar
+    m_pGenerateTraceButton = new QToolButton(ui->mainToolBar);
+    m_pGenerateTraceButton->setText("Generate Trace...");
+    m_pGenerateTraceButton->setEnabled(true);
+    connect(m_pGenerateTraceButton, SIGNAL(clicked()), this, SLOT(prompt_generate_trace()));
+
+    ui->mainToolBar->addWidget(m_pGenerateTraceButton);
+
+    ui->treeView->setModel(NULL);
+    ui->treeView->setContextMenuPolicy(Qt::ActionsContextMenu);
+    ui->treeView->setUniformRowHeights(true);
+
+    // setup timeline
+    m_pTimeline = new vktraceviewer_QTimelineView();
+    if (m_pTimeline != NULL)
+    {
+        m_pTimeline->setMinimumHeight(100);
+        connect(m_pTimeline, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slot_timeline_clicked(const QModelIndex &)));
+        ui->timelineLayout->addWidget(m_pTimeline);
+        ui->timelineLayout->removeWidget(ui->timelineViewPlaceholder);
+        delete ui->timelineViewPlaceholder;
+        ui->timelineViewPlaceholder = NULL;
+    }
+
+    m_pGenerateTraceDialog = new vktraceviewer_QGenerateTraceDialog(this);
+    connect(m_pGenerateTraceDialog, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)), this, SLOT(OnOutputMessage(VktraceLogLevel, const QString&)));
+
+    reset_tracefile_ui();
+
+    // for now, remove these widgets since they are not used
+    ui->bottomTabWidget->removeTab(ui->bottomTabWidget->indexOf(ui->machineInfoTab));
+    ui->bottomTabWidget->removeTab(ui->bottomTabWidget->indexOf(ui->callStackTab));
+}
+
+vktraceviewer::~vktraceviewer()
+{
+    close_trace_file();
+
+    if (m_pTimeline != NULL)
+    {
+        delete m_pTimeline;
+        m_pTimeline = NULL;
+    }
+
+    reset_view();
+
+    delete ui;
+    vktraceviewer_output_deinit();
+}
+
+void vktraceviewer::moveEvent(QMoveEvent *pEvent)
+{
+    g_settings.window_position_left = pEvent->pos().x();
+    g_settings.window_position_top = pEvent->pos().y();
+
+    vktraceviewer_settings_updated();
+}
+
+void vktraceviewer::closeEvent (QCloseEvent *pEvent)
+{
+    vktraceviewer_save_settings();
+    close_trace_file();
+    pEvent->accept();
+}
+
+void vktraceviewer::resizeEvent(QResizeEvent *pEvent)
+{
+    g_settings.window_size_width = pEvent->size().width();
+    g_settings.window_size_height = pEvent->size().height();
+
+    vktraceviewer_settings_updated();
+}
+
+int vktraceviewer::add_custom_state_viewer(QWidget* pWidget, const QString& title, bool bBringToFront)
+{
+    int tabIndex = ui->stateTabWidget->addTab(pWidget, title);
+
+    if (bBringToFront)
+    {
+        ui->stateTabWidget->setCurrentWidget(pWidget);
+    }
+
+    return tabIndex;
+}
+
+void vktraceviewer::remove_custom_state_viewer(int const tabIndex)
+{
+    ui->stateTabWidget->removeTab(tabIndex);
+}
+
+void vktraceviewer::enable_custom_state_viewer(QWidget* pWidget, bool bEnabled)
+{
+    ui->stateTabWidget->setTabEnabled(ui->stateTabWidget->indexOf(pWidget), bEnabled);
+}
+
+QToolButton* vktraceviewer::add_toolbar_button(const QString& title, bool bEnabled)
+{
+    QToolButton* pButton = new QToolButton(ui->mainToolBar);
+    pButton->setText(title);
+    pButton->setEnabled(bEnabled);
+    ui->mainToolBar->addWidget(pButton);
+    return pButton;
+}
+
+void vktraceviewer::set_calltree_model(vktraceviewer_QTraceFileModel* pTraceFileModel, QAbstractProxyModel* pModel)
+{
+    if (m_pTraceFileModel == pTraceFileModel && pModel == m_pProxyModel)
+    {
+        // Setting model and proxy to the same thing they are already set to, so there's nothing to do!
+        return;
+    }
+
+    m_pTraceFileModel = pTraceFileModel;
+    m_pProxyModel = pModel;
+
+    if (m_pTimeline != NULL)
+    {
+        m_pTimeline->setModel(pTraceFileModel);
+    }
+
+    if (pModel == NULL)
+    {
+        ui->treeView->setModel(pTraceFileModel);
+    }
+    else
+    {
+        ui->treeView->setModel(pModel);
+    }
+
+    // initially show all columns before hiding others
+    int columns = ui->treeView->header()->count();
+    for (int i = 0; i < columns; i++)
+    {
+        ui->treeView->showColumn(i);
+    }
+
+    // hide columns that are not very important right now
+    ui->treeView->hideColumn(vktraceviewer_QTraceFileModel::Column_TracerId);
+    ui->treeView->hideColumn(vktraceviewer_QTraceFileModel::Column_BeginTime);
+    ui->treeView->hideColumn(vktraceviewer_QTraceFileModel::Column_EndTime);
+    ui->treeView->hideColumn(vktraceviewer_QTraceFileModel::Column_PacketSize);
+
+    int width = ui->treeView->geometry().width();
+    int firstEqualWidthColumnIndex = 0;
+    float fSharedEqualWidthPct = 1.0;
+
+    if (pModel != NULL && pModel->inherits("vktraceviewer_QGroupThreadsProxyModel"))
+    {
+        ui->treeView->hideColumn(vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        ui->treeView->hideColumn(vktraceviewer_QTraceFileModel::Column_ThreadId);
+
+        ui->treeView->setColumnWidth(vktraceviewer_QTraceFileModel::Column_PacketIndex, width * 0.05);
+        ui->treeView->setColumnWidth(vktraceviewer_QTraceFileModel::Column_CpuDuration, width * 0.08);
+        firstEqualWidthColumnIndex = m_pTraceFileModel->columnCount();
+        fSharedEqualWidthPct = 1.0f - 0.05f - 0.08f;
+    }
+    else
+    {
+        // entrypoint names get the most space
+        ui->treeView->setColumnWidth(vktraceviewer_QTraceFileModel::Column_EntrypointName, width * 0.55);
+        firstEqualWidthColumnIndex = 1;
+        fSharedEqualWidthPct = 1.0f - 0.55f;
+    }
+
+    // the remaining space is divided among visible columns
+    int visibleColumns = 0;
+    for (int i = firstEqualWidthColumnIndex; i < columns; i++)
+    {
+        if (!ui->treeView->isColumnHidden(i))
+        {
+            visibleColumns++;
+        }
+    }
+
+
+    int scollbarWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+    int columnWidths = (width-scollbarWidth) * (fSharedEqualWidthPct / visibleColumns);
+
+    for (int i = firstEqualWidthColumnIndex; i < columns; i++)
+    {
+        if (!ui->treeView->isColumnHidden(i))
+        {
+            ui->treeView->setColumnWidth(i, columnWidths);
+        }
+    }
+}
+
+void vktraceviewer::add_calltree_contextmenu_item(QAction* pAction)
+{
+    ui->treeView->addAction(pAction);
+}
+
+int indexOfColumn(QAbstractItemModel* pModel, const QString &text)
+{
+    for (int i = 0; i < pModel->columnCount(); i++)
+    {
+        if (pModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString() == text)
+        {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void vktraceviewer::select_call_at_packet_index(unsigned long long packetIndex)
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QApplication::setOverrideCursor(Qt::WaitCursor);
+
+        QModelIndex start = m_pTraceFileModel->index(0, vktraceviewer_QTraceFileModel::Column_PacketIndex);
+
+        QModelIndexList matches = m_pTraceFileModel->match(start, Qt::DisplayRole, QVariant(packetIndex), 1, Qt::MatchFixedString | Qt::MatchRecursive | Qt::MatchWrap);
+        if (matches.count() > 0)
+        {
+            // for some reason, we need to recreate the index such that the index and parent both are for column 0
+            QModelIndex updatedMatch = m_pTraceFileModel->index(matches[0].row(), 0, m_pTraceFileModel->index(matches[0].parent().row(), 0));
+
+            selectApicallModelIndex(updatedMatch, true, true);
+            ui->treeView->setFocus();
+
+            if (m_pTimeline != NULL)
+            {
+                m_pTimeline->setCurrentIndex(m_pTraceFileModel->index(matches[0].row(), vktraceviewer_QTraceFileModel::Column_EntrypointName, QModelIndex()));
+            }
+        }
+
+        QApplication::restoreOverrideCursor();
+    }
+}
+
+void vktraceviewer::highlight_timeline_item(unsigned long long packetArrayIndex, bool bScrollTo, bool bSelect)
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QModelIndex location = m_pTraceFileModel->index(packetArrayIndex, 0);
+
+        if (m_pTimeline != NULL && m_pTimeline->currentIndex() != location)
+        {
+            // scroll to the index
+            if (bScrollTo)
+            {
+                m_pTimeline->scrollTo(location);
+            }
+
+            // select the index
+            if (bSelect)
+            {
+                m_pTimeline->setCurrentIndex(location);
+            }
+        }
+    }
+}
+
+
+void vktraceviewer::on_replay_state_changed(bool bReplayInProgress)
+{
+    bool bEnableUi = !bReplayInProgress;
+    ui->treeView->setEnabled(bEnableUi);
+    this->m_pGenerateTraceButton->setEnabled(bEnableUi);
+    ui->nextDrawcallButton->setEnabled(bEnableUi);
+    ui->prevDrawcallButton->setEnabled(bEnableUi);
+    ui->searchNextButton->setEnabled(bEnableUi);
+    ui->searchPrevButton->setEnabled(bEnableUi);
+    ui->searchTextBox->setEnabled(bEnableUi);
+    if (m_pTimeline != NULL)
+    {
+        m_pTimeline->setEnabled(bEnableUi);
+    }
+}
+
+unsigned long long vktraceviewer::get_current_packet_index()
+{
+    QModelIndex currentIndex = ui->treeView->currentIndex();
+    QModelIndex col0Index = currentIndex.sibling(currentIndex.row(), 0);
+    QModelIndex index = mapTreeIndexToModel(col0Index);
+
+    unsigned long long packetIndex = 0;
+    if (index.isValid())
+    {
+        vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+        if (pHeader != NULL)
+        {
+            assert(pHeader != NULL);
+            packetIndex = pHeader->global_packet_index;
+        }
+    }
+    return packetIndex;
+}
+
+void vktraceviewer::reset_view()
+{
+    int count = ui->stateTabWidget->count();
+    while (count > 0)
+    {
+        delete ui->stateTabWidget->widget(0);
+        count = ui->stateTabWidget->count();
+    }
+}
+
+void vktraceviewer::LogAlways(const QString& message)
+{
+    OnOutputMessage(VKTRACE_LOG_VERBOSE, message);
+}
+
+void vktraceviewer::LogWarning(const QString& message)
+{
+    OnOutputMessage(VKTRACE_LOG_WARNING, message);
+}
+
+void vktraceviewer::LogError(const QString& message)
+{
+    OnOutputMessage(VKTRACE_LOG_ERROR, message);
+}
+
+void vktraceviewer::add_setting_group(vktrace_SettingGroup* pGroup)
+{
+    vktrace_SettingGroup_merge(pGroup, &g_pAllSettings, &g_numAllSettings);
+}
+
+unsigned int vktraceviewer::get_global_settings(vktrace_SettingGroup** ppGroups)
+{
+    if (ppGroups != NULL)
+    {
+        *ppGroups = g_pAllSettings;
+    }
+
+    return g_numAllSettings;
+}
+
+bool vktraceviewer::prompt_load_new_trace(const QString& tracefile)
+{
+    int ret = QMessageBox::warning(this, tr(g_PROJECT_NAME.toStdString().c_str()), tr("Would you like to load the new trace file?"),
+                                  QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+
+    if (ret == QMessageBox::Yes)
+    {
+    //    // save current session if there is one
+    //    if (m_openFilename.size() > 0 && m_pTraceReader != NULL && m_pApiCallTreeModel != NULL)
+    //    {
+    //        save_session_to_disk(get_sessionfile_path(m_openFilename, *m_pTraceReader), m_openFilename, m_pTraceReader, m_pApiCallTreeModel);
+    //    }
+
+        // try to open the new file
+        open_trace_file_threaded(tracefile);
+        return true;
+    }
+
+    return false;
+}
+
+void vktraceviewer::on_actionE_xit_triggered()
+{
+    close();
+}
+
+void vktraceviewer::on_action_Open_triggered()
+{
+    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
+                                                    tr("vktrace Binary Files (*.vktrace *.*)"));
+
+    if (!fileName.isEmpty())
+    {
+        open_trace_file_threaded(fileName);
+    }
+}
+
+typedef struct {
+    uint64_t totalCpuExecutionTime;
+    uint64_t totalTraceOverhead;
+    uint32_t totalCallCount;
+} vtvApiUsageStats;
+
+void vktraceviewer::GenerateTraceFileStats()
+{
+    // process trace file to extract some API usage stats
+    // (NOTE: this could happen in a background thread)
+    ui->bottomTabWidget->addTab(m_pTraceStatsTab, "Trace Stats");
+
+    QString statText;
+    m_pTraceStatsTabText->setText(statText);
+
+    vtvApiUsageStats tmpNewStat;
+    tmpNewStat.totalCallCount = 1;
+    tmpNewStat.totalCpuExecutionTime = 0;
+    tmpNewStat.totalTraceOverhead = 0;
+
+    vtvApiUsageStats totalStats;
+    totalStats.totalCallCount = 0;
+    totalStats.totalCpuExecutionTime = 0;
+    totalStats.totalTraceOverhead = 0;
+
+    uint64_t totalTraceTime = 0;
+
+    if (m_traceFileInfo.packetCount > 0)
+    {
+        uint64_t start = m_traceFileInfo.pPacketOffsets[0].pHeader->entrypoint_begin_time;
+        uint64_t end = m_traceFileInfo.pPacketOffsets[m_traceFileInfo.packetCount-1].pHeader->entrypoint_end_time;
+        totalTraceTime = end-start;
+    }
+
+    QMap<uint16_t, vtvApiUsageStats> statMap;
+    for (uint64_t i = 0; i < m_traceFileInfo.packetCount; i++)
+    {
+        vktrace_trace_packet_header* pHeader = m_traceFileInfo.pPacketOffsets[i].pHeader;
+        if (pHeader->packet_id >= VKTRACE_TPI_BEGIN_API_HERE)
+        {
+            totalStats.totalCallCount++;
+            totalStats.totalCpuExecutionTime += (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);
+            totalStats.totalTraceOverhead += ((pHeader->vktrace_end_time - pHeader->vktrace_begin_time) - (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time));
+            if (statMap.contains(pHeader->packet_id))
+            {
+                statMap[pHeader->packet_id].totalCpuExecutionTime += (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);
+                statMap[pHeader->packet_id].totalTraceOverhead += ((pHeader->vktrace_end_time - pHeader->vktrace_begin_time) - (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time));
+                statMap[pHeader->packet_id].totalCallCount++;
+            }
+            else
+            {
+                tmpNewStat.totalCpuExecutionTime = (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);
+                tmpNewStat.totalTraceOverhead = ((pHeader->vktrace_end_time - pHeader->vktrace_begin_time) - (pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time));
+                statMap.insert(pHeader->packet_id, tmpNewStat);
+            }
+        }
+    }
+
+    uint64_t appTime = totalTraceTime - totalStats.totalCpuExecutionTime - totalStats.totalTraceOverhead;
+    uint64_t appDriverTime = totalTraceTime - totalStats.totalTraceOverhead;
+
+    statText += "<table>";
+    statText += QString("<tr><td>Total Trace Time:</td><td>%1 ns</td></tr>").arg(totalTraceTime);
+    statText += QString("<tr><td>Total App+Driver Time:</td><td>%1 ns (%2%)</td></tr>").arg(appDriverTime).arg(100*(float)appDriverTime/(float)totalTraceTime, 0, 'f', 1);
+    statText += QString("<tr><td>Total Driver Time:</td><td>%1 ns (%2%)</td></tr>").arg(totalStats.totalCpuExecutionTime).arg(100*(float)totalStats.totalCpuExecutionTime/(float)totalTraceTime, 0, 'f', 1);
+    statText += QString("<tr><td>Total App Time:</td><td>%1 ns (%2%)</td></tr>").arg(appTime).arg(100*(float)appTime/(float)totalTraceTime, 0, 'f', 1);
+    statText += QString("<tr><td>Total VkTrace Overhead:</td><td>%1 ns (%2%)</td></tr>").arg(totalStats.totalTraceOverhead).arg(100*(float)totalStats.totalTraceOverhead/(float)totalTraceTime, 0, 'f', 1);;
+    statText += QString("<tr><td>Total API Calls:</td><td>%1</td></tr>").arg(totalStats.totalCallCount);
+    statText += QString("<tr><td>Total Entrypoints Called:</td><td>%1</td></tr>").arg(statMap.count());
+    statText += "</table><br/>";
+
+    statText += "<table><thead><tr><th align='left'>Entrypoint</th><th align='right'># Calls (%Total)</th><th align='right'>Driver Time (%Total %AppDr %Driver)</th><th align='right'>VkTrace Overhead (%Total %vktrace)</th></tr></thead><tbody>";
+
+    for (QMap<uint16_t, vtvApiUsageStats>::iterator i = statMap.begin(); i != statMap.end(); i++)
+    {
+        const vtvApiUsageStats stat = i.value();
+        const char* entrypoint = m_pController->GetPacketIdString(i.key());
+        if (entrypoint == NULL)
+        {
+            // Instead of printing entrypoint name, just print i
+            statText += QString("<tr><td>%1</td>").arg(i.key());
+        }
+        else
+        {
+            statText += QString("<tr><td>%1</td>").arg(entrypoint);
+        }
+
+        // Note, because this is being output as HTML, consecutive spaces ' ' get removed, thus I'm using '?' to pad the numbers and replacing them with "&nbsp;" below so that numbers are better aligned.
+        // Also, this text field has been created with a fixed-width font.
+        statText += QString("<td align='right'>%1 (%2%)</td>").arg(stat.totalCallCount).arg(100*(float)stat.totalCallCount/(float)totalStats.totalCallCount, 5, 'f', 1, '?');
+        statText += QString("<td align='right'>%1 ns (%2% %3%??%4%)</td>").arg(stat.totalCpuExecutionTime).arg(100*(float)stat.totalCpuExecutionTime/(float)totalTraceTime, 5, 'f', 1, '?').arg(100*(float)stat.totalCpuExecutionTime/(float)appDriverTime, 5, 'f', 1, '?').arg(100*(float)stat.totalCpuExecutionTime/(float)totalStats.totalCpuExecutionTime, 5, 'f', 1, '?');
+        statText += QString("<td align='right'>%1 ns (%2% %3%)</td></tr>").arg(stat.totalTraceOverhead).arg(100*(float)stat.totalTraceOverhead/(float)totalTraceTime, 5, 'f', 1, '?').arg(100*(float)stat.totalTraceOverhead/(float)totalStats.totalTraceOverhead, 5, 'f', 1, '?');
+
+        statText.replace('?', "&nbsp;");
+    }
+
+    statText += "</tbody></table>";
+    m_pTraceStatsTabText->setHtml(statText);
+}
+
+void vktraceviewer::onTraceFileLoaded(bool bSuccess, const vktraceviewer_trace_file_info& fileInfo, const QString& controllerFilename)
+{
+    QApplication::restoreOverrideCursor();
+
+    if (fileInfo.packetCount == 0)
+    {
+        LogWarning("The trace file has 0 packets.");
+    }
+    else if (fileInfo.pPacketOffsets == NULL)
+    {
+        LogError("No packet offsets read from trace file.");
+        bSuccess = false;
+    }
+
+    if (!bSuccess)
+    {
+        LogAlways("...FAILED!");
+        QMessageBox::critical(this, tr("Error"), tr("Could not open trace file."));
+        close_trace_file();
+
+        if (m_bGeneratingTrace)
+        {
+            // if the user was generating a trace file, but the trace failed to load,
+            // then re-spawn the generate trace dialog.
+            prompt_generate_trace();
+        }
+    }
+    else
+    {
+        m_traceFileInfo = fileInfo;
+
+        setWindowTitle(QString(m_traceFileInfo.filename) + " - " + g_PROJECT_NAME);
+        LogAlways("...success!");
+
+        // update settings to reflect the currently open file
+        g_settings.trace_file_to_open = vktrace_allocate_and_copy(m_traceFileInfo.filename);
+        vktraceviewer_settings_updated();
+
+#ifndef USE_STATIC_CONTROLLER_LIBRARY
+        if (!controllerFilename.isEmpty())
+        {
+            m_pController = m_controllerFactory.Load(controllerFilename.toStdString().c_str());
+        }
+#else
+        m_pController = vtvCreateQController();
+#endif
+
+        if (m_pController != NULL)
+        {
+            connect(m_pController, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)), this, SLOT(OnOutputMessage(VktraceLogLevel, const QString&)));
+            connect(m_pController, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)), this, SLOT(OnOutputMessage(VktraceLogLevel, uint64_t, const QString&)));
+
+            // Merge in settings from the controller.
+            // This won't replace settings that may have already been loaded from disk.
+            vktrace_SettingGroup_merge(m_pController->GetSettings(), &g_pAllSettings, &g_numAllSettings);
+
+            // now update the controller with the loaded settings
+            m_pController->UpdateFromSettings(g_pAllSettings, g_numAllSettings);
+
+            //// trace file was loaded, now attempt to open additional session data
+            //if (load_or_create_session(filename.c_str(), m_pTraceReader) == false)
+            //{
+            //    // failing to load session data is not critical, but may result in unexpected behavior at times.
+            //    vktraceviewer_output_error("VkTraceViewer was unable to create a session folder to save viewing information. Functionality may be limited.");
+            //}
+
+            // Update the UI with the controller
+            m_pController->LoadTraceFile(&m_traceFileInfo, this);
+        }
+
+        // update toolbar
+        ui->searchTextBox->setEnabled(true);
+        ui->searchPrevButton->setEnabled(true);
+        ui->searchNextButton->setEnabled(true);
+
+        ui->action_Close->setEnabled(true);
+        ui->actionExport_API_Calls->setEnabled(true);
+
+        ui->prevDrawcallButton->setEnabled(true);
+        ui->nextDrawcallButton->setEnabled(true);
+
+        // reset flag indicating that the ui may have been generating a trace file.
+        m_bGeneratingTrace = false;
+
+        GenerateTraceFileStats();
+    }
+}
+
+void vktraceviewer::on_action_Close_triggered()
+{
+    close_trace_file();
+}
+
+void vktraceviewer::close_trace_file()
+{
+    if (m_pController != NULL)
+    {
+        ui->bottomTabWidget->removeTab(ui->bottomTabWidget->indexOf(m_pTraceStatsTab));
+        m_pController->UnloadTraceFile();
+#ifndef USE_STATIC_CONTROLLER_LIBRARY
+        m_controllerFactory.Unload(&m_pController);
+#else
+        vtvDeleteQController(&m_pController);
+#endif
+    }
+
+    if (m_pTimeline != NULL && m_pTimeline->model() != NULL)
+    {
+        m_pTimeline->setModel(NULL);
+        m_pTimeline->repaint();
+    }
+
+    if (m_traceFileInfo.packetCount > 0)
+    {
+        for (unsigned int i = 0; i < m_traceFileInfo.packetCount; i++)
+        {
+            if (m_traceFileInfo.pPacketOffsets[i].pHeader != NULL)
+            {
+                vktrace_free(m_traceFileInfo.pPacketOffsets[i].pHeader);
+                m_traceFileInfo.pPacketOffsets[i].pHeader = NULL;
+            }
+        }
+
+        VKTRACE_DELETE(m_traceFileInfo.pPacketOffsets);
+        m_traceFileInfo.pPacketOffsets = NULL;
+        m_traceFileInfo.packetCount = 0;
+    }
+
+    if (m_traceFileInfo.pFile != NULL)
+    {
+        fclose(m_traceFileInfo.pFile);
+        m_traceFileInfo.pFile = NULL;
+    }
+
+    if (m_traceFileInfo.filename != NULL)
+    {
+        vktrace_free(m_traceFileInfo.filename);
+        m_traceFileInfo.filename = NULL;
+
+        LogAlways("Closing trace file.");
+        LogAlways("-------------------");
+
+        // update settings
+        if (g_settings.trace_file_to_open != NULL)
+        {
+            vktrace_free(g_settings.trace_file_to_open);
+            g_settings.trace_file_to_open = NULL;
+            vktraceviewer_settings_updated();
+        }
+    }
+
+    setWindowTitle(g_PROJECT_NAME);
+
+    reset_tracefile_ui();
+}
+
+void vktraceviewer::on_actionExport_API_Calls_triggered()
+{
+    QString suggestedName(m_traceFileInfo.filename);
+
+    int lastIndex = suggestedName.lastIndexOf('.');
+    if (lastIndex != -1)
+    {
+        suggestedName = suggestedName.remove(lastIndex, suggestedName.size() - lastIndex);
+    }
+    suggestedName += "-ApiCalls.txt";
+
+    QString fileName = QFileDialog::getSaveFileName(this, tr("Export API Calls"), suggestedName, tr("Text (*.txt)"));
+
+    if (!fileName.isEmpty())
+    {
+        FILE* pFile = fopen(fileName.toStdString().c_str(), "w");
+        if (pFile == NULL)
+        {
+            LogError("Failed to open file for write. Can't export API calls.");
+            return;
+        }
+
+        // iterate through every packet
+        for (unsigned int i = 0; i < m_traceFileInfo.packetCount; i++)
+        {
+            vktraceviewer_trace_file_packet_offsets* pOffsets = &m_traceFileInfo.pPacketOffsets[i];
+            vktrace_trace_packet_header* pHeader = pOffsets->pHeader;
+            assert(pHeader != NULL);
+            QString string = m_pTraceFileModel->get_packet_string(pHeader);
+
+            // output packet string
+            fprintf(pFile, "%s\n", string.toStdString().c_str());
+        }
+
+        fclose(pFile);
+    }
+}
+
+void vktraceviewer::on_actionEdit_triggered()
+{
+    // make sure dialog is at the size specified by the settings
+    m_settingsDialog.resize(g_settings.settings_dialog_width, g_settings.settings_dialog_height);
+
+    // set the groups so that the dialog is displaying the most recent information
+    m_settingsDialog.setGroups(g_pAllSettings, g_numAllSettings);
+
+    // execute the dialog
+    m_settingsDialog.exec();
+}
+
+void vktraceviewer::on_settingsDialogResized(unsigned int width, unsigned int height)
+{
+    // the dialog was resized, so update the settings
+    g_settings.settings_dialog_width = width;
+    g_settings.settings_dialog_height = height;
+
+    // Update the setting groups with the new values.
+    vktraceviewer_settings_updated();
+
+    // re-set the groups so that the dialog is displaying the most recent information.
+    m_settingsDialog.setGroups(g_pAllSettings, g_numAllSettings);
+}
+
+void vktraceviewer::on_settingsSaved(vktrace_SettingGroup* pUpdatedSettings, unsigned int numGroups)
+{
+    // pUpdatedSettings is already pointing to the same location as g_pAllSettings
+    g_numAllSettings = numGroups;
+
+    // apply updated settings to the settingGroup so that the UI will respond to the changes
+    vktrace_SettingGroup_Apply_Overrides(&g_settingGroup, pUpdatedSettings, numGroups);
+
+    if (m_pController != NULL)
+    {
+        m_pController->UpdateFromSettings(pUpdatedSettings, numGroups);
+    }
+
+    vktraceviewer_save_settings();
+
+    // react to changes in settings
+    this->move(g_settings.window_position_left, g_settings.window_position_top);
+    this->resize(g_settings.window_size_width, g_settings.window_size_height);
+}
+
+void vktraceviewer::open_trace_file_threaded(const QString& filename)
+{
+    // close any existing trace
+    close_trace_file();
+
+    LogAlways("*********************");
+    LogAlways("Opening trace file...");
+    LogAlways(filename);
+
+    QApplication::setOverrideCursor(Qt::WaitCursor);
+
+    vktraceviewer_QTraceFileLoader* pTraceLoader = new vktraceviewer_QTraceFileLoader();
+    m_traceLoaderThread.setObjectName("TraceLoaderThread");
+    pTraceLoader->moveToThread(&m_traceLoaderThread);
+
+    connect(pTraceLoader, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)), this, SLOT(OnOutputMessage(VktraceLogLevel, uint64_t, const QString&)), Qt::QueuedConnection);
+    connect(pTraceLoader, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)), this, SLOT(OnOutputMessage(VktraceLogLevel, const QString&)), Qt::QueuedConnection);
+
+    connect(this, SIGNAL(LoadTraceFile(const QString&)), pTraceLoader, SLOT(loadTraceFile(QString)), Qt::QueuedConnection);
+
+    connect(pTraceLoader, SIGNAL(TraceFileLoaded(bool, vktraceviewer_trace_file_info, const QString&)), this, SLOT(onTraceFileLoaded(bool, vktraceviewer_trace_file_info, const QString&)));
+    connect(pTraceLoader, SIGNAL(Finished()), &m_traceLoaderThread, SLOT(quit()));
+    connect(pTraceLoader, SIGNAL(Finished()), pTraceLoader, SLOT(deleteLater()));
+
+    m_traceLoaderThread.start();
+
+    // Signal the loader to start
+    emit LoadTraceFile(filename);
+}
+
+void vktraceviewer::reset_tracefile_ui()
+{
+    ui->action_Close->setEnabled(false);
+    ui->actionExport_API_Calls->setEnabled(false);
+
+    ui->prevDrawcallButton->setEnabled(false);
+    ui->nextDrawcallButton->setEnabled(false);
+    ui->searchTextBox->clear();
+    ui->searchTextBox->setEnabled(false);
+    ui->searchPrevButton->setEnabled(false);
+    ui->searchNextButton->setEnabled(false);
+
+    //VKTRACEVIEWER_DISABLE_BOTTOM_TAB(ui->machineInfoTab);
+    //VKTRACEVIEWER_DISABLE_BOTTOM_TAB(ui->callStackTab);
+}
+
+void vktraceviewer::on_treeView_clicked(const QModelIndex &index)
+{
+    QModelIndex col0Index = index.sibling(index.row(), vktraceviewer_QTraceFileModel::Column_EntrypointName);
+    QModelIndex srcIndex = mapTreeIndexToModel(col0Index);
+    if (srcIndex.isValid())
+    {
+        selectApicallModelIndex(srcIndex, true, true);
+    }
+}
+
+void vktraceviewer::on_hyperlinkClicked(const QUrl& link)
+{
+    if (link.fileName() == "packet")
+    {
+        if (link.hasFragment())
+        {
+            qulonglong index = link.fragment().toULongLong();
+            this->select_call_at_packet_index(index);
+        }
+    }
+}
+
+void vktraceviewer::slot_timeline_clicked(const QModelIndex &index)
+{
+    selectApicallModelIndex(index, true, true);
+}
+
+void vktraceviewer::selectApicallModelIndex(QModelIndex index, bool scrollTo, bool select)
+{
+    // make sure the index is visible in tree view
+    QModelIndex treeIndex = mapTreeIndexFromModel(index);
+
+    if (ui->treeView->currentIndex() != treeIndex && ui->treeView->isEnabled())
+    {
+        QModelIndex parentIndex = treeIndex.parent();
+        while (parentIndex.isValid())
+        {
+            if (ui->treeView->isExpanded(parentIndex) == false)
+            {
+                ui->treeView->expand(parentIndex);
+            }
+            parentIndex = parentIndex.parent();
+        }
+
+        // scroll to the index
+        if (scrollTo)
+        {
+            ui->treeView->scrollTo(treeIndex);
+        }
+
+        // select the index
+        if (select)
+        {
+            ui->treeView->setCurrentIndex(treeIndex);
+        }
+    }
+
+    if (m_pTimeline != NULL && m_pTimeline->currentIndex() != index)
+    {
+        // scroll to the index
+        if (scrollTo)
+        {
+            m_pTimeline->scrollTo(index);
+        }
+
+        // select the index
+        if (select)
+        {
+            m_pTimeline->setCurrentIndex(index);
+        }
+    }
+}
+
+void vktraceviewer::on_searchTextBox_textChanged(const QString &searchText)
+{
+    QPalette palette(ui->searchTextBox->palette());
+    palette.setColor(QPalette::Base, m_searchTextboxBackgroundColor);
+    ui->searchTextBox->setPalette(palette);
+
+    if (m_pTraceFileModel != NULL)
+    {
+        m_pTraceFileModel->set_highlight_search_string(searchText);
+    }
+
+    // need to briefly give the treeview focus so that it properly redraws and highlights the matching rows
+    // then return focus to the search textbox so that typed keys are not lost
+    ui->treeView->setFocus();
+    ui->searchTextBox->setFocus();
+}
+
+void vktraceviewer::on_searchNextButton_clicked()
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QModelIndex index = ui->treeView->currentIndex();
+        if (!index.isValid())
+        {
+            // If there was no valid current index, then get the first index in the trace file model.
+            index = m_pTraceFileModel->index(0, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+
+        // Need to make sure this index is in model-space
+        if (index.model() == m_pProxyModel)
+        {
+            index = mapTreeIndexToModel(index);
+        }
+
+        // get the next item in the list
+        // TODO: this means that we won't be able to hit "Next" and select the first item in the trace file.
+        // However, if we move this into the loop below, then hitting "Next" will always result in finding the same item.
+        index = index.sibling(index.row()+1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+
+        // Can't get column count from the TreeView, so need to ask both the model and proxy if it exists.
+        int columnCount = m_pTraceFileModel->columnCount(index);
+        if (m_pProxyModel != NULL)
+        {
+            columnCount = m_pProxyModel->columnCount(index);
+        }
+
+        while (index.isValid())
+        {
+            for (int column = 0; column < columnCount; column++)
+            {
+                if (ui->treeView->isColumnHidden(column))
+                    continue;
+
+                QModelIndex treeIndex = mapTreeIndexFromModel(m_pTraceFileModel->index(index.row(), column, index.parent()));
+                if (treeIndex.data(Qt::DisplayRole).toString().contains(ui->searchTextBox->text(), Qt::CaseInsensitive))
+                {
+                    // Get the first column so that it can be selected
+                    QModelIndex srcIndex = m_pTraceFileModel->index(index.row(), vktraceviewer_QTraceFileModel::Column_EntrypointName, index.parent());
+                    selectApicallModelIndex(srcIndex, true, true);
+                    ui->treeView->setFocus();
+                    return;
+                }
+            }
+
+            // wasn't found in that row, so check the next one
+            index = index.sibling(index.row()+1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+    }
+}
+
+void vktraceviewer::on_searchPrevButton_clicked()
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QModelIndex index = ui->treeView->currentIndex();
+        if (!index.isValid())
+        {
+            // If there was no valid current index, then get the first index in the trace file model.
+            index = m_pTraceFileModel->index(0, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+
+        // Need to make sure this index is in model-space
+        if (index.model() == m_pProxyModel)
+        {
+            index = mapTreeIndexToModel(index);
+        }
+
+        // get the next item in the list
+        // TODO: this means that we won't be able to hit "Prev" and select the first item in the trace file.
+        // However, if we move this into the loop below, then hitting "Prev" will always result in finding the same item.
+        index = index.sibling(index.row()-1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+
+        // Can't get column count from the TreeView, so need to ask both the model and proxy if it exists.
+        int columnCount = m_pTraceFileModel->columnCount(index);
+        if (m_pProxyModel != NULL)
+        {
+            columnCount = m_pProxyModel->columnCount(index);
+        }
+
+        while (index.isValid())
+        {
+            for (int column = 0; column < columnCount; column++)
+            {
+                if (ui->treeView->isColumnHidden(column))
+                    continue;
+
+                QModelIndex treeIndex = mapTreeIndexFromModel(m_pTraceFileModel->index(index.row(), column, index.parent()));
+                if (treeIndex.data(Qt::DisplayRole).toString().contains(ui->searchTextBox->text(), Qt::CaseInsensitive))
+                {
+                    // Get the first column so that it can be selected
+                    QModelIndex srcIndex = m_pTraceFileModel->index(index.row(), vktraceviewer_QTraceFileModel::Column_EntrypointName, index.parent());
+                    selectApicallModelIndex(srcIndex, true, true);
+                    ui->treeView->setFocus();
+                    return;
+                }
+            }
+
+            // wasn't found in that row, so check the next one
+            index = index.sibling(index.row()-1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+
+    }
+}
+
+void vktraceviewer::on_prevDrawcallButton_clicked()
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QModelIndex index = ui->treeView->currentIndex();
+
+        index = mapTreeIndexToModel(index);
+
+        QModelIndex indexAbove= index.sibling(index.row()-1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        while (indexAbove.isValid())
+        {
+            vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)indexAbove.internalPointer();
+            if (pHeader != NULL && m_pTraceFileModel->isDrawCall((VKTRACE_TRACE_PACKET_ID)pHeader->packet_id))
+            {
+                selectApicallModelIndex(indexAbove, true, true);
+                ui->treeView->setFocus();
+                return;
+            }
+
+            // that row is not a draw call, so check the prev one
+            indexAbove = indexAbove.sibling(indexAbove.row()-1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+    }
+}
+
+QModelIndex vktraceviewer::mapTreeIndexToModel(const QModelIndex& treeIndex) const
+{
+    if (m_pProxyModel != NULL)
+    {
+        return m_pProxyModel->mapToSource(treeIndex);
+    }
+
+    return treeIndex;
+}
+
+QModelIndex vktraceviewer::mapTreeIndexFromModel(const QModelIndex& modelIndex) const
+{
+    if (m_pProxyModel != NULL)
+    {
+        return m_pProxyModel->mapFromSource(modelIndex);
+    }
+
+    return modelIndex;
+}
+
+void vktraceviewer::on_nextDrawcallButton_clicked()
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QModelIndex index = ui->treeView->currentIndex();
+
+        index = mapTreeIndexToModel(index);
+
+        QModelIndex indexBelow = index.sibling(index.row()+1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        while (indexBelow.isValid())
+        {
+            vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)indexBelow.internalPointer();
+            if (pHeader != NULL && m_pTraceFileModel->isDrawCall((VKTRACE_TRACE_PACKET_ID)pHeader->packet_id))
+            {
+                selectApicallModelIndex(indexBelow, true, true);
+                ui->treeView->setFocus();
+                return;
+            }
+
+            // that row is not a draw call, so check the next one
+            indexBelow = indexBelow.sibling(indexBelow.row()+1, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+    }
+}
+
+void vktraceviewer::on_searchTextBox_returnPressed()
+{
+    if (m_pTraceFileModel != NULL)
+    {
+        QModelIndex index = ui->treeView->indexBelow(ui->treeView->currentIndex());
+        bool bFound = false;
+
+        // search down from the current index
+        while (index.isValid())
+        {
+            for (int column = 0; column < m_pTraceFileModel->columnCount(index); column++)
+            {
+                if (m_pTraceFileModel->data(m_pTraceFileModel->index(index.row(), column, index.parent()), Qt::DisplayRole).toString().contains(ui->searchTextBox->text(), Qt::CaseInsensitive))
+                {
+                    bFound = true;
+                    break;
+                }
+            }
+
+            if (bFound)
+            {
+                break;
+            }
+            else
+            {
+                // wasn't found in that row, so check the next one
+                index = ui->treeView->indexBelow(index);
+            }
+        }
+
+        // if not found yet, then search from the root down to the current node
+        if (!bFound)
+        {
+            index = m_pTraceFileModel->index(0, 0);
+
+            while (index.isValid() && index != ui->treeView->currentIndex())
+            {
+                for (int column = 0; column < m_pTraceFileModel->columnCount(index); column++)
+                {
+                    if (m_pTraceFileModel->data(m_pTraceFileModel->index(index.row(), column, index.parent()), Qt::DisplayRole).toString().contains(ui->searchTextBox->text(), Qt::CaseInsensitive))
+                    {
+                        bFound = true;
+                        break;
+                    }
+                }
+
+                if (bFound)
+                {
+                    break;
+                }
+                else
+                {
+                    // wasn't found in that row, so check the next one
+                    index = ui->treeView->indexBelow(index);
+                }
+            }
+        }
+
+        if (bFound && index.isValid())
+        {
+            // a valid item was found, scroll to it and select it
+            selectApicallModelIndex(index, true, true);
+            ui->searchTextBox->setFocus();
+        }
+        else
+        {
+            // no items were found, so set the textbox background to red (it will get cleared to the original color if the user edits the search text)
+            QPalette palette(ui->searchTextBox->palette());
+            palette.setColor(QPalette::Base, Qt::red);
+            ui->searchTextBox->setPalette(palette);
+        }
+    }
+}
+
+void vktraceviewer::on_contextComboBox_currentIndexChanged(int index)
+{
+}
+
+void vktraceviewer::prompt_generate_trace()
+{
+    m_bGeneratingTrace = true;
+
+    bool bShowDialog = true;
+    while (bShowDialog)
+    {
+        int code = m_pGenerateTraceDialog->exec();
+        if (code != vktraceviewer_QGenerateTraceDialog::Succeeded)
+        {
+            m_bGeneratingTrace = false;
+            bShowDialog = false;
+        }
+        else
+        {
+            QFileInfo fileInfo(m_pGenerateTraceDialog->get_trace_file_path());
+            if (code == vktraceviewer_QGenerateTraceDialog::Succeeded &&
+                fileInfo.exists())
+            {
+                bShowDialog = false;
+                if (prompt_load_new_trace(fileInfo.canonicalFilePath()) == false)
+                {
+                    // The user decided not to load the trace file, so clear the generatingTrace flag.
+                    m_bGeneratingTrace = false;
+                }
+            }
+            else
+            {
+                LogError("Failed to trace the application.");
+                QMessageBox::critical(this, "Error", "Failed to trace application.");
+            }
+        }
+    }
+}
+
+void vktraceviewer::OnOutputMessage(VktraceLogLevel level, const QString &message)
+{
+    OnOutputMessage(level, -1, message);
+}
+
+void vktraceviewer::OnOutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString &message)
+{
+    switch(level)
+    {
+        case VKTRACE_LOG_ERROR:
+        {
+            gs_OUTPUT.error(packetIndex, message);
+            break;
+        }
+        case VKTRACE_LOG_WARNING:
+        {
+            gs_OUTPUT.warning(packetIndex, message);
+            break;
+        }
+        case VKTRACE_LOG_DEBUG:
+        case VKTRACE_LOG_VERBOSE:
+        default:
+        {
+            gs_OUTPUT.message(packetIndex, message);
+            break;
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer.h b/vktrace/src/vktrace_viewer/vktraceviewer.h
new file mode 100644
index 0000000..6823bb6
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer.h
@@ -0,0 +1,176 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#ifndef VKTRACEVIEWER_H
+#define VKTRACEVIEWER_H
+
+#include <QMainWindow>
+#include <QString>
+#include <QThread>
+
+#include "vkreplay_seq.h"
+#include "vkreplay_factory.h"
+
+#include "vktraceviewer_view.h"
+#include "vktraceviewer_trace_file_utils.h"
+#include "vktraceviewer_qtimelineview.h"
+#include "vktraceviewer_controller_factory.h"
+#include "vktraceviewer_controller.h"
+#include "vktraceviewer_QTraceFileModel.h"
+#include "vktraceviewer_qgeneratetracedialog.h"
+#include "vktraceviewer_qsettingsdialog.h"
+#include "vktraceviewer_settings.h"
+
+namespace Ui
+{
+    class vktraceviewer;
+}
+
+class QGridLayout;
+class QModelIndex;
+class QProcess;
+class QProcessEnvironment;
+class QToolButton;
+
+class vktraceviewer : public QMainWindow, public vktraceviewer_view
+{
+    Q_OBJECT
+
+    enum Prompt_Result
+    {
+        vktraceviewer_prompt_error = -1,
+        vktraceviewer_prompt_cancelled = 0,
+        vktraceviewer_prompt_success = 1
+    };
+
+public:
+    explicit vktraceviewer(QWidget *parent = 0);
+    ~vktraceviewer();
+
+    void open_trace_file_threaded(const QString &filename);
+    void close_trace_file();
+
+    // Implementation of vktraceviewer_view
+    virtual void reset_view();
+
+
+    void LogAlways(const QString &message);
+    void LogWarning(const QString& message);
+    void LogError(const QString& message);
+    virtual void add_setting_group(vktrace_SettingGroup* pGroup);
+    virtual unsigned int get_global_settings(vktrace_SettingGroup** ppGroups);
+    virtual int add_custom_state_viewer(QWidget* pWidget, const QString& title, bool bBringToFront = false);
+    virtual void remove_custom_state_viewer(int tabIndex);
+    virtual void enable_custom_state_viewer(QWidget* pWidget, bool bEnabled);
+    virtual QToolButton* add_toolbar_button(const QString& title, bool bEnabled);
+    virtual void add_calltree_contextmenu_item(QAction* pAction);
+    virtual void set_calltree_model(vktraceviewer_QTraceFileModel* pTraceFileModel, QAbstractProxyModel *pModel);
+    virtual void select_call_at_packet_index(unsigned long long packetIndex);
+    virtual void highlight_timeline_item(unsigned long long packetArrayIndex, bool bScrollTo, bool bSelect);
+    virtual void on_replay_state_changed(bool bReplayInProgress);
+    virtual unsigned long long get_current_packet_index();
+
+protected:
+    // re-implemented from QMainWindow
+    virtual void moveEvent(QMoveEvent *pEvent);
+    virtual void closeEvent(QCloseEvent *pEvent);
+    virtual void resizeEvent(QResizeEvent *pEvent);
+
+signals:
+    void LoadTraceFile(const QString& filename);
+
+public slots:
+
+private slots:
+    void on_action_Open_triggered();
+    void on_action_Close_triggered();
+    void on_actionE_xit_triggered();
+    void on_actionExport_API_Calls_triggered();
+    void on_actionEdit_triggered();
+
+    void on_settingsDialogResized(unsigned int width, unsigned int height);
+    void on_settingsSaved(vktrace_SettingGroup* pUpdatedSettings, unsigned int numGroups);
+
+    void onTraceFileLoaded(bool bSuccess, const vktraceviewer_trace_file_info& fileInfo, const QString& controllerFilename);
+
+    void on_treeView_clicked(const QModelIndex &index);
+    void slot_timeline_clicked(const QModelIndex &index);
+
+    void on_searchTextBox_textChanged(const QString &searchText);
+    void on_searchNextButton_clicked();
+    void on_searchPrevButton_clicked();
+    void on_prevDrawcallButton_clicked();
+    void on_nextDrawcallButton_clicked();
+
+    void on_searchTextBox_returnPressed();
+
+    void on_contextComboBox_currentIndexChanged(int index);
+
+    void prompt_generate_trace();
+
+    void OnOutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& message);
+    void OnOutputMessage(VktraceLogLevel level, const QString& message);
+
+    void on_hyperlinkClicked(const QUrl& link);
+
+private:
+    Ui::vktraceviewer *ui;
+    QWidget* m_pTraceStatsTab;
+    QGridLayout* m_pTraceStatsTabLayout;
+    QTextBrowser* m_pTraceStatsTabText;
+
+    QThread m_traceLoaderThread;
+    vktraceviewer_QSettingsDialog m_settingsDialog;
+
+    // Returns true if the user chose to load the file.
+    // Returns false if the user decided NOT to load the file.
+    bool prompt_load_new_trace(const QString &tracefile);
+
+    // Scan the trace file packets to extract API usage stats
+    void GenerateTraceFileStats();
+
+    void reset_tracefile_ui();
+
+    void selectApicallModelIndex(QModelIndex index, bool scrollTo, bool select);
+    QModelIndex mapTreeIndexToModel(const QModelIndex& treeIndex) const;
+    QModelIndex mapTreeIndexFromModel(const QModelIndex& modelIndex) const;
+
+    static float u64ToFloat(uint64_t value);
+    void build_timeline_model();
+
+    vktraceviewer_trace_file_info m_traceFileInfo;
+    vktraceviewer_QTraceFileModel* m_pTraceFileModel;
+    QAbstractProxyModel* m_pProxyModel;
+
+    vktraceviewer_controller_factory m_controllerFactory;
+    vktraceviewer_QController* m_pController;
+
+    QToolButton *m_pGenerateTraceButton;
+
+    vktraceviewer_QTimelineView* m_pTimeline;
+    vktraceviewer_QGenerateTraceDialog* m_pGenerateTraceDialog;
+
+    QColor m_searchTextboxBackgroundColor;
+    bool m_bDelayUpdateUIForContext;
+    bool m_bGeneratingTrace;
+};
+
+#endif // VKTRACEVIEWER_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer.ui b/vktrace/src/vktrace_viewer/vktraceviewer.ui
new file mode 100644
index 0000000..fedc983
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer.ui
@@ -0,0 +1,373 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>vktraceviewer</class>
+ <widget class="QMainWindow" name="vktraceviewer">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1025</width>
+    <height>768</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>VkTrace Viewer</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QGridLayout" name="gridLayout_3">
+    <item row="0" column="0">
+     <widget class="QSplitter" name="splitter_3">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>1</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <widget class="QSplitter" name="splitter_2">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+         <horstretch>0</horstretch>
+         <verstretch>5</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <widget class="QWidget" name="timelineLayoutWidget">
+        <layout class="QVBoxLayout" name="timelineLayout" stretch="0">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="sizeConstraint">
+          <enum>QLayout::SetDefaultConstraint</enum>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QGraphicsView" name="timelineViewPlaceholder"/>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QSplitter" name="splitter">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+          <horstretch>0</horstretch>
+          <verstretch>9</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <widget class="QWidget" name="traceNavigationLayoutWidget">
+         <layout class="QHBoxLayout" name="horizontalLayout" stretch="0">
+          <item>
+           <layout class="QVBoxLayout" name="traceNavigationLayout" stretch="0,0">
+            <item>
+             <layout class="QHBoxLayout" name="horizontalLayout_4">
+              <item>
+               <widget class="QPushButton" name="prevDrawcallButton">
+                <property name="maximumSize">
+                 <size>
+                  <width>65</width>
+                  <height>16777215</height>
+                 </size>
+                </property>
+                <property name="toolTip">
+                 <string>Jump to previous draw call</string>
+                </property>
+                <property name="text">
+                 <string>Prev DC</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QPushButton" name="nextDrawcallButton">
+                <property name="maximumSize">
+                 <size>
+                  <width>65</width>
+                  <height>16777215</height>
+                 </size>
+                </property>
+                <property name="toolTip">
+                 <string>Jump to next draw call</string>
+                </property>
+                <property name="text">
+                 <string>Next DC</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <spacer name="horizontalSpacer">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+              <item>
+               <widget class="QLineEdit" name="searchTextBox">
+                <property name="maximumSize">
+                 <size>
+                  <width>200</width>
+                  <height>16777215</height>
+                 </size>
+                </property>
+                <property name="placeholderText">
+                 <string>Search</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QPushButton" name="searchPrevButton">
+                <property name="maximumSize">
+                 <size>
+                  <width>40</width>
+                  <height>16777215</height>
+                 </size>
+                </property>
+                <property name="toolTip">
+                 <string>Jump to previous occurrence</string>
+                </property>
+                <property name="text">
+                 <string>Prev</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QPushButton" name="searchNextButton">
+                <property name="maximumSize">
+                 <size>
+                  <width>40</width>
+                  <height>16777215</height>
+                 </size>
+                </property>
+                <property name="toolTip">
+                 <string>Jump to next occurrence</string>
+                </property>
+                <property name="text">
+                 <string>Next</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+            <item>
+             <widget class="QTreeView" name="treeView">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="snapshotLayoutWidget">
+         <layout class="QVBoxLayout" name="snapshotLayout">
+          <item>
+           <widget class="QComboBox" name="contextComboBox"/>
+          </item>
+          <item>
+           <widget class="QTabWidget" name="stateTabWidget">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+              <horstretch>2</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="tabShape">
+             <enum>QTabWidget::Rounded</enum>
+            </property>
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <widget class="QWidget" name="stateTab">
+             <attribute name="title">
+              <string>State</string>
+             </attribute>
+             <layout class="QVBoxLayout" name="verticalLayout_3">
+              <item>
+               <widget class="QTreeView" name="stateTreeView">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                  <horstretch>0</horstretch>
+                  <verstretch>3</verstretch>
+                 </sizepolicy>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </widget>
+      </widget>
+      <widget class="QTabWidget" name="bottomTabWidget">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="baseSize">
+        <size>
+         <width>0</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="tabPosition">
+        <enum>QTabWidget::North</enum>
+       </property>
+       <property name="tabShape">
+        <enum>QTabWidget::Rounded</enum>
+       </property>
+       <property name="currentIndex">
+        <number>0</number>
+       </property>
+       <widget class="QWidget" name="outputTab">
+        <attribute name="title">
+         <string>Output</string>
+        </attribute>
+        <layout class="QGridLayout" name="gridLayout_2">
+         <item row="0" column="0">
+          <widget class="QTextBrowser" name="outputTextBrowser">
+           <property name="readOnly">
+            <bool>true</bool>
+           </property>
+           <property name="openLinks">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QWidget" name="machineInfoTab">
+        <attribute name="title">
+         <string>Machine Info</string>
+        </attribute>
+        <layout class="QGridLayout" name="gridLayout_4">
+         <item row="0" column="0">
+          <widget class="QTextBrowser" name="machineInfoText">
+           <property name="lineWrapMode">
+            <enum>QTextEdit::NoWrap</enum>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QWidget" name="callStackTab">
+        <attribute name="title">
+         <string>Call Stack</string>
+        </attribute>
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="0">
+          <widget class="QTextBrowser" name="backtraceText">
+           <property name="minimumSize">
+            <size>
+             <width>0</width>
+             <height>100</height>
+            </size>
+           </property>
+           <property name="baseSize">
+            <size>
+             <width>0</width>
+             <height>100</height>
+            </size>
+           </property>
+           <property name="lineWrapMode">
+            <enum>QTextEdit::NoWrap</enum>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1025</width>
+     <height>25</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>&amp;File</string>
+    </property>
+    <addaction name="action_Open"/>
+    <addaction name="action_Close"/>
+    <addaction name="separator"/>
+    <addaction name="actionExport_API_Calls"/>
+    <addaction name="separator"/>
+    <addaction name="actionE_xit"/>
+   </widget>
+   <widget class="QMenu" name="menuSettings">
+    <property name="title">
+     <string>Settings</string>
+    </property>
+    <addaction name="actionEdit"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuSettings"/>
+  </widget>
+  <widget class="QToolBar" name="mainToolBar">
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+  <action name="action_Open">
+   <property name="text">
+    <string>&amp;Open Trace...</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+O</string>
+   </property>
+  </action>
+  <action name="actionE_xit">
+   <property name="text">
+    <string>E&amp;xit</string>
+   </property>
+  </action>
+  <action name="action_Close">
+   <property name="text">
+    <string>Close Trace</string>
+   </property>
+  </action>
+  <action name="actionExport_API_Calls">
+   <property name="text">
+    <string>Export API Calls...</string>
+   </property>
+  </action>
+  <action name="actionEdit">
+   <property name="text">
+    <string>Edit...</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWidget.h b/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWidget.h
new file mode 100644
index 0000000..af047cd
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWidget.h
@@ -0,0 +1,262 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef _VKTRACEVIEWER_QREPLAYWIDGET_H_
+#define _VKTRACEVIEWER_QREPLAYWIDGET_H_
+
+#include <QWidget>
+#include <QThread>
+#include <QToolBar>
+#include <QToolButton>
+#include <QVBoxLayout>
+#include <QCheckBox>
+
+#include "vktraceviewer_QReplayWorker.h"
+
+class vktraceviewer_QReplayWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit vktraceviewer_QReplayWidget(vktraceviewer_QReplayWorker* pWorker, QWidget *parent = 0)
+        : QWidget(parent),
+          m_pWorker(pWorker)
+    {
+        QVBoxLayout* pLayout = new QVBoxLayout(this);
+        setLayout(pLayout);
+
+        m_pToolBar = new QToolBar("ReplayToolbar", this);
+        pLayout->addWidget(m_pToolBar);
+
+        m_pPlayButton = new QToolButton(m_pToolBar);
+        m_pPlayButton->setText("Play");
+        m_pPlayButton->setEnabled(true);
+        m_pToolBar->addWidget(m_pPlayButton);
+        connect(m_pPlayButton, SIGNAL(clicked()), this, SLOT(onPlayButtonClicked()));
+
+        m_pStepButton = new QToolButton(m_pToolBar);
+        m_pStepButton->setText("Step");
+        m_pStepButton->setEnabled(true);
+        m_pToolBar->addWidget(m_pStepButton);
+        connect(m_pStepButton, SIGNAL(clicked()), this, SLOT(onStepButtonClicked()));
+
+        m_pPauseButton = new QToolButton(m_pToolBar);
+        m_pPauseButton->setText("Pause");
+        m_pPauseButton->setEnabled(false);
+        m_pToolBar->addWidget(m_pPauseButton);
+        connect(m_pPauseButton, SIGNAL(clicked()), this, SLOT(onPauseButtonClicked()));
+
+        m_pContinueButton = new QToolButton(m_pToolBar);
+        m_pContinueButton->setText("Continue");
+        m_pContinueButton->setEnabled(false);
+        m_pToolBar->addWidget(m_pContinueButton);
+        connect(m_pContinueButton, SIGNAL(clicked()), this, SLOT(onContinueButtonClicked()));
+
+        m_pStopButton = new QToolButton(m_pToolBar);
+        m_pStopButton->setText("Stop");
+        m_pStopButton->setEnabled(false);
+        m_pToolBar->addWidget(m_pStopButton);
+        connect(m_pStopButton, SIGNAL(clicked()), this, SLOT(onStopButtonClicked()));
+
+        m_pDetachCheckBox = new QCheckBox(m_pToolBar);
+        m_pDetachCheckBox->setText("Detach");
+        m_pDetachCheckBox->setEnabled(true);
+        m_pToolBar->addWidget(m_pDetachCheckBox);
+        connect(m_pDetachCheckBox, SIGNAL(clicked(bool)), this, SLOT(onDetachCheckBoxClicked(bool)));
+
+        m_pReplayWindow = new QWidget(this);
+        pLayout->addWidget(m_pReplayWindow);
+
+        // connect worker signals to widget actions
+        qRegisterMetaType<uint64_t>("uint64_t");
+        m_replayThread.setObjectName("ReplayThread");
+        m_pWorker->moveToThread(&m_replayThread);
+        m_replayThread.start();
+
+        // Clicking the Pause and Stop buttons are direct connections so that they happen more immediately than a queued connection.
+        // Queued connections are used here whenever the replay will be advanced from a stopped state,
+        // and for all the signals FROM the worker object since it is on a different thread.
+        connect(this, SIGNAL(PlayButtonClicked()), m_pWorker, SLOT(StartReplay()), Qt::QueuedConnection);
+        connect(this, SIGNAL(StepButtonClicked()), m_pWorker, SLOT(StepReplay()), Qt::QueuedConnection);
+        connect(this, SIGNAL(PauseButtonClicked()), m_pWorker, SLOT(PauseReplay()), Qt::DirectConnection);
+        connect(this, SIGNAL(ContinueButtonClicked()), m_pWorker, SLOT(ContinueReplay()), Qt::QueuedConnection);
+        connect(this, SIGNAL(StopButtonClicked()), m_pWorker, SLOT(StopReplay()), Qt::DirectConnection);
+        connect(this, SIGNAL(DetachCheckBoxClicked(bool)), m_pWorker, SLOT(DetachReplay(bool)), Qt::QueuedConnection);
+
+        connect(m_pWorker, SIGNAL(ReplayStarted()), this, SLOT(slotReplayStarted()), Qt::QueuedConnection);
+        connect(m_pWorker, SIGNAL(ReplayPaused(uint64_t)), this, SLOT(slotReplayPaused(uint64_t)), Qt::QueuedConnection);
+        connect(m_pWorker, SIGNAL(ReplayContinued()), this, SLOT(slotReplayContinued()), Qt::QueuedConnection);
+        connect(m_pWorker, SIGNAL(ReplayStopped(uint64_t)), this, SLOT(slotReplayStopped(uint64_t)), Qt::QueuedConnection);
+        connect(m_pWorker, SIGNAL(ReplayFinished(uint64_t)), this, SLOT(slotReplayFinished(uint64_t)), Qt::QueuedConnection);
+        connect(m_pWorker, SIGNAL(ReplayProgressUpdate(uint64_t)), this, SIGNAL(ReplayProgressUpdate(uint64_t)), Qt::QueuedConnection);
+
+        connect(m_pWorker, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)), this, SLOT(OnOutputMessage(VktraceLogLevel, uint64_t, const QString&)), Qt::QueuedConnection);
+    }
+
+    virtual ~vktraceviewer_QReplayWidget()
+    {
+        m_replayThread.quit();
+        m_replayThread.wait();
+    }
+
+    virtual QPaintEngine* paintEngine() const
+    {
+        return NULL;
+    }
+
+    QWidget* GetReplayWindow() const
+    {
+        return m_pReplayWindow;
+    }
+
+signals:
+    void PlayButtonClicked();
+    void StepButtonClicked();
+    void PauseButtonClicked();
+    void ContinueButtonClicked();
+    void StopButtonClicked();
+    void DetachCheckBoxClicked(bool checked);
+
+    void ReplayStarted();
+    void ReplayPaused(uint64_t packetIndex);
+    void ReplayContinued();
+    void ReplayStopped(uint64_t packetIndex);
+    void ReplayFinished(uint64_t packetIndex);
+    void ReplayProgressUpdate(uint64_t packetIndex);
+
+    void OutputMessage(VktraceLogLevel level, const QString& msg);
+    void OutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& msg);
+
+private slots:
+
+    void slotReplayStarted()
+    {
+        m_pPlayButton->setEnabled(false);
+        m_pStepButton->setEnabled(false);
+        m_pPauseButton->setEnabled(true);
+        m_pContinueButton->setEnabled(false);
+        m_pStopButton->setEnabled(true);
+        m_pDetachCheckBox->setEnabled(false);
+
+        emit ReplayStarted();
+    }
+
+    void slotReplayPaused(uint64_t packetIndex)
+    {
+        m_pPlayButton->setEnabled(false);
+        m_pStepButton->setEnabled(true);
+        m_pPauseButton->setEnabled(false);
+        m_pContinueButton->setEnabled(true);
+        m_pStopButton->setEnabled(true);
+        m_pDetachCheckBox->setEnabled(false);
+
+        emit ReplayPaused(packetIndex);
+    }
+
+    void slotReplayContinued()
+    {
+        m_pPlayButton->setEnabled(false);
+        m_pStepButton->setEnabled(false);
+        m_pPauseButton->setEnabled(true);
+        m_pContinueButton->setEnabled(false);
+        m_pStopButton->setEnabled(true);
+        m_pDetachCheckBox->setEnabled(false);
+
+        emit ReplayContinued();
+    }
+
+    void slotReplayStopped(uint64_t packetIndex)
+    {
+        m_pPlayButton->setEnabled(true);
+        m_pStepButton->setEnabled(true);
+        m_pPauseButton->setEnabled(false);
+        m_pContinueButton->setEnabled(false);
+        m_pStopButton->setEnabled(false);
+        m_pDetachCheckBox->setEnabled(true);
+
+        emit ReplayStopped(packetIndex);
+    }
+
+    void slotReplayFinished(uint64_t packetIndex)
+    {
+        m_pPlayButton->setEnabled(true);
+        m_pStepButton->setEnabled(true);
+        m_pPauseButton->setEnabled(false);
+        m_pContinueButton->setEnabled(false);
+        m_pStopButton->setEnabled(false);
+        m_pDetachCheckBox->setEnabled(true);
+
+        emit ReplayFinished(packetIndex);
+    }
+
+    void OnOutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& msg)
+    {
+        emit OutputMessage(level, packetIndex, msg);
+    }
+
+public slots:
+    void onPlayButtonClicked()
+    {
+        emit PlayButtonClicked();
+    }
+
+    void onStepButtonClicked()
+    {
+        emit StepButtonClicked();
+    }
+
+    void onPauseButtonClicked()
+    {
+        m_pPlayButton->setEnabled(false);
+        m_pPauseButton->setEnabled(false);
+        m_pContinueButton->setEnabled(false);
+        m_pStopButton->setEnabled(false);
+
+        emit PauseButtonClicked();
+    }
+
+    void onContinueButtonClicked()
+    {
+        emit ContinueButtonClicked();
+    }
+
+    void onStopButtonClicked()
+    {
+        emit StopButtonClicked();
+    }
+
+    void onDetachCheckBoxClicked(bool checked)
+    {
+        emit DetachCheckBoxClicked(checked);
+    }
+
+private:
+    vktraceviewer_QReplayWorker* m_pWorker;
+    QWidget* m_pReplayWindow;
+    QToolBar* m_pToolBar;
+    QToolButton* m_pPlayButton;
+    QToolButton* m_pStepButton;
+    QToolButton* m_pPauseButton;
+    QToolButton* m_pContinueButton;
+    QToolButton* m_pStopButton;
+    QCheckBox* m_pDetachCheckBox;
+    QThread m_replayThread;
+};
+
+#endif //_VKTRACEVIEWER_QREPLAYWIDGET_H_
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWorker.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWorker.cpp
new file mode 100644
index 0000000..bc2fcf9
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWorker.cpp
@@ -0,0 +1,580 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include "vktraceviewer_QReplayWorker.h"
+#include <QAction>
+#include <QCoreApplication>
+#include "vktraceviewer_trace_file_utils.h"
+
+vktraceviewer_QReplayWorker* g_pWorker;
+static uint64_t s_currentReplayPacket = 0;
+
+static void dbg_msg_callback(vktrace_replay::VKTRACE_DBG_MSG_TYPE msgType, uint64_t packetIndex, const char* pMsg);
+
+void replayWorkerLoggingCallback(VktraceLogLevel level, const char* pMessage)
+{
+    if (g_pWorker != NULL)
+    {
+        switch(level)
+        {
+        case VKTRACE_LOG_DEBUG:
+            dbg_msg_callback(vktrace_replay::VKTRACE_DBG_MSG_INFO, s_currentReplayPacket, pMessage);
+            break;
+        case VKTRACE_LOG_ERROR:
+            dbg_msg_callback(vktrace_replay::VKTRACE_DBG_MSG_ERROR, s_currentReplayPacket, pMessage);
+            break;
+        case VKTRACE_LOG_WARNING:
+            dbg_msg_callback(vktrace_replay::VKTRACE_DBG_MSG_WARNING, s_currentReplayPacket, pMessage);
+            break;
+        case VKTRACE_LOG_VERBOSE:
+            dbg_msg_callback(vktrace_replay::VKTRACE_DBG_MSG_INFO, s_currentReplayPacket, pMessage);
+            break;
+        default:
+            break;
+        }
+    }
+
+#if defined(WIN32)
+#if _DEBUG
+    OutputDebugString(pMessage);
+#endif
+#endif
+}
+
+vktraceviewer_QReplayWorker::vktraceviewer_QReplayWorker()
+    : QObject(NULL),
+      m_bPauseReplay(false),
+      m_bStopReplay(false),
+      m_bReplayInProgress(false),
+      m_pView(NULL),
+      m_pTraceFileInfo(NULL),
+      m_currentReplayPacketIndex(0),
+      m_pActionRunToHere(NULL),
+      m_pauseAtPacketIndex((uint64_t)-1),
+      m_pReplayWindow(NULL),
+      m_pReplayWindowWidth(0),
+      m_pReplayWindowHeight(0),
+      m_bPrintReplayInfoMessages(TRUE),
+      m_bPrintReplayWarningMessages(TRUE),
+      m_bPrintReplayErrorMessages(TRUE),
+      m_bPauseOnReplayInfoMessages(FALSE),
+      m_bPauseOnReplayWarningMessages(FALSE),
+      m_bPauseOnReplayErrorMessages(FALSE)
+{
+    memset(m_pReplayers, 0, sizeof(vktrace_replay::vktrace_trace_packet_replay_library*) * VKTRACE_MAX_TRACER_ID_ARRAY_SIZE);
+    g_pWorker = this;
+}
+
+vktraceviewer_QReplayWorker::~vktraceviewer_QReplayWorker()
+{
+    setView(NULL);
+    g_pWorker = NULL;
+
+    if (m_pActionRunToHere != NULL)
+    {
+        disconnect(m_pActionRunToHere, SIGNAL(triggered()), this, SLOT(onPlayToHere()));
+        delete m_pActionRunToHere;
+        m_pActionRunToHere = NULL;
+    }
+}
+
+void vktraceviewer_QReplayWorker::setPrintReplayMessages(BOOL bPrintInfo, BOOL bPrintWarning, BOOL bPrintError)
+{
+    m_bPrintReplayInfoMessages = bPrintInfo;
+    m_bPrintReplayWarningMessages = bPrintWarning;
+    m_bPrintReplayErrorMessages = bPrintError;
+}
+
+void vktraceviewer_QReplayWorker::setPauseOnReplayMessages(BOOL bPauseOnInfo, BOOL bPauseOnWarning, BOOL bPauseOnError)
+{
+    m_bPauseOnReplayInfoMessages = bPauseOnInfo;
+    m_bPauseOnReplayWarningMessages = bPauseOnWarning;
+    m_bPauseOnReplayErrorMessages = bPauseOnError;
+}
+
+BOOL vktraceviewer_QReplayWorker::PrintReplayInfoMsgs()
+{
+    return m_bPrintReplayInfoMessages;
+}
+
+BOOL vktraceviewer_QReplayWorker::PrintReplayWarningMsgs()
+{
+    return m_bPrintReplayWarningMessages;
+}
+
+BOOL vktraceviewer_QReplayWorker::PrintReplayErrorMsgs()
+{
+    return m_bPrintReplayErrorMessages;
+}
+
+BOOL vktraceviewer_QReplayWorker::PauseOnReplayInfoMsg()
+{
+    return m_bPauseOnReplayInfoMessages;
+}
+
+BOOL vktraceviewer_QReplayWorker::PauseOnReplayWarningMsg()
+{
+    return m_bPauseOnReplayWarningMessages;
+}
+
+BOOL vktraceviewer_QReplayWorker::PauseOnReplayErrorMsg()
+{
+    return m_bPauseOnReplayErrorMessages;
+}
+
+void vktraceviewer_QReplayWorker::setView(vktraceviewer_view* pView)
+{
+    m_pView = pView;
+}
+
+bool vktraceviewer_QReplayWorker::load_replayers(vktraceviewer_trace_file_info* pTraceFileInfo,
+    QWidget* pReplayWindow, int const replayWindowWidth,
+    int const replayWindowHeight, bool const separateReplayWindow)
+{
+    // Get window handle of the widget to replay into.
+    assert(pReplayWindow != NULL);
+    assert(replayWindowWidth > 0);
+    assert(replayWindowHeight > 0);
+
+    m_pReplayWindow = pReplayWindow;
+    m_pReplayWindowWidth = replayWindowWidth;
+    m_pReplayWindowHeight = replayWindowHeight;
+
+    m_pTraceFileInfo = pTraceFileInfo;
+
+    // TODO: Get the width and height from the replayer. We can't do this yet
+    // because the replayer doesn't know the render target's size.
+
+    WId hWindow = pReplayWindow->winId();
+
+    // load any API specific driver libraries and init replayer objects
+    uint8_t tidApi = VKTRACE_TID_RESERVED;
+    bool bReplayerLoaded = false;
+
+    vktrace_replay::ReplayDisplay disp;
+    if(separateReplayWindow)
+    {
+        disp = vktrace_replay::ReplayDisplay(replayWindowWidth, replayWindowHeight, 0, false);
+    }
+    else
+    {
+        disp = vktrace_replay::ReplayDisplay((vktrace_window_handle)hWindow, replayWindowWidth, replayWindowHeight);
+    }
+
+    for (int i = 0; i < VKTRACE_MAX_TRACER_ID_ARRAY_SIZE; i++)
+    {
+        m_pReplayers[i] = NULL;
+    }
+
+    for (int i = 0; i < pTraceFileInfo->header.tracer_count; i++)
+    {
+        uint8_t tracerId = pTraceFileInfo->header.tracer_id_array[i].id;
+        tidApi = tracerId;
+
+        const VKTRACE_TRACER_REPLAYER_INFO* pReplayerInfo = &(gs_tracerReplayerInfo[tracerId]);
+
+        if (pReplayerInfo->tracerId != tracerId)
+        {
+            emit OutputMessage(VKTRACE_LOG_ERROR, QString("Replayer info for TracerId (%1) failed consistency check.").arg(tracerId));
+            assert(!"TracerId in VKTRACE_TRACER_REPLAYER_INFO does not match the requested tracerId. The array needs to be corrected.");
+        }
+        else if (pReplayerInfo->needsReplayer == TRUE)
+        {
+            // Have our factory create the necessary replayer
+            m_pReplayers[tracerId] = m_replayerFactory.Create(tracerId);
+
+            if (m_pReplayers[tracerId] == NULL)
+            {
+                // replayer failed to be created
+                emit OutputMessage(VKTRACE_LOG_ERROR, QString("Couldn't create replayer for TracerId %1.").arg(tracerId));
+                bReplayerLoaded = false;
+            }
+            else
+            {
+                m_pReplayers[tracerId]->SetLogCallback(replayWorkerLoggingCallback);
+                m_pReplayers[tracerId]->SetLogLevel(VKTRACE_LOG_ERROR);
+                m_pReplayers[tracerId]->RegisterDbgMsgCallback((vktrace_replay::VKTRACE_DBG_MSG_CALLBACK_FUNCTION)&dbg_msg_callback);
+
+                // get settings from the replayer
+                m_pView->add_setting_group(m_pReplayers[tracerId]->GetSettings());
+
+                // update replayer with updated state
+                vktrace_SettingGroup* pGlobalSettings = NULL;
+                unsigned int numGlobalSettings = m_pView->get_global_settings(&pGlobalSettings);
+                m_pReplayers[tracerId]->UpdateFromSettings(pGlobalSettings, numGlobalSettings);
+
+                // Initialize the replayer
+                int err = m_pReplayers[tracerId]->Initialize(&disp, NULL);
+                if (err) {
+                    emit OutputMessage(VKTRACE_LOG_ERROR, QString("Couldn't Initialize replayer for TracerId %1.").arg(tracerId));
+                    return false;
+                }
+
+                bReplayerLoaded = true;
+            }
+        }
+    }
+
+    if (tidApi == VKTRACE_TID_RESERVED)
+    {
+        emit OutputMessage(VKTRACE_LOG_ERROR, QString("No API specified in tracefile for replaying."));
+        return false;
+    }
+
+    if (bReplayerLoaded)
+    {
+        m_pActionRunToHere = new QAction("Play to here", NULL);
+        connect(m_pActionRunToHere, SIGNAL(triggered()), this, SLOT(onPlayToHere()));
+        m_pView->add_calltree_contextmenu_item(m_pActionRunToHere);
+    }
+
+    return bReplayerLoaded;
+}
+
+void vktraceviewer_QReplayWorker::unloadReplayers()
+{
+    m_pTraceFileInfo = NULL;
+
+    // Clean up replayers
+    if (m_pReplayers != NULL)
+    {
+        for (int i = 0; i < VKTRACE_MAX_TRACER_ID_ARRAY_SIZE; i++)
+        {
+            if (m_pReplayers[i] != NULL)
+            {
+                m_pReplayers[i]->Deinitialize();
+                m_replayerFactory.Destroy(&m_pReplayers[i]);
+            }
+        }
+    }
+}
+
+void vktraceviewer_QReplayWorker::playCurrentTraceFile(uint64_t startPacketIndex)
+{
+    vktraceviewer_trace_file_info* pTraceFileInfo = m_pTraceFileInfo;
+    vktraceviewer_trace_file_packet_offsets* pCurPacket = NULL;
+    unsigned int res = vktrace_replay::VKTRACE_REPLAY_ERROR;
+    vktrace_replay::vktrace_trace_packet_replay_library *replayer;
+
+    m_bReplayInProgress = true;
+
+    for (uint64_t i = startPacketIndex; i < pTraceFileInfo->packetCount; i++)
+    {
+        m_currentReplayPacketIndex = i;
+        emit ReplayProgressUpdate(m_currentReplayPacketIndex);
+
+        pCurPacket = &pTraceFileInfo->pPacketOffsets[i];
+        s_currentReplayPacket = pCurPacket->pHeader->global_packet_index;
+        switch (pCurPacket->pHeader->packet_id) {
+            case VKTRACE_TPI_MESSAGE:
+            {
+                vktrace_trace_packet_message* msgPacket;
+                msgPacket = (vktrace_trace_packet_message*)pCurPacket->pHeader;
+                replayWorkerLoggingCallback(msgPacket->type, msgPacket->message);
+                break;
+            }
+            case VKTRACE_TPI_MARKER_CHECKPOINT:
+                break;
+            case VKTRACE_TPI_MARKER_API_BOUNDARY:
+                break;
+            case VKTRACE_TPI_MARKER_API_GROUP_BEGIN:
+                break;
+            case VKTRACE_TPI_MARKER_API_GROUP_END:
+                break;
+            case VKTRACE_TPI_MARKER_TERMINATE_PROCESS:
+                break;
+            //TODO processing code for all the above cases
+            default:
+            {
+                if (pCurPacket->pHeader->tracer_id >= VKTRACE_MAX_TRACER_ID_ARRAY_SIZE  || pCurPacket->pHeader->tracer_id == VKTRACE_TID_RESERVED)
+                {
+                    replayWorkerLoggingCallback(VKTRACE_LOG_WARNING, QString("Tracer_id from packet num packet %1 invalid.").arg(pCurPacket->pHeader->packet_id).toStdString().c_str());
+                    continue;
+                }
+                replayer = m_pReplayers[pCurPacket->pHeader->tracer_id];
+                if (replayer == NULL) {
+                    replayWorkerLoggingCallback(VKTRACE_LOG_WARNING, QString("Tracer_id %1 has no valid replayer.").arg(pCurPacket->pHeader->tracer_id).toStdString().c_str());
+                    continue;
+                }
+                if (pCurPacket->pHeader->packet_id >= VKTRACE_TPI_BEGIN_API_HERE)
+                {
+                    // replay the API packet
+                    try
+                    {
+                        res = replayer->Replay(pCurPacket->pHeader);
+                    }
+                    catch (std::exception& e)
+                    {
+                        replayWorkerLoggingCallback(VKTRACE_LOG_ERROR, QString("Caught std::exception while replaying packet %1: %2").arg(pCurPacket->pHeader->global_packet_index).arg(e.what()).toStdString().c_str());
+                    }
+                    catch (int i)
+                    {
+                        replayWorkerLoggingCallback(VKTRACE_LOG_ERROR, QString("Caught int exception: %1").arg(i).toStdString().c_str());
+                    }
+                    catch (...)
+                    {
+                        replayWorkerLoggingCallback(VKTRACE_LOG_ERROR, "Caught unknown exception.");
+                    }
+
+                    if (res == vktrace_replay::VKTRACE_REPLAY_ERROR ||
+                        res == vktrace_replay::VKTRACE_REPLAY_INVALID_ID ||
+                        res == vktrace_replay::VKTRACE_REPLAY_CALL_ERROR)
+                    {
+                        replayWorkerLoggingCallback(VKTRACE_LOG_ERROR, QString("Failed to replay packet %1.").arg(pCurPacket->pHeader->global_packet_index).toStdString().c_str());
+                    }
+                    else if (res == vktrace_replay::VKTRACE_REPLAY_BAD_RETURN)
+                    {
+                        replayWorkerLoggingCallback(VKTRACE_LOG_WARNING, QString("Replay of packet %1 has diverged from trace due to a different return value.").arg(pCurPacket->pHeader->global_packet_index).toStdString().c_str());
+                    }
+                    else if (res == vktrace_replay::VKTRACE_REPLAY_INVALID_PARAMS ||
+                             res == vktrace_replay::VKTRACE_REPLAY_VALIDATION_ERROR)
+                    {
+                        // validation layer should have reported these if the user wanted them, so don't print any additional warnings here.
+                    }
+                    else if (res != vktrace_replay::VKTRACE_REPLAY_SUCCESS)
+                    {
+                        replayWorkerLoggingCallback(VKTRACE_LOG_ERROR, QString("Unknown error caused by packet %1.").arg(pCurPacket->pHeader->global_packet_index).toStdString().c_str());
+                    }
+                }
+                else
+                {
+                    replayWorkerLoggingCallback(VKTRACE_LOG_ERROR, QString("Bad packet type id=%1, index=%2.").arg(pCurPacket->pHeader->packet_id).arg(pCurPacket->pHeader->global_packet_index).toStdString().c_str());
+                }
+            }
+        }
+
+        // Process events and pause or stop if needed
+        if (m_bPauseReplay || m_pauseAtPacketIndex == pCurPacket->pHeader->global_packet_index)
+        {
+            if (m_pauseAtPacketIndex == pCurPacket->pHeader->global_packet_index)
+            {
+                // reset
+                m_pauseAtPacketIndex = -1;
+            }
+
+            m_bReplayInProgress = false;
+            doReplayPaused(pCurPacket->pHeader->global_packet_index);
+            return;
+        }
+
+        if (m_bStopReplay)
+        {
+            m_bReplayInProgress = false;
+            doReplayStopped(pCurPacket->pHeader->global_packet_index);
+            return;
+        }
+    }
+
+    m_bReplayInProgress = false;
+    doReplayFinished(pCurPacket->pHeader->global_packet_index);
+}
+
+void vktraceviewer_QReplayWorker::onPlayToHere()
+{
+    m_pauseAtPacketIndex = m_pView->get_current_packet_index();
+    if (m_pauseAtPacketIndex <= m_currentReplayPacketIndex || m_currentReplayPacketIndex == 0)
+    {
+        // pause location is behind the current replay position, so restart the replay.
+        StartReplay();
+    }
+    else
+    {
+        // pause location is ahead of current replay position, so continue the replay.
+        ContinueReplay();
+    }
+}
+
+
+void vktraceviewer_QReplayWorker::StartReplay()
+{
+    // Starting the replay can happen immediately.
+    emit ReplayStarted();
+
+    // Reset some flags and play the replay from the beginning
+    m_bPauseReplay = false;
+    m_bStopReplay = false;
+    playCurrentTraceFile(0);
+}
+
+void vktraceviewer_QReplayWorker::StepReplay()
+{
+    // Stepping the replay can happen immediately.
+    emit ReplayContinued();
+
+    // Set the pause flag so that the replay will stop after replaying the next packet.
+    m_bPauseReplay = true;
+    m_bStopReplay = false;
+    playCurrentTraceFile(m_currentReplayPacketIndex+1);
+}
+
+void vktraceviewer_QReplayWorker::PauseReplay()
+{
+    // Pausing the replay happens asyncronously.
+    // So set the pause flag and the replay will
+    // react to it as soon as it can. It will call
+    // doReplayPaused() when it has paused.
+    m_bPauseReplay = true;
+}
+
+void vktraceviewer_QReplayWorker::ContinueReplay()
+{
+    // Continuing the replay can happen immediately.
+    emit ReplayContinued();
+
+    // clear the pause and stop flags and continue the replay from the next packet
+    m_bPauseReplay = false;
+    m_bStopReplay = false;
+    playCurrentTraceFile(m_currentReplayPacketIndex+1);
+}
+
+void vktraceviewer_QReplayWorker::StopReplay()
+{
+    if (m_bReplayInProgress)
+    {
+        // If a replay is in progress, then
+        // Stopping the replay happens asycnronously.
+        // Set the stop flag and the replay will
+        // react to it as soon as it can. It will call
+        // doReplayStopped() when it has stopped.
+        m_bStopReplay = true;
+    }
+    else
+    {
+        // Replay is not in progress means:
+        // 1) replay wasn't started (in which case stop button should be disabled and we can't get to this point),
+        // 2) replay is currently paused, so do same actions as if the replay detected that it should stop.
+        uint64_t packetIndex = this->m_pTraceFileInfo->pPacketOffsets[m_currentReplayPacketIndex].pHeader->global_packet_index;
+        doReplayStopped(packetIndex);
+    }
+}
+
+void vktraceviewer_QReplayWorker::onSettingsUpdated(vktrace_SettingGroup* pGroups, unsigned int numGroups)
+{
+    if (m_pReplayers != NULL)
+    {
+        for (unsigned int tracerId = 0; tracerId < VKTRACE_MAX_TRACER_ID_ARRAY_SIZE; tracerId++)
+        {
+            if (m_pReplayers[tracerId] != NULL)
+            {
+                // now update the replayer with the loaded settings
+                m_pReplayers[tracerId]->UpdateFromSettings(pGroups, numGroups);
+            }
+        }
+    }
+}
+
+vktrace_replay::vktrace_trace_packet_replay_library* vktraceviewer_QReplayWorker::getReplayer(VKTRACE_TRACER_ID tracerId)
+{
+    if (tracerId < 0 || tracerId >= VKTRACE_MAX_TRACER_ID_ARRAY_SIZE)
+    {
+        return NULL;
+    }
+
+    return m_pReplayers[tracerId];
+}
+
+void vktraceviewer_QReplayWorker::DetachReplay(bool detach)
+{
+    for (int i = 0; i < VKTRACE_MAX_TRACER_ID_ARRAY_SIZE; i++)
+    {
+        if(m_pReplayers[i] != NULL)
+        {
+            m_pReplayers[i]->Deinitialize();
+
+            vktrace_replay::ReplayDisplay disp;
+            if(detach)
+            {
+                disp = vktrace_replay::ReplayDisplay(m_pReplayWindowWidth, m_pReplayWindowHeight, 0, false);
+            }
+            else
+            {
+                WId hWindow = m_pReplayWindow->winId();
+                disp = vktrace_replay::ReplayDisplay((vktrace_window_handle)hWindow, m_pReplayWindowWidth, m_pReplayWindowHeight);
+            }
+
+            int err = m_pReplayers[i]->Initialize(&disp, NULL);
+            assert(err == 0);
+        }
+    }
+}
+
+void vktraceviewer_QReplayWorker::doReplayPaused(uint64_t packetIndex)
+{
+    emit ReplayPaused(packetIndex);
+}
+
+void vktraceviewer_QReplayWorker::doReplayStopped(uint64_t packetIndex)
+{
+    emit ReplayStopped(packetIndex);
+
+    // Replay will start again from the beginning, so setup for that now.
+    m_currentReplayPacketIndex = 0;
+}
+
+void vktraceviewer_QReplayWorker::doReplayFinished(uint64_t packetIndex)
+{
+    // Indicate that the replay finished at the particular packet.
+    emit ReplayFinished(packetIndex);
+
+    // Replay will start again from the beginning, so setup for that now.
+    m_currentReplayPacketIndex = 0;
+}
+
+//=============================================================================
+void dbg_msg_callback(vktrace_replay::VKTRACE_DBG_MSG_TYPE msgType, uint64_t packetIndex, const char* pMsg)
+{
+    if (g_pWorker != NULL)
+    {
+        if (msgType == vktrace_replay::VKTRACE_DBG_MSG_ERROR)
+        {
+            if (g_pWorker->PrintReplayErrorMsgs())
+            {
+                g_pWorker->OutputMessage(VKTRACE_LOG_ERROR, packetIndex, QString(pMsg));
+            }
+            if (g_pWorker->PauseOnReplayErrorMsg())
+            {
+                g_pWorker->PauseReplay();
+            }
+        }
+        else if (msgType == vktrace_replay::VKTRACE_DBG_MSG_WARNING)
+        {
+            if (g_pWorker->PrintReplayWarningMsgs())
+            {
+                g_pWorker->OutputMessage(VKTRACE_LOG_WARNING, packetIndex, QString(pMsg));
+            }
+            if (g_pWorker->PauseOnReplayWarningMsg())
+            {
+                g_pWorker->PauseReplay();
+            }
+        }
+        else
+        {
+            if (g_pWorker->PrintReplayInfoMsgs())
+            {
+                g_pWorker->OutputMessage(VKTRACE_LOG_VERBOSE, packetIndex, QString(pMsg));
+            }
+            if (g_pWorker->PauseOnReplayInfoMsg())
+            {
+                g_pWorker->PauseReplay();
+            }
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWorker.h b/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWorker.h
new file mode 100644
index 0000000..81d64e7
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_QReplayWorker.h
@@ -0,0 +1,113 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_QREPLAYWORKER_H
+#define VKTRACEVIEWER_QREPLAYWORKER_H
+
+#include <QObject>
+#include "vktraceviewer_view.h"
+#include "vkreplay_factory.h"
+
+class vktraceviewer_QReplayWorker : public QObject
+{
+    Q_OBJECT
+public:
+    vktraceviewer_QReplayWorker();
+    virtual ~vktraceviewer_QReplayWorker();
+
+    void setPrintReplayMessages(BOOL bPrintInfo, BOOL bPrintWarning, BOOL bPrintError);
+    void setPauseOnReplayMessages(BOOL bPauseOnInfo, BOOL bPauseOnWarning, BOOL bPauseOnError);
+
+    BOOL PrintReplayInfoMsgs();
+    BOOL PrintReplayWarningMsgs();
+    BOOL PrintReplayErrorMsgs();
+
+    BOOL PauseOnReplayInfoMsg();
+    BOOL PauseOnReplayWarningMsg();
+    BOOL PauseOnReplayErrorMsg();
+
+    void setView(vktraceviewer_view* pView);
+
+    bool load_replayers(vktraceviewer_trace_file_info* pTraceFileInfo,
+        QWidget* pReplayWindow, int const replayWindowWidth,
+        int const replayWindowHeight, bool const separateReplayWindow);
+
+    void unloadReplayers();
+
+protected slots:
+    virtual void playCurrentTraceFile(uint64_t startPacketIndex);
+    virtual void onPlayToHere();
+
+public slots:
+    void StartReplay();
+    void StepReplay();
+    void PauseReplay();
+    void ContinueReplay();
+    void StopReplay();
+
+    void onSettingsUpdated(vktrace_SettingGroup* pGroups, unsigned int numGroups);
+
+    vktrace_replay::vktrace_trace_packet_replay_library* getReplayer(VKTRACE_TRACER_ID tracerId);
+
+    void DetachReplay(bool detach);
+
+signals:
+    void ReplayStarted();
+    void ReplayPaused(uint64_t packetIndex);
+    void ReplayContinued();
+    void ReplayStopped(uint64_t packetIndex);
+    void ReplayFinished(uint64_t packetIndex);
+
+    void ReplayProgressUpdate(uint64_t packetIndex);
+
+    void OutputMessage(VktraceLogLevel level, const QString& msg);
+    void OutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& msg);
+
+private:
+    volatile bool m_bPauseReplay;
+    volatile bool m_bStopReplay;
+    volatile bool m_bReplayInProgress;
+    vktraceviewer_view* m_pView;
+    vktraceviewer_trace_file_info* m_pTraceFileInfo;
+    uint64_t m_currentReplayPacketIndex;
+    QAction* m_pActionRunToHere;
+    uint64_t m_pauseAtPacketIndex;
+
+    QWidget* m_pReplayWindow;
+    int m_pReplayWindowWidth;
+    int m_pReplayWindowHeight;
+
+    BOOL m_bPrintReplayInfoMessages;
+    BOOL m_bPrintReplayWarningMessages;
+    BOOL m_bPrintReplayErrorMessages;
+
+    BOOL m_bPauseOnReplayInfoMessages;
+    BOOL m_bPauseOnReplayWarningMessages;
+    BOOL m_bPauseOnReplayErrorMessages;
+
+    vktrace_replay::ReplayFactory m_replayerFactory;
+    vktrace_replay::vktrace_trace_packet_replay_library* m_pReplayers[VKTRACE_MAX_TRACER_ID_ARRAY_SIZE];
+
+    void doReplayPaused(uint64_t packetIndex);
+    void doReplayStopped(uint64_t packetIndex);
+    void doReplayFinished(uint64_t packetIndex);
+};
+
+#endif // VKTRACEVIEWER_QREPLAYWORKER_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_QTraceFileModel.h b/vktrace/src/vktrace_viewer/vktraceviewer_QTraceFileModel.h
new file mode 100644
index 0000000..83dd170
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_QTraceFileModel.h
@@ -0,0 +1,303 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#pragma once
+
+#include <QColor>
+#include <QFont>
+#include <QSize>
+#include <qabstractitemmodel.h>
+#include "vktraceviewer_trace_file_utils.h"
+
+class vktraceviewer_QTraceFileModel : public QAbstractItemModel
+{
+    Q_OBJECT
+public:
+    vktraceviewer_QTraceFileModel(QObject* parent, vktraceviewer_trace_file_info* pTraceFileInfo)
+        : QAbstractItemModel(parent)
+    {
+        m_pTraceFileInfo = pTraceFileInfo;
+    }
+
+    virtual ~vktraceviewer_QTraceFileModel()
+    {
+    }
+
+    virtual bool isDrawCall(const VKTRACE_TRACE_PACKET_ID packetId) const
+    {
+        return false;
+    }
+
+    virtual QString get_packet_string(const vktrace_trace_packet_header* pHeader) const
+    {
+        switch (pHeader->packet_id)
+        {
+            case VKTRACE_TPI_MESSAGE:
+            {
+                vktrace_trace_packet_message* pPacket = (vktrace_trace_packet_message*)pHeader->pBody;
+                return QString(pPacket->message);
+            }
+            case VKTRACE_TPI_MARKER_CHECKPOINT:
+            case VKTRACE_TPI_MARKER_API_BOUNDARY:
+            case VKTRACE_TPI_MARKER_API_GROUP_BEGIN:
+            case VKTRACE_TPI_MARKER_API_GROUP_END:
+            case VKTRACE_TPI_MARKER_TERMINATE_PROCESS:
+            default:
+            {
+                return QString ("%1").arg(pHeader->packet_id);
+            }
+        }
+    }
+
+    virtual QString get_packet_string_multiline(const vktrace_trace_packet_header* pHeader) const
+    {
+        // Default implemention is naive.
+        return get_packet_string(pHeader);
+    }
+
+    int rowCount(const QModelIndex &parent = QModelIndex()) const
+    {
+        if (parent.column() > 0)
+        {
+            return 0;
+        }
+
+        int rowCount = 0;
+        if (m_pTraceFileInfo != NULL)
+        {
+            rowCount = m_pTraceFileInfo->packetCount;
+        }
+
+        if (parent.isValid())
+        {
+            // there is a valid parent, so this is a child node, which has no rows
+            rowCount = 0;
+        }
+
+        return rowCount;
+    }
+
+    enum Columns
+    {
+        Column_EntrypointName,
+        Column_TracerId,
+        Column_PacketIndex,
+        Column_ThreadId,
+        Column_BeginTime,
+        Column_EndTime,
+        Column_PacketSize,
+        Column_CpuDuration,
+        cNumColumns
+    };
+
+    int columnCount(const QModelIndex &parent = QModelIndex()) const
+    {
+        return cNumColumns;
+    }
+
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
+    {
+        if (m_pTraceFileInfo == NULL)
+        {
+            return QVariant();
+        }
+
+        if (role == Qt::FontRole)
+        {
+            vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)this->index(index.row(), Column_EntrypointName, index.parent()).internalPointer();
+            if (isDrawCall((VKTRACE_TRACE_PACKET_ID)pHeader->packet_id))
+            {
+                QFont font;
+                font.setBold(true);
+                return font;
+            }
+        }
+
+        if (role == Qt::SizeHintRole)
+        {
+            return QSize(20, 20);
+        }
+
+        if (role == Qt::BackgroundRole && !m_searchString.isEmpty())
+        {
+            QVariant cellData = data(index, Qt::DisplayRole);
+            QString string = cellData.toString();
+            if (string.contains(m_searchString, Qt::CaseInsensitive))
+            {
+                return QColor(Qt::yellow);
+            }
+        }
+
+        if (role == Qt::DisplayRole)
+        {
+            switch (index.column())
+            {
+                case Column_EntrypointName:
+                {
+                    vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+                    QString apiStr = this->get_packet_string(pHeader);
+                    return apiStr;
+                }
+                case Column_TracerId:
+                    return QVariant(*(uint8_t*)index.internalPointer());
+                case Column_PacketIndex:
+                case Column_ThreadId:
+                    return QVariant(*(uint32_t*)index.internalPointer());
+                case Column_BeginTime:
+                case Column_EndTime:
+                case Column_PacketSize:
+                    return QVariant(*(unsigned long long*)index.internalPointer());
+                case Column_CpuDuration:
+                {
+                    vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+                    uint64_t duration = pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time;
+                    return QVariant((unsigned int)duration);
+                }
+            }
+        }
+
+        if (role == Qt::ToolTipRole && index.column() == Column_EntrypointName)
+        {
+            vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+            QString tip;
+            tip += "<html><table>";
+#if defined(_DEBUG)
+            tip += "<tr><td><b>Packet header (pHeader->):</b></td><td/></tr>";
+            tip += QString("<tr><td>size</td><td>= %1 bytes</td></tr>").arg(pHeader->size);
+            tip += QString("<tr><td>global_packet_index</td><td>= %1</td></tr>").arg(pHeader->global_packet_index);
+            tip += QString("<tr><td>tracer_id</td><td>= %1</td></tr>").arg(pHeader->tracer_id);
+            tip += QString("<tr><td>packet_id</td><td>= %1</td></tr>").arg(pHeader->packet_id);
+            tip += QString("<tr><td>thread_id</td><td>= %1</td></tr>").arg(pHeader->thread_id);
+            tip += QString("<tr><td>vktrace_begin_time</td><td>= %1</td></tr>").arg(pHeader->vktrace_begin_time);
+            tip += QString("<tr><td>entrypoint_begin_time</td><td>= %1</td></tr>").arg(pHeader->entrypoint_begin_time);
+            tip += QString("<tr><td>entrypoint_end_time</td><td>= %1 (%2)</td></tr>").arg(pHeader->entrypoint_end_time).arg(pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);
+            tip += QString("<tr><td>vktrace_end_time</td><td>= %1 (%2)</td></tr>").arg(pHeader->vktrace_end_time).arg(pHeader->vktrace_end_time - pHeader->vktrace_begin_time);
+            tip += QString("<tr><td>next_buffers_offset</td><td>= %1</td></tr>").arg(pHeader->next_buffers_offset);
+            tip += QString("<tr><td>pBody</td><td>= %1</td></tr>").arg(pHeader->pBody);
+            tip += "<br>";
+#endif
+            tip += "<tr><td><b>";
+            QString multiline = this->get_packet_string_multiline(pHeader);
+            // only replaces the first '('
+            multiline.replace(multiline.indexOf("("), 1, "</b>(</td><td/></tr><tr><td>");
+            multiline.replace(", ", ", </td></tr><tr><td>");
+            multiline.replace(" = ", "</td><td>= ");
+
+            // only replaces the final ')'
+            multiline.replace(multiline.lastIndexOf(")"), 1, "</td></tr><tr><td>)</td><td>");
+            tip += multiline;
+            tip += "</td></tr></table>";
+            tip += "</html>";
+            return tip;
+        }
+
+        return QVariant();
+    }
+
+    QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const
+    {
+        if (m_pTraceFileInfo == NULL || m_pTraceFileInfo->packetCount == 0)
+        {
+            return createIndex(row, column);
+        }
+
+        if ((uint64_t)row >= m_pTraceFileInfo->packetCount)
+        {
+            return QModelIndex();
+        }
+
+        vktrace_trace_packet_header* pHeader = m_pTraceFileInfo->pPacketOffsets[row].pHeader;
+        void* pData = NULL;
+        switch (column)
+        {
+        case Column_EntrypointName:
+            pData = pHeader;
+            break;
+        case Column_TracerId:
+            pData = &pHeader->tracer_id;
+            break;
+        case Column_PacketIndex:
+            pData = &pHeader->global_packet_index;
+            break;
+        case Column_ThreadId:
+            pData = &pHeader->thread_id;
+            break;
+        case Column_BeginTime:
+            pData = &pHeader->entrypoint_begin_time;
+            break;
+        case Column_EndTime:
+            pData = &pHeader->entrypoint_end_time;
+            break;
+        case Column_PacketSize:
+            pData = &pHeader->size;
+            break;
+        case Column_CpuDuration:
+            pData = pHeader;
+            break;
+        }
+
+        return createIndex(row, column, pData);
+    }
+
+    QModelIndex parent(const QModelIndex& index) const
+    {
+        return QModelIndex();
+    }
+
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const
+    {
+        if (role == Qt::DisplayRole)
+        {
+            if (orientation == Qt::Horizontal)
+            {
+                switch (section)
+                {
+                case Column_EntrypointName:
+                    return QString("API Call");
+                case Column_TracerId:
+                    return QString("Tracer ID");
+                case Column_PacketIndex:
+                    return QString("Index");
+                case Column_ThreadId:
+                    return QString("Thread ID");
+                case Column_BeginTime:
+                    return QString("Start Time");
+                case Column_EndTime:
+                    return QString("End Time");
+                case Column_PacketSize:
+                    return QString("Size (bytes)");
+                case Column_CpuDuration:
+                    return QString("Duration");
+                }
+            }
+        }
+        return QVariant();
+    }
+
+    void set_highlight_search_string(const QString searchString)
+    {
+        m_searchString = searchString;
+    }
+
+private:
+    vktraceviewer_trace_file_info* m_pTraceFileInfo;
+    QString m_searchString;
+
+};
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_apicallitem.h b/vktrace/src/vktrace_viewer/vktraceviewer_apicallitem.h
new file mode 100644
index 0000000..3527954
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_apicallitem.h
@@ -0,0 +1,165 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+#ifndef GLVDEBUG_APICALLITEM_H
+#define GLVDEBUG_APICALLITEM_H
+
+//#include "glvdebug_snapshotitem.h"
+#include "glv_trace_packet_identifiers.h"
+
+// predeclared classes
+//class glvdebug_frameItem;
+//class glvdebug_groupItem;
+//class vogl_trace_packet;
+
+class glvdebug_apiCallItem //: public vogleditor_snapshotItem
+{
+public:
+    glvdebug_apiCallItem(glvdebug_apiCallTreeItem* pParent, glv_trace_packet_header* pTracePacket)
+        //: m_pParentFrame(pFrame),
+        //  m_glPacket(glPacket),
+          //m_pTracePacket(pTracePacket),
+          //m_globalCallIndex(glPacket.m_call_counter),
+          //m_begin_rdtsc(glPacket.m_packet_begin_rdtsc),
+          //m_end_rdtsc(glPacket.m_packet_end_rdtsc),
+          //m_backtrace_hash_index(glPacket.m_backtrace_hash_index)
+    {
+        //if (m_end_rdtsc < m_begin_rdtsc)
+        //{
+        //    m_end_rdtsc = m_begin_rdtsc + 1;
+        //}
+    }
+
+    ~glvdebug_apiCallItem()
+    {
+        //if (m_pTracePacket != NULL)
+        //{
+        //    vogl_delete(m_pTracePacket);
+        //    m_pTracePacket = NULL;
+        //}
+    }
+
+//    inline vogleditor_frameItem *frame() const
+//    {
+//        return m_pParentFrame;
+//    }
+//
+//    inline vogleditor_groupItem *group() const
+//    {
+//        return m_pParentGroup;
+//    }
+//
+//    inline uint64_t globalCallIndex() const
+//    {
+//        return m_globalCallIndex;
+//    }
+//
+//    inline uint64_t startTime() const
+//    {
+//        return m_begin_rdtsc;
+//    }
+//
+//    inline uint64_t endTime() const
+//    {
+//        return m_end_rdtsc;
+//    }
+//
+//    inline uint64_t duration() const
+//    {
+//        return endTime() - startTime();
+//    }
+//
+//    const vogl_trace_gl_entrypoint_packet *getGLPacket() const
+//    {
+//        return &m_glPacket;
+//    }
+//
+//    vogl_trace_packet *getTracePacket()
+//    {
+//        return m_pTracePacket;
+//    }
+//
+//    inline uint64_t backtraceHashIndex() const
+//    {
+//        return m_backtrace_hash_index;
+//    }
+//
+//    // Returns the api function call and its args as a string
+//    QString apiFunctionCall()
+//    {
+//        const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[m_pTracePacket->get_entrypoint_id()];
+//
+//        QString funcCall = entrypoint_desc.m_pName;
+//
+//        // format parameters
+//        funcCall.append("( ");
+//        dynamic_string paramStr;
+//        for (uint param_index = 0; param_index < m_pTracePacket->total_params(); param_index++)
+//        {
+//            if (param_index != 0)
+//                funcCall.append(", ");
+//
+//            paramStr.clear();
+//            m_pTracePacket->pretty_print_param(paramStr, param_index, false);
+//
+//            funcCall.append(paramStr.c_str());
+//        }
+//        funcCall.append(" )");
+//
+//        if (m_pTracePacket->has_return_value())
+//        {
+//            funcCall.append(" = ");
+//            paramStr.clear();
+//            m_pTracePacket->pretty_print_return_value(paramStr, false);
+//            funcCall.append(paramStr.c_str());
+//        }
+//        return funcCall;
+//    }
+//
+//    // Returns the string argument of an apicall in apiFunctionCall() output format
+//    //
+//    // TODO: (as needed) Add logic to return which string (argument count) from
+//    //                   a multi-string argument list (or all as a QStringList)
+//    QString stringArg()
+//    {
+//        QString apiCall = apiFunctionCall();
+//
+//        QString sec, name;
+//        int start = 1;
+//        while (!(sec = apiCall.section('\'', start, start)).isEmpty())
+//        {
+//            name.append(sec);
+//            start += 2;
+//        }
+//        return name;
+//    }
+//
+//private:
+//    glvdebug_apiCallTreeItem *m_pParentFrame;
+//    glvdebug_groupItem *m_pParentGroup;
+////    const vogl_trace_gl_entrypoint_packet m_glPacket;
+////    vogl_trace_packet *m_pTracePacket;
+//
+//    uint64_t m_globalCallIndex;
+//    uint64_t m_begin_rdtsc;
+//    uint64_t m_end_rdtsc;
+//    uint64_t m_backtrace_hash_index;
+};
+
+#endif // GLVDEBUG_APICALLITEM_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_apicalltreeitem.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_apicalltreeitem.cpp
new file mode 100644
index 0000000..959cf7b
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_apicalltreeitem.cpp
@@ -0,0 +1,419 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+#include <QColor>
+#include <QIcon>
+
+#include "glvdebug_apicalltreeitem.h"
+#include "glvdebug_groupitem.h"
+#include "glvdebug_qapicalltreemodel.h"
+#include "glvdebug_frameitem.h"
+
+//#include "vogl_common.h"
+//#include "vogl_trace_file_reader.h"
+//#include "vogl_trace_packet.h"
+//#include "vogl_trace_stream_types.h"
+//#include "glvdebug_gl_state_snapshot.h"
+//#include "glvdebug_settings.h"
+
+// Constructor for root node
+glvdebug_apiCallTreeItem::glvdebug_apiCallTreeItem(int columnCount, glvdebug_QApiCallTreeModel *pModel)
+    : m_parentItem(NULL),
+      //m_pApiCallItem(NULL),
+      //m_pGroupItem(NULL),
+      //m_pFrameItem(NULL),
+      m_pModel(pModel),
+      m_localRowIndex(0),
+      m_columnCount(columnCount)
+{
+    m_columnData = new QVariant[m_columnCount];
+    //m_columnData[VOGL_ACTC_APICALL] = "API Call";
+    //m_columnData[VOGL_ACTC_INDEX] = "Index";
+    //m_columnData[VOGL_ACTC_FLAGS] = "";
+    //m_columnData[VOGL_ACTC_GLCONTEXT] = "GL Context";
+    ////m_ColumnTitles[VOGL_ACTC_BEGINTIME] = "Begin Time";
+    ////m_ColumnTitles[VOGL_ACTC_ENDTIME] = "End Time";
+    //m_columnData[VOGL_ACTC_DURATION] = "Duration (ns)";
+    m_columnData[0] = "API Call";
+    m_columnData[1] = "Index";
+    m_columnData[2] = "";
+    m_columnData[3] = "GL Context";
+    m_columnData[4] = "Duration (ns)";
+}
+//
+//// Constructor for frame nodes
+//glvdebug_apiCallTreeItem::glvdebug_apiCallTreeItem(glvdebug_frameItem *frameItem, glvdebug_apiCallTreeItem *parent)
+//    : m_parentItem(parent),
+//      m_pApiCallItem(NULL),
+//      m_pGroupItem(NULL),
+//      m_pFrameItem(frameItem),
+//      m_pModel(NULL),
+//      m_localRowIndex(0)
+//{
+//    if (frameItem != NULL)
+//    {
+//        QString tmp;
+//        tmp.sprintf("Frame %llu", frameItem->frameNumber());
+//        m_columnData[VOGL_ACTC_APICALL] = tmp;
+//    }
+//
+//    if (m_parentItem != NULL)
+//    {
+//        m_pModel = m_parentItem->m_pModel;
+//    }
+//}
+//
+//// Constructor for group nodes
+//glvdebug_apiCallTreeItem::glvdebug_apiCallTreeItem(glvdebug_groupItem *groupItem, glvdebug_apiCallTreeItem *parent)
+//    : m_parentItem(parent),
+//      m_pApiCallItem(NULL),
+//      m_pGroupItem(groupItem),
+//      m_pFrameItem(NULL),
+//      m_pModel(NULL),
+//      m_localRowIndex(0)
+//{
+//    m_columnData[VOGL_ACTC_APICALL] = cTREEITEM_STATECHANGES;
+//    if (m_parentItem != NULL)
+//    {
+//        m_pModel = m_parentItem->m_pModel;
+//    }
+//}
+
+// Constructor for apiCall nodes
+glvdebug_apiCallTreeItem::glvdebug_apiCallTreeItem(glvdebug_apiCallItem *apiCallItem)
+    : m_parentItem(NULL),
+      //m_pApiCallItem(apiCallItem),
+      //m_pGroupItem(NULL),
+      //m_pFrameItem(NULL),
+      m_pModel(NULL),
+      m_localRowIndex(0)
+{
+    //m_columnData[VOGL_ACTC_APICALL] = apiCallItem->apiFunctionCall();
+
+    //if (apiCallItem != NULL)
+    //{
+    //    m_columnData[VOGL_ACTC_INDEX] = (qulonglong)apiCallItem->globalCallIndex();
+    //    m_columnData[VOGL_ACTC_FLAGS] = "";
+    //    dynamic_string strContext;
+    //    m_columnData[VOGL_ACTC_GLCONTEXT] = strContext.format("0x%" PRIx64, apiCallItem->getGLPacket()->m_context_handle).c_str();
+    //    //m_columnData[VOGL_ACTC_BEGINTIME] = apiCallItem->startTime();
+    //    //m_columnData[VOGL_ACTC_ENDTIME] = apiCallItem->endTime();
+    //    m_columnData[VOGL_ACTC_DURATION] = (qulonglong)apiCallItem->duration();
+    //}
+
+    //if (m_parentItem != NULL)
+    //{
+    //    m_pModel = m_parentItem->m_pModel;
+    //}
+
+    m_columnCount = m_pModel->columnCount();
+    m_columnData = new QVariant[m_columnCount];
+    //m_columnData[VOGL_ACTC_APICALL] = "API Call";
+    //m_columnData[VOGL_ACTC_INDEX] = "Index";
+    //m_columnData[VOGL_ACTC_FLAGS] = "";
+    //m_columnData[VOGL_ACTC_GLCONTEXT] = "GL Context";
+    ////m_ColumnTitles[VOGL_ACTC_BEGINTIME] = "Begin Time";
+    ////m_ColumnTitles[VOGL_ACTC_ENDTIME] = "End Time";
+    //m_columnData[VOGL_ACTC_DURATION] = "Duration (ns)";
+    for (int i = 0; i < m_columnCount; i++)
+    {
+        m_columnData[i] = "data";
+    }
+}
+
+glvdebug_apiCallTreeItem::~glvdebug_apiCallTreeItem()
+{
+    delete [] m_columnData;
+    //if (m_pFrameItem != NULL)
+    //{
+    //    vogl_delete(m_pFrameItem);
+    //    m_pFrameItem = NULL;
+    //}
+
+    //if (m_pGroupItem != NULL)
+    //{
+    //    vogl_delete(m_pGroupItem);
+    //    m_pGroupItem = NULL;
+    //}
+
+    //if (m_pApiCallItem != NULL)
+    //{
+    //    vogl_delete(m_pApiCallItem);
+    //    m_pApiCallItem = NULL;
+    //}
+
+    //for (int i = 0; i < m_childItems.size(); i++)
+    //{
+    //    vogl_delete(m_childItems[i]);
+    //    m_childItems[i] = NULL;
+    //}
+    m_childItems.clear();
+}
+
+void glvdebug_apiCallTreeItem::setParent(glvdebug_apiCallTreeItem* pParent)
+{
+    m_parentItem = pParent;
+    if (m_parentItem != NULL)
+    {
+        m_pModel = m_parentItem->m_pModel;
+    }
+}
+glvdebug_apiCallTreeItem *glvdebug_apiCallTreeItem::parent() const
+{
+    return m_parentItem;
+}
+//bool glvdebug_apiCallTreeItem::isApiCall() const
+//{
+//    return m_pApiCallItem != NULL;
+//}
+//bool glvdebug_apiCallTreeItem::isGroup() const
+//{
+//    return (g_settings.groups_state_render() && (m_pGroupItem != NULL));
+//}
+//bool glvdebug_apiCallTreeItem::isFrame() const
+//{
+//    return m_pFrameItem != NULL;
+//}
+//bool glvdebug_apiCallTreeItem::isRoot() const
+//{
+//    return !(isApiCall() | isGroup() | isFrame());
+//}
+
+void glvdebug_apiCallTreeItem::appendChild(glvdebug_apiCallTreeItem *pChild)
+{
+    pChild->m_localRowIndex = m_childItems.size();
+    pChild->setParent(this);
+    m_childItems.append(pChild);
+}
+
+void glvdebug_apiCallTreeItem::popChild()
+{
+    m_childItems.removeLast();
+}
+
+int glvdebug_apiCallTreeItem::childCount() const
+{
+    return m_childItems.size();
+}
+
+glvdebug_apiCallTreeItem *glvdebug_apiCallTreeItem::child(int index) const
+{
+    if (index < 0 || index >= childCount())
+    {
+        return NULL;
+    }
+
+    return m_childItems[index];
+}
+
+glvdebug_apiCallItem *glvdebug_apiCallTreeItem::apiCallItem() const
+{
+    return m_pApiCallItem;
+}
+
+glvdebug_groupItem *glvdebug_apiCallTreeItem::groupItem() const
+{
+    return m_pGroupItem;
+}
+
+glvdebug_frameItem *glvdebug_apiCallTreeItem::frameItem() const
+{
+    return m_pFrameItem;
+}
+//
+//uint64_t glvdebug_apiCallTreeItem::startTime() const
+//{
+//    uint64_t startTime = 0;
+//
+//    if (m_pApiCallItem)
+//    {
+//        startTime = m_pApiCallItem->startTime();
+//    }
+//    else if (m_pGroupItem)
+//    {
+//        startTime = m_pGroupItem->startTime();
+//    }
+//    else if (m_pFrameItem)
+//    {
+//        startTime = m_pFrameItem->startTime();
+//    }
+//    else // root
+//    {
+//        startTime = child(0)->startTime();
+//    }
+//    return startTime;
+//}
+//
+//uint64_t glvdebug_apiCallTreeItem::endTime() const
+//{
+//    uint64_t endTime = 0;
+//
+//    if (m_pApiCallItem)
+//    {
+//        endTime = m_pApiCallItem->endTime();
+//    }
+//    else if (m_pGroupItem)
+//    {
+//        endTime = m_pGroupItem->endTime();
+//    }
+//    else if (m_pFrameItem)
+//    {
+//        endTime = m_pFrameItem->endTime();
+//    }
+//    else // root
+//    {
+//        endTime = child(childCount() - 1)->endTime();
+//    }
+//    return endTime;
+//}
+//
+//uint64_t glvdebug_apiCallTreeItem::duration() const
+//{
+//    return endTime() - startTime();
+//}
+
+//void glvdebug_apiCallTreeItem::set_snapshot(glvdebug_gl_state_snapshot *pSnapshot)
+//{
+//    if (m_pFrameItem)
+//    {
+//        m_pFrameItem->set_snapshot(pSnapshot);
+//    }
+//
+//    if (m_pApiCallItem)
+//    {
+//        m_pApiCallItem->set_snapshot(pSnapshot);
+//    }
+//}
+//
+//bool glvdebug_apiCallTreeItem::has_snapshot() const
+//{
+//    bool bHasSnapshot = false;
+//    if (m_pFrameItem)
+//    {
+//        bHasSnapshot = m_pFrameItem->has_snapshot();
+//    }
+//
+//    if (m_pApiCallItem)
+//    {
+//        bHasSnapshot = m_pApiCallItem->has_snapshot();
+//    }
+//    return bHasSnapshot;
+//}
+//
+//glvdebug_gl_state_snapshot *glvdebug_apiCallTreeItem::get_snapshot() const
+//{
+//    glvdebug_gl_state_snapshot *pSnapshot = NULL;
+//    if (m_pFrameItem)
+//    {
+//        pSnapshot = m_pFrameItem->get_snapshot();
+//    }
+//
+//    if (m_pApiCallItem)
+//    {
+//        pSnapshot = m_pApiCallItem->get_snapshot();
+//    }
+//    return pSnapshot;
+//}
+
+int glvdebug_apiCallTreeItem::columnCount() const
+{
+    int count = 0;
+    if (m_parentItem == NULL)
+    {
+        count = m_columnCount;
+    }
+    else
+    {
+        m_pModel->columnCount();
+    }
+
+    return count;
+}
+
+QVariant glvdebug_apiCallTreeItem::columnData(int column, int role) const
+{
+    if (column >= m_columnCount)
+    {
+        assert(!"Unexpected column data being requested");
+        return QVariant();
+    }
+
+    if (role == Qt::DecorationRole)
+    {
+        //// handle flags
+        //if (column == VOGL_ACTC_FLAGS)
+        //{
+        //    if (has_snapshot())
+        //    {
+        //        if (get_snapshot()->is_outdated())
+        //        {
+        //            // snapshot was dirtied due to an earlier edit
+        //            return QColor(200, 0, 0);
+        //        }
+        //        else if (get_snapshot()->is_edited())
+        //        {
+        //            // snapshot has been edited
+        //            return QColor(200, 102, 0);
+        //        }
+        //        else
+        //        {
+        //            // snapshot is good
+        //            return QColor(0, 0, 255);
+        //        }
+        //    }
+        //    else if (frameItem() != NULL && frameItem()->get_screenshot_filename().size() > 0)
+        //    {
+        //        return QIcon(frameItem()->get_screenshot_filename().c_str());
+        //    }
+        //}
+    }
+
+    if (role == Qt::DisplayRole)
+    {
+        return m_columnData[column];
+    }
+
+    return QVariant();
+}
+
+//void glvdebug_apiCallTreeItem::setApiCallColumnData(QString name)
+//{
+//    setColumnData(QVariant(name), VOGL_ACTC_APICALL);
+//}
+
+//void glvdebug_apiCallTreeItem::setColumnData(QVariant data, int column)
+//{
+//    m_columnData[column] = data;
+//}
+
+//QString glvdebug_apiCallTreeItem::apiCallColumnData() const
+//{
+//    return (columnData(VOGL_ACTC_APICALL, Qt::DisplayRole)).toString();
+//}
+//
+//QString glvdebug_apiCallTreeItem::apiCallStringArg() const
+//{
+//    return isApiCall() ? apiCallItem()->stringArg() : QString();
+//}
+
+int glvdebug_apiCallTreeItem::row() const
+{
+    // note, this is just the row within the current level of the hierarchy
+    return m_localRowIndex;
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_apicalltreeitem.h b/vktrace/src/vktrace_viewer/vktraceviewer_apicalltreeitem.h
new file mode 100644
index 0000000..af29593
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_apicalltreeitem.h
@@ -0,0 +1,97 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+#ifndef GLVDEBUG_APICALLTREEITEM_H
+#define GLVDEBUG_APICALLTREEITEM_H
+
+#include <QList>
+#include <QVariant>
+
+typedef unsigned long long uint64_t;
+
+class glvdebug_frameItem;
+class glvdebug_groupItem;
+class glvdebug_apiCallItem;
+
+class glvdebug_QApiCallTreeModel;
+
+
+const QString cTREEITEM_STATECHANGES("State changes");
+// TODO: Maybe think about a more unique name so as not to be confused with,
+//       e.g., a marker_push entrypoint that has also been named "Render"
+const QString cTREEITEM_RENDER("Render");
+
+class glvdebug_apiCallTreeItem
+{
+public:
+    // Constructor for the root node
+    glvdebug_apiCallTreeItem(int columnCount, glvdebug_QApiCallTreeModel *pModel);
+
+    //// Constructor for frame nodes
+    //glvdebug_apiCallTreeItem(glvdebug_frameItem *frameItem, glvdebug_apiCallTreeItem *parent);
+
+    //// Constructor for group nodes
+    //glvdebug_apiCallTreeItem(glvdebug_groupItem *groupItem, glvdebug_apiCallTreeItem *parent);
+
+    // Constructor for apiCall nodes
+    glvdebug_apiCallTreeItem(glvdebug_apiCallItem *apiCallItem);
+
+    ~glvdebug_apiCallTreeItem();
+
+    void setParent(glvdebug_apiCallTreeItem* pParent);
+    glvdebug_apiCallTreeItem *parent() const;
+
+    void appendChild(glvdebug_apiCallTreeItem *pChild);
+    void popChild();
+
+    int childCount() const;
+
+    glvdebug_apiCallTreeItem *child(int index) const;
+
+    glvdebug_apiCallItem *apiCallItem() const;
+    glvdebug_groupItem *groupItem() const;
+    glvdebug_frameItem *frameItem() const;
+    
+    int columnCount() const;
+
+    QVariant columnData(int column, int role) const;
+
+    int row() const;
+
+    //bool isApiCall() const;
+    //bool isGroup() const;
+    //bool isFrame() const;
+    //bool isRoot() const;
+
+private:
+//    void setColumnData(QVariant data, int column);
+
+private:
+    QList<glvdebug_apiCallTreeItem *> m_childItems;
+    QVariant* m_columnData;
+    int m_columnCount;
+    glvdebug_apiCallTreeItem *m_parentItem;
+    //glvdebug_apiCallItem *m_pApiCallItem;
+    //glvdebug_groupItem *m_pGroupItem;
+    //glvdebug_frameItem *m_pFrameItem;
+    glvdebug_QApiCallTreeModel *m_pModel;
+    int m_localRowIndex;
+};
+
+#endif // GLVDEBUG_APICALLTREEITEM_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_controller.h b/vktrace/src/vktrace_viewer/vktraceviewer_controller.h
new file mode 100644
index 0000000..753831d
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_controller.h
@@ -0,0 +1,56 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#pragma once
+#include "vktraceviewer_trace_file_utils.h"
+#include "vktraceviewer_view.h"
+#include "vktrace_settings.h"
+
+#include <QObject>
+
+class vktraceviewer_QController : public QObject
+{
+public:
+    vktraceviewer_QController() {}
+    virtual ~vktraceviewer_QController() {}
+
+    virtual const char* GetPacketIdString(uint16_t packetId) = 0;
+    virtual vktrace_SettingGroup* GetSettings() = 0;
+    virtual void UpdateFromSettings(vktrace_SettingGroup* pSettingGroups, unsigned int numSettingGroups) = 0;
+    virtual vktrace_trace_packet_header* InterpretTracePacket(vktrace_trace_packet_header* pHeader) = 0;
+    virtual bool LoadTraceFile(vktraceviewer_trace_file_info* pTraceFileInfo, vktraceviewer_view* pView) = 0;
+    virtual void UnloadTraceFile(void) = 0;
+
+public slots:
+
+signals:
+    void OutputMessage(VktraceLogLevel level, const QString& message);
+    void OutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& message);
+};
+
+extern "C"
+{
+VKTRACER_EXPORT vktraceviewer_QController* VKTRACER_CDECL vtvCreateQController(void);
+VKTRACER_EXPORT void VKTRACER_CDECL vtvDeleteQController(vktraceviewer_QController** ppController);
+
+// entrypoints that must be exposed by each controller library
+typedef vktraceviewer_QController* (VKTRACER_CDECL *funcptr_vktraceviewer_CreateQController)(void);
+typedef void (VKTRACER_CDECL *funcptr_vktraceviewer_DeleteQController)(vktraceviewer_QController* pController);
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_controller_factory.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_controller_factory.cpp
new file mode 100644
index 0000000..265b2c2
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_controller_factory.cpp
@@ -0,0 +1,92 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_controller_factory.h"
+#include "vktrace_platform.h"
+
+vktraceviewer_controller_factory::vktraceviewer_controller_factory()
+{
+}
+
+vktraceviewer_controller_factory::~vktraceviewer_controller_factory()
+{
+}
+
+vktraceviewer_QController *vktraceviewer_controller_factory::Load(const char* filename)
+{
+    void* pLibrary = vktrace_platform_open_library(filename);
+    if (pLibrary == NULL)
+    {
+        vktrace_LogError("Failed to load controller '%s'", filename);
+#if defined(PLATFORM_LINUX)
+        char* error = dlerror();
+        vktrace_LogError("Due to: %s", error);
+#endif
+        return NULL;
+    }
+
+    vktraceviewer_QController* pController = NULL;
+    funcptr_vktraceviewer_CreateQController CreateQController = (funcptr_vktraceviewer_CreateQController)vktrace_platform_get_library_entrypoint(pLibrary, "vtvCreateQController");
+    funcptr_vktraceviewer_DeleteQController DeleteQController = (funcptr_vktraceviewer_DeleteQController)vktrace_platform_get_library_entrypoint(pLibrary, "vtvDeleteQController");
+    if (CreateQController == NULL)
+    {
+        vktrace_LogError("Controller '%s' is missing entrypoint 'vtvCreateQController'.\n", filename);
+    }
+    if (DeleteQController == NULL)
+    {
+        vktrace_LogError("Controller '%s' is missing entrypoint 'vtvDeleteQController'.\n", filename);
+    }
+
+    if (CreateQController != NULL &&
+        DeleteQController != NULL)
+    {
+        pController = CreateQController();
+    }
+
+    if (pController != NULL)
+    {
+        m_controllerToLibraryMap[pController] = pLibrary;
+    }
+
+    return pController;
+}
+
+void vktraceviewer_controller_factory::Unload(vktraceviewer_QController** ppController)
+{
+    assert(ppController != NULL);
+    assert(*ppController != NULL);
+
+    void* pLibrary = m_controllerToLibraryMap[*ppController];
+    if (pLibrary == NULL)
+    {
+        vktrace_LogError("NULL Library encountered while unloading controller.");
+    }
+    else
+    {
+        funcptr_vktraceviewer_DeleteQController DeleteQController = (funcptr_vktraceviewer_DeleteQController)vktrace_platform_get_library_entrypoint(pLibrary, "vtvDeleteQController");
+        if (DeleteQController != NULL)
+        {
+            DeleteQController(*ppController);
+            *ppController = NULL;
+        }
+
+        vktrace_platform_close_library(pLibrary);
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_controller_factory.h b/vktrace/src/vktrace_viewer/vktraceviewer_controller_factory.h
new file mode 100644
index 0000000..cd5242a
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_controller_factory.h
@@ -0,0 +1,46 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_CONTROLLER_FACTORY_H
+#define VKTRACEVIEWER_CONTROLLER_FACTORY_H
+
+#include <QMap>
+
+extern "C" {
+#include "vktrace_common.h"
+#include "vktrace_trace_packet_identifiers.h"
+}
+
+#include "vktraceviewer_controller.h"
+
+class vktraceviewer_controller_factory
+{
+public:
+    vktraceviewer_controller_factory();
+    ~vktraceviewer_controller_factory();
+
+    vktraceviewer_QController* Load(const char* filename);
+    void Unload(vktraceviewer_QController** ppController);
+
+private:
+    QMap<vktraceviewer_QController*, void*> m_controllerToLibraryMap;
+};
+
+#endif // VKTRACEVIEWER_CONTROLLER_FACTORY_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_frameitem.h b/vktrace/src/vktrace_viewer/vktraceviewer_frameitem.h
new file mode 100644
index 0000000..853f011
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_frameitem.h
@@ -0,0 +1,137 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **************************************************************************/
+
+#ifndef VOGLEDITOR_FRAMEITEM_H
+#define VOGLEDITOR_FRAMEITEM_H
+
+#include <QList>
+
+// external class (could be predeclared if
+// definitions were move to a .cpp file)
+#include "glvdebug_apicallitem.h"
+
+class vogleditor_frameItem //: public vogleditor_snapshotItem
+{
+public:
+    vogleditor_frameItem(uint64_t frameNumber)
+        : m_frameNumber(frameNumber)
+    {
+    }
+
+    ~vogleditor_frameItem()
+    {
+        m_apiCallList.clear();
+        m_groupList.clear();
+    }
+
+    //inline uint64_t frameNumber() const
+    //{
+    //    return m_frameNumber;
+    //}
+
+    //void appendGroup(vogleditor_groupItem *pItem)
+    //{
+    //    m_groupList.append(pItem);
+    //}
+
+    //vogleditor_apiCallItem *popApiCall()
+    //{
+    //    return m_apiCallList.takeLast();
+    //}
+
+    //vogleditor_groupItem *popGroup()
+    //{
+    //    return m_groupList.takeLast();
+    //}
+
+    //void appendCall(vogleditor_apiCallItem *pItem)
+    //{
+    //    m_apiCallList.append(pItem);
+    //}
+
+    //inline int callCount() const
+    //{
+    //    return m_apiCallList.size();
+    //}
+
+    //vogleditor_apiCallItem *call(int index) const
+    //{
+    //    if (index < 0 || index > callCount())
+    //    {
+    //        return NULL;
+    //    }
+
+    //    return m_apiCallList[index];
+    //}
+
+    //bool getStartEndTimes(uint64_t &start, uint64_t &end) const
+    //{
+    //    if (callCount() == 0)
+    //    {
+    //        return false;
+    //    }
+
+    //    start = startTime();
+    //    end = endTime();
+    //    return true;
+    //}
+
+    //uint64_t startTime() const
+    //{
+    //    return apiCallStartTime(0);
+    //}
+
+    //uint64_t endTime() const
+    //{
+    //    return apiCallEndTime(callCount() - 1);
+    //}
+
+    //uint64_t apiCallStartTime(uint index) const
+    //{
+    //    return m_apiCallList[index]->startTime();
+    //}
+
+    //uint64_t apiCallEndTime(uint index) const
+    //{
+    //    return m_apiCallList[index]->endTime();
+    //}
+
+    //uint64_t duration() const
+    //{
+    //    return (endTime() - startTime());
+    //}
+
+    //void set_screenshot_filename(const std::string &filename)
+    //{
+    //    m_screenshot_filename = filename;
+    //}
+
+    //const std::string &get_screenshot_filename() const
+    //{
+    //    return m_screenshot_filename;
+    //}
+
+private:
+    uint64_t m_frameNumber;
+    QList<glvdebug_apiCallItem *> m_apiCallList;
+    QList<vogleditor_groupItem *> m_groupList;
+
+    //std::string m_screenshot_filename;
+};
+
+#endif // VOGLEDITOR_FRAMEITEM_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_groupitem.h b/vktrace/src/vktrace_viewer/vktraceviewer_groupitem.h
new file mode 100644
index 0000000..1f2109a
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_groupitem.h
@@ -0,0 +1,92 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+#pragma once
+
+#include <QList>
+//#include "glvdebug_snapshotitem.h"
+#include "glvdebug_apicallitem.h"
+
+class vogleditor_frameItem;
+
+class vogleditor_groupItem //: public vogleditor_snapshotItem
+{
+public:
+    vogleditor_groupItem(vogleditor_frameItem *pFrameItem)
+        : m_pParentFrame(pFrameItem)
+    {
+    }
+
+    ~vogleditor_groupItem()
+    {
+        m_apiCallList.clear();
+    }
+
+    void appendCall(glvdebug_apiCallItem *pItem)
+    {
+        m_apiCallList.append(pItem);
+    }
+
+    inline int callCount() const
+    {
+        return m_apiCallList.size();
+    }
+
+    glvdebug_apiCallItem *call(int index) const
+    {
+        if (index < 0 || index > callCount())
+        {
+            return NULL;
+        }
+        return m_apiCallList[index];
+    }
+
+    //inline uint64_t firstApiCallIndex() const
+    //{
+    //    return apiCallIndex(0);
+    //}
+
+    //inline uint64_t apiCallIndex(int index = 0) const
+    //{
+    //    if (vogleditor_apiCallItem *apiCallItem = call(index))
+    //    {
+    //        return apiCallItem->globalCallIndex();
+    //    }
+    //    return uint64_t(-1); // (-1 index won't be found)
+    //}
+
+    //inline uint64_t startTime() const
+    //{
+    //    return m_apiCallList[0]->startTime();
+    //}
+
+    //inline uint64_t endTime() const
+    //{
+    //    return m_apiCallList[callCount() - 1]->endTime();
+    //}
+
+    //inline uint64_t duration() const
+    //{
+    //    return endTime() - startTime();
+    //}
+
+private:
+    vogleditor_frameItem *m_pParentFrame;
+    QList<glvdebug_apiCallItem *> m_apiCallList;
+};
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_output.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_output.cpp
new file mode 100644
index 0000000..308183f
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_output.cpp
@@ -0,0 +1,109 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_output.h"
+#include <QTextEdit>
+
+vktraceviewer_output gs_OUTPUT;
+
+vktraceviewer_output::vktraceviewer_output()
+{
+}
+
+vktraceviewer_output::~vktraceviewer_output()
+{
+}
+
+void vktraceviewer_output::init(QTextBrowser *pTextEdit)
+{
+    m_pTextEdit = pTextEdit;
+}
+
+QString vktraceviewer_output::convertToHtml(QString message)
+{
+    QString result;
+    if (message.endsWith("\n"))
+    {
+        message.chop(1);
+    }
+    result = message.replace("\n", "<br>");
+    return result;
+}
+
+void vktraceviewer_output::moveCursorToEnd()
+{
+    QTextCursor cursor = m_pTextEdit->textCursor();
+    cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+    m_pTextEdit->setTextCursor(cursor);
+}
+
+void vktraceviewer_output::message(uint64_t packetIndex, const QString& message)
+{
+    if (m_pTextEdit != NULL)
+    {
+        QString msg;
+        if (packetIndex == (uint64_t)-1)
+        {
+            msg = message;
+        }
+        else
+        {
+            msg = QString("(<a href='packet#%1'>%1</a>): %2 ").arg(packetIndex).arg(message);
+        }
+        moveCursorToEnd();
+        m_pTextEdit->append(msg);
+    }
+}
+
+void vktraceviewer_output::warning(uint64_t packetIndex, const QString& warning)
+{
+    if (m_pTextEdit != NULL)
+    {
+        QString msg;
+        if (packetIndex == (uint64_t)-1)
+        {
+            msg = QString("<font color='red'>Warning: %1</font> ").arg(warning);
+        }
+        else
+        {
+            msg = QString("<font color='red'>(<a href='packet#%1'>%1</a>) Warning: %2</font> ").arg(packetIndex).arg(warning);
+        }
+        moveCursorToEnd();
+        m_pTextEdit->append(msg);
+    }
+}
+
+void vktraceviewer_output::error(uint64_t packetIndex, const QString& error)
+{
+    if (m_pTextEdit != NULL)
+    {
+        QString msg;
+        if (packetIndex == (uint64_t)-1)
+        {
+            msg = QString("<font color='red'><b>Error: %1</b></font> ").arg(convertToHtml(error));
+        }
+        else
+        {
+            msg = QString("<font color='red'><b>(<a href='packet#%1'>%1</a>) Error: %2</b></font> ").arg(packetIndex).arg(convertToHtml(error));
+        }
+        moveCursorToEnd();
+        m_pTextEdit->append(msg);
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_output.h b/vktrace/src/vktrace_viewer/vktraceviewer_output.h
new file mode 100644
index 0000000..416f665
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_output.h
@@ -0,0 +1,66 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_OUTPUT_H
+#define VKTRACEVIEWER_OUTPUT_H
+
+#include <QString>
+#include <QTextBrowser>
+extern "C"
+{
+#include "vktrace_platform.h"
+#include "vktrace_tracelog.h"
+}
+
+class QTextEdit;
+
+class vktraceviewer_output
+{
+public:
+    vktraceviewer_output();
+    ~vktraceviewer_output();
+
+    void init(QTextBrowser* pTextEdit);
+
+    void message(uint64_t packetIndex, const QString& message);
+    void warning(uint64_t packetIndex, const QString& warning);
+    void error(uint64_t packetIndex, const QString& error);
+
+private:
+    QString convertToHtml(QString message);
+    void moveCursorToEnd();
+    QTextBrowser* m_pTextEdit;
+};
+
+extern vktraceviewer_output gs_OUTPUT;
+
+inline void vktraceviewer_output_init(QTextBrowser* pTextEdit) { gs_OUTPUT.init(pTextEdit); }
+
+inline void vktraceviewer_output_message(uint64_t packetIndex, const QString& message) { gs_OUTPUT.message(packetIndex, message); }
+inline void vktraceviewer_output_message(const QString& message) { gs_OUTPUT.message(-1, message); }
+
+inline void vktraceviewer_output_warning(uint64_t packetIndex, const QString& warning) { gs_OUTPUT.warning(packetIndex, warning); }
+inline void vktraceviewer_output_warning(const QString& warning) { gs_OUTPUT.warning(-1, warning); }
+
+inline void vktraceviewer_output_error(uint64_t packetIndex, const QString& error) { gs_OUTPUT.error(packetIndex, error); }
+inline void vktraceviewer_output_error(const QString& error) { gs_OUTPUT.error(-1, error); }
+inline void vktraceviewer_output_deinit() { gs_OUTPUT.init(0); }
+
+#endif // VKTRACEVIEWER_OUTPUT_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qapicalltreemodel.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_qapicalltreemodel.cpp
new file mode 100644
index 0000000..92ad805
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qapicalltreemodel.cpp
@@ -0,0 +1,439 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+#include <QColor>
+#include <QFont>
+#include <QLocale>
+
+#include "glvdebug_qapicalltreemodel.h"
+#include "glv_common.h"
+#include "glv_trace_packet_identifiers.h"
+
+#include "glvdebug_apicalltreeitem.h"
+//#include "glvdebug_frameitem.h"
+//#include "glvdebug_groupitem.h"
+//#include "glvdebug_apicallitem.h"
+#include "glvdebug_output.h"
+#include "glvdebug_settings.h"
+
+glvdebug_QApiCallTreeModel::glvdebug_QApiCallTreeModel(int columnCount, QObject *parent)
+    : QAbstractItemModel(parent),
+      m_columnCount(columnCount)
+{
+    m_rootItem = new glvdebug_apiCallTreeItem(columnCount, this);
+}
+
+glvdebug_QApiCallTreeModel::~glvdebug_QApiCallTreeModel()
+{
+    if (m_rootItem != NULL)
+    {
+        delete m_rootItem;
+        m_rootItem = NULL;
+    }
+
+    m_itemList.clear();
+}
+
+
+QModelIndex glvdebug_QApiCallTreeModel::index(int row, int column, const QModelIndex &parent) const
+{
+    if (!hasIndex(row, column, parent))
+        return QModelIndex();
+
+    glvdebug_apiCallTreeItem *parentItem;
+
+    if (!parent.isValid())
+        parentItem = m_rootItem;
+    else
+        parentItem = static_cast<glvdebug_apiCallTreeItem *>(parent.internalPointer());
+
+    glvdebug_apiCallTreeItem *childItem = parentItem->child(row);
+    if (childItem)
+        return createIndex(row, column, childItem);
+    else
+        return QModelIndex();
+}
+//
+//QModelIndex glvdebug_QApiCallTreeModel::indexOf(const glvdebug_apiCallTreeItem *pItem) const
+//{
+//    if (pItem != NULL)
+//        return createIndex(pItem->row(), /*VOGL_ACTC_APICALL*/ 0, (void *)pItem);
+//    else
+//        return QModelIndex();
+//}
+
+QModelIndex glvdebug_QApiCallTreeModel::parent(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return QModelIndex();
+
+    glvdebug_apiCallTreeItem *childItem = static_cast<glvdebug_apiCallTreeItem *>(index.internalPointer());
+    if (childItem == m_rootItem)
+        return QModelIndex();
+
+    glvdebug_apiCallTreeItem *parentItem = childItem->parent();
+
+    if (parentItem == m_rootItem || parentItem == NULL)
+        return QModelIndex();
+
+    return createIndex(parentItem->row(), /*VOGL_ACTC_APICALL*/ 0, parentItem);
+}
+
+int glvdebug_QApiCallTreeModel::rowCount(const QModelIndex &parent) const
+{
+    glvdebug_apiCallTreeItem *parentItem;
+    if (parent.column() > 0)
+        return 0;
+
+    if (!parent.isValid())
+        parentItem = m_rootItem;
+    else
+        parentItem = static_cast<glvdebug_apiCallTreeItem *>(parent.internalPointer());
+
+    return parentItem->childCount();
+}
+
+int glvdebug_QApiCallTreeModel::columnCount(const QModelIndex &parent) const
+{
+    //VOGL_NOTE_UNUSED(parent);
+    return m_columnCount;
+}
+
+QVariant glvdebug_QApiCallTreeModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid())
+        return QVariant();
+
+    glvdebug_apiCallTreeItem *pItem = static_cast<glvdebug_apiCallTreeItem *>(index.internalPointer());
+
+    if (pItem == NULL)
+    {
+        return QVariant();
+    }
+
+    //// make draw call rows appear in bold
+    //if (role == Qt::FontRole && pItem->apiCallItem() != NULL && vogl_is_frame_buffer_write_entrypoint((gl_entrypoint_id_t)pItem->apiCallItem()->getGLPacket()->m_entrypoint_id))
+    //{
+    //    QFont font;
+    //    font.setBold(true);
+    //    return font;
+    //}
+
+    //// highlight the API call cell if it has a substring which matches the searchString
+    //if (role == Qt::BackgroundRole && index.column() == VOGL_ACTC_APICALL)
+    //{
+    //    if (!m_searchString.isEmpty())
+    //    {
+    //        QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
+    //        QString string = data.toString();
+    //        if (string.contains(m_searchString, Qt::CaseInsensitive))
+    //        {
+    //            return QColor(Qt::yellow);
+    //        }
+    //    }
+    //}
+
+    return pItem->columnData(index.column(), role);
+}
+
+Qt::ItemFlags glvdebug_QApiCallTreeModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return 0;
+
+    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+QVariant glvdebug_QApiCallTreeModel::headerData(int section, Qt::Orientation orientation,
+                                                  int role) const
+{
+    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+        return m_rootItem->columnData(section, role);
+
+    return QVariant();
+}
+
+//void glvdebug_QApiCallTreeModel::set_highlight_search_string(const QString searchString)
+//{
+//    m_searchString = searchString;
+//}
+//
+//QModelIndex glvdebug_QApiCallTreeModel::find_prev_search_result(glvdebug_apiCallTreeItem *start, const QString searchText)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    if (start != NULL)
+//    {
+//        if (iter.findNext(start) == false)
+//        {
+//            // the object wasn't found in the list, so return a default (invalid) item
+//            return QModelIndex();
+//        }
+//
+//        // need to back up past the current item
+//        iter.previous();
+//    }
+//    else
+//    {
+//        // set the iterator to the back so that searching starts from the end of the list
+//        iter.toBack();
+//    }
+//
+//    // now the iterator is pointing to the desired start object in the list,
+//    // continually check the prev item and find one with a snapshot
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasPrevious())
+//    {
+//        glvdebug_apiCallTreeItem *pItem = iter.peekPrevious();
+//        QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
+//        QString string = data.toString();
+//        if (string.contains(searchText, Qt::CaseInsensitive))
+//        {
+//            pFound = pItem;
+//            break;
+//        }
+//
+//        iter.previous();
+//    }
+//
+//    return indexOf(pFound);
+//}
+//
+//QModelIndex glvdebug_QApiCallTreeModel::find_next_search_result(glvdebug_apiCallTreeItem *start, const QString searchText)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    if (start != NULL)
+//    {
+//        if (iter.findNext(start) == false)
+//        {
+//            // the object wasn't found in the list, so return a default (invalid) item
+//            return QModelIndex();
+//        }
+//    }
+//
+//    // now the iterator is pointing to the desired start object in the list,
+//    // continually check the next item and find one with a snapshot
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasNext())
+//    {
+//        glvdebug_apiCallTreeItem *pItem = iter.peekNext();
+//        QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
+//        QString string = data.toString();
+//        if (string.contains(searchText, Qt::CaseInsensitive))
+//        {
+//            pFound = pItem;
+//            break;
+//        }
+//
+//        iter.next();
+//    }
+//
+//    return indexOf(pFound);
+//}
+//
+//glvdebug_apiCallTreeItem *glvdebug_QApiCallTreeModel::find_prev_snapshot(glvdebug_apiCallTreeItem *start)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    if (start != NULL)
+//    {
+//        if (iter.findNext(start) == false)
+//        {
+//            // the object wasn't found in the list
+//            return NULL;
+//        }
+//
+//        // need to back up past the current item
+//        iter.previous();
+//    }
+//    else
+//    {
+//        // set the iterator to the back so that searching starts from the end of the list
+//        iter.toBack();
+//    }
+//
+//    // now the iterator is pointing to the desired start object in the list,
+//    // continually check the prev item and find one with a snapshot
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasPrevious())
+//    {
+//        if (iter.peekPrevious()->has_snapshot())
+//        {
+//            pFound = iter.peekPrevious();
+//            break;
+//        }
+//
+//        iter.previous();
+//    }
+//
+//    return pFound;
+//}
+//
+//glvdebug_apiCallTreeItem *glvdebug_QApiCallTreeModel::find_next_snapshot(glvdebug_apiCallTreeItem *start)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    // if start is NULL, then search will begin from top, otherwise it will begin from the start item and search onwards
+//    if (start != NULL)
+//    {
+//        if (iter.findNext(start) == false)
+//        {
+//            // the object wasn't found in the list
+//            return NULL;
+//        }
+//    }
+//
+//    // now the iterator is pointing to the desired start object in the list,
+//    // continually check the next item and find one with a snapshot
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasNext())
+//    {
+//        if (iter.peekNext()->has_snapshot())
+//        {
+//            pFound = iter.peekNext();
+//            break;
+//        }
+//
+//        iter.next();
+//    }
+//
+//    return pFound;
+//}
+//
+//glvdebug_apiCallTreeItem *glvdebug_QApiCallTreeModel::find_prev_drawcall(glvdebug_apiCallTreeItem *start)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    if (start != NULL)
+//    {
+//        if (iter.findNext(start) == false)
+//        {
+//            // the object wasn't found in the list
+//            return NULL;
+//        }
+//
+//        // need to back up past the current item
+//        iter.previous();
+//    }
+//    else
+//    {
+//        // set the iterator to the back so that searching starts from the end of the list
+//        iter.toBack();
+//    }
+//
+//    // now the iterator is pointing to the desired start object in the list,
+//    // continually check the prev item and find one with a snapshot
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasPrevious())
+//    {
+//        glvdebug_apiCallTreeItem *pItem = iter.peekPrevious();
+//        if (pItem->apiCallItem() != NULL)
+//        {
+//            gl_entrypoint_id_t entrypointId = pItem->apiCallItem()->getTracePacket()->get_entrypoint_id();
+//            if (vogl_is_frame_buffer_write_entrypoint(entrypointId))
+//            {
+//                pFound = iter.peekPrevious();
+//                break;
+//            }
+//        }
+//
+//        iter.previous();
+//    }
+//
+//    return pFound;
+//}
+//
+//glvdebug_apiCallTreeItem *glvdebug_QApiCallTreeModel::find_next_drawcall(glvdebug_apiCallTreeItem *start)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    if (iter.findNext(start) == false)
+//    {
+//        // the object wasn't found in the list
+//        return NULL;
+//    }
+//
+//    // now the iterator is pointing to the desired start object in the list,
+//    // continually check the next item and find one with a snapshot
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasNext())
+//    {
+//        glvdebug_apiCallTreeItem *pItem = iter.peekNext();
+//        if (pItem->apiCallItem() != NULL)
+//        {
+//            gl_entrypoint_id_t entrypointId = pItem->apiCallItem()->getTracePacket()->get_entrypoint_id();
+//            if (vogl_is_frame_buffer_write_entrypoint(entrypointId))
+//            {
+//                pFound = iter.peekNext();
+//                break;
+//            }
+//        }
+//
+//        iter.next();
+//    }
+//
+//    return pFound;
+//}
+//
+//glvdebug_apiCallTreeItem *glvdebug_QApiCallTreeModel::find_call_number(unsigned int callNumber)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasNext())
+//    {
+//        glvdebug_apiCallTreeItem *pItem = iter.peekNext();
+//        if (pItem->apiCallItem() != NULL)
+//        {
+//            if (pItem->apiCallItem()->globalCallIndex() == callNumber)
+//            {
+//                pFound = iter.peekNext();
+//                break;
+//            }
+//        }
+//
+//        iter.next();
+//    }
+//
+//    return pFound;
+//}
+//
+//glvdebug_apiCallTreeItem *glvdebug_QApiCallTreeModel::find_frame_number(unsigned int frameNumber)
+//{
+//    QLinkedListIterator<glvdebug_apiCallTreeItem *> iter(m_itemList);
+//
+//    glvdebug_apiCallTreeItem *pFound = NULL;
+//    while (iter.hasNext())
+//    {
+//        glvdebug_apiCallTreeItem *pItem = iter.peekNext();
+//        if (pItem->frameItem() != NULL)
+//        {
+//            if (pItem->frameItem()->frameNumber() == frameNumber)
+//            {
+//                pFound = iter.peekNext();
+//                break;
+//            }
+//        }
+//
+//        iter.next();
+//    }
+//
+//    return pFound;
+//}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qapicalltreemodel.h b/vktrace/src/vktrace_viewer/vktraceviewer_qapicalltreemodel.h
new file mode 100644
index 0000000..67f1d11
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qapicalltreemodel.h
@@ -0,0 +1,103 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************/
+
+#ifndef GLVDEBUG_QAPICALLTREEMODEL_H
+#define GLVDEBUG_QAPICALLTREEMODEL_H
+
+#include <QAbstractItemModel>
+#include <QLinkedList>
+
+#include "glv_common.h"
+
+
+class QVariant;
+class glvdebug_apiCallTreeItem;
+//class glvdebug_groupItem;
+//class glvdebug_frameItem;
+class glvdebug_apiCallItem;
+
+class glvdebug_QApiCallTreeModel : public QAbstractItemModel
+{
+    Q_OBJECT
+
+public:
+    glvdebug_QApiCallTreeModel(int columnCount, QObject *parent = 0);
+    ~glvdebug_QApiCallTreeModel();
+
+    // required to implement
+    virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+    virtual QModelIndex parent(const QModelIndex &index) const;
+    virtual QVariant data(const QModelIndex &index, int role) const;
+    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+    //void appendChild(glvdebug_apiCallTreeItem* pItem)
+    //{
+    //    m_rootItem->appendChild(pItem);
+    //}
+
+    //virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+    //virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+    //QModelIndex indexOf(const glvdebug_apiCallTreeItem *pItem) const;
+
+    //glvdebug_apiCallTreeItem *root() const
+    //{
+    //    return m_rootItem;
+    //}
+
+    //glvdebug_apiCallTreeItem *create_group(glvdebug_frameItem *pFrameObj,
+    //                                         glvdebug_groupItem *&pGroupObj,
+    //                                         glvdebug_apiCallTreeItem *pParentNode);
+    //void set_highlight_search_string(const QString searchString);
+    //QModelIndex find_prev_search_result(glvdebug_apiCallTreeItem *start, const QString searchText);
+    //QModelIndex find_next_search_result(glvdebug_apiCallTreeItem *start, const QString searchText);
+
+    //glvdebug_apiCallTreeItem *find_prev_snapshot(glvdebug_apiCallTreeItem *start);
+    //glvdebug_apiCallTreeItem *find_next_snapshot(glvdebug_apiCallTreeItem *start);
+
+    //glvdebug_apiCallTreeItem *find_prev_drawcall(glvdebug_apiCallTreeItem *start);
+    //glvdebug_apiCallTreeItem *find_next_drawcall(glvdebug_apiCallTreeItem *start);
+
+    //glvdebug_apiCallTreeItem *find_call_number(unsigned int callNumber);
+    //glvdebug_apiCallTreeItem *find_frame_number(unsigned int frameNumber);
+
+signals:
+
+public
+slots:
+
+private:
+    //gl_entrypoint_id_t itemApiCallId(glvdebug_apiCallTreeItem *apiCall) const;
+    //gl_entrypoint_id_t lastItemApiCallId() const;
+
+    //bool processMarkerPushEntrypoint(gl_entrypoint_id_t id);
+    //bool processMarkerPopEntrypoint(gl_entrypoint_id_t id);
+    //bool processStartNestedEntrypoint(gl_entrypoint_id_t id);
+    //bool processEndNestedEntrypoint(gl_entrypoint_id_t id);
+    //bool processFrameBufferWriteEntrypoint(gl_entrypoint_id_t id);
+
+private:
+    int m_columnCount;
+    glvdebug_apiCallTreeItem *m_rootItem;
+    QLinkedList<glvdebug_apiCallTreeItem *> m_itemList;
+//    QString m_searchString;
+};
+
+#endif // GLVDEBUG_QAPICALLTREEMODEL_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qgeneratetracedialog.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_qgeneratetracedialog.cpp
new file mode 100644
index 0000000..61d8c99
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qgeneratetracedialog.cpp
@@ -0,0 +1,475 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_qgeneratetracedialog.h"
+#include "vktraceviewer_settings.h"
+#include <QDir>
+#include <QFileDialog>
+#include <QGridLayout>
+#include <QProcessEnvironment>
+
+vktraceviewer_QGenerateTraceDialog::vktraceviewer_QGenerateTraceDialog(QWidget *parent)
+    : QDialog(parent),
+      m_pGenerateTraceProcess(NULL)
+{
+    setMinimumWidth(500);
+    setWindowTitle("Generate Trace File");
+    m_pGridLayout = new QGridLayout(this);
+    m_pGridLayout->setObjectName("m_pGridLayout");
+    m_pGridLayout->setHorizontalSpacing(2);
+    m_pGridLayout->setVerticalSpacing(1);
+
+    m_pApplicationLabel = new QLabel(this);
+    m_pApplicationLabel->setObjectName(QStringLiteral("m_pApplicationLabel"));
+    QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Preferred);
+    sizePolicy1.setHorizontalStretch(0);
+    sizePolicy1.setVerticalStretch(0);
+    sizePolicy1.setHeightForWidth(m_pApplicationLabel->sizePolicy().hasHeightForWidth());
+    m_pApplicationLabel->setSizePolicy(sizePolicy1);
+    m_pApplicationLabel->setTextFormat(Qt::AutoText);
+    m_pApplicationLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    m_pApplicationLabel->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "<span style=\"color: red;\">*</span>Application to trace:", 0));
+
+    m_pGridLayout->addWidget(m_pApplicationLabel, 0, 0, 1, 1);
+
+    m_pApplicationLineEdit = new QLineEdit(this);
+    m_pApplicationLineEdit->setObjectName(QStringLiteral("m_pApplicationLineEdit"));
+    QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    sizePolicy2.setHorizontalStretch(1);
+    sizePolicy2.setVerticalStretch(0);
+    sizePolicy2.setHeightForWidth(m_pApplicationLineEdit->sizePolicy().hasHeightForWidth());
+    m_pApplicationLineEdit->setSizePolicy(sizePolicy2);
+
+    m_pGridLayout->addWidget(m_pApplicationLineEdit, 0, 1, 1, 1);
+
+    m_pFindApplicationButton = new QPushButton(this);
+    m_pFindApplicationButton->setObjectName(QStringLiteral("m_pFindApplicationButton"));
+    QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    sizePolicy.setHorizontalStretch(0);
+    sizePolicy.setVerticalStretch(0);
+    sizePolicy.setHeightForWidth(m_pFindApplicationButton->sizePolicy().hasHeightForWidth());
+    m_pFindApplicationButton->setSizePolicy(sizePolicy);
+    m_pFindApplicationButton->setMaximumSize(QSize(20, 16777215));
+    m_pFindApplicationButton->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "...", 0));
+
+    m_pGridLayout->addWidget(m_pFindApplicationButton, 0, 2, 1, 1);
+
+    m_pArgumentsLabel = new QLabel(this);
+    m_pArgumentsLabel->setObjectName(QStringLiteral("m_pArgumentsLabel"));
+    sizePolicy1.setHeightForWidth(m_pArgumentsLabel->sizePolicy().hasHeightForWidth());
+    m_pArgumentsLabel->setSizePolicy(sizePolicy1);
+    m_pArgumentsLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    m_pArgumentsLabel->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "Application arguments:", 0));
+
+    m_pGridLayout->addWidget(m_pArgumentsLabel, 1, 0, 1, 1);
+
+    m_pArgumentsLineEdit = new QLineEdit(this);
+    m_pArgumentsLineEdit->setObjectName(QStringLiteral("m_pArgumentsLineEdit"));
+    QSizePolicy sizePolicy3(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    sizePolicy3.setHorizontalStretch(0);
+    sizePolicy3.setVerticalStretch(0);
+    sizePolicy3.setHeightForWidth(m_pArgumentsLineEdit->sizePolicy().hasHeightForWidth());
+    m_pArgumentsLineEdit->setSizePolicy(sizePolicy3);
+
+    m_pGridLayout->addWidget(m_pArgumentsLineEdit, 1, 1, 1, 2);
+
+    m_pWorkingDirLabel = new QLabel(this);
+    m_pWorkingDirLabel->setObjectName(QStringLiteral("m_pWorkingDirLabel"));
+    sizePolicy1.setHeightForWidth(m_pWorkingDirLabel->sizePolicy().hasHeightForWidth());
+    m_pWorkingDirLabel->setSizePolicy(sizePolicy1);
+    m_pWorkingDirLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    m_pWorkingDirLabel->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "Working directory:", 0));
+
+    m_pGridLayout->addWidget(m_pWorkingDirLabel, 2, 0, 1, 1);
+
+    m_pWorkingDirLineEdit = new QLineEdit(this);
+    m_pWorkingDirLineEdit->setObjectName(QStringLiteral("m_pWorkingDirLineEdit"));
+    sizePolicy3.setHeightForWidth(m_pWorkingDirLineEdit->sizePolicy().hasHeightForWidth());
+    m_pWorkingDirLineEdit->setSizePolicy(sizePolicy3);
+
+    m_pGridLayout->addWidget(m_pWorkingDirLineEdit, 2, 1, 1, 2);
+
+    m_pVkLayerPathLabel = new QLabel(this);
+    m_pVkLayerPathLabel->setObjectName(QStringLiteral("m_pVkLayerPathLabel"));
+    m_pVkLayerPathLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    m_pVkLayerPathLabel->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "<span style=\"color: red;\">*</span>VK_LAYER_PATH:", 0));
+    m_pVkLayerPathLabel->setDisabled(true);
+
+    m_pGridLayout->addWidget(m_pVkLayerPathLabel, 3, 0, 1, 1);
+
+    m_pVkLayerPathLineEdit = new QLineEdit(this);
+    m_pVkLayerPathLineEdit->setObjectName(QStringLiteral("m_pVkLayerPathLineEdit"));
+    m_pVkLayerPathLineEdit->setText(QString());
+
+    m_pGridLayout->addWidget(m_pVkLayerPathLineEdit, 3, 1, 1, 1);
+
+    m_pVkLayerPathButton = new QPushButton(this);
+    m_pVkLayerPathButton->setObjectName(QStringLiteral("m_pVkLayerPathButton"));
+    sizePolicy.setHeightForWidth(m_pVkLayerPathButton->sizePolicy().hasHeightForWidth());
+    m_pVkLayerPathButton->setSizePolicy(sizePolicy);
+    m_pVkLayerPathButton->setMinimumSize(QSize(0, 0));
+    m_pVkLayerPathButton->setMaximumSize(QSize(20, 16777215));
+    m_pVkLayerPathButton->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "...", 0));
+
+    m_pGridLayout->addWidget(m_pVkLayerPathButton, 3, 2, 1, 1);
+
+    m_pTracefileLabel = new QLabel(this);
+    m_pTracefileLabel->setObjectName(QStringLiteral("m_pTracefileLabel"));
+    m_pTracefileLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    m_pTracefileLabel->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "<span style=\"color: red;\">*</span>Output trace file:", 0));
+
+    m_pGridLayout->addWidget(m_pTracefileLabel, 4, 0, 1, 1);
+
+    m_pTraceFileLineEdit = new QLineEdit(this);
+    m_pTraceFileLineEdit->setObjectName(QStringLiteral("m_pTraceFileLineEdit"));
+    m_pTraceFileLineEdit->setText(QString());
+
+    m_pGridLayout->addWidget(m_pTraceFileLineEdit, 4, 1, 1, 1);
+
+    m_pFindTraceFileButton = new QPushButton(this);
+    m_pFindTraceFileButton->setObjectName(QStringLiteral("m_pFindTraceFileButton"));
+    sizePolicy.setHeightForWidth(m_pFindTraceFileButton->sizePolicy().hasHeightForWidth());
+    m_pFindTraceFileButton->setSizePolicy(sizePolicy);
+    m_pFindTraceFileButton->setMinimumSize(QSize(0, 0));
+    m_pFindTraceFileButton->setMaximumSize(QSize(20, 16777215));
+    m_pFindTraceFileButton->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "...", 0));
+
+    m_pGridLayout->addWidget(m_pFindTraceFileButton, 4, 2, 1, 1);
+
+    m_pButtonFrame = new QFrame(this);
+    m_pButtonFrame->setObjectName(QStringLiteral("m_pButtonFrame"));
+    m_pButtonFrame->setFrameShape(QFrame::NoFrame);
+    m_pButtonFrame->setFrameShadow(QFrame::Raised);
+
+    m_pButtonHorizontalLayout = new QHBoxLayout(m_pButtonFrame);
+    m_pButtonHorizontalLayout->setObjectName(QStringLiteral("m_pButtonHorizontalLayout"));
+    m_pButtonHorizontalLayout->setContentsMargins(0, 0, 0, 0);
+    m_pButtonHSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+
+    m_pButtonHorizontalLayout->addItem(m_pButtonHSpacer);
+
+    m_pCancelButton = new QPushButton(m_pButtonFrame);
+    m_pCancelButton->setObjectName(QStringLiteral("m_pCancelButton"));
+    m_pCancelButton->setText("Cancel");
+    m_pCancelButton->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "Cancel", 0));
+
+    m_pButtonHorizontalLayout->addWidget(m_pCancelButton);
+
+    m_pOkButton = new QPushButton(m_pButtonFrame);
+    m_pOkButton->setObjectName(QStringLiteral("m_pOkButton"));
+    m_pOkButton->setEnabled(false);
+    m_pOkButton->setText(QApplication::translate("vktraceviewer_QGenerateTraceDialog", "OK", 0));
+
+    m_pButtonHorizontalLayout->addWidget(m_pOkButton);
+
+    m_pButtonHSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
+
+    m_pButtonHorizontalLayout->addItem(m_pButtonHSpacer2);
+
+    m_pGridLayout->addWidget(m_pButtonFrame, 5, 0, 1, 2);
+
+    QWidget::setTabOrder(m_pApplicationLineEdit, m_pFindApplicationButton);
+    QWidget::setTabOrder(m_pFindApplicationButton, m_pArgumentsLineEdit);
+    QWidget::setTabOrder(m_pArgumentsLineEdit, m_pTraceFileLineEdit);
+    QWidget::setTabOrder(m_pTraceFileLineEdit, m_pFindTraceFileButton);
+    QWidget::setTabOrder(m_pFindTraceFileButton, m_pOkButton);
+    QWidget::setTabOrder(m_pOkButton, m_pCancelButton);
+    QWidget::setTabOrder(m_pCancelButton, m_pApplicationLineEdit);
+
+    connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(reject()));
+    connect(m_pOkButton, SIGNAL(clicked()), this, SLOT(accept()));
+    connect(m_pFindApplicationButton, SIGNAL(clicked()), this, SLOT(on_findApplicationButton_clicked()));
+    connect(m_pVkLayerPathButton, SIGNAL(clicked()), this, SLOT(on_vkLayerPathButton_clicked()));
+    connect(m_pFindTraceFileButton, SIGNAL(clicked()), this, SLOT(on_findTraceFileButton_clicked()));
+    connect(m_pApplicationLineEdit, SIGNAL(textChanged(QString)), SLOT(on_applicationLineEdit_textChanged(QString)));
+    connect(m_pVkLayerPathLineEdit, SIGNAL(textChanged(QString)), SLOT(on_vkLayerPathLineEdit_textChanged(QString)));
+    connect(m_pTraceFileLineEdit, SIGNAL(textChanged(QString)), SLOT(on_traceFileLineEdit_textChanged(QString)));
+}
+
+vktraceviewer_QGenerateTraceDialog::~vktraceviewer_QGenerateTraceDialog()
+{
+}
+
+int vktraceviewer_QGenerateTraceDialog::exec()
+{
+    if (g_settings.gentrace_application != NULL)
+    {
+        m_pApplicationLineEdit->setText(QString(g_settings.gentrace_application));
+    }
+    if (g_settings.gentrace_arguments != NULL)
+    {
+        m_pArgumentsLineEdit->setText(QString(g_settings.gentrace_arguments));
+    }
+    if (g_settings.gentrace_working_dir != NULL)
+    {
+        m_pWorkingDirLineEdit->setText(QString(g_settings.gentrace_working_dir));
+    }
+
+    QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
+    if (environment.contains("VK_LAYER_PATH"))
+    {
+        m_pVkLayerPathLineEdit->setText(QString(environment.value("VK_LAYER_PATH")));
+    }
+    else
+    {
+        if (g_settings.gentrace_vk_layer_path != NULL)
+        {
+            m_pVkLayerPathLineEdit->setText(QString(g_settings.gentrace_vk_layer_path));
+        }
+    }
+    if (g_settings.gentrace_output_file != NULL)
+    {
+        m_pTraceFileLineEdit->setText(QString(g_settings.gentrace_output_file));
+    }
+
+    int result = QDialog::exec();
+
+    if (result == QDialog::Accepted)
+    {
+        bool bSuccess = launch_application_to_generate_trace();
+
+        if (!bSuccess)
+        {
+            result = vktraceviewer_QGenerateTraceDialog::Failed;
+        }
+    }
+
+    return result;
+}
+
+QString vktraceviewer_QGenerateTraceDialog::get_trace_file_path()
+{
+    return m_pTraceFileLineEdit->text();
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_applicationLineEdit_textChanged(const QString &text)
+{
+    check_inputs();
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_vkLayerPathLineEdit_textChanged(const QString &text)
+{
+    check_inputs();
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_traceFileLineEdit_textChanged(const QString &text)
+{
+    check_inputs();
+}
+
+void vktraceviewer_QGenerateTraceDialog::check_inputs()
+{
+    bool applicationFileEntered = m_pApplicationLineEdit->text().size() != 0;
+    bool traceFileEntered = m_pTraceFileLineEdit->text().size() != 0;
+    bool vkLayerPathEntered = m_pVkLayerPathLineEdit->text().size() != 0;
+    m_pOkButton->setEnabled(applicationFileEntered && traceFileEntered && vkLayerPathEntered);
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_vkLayerPathButton_clicked()
+{
+    // open file dialog
+    QString suggestedName = m_pVkLayerPathLineEdit->text();
+    if (suggestedName.isEmpty())
+    {
+        suggestedName = QCoreApplication::applicationDirPath();
+    }
+
+    QString selectedName = QFileDialog::getExistingDirectory(this, tr("Find VK_LAYER_PATH Directory"), suggestedName, 0);
+    if (!selectedName.isEmpty())
+    {
+        m_pVkLayerPathLineEdit->setText(selectedName);
+    }
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_findApplicationButton_clicked()
+{
+    // open file dialog
+    QString suggestedName = m_pApplicationLineEdit->text();
+    QString selectedName = QFileDialog::getOpenFileName(this, tr("Find Application to Trace"), suggestedName, "");
+    if (!selectedName.isEmpty())
+    {
+        m_pApplicationLineEdit->setText(selectedName);
+    }
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_findTraceFileButton_clicked()
+{
+    // open file dialog
+    QString suggestedName = m_pTraceFileLineEdit->text();
+    QString selectedName = QFileDialog::getSaveFileName(this, tr("Output Trace File"), suggestedName, tr("vktrace file (*.vktrace *.*)"));
+    if (!selectedName.isEmpty())
+    {
+        m_pTraceFileLineEdit->setText(selectedName);
+    }
+}
+
+bool vktraceviewer_QGenerateTraceDialog::launch_application_to_generate_trace()
+{
+    QString application = m_pApplicationLineEdit->text();
+    QString arguments = m_pArgumentsLineEdit->text();
+    QString workingDirectory = m_pWorkingDirLineEdit->text();
+    QString vkLayerPath = m_pVkLayerPathLineEdit->text();
+    QString outputTraceFile = get_trace_file_path();
+
+    // update settings
+    if (g_settings.gentrace_application != NULL)
+    {
+        vktrace_free(g_settings.gentrace_application);
+    }
+    g_settings.gentrace_application = vktrace_allocate_and_copy(application.toStdString().c_str());
+
+    if (g_settings.gentrace_arguments != NULL)
+    {
+        vktrace_free(g_settings.gentrace_arguments);
+    }
+    g_settings.gentrace_arguments = vktrace_allocate_and_copy(arguments.toStdString().c_str());
+
+    if (g_settings.gentrace_working_dir != NULL)
+    {
+        vktrace_free(g_settings.gentrace_working_dir);
+    }
+    g_settings.gentrace_working_dir = vktrace_allocate_and_copy(workingDirectory.toStdString().c_str());
+
+    if (g_settings.gentrace_vk_layer_path != NULL)
+    {
+        vktrace_free(g_settings.gentrace_vk_layer_path);
+    }
+    g_settings.gentrace_vk_layer_path = vktrace_allocate_and_copy(vkLayerPath.toStdString().c_str());
+
+    if (g_settings.gentrace_output_file != NULL)
+    {
+        vktrace_free(g_settings.gentrace_output_file);
+    }
+    g_settings.gentrace_output_file = vktrace_allocate_and_copy(outputTraceFile.toStdString().c_str());
+    vktraceviewer_settings_updated();
+
+    QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
+    environment.insert("VK_LAYER_PATH", vkLayerPath);
+
+    m_pGenerateTraceProcess = new QProcess();
+    connect(m_pGenerateTraceProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(on_readStandardOutput()));
+    connect(m_pGenerateTraceProcess, SIGNAL(readyReadStandardError()), this, SLOT(on_readStandardError()));
+
+    emit OutputMessage(VKTRACE_LOG_VERBOSE, QString("Tracing application: %1").arg(application));
+
+    // backup existing environment
+    QProcessEnvironment tmpEnv = m_pGenerateTraceProcess->processEnvironment();
+    m_pGenerateTraceProcess->setProcessEnvironment(environment);
+
+    QString vktraceviewer = QCoreApplication::applicationDirPath() + "/vktrace";
+
+#if defined(PLATFORM_64BIT)
+    vktraceviewer += "";
+#else
+    vktraceviewer += "32";
+#endif
+
+#if defined(PLATFORM_WINDOWS)
+    vktraceviewer += ".exe";
+#endif
+
+    QString cmdline = vktraceviewer + " -p \"" + application + "\" -o \"" + outputTraceFile + "\"";
+
+    if (!workingDirectory.isEmpty())
+    {
+        cmdline += " -w \"" + workingDirectory + "\"";
+    }
+
+    if (!arguments.isEmpty())
+    {
+        cmdline += " -- " + arguments;
+    }
+
+    bool bCompleted = false;
+    m_pGenerateTraceProcess->start(cmdline);
+    if (m_pGenerateTraceProcess->waitForStarted() == false)
+    {
+        emit OutputMessage(VKTRACE_LOG_ERROR, "Application could not be executed.");
+    }
+    else
+    {
+        // This is a bad idea as it will wait forever,
+        // but if the replay is taking forever then we have bigger problems.
+        if (m_pGenerateTraceProcess->waitForFinished(-1))
+        {
+            emit OutputMessage(VKTRACE_LOG_VERBOSE, "Trace Completed!");
+        }
+        int procRetValue = m_pGenerateTraceProcess->exitCode();
+        if (procRetValue == -2)
+        {
+            // proc failed to starts
+          emit OutputMessage(VKTRACE_LOG_ERROR, "Application could not be executed.");
+        }
+        else if (procRetValue == -1)
+        {
+            // proc crashed
+            emit OutputMessage(VKTRACE_LOG_ERROR, "Application aborted unexpectedly.");
+        }
+        else if (procRetValue == 0)
+        {
+            // success
+            bCompleted = true;
+        }
+        else
+        {
+            // some other return value
+            bCompleted = false;
+        }
+    }
+
+    // restore previous environment
+    m_pGenerateTraceProcess->setProcessEnvironment(tmpEnv);
+
+    if (m_pGenerateTraceProcess != NULL)
+    {
+        delete m_pGenerateTraceProcess;
+        m_pGenerateTraceProcess = NULL;
+    }
+
+    return bCompleted;
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_readStandardOutput()
+{
+    m_pGenerateTraceProcess->setReadChannel(QProcess::StandardOutput);
+    while (m_pGenerateTraceProcess->canReadLine())
+    {
+        QByteArray output = m_pGenerateTraceProcess->readLine();
+        if (output.endsWith("\n"))
+        {
+            output.remove(output.size() - 1, 1);
+        }
+        emit OutputMessage(VKTRACE_LOG_VERBOSE, output.constData());
+    }
+}
+
+void vktraceviewer_QGenerateTraceDialog::on_readStandardError()
+{
+    m_pGenerateTraceProcess->setReadChannel(QProcess::StandardError);
+    while (m_pGenerateTraceProcess->canReadLine())
+    {
+        QByteArray output = m_pGenerateTraceProcess->readLine();
+        if (output.endsWith("\n"))
+        {
+            output.remove(output.size() - 1, 1);
+        }
+        emit OutputMessage(VKTRACE_LOG_ERROR, output.constData());
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qgeneratetracedialog.h b/vktrace/src/vktrace_viewer/vktraceviewer_qgeneratetracedialog.h
new file mode 100644
index 0000000..61a22be
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qgeneratetracedialog.h
@@ -0,0 +1,104 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_QGENERATETRACEDIALOG_H
+#define VKTRACEVIEWER_QGENERATETRACEDIALOG_H
+
+extern "C"
+{
+#include "vktrace_platform.h"
+#include "vktrace_tracelog.h"
+}
+
+#include <QDialog>
+#include <QProcessEnvironment>
+
+#include <QVariant>
+#include <QAction>
+#include <QApplication>
+#include <QButtonGroup>
+#include <QCheckBox>
+#include <QFrame>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QSpacerItem>
+
+class vktraceviewer_QGenerateTraceDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit vktraceviewer_QGenerateTraceDialog(QWidget *parent = 0);
+    virtual ~vktraceviewer_QGenerateTraceDialog();
+
+    virtual int exec();
+
+    enum DialogCode {Cancelled, Succeeded, Failed};
+
+    QString get_trace_file_path();
+
+signals:
+    void OutputMessage(VktraceLogLevel level, const QString& message);
+
+public slots:
+
+private
+slots:
+    void on_applicationLineEdit_textChanged(const QString &text);
+    void on_traceFileLineEdit_textChanged(const QString &text);
+    void on_vkLayerPathLineEdit_textChanged(const QString &text);
+    void on_findApplicationButton_clicked();
+    void on_vkLayerPathButton_clicked();
+    void on_findTraceFileButton_clicked();
+
+    void on_readStandardOutput();
+    void on_readStandardError();
+private:
+    bool launch_application_to_generate_trace();
+
+    void check_inputs();
+
+    QProcess *m_pGenerateTraceProcess;
+    QGridLayout *m_pGridLayout;
+    QLabel *m_pApplicationLabel;
+    QLineEdit *m_pApplicationLineEdit;
+    QPushButton *m_pFindApplicationButton;
+    QLabel *m_pArgumentsLabel;
+    QLineEdit *m_pArgumentsLineEdit;
+    QLabel *m_pWorkingDirLabel;
+    QLineEdit *m_pWorkingDirLineEdit;
+    QLabel *m_pVkLayerPathLabel;
+    QLineEdit *m_pVkLayerPathLineEdit;
+    QPushButton *m_pVkLayerPathButton;
+    QLabel *m_pTracefileLabel;
+    QLineEdit *m_pTraceFileLineEdit;
+    QPushButton *m_pFindTraceFileButton;
+    QFrame *m_pButtonFrame;
+    QHBoxLayout *m_pButtonHorizontalLayout;
+    QSpacerItem *m_pButtonHSpacer;
+    QPushButton *m_pCancelButton;
+    QPushButton *m_pOkButton;
+    QSpacerItem *m_pButtonHSpacer2;
+};
+
+#endif // VKTRACEVIEWER_QGENERATETRACEDIALOG_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qgroupthreadsproxymodel.h b/vktrace/src/vktrace_viewer/vktraceviewer_qgroupthreadsproxymodel.h
new file mode 100644
index 0000000..9b727f7
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qgroupthreadsproxymodel.h
@@ -0,0 +1,235 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_QGROUPTHREADSPROXYMODEL_H
+#define VKTRACEVIEWER_QGROUPTHREADSPROXYMODEL_H
+
+#include "vktraceviewer_QTraceFileModel.h"
+#include <QAbstractProxyModel>
+#include <QStandardItem>
+#include <QList>
+#include <QDebug>
+
+struct GroupInfo
+{
+    int groupIndex;
+    uint32_t threadId;
+    QPersistentModelIndex modelIndex;
+    QList<QPersistentModelIndex> children;
+};
+
+class vktraceviewer_QGroupThreadsProxyModel : public QAbstractProxyModel
+{
+    Q_OBJECT
+public:
+    vktraceviewer_QGroupThreadsProxyModel(QObject *parent = 0)
+        : QAbstractProxyModel(parent)
+    {
+        buildGroups(NULL);
+    }
+
+    virtual ~vktraceviewer_QGroupThreadsProxyModel()
+    {
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual void setSourceModel(QAbstractItemModel *sourceModel)
+    {
+        QAbstractProxyModel::setSourceModel(sourceModel);
+
+        if (sourceModel->inherits("vktraceviewer_QTraceFileModel"))
+        {
+            vktraceviewer_QTraceFileModel* pTFM = static_cast<vktraceviewer_QTraceFileModel*>(sourceModel);
+            buildGroups(pTFM);
+        }
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual int rowCount(const QModelIndex &parent) const
+    {
+        // ask the source
+        return sourceModel()->rowCount(mapToSource(parent));
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual bool hasChildren(const QModelIndex &parent) const
+    {
+        if (!parent.isValid())
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual QVariant data( const QModelIndex &index, int role ) const
+    {
+        if (!index.isValid())
+        {
+            return QVariant();
+        }
+
+        return mapToSource(index).data(role);
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual Qt::ItemFlags flags(const QModelIndex &index) const
+    {
+        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual int columnCount(const QModelIndex &parent) const
+    {
+        return sourceModel()->columnCount() + m_uniqueThreadIdMapToColumn.count();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
+    {
+        if (!isThreadColumn(section))
+        {
+            return sourceModel()->headerData(section, orientation, role);
+        }
+        else
+        {
+            if (role == Qt::DisplayRole)
+            {
+                int threadIndex = getThreadColumnIndex(section);
+                return QString("Thread %1").arg(m_uniqueThreadIdMapToColumn.key(threadIndex));
+            }
+        }
+
+        return QVariant();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
+    {
+        if (!hasIndex(row, column, parent))
+            return QModelIndex();
+
+        return createIndex(row, column);
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex parent(const QModelIndex &child) const
+    {
+        if (!child.isValid())
+            return QModelIndex();
+
+        return QModelIndex();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex mapToSource(const QModelIndex &proxyIndex) const
+    {
+        if (!proxyIndex.isValid())
+            return QModelIndex();
+
+        QModelIndex result;
+        if (!isThreadColumn(proxyIndex.column()))
+        {
+            // it is a column for the source model and not for one of our thread IDs (which isn't in the source, unless we map it to the same Thread Id column?)
+            result = sourceModel()->index(proxyIndex.row(), proxyIndex.column());
+        }
+        else
+        {
+            int threadIndex = getThreadColumnIndex(proxyIndex.column());
+            if (m_packetIndexToColumn[proxyIndex.row()] == threadIndex)
+            {
+                return sourceModel()->index(proxyIndex.row(), vktraceviewer_QTraceFileModel::Column_EntrypointName);
+            }
+        }
+
+        return result;
+    }
+
+    //---------------------------------------------------------------------------------------------
+    QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
+    {
+        if (!sourceIndex.isValid())
+            return QModelIndex();
+
+        return createIndex(sourceIndex.row(), sourceIndex.column());
+    }
+
+    //---------------------------------------------------------------------------------------------
+    virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
+    {
+        QModelIndexList results = sourceModel()->match(start, role, value, hits, flags);
+
+        for (int i = 0; i < results.count(); i++)
+        {
+            results[i] = mapFromSource(results[i]);
+        }
+
+        return results;
+    }
+
+    //---------------------------------------------------------------------------------------------
+private:
+    QMap<uint32_t, int> m_uniqueThreadIdMapToColumn;
+
+    // Each entry in the list corresponds to a packet index;
+    // the int stored in the list indicates which column the API call belongs in.
+    QList<int> m_packetIndexToColumn;
+
+    //---------------------------------------------------------------------------------------------
+    bool isThreadColumn(int columnIndex) const
+    {
+        return (columnIndex >= sourceModel()->columnCount());
+    }
+
+    //---------------------------------------------------------------------------------------------
+    int getThreadColumnIndex(int proxyColumnIndex) const
+    {
+        return proxyColumnIndex - sourceModel()->columnCount();
+    }
+
+    //---------------------------------------------------------------------------------------------
+    void buildGroups(vktraceviewer_QTraceFileModel* pTFM)
+    {
+        m_uniqueThreadIdMapToColumn.clear();
+        m_packetIndexToColumn.clear();
+
+        if (pTFM != NULL)
+        {
+            // Determine how many additional columns are needed by counting the number if different thread Ids being used.
+            for (int i = 0; i < pTFM->rowCount(); i++)
+            {
+                vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)pTFM->index(i, 0).internalPointer();
+                if (pHeader != NULL)
+                {
+                    if (!m_uniqueThreadIdMapToColumn.contains(pHeader->thread_id))
+                    {
+                        int columnIndex = m_uniqueThreadIdMapToColumn.count();
+                        m_uniqueThreadIdMapToColumn.insert(pHeader->thread_id, columnIndex);
+                    }
+
+                    m_packetIndexToColumn.append(m_uniqueThreadIdMapToColumn[pHeader->thread_id]);
+                }
+            }
+        }
+    }
+};
+
+#endif // VKTRACEVIEWER_QGROUPTHREADSPROXYMODEL_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qimageviewer.h b/vktrace/src/vktrace_viewer/vktraceviewer_qimageviewer.h
new file mode 100644
index 0000000..9f35d32
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qimageviewer.h
@@ -0,0 +1,211 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#ifndef _VKTRACEVIEWER_QIMAGEVIEWER_H_
+#define _VKTRACEVIEWER_QIMAGEVIEWER_H_
+
+#include <cassert>
+
+#include <QFileInfo>
+#include <QLabel>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QWheelEvent>
+
+// Pretend an image is a QScrollArea with some some special event handling.
+class vktraceviewer_qimageviewer : public QScrollArea
+{
+    Q_OBJECT
+public:
+    explicit vktraceviewer_qimageviewer(QWidget* parent = 0)
+        : QScrollArea(parent),
+          m_pImageLabel(NULL),
+          m_pPanStart(0, 0),
+          m_pPan(false),
+          m_pAutoFit(false)
+    {
+        // Create a basic image viewer using a QLabel to display the image.
+        m_pImageLabel = new QLabel;
+        assert(m_pImageLabel != NULL);
+
+        m_pImageLabel->setBackgroundRole(QPalette::Base);
+        m_pImageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+        m_pImageLabel->setScaledContents(true);
+
+        // The QLabel is embedded in a QScrollArea so the image can be panned.
+        this->setBackgroundRole(QPalette::Dark);
+        this->setWidget(m_pImageLabel);
+    }
+
+    virtual ~vktraceviewer_qimageviewer()
+    {
+        if (m_pImageLabel != NULL)
+        {
+            delete m_pImageLabel;
+            m_pImageLabel = NULL;
+        }
+    }
+
+    void mouseMoveEvent(QMouseEvent* event)
+    {
+        if(m_pPan)
+        {
+            this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() -
+                (event->x() - m_pPanStart.x()));
+            this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() -
+                (event->y() - m_pPanStart.y()));
+            m_pPanStart =  event->pos();
+
+            event->accept();
+            return;
+        }
+
+        event->ignore();
+    }
+
+    void mousePressEvent(QMouseEvent* event)
+    {
+        if(event->button() == Qt::MiddleButton)
+        {
+            m_pPan = true;
+            setCursor(Qt::ClosedHandCursor);
+            m_pPanStart = event->pos();
+
+            event->accept();
+            return;
+        }
+
+        event->ignore();
+    }
+
+    void mouseReleaseEvent(QMouseEvent* event)
+    {
+        if(event->button() == Qt::MiddleButton)
+        {
+            m_pPan = false;
+            setCursor(Qt::ArrowCursor);
+
+            event->accept();
+            return;
+        }
+
+        event->ignore();
+    }
+
+    void resizeEvent(QResizeEvent* event)
+    {
+        QSize const size = computeMinimumSize();
+        m_pImageLabel->setMinimumSize(size);
+
+        if(m_pAutoFit)
+        {
+            m_pImageLabel->resize(size);
+        }
+
+        event->accept();
+    }
+
+    void wheelEvent(QWheelEvent* event)
+    {
+        if(event->orientation() == Qt::Vertical)
+        {
+            // Stop automatically resizing the image when zoom is requested.
+            m_pAutoFit = false;
+
+            // Compute the scaling factor.
+            int const numDegrees = event->delta() / 8;
+            int const numSteps = numDegrees / 15;
+            double const factor = 1.0 + 0.1 * numSteps;
+
+            m_pImageLabel->resize(m_pImageLabel->size() * factor);
+
+            zoomScrollBar(this->horizontalScrollBar(), factor);
+            zoomScrollBar(this->verticalScrollBar(), factor);
+
+            event->accept();
+            return;
+        }
+
+        event->ignore();
+    }
+
+    bool loadImage(QString const& fileName)
+    {
+        QFileInfo fileInfo(fileName);
+        if(!fileInfo.exists() || !fileInfo.isFile())
+        {
+            return false;
+        }
+
+        QImage image(fileName);
+        if(image.isNull())
+        {
+            return false;
+        }
+
+        m_pImageLabel->setPixmap(QPixmap::fromImage(image));
+        m_pImageLabel->adjustSize();
+        m_pImageLabel->setMaximumSize(image.size());
+
+        // Resize the image to the scroll area.
+        m_pAutoFit = true;
+
+        return true;
+    }
+
+private:
+    QSize computeMinimumSize() const
+    {
+        if(m_pImageLabel->pixmap() == NULL)
+        {
+            return QSize(0, 0);
+        }
+
+        double const aspect = m_pImageLabel->pixmap()->width() /
+            m_pImageLabel->pixmap()->height();
+        if(aspect > 1.0)
+        {
+            int const minWidth = this->width() - 2 * this->frameWidth();
+            int const minHeight = minWidth * 1.0 / aspect;
+            return QSize(minWidth, minHeight);
+        }
+        else
+        {
+            int const minHeight = this->height() - 2 * this->frameWidth();
+            int const minWidth = minHeight * aspect;
+            return QSize(minWidth, minHeight);
+        }
+    }
+
+    void zoomScrollBar(QScrollBar* scrollBar, double const& factor)
+    {
+        int const value = static_cast<int>(factor * scrollBar->value() +
+            ((factor - 1.0) * scrollBar->pageStep() / 2));
+        scrollBar->setValue(value);
+    }
+
+    QLabel* m_pImageLabel;
+    QPoint m_pPanStart;
+    bool m_pPan;
+    bool m_pAutoFit;
+};
+
+#endif //_VKTRACEVIEWER_QIMAGEVIEWER_H_
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qsettingsdialog.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_qsettingsdialog.cpp
new file mode 100644
index 0000000..d30972b
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qsettingsdialog.cpp
@@ -0,0 +1,168 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_qsettingsdialog.h"
+#include "vktraceviewer_settings.h"
+
+#include <QDialogButtonBox>
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QResizeEvent>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+Q_DECLARE_METATYPE(vktrace_SettingInfo*);
+
+vktraceviewer_QSettingsDialog::vktraceviewer_QSettingsDialog(QWidget *parent)
+    : QDialog(parent),
+      m_pSettingGroups(NULL),
+      m_numSettingGroups(0)
+{
+    this->setWindowTitle("Settings");
+
+    QVBoxLayout* pLayout = new QVBoxLayout(this);
+    this->setLayout(pLayout);
+
+    m_pTabWidget = new QTabWidget(this);
+    pLayout->addWidget(m_pTabWidget);
+
+    QDialogButtonBox* pButtonBox = new QDialogButtonBox(/*QDialogButtonBox::Save | QDialogButtonBox::Cancel*/);
+    pButtonBox->addButton("OK", QDialogButtonBox::RejectRole);
+    pButtonBox->addButton("Save && Apply", QDialogButtonBox::AcceptRole);
+    pLayout->addWidget(pButtonBox);
+    connect(pButtonBox, SIGNAL(accepted()), this, SLOT(acceptCB()));
+    connect(pButtonBox, SIGNAL(rejected()), this, SLOT(cancelCB()));
+}
+
+vktraceviewer_QSettingsDialog::~vktraceviewer_QSettingsDialog()
+{
+    removeTabs();
+}
+
+void vktraceviewer_QSettingsDialog::removeTabs()
+{
+    if (m_pTabWidget == NULL)
+    {
+        return;
+    }
+
+    while (m_pTabWidget->count() > 0)
+    {
+        m_pTabWidget->removeTab(0);
+    }
+}
+
+void vktraceviewer_QSettingsDialog::setGroups(vktrace_SettingGroup* pSettingGroups, unsigned int numGroups)
+{
+    removeTabs();
+
+    m_pSettingGroups = pSettingGroups;
+    m_numSettingGroups = numGroups;
+
+    // add tabs to display other groups of settings
+    for (unsigned int i = 0; i < m_numSettingGroups; i++)
+    {
+        this->add_tab(&m_pSettingGroups[i]);
+    }
+}
+
+void vktraceviewer_QSettingsDialog::acceptCB()
+{
+    save();
+}
+
+void vktraceviewer_QSettingsDialog::cancelCB()
+{
+    reject();
+}
+
+void vktraceviewer_QSettingsDialog::resizeEvent(QResizeEvent *pEvent)
+{
+    emit Resized(pEvent->size().width(), pEvent->size().height());
+}
+
+void vktraceviewer_QSettingsDialog::save()
+{
+    // save vktraceviewer settings
+
+    emit SaveSettings(m_pSettingGroups, m_numSettingGroups);
+    accept();
+}
+
+void vktraceviewer_QSettingsDialog::add_tab(vktrace_SettingGroup* pGroup)
+{
+    QWidget* pTab = new QWidget(m_pTabWidget);
+    m_pTabWidget->addTab(pTab, pGroup->pName);
+    QHBoxLayout* pLayout = new QHBoxLayout(pTab);
+    pTab->setLayout(pLayout);
+
+    QTableWidget* pTable = new QTableWidget(pGroup->numSettings, 2, pTab);
+
+    pLayout->addWidget(pTable, 1);
+
+    QStringList headers;
+    headers << "Name" << "Value";
+    pTable->setHorizontalHeaderLabels(headers);
+    pTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+    pTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
+
+    connect(pTable, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(settingEdited(QTableWidgetItem *)));
+    int row = 0;
+    for (unsigned int i = 0; i < pGroup->numSettings; i++)
+    {
+        QTableWidgetItem *nameItem = new QTableWidgetItem(pGroup->pSettings[i].pLongName);
+        nameItem->setData(Qt::UserRole, QVariant::fromValue(&pGroup->pSettings[i]));
+        pTable->setItem(row, 0, nameItem);
+
+        char* pLeakedMem = vktrace_SettingInfo_stringify_value(&pGroup->pSettings[i]);
+        QTableWidgetItem *valueItem = new QTableWidgetItem(pLeakedMem);
+        valueItem->setData(Qt::UserRole, QVariant::fromValue(&pGroup->pSettings[i]));
+        pTable->setItem(row, 1, valueItem);
+
+        ++row;
+    }
+}
+
+void vktraceviewer_QSettingsDialog::settingEdited(QTableWidgetItem *pItem)
+{
+    vktrace_SettingInfo* pSetting = pItem->data(Qt::UserRole).value<vktrace_SettingInfo*>();
+
+    if (pSetting != NULL)
+    {
+        if (pItem->column() == 0)
+        {
+            vktrace_free((void*)pSetting->pLongName);
+            pSetting->pLongName = vktrace_allocate_and_copy(pItem->text().toStdString().c_str());
+        }
+        else if (pItem->column() == 1)
+        {
+            vktrace_SettingInfo_parse_value(pSetting, pItem->text().toStdString().c_str());
+        }
+        else
+        {
+            // invalid column
+        }
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qsettingsdialog.h b/vktrace/src/vktrace_viewer/vktraceviewer_qsettingsdialog.h
new file mode 100644
index 0000000..ca637d0
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qsettingsdialog.h
@@ -0,0 +1,65 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef _VKTRACEVIEWER_QSETTINGSDIALOG_H_
+#define _VKTRACEVIEWER_QSETTINGSDIALOG_H_
+
+#include <QDialog>
+#include <QTabWidget>
+#include <QTableWidgetItem>
+#include "vktraceviewer_settings.h"
+
+Q_DECLARE_METATYPE(vktrace_SettingGroup)
+
+class vktraceviewer_QSettingsDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    vktraceviewer_QSettingsDialog(QWidget* parent = 0);
+    ~vktraceviewer_QSettingsDialog();
+
+    void setGroups(vktrace_SettingGroup* pSettingGroups, unsigned int numGroups);
+
+    void save();
+
+private:
+    vktrace_SettingGroup* m_pSettingGroups;
+    unsigned int m_numSettingGroups;
+
+    QTabWidget* m_pTabWidget;
+    void add_tab(vktrace_SettingGroup* pGroup);
+    virtual void resizeEvent(QResizeEvent *pEvent);
+    void removeTabs();
+
+signals:
+    void SaveSettings(vktrace_SettingGroup* pUpdatedSettingGroups, unsigned int numGroups);
+    void Resized(unsigned int width, unsigned int height);
+
+private
+slots:
+    void acceptCB();
+    void cancelCB();
+
+    void settingEdited(QTableWidgetItem *pItem);
+
+};
+
+#endif // _VKTRACEVIEWER_QSETTINGSDIALOG_H_
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qsvgviewer.h b/vktrace/src/vktrace_viewer/vktraceviewer_qsvgviewer.h
new file mode 100644
index 0000000..09ac674
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qsvgviewer.h
@@ -0,0 +1,135 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#ifndef _VKTRACEVIEWER_QSVGVIEWER_H_
+#define _VKTRACEVIEWER_QSVGVIEWER_H_
+
+#include <QFileInfo>
+#include <QGraphicsSvgItem>
+#include <QGraphicsView>
+#include <QWheelEvent>
+
+class vktraceviewer_qsvgviewer : public QGraphicsView
+{
+    Q_OBJECT
+public:
+    vktraceviewer_qsvgviewer(QWidget* parent = 0) :
+        QGraphicsView(parent),
+        disabledScene(NULL),
+        enabledScene(NULL),
+        autoFit(false)
+    {
+        // The destructor for QGraphicsScene will be called when this QGraphicsView is
+        // destroyed.
+        enabledScene = new QGraphicsScene(this);
+        disabledScene = new QGraphicsScene(this);
+        this->setScene(disabledScene);
+
+        // Anchor the point under the mouse during view transformations.
+        this->setTransformationAnchor(AnchorUnderMouse);
+
+        // Enable drag scrolling with the left mouse button.
+        this->setDragMode(ScrollHandDrag);
+
+        // Always update the entire viewport. Don't waste time trying to figure out
+        // which items need to be updated since there is only one.
+        this->setViewportUpdateMode(FullViewportUpdate);
+    }
+
+    void changeEvent(QEvent* event)
+    {
+        switch(event->type())
+        {
+            case QEvent::EnabledChange:
+                if(this->isEnabled())
+                {
+                    this->setScene(enabledScene);
+                }
+                else
+                {
+                    this->setScene(disabledScene);
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    void paintEvent(QPaintEvent* event)
+    {
+        // Resize the scene to fit the widget. This is deferred until the first paint
+        // event (when the widget size is known).
+        if(autoFit)
+        {
+            this->fitInView(enabledScene->itemsBoundingRect(), Qt::KeepAspectRatio);
+            autoFit = false;
+        }
+
+        QGraphicsView::paintEvent(event);
+    }
+
+    void wheelEvent(QWheelEvent* event)
+    {
+        if(event->orientation() == Qt::Vertical)
+        {
+            // The delta value is in units of eighths of a degree.
+            qreal const degrees = event->delta() / 8.0;
+
+            // According to Qt documentation, mice have steps of 15-degrees.
+            qreal const steps = degrees / 15.0;
+
+            qreal factor = 1.0 + 0.1 * steps;
+
+            this->scale(factor, factor);
+
+            event->accept();
+        }
+    }
+
+    bool load(QString const& fileName)
+    {
+        QFileInfo fileInfo(fileName);
+        if(!fileInfo.exists() || !fileInfo.isFile())
+        {
+            return false;
+        }
+
+        this->resetTransform();
+
+        enabledScene->clear();
+
+        // The destructor for QGraphicsSvgItem will be called when the scene is cleared.
+        // This occurs when a SVG is loaded or when the QGraphicsScene is destroyed.
+        enabledScene->addItem(new QGraphicsSvgItem(fileName));
+
+        autoFit = true;
+
+        return true;
+    }
+
+private:
+    QGraphicsScene* disabledScene;
+    QGraphicsScene* enabledScene;
+
+    bool autoFit;
+};
+
+#endif // _VKTRACEVIEWER_QSVGVIEWER_H_
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qtimelineview.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_qtimelineview.cpp
new file mode 100644
index 0000000..2995da0
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qtimelineview.cpp
@@ -0,0 +1,761 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include <QApplication>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QToolTip>
+#ifdef _WIN32
+// The following line allows Visual Studio to provide the M_PI_2 constant:
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+#include "vktraceviewer_qtimelineview.h"
+#include "vktraceviewer_QTraceFileModel.h"
+
+// helper
+float u64ToFloat(uint64_t value)
+{
+    // taken from: http://stackoverflow.com/questions/4400747/converting-from-unsigned-long-long-to-float-with-round-to-nearest-even
+    const int mask_bit_count = 31;
+
+    // How many bits are needed?
+    int b = sizeof(uint64_t) * CHAR_BIT - 1;
+    for (; b >= 0; --b)
+    {
+        if (value & (1ull << b))
+        {
+            break;
+        }
+    }
+
+    // If there are few enough significant bits, use normal cast and done.
+    if (b < mask_bit_count)
+    {
+        return static_cast<float>(value & ~1ull);
+    }
+
+    // Save off the low-order useless bits:
+    uint64_t low_bits = value & ((1ull << (b - mask_bit_count)) - 1);
+
+    // Now mask away those useless low bits:
+    value &= ~((1ull << (b - mask_bit_count)) - 1);
+
+    // Finally, decide how to round the new LSB:
+    if (low_bits > ((1ull << (b - mask_bit_count)) / 2ull))
+    {
+        // Round up.
+        value |= (1ull << (b - mask_bit_count));
+    }
+    else
+    {
+        // Round down.
+        value &= ~(1ull << (b - mask_bit_count));
+    }
+
+    return static_cast<float>(value);
+}
+
+//=============================================================================
+vktraceviewer_QTimelineItemDelegate::vktraceviewer_QTimelineItemDelegate(QObject *parent)
+    : QAbstractItemDelegate(parent)
+{
+    assert(parent != NULL);
+}
+
+vktraceviewer_QTimelineItemDelegate::~vktraceviewer_QTimelineItemDelegate()
+{
+
+}
+
+void vktraceviewer_QTimelineItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+    vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+
+    if (pHeader->entrypoint_end_time <= pHeader->entrypoint_begin_time)
+    {
+        return;
+    }
+
+    painter->save();
+    {
+        vktraceviewer_QTimelineView* pTimeline = (vktraceviewer_QTimelineView*)parent();
+        if (pTimeline != NULL)
+        {
+            QRectF rect = option.rect;
+
+            if (rect.width() == 0)
+            {
+                rect.setWidth(1);
+            }
+
+            float duration = u64ToFloat(pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time);
+            float durationRatio = duration / pTimeline->getMaxItemDuration();
+            int intensity = std::min(255, (int)(durationRatio * 255.0f));
+            QColor color(intensity, 255-intensity, 0);
+
+            // add gradient to the items better distinguish between the end of one and beginning of the next
+            QLinearGradient linearGrad(rect.center(), rect.bottomRight());
+            linearGrad.setColorAt(0, color);
+            linearGrad.setColorAt(1, color.darker(150));
+
+            painter->setBrush(linearGrad);
+            painter->setPen(Qt::NoPen);
+
+            painter->drawRect(rect);
+
+            if (rect.width() >= 2)
+            {
+                // draw shadow and highlight around the item
+                painter->setPen(color.darker(175));
+                painter->drawLine(rect.right()-1, rect.top(), rect.right()-1, rect.bottom()-1);
+                painter->drawLine(rect.right()-1, rect.bottom()-1, rect.left(), rect.bottom()-1);
+
+                painter->setPen(color.lighter());
+                painter->drawLine(rect.left(), rect.bottom()-1, rect.left(), rect.top());
+                painter->drawLine(rect.left(), rect.top(), rect.right()-1, rect.top());
+            }
+        }
+    }
+
+    painter->restore();
+}
+
+QSize vktraceviewer_QTimelineItemDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+    QSize size;
+
+    vktraceviewer_QTimelineView* pTimeline = (vktraceviewer_QTimelineView*)parent();
+    if (pTimeline != NULL)
+    {
+        QRectF rect = pTimeline->visualRect(index);
+
+        size = rect.toRect().size();
+    }
+    return QSize();
+}
+
+//=============================================================================
+vktraceviewer_QTimelineView::vktraceviewer_QTimelineView(QWidget *parent) :
+    QAbstractItemView(parent),
+    m_maxItemDuration(0),
+    m_maxZoom(0.001f),
+    m_threadHeight(0),
+    m_hashIsDirty(true),
+    m_margin(10),
+    m_pPixmap(NULL),
+    m_itemDelegate(this)
+{
+    horizontalScrollBar()->setRange(0,0);
+    verticalScrollBar()->setRange(0,0);
+
+    m_background = QBrush(QColor(200,200,200));
+    m_trianglePen = QPen(Qt::darkGray);
+    m_trianglePen.setWidth(1);
+    m_textPen = QPen(Qt::white);
+    m_textFont.setPixelSize(50);
+
+    // Allows tracking the mouse position when it is over the timeline
+    setMouseTracking(true);
+
+    m_durationToViewportScale = 1;
+    m_zoomFactor = 1;
+    m_lineLength = 1;
+    m_scrollBarWidth = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+}
+
+//-----------------------------------------------------------------------------
+vktraceviewer_QTimelineView::~vktraceviewer_QTimelineView()
+{
+    m_threadIdList.clear();
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::setModel(QAbstractItemModel* pModel)
+{
+    QAbstractItemView::setModel(pModel);
+    m_hashIsDirty = true;
+    setItemDelegate(&m_itemDelegate);
+
+    m_threadIdList.clear();
+    m_threadMask.clear();
+    m_maxItemDuration = 0;
+    m_rawStartTime = 0;
+    m_rawEndTime = 0;
+
+    deletePixmap();
+
+    // Gather some stats from the model
+    if (model() == NULL)
+    {
+        horizontalScrollBar()->setRange(0,0);
+        verticalScrollBar()->setRange(0,0);
+        return;
+    }
+
+    int numRows = model()->rowCount();
+    for (int i = 0; i < numRows; i++)
+    {
+        // Count number of unique thread Ids
+        QModelIndex item = model()->index(i, vktraceviewer_QTraceFileModel::Column_ThreadId);
+        if (item.isValid())
+        {
+            uint32_t threadId = item.data().toUInt();
+            if (!m_threadIdList.contains(threadId))
+            {
+                m_threadIdList.append(threadId);
+                m_threadMask.insert(threadId, QVector<int>());
+                m_threadArea.append(QRect());
+            }
+        }
+
+        // Find duration of longest item
+        item = model()->index(i, vktraceviewer_QTraceFileModel::Column_CpuDuration);
+        if (item.isValid())
+        {
+            float duration = item.data().toFloat();
+            if (m_maxItemDuration < duration)
+            {
+                m_maxItemDuration = duration;
+            }
+        }
+    }
+
+    // Get start time
+    QModelIndex start = model()->index(0, vktraceviewer_QTraceFileModel::Column_BeginTime);
+    if (start.isValid())
+    {
+        m_rawStartTime = start.data().toULongLong();
+    }
+
+    // Get end time
+    QModelIndex end = model()->index(numRows - 1, vktraceviewer_QTraceFileModel::Column_EndTime);
+    if (end.isValid())
+    {
+        m_rawEndTime = end.data().toULongLong();
+    }
+
+    // the duration to viewport scale should allow us to map the entire timeline into the current window width.
+    m_lineLength = m_rawEndTime - m_rawStartTime;
+
+    int initialTimelineWidth = viewport()->width() - 2*m_margin - m_scrollBarWidth;
+    m_durationToViewportScale = (float)initialTimelineWidth / u64ToFloat(m_lineLength);
+
+    m_zoomFactor = m_durationToViewportScale;
+
+    verticalScrollBar()->setMaximum(1000);
+    verticalScrollBar()->setValue(0);
+    verticalScrollBar()->setPageStep(1);
+    verticalScrollBar()->setSingleStep(1);
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::calculateRectsIfNecessary()
+{
+    if (!m_hashIsDirty)
+    {
+        return;
+    }
+
+    if (model() == NULL)
+    {
+        return;
+    }
+
+    int itemHeight = m_threadHeight * 0.4;
+
+    for (int threadIndex = 0; threadIndex < m_threadIdList.size(); threadIndex++)
+    {
+        int top = (m_threadHeight * threadIndex) + (m_threadHeight * 0.5) - itemHeight/2;
+        this->m_threadArea[threadIndex] = QRect(0, top, viewport()->width(), itemHeight);
+    }
+
+    int numRows = model()->rowCount();
+    for (int row = 0; row < numRows; row++)
+    {
+        QRectF rect;
+        QModelIndex item = model()->index(row, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+
+        vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)item.internalPointer();
+
+        // make sure item is valid size
+        if (pHeader->entrypoint_end_time > pHeader->entrypoint_begin_time)
+        {
+            int threadIndex = m_threadIdList.indexOf(pHeader->thread_id);
+            int topOffset = (m_threadHeight * threadIndex) + (m_threadHeight * 0.5);
+
+            uint64_t duration = pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time;
+
+            float leftOffset = u64ToFloat(pHeader->entrypoint_begin_time - m_rawStartTime);
+            float Width = u64ToFloat(duration);
+
+            // create the rect that represents this item
+            rect.setLeft(leftOffset);
+            rect.setTop(topOffset - (itemHeight/2));
+            rect.setWidth(Width);
+            rect.setHeight(itemHeight);
+        }
+
+        m_rowToRectMap[row] = rect;
+    }
+
+    m_hashIsDirty = false;
+    viewport()->update();
+}
+
+//-----------------------------------------------------------------------------
+QRectF vktraceviewer_QTimelineView::itemRect(const QModelIndex &item) const
+{
+    QRectF rect;
+    if (item.isValid())
+    {
+        rect = m_rowToRectMap.value(item.row());
+    }
+    return rect;
+}
+
+//-----------------------------------------------------------------------------
+bool vktraceviewer_QTimelineView::event(QEvent * e)
+{
+    if (e->type() == QEvent::ToolTip)
+    {
+        QHelpEvent* pHelp = static_cast<QHelpEvent*>(e);
+        QModelIndex index = indexAt(pHelp->pos());
+        if (index.isValid())
+        {
+            vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+            QToolTip::showText(pHelp->globalPos(), QString("Call %1:\n%2").arg(pHeader->global_packet_index).arg(index.data().toString()));
+            return true;
+        }
+        else
+        {
+            QToolTip::hideText();
+        }
+    }
+
+    return QAbstractItemView::event(e);
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::resizeEvent(QResizeEvent *event)
+{
+    m_hashIsDirty = true;
+    deletePixmap();
+
+    // The duration to viewport scale should allow us to map the entire timeline into the current window width.
+    if (m_lineLength > 0)
+    {
+        // Calculate zoom ratio prior to the resize
+        float ratio = m_zoomFactor / m_durationToViewportScale;
+
+        // Adjust scale that fits the timeline duration to the viewport area
+        int timelineViewportWidth = viewport()->width() - 2*m_margin - m_scrollBarWidth;
+        m_durationToViewportScale = (float)timelineViewportWidth / u64ToFloat(m_lineLength);
+
+        // Adjust the zoom factor based on the new duration to viewport scale and the previous ratio
+        m_zoomFactor = m_durationToViewportScale * ratio;
+
+        // Adjust horizontal scroll bar to maintain current view as best as possible
+        float hRatio = (float)horizontalScrollBar()->value() / qMax(1.0f,(float)horizontalScrollBar()->maximum());
+        updateGeometries();
+        horizontalScrollBar()->setValue(hRatio * horizontalScrollBar()->maximum());
+    }
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::scrollContentsBy(int dx, int dy)
+{
+    deletePixmap();
+
+    if (dy != 0)
+    {
+        QPoint pos = m_mousePosition;
+        int focusX = pos.x();
+        if (pos.isNull())
+        {
+            // If the mouse position is NULL (ie, the mouse is not in the viewport)
+            // zooming will happen around the center of the timeline
+            focusX = (viewport()->width() - m_scrollBarWidth) / 2;
+        }
+
+        int x = focusX + horizontalScrollBar()->value();
+        float wx = (float)x / m_zoomFactor;
+
+        // The zoom factor is a smoothed interpolation (based on the vertical scroll bar value and max)
+        // between the m_durationToViewportScale (which fits the entire timeline into the viewport width)
+        // and m_maxZoom (which is initialized to 1/1000)
+        float zoomRatio = (float)verticalScrollBar()->value() / (float)verticalScrollBar()->maximum();
+        zoomRatio = 1-cos(zoomRatio*M_PI_2);
+        float diff = m_maxZoom - m_durationToViewportScale;
+        m_zoomFactor = m_durationToViewportScale + zoomRatio * diff;
+        updateGeometries();
+
+        int newValue = (wx * m_zoomFactor) - focusX;
+
+        horizontalScrollBar()->setValue(newValue);
+    }
+
+    viewport()->update();
+//    scrollDirtyRegion(dx, 0);
+//    viewport()->scroll(dx, 0);
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::mousePressEvent(QMouseEvent * event)
+{
+    QAbstractItemView::mousePressEvent(event);
+    QModelIndex index = indexAt(event->pos());
+    if (index.isValid())
+    {
+        setCurrentIndex(index);
+    }
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::mouseMoveEvent(QMouseEvent * event)
+{
+    // Since we are tracking the mouse position, we really should pass the event
+    // to our base class, Unfortunately it really sucks up performance, so don't do it for now.
+    //QAbstractItemView::mouseMoveEvent(event);
+
+    if (event->pos().x() > viewport()->width() - m_margin)
+    {
+        // The mouse position was within m_margin of the vertical scroll bar (used for zooming)
+        // Make this a null point so that zooming will happen on the center of the timeline
+        // rather than the very edge where the mouse cursor was last seen.
+        m_mousePosition = QPoint();
+    }
+    else
+    {
+        m_mousePosition = event->pos();
+    }
+    event->accept();
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::updateGeometries()
+{
+    uint64_t duration = m_rawEndTime - m_rawStartTime;
+    int hMax = scaleDurationHorizontally(duration) + 2*m_margin - viewport()->width();
+    horizontalScrollBar()->setRange(0, qMax(0, hMax));
+    horizontalScrollBar()->setPageStep(viewport()->width());
+    horizontalScrollBar()->setSingleStep(1);
+}
+
+//-----------------------------------------------------------------------------
+QRect vktraceviewer_QTimelineView::visualRect(const QModelIndex &index) const
+{
+    QRectF rectf = viewportRect(index);
+    return rectf.toRect();
+}
+
+//-----------------------------------------------------------------------------
+QRectF vktraceviewer_QTimelineView::viewportRect(const QModelIndex &index) const
+{
+    QRectF rectf = itemRect(index);
+    if (rectf.isValid())
+    {
+        rectf.moveLeft( rectf.left() * m_zoomFactor);
+        rectf.setWidth( rectf.width() * m_zoomFactor);
+
+        rectf.adjust(-horizontalScrollBar()->value(), 0,
+                     -horizontalScrollBar()->value(), 0);
+
+        // the margin is added last so that it is NOT scaled
+        rectf.adjust(m_margin, 0,
+                     m_margin, 0);
+    }
+
+    return rectf;
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::scrollTo(const QModelIndex &index, ScrollHint hint/* = EnsureVisible*/)
+{
+    if (!index.isValid())
+    {
+        return;
+    }
+
+    QRect viewRect = viewport()->rect();
+    QRect itemRect = visualRect(index);
+
+    if (itemRect.left() < viewRect.left())
+    {
+        horizontalScrollBar()->setValue(horizontalScrollBar()->value() + itemRect.center().x() - viewport()->width()/2);
+    }
+    else if (itemRect.right() > viewRect.right())
+    {
+        horizontalScrollBar()->setValue(horizontalScrollBar()->value() + itemRect.center().x() - viewport()->width()/2);
+    }
+
+    if (!viewRect.contains(itemRect))
+    {
+        deletePixmap();
+    }
+    viewport()->update();
+}
+
+//-----------------------------------------------------------------------------
+QModelIndex vktraceviewer_QTimelineView::indexAt(const QPoint &point) const
+{
+    if (model() == NULL)
+        return QModelIndex();
+
+    float wy = (float)point.y();
+
+    // Early out if the point is not in the areas covered by timeline items
+    bool inThreadArea = false;
+    for (int i = 0; i < m_threadArea.size(); i++)
+    {
+        if (wy >= m_threadArea[i].top() && wy <= m_threadArea[i].bottom())
+        {
+            inThreadArea = true;
+            break;
+        }
+    }
+
+    if (inThreadArea == false)
+    {
+        // point is outside the areas that timeline items are drawn to.
+        return QModelIndex();
+    }
+
+    // Transform the view coordinates into content widget coordinates.
+    int x = point.x() - m_margin + horizontalScrollBar()->value();
+    float wx = (float)x / m_zoomFactor;
+
+    QHashIterator<int, QRectF> iter(m_rowToRectMap);
+    while (iter.hasNext())
+    {
+        iter.next();
+        QRectF value = iter.value();
+        if (value.contains(wx, wy))
+        {
+            return model()->index(iter.key(), vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        }
+    }
+
+    return QModelIndex();
+}
+
+//-----------------------------------------------------------------------------
+QRegion vktraceviewer_QTimelineView::itemRegion(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return QRegion();
+
+    return QRegion(viewportRect(index).toRect());
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::paintEvent(QPaintEvent *event)
+{
+    QPainter painter(viewport());
+    paint(&painter, event);
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::drawBaseTimelines(QPainter* painter, const QRect& rect, const QList<uint32_t> &threadList)
+{
+    int numThreads = threadList.count();
+
+    painter->save();
+    QFont font = painter->font();
+    int fontHeight = qMin((int)(m_threadHeight * 0.3), font.pointSize());
+    font.setPointSize(fontHeight);
+    painter->setFont(font);
+
+    for (int i = 0; i < numThreads; i++)
+    {
+        int threadTop = (i*m_threadHeight);
+
+        painter->drawText(0, threadTop + fontHeight, QString("Thread %1").arg(threadList[i]));
+
+        // draw the timeline in the middle of this thread's area
+        int lineStart = m_margin - horizontalOffset();
+        int lineEnd = lineStart + scaleDurationHorizontally(m_lineLength);
+        int lineY = threadTop + m_threadHeight/2;
+        painter->drawLine(lineStart, lineY, lineEnd, lineY);
+    }
+    painter->restore();
+}
+
+//-----------------------------------------------------------------------------
+QList<uint32_t> vktraceviewer_QTimelineView::getModelThreadList() const
+{
+    return m_threadIdList;
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::paint(QPainter *painter, QPaintEvent *event)
+{
+    m_threadHeight = event->rect().height();
+    if (m_threadIdList.count() > 0)
+    {
+        m_threadHeight /= m_threadIdList.count();
+    }
+
+    int arrowHeight = 12;
+    int arrowTop = 2;
+    int arrowHalfWidth = 4;
+
+    QPolygon triangle(3);
+    triangle.setPoint(0, 0, arrowTop);
+    triangle.setPoint(1, -arrowHalfWidth, arrowTop+arrowHeight);
+    triangle.setPoint(2, arrowHalfWidth, arrowTop+arrowHeight);
+
+    QList<uint32_t> threadList = getModelThreadList();
+
+    calculateRectsIfNecessary();
+
+    if (m_pPixmap == NULL)
+    {
+        int pixmapHeight = event->rect().height();
+        int pixmapWidth = event->rect().width();
+
+        m_pPixmap = new QPixmap(pixmapWidth, pixmapHeight);
+
+        for (int t = 0; t < m_threadIdList.size(); t++)
+        {
+            m_threadMask[m_threadIdList[t]] = QVector<int>(pixmapWidth, 0);
+        }
+
+        QPainter pixmapPainter(m_pPixmap);
+
+        // fill entire background with background color
+        pixmapPainter.fillRect(event->rect(), m_background);
+        drawBaseTimelines(&pixmapPainter, event->rect(), threadList);
+
+        if (model() != NULL)
+        {
+            int numRows = model()->rowCount();
+
+            for (int r = 0; r < numRows; r++)
+            {
+                QModelIndex index = model()->index(r, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+
+                drawTimelineItem(&pixmapPainter, index);
+            }
+        }
+    }
+    painter->drawPixmap(event->rect(), *m_pPixmap, m_pPixmap->rect());
+
+    if (model() == NULL)
+    {
+        return;
+    }
+
+    // draw current api call marker
+    int currentIndexRow = currentIndex().row();
+    if (currentIndexRow >= 0)
+    {
+        // Overlay a black rectangle around the current item.
+        // For more information on how rects are drawn as outlines,
+        // see here: http://qt-project.org/doc/qt-4.8/qrectf.html#rendering
+        int penWidth = 2;
+        int penWidthHalf = 1;
+        QPen blackPen(Qt::black);
+        blackPen.setWidth(penWidth);
+        blackPen.setJoinStyle(Qt::MiterJoin);
+        painter->setPen(blackPen);
+
+        // Don't fill the rectangle
+        painter->setBrush(Qt::NoBrush);
+
+        QModelIndex index = model()->index(currentIndexRow, vktraceviewer_QTraceFileModel::Column_EntrypointName);
+        QRectF rect = visualRect(index);
+        rect.adjust(-penWidthHalf, -penWidthHalf, penWidthHalf, penWidthHalf);
+        painter->drawRect(rect);
+
+        // Draw marker underneath the current rect
+        painter->save();
+        QPainter::RenderHints hints = painter->renderHints();
+        painter->setRenderHints(QPainter::Antialiasing);
+        painter->setPen(m_trianglePen);
+        painter->setBrush(QColor(Qt::yellow));
+        painter->translate(rect.center().x(), rect.bottom());
+        painter->drawPolygon(triangle);
+        painter->setRenderHints(hints, false);
+        painter->restore();
+    }
+}
+
+//-----------------------------------------------------------------------------
+float vktraceviewer_QTimelineView::scaleDurationHorizontally(uint64_t value) const
+{
+    float scaled = u64ToFloat(value) * m_zoomFactor;
+
+    return scaled;
+}
+
+//-----------------------------------------------------------------------------
+float vktraceviewer_QTimelineView::scalePositionHorizontally(uint64_t value) const
+{
+    uint64_t shiftedValue = value - m_rawStartTime;
+    float offset = scaleDurationHorizontally(shiftedValue);
+
+    return offset;
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTimelineView::drawTimelineItem(QPainter* painter, const QModelIndex &index)
+{
+    QRectF rect = viewportRect(index);
+
+    // don't draw if the rect is outside the viewport
+    if (!rect.isValid() ||
+        rect.bottom() < 0 ||
+        rect.y() > viewport()->height() ||
+        rect.x() > viewport()->width() ||
+        rect.right() < 0)
+    {
+        return;
+    }
+
+    QStyleOptionViewItem option = viewOptions();
+    option.rect = rect.toRect();
+    if (selectionModel()->isSelected(index))
+        option.state |= QStyle::State_Selected;
+    if (currentIndex() == index)
+        option.state |= QStyle::State_HasFocus;
+
+    // check mask to determine if this item should be drawn, or if something has already covered it's pixels
+    vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer();
+    QVector<int>& mask = m_threadMask[pHeader->thread_id];
+    bool drawItem = false;
+    int x = option.rect.x();
+    int right = qMin( qMax(x, option.rect.right()), viewport()->width()-1);
+    for (int pixel = qMax(0, x); pixel <= right; pixel++)
+    {
+        if (mask[pixel] == 0)
+        {
+            drawItem = true;
+            mask[pixel] = 1;
+        }
+    }
+
+    // draw item if it should be visible
+    if (drawItem)
+    {
+        itemDelegate()->paint(painter, option, index);
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qtimelineview.h b/vktrace/src/vktrace_viewer/vktraceviewer_qtimelineview.h
new file mode 100644
index 0000000..ec1f176
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qtimelineview.h
@@ -0,0 +1,171 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#ifndef VKTRACEVIEWER_QTIMELINEVIEW_H
+#define VKTRACEVIEWER_QTIMELINEVIEW_H
+
+#include <stdint.h>
+#include "vktrace_trace_packet_identifiers.h"
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QPainter;
+class QPaintEvent;
+QT_END_NAMESPACE
+
+#include <QAbstractItemView>
+#include <QBrush>
+#include <QFont>
+#include <QPen>
+#include <QScrollBar>
+
+class vktraceviewer_QTimelineItemDelegate : public QAbstractItemDelegate
+{
+    Q_OBJECT
+public:
+    vktraceviewer_QTimelineItemDelegate(QObject *parent = 0);
+    virtual ~vktraceviewer_QTimelineItemDelegate();
+
+    virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+    virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+};
+
+// Implementation of the QTimelineView has benefited greatly from the following site:
+// http://www.informit.com/articles/article.aspx?p=1613548
+class vktraceviewer_QTimelineView : public QAbstractItemView
+{
+    Q_OBJECT
+public:
+    explicit vktraceviewer_QTimelineView(QWidget *parent = 0);
+    virtual ~vktraceviewer_QTimelineView();
+
+    virtual void setModel(QAbstractItemModel* pModel);
+
+    // Begin public virtual functions of QAbstractItemView
+    virtual QRect visualRect(const QModelIndex &index) const;
+    virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
+    virtual QModelIndex indexAt(const QPoint &point) const;
+    // End public virtual functions of QAbstractItemView
+
+    QList<uint32_t> getModelThreadList() const;
+    QRectF itemRect(const QModelIndex &item) const;
+    float getMaxItemDuration() const
+    {
+        return m_maxItemDuration;
+    }
+
+    void deletePixmap()
+    {
+        if (m_pPixmap != NULL)
+        {
+            delete m_pPixmap;
+            m_pPixmap = NULL;
+        }
+    }
+
+private:
+    QBrush m_background;
+    QPen m_trianglePen;
+    QPen m_textPen;
+    QFont m_textFont;
+
+    // new members
+    QList<uint32_t> m_threadIdList;
+    QHash< uint32_t, QVector<int> > m_threadMask;
+    QList<QRect> m_threadArea;
+    float m_maxItemDuration;
+    uint64_t m_rawStartTime;
+    uint64_t m_rawEndTime;
+    uint64_t m_lineLength;
+    float m_durationToViewportScale;
+    float m_zoomFactor;
+    float m_maxZoom;
+    int m_threadHeight;
+    QHash<int, QRectF> m_rowToRectMap;
+    bool m_hashIsDirty;
+    int m_margin;
+    int m_scrollBarWidth;
+    QPoint m_mousePosition;
+
+    QPixmap *m_pPixmap;
+    vktraceviewer_QTimelineItemDelegate m_itemDelegate;
+
+    void calculateRectsIfNecessary();
+    void drawBaseTimelines(QPainter *painter, const QRect &rect, const QList<uint32_t> &threadList);
+    void drawTimelineItem(QPainter* painter, const QModelIndex &index);
+
+    QRectF viewportRect(const QModelIndex &index) const;
+    float scaleDurationHorizontally(uint64_t value) const;
+    float scalePositionHorizontally(uint64_t value) const;
+
+    // Begin Private...
+    virtual QRegion itemRegion(const QModelIndex &index) const;
+    // End private...
+
+protected:
+    void paintEvent(QPaintEvent *event);
+    void paint(QPainter *painter, QPaintEvent *event);
+
+    virtual bool event(QEvent * e);
+    virtual void resizeEvent(QResizeEvent *event);
+    virtual void mousePressEvent(QMouseEvent * event);
+    virtual void mouseMoveEvent(QMouseEvent * event);
+    virtual void scrollContentsBy(int dx, int dy);
+
+    // Begin protected virtual functions of QAbstractItemView
+    virtual QModelIndex moveCursor(CursorAction cursorAction,
+                                   Qt::KeyboardModifiers modifiers)
+    {
+        return QModelIndex();
+    }
+
+    virtual int horizontalOffset() const
+    {
+        return horizontalScrollBar()->value();
+    }
+    virtual int verticalOffset() const
+    {
+        return verticalScrollBar()->value();
+    }
+
+    virtual bool isIndexHidden(const QModelIndex &index) const
+    {
+        return false;
+    }
+
+    virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) {}
+    virtual QRegion visualRegionForSelection(const QItemSelection &selection) const
+    {
+        return QRegion();
+    }
+    // End protected virtual functions of QAbstractItemView
+
+protected slots:
+    virtual void updateGeometries();
+
+signals:
+
+public
+slots:
+};
+
+#endif // VKTRACEVIEWER_QTIMELINEVIEW_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qtracefileloader.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_qtracefileloader.cpp
new file mode 100644
index 0000000..b380d19
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qtracefileloader.cpp
@@ -0,0 +1,284 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+
+#include "vktraceviewer_qtracefileloader.h"
+#include "vktraceviewer_controller_factory.h"
+
+extern "C" {
+#include "vktrace_trace_packet_utils.h"
+}
+
+vktraceviewer_QTraceFileLoader::vktraceviewer_QTraceFileLoader()
+    : QObject(NULL)
+{
+    qRegisterMetaType<vktraceviewer_trace_file_info>("vktraceviewer_trace_file_info");
+}
+
+vktraceviewer_QTraceFileLoader::~vktraceviewer_QTraceFileLoader()
+{
+}
+
+//-----------------------------------------------------------------------------
+void vktraceviewer_QTraceFileLoader::loadTraceFile(const QString& filename)
+{
+    // open trace file and read in header
+    memset(&m_traceFileInfo, 0, sizeof(vktraceviewer_trace_file_info));
+    m_traceFileInfo.pFile = fopen(filename.toStdString().c_str(), "rb");
+
+    bool bOpened = (m_traceFileInfo.pFile != NULL);
+    if (!bOpened)
+    {
+        emit OutputMessage(VKTRACE_LOG_ERROR, "Unable to open file.");
+    }
+    else
+    {
+        m_traceFileInfo.filename = vktrace_allocate_and_copy(filename.toStdString().c_str());
+        if (populate_trace_file_info(&m_traceFileInfo) == FALSE)
+        {
+            emit OutputMessage(VKTRACE_LOG_ERROR, "Unable to populate trace file info from file.");
+            bOpened = false;
+        }
+        else
+        {
+            // Make sure trace file version is supported
+            if (m_traceFileInfo.header.trace_file_version < VKTRACE_TRACE_FILE_VERSION_MINIMUM_COMPATIBLE)
+            {
+                emit OutputMessage(VKTRACE_LOG_ERROR, QString("Trace file version %1 is older than minimum compatible version (%2).\nYou'll need to make a new trace file, or use an older replayer.").arg(m_traceFileInfo.header.trace_file_version).arg(VKTRACE_TRACE_FILE_VERSION_MINIMUM_COMPATIBLE));
+                bOpened = false;
+            }
+
+#ifdef USE_STATIC_CONTROLLER_LIBRARY
+            m_pController = vtvCreateQController();
+            if (bOpened)
+#else
+            if (!load_controllers(&m_traceFileInfo))
+            {
+                emit OutputMessage(VKTRACE_LOG_ERROR, "Failed to load necessary debug controllers.");
+                bOpened = false;
+            }
+            else if (bOpened)
+#endif
+            {
+                connect(m_pController, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)), this, SIGNAL(OutputMessage(VktraceLogLevel, const QString&)));
+                connect(m_pController, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)), this, SIGNAL(OutputMessage(VktraceLogLevel, uint64_t, const QString&)));
+
+                // interpret the trace file packets
+                for (uint64_t i = 0; i < m_traceFileInfo.packetCount; i++)
+                {
+                    vktraceviewer_trace_file_packet_offsets* pOffsets = &m_traceFileInfo.pPacketOffsets[i];
+                    switch (pOffsets->pHeader->packet_id) {
+                        case VKTRACE_TPI_MESSAGE:
+                            m_traceFileInfo.pPacketOffsets[i].pHeader = vktrace_interpret_body_as_trace_packet_message(pOffsets->pHeader)->pHeader;
+                            break;
+                        case VKTRACE_TPI_MARKER_CHECKPOINT:
+                            break;
+                        case VKTRACE_TPI_MARKER_API_BOUNDARY:
+                            break;
+                        case VKTRACE_TPI_MARKER_API_GROUP_BEGIN:
+                            break;
+                        case VKTRACE_TPI_MARKER_API_GROUP_END:
+                            break;
+                        case VKTRACE_TPI_MARKER_TERMINATE_PROCESS:
+                            break;
+                        //TODO processing code for all the above cases
+                        default:
+                        {
+                            vktrace_trace_packet_header* pHeader = m_pController->InterpretTracePacket(pOffsets->pHeader);
+                            if (pHeader == NULL)
+                            {
+                                bOpened = false;
+                                emit OutputMessage(VKTRACE_LOG_ERROR, QString("Unrecognized packet type: %1").arg(pOffsets->pHeader->packet_id));
+                                m_traceFileInfo.pPacketOffsets[i].pHeader = NULL;
+                                break;
+                            }
+                            m_traceFileInfo.pPacketOffsets[i].pHeader = pHeader;
+                        }
+                    }
+
+                    // break from loop if there is an error
+                    if (bOpened == false)
+                    {
+                        break;
+                    }
+                }
+
+#ifdef USE_STATIC_CONTROLLER_LIBRARY
+            vtvDeleteQController(&m_pController);
+#else
+            m_controllerFactory.Unload(&m_pController);
+#endif
+            }
+        }
+
+        // TODO: We don't really want to close the trace file yet.
+        // I think we want to keep it open so that we can dynamically read from it.
+        // BUT we definitely don't want it to get locked open, so we need a smart
+        // way to open / close from it when reading.
+        fclose(m_traceFileInfo.pFile);
+        m_traceFileInfo.pFile = NULL;
+    }
+
+    // populate the UI based on trace file info
+    emit TraceFileLoaded(bOpened, m_traceFileInfo, m_controllerFilename);
+
+    emit Finished();
+}
+
+//-----------------------------------------------------------------------------
+bool vktraceviewer_QTraceFileLoader::load_controllers(vktraceviewer_trace_file_info* pTraceFileInfo)
+{
+    if (pTraceFileInfo->header.tracer_count == 0)
+    {
+        emit OutputMessage(VKTRACE_LOG_ERROR, "No API specified in tracefile for replaying.");
+        return false;
+    }
+
+    for (int i = 0; i < pTraceFileInfo->header.tracer_count; i++)
+    {
+        uint8_t tracerId = pTraceFileInfo->header.tracer_id_array[i].id;
+
+        const VKTRACE_TRACER_REPLAYER_INFO* pReplayerInfo = &(gs_tracerReplayerInfo[tracerId]);
+
+        if (pReplayerInfo->tracerId != tracerId)
+        {
+            emit OutputMessage(VKTRACE_LOG_ERROR, QString("Replayer info for TracerId (%1) failed consistency check.").arg(tracerId));
+            assert(!"TracerId in VKTRACE_TRACER_REPLAYER_INFO does not match the requested tracerId. The array needs to be corrected.");
+        }
+        else if (strlen(pReplayerInfo->debuggerLibraryname) != 0)
+        {
+            // Have our factory create the necessary controller
+            emit OutputMessage(VKTRACE_LOG_VERBOSE, QString("Loading controller: %1").arg(pReplayerInfo->debuggerLibraryname));
+
+            m_pController = m_controllerFactory.Load(pReplayerInfo->debuggerLibraryname);
+
+            if (m_pController != NULL)
+            {
+                m_controllerFilename = QString(pReplayerInfo->debuggerLibraryname);
+                // Only one controller needs to be loaded, so break from loop
+                break;
+            }
+            else
+            {
+                // controller failed to be created
+                emit OutputMessage(VKTRACE_LOG_ERROR, QString("Unable to load controller for TracerId %1.").arg(tracerId));
+            }
+        }
+    }
+
+    return m_pController != NULL;
+}
+
+//-----------------------------------------------------------------------------
+bool vktraceviewer_QTraceFileLoader::populate_trace_file_info(vktraceviewer_trace_file_info* pTraceFileInfo)
+{
+    assert(pTraceFileInfo != NULL);
+    assert(pTraceFileInfo->pFile != NULL);
+
+    // read trace file header
+    if (1 != fread(&(pTraceFileInfo->header), sizeof(vktrace_trace_file_header), 1, pTraceFileInfo->pFile))
+    {
+        emit OutputMessage(VKTRACE_LOG_ERROR, "Unable to read header from file.");
+        return false;
+    }
+
+    // Find out how many trace packets there are.
+
+    // Seek to first packet
+    long first_offset = pTraceFileInfo->header.first_packet_offset;
+    int seekResult = fseek(pTraceFileInfo->pFile, first_offset, SEEK_SET);
+    if (seekResult != 0)
+    {
+        emit OutputMessage(VKTRACE_LOG_WARNING, "Failed to seek to the first packet offset in the trace file.");
+    }
+
+    uint64_t fileOffset = pTraceFileInfo->header.first_packet_offset;
+    uint64_t packetSize = 0;
+    while(1 == fread(&packetSize, sizeof(uint64_t), 1, pTraceFileInfo->pFile))
+    {
+        // success!
+        pTraceFileInfo->packetCount++;
+        fileOffset += packetSize;
+
+        fseek(pTraceFileInfo->pFile, fileOffset, SEEK_SET);
+    }
+
+    if (pTraceFileInfo->packetCount == 0)
+    {
+        if (ferror(pTraceFileInfo->pFile) != 0)
+        {
+            perror("File Read error:");
+            emit OutputMessage(VKTRACE_LOG_ERROR, "There was an error reading the trace file.");
+            return false;
+        }
+        else if (feof(pTraceFileInfo->pFile) != 0)
+        {
+            emit OutputMessage(VKTRACE_LOG_WARNING, "Reached the end of the file.");
+        }
+        emit OutputMessage(VKTRACE_LOG_WARNING, "There are no trace packets in this trace file.");
+        pTraceFileInfo->pPacketOffsets = NULL;
+    }
+    else
+    {
+        pTraceFileInfo->pPacketOffsets = VKTRACE_NEW_ARRAY(vktraceviewer_trace_file_packet_offsets, pTraceFileInfo->packetCount);
+
+        // rewind to first packet and this time, populate the packet offsets
+        if (fseek(pTraceFileInfo->pFile, first_offset, SEEK_SET) != 0)
+        {
+            emit OutputMessage(VKTRACE_LOG_ERROR, "Unable to rewind trace file to gather packet offsets.");
+            return false;
+        }
+
+        unsigned int packetIndex = 0;
+        fileOffset = first_offset;
+        while(1 == fread(&packetSize, sizeof(uint64_t), 1, pTraceFileInfo->pFile))
+        {
+            // the fread confirms that this packet exists
+            // NOTE: We do not actually read the entire packet into memory right now.
+            pTraceFileInfo->pPacketOffsets[packetIndex].fileOffset = fileOffset;
+
+            // rewind slightly
+            fseek(pTraceFileInfo->pFile, -1*(long)sizeof(uint64_t), SEEK_CUR);
+
+            // allocate space for the packet and read it in
+            pTraceFileInfo->pPacketOffsets[packetIndex].pHeader = (vktrace_trace_packet_header*)vktrace_malloc(packetSize);
+            if (1 != fread(pTraceFileInfo->pPacketOffsets[packetIndex].pHeader, packetSize, 1, pTraceFileInfo->pFile))
+            {
+                emit OutputMessage(VKTRACE_LOG_ERROR, "Unable to read in a trace packet.");
+                return false;
+            }
+
+            // adjust pointer to body of the packet
+            pTraceFileInfo->pPacketOffsets[packetIndex].pHeader->pBody = (uintptr_t)pTraceFileInfo->pPacketOffsets[packetIndex].pHeader + sizeof(vktrace_trace_packet_header);
+
+            // now seek to what should be the next packet
+            fileOffset += packetSize;
+            packetIndex++;
+        }
+
+        if (fseek(pTraceFileInfo->pFile, first_offset, SEEK_SET) != 0)
+        {
+            emit OutputMessage(VKTRACE_LOG_ERROR, "Unable to rewind trace file to restore position.");
+            return false;
+        }
+    }
+
+    return true;
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_qtracefileloader.h b/vktrace/src/vktrace_viewer/vktraceviewer_qtracefileloader.h
new file mode 100644
index 0000000..809eb46
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_qtracefileloader.h
@@ -0,0 +1,59 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_QTRACEFILELOADER_H
+#define VKTRACEVIEWER_QTRACEFILELOADER_H
+
+#include <QObject>
+#include "vktraceviewer_controller_factory.h"
+#include "vktraceviewer_controller.h"
+
+#define USE_STATIC_CONTROLLER_LIBRARY 1
+class vktraceviewer_QTraceFileLoader : public QObject
+{
+    Q_OBJECT
+public:
+    vktraceviewer_QTraceFileLoader();
+    virtual ~vktraceviewer_QTraceFileLoader();
+
+public slots:
+    void loadTraceFile(const QString& filename);
+
+signals:
+    void OutputMessage(VktraceLogLevel level, uint64_t packetIndex, const QString& message);
+    void OutputMessage(VktraceLogLevel level, const QString& message);
+
+    void TraceFileLoaded(bool bSuccess, const vktraceviewer_trace_file_info& fileInfo, const QString& controllerFilename);
+
+    void Finished();
+
+private:
+    vktraceviewer_trace_file_info m_traceFileInfo;
+    vktraceviewer_controller_factory m_controllerFactory;
+    vktraceviewer_QController* m_pController;
+    QString m_controllerFilename;
+
+    bool load_controllers(vktraceviewer_trace_file_info* pTraceFileInfo);
+
+    bool populate_trace_file_info(vktraceviewer_trace_file_info* pTraceFileInfo);
+
+};
+
+#endif // VKTRACEVIEWER_QTRACEFILELOADER_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_settings.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_settings.cpp
new file mode 100644
index 0000000..1b03567
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_settings.cpp
@@ -0,0 +1,202 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_settings.h"
+#include "vktraceviewer_output.h"
+#include <assert.h>
+
+#include <QCoreApplication>
+#include <QDir>
+
+extern "C" {
+#include "vktrace_settings.h"
+}
+
+static const unsigned int VKTRACEVIEWER_SETTINGS_FILE_FORMAT_VERSION_1 = 1;
+static const unsigned int VKTRACEVIEWER_SETTINGS_FILE_FORMAT_VERSION = VKTRACEVIEWER_SETTINGS_FILE_FORMAT_VERSION_1;
+
+static const char *s_SETTINGS_FILE = "vktraceviewer_settings.txt";
+
+// declared as extern in header
+vktraceviewer_settings g_settings;
+static vktraceviewer_settings s_default_settings;
+vktrace_SettingGroup* g_pAllSettings = NULL;
+unsigned int g_numAllSettings = 0;
+
+vktrace_SettingInfo g_settings_info[] =
+{
+    { "o", "OpenTraceFile", VKTRACE_SETTING_STRING, &g_settings.trace_file_to_open, &s_default_settings.trace_file_to_open, TRUE, "Load the specified trace file when vktraceviewer is opened."},
+    { "wl", "WindowLeft", VKTRACE_SETTING_INT, &g_settings.window_position_left, &s_default_settings.window_position_left, TRUE, "Left coordinate of VkTraceViewer window on startup."},
+    { "wt", "WindowTop", VKTRACE_SETTING_INT, &g_settings.window_position_top, &s_default_settings.window_position_top, TRUE, "Top coordinate of VkTraceViewer window on startup."},
+    { "ww", "WindowWidth", VKTRACE_SETTING_INT, &g_settings.window_size_width, &s_default_settings.window_size_width, TRUE, "Width of VkTraceViewer window on startup."},
+    { "wh", "WindowHeight", VKTRACE_SETTING_INT, &g_settings.window_size_height, &s_default_settings.window_size_height, TRUE, "Height of VkTraceViewer window on startup."},
+
+    { "", "GenTraceApplication", VKTRACE_SETTING_STRING, &g_settings.gentrace_application, &s_default_settings.gentrace_application, FALSE, "The most recent application path in the 'Generate Trace' dialog."},
+    { "", "GenTraceArguments", VKTRACE_SETTING_STRING, &g_settings.gentrace_arguments, &s_default_settings.gentrace_arguments, FALSE, "The most recent application arguments in the 'Generate Trace' dialog."},
+    { "", "GenTraceWorkingDir", VKTRACE_SETTING_STRING, &g_settings.gentrace_working_dir, &s_default_settings.gentrace_working_dir, FALSE, "The most recent working directory in the 'Generate Trace' dialog."},
+    { "", "GenTraceVkLayerPath", VKTRACE_SETTING_STRING, &g_settings.gentrace_vk_layer_path, &s_default_settings.gentrace_vk_layer_path, FALSE, "The most recent VK_LAYER_PATH used in the 'Generate Trace' dialog."},
+    { "", "GenTraceOutputFile", VKTRACE_SETTING_STRING, &g_settings.gentrace_output_file, &s_default_settings.gentrace_output_file, FALSE, "The most recent output trace file in the 'Generate Trace' dialog."},
+
+    { "", "SettingsDialogWidth", VKTRACE_SETTING_INT, &g_settings.settings_dialog_width, &s_default_settings.settings_dialog_width, TRUE, "Width of VkTraceViewer settings dialog when opened."},
+    { "", "SettingsDialogHeight", VKTRACE_SETTING_INT, &g_settings.settings_dialog_height, &s_default_settings.settings_dialog_height, TRUE, "Height of VkTraceViewer settings dialog when opened."},
+
+    //{ "tltps", "trim_large_trace_prompt_size", VKTRACE_SETTING_UINT, &g_settings.trim_large_trace_prompt_size, &s_default_settings.trim_large_trace_prompt_size, TRUE, "The number of frames in a trace file at which the user should be prompted to trim the trace before loading."},
+    //{ "gsr", "group_state_render", VKTRACE_SETTING_BOOL, &g_settings.groups_state_render, &s_default_settings.groups_state_render, TRUE, "Path to the dynamic tracer library to be injected, may use [0-15]."},
+    //{ "gppm", "groups_push_pop_markers", VKTRACE_SETTING_BOOL, &g_settings.groups_push_pop_markers, &s_default_settings.groups_push_pop_markers, TRUE, "Path to the dynamic tracer library to be injected, may use [0-15]."},
+    //{ "gnc", "groups_nested_calls", VKTRACE_SETTING_BOOL, &g_settings.groups_nested_calls, &s_default_settings.groups_nested_calls, TRUE, "Path to the dynamic tracer library to be injected, may use [0-15]."},
+};
+
+vktrace_SettingGroup g_settingGroup =
+{
+    "vktraceviewer",
+    sizeof(g_settings_info) / sizeof(g_settings_info[0]),
+    &g_settings_info[0]
+};
+
+QString vktraceviewer_get_settings_file_path()
+{
+    QString result = vktraceviewer_get_settings_directory() + "/" + QString(s_SETTINGS_FILE);
+    return result;
+}
+
+QString vktraceviewer_get_settings_directory()
+{
+    char * pSettingsPath = vktrace_platform_get_settings_path();
+    QString result = QString(pSettingsPath);
+    vktrace_free(pSettingsPath);
+    return result;
+}
+
+QString vktraceviewer_get_sessions_directory()
+{
+    char * pDataPath = vktrace_platform_get_data_path();
+    QString result = QString(pDataPath) + "/sessions/";
+    vktrace_free(pDataPath);
+    return result;
+}
+
+bool vktraceviewer_initialize_settings(int argc, char* argv[])
+{
+    bool bSuccess = true;
+
+    // setup default values
+    memset(&s_default_settings, 0, sizeof(vktraceviewer_settings));
+
+    s_default_settings.trace_file_to_open = NULL;
+    s_default_settings.window_position_left = 0;
+    s_default_settings.window_position_top = 0;
+    s_default_settings.window_size_width = 1024;
+    s_default_settings.window_size_height = 768;
+
+    s_default_settings.gentrace_application = NULL;
+    s_default_settings.gentrace_arguments = NULL;
+    s_default_settings.gentrace_working_dir = NULL;
+    s_default_settings.gentrace_vk_layer_path = NULL;
+    s_default_settings.gentrace_output_file = NULL;
+
+    // This seems to be a reasonable default size for the dialog.
+    s_default_settings.settings_dialog_width = 600;
+    s_default_settings.settings_dialog_height = 600;
+
+    // initialize settings as defaults
+    g_settings = s_default_settings;
+
+    QString settingsFilePath = vktraceviewer_get_settings_file_path();
+    FILE* pFile = fopen(settingsFilePath.toStdString().c_str(), "r");
+    if (pFile == NULL)
+    {
+        vktrace_LogWarning("Unable to open settings file: '%s'.", settingsFilePath.toStdString().c_str());
+    }
+
+    // Secondly set options based on settings file
+    if (pFile != NULL)
+    {
+        g_pAllSettings = NULL;
+        g_numAllSettings = 0;
+        if (vktrace_SettingGroup_Load_from_file(pFile, &g_pAllSettings, &g_numAllSettings) == -1)
+        {
+            vktrace_SettingGroup_print(&g_settingGroup);
+            return false;
+        }
+
+        if (g_pAllSettings != NULL && g_numAllSettings > 0)
+        {
+            vktrace_SettingGroup_Apply_Overrides(&g_settingGroup, g_pAllSettings, g_numAllSettings);
+        }
+
+        fclose(pFile);
+        pFile = NULL;
+    }
+
+    // apply settings from settings file and from cmd-line args
+    if (vktrace_SettingGroup_init_from_cmdline(&g_settingGroup, argc, argv, &g_settings.trace_file_to_open) != 0)
+    {
+        // invalid options specified
+        bSuccess = false;
+    }
+
+    // Merge known vktraceviewer settings into the loaded settings.
+    // This ensures that new known settings are added to the settings dialog
+    // and will be re-written to the settings file upon saving.
+    vktrace_SettingGroup_merge(&g_settingGroup, &g_pAllSettings, &g_numAllSettings);
+
+    // This would be a good place to validate any "required" settings, but right now there aren't any!
+
+    if (bSuccess == false)
+    {
+        vktrace_SettingGroup_print(&g_settingGroup);
+        vktrace_SettingGroup_delete(&g_settingGroup);
+    }
+
+    return bSuccess;
+}
+
+void vktraceviewer_settings_updated()
+{
+    vktrace_SettingGroup_update(&g_settingGroup, g_pAllSettings, g_numAllSettings);
+}
+
+void vktraceviewer_save_settings()
+{
+    QDir settingsDir(vktraceviewer_get_settings_directory());
+    if (settingsDir.mkpath(".") == false)
+    {
+        QString error = "Failed to create " + settingsDir.path();
+        vktraceviewer_output_error(error);
+    }
+
+    QString filepath = vktraceviewer_get_settings_file_path();
+    FILE* pSettingsFile = fopen(filepath.toStdString().c_str(), "w");
+    if (pSettingsFile == NULL)
+    {
+        QString error = "Failed to open settings file for writing: " + filepath;
+        vktraceviewer_output_error(error);
+    }
+    else
+    {
+        if (vktrace_SettingGroup_save(g_pAllSettings, g_numAllSettings, pSettingsFile) == FALSE)
+        {
+            QString error = "Failed to save settings file: " + filepath;
+            vktraceviewer_output_error(error);
+        }
+
+        fclose(pSettingsFile);
+    }
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_settings.h b/vktrace/src/vktrace_viewer/vktraceviewer_settings.h
new file mode 100644
index 0000000..7bf9fec
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_settings.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_SETTINGS_H
+#define VKTRACEVIEWER_SETTINGS_H
+
+extern "C" {
+#include "vktrace_settings.h"
+}
+
+#include <QString>
+
+extern vktrace_SettingGroup* g_pAllSettings;
+extern unsigned int g_numAllSettings;
+
+typedef struct vktraceviewer_settings
+{
+    char * trace_file_to_open;
+    int window_position_left;
+    int window_position_top;
+    int window_size_width;
+    int window_size_height;
+    char * gentrace_application;
+    char * gentrace_arguments;
+    char * gentrace_working_dir;
+    char * gentrace_vk_layer_path;
+    char * gentrace_output_file;
+    int settings_dialog_width;
+    int settings_dialog_height;
+    //unsigned int trim_large_trace_prompt_size;
+
+    //bool groups_state_render;
+    //bool groups_push_pop_markers;
+    //bool groups_nested_calls;
+} vktraceviewer_settings;
+
+extern vktraceviewer_settings g_settings;
+extern vktrace_SettingGroup g_settingGroup;
+
+bool vktraceviewer_initialize_settings(int argc, char* argv[]);
+
+void vktraceviewer_settings_updated();
+
+void vktraceviewer_save_settings();
+
+QString vktraceviewer_get_settings_file_path();
+QString vktraceviewer_get_settings_directory();
+QString vktraceviewer_get_sessions_directory();
+
+#endif // VKTRACEVIEWER_SETTINGS_H
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_trace_file_utils.cpp b/vktrace/src/vktrace_viewer/vktraceviewer_trace_file_utils.cpp
new file mode 100644
index 0000000..ac987a3
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_trace_file_utils.cpp
@@ -0,0 +1,118 @@
+/**************************************************************************
+ *
+ * Copyright 2014-2016 Valve Corporation
+ * Copyright (C) 2014-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#include "vktraceviewer_trace_file_utils.h"
+#include "vktrace_memory.h"
+
+BOOL vktraceviewer_populate_trace_file_info(vktraceviewer_trace_file_info* pTraceFileInfo)
+{
+    assert(pTraceFileInfo != NULL);
+    assert(pTraceFileInfo->pFile != NULL);
+
+    // read trace file header
+    if (1 != fread(&(pTraceFileInfo->header), sizeof(vktrace_trace_file_header), 1, pTraceFileInfo->pFile))
+    {
+        vktraceviewer_output_error("Unable to read header from file.");
+        return FALSE;
+    }
+
+    // Find out how many trace packets there are.
+
+    // Seek to first packet
+    long first_offset = pTraceFileInfo->header.first_packet_offset;
+    int seekResult = fseek(pTraceFileInfo->pFile, first_offset, SEEK_SET);
+    if (seekResult != 0)
+    {
+        vktraceviewer_output_warning("Failed to seek to the first packet offset in the trace file.");
+    }
+
+    uint64_t fileOffset = pTraceFileInfo->header.first_packet_offset;
+    uint64_t packetSize = 0;
+    while(1 == fread(&packetSize, sizeof(uint64_t), 1, pTraceFileInfo->pFile))
+    {
+        // success!
+        pTraceFileInfo->packetCount++;
+        fileOffset += packetSize;
+
+        fseek(pTraceFileInfo->pFile, fileOffset, SEEK_SET);
+    }
+
+    if (pTraceFileInfo->packetCount == 0)
+    {
+        if (ferror(pTraceFileInfo->pFile) != 0)
+        {
+            perror("File Read error:");
+            vktraceviewer_output_warning("There was an error reading the trace file.");
+            return FALSE;
+        }
+        else if (feof(pTraceFileInfo->pFile) != 0)
+        {
+            vktraceviewer_output_warning("Reached the end of the file.");
+        }
+        vktraceviewer_output_warning("There are no trace packets in this trace file.");
+        pTraceFileInfo->pPacketOffsets = NULL;
+    }
+    else
+    {
+        pTraceFileInfo->pPacketOffsets = VKTRACE_NEW_ARRAY(vktraceviewer_trace_file_packet_offsets, pTraceFileInfo->packetCount);
+
+        // rewind to first packet and this time, populate the packet offsets
+        if (fseek(pTraceFileInfo->pFile, first_offset, SEEK_SET) != 0)
+        {
+            vktraceviewer_output_error("Unable to rewind trace file to gather packet offsets.");
+            return FALSE;
+        }
+
+        unsigned int packetIndex = 0;
+        fileOffset = first_offset;
+        while(1 == fread(&packetSize, sizeof(uint64_t), 1, pTraceFileInfo->pFile))
+        {
+            // the fread confirms that this packet exists
+            // NOTE: We do not actually read the entire packet into memory right now.
+            pTraceFileInfo->pPacketOffsets[packetIndex].fileOffset = fileOffset;
+
+            // rewind slightly
+            fseek(pTraceFileInfo->pFile, -1*(long)sizeof(uint64_t), SEEK_CUR);
+
+            // allocate space for the packet and read it in
+            pTraceFileInfo->pPacketOffsets[packetIndex].pHeader = (vktrace_trace_packet_header*)vktrace_malloc(packetSize);
+            if (1 != fread(pTraceFileInfo->pPacketOffsets[packetIndex].pHeader, packetSize, 1, pTraceFileInfo->pFile))
+            {
+                vktraceviewer_output_error("Unable to read in a trace packet.");
+                return FALSE;
+            }
+
+            // adjust pointer to body of the packet
+            pTraceFileInfo->pPacketOffsets[packetIndex].pHeader->pBody = (uintptr_t)pTraceFileInfo->pPacketOffsets[packetIndex].pHeader + sizeof(vktrace_trace_packet_header);
+
+            // now seek to what should be the next packet
+            fileOffset += packetSize;
+            packetIndex++;
+        }
+
+        if (fseek(pTraceFileInfo->pFile, first_offset, SEEK_SET) != 0)
+        {
+            vktraceviewer_output_error("Unable to rewind trace file to restore position.");
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_trace_file_utils.h b/vktrace/src/vktrace_viewer/vktraceviewer_trace_file_utils.h
new file mode 100644
index 0000000..85f48c2
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_trace_file_utils.h
@@ -0,0 +1,61 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#ifndef VKTRACEVIEWER_TRACE_FILE_UTILS_H_
+#define VKTRACEVIEWER_TRACE_FILE_UTILS_H_
+
+//#include <string>
+#include <QString>
+
+extern "C" {
+#include "vktrace_trace_packet_identifiers.h"
+}
+#include "vktraceviewer_output.h"
+
+struct vktraceviewer_trace_file_packet_offsets
+{
+    // the file offset to this particular packet
+    unsigned int fileOffset;
+
+    // Pointer to the packet header if it's been read from disk
+    vktrace_trace_packet_header* pHeader;
+};
+
+struct vktraceviewer_trace_file_info
+{
+    // the trace file name & path
+    char* filename;
+
+    // the trace file
+    FILE* pFile;
+
+    // copy of the trace file header
+    vktrace_trace_file_header header;
+
+    // number of packets in file which should also be number of elements in pPacketOffsets array
+    uint64_t packetCount;
+
+    // array of packet offsets
+    vktraceviewer_trace_file_packet_offsets* pPacketOffsets;
+};
+
+BOOL vktraceviewer_populate_trace_file_info(vktraceviewer_trace_file_info* pTraceFileInfo);
+
+#endif //VKTRACEVIEWER_TRACE_FILE_UTILS_H_
diff --git a/vktrace/src/vktrace_viewer/vktraceviewer_view.h b/vktrace/src/vktrace_viewer/vktraceviewer_view.h
new file mode 100644
index 0000000..da1300d
--- /dev/null
+++ b/vktrace/src/vktrace_viewer/vktraceviewer_view.h
@@ -0,0 +1,59 @@
+/**************************************************************************
+ *
+ * Copyright 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ * All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Peter Lohrmann <peterl@valvesoftware.com> <plohrmann@gmail.com>
+ **************************************************************************/
+#pragma once
+
+#include "vktraceviewer_QTraceFileModel.h"
+
+struct vktraceviewer_trace_file_info;
+struct vktrace_SettingGroup;
+class QWidget;
+class QToolButton;
+class QAction;
+class QAbstractProxyModel;
+
+class vktraceviewer_view
+{
+public:
+    virtual void reset_view() = 0;
+
+//    virtual void output_message(uint64_t packetIndex, QString message) = 0;
+//    virtual void output_warning(uint64_t packetIndex, QString message) = 0;
+//    virtual void output_error(uint64_t packetIndex, QString message) = 0;
+
+    virtual void add_setting_group(vktrace_SettingGroup* pGroup) = 0;
+    virtual unsigned int get_global_settings(vktrace_SettingGroup** ppGroups) = 0;
+
+    virtual void set_calltree_model(vktraceviewer_QTraceFileModel* pTraceFileModel, QAbstractProxyModel *pModel) = 0;
+    virtual void add_calltree_contextmenu_item(QAction* pAction) = 0;
+    virtual void select_call_at_packet_index(unsigned long long packetIndex) = 0;
+    virtual void highlight_timeline_item(unsigned long long packetArrayIndex, bool bScrollTo, bool bSelect) = 0;
+
+    // \return tab index of state viewer
+    virtual int add_custom_state_viewer(QWidget* pWidget, const QString& title, bool bBringToFront = false) = 0;
+    virtual void remove_custom_state_viewer(int tabIndex) = 0;
+    virtual void enable_custom_state_viewer(QWidget* pWidget, bool bEnabled) = 0;
+
+    virtual QToolButton* add_toolbar_button(const QString& title, bool bEnabled) = 0;
+
+    virtual void on_replay_state_changed(bool bReplayInProgress) = 0;
+
+    virtual unsigned long long get_current_packet_index() = 0;
+};
diff --git a/vktrace/vktrace_generate.py b/vktrace/vktrace_generate.py
new file mode 100755
index 0000000..4651764
--- /dev/null
+++ b/vktrace/vktrace_generate.py
@@ -0,0 +1,2350 @@
+#!/usr/bin/env python3
+#
+# Vulkan
+#
+# Copyright (C) 2015-2016 Valve Corporation
+# Copyright (C) 2015-2016 LunarG, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Jon Ashburn <jon@lunarg.com>
+# Author: Tobin Ehlis <tobin@lunarg.com>
+# Author: Peter Lohrmann <peterl@valvesoftware.com>
+#
+
+import os, sys
+
+
+# add main repo directory so vulkan.py can be imported. This needs to be a complete path.
+vktrace_scripts_path = os.path.dirname(os.path.abspath(__file__))
+main_path = os.path.abspath(vktrace_scripts_path + "/../scripts/")
+sys.path.append(main_path)
+from source_line_info import sourcelineinfo
+
+import vulkan
+
+# vulkan.py doesn't include all the extensions (debug_report missing)
+headers = []
+objects = []
+protos = []
+proto_exclusions = [ 'CreateWaylandSurfaceKHR', 'CreateMirSurfaceKHR',
+                     'GetPhysicalDeviceWaylandPresentationSupportKHR', 'GetPhysicalDeviceMirPresentationSupportKHR',
+                     'GetPhysicalDeviceDisplayPropertiesKHR', 'GetPhysicalDeviceDisplayPlanePropertiesKHR',
+                     'GetDisplayPlaneSupportedDisplaysKHR', 'GetDisplayModePropertiesKHR',
+                     'CreateDisplayModeKHR', 'GetDisplayPlaneCapabilitiesKHR', 'CreateDisplayPlaneSurfaceKHR']
+
+for ext in vulkan.extensions_all:
+    headers.extend(ext.headers)
+    objects.extend(ext.objects)
+    protos.extend(ext.protos)
+
+# Add parameters we need to remap, along with their type, in pairs
+additional_remap_dict = {}
+additional_remap_dict['pImageIndex'] = "uint32_t"
+
+class Subcommand(object):
+    def __init__(self, argv):
+        self.argv = argv
+        self.extensionName = argv
+        self.headers = headers
+        self.objects = objects
+        self.protos = protos
+        self.lineinfo = sourcelineinfo()
+
+    def run(self):
+        print(self.generate(self.extensionName))
+
+    def generate(self, extensionName):
+        copyright = self.generate_copyright()
+        header = self.generate_header(extensionName)
+        body = self.generate_body()
+        footer = self.generate_footer()
+        contents = []
+        if copyright:
+            contents.append(copyright)
+        if header:
+            contents.append(header)
+        if body:
+            contents.append(body)
+        if footer:
+            contents.append(footer)
+
+        return "\n\n".join(contents)
+
+    def generate_copyright(self):
+        return """/* THIS FILE IS GENERATED.  DO NOT EDIT. */
+
+/*
+ *
+ * Copyright (C) 2015-2016 Valve Corporation
+ * Copyright (C) 2015-2016 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Tobin Ehlis <tobin@lunarg.com>
+ * Author: Peter Lohrmann <peterl@valvesoftware.com>
+ */"""
+
+    def generate_header(self, extensionName):
+        return "\n".join(["#include <" + h + ">" for h in self.headers])
+
+    def generate_body(self):
+        pass
+
+    def generate_footer(self):
+        pass
+
+    def _generate_trace_func_protos(self):
+        func_protos = []
+        func_protos.append('#ifdef __cplusplus')
+        func_protos.append('extern"C" {')
+        func_protos.append('#endif')
+        func_protos.append('// Hooked function prototypes\n')
+        for ext in vulkan.extensions_all:
+            if ext.ifdef:
+                func_protos.append('#ifdef %s' % ext.ifdef)
+            for proto in ext.protos:
+                if proto.name not in proto_exclusions:
+                    func_protos.append('VKTRACER_EXPORT %s;' % proto.c_func(prefix="__HOOKED_vk", attr="VKAPI"))
+
+                # LoaderLayerInterface V0
+                if proto.name in [ 'GetInstanceProcAddr', 'GetDeviceProcAddr']:
+                    func_protos.append('VK_LAYER_EXPORT %s;' % proto.c_func(prefix="VK_LAYER_LUNARG_vktrace", attr="VKAPI"))
+                if proto.name in [ 'EnumerateInstanceLayerProperties', 'EnumerateInstanceExtensionProperties',
+                                   'EnumerateDeviceLayerProperties', 'EnumerateDeviceExtensionProperties' ]:
+                    func_protos.append('VK_LAYER_EXPORT %s;' % proto.c_func(prefix="vk", attr="VKAPI"))
+
+            if ext.ifdef:
+                func_protos.append('#endif /* %s */' % ext.ifdef)
+
+        func_protos.append('#ifdef __cplusplus')
+        func_protos.append('}')
+        func_protos.append('#endif')
+        return "\n".join(func_protos)
+
+    def _generate_trace_func_protos_ext(self, extensionName):
+        func_protos = []
+        func_protos.append('// Hooked function prototypes\n')
+        for ext in vulkan.extensions_all:
+            if (extensionName.lower() == ext.name.lower()):
+                if ext.ifdef:
+                    func_protos.append('#ifdef %s' % ext.ifdef)
+                for proto in ext.protos:
+                    if proto.name not in proto_exclusions:
+                        func_protos.append('VKTRACER_EXPORT %s;' % proto.c_func(prefix="__HOOKED_vk", attr="VKAPI"))
+
+                    # LoaderLayerInterface V0
+                    if proto.name in [ 'GetInstanceProcAddr', 'GetDeviceProcAddr']:
+                        func_protos.append('VK_LAYER_EXPORT %s;' % proto.c_func(prefix="VK_LAYER_LUNARG_vktrace", attr="VKAPI"))
+                    if proto.name in [ 'EnumerateInstanceLayerProperties', 'EnumerateInstanceExtensionProperties',
+                                       'EnumerateDeviceLayerProperties', 'EnumerateDeviceExtensionProperties' ]:
+                        func_protos.append('VK_LAYER_EXPORT %s;' % proto.c_func(prefix="vk", attr="VKAPI"))
+
+                if ext.ifdef:
+                    func_protos.append('#endif /* %s */' % ext.ifdef)
+
+        return "\n".join(func_protos)
+
+    # Return set of printf '%' qualifier, input to that qualifier, and any dereference
+    def _get_printf_params(self, vk_type, name, output_param):
+        deref = ""
+        # TODO : Need ENUM and STRUCT checks here
+        if "VkImageLayout" in vk_type:
+            return ("%s", "string_%s(%s)" % (vk_type.replace('const ', '').strip('*'), name), deref)
+        if "VkClearColor" in vk_type:
+            return ("%p", "(void*)&%s" % name, deref)
+        if "_type" in vk_type.lower(): # TODO : This should be generic ENUM check
+            return ("%s", "string_%s(%s)" % (vk_type.replace('const ', '').strip('*'), name), deref)
+        if "char*" in vk_type:
+            return ("\\\"%s\\\"", name, "*")
+        if "uint64_t" in vk_type:
+            if '*' in vk_type:
+                return ("%\" PRIu64 \"",  "(%s == NULL) ? 0 : *(%s)" % (name, name), "*")
+            return ("%\" PRIu64 \"", name, deref)
+        if "uint32_t" in vk_type:
+            if '*' in vk_type:
+                return ("%u",  "(%s == NULL) ? 0 : *(%s)" % (name, name), "*")
+            return ("%u", name, deref)
+        if "xcb_visualid_t" in vk_type:
+            if '*' in vk_type:
+                return ("%u",  "(%s == NULL) ? 0 : *(%s)" % (name, name), "*")
+            return ("%u", name, deref)
+        if "VisualID" in vk_type:
+            return ("%\" PRIu64 \"", "(uint64_t)%s" % name, deref)
+        if "VkBool32" in vk_type:
+            if '*' in vk_type:
+                return ("%s",  "(*%s == VK_TRUE) ? \"VK_TRUE\" : \"VK_FALSE\"" % (name), "*")
+            return ("%s", "(%s == VK_TRUE) ? \"VK_TRUE\" : \"VK_FALSE\"" %(name), deref)
+        if "size_t" in vk_type:
+            if '*' in vk_type:
+                return ("\" VK_SIZE_T_SPECIFIER \"", "(%s == NULL) ? 0 : *(%s)" % (name, name), "*")
+            return ("\" VK_SIZE_T_SPECIFIER \"", name, deref)
+        if "float" in vk_type:
+            if '[' in vk_type: # handle array, current hard-coded to 4 (TODO: Make this dynamic)
+                return ("[%f, %f, %f, %f]", "%s[0], %s[1], %s[2], %s[3]" % (name, name, name, name), deref)
+            return ("%f", name, deref)
+        if "bool" in vk_type or 'xcb_randr_crtc_t' in vk_type:
+            return ("%u", name, deref)
+        if True in [t in vk_type.lower() for t in ["int", "flags", "mask", "xcb_window_t"]]:
+            if '[' in vk_type: # handle array, current hard-coded to 4 (TODO: Make this dynamic)
+                return ("[%i, %i, %i, %i]", "%s[0], %s[1], %s[2], %s[3]" % (name, name, name, name), deref)
+            if '*' in vk_type:
+                return ("%i", "(%s == NULL) ? 0 : *(%s)" % (name, name), "*")
+            return ("%i", name, deref)
+        if output_param:
+            return ("%p {%\" PRIx64 \"}", "(void*)%s, (%s == NULL) ? 0 : (uint64_t)*(%s)" % (name, name, name), deref)
+        return ("%p", "(void*)(%s)" % name, deref)
+
+    def _generate_init_funcs(self):
+        init_tracer = []
+        init_tracer.append('void send_vk_api_version_packet()\n{')
+        init_tracer.append('    packet_vkApiVersion* pPacket;')
+        init_tracer.append('    vktrace_trace_packet_header* pHeader;')
+        init_tracer.append('    pHeader = vktrace_create_trace_packet(VKTRACE_TID_VULKAN, VKTRACE_TPI_VK_vkApiVersion, sizeof(packet_vkApiVersion), 0);')
+        init_tracer.append('    pPacket = interpret_body_as_vkApiVersion(pHeader);')
+        init_tracer.append('    pPacket->version = VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION);')
+        init_tracer.append('    vktrace_set_packet_entrypoint_end_time(pHeader);')
+        init_tracer.append('    FINISH_TRACE_PACKET();\n}\n')
+
+        init_tracer.append('extern VKTRACE_CRITICAL_SECTION g_memInfoLock;')
+
+        init_tracer.append('#ifdef WIN32\n')
+        init_tracer.append('BOOL CALLBACK InitTracer(_Inout_ PINIT_ONCE initOnce, _Inout_opt_ PVOID param, _Out_opt_ PVOID *lpContext)\n{')
+        init_tracer.append('#elif defined(PLATFORM_LINUX)\n')
+        init_tracer.append('void InitTracer(void)\n{')
+        init_tracer.append('#endif\n')
+        init_tracer.append('#if defined(ANDROID)')
+        init_tracer.append('    // On Android, we can use an abstract socket to fit permissions model')
+        init_tracer.append('    const char *ipAddr = "localabstract";')
+        init_tracer.append('    const char *ipPort = "vktrace";')
+        init_tracer.append('    gMessageStream = vktrace_MessageStream_create_port_string(FALSE, ipAddr, ipPort);')
+        init_tracer.append('#else')
+        init_tracer.append('    const char *ipAddr = vktrace_get_global_var("VKTRACE_LIB_IPADDR");')
+        init_tracer.append('    if (ipAddr == NULL)')
+        init_tracer.append('        ipAddr = "127.0.0.1";')
+        init_tracer.append('    gMessageStream = vktrace_MessageStream_create(FALSE, ipAddr, VKTRACE_BASE_PORT + VKTRACE_TID_VULKAN);')
+        init_tracer.append('#endif')
+        init_tracer.append('    vktrace_trace_set_trace_file(vktrace_FileLike_create_msg(gMessageStream));')
+        init_tracer.append('    vktrace_tracelog_set_tracer_id(VKTRACE_TID_VULKAN);')
+        init_tracer.append('    vktrace_create_critical_section(&g_memInfoLock);')
+        init_tracer.append('    if (gMessageStream != NULL)')
+        init_tracer.append('        send_vk_api_version_packet();\n')
+        init_tracer.append('#ifdef WIN32\n')
+        init_tracer.append('    return true;\n}\n')
+        init_tracer.append('#elif defined(PLATFORM_LINUX)\n')
+        init_tracer.append('    return;\n}\n')
+        init_tracer.append('#endif\n')
+        return "\n".join(init_tracer)
+
+    # Take a list of params and return a list of dicts w/ ptr param details
+    def _get_packet_ptr_param_list(self, params):
+        ptr_param_list = []
+        # TODO : This is a slightly nicer way to handle custom cases than initial code, however
+        #   this can still be further generalized to eliminate more custom code
+        #   big case to handle is when ptrs to structs have embedded data that needs to be accounted for in packet
+        custom_ptr_dict = {'VkDeviceCreateInfo': {'add_txt': 'add_VkDeviceCreateInfo_to_packet(pHeader, (VkDeviceCreateInfo**) &(pPacket->pCreateInfo), pCreateInfo)',
+                                                  'finalize_txt': ''},
+                           'VkApplicationInfo': {'add_txt': 'add_VkApplicationInfo_to_packet(pHeader, (VkApplicationInfo**)&(pPacket->pApplicationInfo), pApplicationInfo)',
+                                                 'finalize_txt': ''},
+                           'VkPhysicalDevice': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pGpus), *pGpuCount*sizeof(VkPhysicalDevice), pGpus)',
+                                                'finalize_txt': 'default'},
+                           'VkImageCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkImageCreateInfo), pCreateInfo);\n'
+						                                    '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices), sizeof(uint32_t) * pCreateInfo->queueFamilyIndexCount, pCreateInfo->pQueueFamilyIndices)',
+                                               'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices));\n'
+											                   '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'VkBufferCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkBufferCreateInfo), pCreateInfo);\n'
+                                                            '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices), sizeof(uint32_t) * pCreateInfo->queueFamilyIndexCount, pCreateInfo->pQueueFamilyIndices)',
+                                               'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices));\n'
+                                                               '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'pDataSize': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDataSize), sizeof(size_t), &_dataSize)',
+                                         'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pDataSize))'},
+                           'pData': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pData), _dataSize, pData)',
+                                     'finalize_txt': 'default'},
+                           'pName': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pName), ((pName != NULL) ? ROUNDUP_TO_4(strlen(pName) + 1) : 0), pName)',
+                                     'finalize_txt': 'default'},
+                           'pMarker': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pMarker), ((pMarker != NULL) ? ROUNDUP_TO_4(strlen(pMarker) + 1) : 0), pMarker)',
+                                       'finalize_txt': 'default'},
+                           'pExtName': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pExtName), ((pExtName != NULL) ? ROUNDUP_TO_4(strlen(pExtName) + 1) : 0), pExtName)',
+                                        'finalize_txt': 'default'},
+                           'pDescriptorSets': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pDescriptorSets), customSize, pDescriptorSets)',
+                                               'finalize_txt': 'default'},
+                           'pSparseMemoryRequirements': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pSparseMemoryRequirements), (*pSparseMemoryRequirementCount) * sizeof(VkSparseImageMemoryRequirements), pSparseMemoryRequirements)',
+                                               'finalize_txt': 'default'},
+                           'pAllocator': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocator), sizeof(VkAllocationCallbacks), NULL)',
+                                          'finalize_txt': 'default'},
+                           'VkSparseImageFormatProperties': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pProperties), (*pPropertyCount) * sizeof(VkSparseImageFormatProperties), pProperties)',
+                                               'finalize_txt': 'default'},
+                           'VkSparseMemoryBindInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo), numBindings * sizeof(VkSparseMemoryBindInfo), pBindInfo)',
+                                               'finalize_txt': 'default'},
+                           'VkSparseImageMemoryBindInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pBindInfo), numBindings * sizeof(VkSparseImageMemoryBindInfo), pBindInfo)',
+                                               'finalize_txt': 'default'},
+                           'VkFramebufferCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkFramebufferCreateInfo), pCreateInfo);\n'
+                                                                  '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pColorAttachments), colorCount * sizeof(VkColorAttachmentBindInfo), pCreateInfo->pColorAttachments);\n'
+                                                                  '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pDepthStencilAttachment), dsSize, pCreateInfo->pDepthStencilAttachment)',
+                                                  'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pColorAttachments));\n'
+                                                                  '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pDepthStencilAttachment));\n'
+                                                                  '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'VkRenderPassCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkRenderPassCreateInfo), pCreateInfo);\n'
+                                                                 '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pColorFormats), colorCount * sizeof(VkFormat), pCreateInfo->pColorFormats);\n'
+                                                                 '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pColorLayouts), colorCount * sizeof(VkImageLayout), pCreateInfo->pColorLayouts);\n'
+                                                                 '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pColorLoadOps), colorCount * sizeof(VkAttachmentLoadOp), pCreateInfo->pColorLoadOps);\n'
+                                                                 '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pColorStoreOps), colorCount * sizeof(VkAttachmentStoreOp), pCreateInfo->pColorStoreOps);\n'
+                                                                 '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pColorLoadClearValues), colorCount * sizeof(VkClearColor), pCreateInfo->pColorLoadClearValues)',
+                                                 'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pColorFormats));\n'
+                                                                 '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pColorLayouts));\n'
+                                                                 '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pColorLoadOps));\n'
+                                                                 '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pColorStoreOps));\n'
+                                                                 '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pColorLoadClearValues));\n'
+                                                                 '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'VkPipelineLayoutCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkPipelineLayoutCreateInfo), pCreateInfo);\n'
+                                                                     '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pSetLayouts), pCreateInfo->setLayoutCount * sizeof(VkDescriptorSetLayout), pCreateInfo->pSetLayouts);'
+                                                                     '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pPushConstantRanges), pCreateInfo->pushConstantRangeCount * sizeof(VkPushConstantRange), pCreateInfo->pPushConstantRanges);',
+                                                     'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pSetLayouts));\n'
+                                                                     'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pPushConstantRanges));\n'
+                                                                     'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'VkMemoryAllocateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo), sizeof(VkMemoryAllocateInfo), pAllocateInfo);\n'
+                                                            '    add_alloc_memory_to_trace_packet(pHeader, (void**)&(pPacket->pAllocateInfo->pNext), pAllocateInfo->pNext)',
+                                            'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pAllocateInfo))'},
+#                          'VkGraphicsPipelineCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfos), count*sizeof(VkGraphicsPipelineCreateInfo), pCreateInfos);\n'
+#                                                                      '    add_VkGraphicsPipelineCreateInfos_to_trace_packet(pHeader, (VkGraphicsPipelineCreateInfo*)pPacket->pCreateInfos, pCreateInfos, count)',
+#                                                      'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfos))'},
+#                          'VkComputePipelineCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfos), count*sizeof(VkComputePipelineCreateInfo), pCreateInfos);\n'
+#                                                                      '    add_VkComputePipelineCreateInfos_to_trace_packet(pHeader, (VkComputePipelineCreateInfo*)pPacket->pCreateInfos, pCreateInfos, count)',
+#                                                      'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfos))'},
+                           'VkDescriptorPoolCreateInfo': {'add_txt': 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkDescriptorPoolCreateInfo), pCreateInfo);\n'
+                                                                     '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pPoolSizes), pCreateInfo->poolSizeCount * sizeof(VkDescriptorPoolSize), pCreateInfo->pPoolSizes)',
+                                                     'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pPoolSizes));\n'
+                                                                     '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'VkDescriptorSetLayoutCreateInfo': {'add_txt': 'add_create_ds_layout_to_trace_packet(pHeader, &pPacket->pCreateInfo, pCreateInfo)',
+                                                          'finalize_txt': '// pCreateInfo finalized in add_create_ds_layout_to_trace_packet'},
+                           'VkSwapchainCreateInfoKHR': {'add_txt':      'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkSwapchainCreateInfoKHR), pCreateInfo);\n'
+                                                                        '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices), pPacket->pCreateInfo->queueFamilyIndexCount * sizeof(uint32_t), pCreateInfo->pQueueFamilyIndices)',
+                                                        'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pQueueFamilyIndices));\n'
+                                                                        '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                           'VkShaderModuleCreateInfo': {'add_txt':      'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo), sizeof(VkShaderModuleCreateInfo), pCreateInfo);\n'
+                                                                        '    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->pCreateInfo->pCode), pPacket->pCreateInfo->codeSize, pCreateInfo->pCode)',
+                                                        'finalize_txt': 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo->pCode));\n'
+                                                                        '    vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->pCreateInfo))'},
+                          }
+
+        for p in params:
+            pp_dict = {}
+            if '*' in p.ty and p.name not in ['pTag', 'pUserData']:
+                if 'const' in p.ty.lower() and 'count' in params[params.index(p)-1].name.lower():
+                    pp_dict['add_txt'] = 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->%s), %s*sizeof(%s), %s)' % (p.name, params[params.index(p)-1].name, p.ty.strip('*').replace('const ', ''), p.name)
+                elif 'pOffsets' == p.name: # TODO : This is a custom case for BindVertexBuffers last param, need to clean this up
+                    pp_dict['add_txt'] = 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->%s), %s*sizeof(%s), %s)' % (p.name, params[params.index(p)-2].name, p.ty.strip('*').replace('const ', ''), p.name)
+                elif p.ty.strip('*').replace('const ', '') in custom_ptr_dict:
+                    pp_dict['add_txt'] = custom_ptr_dict[p.ty.strip('*').replace('const ', '')]['add_txt']
+                    pp_dict['finalize_txt'] = custom_ptr_dict[p.ty.strip('*').replace('const ', '')]['finalize_txt']
+                elif p.name in custom_ptr_dict:
+                    pp_dict['add_txt'] = custom_ptr_dict[p.name]['add_txt']
+                    pp_dict['finalize_txt'] = custom_ptr_dict[p.name]['finalize_txt']
+                    # TODO : This is custom hack to account for 2 pData items with dataSize param for sizing
+                    if 'pData' == p.name and 'dataSize' == params[params.index(p)-1].name:
+                        pp_dict['add_txt'] = pp_dict['add_txt'].replace('_dataSize', 'dataSize')
+                elif 'void' in p.ty and (p.name == 'pData' or p.name == 'pValues'):
+                    pp_dict['add_txt'] = '//TODO FIXME vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->%s), sizeof(%s), %s)' % (p.name, p.ty.strip('*').replace('const ', ''), p.name)
+                    pp_dict['finalize_txt'] = '//TODO FIXME vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->%s))' % (p.name)
+                else:
+                    pp_dict['add_txt'] = 'vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(pPacket->%s), sizeof(%s), %s)' % (p.name, p.ty.strip('*').replace('const ', ''), p.name)
+                if 'finalize_txt' not in pp_dict or 'default' == pp_dict['finalize_txt']:
+                    pp_dict['finalize_txt'] = 'vktrace_finalize_buffer_address(pHeader, (void**)&(pPacket->%s))' % (p.name)
+                pp_dict['index'] = params.index(p)
+                ptr_param_list.append(pp_dict)
+        return ptr_param_list
+
+    # Take a list of params and return a list of packet size elements
+    def _get_packet_size(self, extensionName, params):
+        ps = [] # List of elements to be added together to account for packet size for given params
+        skip_list = [] # store params that are already accounted for so we don't count them twice
+        # Dict of specific params with unique custom sizes
+        # TODO: Now using bitfields for all stages, need pSetBindPoints to accommodate that.
+        custom_size_dict = {'pSetBindPoints': '(VK_SHADER_STAGE_COMPUTE * sizeof(uint32_t))', # Accounting for largest possible array
+                            'VkSwapchainCreateInfoKHR' : 'vk_size_vkswapchaincreateinfokhr(pCreateInfo)',
+                            }
+        size_func_suffix = ''
+        if extensionName.lower() != "vk_version_1_0":
+            size_func_suffix = '_%s' % extensionName.lower()
+        for p in params:
+            #First handle custom cases
+            if p.name in ['pCreateInfo', 'pSetLayoutInfoList', 'pBeginInfo', 'pAllocateInfo'] and 'khr' not in p.ty.lower() and 'lunarg' not in p.ty.lower() and 'ext' not in p.ty.lower():
+                ps.append('get_struct_chain_size%s((void*)%s)' % (size_func_suffix, p.name))
+                skip_list.append(p.name)
+            elif p.name in custom_size_dict:
+                ps.append(custom_size_dict[p.name])
+                skip_list.append(p.name)
+            elif p.ty.strip('*').replace('const ', '') in custom_size_dict:
+                tmp_ty = p.ty.strip('*').replace('const ', '')
+                ps.append(custom_size_dict[tmp_ty])
+                skip_list.append(p.name)
+            # Skip any params already handled
+            if p.name in skip_list:
+                continue
+            # Now check to identify dynamic arrays which depend on two params
+            if 'count' in p.name.lower():
+                next_idx = params.index(p)+1
+                # If next element is a const *, then multiply count and array type
+                if next_idx < len(params) and '*' in params[next_idx].ty and 'const' in params[next_idx].ty.lower():
+                    if '*' in p.ty:
+                        ps.append('*%s*sizeof(%s)' % (p.name, params[next_idx].ty.strip('*').replace('const ', '')))
+                    else:
+                        ps.append('%s*sizeof(%s)' % (p.name, params[next_idx].ty.strip('*').replace('const ', '')))
+                    skip_list.append(params[next_idx].name)
+                if 'bindingCount' == p.name: # TODO : This is custom case for CmdBindVertexBuffers, need to clean it up
+                    ps.append('%s*sizeof(%s)' % (p.name, params[next_idx+1].ty.strip('*').replace('const ', '')))
+                    skip_list.append(params[next_idx+1].name)
+                elif '*' in p.ty: # Not a custom array size we're aware of, but ptr so need to account for its size
+                    ps.append('sizeof(%s)' % (p.ty.strip('*').replace('const ', '')))
+            elif '*' in p.ty and p.name not in ['pSysMem', 'pReserved']:
+                if 'pData' == p.name:
+                    if 'dataSize' == params[params.index(p)-1].name:
+                        ps.append('dataSize')
+                    elif 'counterCount' == params[params.index(p)-1].name:
+                        ps.append('sizeof(%s)' % p.ty.strip('*').replace('const ', ''))
+                    else:
+                        #ps.append('((pDataSize != NULL && pData != NULL) ? *pDataSize : 0)')
+                        ps.append('sizeof(void*)')
+                elif '**' in p.ty and 'void' in p.ty:
+                    ps.append('sizeof(void*)')
+                elif 'void' in p.ty:
+                    ps.append('sizeof(%s)' % p.name)
+                elif 'char' in p.ty:
+                    ps.append('((%s != NULL) ? ROUNDUP_TO_4(strlen(%s) + 1) : 0)' % (p.name, p.name))
+                elif 'pDataSize' in p.name:
+                    ps.append('((pDataSize != NULL) ? sizeof(size_t) : 0)')
+                elif 'IMAGE_SUBRESOURCE' in p.ty and 'pSubresource' == p.name:
+                    ps.append('((pSubresource != NULL) ? sizeof(VkImage_SUBRESOURCE) : 0)')
+                else:
+                    ps.append('sizeof(%s)' % (p.ty.strip('*').replace('const ', '')))
+        return ps
+
+    # Generate functions used to trace API calls and store the input and result data into a packet
+    # Here's the general flow of code insertion w/ option items flagged w/ "?"
+    # Result decl?
+    # Packet struct decl
+    # ?Special case : setup call to function first and do custom API call time tracking
+    # CREATE_PACKET
+    # call real entrypoint and get return value (if there is one)
+    # Assign packet values
+    # FINISH packet
+    # return result if needed
+    def _generate_trace_funcs(self, extensionName):
+        func_body = []
+        manually_written_hooked_funcs = ['AllocateCommandBuffers',
+                                         'AllocateMemory',
+                                         'AllocateDescriptorSets',
+                                         'BeginCommandBuffer',
+                                         'CreateDescriptorPool',
+                                         'CreateDevice',
+                                         'CreateFramebuffer',
+                                         'CreateInstance',
+                                         'CreatePipelineCache',
+                                         'CreateRenderPass',
+                                         'GetPipelineCacheData',
+                                         'CreateGraphicsPipelines',
+                                         'CreateComputePipelines',
+                                         'CmdPipelineBarrier',
+                                         'CmdWaitEvents',
+                                         'CmdBeginRenderPass',
+                                         'CmdPushConstants',
+                                         'DestroyInstance',
+                                         'EnumeratePhysicalDevices',
+                                         'FreeMemory',
+                                         'FreeDescriptorSets',
+                                         'QueueSubmit',
+                                         'QueueBindSparse',
+                                         'FlushMappedMemoryRanges',
+                                         'InvalidateMappedMemoryRanges',
+                                         'GetDeviceProcAddr',
+                                         'GetInstanceProcAddr',
+                                         'EnumerateInstanceExtensionProperties',
+                                         'EnumerateDeviceExtensionProperties',
+                                         'EnumerateInstanceLayerProperties',
+                                         'EnumerateDeviceLayerProperties',
+                                         'GetPhysicalDeviceQueueFamilyProperties',
+                                         'GetQueryPoolResults',
+                                         'MapMemory',
+                                         'UnmapMemory',
+                                         'UpdateDescriptorSets',
+                                         'GetPhysicalDeviceSurfaceCapabilitiesKHR',
+                                         'GetPhysicalDeviceSurfaceFormatsKHR',
+                                         'GetPhysicalDeviceSurfacePresentModesKHR',
+                                         'CreateSwapchainKHR',
+                                         'GetSwapchainImagesKHR',
+                                         'QueuePresentKHR',
+                                         #TODO add Wayland, Mir
+                                         'CreateXcbSurfaceKHR',
+                                         'CreateXlibSurfaceKHR',
+                                         'GetPhysicalDeviceXcbPresentationSupportKHR',
+                                         'GetPhysicalDeviceXlibPresentationSupportKHR',
+                                         'CreateWin32SurfaceKHR',
+                                         'GetPhysicalDeviceWin32PresentationSupportKHR',
+                                         'CreateAndroidSurfaceKHR',
+                                         ]
+
+        # validate the manually_written_hooked_funcs list
+        protoFuncs = [proto.name for proto in self.protos]
+        wsi_platform_manual_funcs = ['CreateWin32SurfaceKHR', 'CreateXcbSurfaceKHR', 'CreateXlibSurfaceKHR', 'CreateAndroidSurfaceKHR',
+                                     'GetPhysicalDeviceXcbPresentationSupportKHR','GetPhysicalDeviceXlibPresentationSupportKHR', 'GetPhysicalDeviceWin32PresentationSupportKHR']
+        for func in manually_written_hooked_funcs:
+            if (func not in protoFuncs) and (func not in wsi_platform_manual_funcs):
+                sys.exit("Entry '%s' in manually_written_hooked_funcs list is not in the vulkan function prototypes" % func)
+
+        # process each of the entrypoint prototypes
+        approved_ext = ['vk_khr_surface', 'vk_khr_swapchain', 'vk_khr_win32_surface', 'vk_khr_xcb_surface', 'vk_ext_debug_report']
+        for ext in vulkan.extensions_all:
+            if (ext.name.lower() == extensionName.lower()) or ((extensionName.lower() == 'vk_version_1_0') and (ext.name.lower() in approved_ext)):
+                for proto in ext.protos:
+                    if proto.name in manually_written_hooked_funcs:
+                        func_body.append( '// __HOOKED_vk%s is manually written. Look in vktrace_lib_trace.cpp\n' % proto.name)
+                    elif proto.name not in proto_exclusions:
+                        raw_packet_update_list = [] # non-ptr elements placed directly into packet
+                        ptr_packet_update_list = [] # ptr elements to be updated into packet
+                        return_txt = ''
+                        packet_size = []
+                        in_data_size = False # flag when we need to capture local input size variable for in/out size
+                        func_body.append('%s' % self.lineinfo.get())
+                        func_body.append('VKTRACER_EXPORT VKAPI_ATTR %s VKAPI_CALL __HOOKED_vk%s(' % (proto.ret, proto.name))
+                        for p in proto.params: # TODO : For all of the ptr types, check them for NULL and return 0 if NULL
+                            func_body.append('    %s,' % p.c())
+                            if '*' in p.ty and p.name not in ['pSysMem', 'pReserved']:
+                                if 'pDataSize' in p.name:
+                                    in_data_size = True;
+                            elif 'pfnMsgCallback' == p.name:
+                                raw_packet_update_list.append('    PFN_vkDebugReportCallbackEXT* pNonConstCallback = (PFN_vkDebugReportCallbackEXT*)&pPacket->pfnMsgCallback;')
+                                raw_packet_update_list.append('    *pNonConstCallback = pfnMsgCallback;')
+                            elif '[' in p.ty:
+                                raw_packet_update_list.append('    memcpy((void *) pPacket->%s, %s, sizeof(pPacket->%s));' % (p.name, p.name, p.name))
+                            else:
+                                raw_packet_update_list.append('    pPacket->%s = %s;' % (p.name, p.name))
+                        # Get list of packet size modifiers due to ptr params
+                        packet_size = self._get_packet_size(extensionName, proto.params)
+                        ptr_packet_update_list = self._get_packet_ptr_param_list(proto.params)
+                        func_body[-1] = func_body[-1].replace(',', ')')
+                        # End of function declaration portion, begin function body
+                        func_body.append('{\n    vktrace_trace_packet_header* pHeader;')
+                        if 'void' not in proto.ret or '*' in proto.ret:
+                            func_body.append('    %s result;' % proto.ret)
+                            return_txt = 'result = '
+                        if in_data_size:
+                            func_body.append('    size_t _dataSize;')
+                        func_body.append('    packet_vk%s* pPacket = NULL;' % proto.name)
+                        if proto.name == "DestroyInstance" or proto.name == "DestroyDevice":
+                            func_body.append('    dispatch_key key = get_dispatch_key(%s);' % proto.params[0].name)
+
+                        if (0 == len(packet_size)):
+                            func_body.append('    CREATE_TRACE_PACKET(vk%s, 0);' % (proto.name))
+                        else:
+                            func_body.append('    CREATE_TRACE_PACKET(vk%s, %s);' % (proto.name, ' + '.join(packet_size)))
+
+                        # call down the layer chain and get return value (if there is one)
+                        # Note: this logic doesn't work for CreateInstance or CreateDevice but those are handwritten
+                        if extensionName == 'vk_lunarg_debug_marker':
+                            table_txt = 'mdd(%s)->debugMarkerTable' % proto.params[0].name
+                        elif proto.params[0].ty in ['VkInstance', 'VkPhysicalDevice']:
+                           table_txt = 'mid(%s)->instTable' % proto.params[0].name
+                        else:
+                           table_txt = 'mdd(%s)->devTable' % proto.params[0].name
+                        func_body.append('    %s%s.%s;' % (return_txt, table_txt, proto.c_call()))
+                        func_body.append('    vktrace_set_packet_entrypoint_end_time(pHeader);')
+
+                        if in_data_size:
+                            func_body.append('    _dataSize = (pDataSize == NULL || pData == NULL) ? 0 : *pDataSize;')
+                        func_body.append('    pPacket = interpret_body_as_vk%s(pHeader);' % proto.name)
+                        func_body.append('\n'.join(raw_packet_update_list))
+                        for pp_dict in ptr_packet_update_list: #buff_ptr_indices:
+                            func_body.append('    %s;' % (pp_dict['add_txt']))
+                        if 'void' not in proto.ret or '*' in proto.ret:
+                            func_body.append('    pPacket->result = result;')
+                        for pp_dict in ptr_packet_update_list:
+                            if ('DeviceCreateInfo' not in proto.params[pp_dict['index']].ty):
+                                func_body.append('    %s;' % (pp_dict['finalize_txt']))
+                        # All buffers should be finalized by now, and the trace packet can be finished (which sends it over the socket)
+                        func_body.append('    FINISH_TRACE_PACKET();')
+                        if proto.name == "DestroyInstance":
+                            func_body.append('    g_instanceDataMap.erase(key);')
+                        elif proto.name == "DestroyDevice":
+                            func_body.append('    g_deviceDataMap.erase(key);')
+
+                        # return result if needed
+                        if 'void' not in proto.ret or '*' in proto.ret:
+                            func_body.append('    return result;')
+                        func_body.append('}\n')
+        return "\n".join(func_body)
+
+    def _generate_packet_id_enum(self):
+        pid_enum = []
+        pid_enum.append('enum VKTRACE_TRACE_PACKET_ID_VK')
+        pid_enum.append('{')
+        first_func = True
+        for proto in self.protos:
+            if proto.name in proto_exclusions:
+                continue
+            if first_func:
+                first_func = False
+                pid_enum.append('    VKTRACE_TPI_VK_vkApiVersion = VKTRACE_TPI_BEGIN_API_HERE,')
+                pid_enum.append('    VKTRACE_TPI_VK_vk%s,' % proto.name)
+            else:
+                pid_enum.append('    VKTRACE_TPI_VK_vk%s,' % proto.name)
+        pid_enum.append('};\n')
+        return "\n".join(pid_enum)
+
+    def _generate_packet_id_name_func(self):
+        func_body = []
+        func_body.append('static const char *vktrace_vk_packet_id_name(const enum VKTRACE_TRACE_PACKET_ID_VK id)')
+        func_body.append('{')
+        func_body.append('    switch(id) {')
+        func_body.append('    case VKTRACE_TPI_VK_vkApiVersion:')
+        func_body.append('    {')
+        func_body.append('        return "vkApiVersion";')
+        func_body.append('    }')
+        for proto in self.protos:
+            if proto.name in proto_exclusions:
+                continue
+            func_body.append('    case VKTRACE_TPI_VK_vk%s:' % proto.name)
+            func_body.append('    {')
+            func_body.append('        return "vk%s";' % proto.name)
+            func_body.append('    }')
+        func_body.append('    default:')
+        func_body.append('        return NULL;')
+        func_body.append('    }')
+        func_body.append('}\n')
+        return "\n".join(func_body)
+
+    def _generate_stringify_func(self):
+        func_body = []
+        func_body.append('static const char *vktrace_stringify_vk_packet_id(const enum VKTRACE_TRACE_PACKET_ID_VK id, const vktrace_trace_packet_header* pHeader)')
+        func_body.append('{')
+        func_body.append('    static char str[1024];')
+        func_body.append('    switch(id) {')
+        func_body.append('    case VKTRACE_TPI_VK_vkApiVersion:')
+        func_body.append('    {')
+        func_body.append('        packet_vkApiVersion* pPacket = (packet_vkApiVersion*)(pHeader->pBody);')
+        func_body.append('        snprintf(str, 1024, "vkApiVersion = 0x%x", pPacket->version);')
+        func_body.append('        return str;')
+        func_body.append('    }')
+        for proto in self.protos:
+            if proto.name in proto_exclusions:
+                continue
+            func_body.append('    case VKTRACE_TPI_VK_vk%s:' % proto.name)
+            func_body.append('    {')
+            func_str = 'vk%s(' % proto.name
+            print_vals = ''
+            create_func = False
+            if 'Create' in proto.name or 'Alloc' in proto.name or 'MapMemory' in proto.name:
+                create_func = True
+            for p in proto.params:
+                last_param = False
+                if (p.name == proto.params[-1].name):
+                    last_param = True
+                if last_param and create_func: # last param of create func
+                    (pft, pfi, ptr) = self._get_printf_params(p.ty,'pPacket->%s' % p.name, True)
+                else:
+                    (pft, pfi, ptr) = self._get_printf_params(p.ty, 'pPacket->%s' % p.name, False)
+                if last_param == True:
+                    func_str += '%s%s = %s)' % (ptr, p.name, pft)
+                    print_vals += ', %s' % (pfi)
+                else:
+                    func_str += '%s%s = %s, ' % (ptr, p.name, pft)
+                    print_vals += ', %s' % (pfi)
+            func_body.append('        packet_vk%s* pPacket = (packet_vk%s*)(pHeader->pBody);' % (proto.name, proto.name))
+            func_body.append('        snprintf(str, 1024, "%s"%s);' % (func_str, print_vals))
+            func_body.append('        return str;')
+            func_body.append('    }')
+        func_body.append('    default:')
+        func_body.append('        return NULL;')
+        func_body.append('    }')
+        func_body.append('};\n')
+        return "\n".join(func_body)
+    
+    def _generate_interp_func(self):
+        interp_func_body = []
+        interp_func_body.append('%s' % self.lineinfo.get())
+        interp_func_body.append('static vktrace_trace_packet_header* interpret_trace_packet_vk(vktrace_trace_packet_header* pHeader)')
+        interp_func_body.append('{')
+        interp_func_body.append('    if (pHeader == NULL)')
+        interp_func_body.append('    {')
+        interp_func_body.append('        return NULL;')
+        interp_func_body.append('    }')
+        interp_func_body.append('    switch (pHeader->packet_id)')
+        interp_func_body.append('    {')
+        interp_func_body.append('        case VKTRACE_TPI_VK_vkApiVersion:')
+        interp_func_body.append('        {')
+        interp_func_body.append('            return interpret_body_as_vkApiVersion(pHeader)->header;')
+        interp_func_body.append('        }')
+        for proto in self.protos:
+            if proto.name in proto_exclusions:
+                continue
+
+            interp_func_body.append('        case VKTRACE_TPI_VK_vk%s:\n        {' % proto.name)
+            header_prefix = 'h'
+            if 'Dbg' in proto.name :
+                header_prefix = 'pH'
+            interp_func_body.append('%s' % self.lineinfo.get())
+            interp_func_body.append('            return interpret_body_as_vk%s(pHeader)->%seader;\n        }' % (proto.name, header_prefix))
+        interp_func_body.append('        default:')
+        interp_func_body.append('            return NULL;')
+        interp_func_body.append('    }')
+        interp_func_body.append('    return NULL;')
+        interp_func_body.append('}')
+        return "\n".join(interp_func_body)
+
+    def _generate_struct_util_funcs(self):
+        lineinfo = self.lineinfo
+        pid_enum = []
+        pid_enum.append('%s' % lineinfo.get())
+        pid_enum.append('//=============================================================================')
+        pid_enum.append('static void add_VkApplicationInfo_to_packet(vktrace_trace_packet_header*  pHeader, VkApplicationInfo** ppStruct, const VkApplicationInfo *pInStruct)')
+        pid_enum.append('{')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)ppStruct, sizeof(VkApplicationInfo), pInStruct);')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppStruct)->pApplicationName), (pInStruct->pApplicationName != NULL) ? ROUNDUP_TO_4(strlen(pInStruct->pApplicationName) + 1) : 0, pInStruct->pApplicationName);')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppStruct)->pEngineName), (pInStruct->pEngineName != NULL) ? ROUNDUP_TO_4(strlen(pInStruct->pEngineName) + 1) : 0, pInStruct->pEngineName);')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)&((*ppStruct)->pApplicationName));')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)&((*ppStruct)->pEngineName));')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)&*ppStruct);')
+        pid_enum.append('};\n')
+        pid_enum.append('%s' % lineinfo.get())
+        pid_enum.append('static void add_VkInstanceCreateInfo_to_packet(vktrace_trace_packet_header* pHeader, VkInstanceCreateInfo** ppStruct, VkInstanceCreateInfo *pInStruct)')
+        pid_enum.append('{')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)ppStruct, sizeof(VkInstanceCreateInfo), pInStruct);')
+        pid_enum.append('    if (pInStruct->pApplicationInfo) add_VkApplicationInfo_to_packet(pHeader, (VkApplicationInfo**)&((*ppStruct)->pApplicationInfo), pInStruct->pApplicationInfo);')
+        # TODO138 : This is an initial pass at getting the extension/layer arrays correct, needs to be validated.
+        pid_enum.append('    uint32_t i, siz = 0;')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppStruct)->ppEnabledLayerNames), pInStruct->enabledLayerCount * sizeof(char*), pInStruct->ppEnabledLayerNames);')
+        pid_enum.append('    if (pInStruct->enabledLayerCount > 0) ')
+        pid_enum.append('    {')
+        pid_enum.append('        for (i = 0; i < pInStruct->enabledLayerCount; i++) {')
+        pid_enum.append('            siz = (uint32_t) ROUNDUP_TO_4(1 + strlen(pInStruct->ppEnabledLayerNames[i]));')
+        pid_enum.append('            vktrace_add_buffer_to_trace_packet(pHeader, (void**)(&(*ppStruct)->ppEnabledLayerNames[i]), siz, pInStruct->ppEnabledLayerNames[i]);')
+        pid_enum.append('            vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledLayerNames[i]);')
+        pid_enum.append('        }')
+        pid_enum.append('    }')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledLayerNames);')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppStruct)->ppEnabledExtensionNames), pInStruct->enabledExtensionCount * sizeof(char*), pInStruct->ppEnabledExtensionNames);')
+        pid_enum.append('    if (pInStruct->enabledExtensionCount > 0) ')
+        pid_enum.append('    {')
+        pid_enum.append('        for (i = 0; i < pInStruct->enabledExtensionCount; i++) {')
+        pid_enum.append('            siz = (uint32_t) ROUNDUP_TO_4(1 + strlen(pInStruct->ppEnabledExtensionNames[i]));')
+        pid_enum.append('            vktrace_add_buffer_to_trace_packet(pHeader, (void**)(&(*ppStruct)->ppEnabledExtensionNames[i]), siz, pInStruct->ppEnabledExtensionNames[i]);')
+        pid_enum.append('            vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledExtensionNames[i]);')
+        pid_enum.append('        }')
+        pid_enum.append('    }')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledExtensionNames);')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)ppStruct);')
+        pid_enum.append('}\n')
+        pid_enum.append('%s' % lineinfo.get())
+        pid_enum.append('static void add_VkDeviceCreateInfo_to_packet(vktrace_trace_packet_header*  pHeader, VkDeviceCreateInfo** ppStruct, const VkDeviceCreateInfo *pInStruct)')
+        pid_enum.append('{')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)ppStruct, sizeof(VkDeviceCreateInfo), pInStruct);')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(*ppStruct)->pQueueCreateInfos, pInStruct->queueCreateInfoCount*sizeof(VkDeviceQueueCreateInfo), pInStruct->pQueueCreateInfos);')
+        pid_enum.append('    for (uint32_t i = 0; i < pInStruct->queueCreateInfoCount; i++) {')
+        pid_enum.append('        vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(*ppStruct)->pQueueCreateInfos[i].pQueuePriorities,')
+        pid_enum.append('                                   pInStruct->pQueueCreateInfos[i].queueCount*sizeof(float),')
+        pid_enum.append('                                   pInStruct->pQueueCreateInfos[i].pQueuePriorities);')
+        pid_enum.append('        vktrace_finalize_buffer_address(pHeader, (void**)&(*ppStruct)->pQueueCreateInfos[i].pQueuePriorities);')
+        pid_enum.append('    }')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)&(*ppStruct)->pQueueCreateInfos);')
+        # TODO138 : This is an initial pass at getting the extension/layer arrays correct, needs to be validated.
+        pid_enum.append('    uint32_t i, siz = 0;')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppStruct)->ppEnabledLayerNames), pInStruct->enabledLayerCount * sizeof(char*), pInStruct->ppEnabledLayerNames);')
+        pid_enum.append('    if (pInStruct->enabledLayerCount > 0) ')
+        pid_enum.append('    {')
+        pid_enum.append('        for (i = 0; i < pInStruct->enabledLayerCount; i++) {')
+        pid_enum.append('            siz = (uint32_t) ROUNDUP_TO_4(1 + strlen(pInStruct->ppEnabledLayerNames[i]));')
+        pid_enum.append('            vktrace_add_buffer_to_trace_packet(pHeader, (void**)(&(*ppStruct)->ppEnabledLayerNames[i]), siz, pInStruct->ppEnabledLayerNames[i]);')
+        pid_enum.append('            vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledLayerNames[i]);')
+        pid_enum.append('        }')
+        pid_enum.append('    }')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledLayerNames);')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&((*ppStruct)->ppEnabledExtensionNames), pInStruct->enabledExtensionCount * sizeof(char*), pInStruct->ppEnabledExtensionNames);')
+        pid_enum.append('    if (pInStruct->enabledExtensionCount > 0) ')
+        pid_enum.append('    {')
+        pid_enum.append('        for (i = 0; i < pInStruct->enabledExtensionCount; i++) {')
+        pid_enum.append('            siz = (uint32_t) ROUNDUP_TO_4(1 + strlen(pInStruct->ppEnabledExtensionNames[i]));')
+        pid_enum.append('            vktrace_add_buffer_to_trace_packet(pHeader, (void**)(&(*ppStruct)->ppEnabledExtensionNames[i]), siz, pInStruct->ppEnabledExtensionNames[i]);')
+        pid_enum.append('            vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledExtensionNames[i]);')
+        pid_enum.append('        }')
+        pid_enum.append('    }')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void **)&(*ppStruct)->ppEnabledExtensionNames);')
+        pid_enum.append('    vktrace_add_buffer_to_trace_packet(pHeader, (void**)&(*ppStruct)->pEnabledFeatures, sizeof(VkPhysicalDeviceFeatures), pInStruct->pEnabledFeatures);')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)&(*ppStruct)->pEnabledFeatures);')
+        pid_enum.append('    vktrace_finalize_buffer_address(pHeader, (void**)ppStruct);')
+        pid_enum.append('}\n')
+        pid_enum.append('%s' % lineinfo.get())
+        pid_enum.append('//=============================================================================\n')
+        pid_enum.append('static VkInstanceCreateInfo* interpret_VkInstanceCreateInfo(vktrace_trace_packet_header*  pHeader, intptr_t ptr_variable)')
+        pid_enum.append('{')
+        pid_enum.append('    VkInstanceCreateInfo* pVkInstanceCreateInfo = (VkInstanceCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)ptr_variable);\n')
+        pid_enum.append('    uint32_t i;')
+        pid_enum.append('    if (pVkInstanceCreateInfo != NULL)')
+        pid_enum.append('    {')
+        pid_enum.append('        pVkInstanceCreateInfo->pApplicationInfo = (VkApplicationInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->pApplicationInfo);')
+        pid_enum.append('        VkApplicationInfo** ppApplicationInfo = (VkApplicationInfo**) &pVkInstanceCreateInfo->pApplicationInfo;')
+        pid_enum.append('        if (pVkInstanceCreateInfo->pApplicationInfo)')
+        pid_enum.append('        {')
+        pid_enum.append('            (*ppApplicationInfo)->pApplicationName = (const char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->pApplicationInfo->pApplicationName);')
+        pid_enum.append('            (*ppApplicationInfo)->pEngineName = (const char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->pApplicationInfo->pEngineName);')
+        pid_enum.append('        }')
+        pid_enum.append('        if (pVkInstanceCreateInfo->enabledLayerCount > 0)')
+        pid_enum.append('        {')
+        pid_enum.append('            pVkInstanceCreateInfo->ppEnabledLayerNames = (const char* const*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->ppEnabledLayerNames);')
+        pid_enum.append('            for (i = 0; i < pVkInstanceCreateInfo->enabledLayerCount; i++) {')
+        pid_enum.append('                char** ppTmp = (char**)&pVkInstanceCreateInfo->ppEnabledLayerNames[i];')
+        pid_enum.append('                *ppTmp = (char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->ppEnabledLayerNames[i]);')
+        pid_enum.append('            }')
+        pid_enum.append('        }')
+        pid_enum.append('        if (pVkInstanceCreateInfo->enabledExtensionCount > 0)')
+        pid_enum.append('        {')
+        pid_enum.append('            pVkInstanceCreateInfo->ppEnabledExtensionNames = (const char* const*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->ppEnabledExtensionNames);')
+        pid_enum.append('            for (i = 0; i < pVkInstanceCreateInfo->enabledExtensionCount; i++) {')
+        pid_enum.append('                char** ppTmp = (char**)&pVkInstanceCreateInfo->ppEnabledExtensionNames[i];')
+        pid_enum.append('                *ppTmp = (char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkInstanceCreateInfo->ppEnabledExtensionNames[i]);')
+        pid_enum.append('            }')
+        pid_enum.append('        }')
+        pid_enum.append('    }\n')
+        pid_enum.append('    return pVkInstanceCreateInfo;')
+        pid_enum.append('}\n')
+        pid_enum.append('%s' % lineinfo.get())
+        pid_enum.append('static VkDeviceCreateInfo* interpret_VkDeviceCreateInfo(vktrace_trace_packet_header*  pHeader, intptr_t ptr_variable)')
+        pid_enum.append('{')
+        pid_enum.append('    VkDeviceCreateInfo* pVkDeviceCreateInfo = (VkDeviceCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)ptr_variable);\n')
+        pid_enum.append('    uint32_t i;')
+        pid_enum.append('    if (pVkDeviceCreateInfo != NULL)')
+        pid_enum.append('    {')
+        pid_enum.append('        if (pVkDeviceCreateInfo->queueCreateInfoCount > 0)')
+        pid_enum.append('        {')
+        pid_enum.append('            pVkDeviceCreateInfo->pQueueCreateInfos = (const VkDeviceQueueCreateInfo *)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->pQueueCreateInfos);')
+        pid_enum.append('            for (i = 0; i < pVkDeviceCreateInfo->queueCreateInfoCount; i++) {')
+        pid_enum.append('                float** ppQueuePriority = (float**)&pVkDeviceCreateInfo->pQueueCreateInfos[i].pQueuePriorities;')
+        pid_enum.append('                *ppQueuePriority = (float*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->pQueueCreateInfos[i].pQueuePriorities);')
+        pid_enum.append('            }')
+        pid_enum.append('        }')
+        pid_enum.append('        if (pVkDeviceCreateInfo->enabledLayerCount > 0)')
+        pid_enum.append('        {')
+        pid_enum.append('            pVkDeviceCreateInfo->ppEnabledLayerNames = (const char* const*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->ppEnabledLayerNames);')
+        pid_enum.append('            for (i = 0; i < pVkDeviceCreateInfo->enabledLayerCount; i++) {')
+        pid_enum.append('                char** ppTmp = (char**)&pVkDeviceCreateInfo->ppEnabledLayerNames[i];')
+        pid_enum.append('                *ppTmp = (char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->ppEnabledLayerNames[i]);')
+        pid_enum.append('            }')
+        pid_enum.append('        }')
+        pid_enum.append('        if (pVkDeviceCreateInfo->enabledExtensionCount > 0)')
+        pid_enum.append('        {')
+        pid_enum.append('            pVkDeviceCreateInfo->ppEnabledExtensionNames = (const char* const*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->ppEnabledExtensionNames);')
+        pid_enum.append('            for (i = 0; i < pVkDeviceCreateInfo->enabledExtensionCount; i++) {')
+        pid_enum.append('                char** ppTmp = (char**)&pVkDeviceCreateInfo->ppEnabledExtensionNames[i];')
+        pid_enum.append('                *ppTmp = (char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->ppEnabledExtensionNames[i]);')
+        pid_enum.append('            }')
+        pid_enum.append('        }')
+        pid_enum.append('        pVkDeviceCreateInfo->pEnabledFeatures = (const VkPhysicalDeviceFeatures*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pVkDeviceCreateInfo->pEnabledFeatures);\n')
+        pid_enum.append('    }\n')
+        pid_enum.append('    return pVkDeviceCreateInfo;')
+        pid_enum.append('}\n')
+        pid_enum.append('%s' % lineinfo.get())
+        pid_enum.append('static void interpret_VkPipelineShaderStageCreateInfo(vktrace_trace_packet_header*  pHeader, VkPipelineShaderStageCreateInfo* pShader)')
+        pid_enum.append('{')
+        pid_enum.append('    if (pShader != NULL)')
+        pid_enum.append('    {')
+        pid_enum.append('        pShader->pName = (const char*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pShader->pName);')
+        pid_enum.append('        // specialization info')
+        pid_enum.append('        pShader->pSpecializationInfo = (const VkSpecializationInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pShader->pSpecializationInfo);')
+        pid_enum.append('        if (pShader->pSpecializationInfo != NULL)')
+        pid_enum.append('        {')
+        pid_enum.append('            VkSpecializationInfo* pInfo = (VkSpecializationInfo*)pShader->pSpecializationInfo;')
+        pid_enum.append('            pInfo->pMapEntries = (const VkSpecializationMapEntry*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pShader->pSpecializationInfo->pMapEntries);')
+        pid_enum.append('            pInfo->pData = (const void*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pShader->pSpecializationInfo->pData);')
+        pid_enum.append('        }')
+        pid_enum.append('    }')
+        pid_enum.append('}\n')
+        pid_enum.append('//=============================================================================')
+        return "\n".join(pid_enum)
+
+    # Interpret functions used on replay to read in packets and interpret their contents
+    #  This code gets generated into vktrace_vk_vk_packets.h file
+    def _generate_interp_funcs(self):
+        # Custom txt for given function and parameter.  First check if param is NULL, then insert txt if not
+        # First some common code used by both CmdWaitEvents & CmdPipelineBarrier
+        mem_barrier_interp = ['uint32_t i = 0;\n',
+                              'for (i = 0; i < pPacket->memoryBarrierCount; i++) {\n',
+                              '    void** ppMB = (void**)&(pPacket->ppMemoryBarriers[i]);\n',
+                              '    *ppMB = vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->ppMemoryBarriers[i]);\n',
+                              '    //VkMemoryBarrier* pBarr = (VkMemoryBarrier*)pPacket->ppMemoryBarriers[i];\n',
+                              '    // TODO : Could fix up the pNext ptrs here if they were finalized and if we cared by switching on Barrier type and remapping\n',
+                              '}']
+        create_rp_interp = ['VkRenderPassCreateInfo* pInfo = (VkRenderPassCreateInfo*)pPacket->pCreateInfo;\n',
+                            'uint32_t i = 0;\n',
+                            'VkAttachmentDescription **ppAD = (VkAttachmentDescription **)&(pInfo->pAttachments);\n',
+                            '*ppAD = (VkAttachmentDescription*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pAttachments);\n',
+                            'VkSubpassDescription** ppSP = (VkSubpassDescription**)&(pInfo->pSubpasses);\n',
+                            '*ppSP = (VkSubpassDescription*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pSubpasses);\n',
+                            'for (i=0; i<pInfo->subpassCount; i++) {\n',
+                            '    VkAttachmentReference** pAR = (VkAttachmentReference**)&(pInfo->pSubpasses[i].pInputAttachments);\n',
+                            '    *pAR = (VkAttachmentReference*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pSubpasses[i].pInputAttachments);\n',
+                            '    pAR = (VkAttachmentReference**)&(pInfo->pSubpasses[i].pColorAttachments);\n',
+                            '    *pAR = (VkAttachmentReference*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pSubpasses[i].pColorAttachments);\n',
+                            '    pAR = (VkAttachmentReference**)&(pInfo->pSubpasses[i].pResolveAttachments);\n',
+                            '    *pAR = (VkAttachmentReference*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pSubpasses[i].pResolveAttachments);\n',
+                            '    pAR = (VkAttachmentReference**)&(pInfo->pSubpasses[i].pDepthStencilAttachment);\n',
+                            '    *pAR = (VkAttachmentReference*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pSubpasses[i].pDepthStencilAttachment);\n',
+                            '    pAR = (VkAttachmentReference**)&(pInfo->pSubpasses[i].pPreserveAttachments);\n',
+                            '    *pAR = (VkAttachmentReference*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pSubpasses[i].pPreserveAttachments);\n',
+                            '}\n',
+                            'VkSubpassDependency** ppSD = (VkSubpassDependency**)&(pInfo->pDependencies);\n',
+                            '*ppSD = (VkSubpassDependency*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pInfo->pDependencies);\n']
+        create_gfx_pipe = ['uint32_t i;\n',
+                           'uint32_t j;\n',
+                           'for (i=0; i<pPacket->createInfoCount; i++) {\n',
+                            'if (pPacket->pCreateInfos[i].sType == VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO) {\n',
+                            '// need to make a non-const pointer to the pointer so that we can properly change the original pointer to the interpretted one\n',
+                            'VkGraphicsPipelineCreateInfo* pNonConst = (VkGraphicsPipelineCreateInfo*)&(pPacket->pCreateInfos[i]);\n',
+                            '// shader stages array\n',
+                            'pNonConst->pStages = (VkPipelineShaderStageCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pStages);\n',
+                            'for (j = 0; j < pPacket->pCreateInfos[i].stageCount; j++)\n',
+                            '{\n',
+                            '    interpret_VkPipelineShaderStageCreateInfo(pHeader, (VkPipelineShaderStageCreateInfo*)&pPacket->pCreateInfos[i].pStages[j]);\n',
+                            '}\n',
+                            '// Vertex Input State\n',
+                            'pNonConst->pVertexInputState = (VkPipelineVertexInputStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pVertexInputState);\n',
+                            'VkPipelineVertexInputStateCreateInfo* pNonConstVIState = (VkPipelineVertexInputStateCreateInfo*)pNonConst->pVertexInputState;\n',
+                            'if (pNonConstVIState) {\n',
+                            '    pNonConstVIState->pVertexBindingDescriptions = (const VkVertexInputBindingDescription*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pVertexInputState->pVertexBindingDescriptions);\n',
+                            '    pNonConstVIState->pVertexAttributeDescriptions = (const VkVertexInputAttributeDescription*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pVertexInputState->pVertexAttributeDescriptions);\n',
+                            '}\n',
+                            '// Input Assembly State\n',
+                            'pNonConst->pInputAssemblyState = (const VkPipelineInputAssemblyStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pInputAssemblyState);\n',
+                            '// Tesselation State\n',
+                            'pNonConst->pTessellationState = (const VkPipelineTessellationStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pTessellationState);\n',
+                            '// Viewport State\n',
+                            'pNonConst->pViewportState = (const VkPipelineViewportStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pViewportState);\n',
+                            '// Raster State\n',
+                            'pNonConst->pRasterizationState = (const VkPipelineRasterizationStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pRasterizationState);\n',
+                            '// MultiSample State\n',
+                            'pNonConst->pMultisampleState = (const VkPipelineMultisampleStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pMultisampleState);\n',
+                            '// DepthStencil State\n',
+                            'pNonConst->pDepthStencilState = (const VkPipelineDepthStencilStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pDepthStencilState);\n',
+                            '// DynamicState State\n',
+                            'pNonConst->pDynamicState = (const VkPipelineDynamicStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pDynamicState);\n',
+                            'VkPipelineDynamicStateCreateInfo* pNonConstDyState = (VkPipelineDynamicStateCreateInfo*)pNonConst->pDynamicState;\n',
+                            'if (pNonConstDyState) {\n',
+                            '    pNonConstDyState->pDynamicStates = (const VkDynamicState*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pDynamicState->pDynamicStates);\n',
+                            '}\n',
+
+                            '// ColorBuffer State\n',
+                            'pNonConst->pColorBlendState = (const VkPipelineColorBlendStateCreateInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pColorBlendState);\n',
+                            'VkPipelineColorBlendStateCreateInfo* pNonConstCbState = (VkPipelineColorBlendStateCreateInfo*)pNonConst->pColorBlendState;\n',
+                            'if (pNonConstCbState)\n',
+                            '    pNonConstCbState->pAttachments = (const VkPipelineColorBlendAttachmentState*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfos[i].pColorBlendState->pAttachments);\n',
+                            '} else {\n',
+                            '    // This is unexpected.\n',
+                            '    vktrace_LogError("CreateGraphicsPipelines must have CreateInfo stype of VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO.");\n',
+                            '    pPacket->header = NULL;\n',
+                            '}\n',
+                            '}\n']
+        # TODO : This code is now too large and complex, need to make codegen smarter for pointers embedded in struct params to handle those cases automatically
+                              # TODO138 : Just ripped out a bunch of custom code here that was out of date. Need to scrub these function and verify they're correct
+        custom_case_dict = { #'CreateFramebuffer' : {'param': 'pCreateInfo', 'txt': ['VkFramebufferCreateInfo* pInfo = (VkFramebufferCreateInfo*)pPacket->pCreateInfo;\n',
+                              #                      'pInfo->pColorAttachments = (VkColorAttachmentBindInfo*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pColorAttachments);\n',
+                               #                     'pInfo->pDepthStencilAttachment = (VkDepthStencilBindInfo*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pDepthStencilAttachment);\n']},
+                             'CreateRenderPass' : {'param': 'pCreateInfo', 'txt': create_rp_interp},
+                             'CreatePipelineCache' : {'param': 'pCreateInfo', 'txt': [
+                                                       '((VkPipelineCacheCreateInfo *)pPacket->pCreateInfo)->pInitialData = (const void*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pInitialData);\n']},
+                             'CreatePipelineLayout' : {'param': 'pCreateInfo', 'txt': ['VkPipelineLayoutCreateInfo* pInfo = (VkPipelineLayoutCreateInfo*)pPacket->pCreateInfo;\n',
+                                                       'pInfo->pSetLayouts = (VkDescriptorSetLayout*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pSetLayouts);\n',
+                                                       'pInfo->pPushConstantRanges = (VkPushConstantRange*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pPushConstantRanges);\n']},
+                             'CreateDescriptorPool' : {'param': 'pCreateInfo', 'txt': ['VkDescriptorPoolCreateInfo* pInfo = (VkDescriptorPoolCreateInfo*)pPacket->pCreateInfo;\n',
+                                                       'pInfo->pPoolSizes = (VkDescriptorPoolSize*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pPoolSizes);\n']},
+                             'CmdWaitEvents' : {'param': 'ppMemoryBarriers', 'txt': mem_barrier_interp},
+                             'CmdPipelineBarrier' : {'param': 'ppMemoryBarriers', 'txt': mem_barrier_interp},
+                             'CreateDescriptorSetLayout' : {'param': 'pCreateInfo', 'txt': ['if (pPacket->pCreateInfo->sType == VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO) {\n',
+                                                                                         '    VkDescriptorSetLayoutCreateInfo* pNext = (VkDescriptorSetLayoutCreateInfo*)pPacket->pCreateInfo;\n',
+                                                                                         '    do\n','    {\n',
+                                                                                         '        // need to make a non-const pointer to the pointer so that we can properly change the original pointer to the interpretted one\n',
+                                                                                         '        void** ppNextVoidPtr = (void**)&(pNext->pNext);\n',
+                                                                                         '        *ppNextVoidPtr = (void*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pNext->pNext);\n',
+                                                                                         '        switch(pNext->sType)\n', '        {\n',
+                                                                                         '            case VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO:\n',
+                                                                                         '            {\n' ,
+                                                                                         '                unsigned int i = 0;\n',
+                                                                                         '                pNext->pBindings = (VkDescriptorSetLayoutBinding*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pNext->pBindings);\n',
+                                                                                         '                for (i = 0; i < pNext->bindingCount; i++)\n','                {\n',
+                                                                                         '                    VkSampler** ppSamplers = (VkSampler**)&(pNext->pBindings[i].pImmutableSamplers);\n',
+                                                                                         '                    *ppSamplers = (VkSampler*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pNext->pBindings[i].pImmutableSamplers);\n',
+                                                                                         '                }\n',
+                                                                                         '                break;\n',
+                                                                                         '            }\n',
+                                                                                         '            default:\n',
+                                                                                         '            {\n',
+                                                                                         '                vktrace_LogError("Encountered an unexpected type in descriptor set layout create list.");\n',
+                                                                                         '                pPacket->header = NULL;\n',
+                                                                                         '                pNext->pNext = NULL;\n',
+                                                                                         '            }\n',
+                                                                                         '        }\n',
+                                                                                         '        pNext = (VkDescriptorSetLayoutCreateInfo*)pNext->pNext;\n',
+                                                                                         '     }  while (NULL != pNext);\n',
+                                                                                         '} else {\n',
+                                                                                         '     // This is unexpected.\n',
+                                                                                         '     vktrace_LogError("CreateDescriptorSetLayout must have pCreateInfo->stype of VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO.");\n',
+                                                                                         '     pPacket->header = NULL;\n',
+                                                                                         '}']},
+                             'BeginCommandBuffer' : {'param': 'pBeginInfo', 'txt': [
+                                                                                          'VkCommandBufferBeginInfo* pInfo = (VkCommandBufferBeginInfo*) pPacket->pBeginInfo;\n',
+                                                       'pInfo->pInheritanceInfo = (VkCommandBufferInheritanceInfo*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pBeginInfo->pInheritanceInfo);\n']},
+                             'AllocateMemory' : {'param': 'pAllocateInfo', 'txt': ['if (pPacket->pAllocateInfo->sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO) {\n',
+                                                                                         '    VkMemoryAllocateInfo** ppNext = (VkMemoryAllocateInfo**) &(pPacket->pAllocateInfo->pNext);\n',
+                                                                                         '    *ppNext = (VkMemoryAllocateInfo*) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pAllocateInfo->pNext);\n',
+                                                                                         '} else {\n',
+                                                                                         '    // This is unexpected.\n',
+                                                                                         '    vktrace_LogError("AllocateMemory must have AllocInfo stype of VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO.");\n',
+                                                                                         '    pPacket->header = NULL;\n',
+                                                                                         '}']},
+                             'AllocateDescriptorSets' : {'param': 'pAllocateInfo', 'txt':
+                                                                               ['VkDescriptorSetLayout **ppDescSetLayout = (VkDescriptorSetLayout **) &pPacket->pAllocateInfo->pSetLayouts;\n'
+                                                                                '        *ppDescSetLayout = (VkDescriptorSetLayout *) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pAllocateInfo->pSetLayouts));']},
+                             'UpdateDescriptorSets' : {'param': 'pDescriptorWrites', 'txt':
+                                                                               [ 'uint32_t i;\n',
+                                                                                 'for (i = 0; i < pPacket->descriptorWriteCount; i++) {\n',
+                                                                                 '    switch (pPacket->pDescriptorWrites[i].descriptorType) {',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_SAMPLER:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:',
+                                                                                 '        {',
+                                                                                 '            VkDescriptorImageInfo** ppImageInfo = (VkDescriptorImageInfo**)&pPacket->pDescriptorWrites[i].pImageInfo;\n',
+                                                                                 '            *ppImageInfo = (VkDescriptorImageInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pDescriptorWrites[i].pImageInfo);\n',
+                                                                                 '        }',
+                                                                                 '        break;',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:',
+                                                                                 '        {',
+                                                                                 '            VkBufferView** ppTexelBufferView = (VkBufferView**)&pPacket->pDescriptorWrites[i].pTexelBufferView;\n',
+                                                                                 '            *ppTexelBufferView = (VkBufferView*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pDescriptorWrites[i].pTexelBufferView);\n',
+                                                                                 '        }',
+                                                                                 '        break;',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:',
+                                                                                 '    case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:',
+                                                                                 '        {',
+                                                                                 '            VkDescriptorBufferInfo** ppBufferInfo = (VkDescriptorBufferInfo**)&pPacket->pDescriptorWrites[i].pBufferInfo;\n',
+                                                                                 '            *ppBufferInfo = (VkDescriptorBufferInfo*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pDescriptorWrites[i].pBufferInfo);\n',
+                                                                                 '        }',
+                                                                                 '        break;',
+                                                                                 '    default:',
+                                                                                 '        break;',
+                                                                                 '    }',
+                                                                                 '}'
+                                                                               ]},
+                             'QueueSubmit' : {'param': 'pSubmits', 'txt':
+                                                                               [ 'uint32_t i;\n',
+                                                                                 'for (i = 0; i < pPacket->submitCount; i++) {\n',
+                                                                                 '   VkCommandBuffer** ppCBs = (VkCommandBuffer**)&pPacket->pSubmits[i].pCommandBuffers;\n',
+                                                                                 '   *ppCBs = (VkCommandBuffer*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pSubmits[i].pCommandBuffers);\n',
+                                                                                 '   VkSemaphore** ppSems = (VkSemaphore**)&pPacket->pSubmits[i].pWaitSemaphores;\n',
+                                                                                 '   *ppSems = (VkSemaphore*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pSubmits[i].pWaitSemaphores);\n',
+                                                                                 '   ppSems = (VkSemaphore**)&pPacket->pSubmits[i].pSignalSemaphores;\n',
+                                                                                 '   *ppSems = (VkSemaphore*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pSubmits[i].pSignalSemaphores);\n',
+                                                                                 '   VkPipelineStageFlags** ppStageMask = (VkPipelineStageFlags**)&pPacket->pSubmits[i].pWaitDstStageMask;\n',
+                                                                                 '   *ppStageMask = (VkPipelineStageFlags*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pSubmits[i].pWaitDstStageMask);\n',
+                                                                                 '}'
+                                                                               ]},
+                             'CreateGraphicsPipelines' : {'param': 'pCreateInfos', 'txt': create_gfx_pipe},
+                             'CreateComputePipeline' : {'param': 'pCreateInfo', 'txt': ['interpret_VkPipelineShaderStageCreateInfo(pHeader, (VkPipelineShaderStageCreateInfo*)(&pPacket->pCreateInfo->cs));']},
+                             'CreateFramebuffer' : {'param': 'pCreateInfo', 'txt': ['VkImageView** ppAV = (VkImageView**)&(pPacket->pCreateInfo->pAttachments);\n',
+                                                                                    '*ppAV = (VkImageView*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pCreateInfo->pAttachments));']},
+                             'CmdBeginRenderPass' : {'param': 'pRenderPassBegin', 'txt': ['VkClearValue** ppCV = (VkClearValue**)&(pPacket->pRenderPassBegin->pClearValues);\n',
+                                                                                          '*ppCV = (VkClearValue*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pRenderPassBegin->pClearValues));']},
+                             'CreateShaderModule' : {'param': 'pCreateInfo', 'txt': ['void** ppCode = (void**)&(pPacket->pCreateInfo->pCode);\n',
+                                                                                     '*ppCode = (void*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pCode);']},
+                             'CreateImage' : {'param': 'pCreateInfo', 'txt': ['uint32_t** ppQueueFamilyIndices = (uint32_t**)&(pPacket->pCreateInfo->pQueueFamilyIndices);\n',
+                                                                              '*ppQueueFamilyIndices = (uint32_t*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pQueueFamilyIndices);']},
+                             'CreateBuffer' : {'param': 'pCreateInfo', 'txt': ['uint32_t** ppQueueFamilyIndices = (uint32_t**)&(pPacket->pCreateInfo->pQueueFamilyIndices);\n',
+                                                                              '*ppQueueFamilyIndices = (uint32_t*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->pCreateInfo->pQueueFamilyIndices);']},
+                             'FlushMappedMemoryRanges' : {'param': 'ppData', 'txt': ['uint32_t i = 0;\n',
+                                                                                     'for (i = 0; i < pPacket->memoryRangeCount; i++)\n',
+                                                                                     '{\n',
+                                                                                     '    pPacket->ppData[i] = (void*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->ppData[i]);\n',
+                                                                                     '}']},
+                             'InvalidateMappedMemoryRanges' : {'param': 'ppData', 'txt': ['uint32_t i = 0;\n',
+                                                                                     'for (i = 0; i < pPacket->memoryRangeCount; i++)\n',
+                                                                                     '{\n',
+                                                                                     '    pPacket->ppData[i] = (void*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->ppData[i]);\n',
+                                                                                     '}']},
+                             'QueuePresentKHR' : {'param': 'pPresentInfo', 'txt': ['VkSwapchainKHR **ppSC = (VkSwapchainKHR **)& pPacket->pPresentInfo->pSwapchains;\n',
+                                                                                   '*ppSC = (VkSwapchainKHR*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pPresentInfo->pSwapchains));\n',
+                                                                                   'VkSemaphore **ppS = (VkSemaphore **) &pPacket->pPresentInfo->pWaitSemaphores;\n',
+                                                                                   '*ppS = (VkSemaphore *) vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pPresentInfo->pWaitSemaphores));\n',
+                                                                                   'uint32_t **ppII = (uint32_t **) &pPacket->pPresentInfo->pImageIndices;\n',
+                                                                                   '*ppII = (uint32_t*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pPresentInfo->pImageIndices));\n',
+                                                                                   'if (pPacket->pPresentInfo->pResults != NULL) {\n',
+                                                                                   '    VkResult **ppR = (VkResult **) &pPacket->pPresentInfo->pResults;\n',
+                                                                                   '    *ppR = (VkResult*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pPresentInfo->pResults));\n',
+                                                                                   '}']},
+                             'CreateSwapchainKHR' : {'param': 'pCreateInfo', 'txt': ['uint32_t **ppQFI = (uint32_t**)&pPacket->pCreateInfo->pQueueFamilyIndices;\n',
+                                                     '(*ppQFI) = (uint32_t*)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)(pPacket->pCreateInfo->pQueueFamilyIndices));']},
+
+        }
+        if_body = []
+        if_body.append('typedef struct packet_vkApiVersion {')
+        if_body.append('    vktrace_trace_packet_header* header;')
+        if_body.append('    uint32_t version;')
+        if_body.append('} packet_vkApiVersion;\n')
+        if_body.append('static packet_vkApiVersion* interpret_body_as_vkApiVersion(vktrace_trace_packet_header* pHeader)')
+        if_body.append('{')
+        if_body.append('    packet_vkApiVersion* pPacket = (packet_vkApiVersion*)pHeader->pBody;')
+        if_body.append('    pPacket->header = pHeader;')
+        if_body.append('    return pPacket;')
+        if_body.append('}\n')
+        for proto in self.protos:
+            if proto.name not in proto_exclusions:
+                if 'UnmapMemory' == proto.name:
+                    proto.params.append(vulkan.Param("void*", "pData"))
+                elif 'FlushMappedMemoryRanges' == proto.name:
+                    proto.params.append(vulkan.Param("void**", "ppData"))
+                elif 'InvalidateMappedMemoryRanges' == proto.name:
+                    proto.params.append(vulkan.Param("void**", "ppData"))
+                if_body.append('%s' % self.lineinfo.get())
+                if_body.append('typedef struct packet_vk%s {' % proto.name)
+                if_body.append('    vktrace_trace_packet_header* header;')
+                for p in proto.params:
+                    if_body.append('    %s;' % p.c())
+                if 'void' != proto.ret:
+                    if_body.append('    %s result;' % proto.ret)
+                if_body.append('} packet_vk%s;\n' % proto.name)
+                if_body.append('static packet_vk%s* interpret_body_as_vk%s(vktrace_trace_packet_header* pHeader)' % (proto.name, proto.name))
+                if_body.append('{')
+                if_body.append('    packet_vk%s* pPacket = (packet_vk%s*)pHeader->pBody;' % (proto.name, proto.name))
+                if_body.append('    pPacket->header = pHeader;')
+                for p in proto.params:
+                    if '*' in p.ty:
+                        if 'DeviceCreateInfo' in p.ty:
+                            if_body.append('    pPacket->%s = interpret_VkDeviceCreateInfo(pHeader, (intptr_t)pPacket->%s);' % (p.name, p.name))
+                        elif 'InstanceCreateInfo' in p.ty:
+                            if_body.append('    pPacket->%s = interpret_VkInstanceCreateInfo(pHeader, (intptr_t)pPacket->%s);' % (p.name, p.name))
+                        else:
+                            if_body.append('    pPacket->%s = (%s)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->%s);' % (p.name, p.ty, p.name))
+                        # TODO : Generalize this custom code to kill dict data struct above.
+                        #  Really the point of this block is to catch params w/ embedded ptrs to structs and chains of structs
+                        if proto.name in custom_case_dict and p.name == custom_case_dict[proto.name]['param']:
+                            if_body.append('    if (pPacket->%s != NULL)' % custom_case_dict[proto.name]['param'])
+                            if_body.append('    {')
+                            if_body.append('        %s' % "        ".join(custom_case_dict[proto.name]['txt']))
+                            if_body.append('    }')
+                if_body.append('    return pPacket;')
+                if_body.append('}\n')
+        return "\n".join(if_body)
+
+    def _generate_interp_funcs_ext(self, extensionName):
+        if_body = []
+        custom_case_dict = { }
+        for ext in vulkan.extensions_all:
+            if ext.name.lower() == extensionName.lower():
+                if ext.ifdef:
+                    if_body.append('#ifdef %s' % ext.ifdef)
+                for proto in ext.protos:
+                    if_body.append('typedef struct packet_vk%s {' % proto.name)
+                    if_body.append('    vktrace_trace_packet_header* pHeader;')
+                    for p in proto.params:
+                        if_body.append('    %s;' % p.c())
+                    if 'void' != proto.ret:
+                        if_body.append('    %s result;' % proto.ret)
+                    if_body.append('} packet_vk%s;\n' % proto.name)
+                    if_body.append('static packet_vk%s* interpret_body_as_vk%s(vktrace_trace_packet_header* pHeader)' % (proto.name, proto.name))
+                    if_body.append('{')
+                    if_body.append('    packet_vk%s* pPacket = (packet_vk%s*)pHeader->pBody;' % (proto.name, proto.name))
+                    if_body.append('    pPacket->pHeader = pHeader;')
+                    for p in proto.params:
+                        if '*' in p.ty:
+                            if_body.append('    pPacket->%s = (%s)vktrace_trace_packet_interpret_buffer_pointer(pHeader, (intptr_t)pPacket->%s);' % (p.name, p.ty, p.name))
+                            # TODO : Generalize this custom code to kill dict data struct above.
+                            #  Really the point of this block is to catch params w/ embedded ptrs to structs and chains of structs
+                            if proto.name in custom_case_dict and p.name == custom_case_dict[proto.name]['param']:
+                                if_body.append('    if (pPacket->%s != NULL)' % custom_case_dict[proto.name]['param'])
+                                if_body.append('    {')
+                                if_body.append('        %s' % "        ".join(custom_case_dict[proto.name]['txt']))
+                                if_body.append('    }')
+                    if_body.append('    return pPacket;')
+                    if_body.append('}\n')
+                if ext.ifdef:
+                    if_body.append('#endif /* %s */' % ext.ifdef)
+        return "\n".join(if_body)
+
+    def _generate_replay_func_ptrs(self):
+        xf_body = []
+        xf_body.append('struct vkFuncs {')
+        xf_body.append('    void init_funcs(void * libHandle);')
+        xf_body.append('    void *m_libHandle;\n')
+        for ext in vulkan.extensions_all:
+            if ext.ifdef:
+                xf_body.append('#ifdef %s' % ext.ifdef)
+            for proto in ext.protos:
+                if proto.name in proto_exclusions:
+                    continue
+
+                xf_body.append('    typedef %s( VKAPI_PTR * type_vk%s)(' % (proto.ret, proto.name))
+                for p in proto.params:
+                    xf_body.append('        %s,' % p.c())
+                xf_body[-1] = xf_body[-1].replace(',', ');')
+                xf_body.append('    type_vk%s real_vk%s;' % (proto.name, proto.name))
+            if ext.ifdef:
+                xf_body.append('#endif /* %s */' % ext.ifdef)
+        xf_body.append('};')
+        return "\n".join(xf_body)
+
+    def _map_decl(self, type1, type2, name):
+        return '    std::map<%s, %s> %s;' % (type1, type2, name)
+
+    def _add_to_map_decl(self, type1, type2, name):
+        txt = '    void add_to_%s_map(%s pTraceVal, %s pReplayVal)\n    {\n' % (name[2:], type1, type2)
+        #TODO138 : These checks need to vary between disp & non-disp objects
+        #txt += '        assert(pTraceVal != 0);\n'
+        #txt += '        assert(pReplayVal != 0);\n'
+        txt += '        %s[pTraceVal] = pReplayVal;\n    }\n' % name
+        return txt
+
+    def _rm_from_map_decl(self, ty, name):
+        txt = '    void rm_from_%s_map(const %s& key)\n    {\n' % (name[2:], ty)
+        txt += '        %s.erase(key);\n    }\n' % name
+        return txt
+
+    def _remap_decl(self, ty, name):
+        txt = '    %s remap_%s(const %s& value)\n    {\n' % (ty, name[2:], ty)
+        txt += '        if (value == 0) { return 0; }\n'
+        txt += '        std::map<%s, %s>::const_iterator q = %s.find(value);\n' % (ty, ty, name)
+        txt += '        if (q == %s.end()) { vktrace_LogError("Failed to remap %s."); return VK_NULL_HANDLE; }\n' % (name, ty)
+        txt += '        return q->second;\n    }\n'
+        return txt
+
+    def _generate_replay_objMemory_funcs(self):
+        rof_body = []
+        # Custom code for memory mapping functions for app writes into mapped memory
+        rof_body.append('// memory mapping functions for app writes into mapped memory')
+        rof_body.append('    bool isPendingAlloc()')
+        rof_body.append('    {')
+        rof_body.append('        return m_pendingAlloc;')
+        rof_body.append('    }')
+        rof_body.append('')
+        rof_body.append('    void setAllocInfo(const VkMemoryAllocateInfo *info, const bool pending)')
+        rof_body.append('    {')
+        rof_body.append('        m_pendingAlloc = pending;')
+        rof_body.append('        m_allocInfo = *info;')
+        rof_body.append('    }')
+        rof_body.append('')
+        rof_body.append('    void setMemoryDataAddr(void *pBuf)')
+        rof_body.append('    {')
+        rof_body.append('        if (m_mapRange.empty())')
+        rof_body.append('        {')
+        rof_body.append('            return;')
+        rof_body.append('        }')
+        rof_body.append('        MapRange mr = m_mapRange.back();')
+        rof_body.append('        if (mr.pData != NULL)')
+        rof_body.append('            vktrace_LogWarning("gpuMemory::setMemoryDataAddr() data already mapped overwrite old mapping.");')
+        rof_body.append('        else if (pBuf == NULL)')
+        rof_body.append('            vktrace_LogWarning("gpuMemory::setMemoryDataAddr() adding NULL pointer.");')
+        rof_body.append('        mr.pData = (uint8_t *) pBuf;')
+        rof_body.append('    }')
+        rof_body.append('')
+        # add for page guard optimization
+        rof_body.append('    void copyMappingDataPageGuard(const void* pSrcData)')
+        rof_body.append('    {')
+        rof_body.append('        if (m_mapRange.empty())')
+        rof_body.append('        {')
+        rof_body.append('            return;')
+        rof_body.append('        }')
+        rof_body.append('        MapRange mr = m_mapRange.back();')
+        rof_body.append('        if (!pSrcData || !mr.pData)')
+        rof_body.append('        {')
+        rof_body.append('            if (!pSrcData)')
+        rof_body.append('                vktrace_LogError("gpuMemory::copyMappingData() null src pointer.");')
+        rof_body.append('            else')
+        rof_body.append('                vktrace_LogError("gpuMemory::copyMappingData() null dest pointer totalSize=%u.", m_allocInfo.allocationSize);')
+        rof_body.append('            m_mapRange.pop_back();')
+        rof_body.append('            return;')
+        rof_body.append('        }')
+        rof_body.append('')
+        rof_body.append('        PageGuardChangedBlockInfo *pChangedInfoArray = (PageGuardChangedBlockInfo *)pSrcData;')
+        rof_body.append('        if (pChangedInfoArray[0].length)')
+        rof_body.append('        {')
+        rof_body.append('            PBYTE pChangedData = (PBYTE)(pSrcData)+sizeof(PageGuardChangedBlockInfo)*(pChangedInfoArray[0].offset + 1);')
+        rof_body.append('            DWORD CurrentOffset = 0;')
+        rof_body.append('            for (DWORD i = 0; i < pChangedInfoArray[0].offset; i++)')
+        rof_body.append('            {')
+        rof_body.append('                if ((size_t)pChangedInfoArray[i + 1].length)')
+        rof_body.append('                {')
+        rof_body.append('                    memcpy(mr.pData +  (size_t)pChangedInfoArray[i + 1].offset, pChangedData + CurrentOffset, (size_t)pChangedInfoArray[i + 1].length);')
+        rof_body.append('                }')
+        rof_body.append('                CurrentOffset += pChangedInfoArray[i + 1].length;')
+        rof_body.append('            }')
+        rof_body.append('        }')
+        rof_body.append('    }')
+        rof_body.append('')
+        #  add for page guard optimization end
+        rof_body.append('    void setMemoryMapRange(void *pBuf, const size_t size, const size_t offset, const bool pending)')
+        rof_body.append('    {')
+        rof_body.append('        MapRange mr;')
+        rof_body.append('        mr.pData = (uint8_t *) pBuf;')
+        rof_body.append('        if (size == 0)')
+        rof_body.append('            mr.size = (size_t)m_allocInfo.allocationSize - offset;')
+        rof_body.append('        else')
+        rof_body.append('            mr.size = size;')
+        rof_body.append('        mr.offset = offset;')
+        rof_body.append('        mr.pending = pending;')
+        rof_body.append('        m_mapRange.push_back(mr);')
+        rof_body.append('        assert((size_t)m_allocInfo.allocationSize >= (size + offset));')
+        rof_body.append('    }')
+        rof_body.append('')
+        rof_body.append('    void copyMappingData(const void* pSrcData, bool entire_map, size_t size, size_t offset)')
+        rof_body.append('    {')
+        rof_body.append('        if (m_mapRange.empty())')
+        rof_body.append('        {')
+        rof_body.append('            return;')
+        rof_body.append('        }')
+        rof_body.append('        MapRange mr = m_mapRange.back();')
+        rof_body.append('        if (!pSrcData || !mr.pData)')
+        rof_body.append('        {')
+        rof_body.append('            if (!pSrcData)')
+        rof_body.append('                vktrace_LogError("gpuMemory::copyMappingData() null src pointer.");')
+        rof_body.append('            else')
+        rof_body.append('                vktrace_LogError("gpuMemory::copyMappingData() null dest pointer totalSize=%u.", m_allocInfo.allocationSize);')
+        rof_body.append('            m_mapRange.pop_back();')
+        rof_body.append('            return;')
+        rof_body.append('        }')
+        rof_body.append('        if (entire_map)')
+        rof_body.append('        {')
+        rof_body.append('            size = mr.size;')
+        rof_body.append('            offset = 0;   // pointer to mapped buffer is from offset 0')
+        rof_body.append('        }')
+        rof_body.append('        else')
+        rof_body.append('        {')
+        rof_body.append('            assert(offset >= mr.offset);')
+        rof_body.append('            assert(size <= mr.size && (size + offset) <= (size_t)m_allocInfo.allocationSize);')
+        rof_body.append('        }')
+        rof_body.append('        memcpy(mr.pData + offset, pSrcData, size);')
+        rof_body.append('        if (!mr.pending && entire_map)')
+        rof_body.append('            m_mapRange.pop_back();')
+        rof_body.append('    }')
+        rof_body.append('')
+        rof_body.append('    size_t getMemoryMapSize()')
+        rof_body.append('    {')
+        rof_body.append('        return (!m_mapRange.empty()) ? m_mapRange.back().size : 0;')
+        rof_body.append('    }\n')
+        return "\n".join(rof_body)
+
+    def _generate_replay_objmapper_class(self):
+        # Create dict mapping member var names to VK type (i.e. 'm_imageViews' : 'VkImage_VIEW')
+        obj_map_dict = {}
+        for obj in vulkan.object_type_list:
+            if (obj.startswith('Vk')):
+                mem_var = obj.replace('Vk', '').lower()
+            mem_var_list = mem_var.split('_')
+            mem_var = 'm_%s%ss' % (mem_var_list[0], "".join([m.title() for m in mem_var_list[1:]]))
+            obj_map_dict[mem_var] = obj
+        rc_body = []
+        rc_body.append('#define VKTRACE_VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN (VkObjectType)-1')
+        rc_body.append('')
+        rc_body.append('typedef struct _VKAllocInfo {')
+        rc_body.append('    VkDeviceSize size;')
+        rc_body.append('    uint8_t *pData;')
+        rc_body.append('    bool rangeUpdated;')
+        rc_body.append('} VKAllocInfo;')
+        rc_body.append('')
+        rc_body.append('class objMemory {')
+        rc_body.append('public:')
+        rc_body.append('    objMemory() : m_numAllocations(0), m_pMemReqs(NULL) {}')
+        rc_body.append('    ~objMemory() { free(m_pMemReqs);}')
+        rc_body.append('    void setCount(const uint32_t num)')
+        rc_body.append('    {')
+        rc_body.append('        m_numAllocations = num;')
+        rc_body.append('    }\n')
+        rc_body.append('    void setReqs(const VkMemoryRequirements *pReqs, const uint32_t num)')
+        rc_body.append('    {')
+        rc_body.append('        if (m_numAllocations != num && m_numAllocations != 0)')
+        rc_body.append('            vktrace_LogError("objMemory::setReqs, internal mismatch on number of allocations.");')
+        rc_body.append('        if (m_pMemReqs == NULL && pReqs != NULL)')
+        rc_body.append('        {')
+        rc_body.append('            m_pMemReqs = (VkMemoryRequirements *) vktrace_malloc(num * sizeof(VkMemoryRequirements));')
+        rc_body.append('            if (m_pMemReqs == NULL)')
+        rc_body.append('            {')
+        rc_body.append('                vktrace_LogError("objMemory::setReqs out of memory.");')
+        rc_body.append('                return;')
+        rc_body.append('            }')
+        rc_body.append('            memcpy(m_pMemReqs, pReqs, num * sizeof(VkMemoryRequirements));')
+        rc_body.append('        }')
+        rc_body.append('    }\n')
+        rc_body.append('private:')
+        rc_body.append('    uint32_t m_numAllocations;')
+        rc_body.append('    VkMemoryRequirements *m_pMemReqs;')
+        rc_body.append('};')
+        rc_body.append('')
+        rc_body.append('class gpuMemory {')
+        rc_body.append('public:')
+        rc_body.append('    gpuMemory() : m_pendingAlloc(false) {m_allocInfo.allocationSize = 0;}')
+        rc_body.append('    ~gpuMemory() {}')
+        rc_body.append(self._generate_replay_objMemory_funcs())
+        rc_body.append('private:')
+        rc_body.append('    bool m_pendingAlloc;')
+        rc_body.append('    struct MapRange {')
+        rc_body.append('        bool pending;')
+        rc_body.append('        size_t size;')
+        rc_body.append('        size_t offset;')
+        rc_body.append('        uint8_t* pData;')
+        rc_body.append('    };')
+        rc_body.append('    std::vector<MapRange> m_mapRange;')
+        rc_body.append('    VkMemoryAllocateInfo m_allocInfo;')
+        rc_body.append('};')
+        rc_body.append('')
+        rc_body.append('typedef struct _imageObj {')
+        rc_body.append('     objMemory imageMem;')
+        rc_body.append('     VkImage replayImage;')
+        rc_body.append(' } imageObj;')
+        rc_body.append('')
+        rc_body.append('typedef struct _bufferObj {')
+        rc_body.append('     objMemory bufferMem;')
+        rc_body.append('     VkBuffer replayBuffer;')
+        rc_body.append(' } bufferObj;')
+        rc_body.append('')
+        rc_body.append('typedef struct _gpuMemObj {')
+        rc_body.append('     gpuMemory *pGpuMem;')
+        rc_body.append('     VkDeviceMemory replayGpuMem;')
+        rc_body.append(' } gpuMemObj;')
+        rc_body.append('')
+        rc_body.append('')
+        rc_body.append('class vkReplayObjMapper {')
+        rc_body.append('public:')
+        rc_body.append('    vkReplayObjMapper() {}')
+        rc_body.append('    ~vkReplayObjMapper() {}')
+        rc_body.append('')
+        rc_body.append(' bool m_adjustForGPU; // true if replay adjusts behavior based on GPU')
+        # Code for memory objects for handling replay GPU != trace GPU object memory requirements
+        rc_body.append('void init_objMemCount(const uint64_t handle, const VkDebugReportObjectTypeEXT objectType, const uint32_t &num)\n {')
+        rc_body.append('    switch (objectType) {')
+        rc_body.append('        case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT:')
+        rc_body.append('        {')
+        rc_body.append('            std::map<VkBuffer, bufferObj>::iterator it = m_buffers.find((VkBuffer) handle);')
+        rc_body.append('            if (it != m_buffers.end()) {')
+        rc_body.append('                objMemory obj = it->second.bufferMem;')
+        rc_body.append('                obj.setCount(num);')
+        rc_body.append('                return;')
+        rc_body.append('            }')
+        rc_body.append('            break;')
+        rc_body.append('        }')
+        rc_body.append('        case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT:')
+        rc_body.append('        {')
+        rc_body.append('            std::map<VkImage, imageObj>::iterator it = m_images.find((VkImage) handle);')
+        rc_body.append('            if (it != m_images.end()) {')
+        rc_body.append('                objMemory obj = it->second.imageMem;')
+        rc_body.append('                obj.setCount(num);')
+        rc_body.append('                return;')
+        rc_body.append('            }')
+        rc_body.append('            break;')
+        rc_body.append('        }')
+        rc_body.append('        default:')
+        rc_body.append('            break;')
+        rc_body.append('    }')
+        rc_body.append('    return;')
+        rc_body.append('}\n')
+        rc_body.append('void init_objMemReqs(const uint64_t handle, const VkDebugReportObjectTypeEXT objectType, const VkMemoryRequirements *pMemReqs, const unsigned int num)\n    {')
+        rc_body.append('    switch (objectType) {')
+        rc_body.append('        case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT:')
+        rc_body.append('        {')
+        rc_body.append('            std::map<VkBuffer, bufferObj>::iterator it = m_buffers.find((VkBuffer) handle);')
+        rc_body.append('            if (it != m_buffers.end()) {')
+        rc_body.append('                objMemory obj = it->second.bufferMem;')
+        rc_body.append('                obj.setReqs(pMemReqs, num);')
+        rc_body.append('                return;')
+        rc_body.append('            }')
+        rc_body.append('            break;')
+        rc_body.append('        }')
+        rc_body.append('        case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT:')
+        rc_body.append('        {')
+        rc_body.append('            std::map<VkImage, imageObj>::iterator it = m_images.find((VkImage) handle);')
+        rc_body.append('            if (it != m_images.end()) {')
+        rc_body.append('                objMemory obj = it->second.imageMem;')
+        rc_body.append('                obj.setReqs(pMemReqs, num);')
+        rc_body.append('                return;')
+        rc_body.append('            }')
+        rc_body.append('            break;')
+        rc_body.append('        }')
+        rc_body.append('        default:')
+        rc_body.append('            break;')
+        rc_body.append('    }')
+        rc_body.append('    return;')
+        rc_body.append('    }')
+        rc_body.append('')
+        rc_body.append('    void clear_all_map_handles()\n    {')
+        for var in sorted(obj_map_dict):
+            rc_body.append('        %s.clear();' % var)
+        for var in additional_remap_dict:
+            rc_body.append('        m_%s.clear();' %var)
+        rc_body.append('    }\n')
+        disp_obj_types = [obj for obj in vulkan.object_dispatch_list]
+        for var in sorted(obj_map_dict):
+            if obj_map_dict[var] == 'VkImage':
+                rc_body.append(self._map_decl('VkImage', 'imageObj', var))
+                rc_body.append(self._add_to_map_decl('VkImage', 'imageObj', var))
+                rc_body.append(self._rm_from_map_decl('VkImage', var))
+                rc_body.append('    VkImage remap_images(const VkImage& value)')
+                rc_body.append('    {')
+                rc_body.append('        if (value == 0) { return 0; }')
+                rc_body.append('')
+                rc_body.append('        std::map<VkImage, imageObj>::const_iterator q = m_images.find(value);')
+                rc_body.append('        if (q == m_images.end()) { vktrace_LogError("Failed to remap VkImage."); return VK_NULL_HANDLE; }\n')
+                rc_body.append('        return q->second.replayImage;')
+                rc_body.append('    }\n')
+            elif obj_map_dict[var] == 'VkBuffer':
+                rc_body.append(self._map_decl('VkBuffer', 'bufferObj', var))
+                rc_body.append(self._add_to_map_decl('VkBuffer', 'bufferObj', var))
+                rc_body.append(self._rm_from_map_decl('VkBuffer', var))
+                rc_body.append('    VkBuffer remap_buffers(const VkBuffer& value)')
+                rc_body.append('    {')
+                rc_body.append('        if (value == 0) { return 0; }')
+                rc_body.append('')
+                rc_body.append('        std::map<VkBuffer, bufferObj>::const_iterator q = m_buffers.find(value);')
+                rc_body.append('        if (q == m_buffers.end()) { vktrace_LogError("Failed to remap VkBuffer."); return VK_NULL_HANDLE; }\n')
+                rc_body.append('        return q->second.replayBuffer;')
+                rc_body.append('    }\n')
+            elif obj_map_dict[var] == 'VkDeviceMemory':
+                rc_body.append(self._map_decl('VkDeviceMemory', 'gpuMemObj', var))
+                rc_body.append(self._add_to_map_decl('VkDeviceMemory', 'gpuMemObj', var))
+                rc_body.append(self._rm_from_map_decl('VkDeviceMemory', var))
+                rc_body.append('    VkDeviceMemory remap_devicememorys(const VkDeviceMemory& value)')
+                rc_body.append('    {')
+                rc_body.append('        if (value == 0) { return 0; }')
+                rc_body.append('')
+                rc_body.append('        std::map<VkDeviceMemory, gpuMemObj>::const_iterator q = m_devicememorys.find(value);')
+                rc_body.append('        if (q == m_devicememorys.end()) { vktrace_LogError("Failed to remap VkDeviceMemory."); return VK_NULL_HANDLE; }')
+                rc_body.append('        return q->second.replayGpuMem;')
+                rc_body.append('    }\n')
+            else:
+                rc_body.append(self._map_decl(obj_map_dict[var], obj_map_dict[var], var))
+                rc_body.append(self._add_to_map_decl(obj_map_dict[var], obj_map_dict[var], var))
+                rc_body.append(self._rm_from_map_decl(obj_map_dict[var], var))
+                rc_body.append(self._remap_decl(obj_map_dict[var], var))
+        for var in additional_remap_dict:
+            rc_body.append('        std::map<%s, %s> m_%s;' % (additional_remap_dict[var], additional_remap_dict[var], var))
+            rc_body.append('        void add_to_%s_map(%s traceVal, %s replayVal)' % (var, additional_remap_dict[var], additional_remap_dict[var]))
+            rc_body.append('        {')
+            rc_body.append('            m_%s[traceVal] = replayVal;' % var)
+            rc_body.append('        }')
+            rc_body.append('')
+            rc_body.append('        void rm_from_%s_map(const %s& key)' % (var, additional_remap_dict[var]))
+            rc_body.append('        {')
+            rc_body.append('            m_%s.erase(key);' % var)
+            rc_body.append('        }')
+            rc_body.append('')
+            rc_body.append('        %s remap_%s(const %s& value)' % (additional_remap_dict[var], var, additional_remap_dict[var]))
+            rc_body.append('        {')
+            rc_body.append('            std::map<%s, %s>::const_iterator q = m_%s.find(value);' % (additional_remap_dict[var], additional_remap_dict[var], var))
+            rc_body.append('            if (q == m_%s.end()) { vktrace_LogError("Failed to remap %s."); return UINT32_MAX; }' % (var, var))
+            rc_body.append('            return q->second;')
+            rc_body.append('        }')
+
+        # VkDynamicStateObject code
+# TODO138 : Each dynamic state object is now unique so need to make sure their re-mapping is being handled correctly
+#        state_obj_remap_types = vulkan.object_dynamic_state_list
+#        state_obj_bindings = vulkan.object_dynamic_state_bind_point_list
+#        rc_body.append('    VkDynamicStateObject remap(const VkDynamicStateObject& state, const VkStateBindPoint& bindPoint)\n    {')
+#        rc_body.append('        VkDynamicStateObject obj;')
+#        index = 0
+#        while index < len(state_obj_remap_types):
+#            obj = state_obj_remap_types[index]
+#            type = state_obj_bindings[index]
+#            rc_body.append('        if (bindPoint == %s) {' % type)
+#            rc_body.append('            if ((obj = remap(static_cast <%s> (state))) != VK_NULL_HANDLE)' % obj.type)
+#            rc_body.append('                return obj;')
+#            rc_body.append('        }')
+#            index += 1
+#        for obj in state_obj_remap_types:
+#            rc_body.append('//        if ((obj = remap(static_cast <%s> (state))) != VK_NULL_HANDLE)' % obj.type)
+#            rc_body.append('//            return obj;')
+#        rc_body.append('        vktrace_LogWarning("Failed to remap VkDynamicStateObject.");')
+#        rc_body.append('        return VK_NULL_HANDLE;\n    }')
+#        rc_body.append('    void rm_from_map(const VkDynamicStateObject& state)\n    {')
+#        for obj in state_obj_remap_types:
+#            rc_body.append('        rm_from_map(static_cast <%s> (state));' % obj.type)
+#        rc_body.append('    }')
+#        rc_body.append('')
+        rc_body.append('};')
+        return "\n".join(rc_body)
+
+    def _generate_replay_init_funcs(self):
+        rif_body = []
+        rif_body.append('void vkFuncs::init_funcs(void * handle)\n{\n    m_libHandle = handle;')
+        for ext in vulkan.extensions_all:
+            if ext.ifdef:
+                rif_body.append('#ifdef %s' % ext.ifdef)
+            for proto in ext.protos:
+                if proto.name in proto_exclusions:
+                    continue
+                if 'DebugReport' not in proto.name:
+                    rif_body.append('    real_vk%s = (type_vk%s)(vktrace_platform_get_library_entrypoint(handle, "vk%s"));' % (proto.name, proto.name, proto.name))
+                else: # These func ptrs get assigned at GetProcAddr time
+                    rif_body.append('    real_vk%s = (type_vk%s)NULL;' % (proto.name, proto.name))
+            if ext.ifdef:
+                rif_body.append('#endif /* %s */' % ext.ifdef)
+        rif_body.append('}')
+        return "\n".join(rif_body)
+
+    def _remap_packet_param(self, funcName, paramType, paramName, lastName):
+        remap_list = vulkan.object_type_list
+        param_exclude_list = ['pDescriptorSets', 'pFences']
+        cleanParamType = paramType.strip('*').replace('const ', '')
+        VulkNonDispObjects = [o for o in vulkan.object_non_dispatch_list]
+        for obj in remap_list:
+            if obj == cleanParamType and paramName not in param_exclude_list:
+                objectTypeRemapParam = ''
+                if 'VkDynamicStateObject' == cleanParamType:
+                    objectTypeRemapParam = ', pPacket->stateBindPoint'
+                elif 'object' == paramName:
+                    if 'DbgSetObjectTag' == funcName:
+                        objectTypeRemapParam = ', VKTRACE_VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN'
+                    else:
+                        objectTypeRemapParam = ', pPacket->objType'
+                elif 'srcObject' == paramName and 'Callback' in funcName:
+                    objectTypeRemapParam = ', pPacket->objType'
+                pArray = ''
+                if '*' in paramType:
+                    if 'const' not in paramType:
+                        result = '        %s remapped%s = m_objMapper.remap_%ss(*pPacket->%s%s);\n' % (cleanParamType, paramName, paramName.lower(), paramName, objectTypeRemapParam)
+                        result += '        if (pPacket->%s != VK_NULL_HANDLE && remapped%s == VK_NULL_HANDLE)\n' % (paramName, paramName)
+                        result += '        {\n'
+                        result += '            vktrace_LogError("Error detected in %s() due to invalid remapped %s.");\n' % (funcName, cleanParamType)
+                        result += '            return vktrace_replay::VKTRACE_REPLAY_ERROR;\n'
+                        result += '        }\n'
+                        return result
+                    else:
+                        if lastName == '':
+                            return '            // pPacket->%s should have been remapped with special case code' % (paramName)
+                        pArray = '[pPacket->%s]' % lastName
+                        result = '            %s *remapped%s = new %s%s;\n' % (cleanParamType, paramName, cleanParamType, pArray)
+                        result += '%s\n' % self.lineinfo.get()
+                        result += '            for (uint32_t i = 0; i < pPacket->%s; i++) {\n' % lastName
+                        result += '                remapped%s[i] = m_objMapper.remap_%ss(pPacket->%s[i]%s);\n' % (paramName, cleanParamType.lower()[2:], paramName, objectTypeRemapParam)
+                        result += '                if (pPacket->%s[i] != VK_NULL_HANDLE && remapped%s[i] == VK_NULL_HANDLE)\n' % (paramName, paramName)
+                        result += '                {\n'
+                        result += '                    vktrace_LogError("Error detected in %s() due to invalid remapped %s.");\n' % (funcName, cleanParamType)
+                        result += '                    return vktrace_replay::VKTRACE_REPLAY_ERROR;\n'
+                        result += '                }\n'
+                        result += '            }\n'
+                        return result
+
+                result = '            %s remapped%s = m_objMapper.remap_%ss(pPacket->%s%s);\n' % (paramType, paramName, cleanParamType.lower()[2:], paramName, objectTypeRemapParam)
+                result += '%s\n' % self.lineinfo.get()
+                result += '            if (pPacket->%s != VK_NULL_HANDLE && remapped%s == VK_NULL_HANDLE)\n' % (paramName, paramName)
+                result += '            {\n'
+                result += '                vktrace_LogError("Error detected in %s() due to invalid remapped %s.");\n' % (funcName, cleanParamType)
+                result += '                return vktrace_replay::VKTRACE_REPLAY_ERROR;\n'
+                result += '            }\n'
+                return result
+        return '            // No need to remap %s' % (paramName)
+
+    def _get_packet_param(self, funcName, paramType, paramName):
+        # list of types that require remapping
+        remap_list = vulkan.object_type_list
+        param_exclude_list = ['pDescriptorSets', 'pFences']
+        cleanParamType = paramType.strip('*').replace('const ', '')
+        for obj in remap_list:
+            if obj == cleanParamType and paramName not in param_exclude_list:
+                objectTypeRemapParam = ''
+                if 'object' == paramName:
+                    if 'DbgSetObjectTag' == funcName:
+                        objectTypeRemapParam = ', VKTRACE_VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN'
+                    else:
+                        objectTypeRemapParam = ', pPacket->objType'
+                return 'remapped%s' % (paramName)
+        return 'pPacket->%s' % (paramName)
+
+    def _gen_replay_create_instance(self):
+        cb_body = []
+        cb_body.append('            replayResult = manually_replay_vkCreateInstance(pPacket);')
+        cb_body.append('            CHECK_RETURN_VALUE(vkCreateInstance);')
+        cb_body.append('            if (replayResult == VK_SUCCESS) {')
+        cb_body.append('                VkInstance remappedInstance = m_objMapper.remap_instances(*pPacket->pInstance);')
+        cb_body.append('                if (remappedInstance == VK_NULL_HANDLE) {')
+        cb_body.append('                    vktrace_LogError("Error detected in vkCreateInstance() due to invalid remapped VkInstance.");')
+        cb_body.append('                    returnValue = vktrace_replay::VKTRACE_REPLAY_ERROR;')
+        cb_body.append('                    break;')
+        cb_body.append('                }')
+        cb_body.append('                VkFlags reportFlags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT;')
+        cb_body.append('                PFN_vkCreateDebugReportCallbackEXT callback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(remappedInstance, "vkCreateDebugReportCallbackEXT");')
+        cb_body.append('                if (callback != NULL) {')
+        cb_body.append('                    VkDebugReportCallbackCreateInfoEXT dbgCreateInfo;')
+        cb_body.append('                    memset(&dbgCreateInfo, 0, sizeof(dbgCreateInfo));')
+        cb_body.append('                    dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;')
+        cb_body.append('                    dbgCreateInfo.flags = reportFlags;')
+        cb_body.append('                    dbgCreateInfo.pfnCallback = g_fpDbgMsgCallback;')
+        cb_body.append('                    dbgCreateInfo.pUserData = NULL;')
+        cb_body.append('                    if (callback(remappedInstance, &dbgCreateInfo, NULL, &m_dbgMsgCallbackObj) != VK_SUCCESS) {')
+        cb_body.append('                        vktrace_LogWarning("Failed to register vulkan callback for replayer error handling.");')
+        cb_body.append('                        returnValue = vktrace_replay::VKTRACE_REPLAY_ERROR;')
+        cb_body.append('                        break;')
+        cb_body.append('                    }')
+        cb_body.append('                }')
+        cb_body.append('            }')
+        return "\n".join(cb_body)
+
+    # These are customized because they are the only entry points returning VkBool32
+    def _gen_replay_GetPhysicalDeviceXcbPresentationSupportKHR (self):
+        cb_body = []
+        cb_body.append('            VkBool32 rval = manually_replay_vkGetPhysicalDeviceXcbPresentationSupportKHR(pPacket);')
+        cb_body.append('            if (rval != pPacket->result)')
+        cb_body.append('            {')
+        cb_body.append('                vktrace_LogError("Return value %d from API call (vkGetPhysicalDeviceXcbPresentationSupportKHR) does not match return value from trace file %d.",')
+        cb_body.append('                                 rval, pPacket->result);')
+        cb_body.append('                returnValue = vktrace_replay::VKTRACE_REPLAY_BAD_RETURN;')
+        cb_body.append('            }')
+        return "\n".join(cb_body)
+
+    def _gen_replay_GetPhysicalDeviceXlibPresentationSupportKHR (self):
+        cb_body = []
+        cb_body.append('            VkBool32 rval = manually_replay_vkGetPhysicalDeviceXlibPresentationSupportKHR(pPacket);')
+        cb_body.append('            if (rval != pPacket->result)')
+        cb_body.append('            {')
+        cb_body.append('                vktrace_LogError("Return value %d from API call (vkGetPhysicalDeviceXlibPresentationSupportKHR) does not match return value from trace file %d.",')
+        cb_body.append('                                 rval, pPacket->result);')
+        cb_body.append('                returnValue = vktrace_replay::VKTRACE_REPLAY_BAD_RETURN;')
+        cb_body.append('            }')
+        return "\n".join(cb_body)
+
+    def _gen_replay_GetPhysicalDeviceWin32PresentationSupportKHR (self):
+        cb_body = []
+        cb_body.append('            VkBool32 rval = manually_replay_vkGetPhysicalDeviceWin32PresentationSupportKHR(pPacket);')
+        cb_body.append('            if (rval != pPacket->result)')
+        cb_body.append('            {')
+        cb_body.append('                vktrace_LogError("Return value %d from API call (vkGetPhysicalDeviceWin32PresentationSupportKHR) does not match return value from trace file %d.",')
+        cb_body.append('                                 rval, pPacket->result);')
+        cb_body.append('                returnValue = vktrace_replay::VKTRACE_REPLAY_BAD_RETURN;')
+        cb_body.append('            }')
+        return "\n".join(cb_body)
+
+    # Generate main replay case statements where actual replay API call is dispatched based on input packet data
+    def _generate_replay(self):
+        manually_replay_funcs = ['AllocateMemory',
+                                 'BeginCommandBuffer',
+                                 'CreateDescriptorSetLayout',
+                                 'CreateDevice',
+                                 'CreateBuffer',
+                                 'CreateImage',
+                                 'CreateCommandPool',
+                                 'CreateFramebuffer',
+                                 'GetPipelineCacheData',
+                                 'CreateGraphicsPipelines',
+                                 'CreateComputePipelines',
+                                 #'CreateInstance',
+                                 'CreatePipelineLayout',
+                                 'CreateRenderPass',
+                                 'CmdBeginRenderPass',
+                                 'CmdBindDescriptorSets',
+                                 'CmdBindVertexBuffers',
+                                 'CmdPipelineBarrier',
+                                 'QueuePresentKHR',
+                                 'CmdWaitEvents',
+                                 #'DestroyObject',
+                                 'EnumeratePhysicalDevices',
+                                 'FreeMemory',
+                                 'FreeDescriptorSets',
+                                 'FlushMappedMemoryRanges',
+                                 'InvalidateMappedMemoryRanges',
+                                 #'GetGlobalExtensionInfo',
+                                 #'GetImageSubresourceInfo',
+                                 #'GetObjectInfo',
+                                 #'GetPhysicalDeviceExtensionInfo',
+                                 'GetPhysicalDeviceMemoryProperties',
+                                 'GetPhysicalDeviceQueueFamilyProperties',
+                                 'GetPhysicalDeviceSurfaceSupportKHR',
+                                 'GetPhysicalDeviceSurfaceCapabilitiesKHR',
+                                 'GetPhysicalDeviceSurfaceFormatsKHR',
+                                 'GetPhysicalDeviceSurfacePresentModesKHR',
+                                 'CreateSwapchainKHR',
+                                 'GetSwapchainImagesKHR',
+                                 'CreateXcbSurfaceKHR',
+                                 'CreateXlibSurfaceKHR',
+                                 'GetPhysicalDeviceXcbPresentationSupportKHR',
+                                 'GetPhysicalDeviceXlibPresentationSupportKHR',
+                                 'CreateWin32SurfaceKHR',
+                                 'GetPhysicalDeviceWin32PresentationSupportKHR',
+                                 'CreateAndroidSurfaceKHR',
+                                 #TODO Wayland, Mir, Xlib
+                                 #'GetPhysicalDeviceInfo',
+                                 'MapMemory',
+                                 'QueueSubmit',
+                                 'QueueBindSparse',
+                                 #'StorePipeline',
+                                 'UnmapMemory',
+                                 'UpdateDescriptorSets',
+                                 'WaitForFences',
+                                 'CreateDebugReportCallbackEXT',
+                                 'DestroyDebugReportCallbackEXT',
+                                 'AllocateCommandBuffers',
+                                 ]
+
+        # validate the manually_replay_funcs list
+        protoFuncs = [proto.name for proto in self.protos]
+        wsi_platform_manual_funcs = ['CreateWin32SurfaceKHR', 'CreateXcbSurfaceKHR', 'CreateXlibSurfaceKHR', 'CreateAndroidSurfaceKHR']
+
+        for func in manually_replay_funcs:
+            if (func not in protoFuncs) and (func not in wsi_platform_manual_funcs):
+                sys.exit("Entry '%s' in manually_replay_funcs list is not in the vulkan function prototypes" % func)
+
+        # map protos to custom functions if body is fully custom
+        custom_body_dict = {'CreateInstance': self._gen_replay_create_instance,
+                            'GetPhysicalDeviceXcbPresentationSupportKHR': self._gen_replay_GetPhysicalDeviceXcbPresentationSupportKHR,
+                            'GetPhysicalDeviceXlibPresentationSupportKHR': self._gen_replay_GetPhysicalDeviceXlibPresentationSupportKHR,
+                            'GetPhysicalDeviceWin32PresentationSupportKHR': self._gen_replay_GetPhysicalDeviceWin32PresentationSupportKHR }
+        # multi-gpu Open funcs w/ list of local params to create
+        custom_open_params = {'OpenSharedMemory': (-1,),
+                              'OpenSharedSemaphore': (-1,),
+                              'OpenPeerMemory': (-1,),
+                              'OpenPeerImage': (-1, -2,)}
+        # Functions that create views are unique from other create functions
+        create_view_list = ['CreateBufferView', 'CreateImageView', 'CreateComputePipeline']
+        # Functions to treat as "Create' that don't have 'Create' in the name
+        special_create_list = ['LoadPipeline', 'LoadPipelineDerivative', 'AllocateMemory', 'GetDeviceQueue', 'PinSystemMemory', 'AllocateDescriptorSets', 'AcquireNextImageKHR']
+        # A couple funcs use do while loops
+        do_while_dict = {'GetFenceStatus': 'replayResult != pPacket->result  && pPacket->result == VK_SUCCESS', 'GetEventStatus': '(pPacket->result == VK_EVENT_SET || pPacket->result == VK_EVENT_RESET) && replayResult != pPacket->result', 'GetQueryPoolResults': 'pPacket->result == VK_SUCCESS && replayResult != pPacket->result'}
+        rbody = []
+        rbody.append('%s' % self.lineinfo.get())
+        rbody.append('vktrace_replay::VKTRACE_REPLAY_RESULT vkReplay::replay(vktrace_trace_packet_header *packet)')
+        rbody.append('{')
+        rbody.append('    vktrace_replay::VKTRACE_REPLAY_RESULT returnValue = vktrace_replay::VKTRACE_REPLAY_SUCCESS;')
+        rbody.append('    VkResult replayResult = VK_ERROR_VALIDATION_FAILED_EXT;')
+        rbody.append('    switch (packet->packet_id)')
+        rbody.append('    {')
+        rbody.append('        case VKTRACE_TPI_VK_vkApiVersion:')
+        rbody.append('        {')
+        rbody.append('            packet_vkApiVersion* pPacket = (packet_vkApiVersion*)(packet->pBody);')
+        rbody.append('            if (VK_VERSION_MAJOR(pPacket->version) != 1 || VK_VERSION_MINOR (pPacket->version) != 0)')
+        rbody.append('            {')
+        rbody.append('                vktrace_LogError("Trace file is from Vulkan version 0x%x (%u.%u.%u), but the vktrace plugin only supports version 0x%x (%u.%u.%u).", pPacket->version, (pPacket->version & 0xFFC00000) >> 22, (pPacket->version & 0x003FF000) >> 12, (pPacket->version & 0x00000FFF), VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), ((VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION)) & 0xFFC00000) >> 22, ((VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION)) & 0x003FF000) >> 12, ((VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION)) & 0x00000FFF));')
+        rbody.append('                returnValue = vktrace_replay::VKTRACE_REPLAY_ERROR;')
+        rbody.append('            }')
+        rbody.append('            break;')
+        rbody.append('        }')
+        for proto in self.protos:
+            if proto.name in proto_exclusions:
+                continue
+
+            # TODO : This is an O(N^4) way of finding if this proto is guarded by an ifdef.
+            # If the concept of an ifdef field is ok, rewrite the outer loop to already have the ext.ifdef value ready:
+            # for ext in vulkan.extensions_all:
+            #     if ext.ifdef: if_body.append('#ifdef') # wrap all the protos in a single #ifdef block instead of repeating #ifdef for each proto
+            #     for proto in ext.protos:
+            proto_ext_ifdef = None
+            for ext in vulkan.extensions_all:
+                if ext.ifdef:
+                    for ext_proto in ext.protos:
+                        if proto.name == ext_proto.name:
+                            proto_ext_ifdef = ext.ifdef
+            if proto_ext_ifdef:
+                rbody.append('#ifdef %s' % proto_ext_ifdef)
+
+            ret_value = False
+            create_view = False
+            create_func = False
+            # TODO : How to handle void* return of GetProcAddr?
+#TODO make sure vkDestroy object functions really do clean up the object maps
+            if ('void' not in proto.ret.lower()) and ('size_t' not in proto.ret) and (proto.name not in custom_body_dict):
+                ret_value = True
+            if proto.name in create_view_list:
+                create_view = True
+            elif 'Create' in proto.name or proto.name in special_create_list:
+                create_func = True
+            rbody.append('        case VKTRACE_TPI_VK_vk%s:' % proto.name)
+            rbody.append('        {')
+            rbody.append('            packet_vk%s* pPacket = (packet_vk%s*)(packet->pBody);' % (proto.name, proto.name))
+            if proto.name in manually_replay_funcs:
+                if ret_value == True:
+                    rbody.append('            replayResult = manually_replay_vk%s(pPacket);' % proto.name)
+                else:
+                    rbody.append('            manually_replay_vk%s(pPacket);' % proto.name)
+            elif proto.name in custom_body_dict:
+                rbody.append(custom_body_dict[proto.name]())
+            else:
+                if proto.name in custom_open_params:
+                    for pidx in custom_open_params[proto.name]:
+                        rbody.append('            %s local_%s;' % (proto.params[pidx].ty.replace('const ', '').strip('*'), proto.params[pidx].name))
+                elif create_view:
+                    rbody.append('            %s createInfo;' % (proto.params[1].ty.strip('*').replace('const ', '')))
+                    rbody.append('            memcpy(&createInfo, pPacket->pCreateInfo, sizeof(%s));' % (proto.params[1].ty.strip('*').replace('const ', '')))
+                    if 'CreateComputePipeline' == proto.name:
+                        rbody.append('            createInfo.cs.shader = m_objMapper.remap_shaders(pPacket->pCreateInfo->cs.shader);')
+                        rbody.append('            if (createInfo.cs.shader == VK_NULL_HANDLE && pPacket->pCreateInfo->cs.shader != VK_NULL_HANDLE)')
+                        rbody.append('            {')
+                        rbody.append('                vktrace_LogError("Error detected in vkCreateComputePipelines() due to invalid remapped VkShader.");')
+                        rbody.append('                return vktrace_replay::VKTRACE_REPLAY_ERROR;')
+                        rbody.append('            }')
+                    elif 'CreateBufferView' == proto.name:
+                        rbody.append('            createInfo.buffer = m_objMapper.remap_buffers(pPacket->pCreateInfo->buffer);')
+                        rbody.append('            if (createInfo.buffer == VK_NULL_HANDLE && pPacket->pCreateInfo->buffer != VK_NULL_HANDLE)')
+                        rbody.append('            {')
+                        rbody.append('                vktrace_LogError("Error detected in vkCreateBufferView() due to invalid remapped VkBuffer.");')
+                        rbody.append('                return vktrace_replay::VKTRACE_REPLAY_ERROR;')
+                        rbody.append('            }')
+                    else:
+                        rbody.append('            createInfo.image = m_objMapper.remap_images(pPacket->pCreateInfo->image);')
+                        rbody.append('            if (createInfo.image == VK_NULL_HANDLE && pPacket->pCreateInfo->image != VK_NULL_HANDLE)')
+                        rbody.append('            {')
+                        rbody.append('                vktrace_LogError("Error detected in vkCreateImageView() due to invalid remapped VkImage.");')
+                        rbody.append('                return vktrace_replay::VKTRACE_REPLAY_ERROR;')
+                        rbody.append('            }')
+                    rbody.append('            %s local_%s;' % (proto.params[-1].ty.strip('*').replace('const ', ''), proto.params[-1].name))
+                elif create_func: # Declare local var to store created handle into
+                    if 'AllocateDescriptorSets' == proto.name:
+                        p_ty = proto.params[-1].ty.strip('*').replace('const ', '')
+                        rbody.append('            %s* local_%s = (%s*)malloc(pPacket->pAllocateInfo->descriptorSetCount * sizeof(%s));' % (p_ty, proto.params[-1].name, p_ty, p_ty))
+                        rbody.append('            VkDescriptorSetLayout* local_pSetLayouts = (VkDescriptorSetLayout*)malloc(pPacket->pAllocateInfo->descriptorSetCount * sizeof(VkDescriptorSetLayout));')
+                        rbody.append('            VkDescriptorSetAllocateInfo local_AllocInfo, *local_pAllocateInfo = &local_AllocInfo;')
+                        rbody.append('            VkDescriptorPool local_descPool;')
+                        rbody.append('            local_descPool = m_objMapper.remap_descriptorpools(pPacket->pAllocateInfo->descriptorPool);')
+                        rbody.append('            if (local_descPool == VK_NULL_HANDLE)')
+                        rbody.append('            {')
+                        rbody.append('                vktrace_LogError("Error detected in vkAllocateDescriptorSets() due to invalid remapped VkDescriptorPool.");')
+                        rbody.append('                return vktrace_replay::VKTRACE_REPLAY_ERROR;')
+                        rbody.append('            }')
+                        rbody.append('            for (uint32_t i = 0; i < pPacket->pAllocateInfo->descriptorSetCount; i++)')
+                        rbody.append('            {')
+                        rbody.append('                local_pSetLayouts[i] = m_objMapper.remap_descriptorsetlayouts(pPacket->%s->pSetLayouts[i]);' % (proto.params[-2].name))
+                        rbody.append('                if (local_pSetLayouts[i] == VK_NULL_HANDLE)')
+                        rbody.append('                {')
+                        rbody.append('                    vktrace_LogError("Error detected in vkAllocateDescriptorSets() due to invalid remapped VkDescriptorSetLayout.");')
+                        rbody.append('                    return vktrace_replay::VKTRACE_REPLAY_ERROR;')
+                        rbody.append('                }')
+                        rbody.append('            }')
+                        rbody.append('            memcpy(local_pAllocateInfo, pPacket->pAllocateInfo, sizeof(VkDescriptorSetAllocateInfo));')
+                        rbody.append('            local_pAllocateInfo->pSetLayouts = local_pSetLayouts;')
+                        rbody.append('            local_pAllocateInfo->descriptorPool = local_descPool;')
+                    else:
+                        rbody.append('            %s local_%s;' % (proto.params[-1].ty.strip('*').replace('const ', ''), proto.params[-1].name))
+                elif proto.name == 'ResetFences':
+                    rbody.append('            VkFence* fences = VKTRACE_NEW_ARRAY(VkFence, pPacket->fenceCount);')
+                    rbody.append('            for (uint32_t i = 0; i < pPacket->fenceCount; i++)')
+                    rbody.append('            {')
+                    rbody.append('                fences[i] = m_objMapper.remap_fences(pPacket->%s[i]);' % (proto.params[-1].name))
+                    rbody.append('                if (fences[i] == VK_NULL_HANDLE)')
+                    rbody.append('                {')
+                    rbody.append('                    vktrace_LogError("Error detected in vkResetFences() due to invalid remapped VkFence.");')
+                    rbody.append('                    return vktrace_replay::VKTRACE_REPLAY_ERROR;')
+                    rbody.append('                }')
+                    rbody.append('            }')
+                elif proto.name in do_while_dict:
+                    rbody.append('            do {')
+                last_name = ''
+                for p in proto.params:
+                    if create_func or create_view:
+                        if p.name != proto.params[-1].name:
+                            rbody.append(self._remap_packet_param(proto.name, p.ty, p.name, last_name))
+                    else:
+                        rbody.append(self._remap_packet_param(proto.name, p.ty, p.name, last_name))
+                    last_name = p.name
+
+                if proto.name == 'DestroyInstance':
+                    rbody.append('            if (m_vkFuncs.real_vkDestroyDebugReportCallbackEXT != NULL)')
+                    rbody.append('            {')
+                    rbody.append('                m_vkFuncs.real_vkDestroyDebugReportCallbackEXT(remappedinstance, m_dbgMsgCallbackObj, pPacket->pAllocator);')
+                    rbody.append('            }')
+                # TODO: need a better way to indicate which extensions should be mapped to which Get*ProcAddr
+                elif proto.name == 'GetInstanceProcAddr':
+                    for iExt in vulkan.extensions_all:
+                        if iExt.ifdef:
+                            rbody.append('#ifdef %s' % iExt.ifdef)
+                        for iProto in iExt.protos:
+                            if iProto.name in proto_exclusions:
+                                continue
+                            if 'DebugReport' in iProto.name:
+                                rbody.append('            if (strcmp(pPacket->pName, "vk%s") == 0) {' % (iProto.name))
+                                rbody.append('               m_vkFuncs.real_vk%s = (PFN_vk%s)vk%s(remappedinstance, pPacket->pName);' % (iProto.name, iProto.name, proto.name))
+                                rbody.append('            }')
+                            elif  (iProto.params[0].ty == 'VkInstance' or iProto.params[0].ty != 'VkPhysicalDevice')  and 'KHR' in iProto.name:
+                                rbody.append('            if (strcmp(pPacket->pName, "vk%s") == 0) {' % (iProto.name))
+                                rbody.append('               m_vkFuncs.real_vk%s = (PFN_vk%s)vk%s(remappedinstance, pPacket->pName);' % (iProto.name, iProto.name, proto.name))
+                                rbody.append('            }')
+                        if iExt.ifdef:
+                            rbody.append('#endif /* %s */' % iExt.ifdef)
+                elif proto.name == 'GetDeviceProcAddr':
+                    for dProto in self.protos:
+                       if dProto.name in proto_exclusions:
+                            continue
+                       if 'KHR' in dProto.name and dProto.params[0].ty != 'VkInstance' and dProto.params[0].ty != 'VkPhysicalDevice':
+                       # if 'KHR' in dProto.name:
+                            rbody.append('            if (strcmp(pPacket->pName, "vk%s") == 0) {' % (dProto.name))
+                            rbody.append('               m_vkFuncs.real_vk%s = (PFN_vk%s)vk%s(remappeddevice, pPacket->pName);' % (dProto.name, dProto.name, proto.name))
+                            rbody.append('            }')
+
+                # build the call to the "real_" entrypoint
+                rr_string = '            '
+                if ret_value:
+                    if proto.ret != 'VkResult':
+                        ret_value = False
+                    else:
+                        rr_string = '            replayResult = '
+                rr_string += 'm_vkFuncs.real_vk%s(' % proto.name
+                for p in proto.params:
+                    # For last param of Create funcs, pass address of param
+                    if create_func:
+                        if proto.name == 'AllocateDescriptorSets' and ((p.name == proto.params[-2].name) or (p.name == proto.params[-1].name)):
+                            rr_string += 'local_%s, ' % p.name
+                        elif p.name == proto.params[-1].name:
+                            rr_string += '&local_%s, ' % p.name
+                        else:
+                            rr_string += '%s, ' % self._get_packet_param(proto.name, p.ty, p.name)
+                    else:
+                        rr_string += '%s, ' % self._get_packet_param(proto.name, p.ty, p.name)
+                rr_string = '%s);' % rr_string[:-2]
+                if proto.name in custom_open_params:
+                    rr_list = rr_string.split(', ')
+                    for pidx in custom_open_params[proto.name]:
+                        rr_list[pidx] = '&local_%s' % proto.params[pidx].name
+                    rr_string = ', '.join(rr_list)
+                    rr_string += ');'
+                elif create_view:
+                    rr_list = rr_string.split(', ')
+                    rr_list[-3] = '&createInfo'
+                    rr_list[-2] = 'NULL'
+                    rr_list[-1] = '&local_%s);' % proto.params[-1].name
+                    rr_string = ', '.join(rr_list)
+                    # this is a sneaky shortcut to use generic create code below to add_to_map
+                    create_func = True
+                elif proto.name == 'AllocateDescriptorSets':
+                    rr_string = rr_string.replace('pPacket->pSetLayouts', 'pLocalDescSetLayouts')
+                elif proto.name == 'ResetFences':
+                   rr_string = rr_string.replace('pPacket->pFences', 'fences')
+
+                # insert the real_*(..) call
+                rbody.append(rr_string)
+
+                # handle return values or anything that needs to happen after the real_*(..) call
+                get_ext_layers_proto = ['EnumerateInstanceExtensionProperties', 'EnumerateDeviceExtensionProperties','EnumerateInstanceLayerProperties', 'EnumerateDeviceLayerProperties']
+                if 'DestroyDevice' in proto.name:
+                    rbody.append('            if (replayResult == VK_SUCCESS)')
+                    rbody.append('            {')
+                    rbody.append('                m_pCBDump = NULL;')
+                    rbody.append('                m_pDSDump = NULL;')
+                    #TODO138 : disabling snapshot
+                    #rbody.append('                m_pVktraceSnapshotPrint = NULL;')
+                    rbody.append('                m_objMapper.rm_from_devices_map(pPacket->device);')
+                    rbody.append('                m_display->m_initedVK = false;')
+                    rbody.append('            }')
+                elif proto.name in get_ext_layers_proto:
+                    rbody.append('            if (replayResult == VK_ERROR_LAYER_NOT_PRESENT || replayResult == VK_INCOMPLETE)')
+                    rbody.append('            { // ignore errors caused by trace config != replay config')
+                    rbody.append('                replayResult = VK_SUCCESS;')
+                    rbody.append('            }')
+                elif 'DestroySwapchainKHR' in proto.name:
+                    rbody.append('            if (replayResult == VK_SUCCESS)')
+                    rbody.append('            {')
+                    rbody.append('                m_objMapper.rm_from_swapchainkhrs_map(pPacket->swapchain);')
+                    rbody.append('            }')
+                elif 'AcquireNextImageKHR' in proto.name:
+                    rbody.append('            m_objMapper.add_to_pImageIndex_map(*(pPacket->pImageIndex), local_pImageIndex);')
+                elif 'DestroyInstance' in proto.name:
+                    rbody.append('            if (replayResult == VK_SUCCESS)')
+                    rbody.append('            {')
+                    rbody.append('                // TODO need to handle multiple instances and only clearing maps within an instance.')
+                    rbody.append('                // TODO this only works with a single instance used at any given time.')
+                    rbody.append('                m_objMapper.clear_all_map_handles();')
+                    rbody.append('            }')
+                elif 'MergePipelineCaches' in proto.name:
+                    rbody.append('            delete[] remappedpSrcCaches;')
+                elif 'FreeCommandBuffers' in proto.name:
+                    rbody.append('            delete[] remappedpCommandBuffers;')
+                elif 'CmdExecuteCommands' in proto.name:
+                    rbody.append('            delete[] remappedpCommandBuffers;')
+                elif 'AllocateDescriptorSets' in proto.name:
+                    rbody.append('            if (replayResult == VK_SUCCESS)')
+                    rbody.append('            {')
+                    rbody.append('                for (uint32_t i = 0; i < pPacket->pAllocateInfo->descriptorSetCount; i++) {')
+                    rbody.append('                    m_objMapper.add_to_descriptorsets_map(pPacket->%s[i], local_%s[i]);' % (proto.params[-1].name, proto.params[-1].name))
+                    rbody.append('                }')
+                    rbody.append('            }')
+                    rbody.append('            free(local_pSetLayouts);')
+                    rbody.append('            free(local_pDescriptorSets);')
+                elif proto.name == 'ResetFences':
+                    rbody.append('            VKTRACE_DELETE(fences);')
+                elif create_func: # save handle mapping if create successful
+                    if ret_value:
+                        rbody.append('            if (replayResult == VK_SUCCESS)')
+                        rbody.append('            {')
+                    clean_type = proto.params[-1].ty.strip('*').replace('const ', '')
+                    VkNonDispObjType = [o for o in vulkan.object_non_dispatch_list]
+                    rbody.append('                m_objMapper.add_to_%ss_map(*(pPacket->%s), local_%s);' % (clean_type.lower()[2:], proto.params[-1].name, proto.params[-1].name))
+                    if 'AllocateMemory' == proto.name:
+                        rbody.append('                m_objMapper.add_entry_to_mapData(local_%s, pPacket->pAllocateInfo->allocationSize);' % (proto.params[-1].name))
+                    if ret_value:
+                        rbody.append('            }')
+                elif proto.name in do_while_dict:
+                    rbody[-1] = '    %s' % rbody[-1]
+                    rbody.append('            } while (%s);' % do_while_dict[proto.name])
+                    rbody.append('            if (pPacket->result != VK_NOT_READY || replayResult != VK_SUCCESS)')
+            if ret_value:
+                rbody.append('            CHECK_RETURN_VALUE(vk%s);' % proto.name)
+            rbody.append('            break;')
+            rbody.append('        }')
+            if proto_ext_ifdef:
+                rbody.append('#endif /* %s */' % proto_ext_ifdef)
+        rbody.append('        default:')
+        rbody.append('            vktrace_LogWarning("Unrecognized packet_id %u, skipping.", packet->packet_id);')
+        rbody.append('            returnValue = vktrace_replay::VKTRACE_REPLAY_INVALID_ID;')
+        rbody.append('            break;')
+        rbody.append('    }')
+        rbody.append('    return returnValue;')
+        rbody.append('}')
+        return "\n".join(rbody)
+
+class VktraceTraceHeader(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#include "vktrace_vk_vk_packets.h"')
+        header_txt.append('#include "vktrace_vk_packet_id.h"')
+        header_txt.append('#include "vulkan/vk_layer.h"\n\n')
+        header_txt.append('void InitTracer(void);\n\n')
+        header_txt.append('#ifdef WIN32')
+        header_txt.append('BOOL CALLBACK InitTracer(_Inout_ PINIT_ONCE initOnce, _Inout_opt_ PVOID param, _Out_opt_ PVOID *lpContext);')
+        header_txt.append('extern INIT_ONCE gInitOnce;')
+        header_txt.append('\n#elif defined(PLATFORM_LINUX)')
+        header_txt.append('void InitTracer(void);')
+        header_txt.append('extern pthread_once_t gInitOnce;')
+        header_txt.append('#endif\n')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_trace_func_protos()]
+
+        return "\n".join(body)
+
+class VktraceTraceC(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#include "vktrace_platform.h"')
+        header_txt.append('#include "vktrace_common.h"')
+        header_txt.append('#include "vktrace_lib_helpers.h"')
+        header_txt.append('#include "vktrace_vk_vk.h"')
+        #header_txt.append('#include "vktrace_vk_vk_lunarg_debug_marker.h"')
+        header_txt.append('#include "vktrace_interconnect.h"')
+        header_txt.append('#include "vktrace_filelike.h"')
+        header_txt.append('#include "vk_struct_size_helper.h"')
+        header_txt.append('#ifdef PLATFORM_LINUX')
+        header_txt.append('#include <pthread.h>')
+        header_txt.append('#endif')
+        header_txt.append('#include "vktrace_trace_packet_utils.h"')
+        header_txt.append('#include <stdio.h>\n')
+        header_txt.append('#include <string.h>\n')
+        header_txt.append('#ifdef WIN32')
+        header_txt.append('INIT_ONCE gInitOnce = INIT_ONCE_STATIC_INIT;')
+        header_txt.append('#elif defined(PLATFORM_LINUX)')
+        header_txt.append('pthread_once_t gInitOnce = PTHREAD_ONCE_INIT;')
+        header_txt.append('#endif')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_init_funcs(),
+                self._generate_trace_funcs(self.extensionName)]
+
+        return "\n".join(body)
+
+class VktracePacketID(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#pragma once\n')
+        header_txt.append('#include "vktrace_vk_vk_packets.h"')
+        header_txt.append('#include "vktrace_trace_packet_utils.h"')
+        header_txt.append('#include "vktrace_trace_packet_identifiers.h"')
+        header_txt.append('#include "vktrace_interconnect.h"')
+        header_txt.append("#include <inttypes.h>")
+        #header_txt.append('#include "vktrace_vk_vk_lunarg_debug_marker_packets.h"')
+        header_txt.append('#include "vk_enum_string_helper.h"')
+        header_txt.append('#ifndef _WIN32')
+        header_txt.append(' #pragma GCC diagnostic ignored "-Wwrite-strings"')
+        header_txt.append('#endif')
+        header_txt.append('#ifndef _WIN32')
+        header_txt.append(' #pragma GCC diagnostic warning "-Wwrite-strings"')
+        header_txt.append('#endif')
+        header_txt.append('#if defined(WIN32)')
+        header_txt.append('#define snprintf _snprintf')
+        header_txt.append('#endif')
+        header_txt.append('#if defined(WIN32)')
+        header_txt.append('#define VK_SIZE_T_SPECIFIER "%Iu"')
+        header_txt.append('#else')
+        header_txt.append('#define VK_SIZE_T_SPECIFIER "%zu"')
+        header_txt.append('#endif')
+        header_txt.append('#define SEND_ENTRYPOINT_ID(entrypoint) ;')
+        header_txt.append('//#define SEND_ENTRYPOINT_ID(entrypoint) vktrace_TraceInfo(#entrypoint);\n')
+        header_txt.append('#define SEND_ENTRYPOINT_PARAMS(entrypoint, ...) ;')
+        header_txt.append('//#define SEND_ENTRYPOINT_PARAMS(entrypoint, ...) vktrace_TraceInfo(entrypoint, __VA_ARGS__);\n')
+        header_txt.append('#define CREATE_TRACE_PACKET(entrypoint, buffer_bytes_needed) \\')
+        header_txt.append('    pHeader = vktrace_create_trace_packet(VKTRACE_TID_VULKAN, VKTRACE_TPI_VK_##entrypoint, sizeof(packet_##entrypoint), buffer_bytes_needed);\n')
+        header_txt.append('#define FINISH_TRACE_PACKET() \\')
+        header_txt.append('    vktrace_finalize_trace_packet(pHeader); \\')
+        header_txt.append('    vktrace_write_trace_packet(pHeader, vktrace_trace_get_trace_file()); \\')
+        header_txt.append('    vktrace_delete_trace_packet(&pHeader);')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_packet_id_enum(),
+                self._generate_packet_id_name_func(),
+                self._generate_stringify_func(),
+                self._generate_interp_func()]
+
+        return "\n".join(body)
+
+class VktraceCoreTracePackets(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#pragma once\n')
+        header_txt.append('#include "vulkan/vulkan.h"')
+        header_txt.append('#include "vktrace_trace_packet_utils.h"\n')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_struct_util_funcs(),
+                self._generate_interp_funcs()]
+
+        return "\n".join(body)
+
+class VktraceExtTraceHeader(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#pragma once\n')
+        header_txt.append('#include "vulkan/vulkan.h"')
+        header_txt.append('#include "%s.h"' % extensionName.lower())
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_trace_func_protos_ext(self.extensionName)]
+
+        return "\n".join(body)
+
+class VktraceExtTraceC(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#include "vktrace_vk_packet_id.h"')
+        header_txt.append('#include "vktrace_platform.h"')
+        header_txt.append('#include "vktrace_common.h"')
+        header_txt.append('#include "vktrace_vk_%s.h"' % extensionName.lower())
+        header_txt.append('#include "vktrace_vk_%s_packets.h"' % extensionName.lower())
+        header_txt.append('#include "vk_struct_size_helper.h"')
+        header_txt.append('#include "%s_struct_size_helper.h"' % extensionName.lower())
+        #if extensionName == 'vk_lunarg_debug_marker':
+        #    header_txt.append('#include "vk_debug_marker_layer.h"\n')
+
+        header_txt.append('#include "vktrace_lib_helpers.h"')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_trace_funcs(self.extensionName)]
+
+        return "\n".join(body)
+
+class VktraceExtTracePackets(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#pragma once\n')
+        header_txt.append('#include "%s.h"' % extensionName.lower())
+        header_txt.append('#include "vktrace_trace_packet_utils.h"\n')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_interp_funcs_ext(self.extensionName)]
+
+        return "\n".join(body)
+
+class VktraceReplayVkFuncPtrs(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#pragma once\n')
+        header_txt.append('#if defined(PLATFORM_LINUX) || defined(XCB_NVIDIA)')
+        header_txt.append('#if !defined(ANDROID)')
+        header_txt.append('#include <xcb/xcb.h>\n')
+        header_txt.append('#endif')
+        header_txt.append('#endif')
+        header_txt.append('#include "vulkan/vulkan.h"')
+        #header_txt.append('#include "vulkan/vk_lunarg_debug_marker.h"')
+
+    def generate_body(self):
+        body = [self._generate_replay_func_ptrs()]
+        return "\n".join(body)
+
+class VktraceReplayObjMapperHeader(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#pragma once\n')
+        header_txt.append('#include <set>')
+        header_txt.append('#include <map>')
+        header_txt.append('#include <vector>')
+        header_txt.append('#include <string>')
+        header_txt.append('#include "vulkan/vulkan.h"')
+        header_txt.append('#include "vktrace_pageguard_memorycopy.h"')
+        #header_txt.append('#include "vulkan/vk_lunarg_debug_marker.h"')
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_replay_objmapper_class()]
+        return "\n".join(body)
+
+class VktraceReplayC(Subcommand):
+    def generate_header(self, extensionName):
+        header_txt = []
+        header_txt.append('#include "vkreplay_vkreplay.h"\n')
+        header_txt.append('#include "vkreplay.h"\n')
+        header_txt.append('#include "vkreplay_main.h"\n')
+        header_txt.append('#include <algorithm>')
+        header_txt.append('#include <queue>')
+        header_txt.append('\n')
+        header_txt.append('extern "C" {')
+        header_txt.append('#include "vktrace_vk_vk_packets.h"')
+        #header_txt.append('#include "vktrace_vk_vk_lunarg_debug_marker_packets.h"')
+        header_txt.append('#include "vktrace_vk_packet_id.h"')
+        #header_txt.append('#include "vk_enum_string_helper.h"\n}\n')
+
+        return "\n".join(header_txt)
+
+    def generate_body(self):
+        body = [self._generate_replay_init_funcs(),
+                self._generate_replay()]
+        body.append("}")
+        return "\n".join(body)
+
+def main():
+
+    subcommands = {
+            "vktrace-trace-h" : VktraceTraceHeader,
+            "vktrace-trace-c" : VktraceTraceC,
+            "vktrace-packet-id" : VktracePacketID,
+            "vktrace-core-trace-packets" : VktraceCoreTracePackets,
+            "vktrace-ext-trace-h" : VktraceExtTraceHeader,
+            "vktrace-ext-trace-c" : VktraceExtTraceC,
+            "vktrace-ext-trace-packets" : VktraceExtTracePackets,
+            "vktrace-replay-vk-funcs" : VktraceReplayVkFuncPtrs,
+            "vktrace-replay-obj-mapper-h" : VktraceReplayObjMapperHeader,
+            "vktrace-replay-c" : VktraceReplayC,
+    }
+
+    if len(sys.argv) < 3 or sys.argv[2] not in subcommands:
+        print("Available subcommands are: %s" % " ".join(subcommands))
+        exit(1)
+
+    subcmd = subcommands[sys.argv[2]](sys.argv[3])
+    subcmd.run()
+
+if __name__ == "__main__":
+    main()