فهرست منبع

Adds initial vulkan renderer with imgui hooked up for ui backend.

Sean Taylor 4 سال پیش
والد
کامیت
b307b036dd

+ 10 - 6
config/app.config.toml

@@ -1,5 +1,5 @@
 [window]
-title = "gameplay - editor"
+title = "GamePlay - Editor"
 width = 1920
 height = 1080
 fullscreen = false
@@ -9,12 +9,9 @@ hints = "NONE"
 level = "INFO"
 file = "gameplay.log"
 
-[[resource.alias]]
-name = "data"
-path = "@app.dir/../../../data"
 [[resource.alias]]
 name = "fonts"
-path = "@data/fonts"
+path = "@app.dir/../../../_deps/imgui/misc/fonts"
 [[resource.alias]]
 name = "icons"
 path = "@data/icons"
@@ -29,4 +26,11 @@ name = "scenes"
 path = "@data/scenes"
 
 [input]
-gamepad_mappings_db = "@app.dir/../../../config/gamecontrollerdb.txt"
+gamepad_mappings_db = "@app.dir/../../../config/gamecontrollerdb.txt"
+
+[graphics]
+validation = false
+minImageCount = 2
+
+[ui]
+defaultFont = "@fonts/Roboto-Medium.ttf"

+ 17 - 0
include/gameplay/App.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "Defines.h"
+#include "Signal.h"
 #include "Types.h"
 #include <memory>
 #include <string>
@@ -149,6 +150,22 @@ public:
      */
     bool resolve_resource_path(std::string& path);
 
+    /**
+     * Signals the application has completed startup.
+     *
+     * emits:
+     * <App*> The running application.
+     */
+    Signal<App*> on_startup;
+
+     /**
+     * Signals the application has started to shutdown.
+     *
+     * emits:
+     * <App*> The running application.
+     */
+    Signal<App*> on_shutdown;
+
 private:
 	App();
 	struct Impl;

+ 8 - 5
include/gameplay/Config.h

@@ -4,7 +4,6 @@
 
 namespace gameplay
 {
-
 /**
  * Defines a way to get/set config values from the system.
  *
@@ -44,8 +43,9 @@ public:
      *
      * @param key The key for which to set the value.
      * @param value The (default) value to be set into config.
+     * @return The resolved value in config after attempting to be set
      */
-    void set_string(const char* key, const char* value);
+    std::string set_string(const char* key, const char* value);
 
     /**
      * Gets a boolean value from config using the lookup key.
@@ -62,8 +62,9 @@ public:
      *
      * @param key The key for which to set the value.
      * @param value The (default) value to be set into config.
+     * @return The resolved value in config after attempting to be set
      */
-    void set_bool(const char* key, bool value);
+    bool set_bool(const char* key, bool value);
 
     /**
      * Gets a integer value from config using the lookup key.
@@ -80,8 +81,9 @@ public:
      *
      * @param key The key for which to set the value.
      * @param value The (default) value to be set into config.
+     * @return The resolved value in config after attempting to be set
      */
-    void set_int(const char* key, int value);
+    int set_int(const char* key, int value);
 
     /**
      * Gets a floating-point value from config using the lookup key.
@@ -98,8 +100,9 @@ public:
      *
      * @param key The key for which to set the value.
      * @param value The (default) value to be set into config.
+     * @return The resolved value in config after attempting to be set
      */
-    void set_float(const char* key, float value);
+    float set_float(const char* key, float value);
 
     /**
      * Gets the size (number of elements) for the array at the specified key.

+ 0 - 3
include/gameplay/FileSystem.h

@@ -6,7 +6,6 @@
 
 namespace gameplay
 {
-
 struct File;
 
 enum class FileMode
@@ -498,9 +497,7 @@ private:
     FileSystem();
     ~FileSystem();
     void set_app_executable_path(const char* path);
-
     struct Impl;
     Impl* _impl;
-    
 };
 }

+ 7 - 8
include/gameplay/Input.h

@@ -6,16 +6,8 @@
 
 namespace gameplay
 {
-
 class Window;
 
-enum class CursorMode : uint32_t
-{
-    NORMAL,
-    HIDDEN,
-    DISABLED
-};
-
 enum class Key : uint32_t
 {
     KEY_UNKNOWN,
@@ -206,6 +198,13 @@ enum class InputMode : uint32_t
     RAW_MOUSE_MOTION
 };
 
+enum class CursorMode : uint32_t
+{
+    NORMAL,
+    HIDDEN,
+    DISABLED
+};
+
 /**
  * Defines all static utility for polling for input on the main window.
  *

+ 0 - 1
include/gameplay/Logging.h

@@ -13,7 +13,6 @@
 
 namespace gameplay
 {
-
 /**
  * Defines the system for managing and controlling logging output.
  *

+ 2 - 2
include/gameplay/Monitor.h

@@ -4,6 +4,8 @@
 
 namespace gameplay
 {
+struct MonitorHandle;
+
 struct VideoMode
 {
     int32_t width;
@@ -27,8 +29,6 @@ enum class MonitorChangeEvent : uint32_t
     DISCONNECTED
 };
 
-struct MonitorHandle;
-
 
 /**
  * Defines a monitor.

+ 6 - 1
include/gameplay/Renderer.h

@@ -8,12 +8,17 @@ class GP_API Renderer
 {
     friend class App;
 public:
-    // todo
 
 private:
 	Renderer();
 	~Renderer();
 	void startup();
 	void shutdown();
+    void update();
+    void next_frame();
+    void render_frame();
+    void present_frame();
+    struct Impl;
+    Impl* _impl = nullptr;
 };
 }

+ 1 - 1
include/gameplay/Signal.h

@@ -7,7 +7,6 @@
 
 namespace gameplay
 {
-
 /**
  * Defines a signal for an event that can be emitted.
  *
@@ -25,6 +24,7 @@ namespace gameplay
  *     std::cout << arg1 << " " << arg2 << std::endl;
  * });
  *
+ * // emit a signal event
  * signal.emit("The answer:", 42);
  */
 template <typename... Args>

+ 0 - 1
include/gameplay/StringUtils.h

@@ -12,7 +12,6 @@ namespace gameplay
 class GP_API StringUtils
 {
 public:
-
     /**
      * Checks if the string begins with the given prefix.
      *

+ 0 - 1
include/gameplay/Timer.h

@@ -5,7 +5,6 @@
 
 namespace gameplay
 {
-
 /**
  * Defines a timer with configurable resolution.
  */

+ 4 - 1
include/gameplay/UI.h

@@ -8,12 +8,15 @@ class GP_API UI
 {
     friend class App;
 public:
-    // todo
+    // todo: manage styles, fonts, etc.
 
 private:
 	UI();
 	~UI();
 	void startup();
 	void shutdown();
+    void update();
+    struct Impl;
+    Impl* _impl = nullptr;
 };
 }

+ 0 - 2
include/gameplay/Unicode.h

@@ -6,7 +6,6 @@
 
 namespace gameplay
 {
-
 /**
  * Collection of unicode conversion utilities.
  *
@@ -137,7 +136,6 @@ public:
      */
     static void convert_wide_string_to_cowercase_in_place(std::wstring& string);
 #endif
-
 };
 
 

+ 0 - 1
include/gameplay/Window.h

@@ -6,7 +6,6 @@
 
 namespace gameplay
 {
-
 struct WindowHandle;
 struct Cursor;
 

+ 2 - 10
include/gameplay/Windowing.h

@@ -9,14 +9,10 @@
 
 namespace gameplay
 {
-
 class Window;
 class Monitor;
 struct Cursor;
 
-/**
- * Window hints. Uses when you create a window.
- */
 typedef uint32_t WindowHints;
 constexpr WindowHints WINDOW_HINT_NONE = 0;
 constexpr WindowHints WINDOW_HINT_NO_RESIZE = 1 << 0;
@@ -27,11 +23,6 @@ constexpr WindowHints WINDOW_HINT_SCALE_TO_MONITOR = 1 << 4;
 constexpr WindowHints WINDOW_HINT_FLOATING = 1 << 5;
 constexpr WindowHints WINDOW_HINT_MAXIMIZED = 1 << 6;
 
-/**
- * The window descriptor.
- *
- * Describes how to create a window.
- */
 struct WindowDesc
 {
     const char* title;
@@ -50,6 +41,8 @@ enum class CursorStandardShape : uint32_t
     HRESIZE,
     VRESIZE
 };
+
+
 /**
  * Defines the windowing system for window management.
  */
@@ -187,7 +180,6 @@ private:
     ~Windowing();
     void startup();
     void shutdown();
-
     struct Impl;
     Impl* _impl = nullptr;
 };

+ 5 - 7
premake5.lua

@@ -31,7 +31,7 @@ workspace "gameplay"
         buildoptions { "/permissive-" }
         buildoptions { "/WX" }
         warnings "Extra"
-        disablewarnings { "4100", "4127", "4189", "4201", "4251", "4530" }
+        disablewarnings { "4100", "4127", "4189", "4201", "4251", "4530", "4456", "4458", "26812" }
     filter { "system:linux" }
         platforms { "x86_64" }
         defaultplatform "x86_64"
@@ -63,6 +63,7 @@ workspace "gameplay"
             "source/gameplay",
             vulkan_sdk_dir.."/include",
             deps_dir.."/imgui", 
+            deps_dir.."/imgui/backends", 
             deps_dir.."/freetype/include",
             deps_dir.."/spdlog/include",
             deps_dir.."/cpptoml/include",
@@ -94,12 +95,9 @@ workspace "gameplay"
             "include/gameplay/*.*", 
             "source/gameplay/*.*",
              deps_dir.."/imgui/*.h", 
-             deps_dir.."/imgui/imgui.cpp",
-             deps_dir.."/imgui/imgui_demo.cpp",
-             deps_dir.."/imgui/imgui_draw.cpp",
-             deps_dir.."/imgui/imgui_tables.cpp",
-             deps_dir.."/imgui/imgui_widgets.cpp",
-             deps_dir.."/imgui/imgui_widgets.cpp",
+             deps_dir.."/imgui/*.cpp",
+             deps_dir.."/imgui/backends/imgui_impl_glfw.*",
+             deps_dir.."/imgui/backends/imgui_impl_vulkan.*",
              deps_dir.."/imgui/misc/freetype/imgui_freetype.*",
         }
         vpaths 

+ 24 - 21
source/gameplay/App.cpp

@@ -10,10 +10,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
-#pragma comment(lib, "legacy_stdio_definitions")
-#endif
+#include <imgui.h>
 
 namespace gameplay
 {
@@ -21,11 +18,11 @@ namespace gameplay
 struct App::Impl
 {
     bool running = false;
-    FileSystem* fileSystem = nullptr;
+    FileSystem* fs = nullptr;
     Config* config = nullptr;
     Logging* logging = nullptr;
     Windowing* windowing = nullptr;
-    Window* mainWindow = nullptr;
+    Window* window = nullptr;
     Renderer* renderer = nullptr;
     UI* ui = nullptr;
     std::unordered_map<std::string, std::string> resourceAliases;
@@ -55,14 +52,14 @@ int App::exec(int argc, char** argv)
 	_impl->running = true;
 
     // create the file system
-    _impl->fileSystem = new FileSystem();
+    _impl->fs = new FileSystem();
 
     // create and load settings
     _impl->config = new Config();
     _impl->config->load(argc, argv);
 
     // register application executable directory as a file system alias
-    _impl->resourceAliases["app.dir"] = _impl->fileSystem->get_app_directory_path();
+    _impl->resourceAliases["app.dir"] = _impl->fs->get_app_directory_path();
 
     // register file system aliases from config
     _impl->config->for_each_table("resource.alias",
@@ -97,30 +94,36 @@ int App::exec(int argc, char** argv)
 
     // create the main window
     WindowDesc windowDesc = { windowTitle.c_str(), windowWidth, windowHeight, windowFullscreen, windowHints };
-    _impl->mainWindow = _impl->windowing->create_window(windowDesc);
+    _impl->window = _impl->windowing->create_window(windowDesc);
     // adjust the window position where the frame is at 0, 0
     int top;
-    _impl->mainWindow->get_frame_size(nullptr, &top, nullptr, nullptr);
-    _impl->mainWindow->set_pos({0, top});
-
-    // startup the renderer
-    _impl->renderer = new Renderer();
-    _impl->renderer->startup();
+    _impl->window->get_frame_size(nullptr, &top, nullptr, nullptr);
+    _impl->window->set_pos({0, top});
 
     // startup the ui system
     _impl->ui = new UI();
     _impl->ui->startup();
+
+    // startup the renderer
+    _impl->renderer = new Renderer();
+    _impl->renderer->startup();
    
     // run the main window event loop until we should close
-    while (!_impl->mainWindow->should_close())
+    while (!_impl->window->should_close())
     {
         _impl->windowing->poll_events();
 
-        // new frame 
+        _impl->renderer->next_frame();
+
+        _impl->renderer->update();
+        _impl->ui->update();
+
+        _impl->renderer->render_frame();
+        _impl->renderer->present_frame();
     }
 
     // destroy main window and shutdown
-    _impl->windowing->destroy_window(_impl->mainWindow);
+    _impl->windowing->destroy_window(_impl->window);
     _impl->windowing->shutdown();
 
 	return 0;
@@ -128,7 +131,7 @@ int App::exec(int argc, char** argv)
 
 void App::exit()
 {
-    _impl->mainWindow->close();
+    _impl->window->close();
 }
 
 void App::set_time(double timeSecs)
@@ -143,7 +146,7 @@ double App::get_time() const
 
 FileSystem* App::get_file_system() const
 {
-	return _impl->fileSystem;
+	return _impl->fs;
 }
 
 Config* App::get_config() const
@@ -163,7 +166,7 @@ Windowing* App::get_windowing() const
 
 Window* App::get_main_window() const
 {
-	return _impl->mainWindow;
+	return _impl->window;
 }
 
 Renderer* App::get_renderer() const

+ 88 - 52
source/gameplay/Config.cpp

@@ -141,7 +141,6 @@ void Config::load(int argc, char** argv)
             }
         }
     }
-
     // if we found a config file that exists
     if (configFound)
     {
@@ -159,7 +158,6 @@ void Config::load(int argc, char** argv)
     {
         _impl->root = cpptoml::make_table();
     }
-
     // apply our settings from command line arguments
     if (argc > 0)
     {
@@ -272,57 +270,106 @@ std::string Config::get_string(const char* key, const char* altValue)
     }
 }
 
-void Config::set_string(const char* key, const char* value)
+std::string Config::set_string(const char* key, const char* value)
 {
+    std::string ret = value;
     auto kt = __get_key_and_table(_impl->root, key);
-    kt.second->insert(kt.first, value);
+    if (kt.second && kt.second->contains(kt.first))
+    {
+        ret = kt.second->get_as<std::string>(kt.first).value_or(value);
+    }
+    else
+    {
+        kt.second->insert(kt.first, value);
+    }
+    return ret;
 }
 
-void Config::get_string_array(const char* key, std::string* arr, size_t arrSize)
+bool Config::get_bool(const char* key, bool altValue)
 {
-    auto values = _impl->root->get_qualified_array_of<std::string>(key);
-    GP_ASSERT(arr && arrSize >= values->size());
-    size_t i = 0;
-    for (const auto& value : *values)
+    if(strchr(key, '.'))
     {
-        arr[i] = value;
-        i++;
+        return (int)_impl->root->get_qualified_as<bool>(key).value_or(altValue);
+    }
+    else
+    {
+        return (int)_impl->root->get_as<bool>(key).value_or(altValue);
     }
 }
 
-void Config::set_string_array(const char* key, const std::string* arr, size_t arrSize)
+bool Config::set_bool(const char* key, bool value)
 {
-    auto new_array = cpptoml::make_array();
-    for (size_t i = 0; i < arrSize; i++)
+    bool ret = value;
+    auto kt = __get_key_and_table(_impl->root, key);
+    if (kt.second && kt.second->contains(kt.first))
     {
-        new_array->push_back(arr[i]);
+        ret = kt.second->get_as<bool>(kt.first).value_or(value);
     }
-    auto kt = __get_key_and_table(_impl->root, key);
-    kt.second->insert(kt.first, new_array);
+    else
+    {
+        kt.second->insert(kt.first, value);
+    }
+    return ret;
+}
 
+int Config::get_int(const char* key, int altValue)
+{
+    if(strchr(key, '.'))
+    {
+        return (int)_impl->root->get_qualified_as<int64_t>(key).value_or(altValue);
+    }
+    else
+    {
+        return (int)_impl->root->get_as<int64_t>(key).value_or(altValue);
+    }
 }
 
-bool Config::get_bool(const char* key, bool altValue)
+int Config::set_int(const char* key, int value)
+{
+    int ret = value;
+    auto kt = __get_key_and_table(_impl->root, key);
+    if (kt.second && kt.second->contains(kt.first))
+    {
+        ret = (int)kt.second->get_as<int64_t>(kt.first).value_or(value);
+    }
+    else
+    {
+        kt.second->insert(kt.first, (int64_t)value);
+    }
+    return ret;
+}
+
+float Config::get_float(const char* key, float altValue)
 {
     if(strchr(key, '.'))
     {
-        return _impl->root->get_qualified_as<bool>(key).value_or(altValue);
+        return (float)_impl->root->get_qualified_as<double>(key).value_or((double)altValue);
     }
     else
     {
-        return _impl->root->get_as<bool>(key).value_or(altValue);
+        return (float)_impl->root->get_as<double>(key).value_or((double)altValue);
     }
 }
 
-void Config::set_bool(const char* key, bool value)
+float Config::set_float(const char* key, float value)
 {
+    float ret = value;
     auto kt = __get_key_and_table(_impl->root, key);
-    kt.second->insert(kt.first, value);
+    if (kt.second && kt.second->contains(kt.first))
+    {
+        ret = (float)kt.second->get_as<double>(kt.first).value_or(value);
+    }
+    else
+    {
+        kt.second->insert(kt.first, (double)value);
+    }
+    return ret;
 }
 
-void Config::get_bool_array(const char* key, bool* arr, size_t arrSize)
+
+void Config::get_string_array(const char* key, std::string* arr, size_t arrSize)
 {
-    auto values = _impl->root->get_qualified_array_of<bool>(key);
+    auto values = _impl->root->get_qualified_array_of<std::string>(key);
     GP_ASSERT(arr && arrSize >= values->size());
     size_t i = 0;
     for (const auto& value : *values)
@@ -332,7 +379,7 @@ void Config::get_bool_array(const char* key, bool* arr, size_t arrSize)
     }
 }
 
-void Config::set_bool_array(const char* key, const bool* arr, size_t arrSize)
+void Config::set_string_array(const char* key, const std::string* arr, size_t arrSize)
 {
     auto new_array = cpptoml::make_array();
     for (size_t i = 0; i < arrSize; i++)
@@ -341,26 +388,33 @@ void Config::set_bool_array(const char* key, const bool* arr, size_t arrSize)
     }
     auto kt = __get_key_and_table(_impl->root, key);
     kt.second->insert(kt.first, new_array);
+
 }
 
-int Config::get_int(const char* key, int altValue)
+void Config::get_bool_array(const char* key, bool* arr, size_t arrSize)
 {
-    if(strchr(key, '.'))
-    {
-        return (int)_impl->root->get_qualified_as<int64_t>(key).value_or(altValue);
-    }
-    else
+    auto values = _impl->root->get_qualified_array_of<bool>(key);
+    GP_ASSERT(arr && arrSize >= values->size());
+    size_t i = 0;
+    for (const auto& value : *values)
     {
-        return (int)_impl->root->get_as<int64_t>(key).value_or(altValue);
+        arr[i] = value;
+        i++;
     }
 }
 
-void Config::set_int(const char* key, int value)
+void Config::set_bool_array(const char* key, const bool* arr, size_t arrSize)
 {
+    auto new_array = cpptoml::make_array();
+    for (size_t i = 0; i < arrSize; i++)
+    {
+        new_array->push_back(arr[i]);
+    }
     auto kt = __get_key_and_table(_impl->root, key);
-    kt.second->insert(kt.first, (int64_t)value);
+    kt.second->insert(kt.first, new_array);
 }
 
+
 void Config::get_int_array(const char* key, int* arr, size_t arrSize)
 {
     auto values = _impl->root->get_qualified_array_of<int64_t>(key);
@@ -384,24 +438,6 @@ void Config::set_int_array(const char* key, const int* arr, size_t arrSize)
     kt.second->insert(kt.first, new_array);
 }
 
-float Config::get_float(const char* key, float altValue)
-{
-    if(strchr(key, '.'))
-    {
-        return (float)_impl->root->get_qualified_as<double>(key).value_or((double)altValue);
-    }
-    else
-    {
-        return (float)_impl->root->get_as<double>(key).value_or((double)altValue);
-    }
-}
-
-void Config::set_float(const char* key, float value)
-{
-    auto kt = __get_key_and_table(_impl->root, key);
-    kt.second->insert(kt.first, (double)value);
-}
-
 void Config::get_float_array(const char* key, float* arr, size_t arrSize)
 {
     auto values = _impl->root->get_qualified_array_of<double>(key);

+ 872 - 5
source/gameplay/Renderer.cpp

@@ -1,31 +1,898 @@
 #include "Renderer.h"
+#include "Config.h"
+#include "FileSystem.h"
+#include "Logging.h"
+#include "RendererVK.h"
+#include "Path.h"
+#include "Window.h"
 #include "WindowingGLFW.h"
+#include "imgui_impl_vulkan.h"
+#include <vector>
 
 namespace gameplay
 {
 
+struct RenderFrame
+{
+    VkCommandPool commandPool = VK_NULL_HANDLE;
+    VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
+    VkFence fence = VK_NULL_HANDLE;
+    VkImage backbuffer = VK_NULL_HANDLE;
+    VkImageView backbufferView = VK_NULL_HANDLE;
+    VkFramebuffer framebuffer = VK_NULL_HANDLE;
+    VkSemaphore imageAcquiredSemaphore = VK_NULL_HANDLE;
+    VkSemaphore renderCompleteSemaphore = VK_NULL_HANDLE;
+};
+
+struct Renderer::Impl
+{   
+    bool validation = false;
+    uint32_t minImageCount = 2;
+    GLFWwindow* glfwWindow = nullptr;
+    VkAllocationCallbacks* allocator = nullptr;
+    VkDebugReportCallbackEXT debugReport = nullptr; 
+    VkInstance instance = VK_NULL_HANDLE;
+    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
+    VkPhysicalDeviceProperties physicalDeviceProperties = {};
+    VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties = {};
+    VkDevice device = VK_NULL_HANDLE;
+    std::vector<VkQueueFamilyProperties> queueFamilyProperties;
+    struct
+    {
+        uint32_t present = 0;
+        uint32_t graphics = 0;
+        uint32_t compute = 0;
+        uint32_t transfer = 0;
+    } queueFamilyIndices;
+    VkQueue queue = VK_NULL_HANDLE;
+    int surfaceWidth = 0;
+    int surfaceHeight = 0;
+    VkSurfaceKHR surface = VK_NULL_HANDLE;
+    VkSurfaceFormatKHR surfaceFormat;
+    VkColorSpaceKHR colorSpace;
+    VkSwapchainKHR swapchain = VK_NULL_HANDLE;
+    VkPresentModeKHR presentMode;
+    VkRenderPass renderPass = VK_NULL_HANDLE;
+    VkPipeline pipeline = VK_NULL_HANDLE;
+    bool clearEnable = true;
+    VkClearValue clearValue = {};
+    uint32_t frameIndex = 0;
+    uint32_t semaphoreIndex = 0;
+    VkFormat imageFormat;
+    uint32_t imageCount = 0;
+    std::vector<RenderFrame> frames;
+    VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+    VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
+    bool rebuildSwapchain = false;
+
+    void startup_vulkan();
+    void shutdown_vulkan();
+    void startup_window_surface();
+    void shutdown_window_surface();
+    void create_or_resize_window_swapchain();
+    void destroy_render_frame(RenderFrame* renderFrame);
+    uint32_t get_queue_family_index(VkQueueFlagBits queueFlags);
+    VkBool32 is_device_extension_present(VkPhysicalDevice physicalDevice, const char* extensionName);
+    VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, const VkFormat* requestFormats, int requestFormatCount, VkColorSpaceKHR requestColorSpace);
+    VkPresentModeKHR select_present_mode(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,  const VkPresentModeKHR* requestModes, int requestModeCount);
+    uint32_t get_image_count_for_present_mode(VkPresentModeKHR present_mode);
+    void next_frame();
+    void present_frame();
+    void startup_imgui();
+    void shutdow_imgui();
+    void render_imgui();
+};
+
 Renderer::Renderer()
 {
+    _impl = new Renderer::Impl();
+    auto config = App::get_app()->get_config();
+    _impl->validation = config->set_bool("graphics.validation", false);
+    _impl->minImageCount = (uint32_t)config->set_int("graphics.minImageCount", 2);
 }
 
 Renderer::~Renderer()
 {
+    GP_SAFE_DELETE(_impl);
 }
 
 void Renderer::startup()
 {
-    /*if (!glfwVulkanSupported())
+    if (!glfwVulkanSupported())
+    {
+        GP_LOG_ERROR("GLFW: Vulkan Not Supported");
+    }
+    _impl->startup_vulkan();
+    _impl->startup_window_surface();
+    _impl->startup_imgui();
+}
+
+void Renderer::Impl::startup_vulkan()
+{
+    load_vulkan_library();
+
+    std::vector<const char*> instanceExtension;
+    // get the required surface extensions from glfw
+    uint32_t glfwExtensionCount = 0;
+    const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
+    for ( uint32_t i = 0; i < glfwExtensionCount; i++)
+    {
+        instanceExtension.push_back(glfwExtensions[i]);
+    }
+
+    // initialize the vulkan application
+    auto fs = App::get_app()->get_file_system();
+    std::string appName = Path(fs->get_app_executable_path()).get_stem();
+    VkApplicationInfo appInfo = {};
+	appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+	appInfo.pApplicationName = appName.c_str();
+	appInfo.pEngineName = appName.c_str();
+	appInfo.apiVersion = VK_API_VERSION_1_0;
+
+    // create the vulkan instance
+    VkInstanceCreateInfo instanceCreateInfo = {};
+    instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+    instanceCreateInfo.pNext = nullptr;
+    instanceCreateInfo.pApplicationInfo = &appInfo;
+    if (instanceExtension.size() > 0)
+    {
+        if (validation)
+        {
+            instanceExtension.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
+        }
+        instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtension.size();
+        instanceCreateInfo.ppEnabledExtensionNames = instanceExtension.data();
+    }
+    if (validation)
+    {
+        instanceCreateInfo.enabledLayerCount = (uint32_t)vulkan_validation_layers.size();
+        instanceCreateInfo.ppEnabledLayerNames = vulkan_validation_layers.data();
+    }
+    VK_CHECK_RESULT(vkCreateInstance(&instanceCreateInfo, allocator, &instance));
+
+    // register validation debug report
+    if (validation)
+    {
+        vkCreateDebugReportCallbackEXT = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"));
+        vkDestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"));
+        vkDebugReportMessageEXT = reinterpret_cast<PFN_vkDebugReportMessageEXT>(vkGetInstanceProcAddr(instance, "vkDebugReportMessageEXT"));
+        VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
+        dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
+        dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
+        dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)log_vulkan_validation_debug_report;
+        dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
+        VK_CHECK_RESULT(vkCreateDebugReportCallbackEXT(instance, &dbgCreateInfo, allocator, &debugReport));
+    }
+
+    // load functions (needed on some platgforms)
+    load_vulkan_functions();
+
+    // get the available gpus
+    uint32_t gpuCount = 0;
+    VK_CHECK_RESULT(vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr));
+    GP_ASSERT(gpuCount > 0);
+    std::vector<VkPhysicalDevice> physicalDevices(gpuCount);
+    VK_CHECK_RESULT(vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data()));
+
+    // rate the physical devices based on  important properties and features
+    std::map<int, VkPhysicalDevice> physicalDevicesRated;
+    for (const auto& physicalDev : physicalDevices)
+    {
+        VkPhysicalDeviceProperties properties;
+        vkGetPhysicalDeviceProperties(physicalDev, &properties);
+        VkPhysicalDeviceFeatures features;
+        vkGetPhysicalDeviceFeatures(physicalDev, &features);
+        int score = 0;
+        if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
+        {
+            score += 1000;
+        }
+        score += properties.limits.maxImageDimension2D;
+        if (!features.geometryShader)
+        {
+            score = 0;
+        }
+        physicalDevicesRated[score] = physicalDev;
+    }
+    // take the first device from rated devices that support our queue requirements
+    physicalDevice = physicalDevicesRated.begin()->second;
+
+    // get properties various properties of the physical device
+    vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
+    vkGetPhysicalDeviceMemoryProperties(physicalDevice, &physicalDeviceMemoryProperties);
+    uint32_t queueFamilyCount;
+    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
+    queueFamilyProperties.resize(queueFamilyCount);
+    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data());
+
+    // get queue create infos for queues
+    std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};
+
+    // graphics queue create info
+    const float defaultQueuePriority(0.0f);
+    queueFamilyIndices.graphics = get_queue_family_index(VK_QUEUE_GRAPHICS_BIT);
+    VkDeviceQueueCreateInfo queueInfo{};
+    queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+    queueInfo.queueFamilyIndex = queueFamilyIndices.graphics;
+    queueInfo.queueCount = 1;
+    queueInfo.pQueuePriorities = &defaultQueuePriority;
+    queueCreateInfos.push_back(queueInfo);
+
+    // compute queue create info (if different) and requested
+    queueFamilyIndices.compute = get_queue_family_index(VK_QUEUE_COMPUTE_BIT);
+    if (queueFamilyIndices.compute != queueFamilyIndices.graphics)
+    {
+        float queuePriority(0.0f);
+        VkDeviceQueueCreateInfo queueInfo{};
+        queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        queueInfo.queueFamilyIndex = queueFamilyIndices.compute;
+        queueInfo.queueCount = 1;
+        queueInfo.pQueuePriorities = &defaultQueuePriority;
+        queueCreateInfos.push_back(queueInfo);
+    }
+    else
+    {
+        queueFamilyIndices.compute = queueFamilyIndices.graphics;
+    }
+
+    if ((queueFamilyIndices.transfer != queueFamilyIndices.graphics) && (queueFamilyIndices.transfer != queueFamilyIndices.compute))
+    {
+        // if compute family index differs, we need an additional queue create info for the compute queue
+        VkDeviceQueueCreateInfo queueInfo{};
+        queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        queueInfo.queueFamilyIndex = queueFamilyIndices.transfer;
+        queueInfo.queueCount = 1;
+        queueInfo.pQueuePriorities = &defaultQueuePriority;
+        queueCreateInfos.push_back(queueInfo);
+    }
+    else
+    {
+        queueFamilyIndices.transfer = queueFamilyIndices.graphics;
+    }
+        
+    // Create the logical device
+    VkPhysicalDeviceFeatures deviceFeatures = {};
+    VkDeviceCreateInfo deviceCreateInfo = {};
+    deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+    deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size();
+    deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
+    deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
+
+    std::vector<const char*> deviceExtensions;
+    deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
+    // Add validation layers/extensions
+    if (validation && is_device_extension_present(physicalDevice, VK_EXT_DEBUG_MARKER_EXTENSION_NAME))
+    {
+        deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
+    }
+    
+    if (deviceExtensions.size() > 0)
+    {
+        deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensions.size();
+        deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
+    }
+    else
+    {
+        deviceCreateInfo.enabledExtensionCount = 0;
+    }
+    if (validation)
+    {
+        deviceCreateInfo.enabledLayerCount = (uint32_t)vulkan_validation_layers.size();
+        deviceCreateInfo.ppEnabledLayerNames = vulkan_validation_layers.data();
+    }
+    else
+    {
+        deviceCreateInfo.enabledLayerCount = 0;
+    }
+    // create device and get the queue
+    VK_CHECK_RESULT(vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device));
+    vkGetDeviceQueue(device, queueFamilyIndices.graphics, 0, &queue);
+
+    // setup and descriptor pool
+    VkDescriptorPoolSize poolSizes[] =
+    {
+        { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
+        { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
+        { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
+        { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
+        { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
+        { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
+        { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
+        { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
+        { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
+        { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
+        { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
+    };
+    VkDescriptorPoolCreateInfo poolInfo = {};
+    poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+    poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+    poolInfo.maxSets = 1000 * (uint32_t)GP_COUNTOF(poolSizes);
+    poolInfo.poolSizeCount = (uint32_t)GP_COUNTOF(poolSizes);
+    poolInfo.pPoolSizes = poolSizes;
+    VK_CHECK_RESULT(vkCreateDescriptorPool(device, &poolInfo, allocator, &descriptorPool));
+}
+
+void Renderer::Impl::shutdown_vulkan()
+{
+    vkDestroyDescriptorPool(device, descriptorPool, allocator);
+    if (validation)
+    {
+        vkDestroyDebugReportCallbackEXT(instance, debugReport, allocator);
+    }
+    vkDestroyDevice(device, allocator);
+    vkDestroyInstance(instance, allocator);
+}
+
+void Renderer::Impl::startup_window_surface()
+{
+    glfwWindow = App::get_app()->get_main_window()->handle->glfwWindow;
+
+    // create the window surface
+    VK_CHECK_RESULT(glfwCreateWindowSurface(instance, glfwWindow, allocator, &surface));
+
+    // get the framebuffer size
+    glfwGetFramebufferSize(glfwWindow, &surfaceWidth, &surfaceHeight);
+
+    // lookup device surface and swapchain extensions
+    vkGetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR"));
+    vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
+    vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR"));
+    vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR"));
+    vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(vkGetInstanceProcAddr(instance, "vkCreateSwapchainKHR"));
+    vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetInstanceProcAddr(instance, "vkDestroySwapchainKHR"));
+    vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetInstanceProcAddr(instance, "vkGetSwapchainImagesKHR"));
+    vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetInstanceProcAddr(instance, "vkAcquireNextImageKHR"));
+    vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(vkGetInstanceProcAddr(instance, "vkQueuePresentKHR"));
+
+    VkBool32 res;
+    vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndices.graphics, surface, &res);
+    if (res != VK_TRUE)
+    {
+        GP_LOG_ERROR("Failed vkGetPhysicalDeviceSurfaceSupportKHR");
+        return;
+    }
+    // select surface format
+    const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
+    const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+    surfaceFormat = select_surface_format(physicalDevice, surface, requestSurfaceImageFormat, (uint32_t)GP_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace);
+
+    // get the present mode and image count for the present mode
+    VkPresentModeKHR presentModes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
+    presentMode = select_present_mode(physicalDevice, surface, &presentModes[0], (uint32_t)GP_COUNTOF(presentModes));
+
+    // create the swapchain with render frames
+    create_or_resize_window_swapchain();
+}
+
+
+void Renderer::Impl::shutdown_window_surface()
+{
+    vkDeviceWaitIdle(device);
+    for (uint32_t i = 0; i < imageCount; i++)
+    {
+        destroy_render_frame(&frames[i]);
+    }
+    frames.clear();
+    vkDestroyPipeline(device, pipeline, allocator);
+    vkDestroyRenderPass(device, renderPass, allocator);
+    vkDestroySwapchainKHR(device,swapchain, allocator);
+    vkDestroySurfaceKHR(instance, surface, allocator);
+}
+
+uint32_t Renderer::Impl::get_queue_family_index(VkQueueFlagBits queueFlags)
+{
+    if (queueFlags & VK_QUEUE_COMPUTE_BIT)
+    {
+        for (uint32_t i = 0; i < static_cast<uint32_t>(queueFamilyProperties.size()); i++)
+        {
+            if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0))
+            {
+                return i;
+                break;
+            }
+        }
+    }
+    for (uint32_t i = 0; i < static_cast<uint32_t>(queueFamilyProperties.size()); i++)
+    {
+        if (queueFamilyProperties[i].queueFlags & queueFlags)
+        {
+            return i;
+            break;
+        }
+    }
+    return 0;
+}
+
+VkBool32 Renderer::Impl::is_device_extension_present(VkPhysicalDevice physicalDevice, const char* extensionName)
+{
+    uint32_t extensionCount = 0;
+    std::vector<VkExtensionProperties> extensions;
+    vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, NULL);
+    extensions.resize(extensionCount);
+    vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, extensions.data());
+    for (auto& ext : extensions)
+    {
+        if (!strcmp(extensionName, ext.extensionName))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+VkSurfaceFormatKHR Renderer::Impl::select_surface_format(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, const VkFormat* requestFormats, int requestFormatCount, VkColorSpaceKHR requestColorSpace)
+{
+    uint32_t availSurfaceFormatCount;
+    vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &availSurfaceFormatCount, NULL);
+    std::vector<VkSurfaceFormatKHR> availFormats;
+    availFormats.resize((int)availSurfaceFormatCount);
+    vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &availSurfaceFormatCount, availFormats.data());
+
+    if (availSurfaceFormatCount == 1)
+    {
+        if (availFormats[0].format == VK_FORMAT_UNDEFINED)
+        {
+            VkSurfaceFormatKHR ret;
+            ret.format = requestFormats[0];
+            ret.colorSpace = requestColorSpace;
+            return ret;
+        }
+        else
+        {
+            return availFormats[0];
+        }
+    }
+    else
+    {
+        for (int i = 0; i < requestFormatCount; i++)
+        {
+            for (uint32_t i = 0; i < availSurfaceFormatCount; i++)
+            {
+                if (availFormats[i].format == requestFormats[i] && availFormats[i].colorSpace == requestColorSpace)
+                {
+                    return availFormats[i];
+                }
+            }
+        }
+        return availFormats[0];
+    }
+}
+
+VkPresentModeKHR Renderer::Impl::select_present_mode(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, const VkPresentModeKHR* requestModes, int requestModeCount)
+{
+    // request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory
+    uint32_t availModeCount = 0;
+    vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &availModeCount, nullptr);
+    std::vector<VkPresentModeKHR> availModes;
+    availModes.resize((int)availModeCount);
+    vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &availModeCount, availModes.data());
+
+    for (int req = 0; req < requestModeCount; req++)
+    {
+        for (uint32_t avail = 0; avail < availModeCount; avail++)
+        {
+            if (requestModes[req] == availModes[avail])
+            {
+                return requestModes[req];
+            }
+        }
+    }
+    return VK_PRESENT_MODE_FIFO_KHR;
+}
+
+uint32_t Renderer::Impl::get_image_count_for_present_mode(VkPresentModeKHR mode)
+{
+    switch(mode)
+    {
+    case VK_PRESENT_MODE_MAILBOX_KHR:
+        return 3;
+    case VK_PRESENT_MODE_FIFO_KHR:
+    case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
+        return 2;
+    case VK_PRESENT_MODE_IMMEDIATE_KHR:
+        return 1;
+    default:
+        return 1;
+    }
+}
+
+void Renderer::Impl::create_or_resize_window_swapchain()
+{
+    // preserve previous swa
+    VkSwapchainKHR oldSwapchain = swapchain;
+    swapchain = VK_NULL_HANDLE;
+    VK_CHECK_RESULT(vkDeviceWaitIdle(device));
+
+    // cleanup previous swapchain resources
+    for (size_t i = 0; i <imageCount; i++)
+    {
+        destroy_render_frame(&frames[i]);
+    }
+    if (renderPass)
+    {
+        vkDestroyRenderPass(device, renderPass, allocator);
+    }
+    if (pipeline)
+    {
+        vkDestroyPipeline(device, pipeline, allocator);
+    }
+    uint32_t minImageCountForMode = get_image_count_for_present_mode(presentMode);
+    if (minImageCount < minImageCountForMode)
+    {
+        minImageCount = minImageCountForMode;
+    }
+
+    // setup new swapchain
+    VkSwapchainCreateInfoKHR info = {};
+    info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+    info.surface = surface;
+    info.minImageCount = minImageCount;
+    info.imageFormat = surfaceFormat.format;
+    info.imageColorSpace = surfaceFormat.colorSpace;
+    info.imageArrayLayers = 1;
+    info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;           // Assume that graphics family == present family
+    info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    info.presentMode = presentMode;
+    info.clipped = VK_TRUE;
+    info.oldSwapchain = swapchain;
+    VkSurfaceCapabilitiesKHR surfaceCaps;
+    VK_CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCaps));
+    if (info.minImageCount < surfaceCaps.minImageCount)
+    {
+        info.minImageCount = surfaceCaps.minImageCount;
+    }
+    else if (surfaceCaps.maxImageCount != 0 && info.minImageCount > surfaceCaps.maxImageCount)
+    {
+        info.minImageCount = surfaceCaps.maxImageCount;
+    }
+
+    if (surfaceCaps.currentExtent.width == 0xffffffff)
+    {
+        info.imageExtent.width = surfaceWidth;
+        info.imageExtent.height = surfaceHeight;
+    }
+    else
+    {
+        info.imageExtent.width = surfaceWidth = surfaceCaps.currentExtent.width;
+        info.imageExtent.height = surfaceHeight = surfaceCaps.currentExtent.height;
+    }
+    VK_CHECK_RESULT(vkCreateSwapchainKHR(device, &info, allocator, &swapchain));
+    
+
+    // get the number of back buffers
+    VK_CHECK_RESULT(vkGetSwapchainImagesKHR(device, swapchain, &imageCount, NULL));
+    frames.resize(imageCount);
+
+    // get the back buffer images and assign into the render frames
+    std::vector<VkImage> backbuffers;
+    backbuffers.resize(imageCount);
+    VK_CHECK_RESULT(vkGetSwapchainImagesKHR(device, swapchain, &imageCount, backbuffers.data()));    
+    for (uint32_t i = 0; i < imageCount; i++)
+    {
+        frames[i].backbuffer = backbuffers[i];
+    }
+    // destroy any previous swapchain that may exist
+    if (oldSwapchain)
+    {
+        vkDestroySwapchainKHR(device, oldSwapchain, allocator);
+    }
+
+    // create a render pass for ui
+    {
+        VkAttachmentDescription attachment = {};
+        attachment.format = surfaceFormat.format;
+        attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+        attachment.loadOp = clearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+        attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+        attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+        VkAttachmentReference color_attachment = {};
+        color_attachment.attachment = 0;
+        color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        VkSubpassDescription subpass = {};
+        subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+        subpass.colorAttachmentCount = 1;
+        subpass.pColorAttachments = &color_attachment;
+        VkSubpassDependency dependency = {};
+        dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+        dependency.dstSubpass = 0;
+        dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+        dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+        dependency.srcAccessMask = 0;
+        dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+        VkRenderPassCreateInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+        info.attachmentCount = 1;
+        info.pAttachments = &attachment;
+        info.subpassCount = 1;
+        info.pSubpasses = &subpass;
+        info.dependencyCount = 1;
+        info.pDependencies = &dependency;
+        VK_CHECK_RESULT(vkCreateRenderPass(device, &info, allocator, &renderPass));
+    }
+
+    // create the iamge view
+    {
+        VkImageViewCreateInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+        info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+        info.format = surfaceFormat.format;
+        info.components.r = VK_COMPONENT_SWIZZLE_R;
+        info.components.g = VK_COMPONENT_SWIZZLE_G;
+        info.components.b = VK_COMPONENT_SWIZZLE_B;
+        info.components.a = VK_COMPONENT_SWIZZLE_A;
+        VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+        info.subresourceRange = image_range;
+        for (uint32_t i = 0; i < imageCount; i++)
+        {
+            RenderFrame* renderFrame = &frames[i];
+            info.image = renderFrame->backbuffer;
+            VK_CHECK_RESULT(vkCreateImageView(device, &info, allocator, &renderFrame->backbufferView));
+        }
+    }
+
+    // create the framebuffer
+    {
+        VkImageView attachment[1];
+        VkFramebufferCreateInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+        info.renderPass = renderPass;
+        info.attachmentCount = 1;
+        info.pAttachments = attachment;
+        info.width = surfaceWidth;
+        info.height = surfaceHeight;
+        info.layers = 1;
+        for (uint32_t i = 0; i < imageCount; i++)
+        {
+            RenderFrame* renderFrame = &frames[i];
+            attachment[0] = renderFrame->backbufferView;
+            VK_CHECK_RESULT(vkCreateFramebuffer(device, &info, allocator, &renderFrame->framebuffer));
+        }
+    }
+
+    // create command buffers for each render frame
+    for (uint32_t i = 0; i < imageCount; i++)
+    {
+        RenderFrame* renderFrame = &frames[i];
+        // create the command pool
+        {
+            VkCommandPoolCreateInfo info = {};
+            info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+            info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+            info.queueFamilyIndex = queueFamilyIndices.graphics;
+            VK_CHECK_RESULT(vkCreateCommandPool(device, &info, allocator, &renderFrame->commandPool));
+        }
+        // create the command buffer
+        {
+            VkCommandBufferAllocateInfo info = {};
+            info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+            info.commandPool = renderFrame->commandPool;
+            info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+            info.commandBufferCount = 1;
+            VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &info, &renderFrame->commandBuffer));
+        }
+        // create the fence
+        {
+            VkFenceCreateInfo info = {};
+            info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+            info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+            VK_CHECK_RESULT(vkCreateFence(device, &info, allocator, &renderFrame->fence));
+        }
+        // create the semaphore
+        {
+            VkSemaphoreCreateInfo info = {};
+            info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+            VK_CHECK_RESULT(vkCreateSemaphore(device, &info, allocator, &renderFrame->imageAcquiredSemaphore));
+            VK_CHECK_RESULT(vkCreateSemaphore(device, &info, allocator, &renderFrame->renderCompleteSemaphore));
+        }
+    }
+}
+
+void Renderer::Impl::destroy_render_frame(RenderFrame* renderFrame)
+{
+    vkDestroyFence(device, renderFrame->fence, allocator);
+    vkFreeCommandBuffers(device, renderFrame->commandPool, 1, &renderFrame->commandBuffer);
+    vkDestroyCommandPool(device, renderFrame->commandPool, allocator);
+    renderFrame->fence = VK_NULL_HANDLE;
+    renderFrame->commandBuffer = VK_NULL_HANDLE;
+    renderFrame->commandPool = VK_NULL_HANDLE;
+
+    vkDestroyImageView(device, renderFrame->backbufferView, allocator);
+    vkDestroyFramebuffer(device, renderFrame->framebuffer, allocator);
+
+    vkDestroySemaphore(device, renderFrame->imageAcquiredSemaphore, allocator);
+    vkDestroySemaphore(device, renderFrame->renderCompleteSemaphore, allocator);
+    renderFrame->imageAcquiredSemaphore = renderFrame->renderCompleteSemaphore = VK_NULL_HANDLE;
+}
+
+void Renderer::Impl::next_frame()
+{
+    if (rebuildSwapchain)
     {
-        printf("GLFW: Vulkan not supported\n");
+        glfwGetFramebufferSize(glfwWindow, &surfaceWidth, &surfaceWidth);
+        if (surfaceWidth > 0 && surfaceHeight > 0)
+        {
+            create_or_resize_window_swapchain();
+            frameIndex = 0;
+            rebuildSwapchain = false;
+        }
+    }
+}
+
+void Renderer::Impl::present_frame()
+{
+     if (rebuildSwapchain)
+     {
+        return;
+     }
+    VkSemaphore render_complete_semaphore = frames[semaphoreIndex].renderCompleteSemaphore;
+    VkPresentInfoKHR info = {};
+    info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+    info.waitSemaphoreCount = 1;
+    info.pWaitSemaphores = &render_complete_semaphore;
+    info.swapchainCount = 1;
+    info.pSwapchains = &swapchain;
+    info.pImageIndices = &frameIndex;
+    VkResult res = vkQueuePresentKHR(queue, &info);
+    if (res == VK_ERROR_OUT_OF_DATE_KHR)
+    {
+        rebuildSwapchain = true;
+        return;
+    }
+    check_vulkan_result(res);
+    semaphoreIndex = (semaphoreIndex + 1) % imageCount; 
+}
+
+void Renderer::Impl::startup_imgui()
+{
+    ImGui_ImplVulkan_InitInfo init_info = {};
+    init_info.Instance = instance;
+    init_info.PhysicalDevice = physicalDevice;
+    init_info.Device = device;
+    init_info.QueueFamily = queueFamilyIndices.graphics;
+    init_info.Queue = queue;
+    init_info.PipelineCache = pipelineCache;
+    init_info.DescriptorPool = descriptorPool;
+    init_info.Allocator = allocator;
+    init_info.MinImageCount = minImageCount;
+    init_info.ImageCount = imageCount;
+    init_info.CheckVkResultFn = check_vulkan_result;
+    ImGui_ImplVulkan_Init(&init_info, renderPass);
+
+    // upload the fonts
+    {
+        // Use any command queue
+        VkCommandPool commandPool = frames[frameIndex].commandPool;
+        VkCommandBuffer commandBuffer = frames[frameIndex].commandBuffer;
+
+        VK_CHECK_RESULT(vkResetCommandPool(device, commandPool, 0));
+        
+        VkCommandBufferBeginInfo beginInfo = {};
+        beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        beginInfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+        VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &beginInfo));
+
+        ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
+
+        VkSubmitInfo endInfo = {};
+        endInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        endInfo.commandBufferCount = 1;
+        endInfo.pCommandBuffers = &commandBuffer;
+        VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));
+        
+        VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &endInfo, VK_NULL_HANDLE));
+        
+        VK_CHECK_RESULT(vkDeviceWaitIdle(device));
+
+        ImGui_ImplVulkan_DestroyFontUploadObjects();
+    }
+}
+
+void Renderer::Impl::shutdow_imgui()
+{
+    vkDeviceWaitIdle(device);
+    ImGui_ImplVulkan_Shutdown();
+}
+
+void Renderer::Impl::render_imgui()
+{
+    VkSemaphore imageAcquiredSemaphore = frames[semaphoreIndex].imageAcquiredSemaphore;
+    VkSemaphore renderCompleteSemaphore = frames[semaphoreIndex].renderCompleteSemaphore;
+    VkResult res = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &frameIndex);
+    if (res == VK_ERROR_OUT_OF_DATE_KHR)
+    {
+        rebuildSwapchain = true;
         return;
     }
-    uint32_t extensionsCount = 0;
-    const char** extensions = glfwGetRequiredInstanceExtensions(&extensionsCount);
-    */
+    check_vulkan_result(res);
+
+    RenderFrame* frame = &frames[frameIndex];
+
+    VK_CHECK_RESULT(vkWaitForFences(device, 1, &frame->fence, VK_TRUE, UINT64_MAX));
+    VK_CHECK_RESULT(vkResetFences(device, 1, &frame->fence));
+    VK_CHECK_RESULT(vkResetCommandPool(device, frame->commandPool, 0));
+
+    // begin a command buffer
+    {
+        VkCommandBufferBeginInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+        VK_CHECK_RESULT(vkBeginCommandBuffer(frame->commandBuffer, &info));
+    }
+
+    // begin a render pass
+    {
+        VkRenderPassBeginInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+        info.renderPass = renderPass;
+        info.framebuffer = frame->framebuffer;
+        info.renderArea.extent.width = surfaceWidth;
+        info.renderArea.extent.height = surfaceHeight;
+        info.clearValueCount = 1;
+        info.pClearValues = &clearValue;
+        vkCmdBeginRenderPass(frame->commandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
+    }
+
+    // build commands from imgui draw data
+    ImDrawData* drawData = ImGui::GetDrawData();
+    ImGui_ImplVulkan_RenderDrawData(drawData, frame->commandBuffer);
+
+    // end the render pass
+    vkCmdEndRenderPass(frame->commandBuffer);
+    {
+        VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+        VkSubmitInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        info.waitSemaphoreCount = 1;
+        info.pWaitSemaphores = &imageAcquiredSemaphore;
+        info.pWaitDstStageMask = &wait_stage;
+        info.commandBufferCount = 1;
+        info.pCommandBuffers = &frame->commandBuffer;
+        info.signalSemaphoreCount = 1;
+        info.pSignalSemaphores = &renderCompleteSemaphore;
+
+        VK_CHECK_RESULT(vkEndCommandBuffer(frame->commandBuffer));
+        VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &info, frame->fence));
+    }
 }
 
 void Renderer::shutdown()
 {
+    _impl->shutdow_imgui();
+    _impl->shutdown_window_surface();
+    _impl->shutdown_vulkan();
+    unload_vulkan_library();
+}
+
+void Renderer::next_frame()
+{
+    _impl->next_frame();
 }
 
+void Renderer::update()
+{
+    // update the scene with ecs
+}
+
+void Renderer::render_frame()
+{
+    _impl->render_imgui();
+
+    ImGuiIO& io = ImGui::GetIO();
+    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+    {
+        ImGui::UpdatePlatformWindows();
+        ImGui::RenderPlatformWindowsDefault();
+    }
+}
+
+void Renderer::present_frame()
+{
+    _impl->present_frame();
+}
 }

+ 369 - 0
source/gameplay/RendererVK.h

@@ -0,0 +1,369 @@
+#pragma once
+
+#include "Defines.h"
+#include "Logging.h"
+#include "vulkan/vulkan.h"
+
+#if GP_PLATFORM_WINDOWS
+#   define VK_USE_PLATFORM_WIN32_KHR
+#elif GP_PLATFORM_LINUX
+#   define VK_USE_PLATFORM_XLIB_KHR
+#elif GP_PLATFORM_MACOS
+#   define VK_USE_PLATFORM_MACOS_MVK
+#elif GP_PLATFORM_IOS
+#   define VK_USE_PLATFORM_IOS_MVK
+#elif GP_PLATFORM_ANDROID
+#   define VK_USE_PLATFORM_ANDROID_KHR
+#else
+#   error Unsupported renderer platform
+#endif
+
+namespace gameplay
+{
+const std::vector<const char*> vulkan_validation_layers = { "VK_LAYER_LUNARG_standard_validation" };
+
+std::string to_vulkan_error_string(VkResult result);
+
+static void check_vulkan_result(VkResult res)
+{
+    if (res != VK_SUCCESS)
+    {
+        GP_LOG_ERROR("Fatal: VkResult is: %s, file: %s, line: %s", to_vulkan_error_string(res),  __FILE__, __LINE__);
+        GP_ASSERT(res == VK_SUCCESS);
+        if (res < 0)
+            abort();
+    }
+}
+
+#define VK_CHECK_RESULT(f)                                                                                            \
+{                                                                                                                     \
+    VkResult res = (f);                                                                                               \
+    if (res != VK_SUCCESS)                                                                                            \
+    {                                                                                                                 \
+        GP_LOG_ERROR("Fatal: VkResult is: %s, file: %s, line: %s", to_vulkan_error_string(res),  __FILE__, __LINE__); \
+        GP_ASSERT(res == VK_SUCCESS);                                                                                 \
+        if (res < 0)                                                                                                  \
+            abort();                                                                                                  \
+    }                                                                                                                 \
+}
+
+VkBool32 log_vulkan_validation_debug_report(VkDebugReportFlagsEXT flags, 
+                                            VkDebugReportObjectTypeEXT objType,
+                                            uint64_t srcObject, 
+                                            size_t location, 
+                                            int32_t msgCode,
+                                            const char* layerPrefix, 
+                                            const char* msg,
+                                            void* userPtr)
+{
+    std::string prefix("");
+    if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
+        prefix += "ERROR:";
+    if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
+        prefix += "WARNING:";
+    if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
+        prefix += "PERFORMANCE:";
+    if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
+        prefix += "INFO:";
+    if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
+        prefix += "DEBUG:";
+    GP_LOG_DEBUG("%s [%s] Code: %d:%s", prefix.c_str(), layerPrefix, msgCode, msg);
+    return VK_FALSE;
+}
+
+#if GP_PLATFORM_ANDROID
+
+#include <android/log.h>
+#include <dlfcn.h>
+
+PFN_vkCreateInstance vkCreateInstance;
+PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
+PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+PFN_vkCreateDevice vkCreateDevice;
+PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
+PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
+PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
+PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
+PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
+PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
+PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
+PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
+PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
+PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
+PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
+PFN_vkCreateShaderModule vkCreateShaderModule;
+PFN_vkCreateBuffer vkCreateBuffer;
+PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
+PFN_vkMapMemory vkMapMemory;
+PFN_vkUnmapMemory vkUnmapMemory;
+PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
+PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
+PFN_vkBindBufferMemory vkBindBufferMemory;
+PFN_vkDestroyBuffer vkDestroyBuffer;
+PFN_vkAllocateMemory vkAllocateMemory;
+PFN_vkBindImageMemory vkBindImageMemory;
+PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
+PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
+PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
+PFN_vkCmdCopyImage vkCmdCopyImage;
+PFN_vkCmdBlitImage vkCmdBlitImage;
+PFN_vkCmdClearAttachments vkCmdClearAttachments;
+PFN_vkCreateSampler vkCreateSampler;
+PFN_vkDestroySampler vkDestroySampler;
+PFN_vkDestroyImage vkDestroyImage;
+PFN_vkFreeMemory vkFreeMemory;
+PFN_vkCreateRenderPass vkCreateRenderPass;
+PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
+PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
+PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
+PFN_vkCreateImage vkCreateImage;
+PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
+PFN_vkCreateImageView vkCreateImageView;
+PFN_vkDestroyImageView vkDestroyImageView;
+PFN_vkCreateSemaphore vkCreateSemaphore;
+PFN_vkDestroySemaphore vkDestroySemaphore;
+PFN_vkCreateFence vkCreateFence;
+PFN_vkDestroyFence vkDestroyFence;
+PFN_vkWaitForFences vkWaitForFences;
+PFN_vkResetFences vkResetFences;
+PFN_vkCreateCommandPool vkCreateCommandPool;
+PFN_vkDestroyCommandPool vkDestroyCommandPool;
+PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
+PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
+PFN_vkEndCommandBuffer vkEndCommandBuffer;
+PFN_vkGetDeviceQueue vkGetDeviceQueue;
+PFN_vkQueueSubmit vkQueueSubmit;
+PFN_vkQueueWaitIdle vkQueueWaitIdle;
+PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
+PFN_vkCreateFramebuffer vkCreateFramebuffer;
+PFN_vkCreatePipelineCache vkCreatePipelineCache;
+PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
+PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
+PFN_vkCreateComputePipelines vkCreateComputePipelines;
+PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
+PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
+PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
+PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
+PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
+PFN_vkCmdBindPipeline vkCmdBindPipeline;
+PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
+PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
+PFN_vkCmdSetViewport vkCmdSetViewport;
+PFN_vkCmdSetScissor vkCmdSetScissor;
+PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
+PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
+PFN_vkCmdPushConstants vkCmdPushConstants;
+PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
+PFN_vkCmdDraw vkCmdDraw;
+PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
+PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
+PFN_vkCmdDispatch vkCmdDispatch;
+PFN_vkDestroyPipeline vkDestroyPipeline;
+PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
+PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
+PFN_vkDestroyDevice vkDestroyDevice;
+PFN_vkDestroyInstance vkDestroyInstance;
+PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
+PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
+PFN_vkDestroyRenderPass vkDestroyRenderPass;
+PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
+PFN_vkDestroyShaderModule vkDestroyShaderModule;
+PFN_vkDestroyPipelineCache vkDestroyPipelineCache;
+PFN_vkCreateQueryPool vkCreateQueryPool;
+PFN_vkDestroyQueryPool vkDestroyQueryPool;
+PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
+PFN_vkCmdBeginQuery vkCmdBeginQuery;
+PFN_vkCmdEndQuery vkCmdEndQuery;
+PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
+PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
+PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR;
+PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
+void* libVulkan;
+#endif
+
+
+static void load_vulkan_library()
+{
+#if GP_PLATFORM_ANDROID
+    __android_log_print(ANDROID_LOG_INFO, "vulkanandroid", "Loading libvulkan.so...\n");
+
+    // Load the vulkan library
+    libVulkan = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
+    if (!libVulkan)
+    {
+        __android_log_print(ANDROID_LOG_INFO, "vulkanandroid", "Could not load vulkan library : %s!\n", dlerror());
+        return false;
+    }
+
+    // Load base function pointers
+    vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(dlsym(libVulkan, "vkEnumerateInstanceExtensionProperties"));
+    vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(dlsym(libVulkan, "vkEnumerateInstanceLayerProperties"));
+    vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(dlsym(libVulkan, "vkCreateInstance"));
+    vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(libVulkan, "vkGetInstanceProcAddr"));
+    vkGetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(dlsym(libVulkan, "vkGetDeviceProcAddr"));
+#elif GP_PLATFORM_LINUX
+    //initxcbConnection();
+#endif
+}
+
+static void unload_vulkan_library()
+{
+#if GP_PLATFORM_ANDROID
+    dlclose(libVulkan);
+#endif
+}
+
+static void load_vulkan_functions()
+{
+#if GP_PLATFORM_ANDROID
+    __android_log_print(ANDROID_LOG_INFO, "vulkanandroid", "Loading instance based function pointers...\n");
+    vkEnumeratePhysicalDevices = reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"));
+    vkGetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties"));
+    vkEnumerateDeviceLayerProperties = reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(vkGetInstanceProcAddr(instance, "vkEnumerateDeviceLayerProperties"));
+    vkEnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(vkGetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties"));
+    vkGetPhysicalDeviceQueueFamilyProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties"));
+    vkGetPhysicalDeviceFeatures = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures"));
+    vkCreateDevice = reinterpret_cast<PFN_vkCreateDevice>(vkGetInstanceProcAddr(instance, "vkCreateDevice"));
+    vkGetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties"));
+    vkGetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties"));
+    vkCmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(vkGetInstanceProcAddr(instance, "vkCmdPipelineBarrier"));
+    vkCreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(vkGetInstanceProcAddr(instance, "vkCreateShaderModule"));
+    vkCreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(vkGetInstanceProcAddr(instance, "vkCreateBuffer"));
+    vkGetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(vkGetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements"));
+    vkMapMemory = reinterpret_cast<PFN_vkMapMemory>(vkGetInstanceProcAddr(instance, "vkMapMemory"));
+    vkUnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(vkGetInstanceProcAddr(instance, "vkUnmapMemory"));
+    vkFlushMappedMemoryRanges = reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(vkGetInstanceProcAddr(instance, "vkFlushMappedMemoryRanges"));
+    vkInvalidateMappedMemoryRanges = reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(vkGetInstanceProcAddr(instance, "vkInvalidateMappedMemoryRanges"));
+    vkBindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(vkGetInstanceProcAddr(instance, "vkBindBufferMemory"));
+    vkDestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(vkGetInstanceProcAddr(instance, "vkDestroyBuffer"));
+    vkAllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(vkGetInstanceProcAddr(instance, "vkAllocateMemory"));
+    vkFreeMemory = reinterpret_cast<PFN_vkFreeMemory>(vkGetInstanceProcAddr(instance, "vkFreeMemory"));
+    vkCreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(vkGetInstanceProcAddr(instance, "vkCreateRenderPass"));
+    vkCmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(vkGetInstanceProcAddr(instance, "vkCmdBeginRenderPass"));
+    vkCmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(vkGetInstanceProcAddr(instance, "vkCmdEndRenderPass"));
+    vkCmdExecuteCommands = reinterpret_cast<PFN_vkCmdExecuteCommands>(vkGetInstanceProcAddr(instance, "vkCmdExecuteCommands"));
+    vkCreateImage = reinterpret_cast<PFN_vkCreateImage>(vkGetInstanceProcAddr(instance, "vkCreateImage"));
+    vkGetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(vkGetInstanceProcAddr(instance, "vkGetImageMemoryRequirements"));
+    vkCreateImageView = reinterpret_cast<PFN_vkCreateImageView>(vkGetInstanceProcAddr(instance, "vkCreateImageView"));
+    vkDestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(vkGetInstanceProcAddr(instance, "vkDestroyImageView"));
+    vkBindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(vkGetInstanceProcAddr(instance, "vkBindImageMemory"));
+    vkGetImageSubresourceLayout = reinterpret_cast<PFN_vkGetImageSubresourceLayout>(vkGetInstanceProcAddr(instance, "vkGetImageSubresourceLayout"));
+    vkCmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(vkGetInstanceProcAddr(instance, "vkCmdCopyImage"));
+    vkCmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(vkGetInstanceProcAddr(instance, "vkCmdBlitImage"));
+    vkDestroyImage = reinterpret_cast<PFN_vkDestroyImage>(vkGetInstanceProcAddr(instance, "vkDestroyImage"));
+    vkCmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(vkGetInstanceProcAddr(instance, "vkCmdClearAttachments"));
+    vkCmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(vkGetInstanceProcAddr(instance, "vkCmdCopyBuffer"));
+    vkCmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(vkGetInstanceProcAddr(instance, "vkCmdCopyBufferToImage"));
+    vkCreateSampler = reinterpret_cast<PFN_vkCreateSampler>(vkGetInstanceProcAddr(instance, "vkCreateSampler"));
+    vkDestroySampler = reinterpret_cast<PFN_vkDestroySampler>(vkGetInstanceProcAddr(instance, "vkDestroySampler"));;
+    vkCreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(vkGetInstanceProcAddr(instance, "vkCreateSemaphore"));
+    vkDestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(vkGetInstanceProcAddr(instance, "vkDestroySemaphore"));
+    vkCreateFence = reinterpret_cast<PFN_vkCreateFence>(vkGetInstanceProcAddr(instance, "vkCreateFence"));
+    vkDestroyFence = reinterpret_cast<PFN_vkDestroyFence>(vkGetInstanceProcAddr(instance, "vkDestroyFence"));
+    vkWaitForFences = reinterpret_cast<PFN_vkWaitForFences>(vkGetInstanceProcAddr(instance, "vkWaitForFences"));
+    vkResetFences = reinterpret_cast<PFN_vkResetFences>(vkGetInstanceProcAddr(instance, "vkResetFences"));;
+    vkCreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(vkGetInstanceProcAddr(instance, "vkCreateCommandPool"));
+    vkDestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(vkGetInstanceProcAddr(instance, "vkDestroyCommandPool"));;
+    vkAllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(vkGetInstanceProcAddr(instance, "vkAllocateCommandBuffers"));
+    vkBeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(vkGetInstanceProcAddr(instance, "vkBeginCommandBuffer"));
+    vkEndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(vkGetInstanceProcAddr(instance, "vkEndCommandBuffer"));
+    vkGetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(vkGetInstanceProcAddr(instance, "vkGetDeviceQueue"));
+    vkQueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(vkGetInstanceProcAddr(instance, "vkQueueSubmit"));
+    vkQueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(vkGetInstanceProcAddr(instance, "vkQueueWaitIdle"));
+    vkDeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(vkGetInstanceProcAddr(instance, "vkDeviceWaitIdle"));
+    vkCreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(vkGetInstanceProcAddr(instance, "vkCreateFramebuffer"));
+    vkCreatePipelineCache = reinterpret_cast<PFN_vkCreatePipelineCache>(vkGetInstanceProcAddr(instance, "vkCreatePipelineCache"));
+    vkCreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(vkGetInstanceProcAddr(instance, "vkCreatePipelineLayout"));
+    vkCreateGraphicsPipelines = reinterpret_cast<PFN_vkCreateGraphicsPipelines>(vkGetInstanceProcAddr(instance, "vkCreateGraphicsPipelines"));
+    vkCreateComputePipelines = reinterpret_cast<PFN_vkCreateComputePipelines>(vkGetInstanceProcAddr(instance, "vkCreateComputePipelines"));
+    vkCreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(vkGetInstanceProcAddr(instance, "vkCreateDescriptorPool"));
+    vkCreateDescriptorSetLayout = reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(vkGetInstanceProcAddr(instance, "vkCreateDescriptorSetLayout"));
+    vkAllocateDescriptorSets = reinterpret_cast<PFN_vkAllocateDescriptorSets>(vkGetInstanceProcAddr(instance, "vkAllocateDescriptorSets"));
+    vkUpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(vkGetInstanceProcAddr(instance, "vkUpdateDescriptorSets"));
+    vkCmdBindDescriptorSets = reinterpret_cast<PFN_vkCmdBindDescriptorSets>(vkGetInstanceProcAddr(instance, "vkCmdBindDescriptorSets"));
+    vkCmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(vkGetInstanceProcAddr(instance, "vkCmdBindPipeline"));
+    vkCmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(vkGetInstanceProcAddr(instance, "vkCmdBindVertexBuffers"));
+    vkCmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(vkGetInstanceProcAddr(instance, "vkCmdBindIndexBuffer"));
+    vkCmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(vkGetInstanceProcAddr(instance, "vkCmdSetViewport"));
+    vkCmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(vkGetInstanceProcAddr(instance, "vkCmdSetScissor"));
+    vkCmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(vkGetInstanceProcAddr(instance, "vkCmdSetLineWidth"));
+    vkCmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(vkGetInstanceProcAddr(instance, "vkCmdSetDepthBias"));
+    vkCmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(vkGetInstanceProcAddr(instance, "vkCmdPushConstants"));;
+    vkCmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(vkGetInstanceProcAddr(instance, "vkCmdDrawIndexed"));
+    vkCmdDraw = reinterpret_cast<PFN_vkCmdDraw>(vkGetInstanceProcAddr(instance, "vkCmdDraw"));
+    vkCmdDrawIndexedIndirect = reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(vkGetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirect"));
+    vkCmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(vkGetInstanceProcAddr(instance, "vkCmdDrawIndirect"));
+    vkCmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(vkGetInstanceProcAddr(instance, "vkCmdDispatch"));
+    vkDestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(vkGetInstanceProcAddr(instance, "vkDestroyPipeline"));
+    vkDestroyPipelineLayout = reinterpret_cast<PFN_vkDestroyPipelineLayout>(vkGetInstanceProcAddr(instance, "vkDestroyPipelineLayout"));;
+    vkDestroyDescriptorSetLayout = reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(vkGetInstanceProcAddr(instance, "vkDestroyDescriptorSetLayout"));
+    vkDestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(vkGetInstanceProcAddr(instance, "vkDestroyDevice"));
+    vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(vkGetInstanceProcAddr(instance, "vkDestroyInstance"));
+    vkDestroyDescriptorPool = reinterpret_cast<PFN_vkDestroyDescriptorPool>(vkGetInstanceProcAddr(instance, "vkDestroyDescriptorPool"));
+    vkFreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(vkGetInstanceProcAddr(instance, "vkFreeCommandBuffers"));
+    vkDestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(vkGetInstanceProcAddr(instance, "vkDestroyRenderPass"));
+    vkDestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(vkGetInstanceProcAddr(instance, "vkDestroyFramebuffer"));
+    vkDestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(vkGetInstanceProcAddr(instance, "vkDestroyShaderModule"));
+    vkDestroyPipelineCache = reinterpret_cast<PFN_vkDestroyPipelineCache>(vkGetInstanceProcAddr(instance, "vkDestroyPipelineCache"));
+    vkCreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(vkGetInstanceProcAddr(instance, "vkCreateQueryPool"));
+    vkDestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(vkGetInstanceProcAddr(instance, "vkDestroyQueryPool"));
+    vkGetQueryPoolResults = reinterpret_cast<PFN_vkGetQueryPoolResults>(vkGetInstanceProcAddr(instance, "vkGetQueryPoolResults"));
+    vkCmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(vkGetInstanceProcAddr(instance, "vkCmdBeginQuery"));
+    vkCmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(vkGetInstanceProcAddr(instance, "vkCmdEndQuery"));
+    vkCmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(vkGetInstanceProcAddr(instance, "vkCmdResetQueryPool"));
+    vkCmdCopyQueryPoolResults = reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(vkGetInstanceProcAddr(instance, "vkCmdCopyQueryPoolResults"));
+    vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr(instance, "vkCreateAndroidSurfaceKHR"));
+    vkDestroySurfaceKHR = reinterpret_cast<PFN_vkDestroySurfaceKHR>(vkGetInstanceProcAddr(instance, "vkDestroySurfaceKHR"));
+#endif
+}
+
+PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
+PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
+PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT;
+PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
+PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
+PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
+PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
+PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
+PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
+PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
+PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
+PFN_vkQueuePresentKHR vkQueuePresentKHR;
+
+
+std::string to_vulkan_error_string(VkResult result)
+{
+    switch (result)
+    {
+#define STR(r) case VK_ ##r: return #r
+        STR(NOT_READY);
+        STR(TIMEOUT);
+        STR(EVENT_SET);
+        STR(EVENT_RESET);
+        STR(INCOMPLETE);
+        STR(ERROR_OUT_OF_HOST_MEMORY);
+        STR(ERROR_OUT_OF_DEVICE_MEMORY);
+        STR(ERROR_INITIALIZATION_FAILED);
+        STR(ERROR_DEVICE_LOST);
+        STR(ERROR_MEMORY_MAP_FAILED);
+        STR(ERROR_LAYER_NOT_PRESENT);
+        STR(ERROR_EXTENSION_NOT_PRESENT);
+        STR(ERROR_FEATURE_NOT_PRESENT);
+        STR(ERROR_INCOMPATIBLE_DRIVER);
+        STR(ERROR_TOO_MANY_OBJECTS);
+        STR(ERROR_FORMAT_NOT_SUPPORTED);
+        STR(ERROR_SURFACE_LOST_KHR);
+        STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
+        STR(SUBOPTIMAL_KHR);
+        STR(ERROR_OUT_OF_DATE_KHR);
+        STR(ERROR_INCOMPATIBLE_DISPLAY_KHR);
+        STR(ERROR_VALIDATION_FAILED_EXT);
+        STR(ERROR_INVALID_SHADER_NV);
+#undef STR
+    default:
+        return "UNKNOWN_ERROR";
+    }
+}
+
+}
+

+ 63 - 1
source/gameplay/UI.cpp

@@ -1,23 +1,85 @@
 #include "UI.h"
-#include "imgui.h"
+#include "Config.h"
+#include "FileSystem.h"
+#include "Window.h"
+#include "WindowingGLFW.h"
+#include "imgui_impl_glfw.h"
+
 
 namespace gameplay
 {
 
+struct UI::Impl
+{
+
+};
+
 UI::UI()
 {
+    _impl = new UI::Impl();
 }
 
 UI::~UI()
 {
+    GP_SAFE_DELETE(_impl);
 }
 
+
 void UI::startup()
 {
+    // setup imgui for ui
+    IMGUI_CHECKVERSION();
+    ImGui::CreateContext();
+    ImGuiIO& io = ImGui::GetIO();
+    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
+    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
+    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
+    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
+    //io.ConfigViewportsNoAutoMerge = true;
+    //io.ConfigViewportsNoTaskBarIcon = true;
+
+    ImGui::StyleColorsDark();
+    ImGuiStyle& style = ImGui::GetStyle();
+    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+    {
+        style.WindowRounding = 0.0f;
+        style.Colors[ImGuiCol_WindowBg].w = 1.0f;
+    }
+
+    auto window = App::get_app()->get_main_window()->handle->glfwWindow;
+    ImGui_ImplGlfw_InitForVulkan(window, true);
+
+    // load the system fonts
+    auto config = App::get_app()->get_config();
+    std::string defaultFont = config->get_string("ui.defaultFont", "");
+    if (defaultFont.size() > 0 && App::get_app()->resolve_resource_path(defaultFont))
+    {
+        auto fs = App::get_app()->get_file_system();
+        if (fs->exists(defaultFont.c_str()))
+        {
+            io.Fonts->AddFontFromFileTTF(defaultFont.c_str(), 16.0f);
+        }
+    }
 }
 
 void UI::shutdown()
 {
+    ImGui_ImplGlfw_Shutdown();
+    ImGui::DestroyContext();
 }
 
+void UI::update()
+{
+    ImGui_ImplGlfw_NewFrame();
+    ImGui::NewFrame();
+
+    // sample content directly with imgui for now...
+    static bool show_demo_window = true;
+    if (show_demo_window)
+    {
+        ImGui::ShowDemoWindow(&show_demo_window);
+    }
+
+    ImGui::Render();
+}
 }