text: /*
  * Vulkan Example base class
  *
  * Copyright (C) by Sascha Willems - www.saschawillems.de
  *
  * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
  */
  
  #pragma once
  
  #ifdef _WIN32
  #pragma comment(linker, "/subsystem:console")
  #include <windows.h>
  #include <fcntl.h>
  #include <io.h>
  #include <ShellScalingAPI.h>
  #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
  #include <android/native_activity.h>
  #include <android/asset_manager.h>
  #include <android_native_app_glue.h>
  #include <sys/system_properties.h>
  #include "VulkanAndroid.h"
  #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
  #include <wayland-client.h>
  #include "xdg-shell-client-protocol.h"
  #elif defined(_DIRECT2DISPLAY)
  //
  #elif defined(VK_USE_PLATFORM_XCB_KHR)
  #include <xcb/xcb.h>
  #endif
  
  #include <iostream>
  #include <chrono>
  #include <sys/stat.h>
  
  #define GLM_FORCE_RADIANS
  #define GLM_FORCE_DEPTH_ZERO_TO_ONE
  #define GLM_ENABLE_EXPERIMENTAL
  #include <glm/glm.hpp>
  #include <string>
  #include <array>
  #include <numeric>
  
  #include "vulkan/vulkan.h"
  
  #include "keycodes.hpp"
  #include "VulkanTools.h"
  #include "VulkanDebug.h"
  #include "VulkanUIOverlay.h"
  
  #include "VulkanInitializers.hpp"
  #include "VulkanDevice.hpp"
  #include "VulkanSwapChain.hpp"
  #include "camera.hpp"
  #include "benchmark.hpp"
  
  #include "vulkanexamplebase_def.h"
  
  class VK_BASE_EXPORT VulkanExampleBase
  {
  protected:
  	// Get window title with example name, device, et.
  	std::string getWindowTitle();
  	/** brief Indicates that the view (position, rotation) has changed and buffers containing camera matrices need to be updated */
  	bool viewUpdated = false;
  	// Destination dimensions for resizing the window
  	uint32_t destWidth;
  	uint32_t destHeight;
  	bool resizing = false;
  	// Called if the window is resized and some resources have to be recreatesd
  	void windowResize();
  	void handleMouseMove(int32_t x, int32_t y);
  protected:
  	// Frame counter to display fps
  	uint32_t frameCounter = 0;
  	uint32_t lastFPS = 0;
  	std::chrono::time_point<std::chrono::high_resolution_clock> lastTimestamp;
  	// Vulkan instance, stores all per-application states
  	VkInstance instance;
  	// Physical device (GPU) that Vulkan will ise
  	VkPhysicalDevice physicalDevice;
  	// Stores physical device properties (for e.g. checking device limits)
  	VkPhysicalDeviceProperties deviceProperties;
  	// Stores the features available on the selected physical device (for e.g. checking if a feature is available)
  	VkPhysicalDeviceFeatures deviceFeatures;
  	// Stores all available memory (type) properties for the physical device
  	VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
  	/** @brief Set of physical device features to be enabled for this example (must be set in the derived constructor) */
  	VkPhysicalDeviceFeatures enabledFeatures{};
  	/** @brief Set of device extensions to be enabled for this example (must be set in the derived constructor) */
  	std::vector<const char*> enabledDeviceExtensions;
  	std::vector<const char*> enabledInstanceExtensions;
  	/** @brief Optional pNext structure for passing extension structures to device creation */
  	void* deviceCreatepNextChain = nullptr;
  	/** @brief Logical device, application's view of the physical device (GPU) */
  	VkDevice device;
  	// Handle to the device graphics queue that command buffers are submitted to
  	VkQueue queue;
  	// Depth buffer format (selected during Vulkan initialization)
  	VkFormat depthFormat;
  	// Command buffer pool
  	VkCommandPool cmdPool;
  	/** @brief Pipeline stages used to wait at for graphics queue submissions */
  	VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  	// Contains command buffers and semaphores to be presented to the queue
  	VkSubmitInfo submitInfo;
  	// Command buffers used for rendering
  	std::vector<VkCommandBuffer> drawCmdBuffers;
  	// Global render pass for frame buffer writes
  	VkRenderPass renderPass;
  	// List of available frame buffers (same as number of swap chain images)
  	std::vector<VkFramebuffer>frameBuffers;
  	// Active frame buffer index
  	uint32_t currentBuffer = 0;
  	// Descriptor set pool
  	VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
  	// List of shader modules created (stored for cleanup)
  	std::vector<VkShaderModule> shaderModules;
  	// Pipeline cache object
  	VkPipelineCache pipelineCache;
  	// Wraps the swap chain to present images (framebuffers) to the windowing system
  	VulkanSwapChain swapChain;
  	// Synchronization semaphores
  	struct {
  		// Swap chain image presentation
  		VkSemaphore presentComplete;
  		// Command buffer submission and execution
  		VkSemaphore renderComplete;
  	} semaphores;
  	std::vector<VkFence> waitFences;
  public: 
  	bool prepared = false;
  	uint32_t width = 1280;
  	uint32_t height = 720;
  
  	vks::UIOverlay UIOverlay;
  
  	/** @brief Last frame time measured using a high performance timer (if available) */
  	float frameTimer = 1.0f;
  	/** @brief Returns os specific base asset path (for shaders, models, textures) */
  	const std::string getAssetPath();
  
  	vks::Benchmark benchmark;
  
  	/** @brief Encapsulated physical and logical vulkan device */
  	vks::VulkanDevice *vulkanDevice;
  
  	/** @brief Example settings that can be changed e.g. by command line arguments */
  	struct Settings {
  		/** @brief Activates validation layers (and message output) when set to true */
  		bool validation = false;
  		/** @brief Set to true if fullscreen mode has been requested via command line */
  		bool fullscreen = false;
  		/** @brief Set to true if v-sync will be forced for the swapchain */
  		bool vsync = false;
  		/** @brief Enable UI overlay */
  		bool overlay = false;
  	} settings;
  
  	VkClearColorValue defaultClearColor = { { 0.025f, 0.025f, 0.025f, 1.0f } };
  
  	float zoom = 0;
  
  	static std::vector<const char*> args;
  
  	// Defines a frame rate independent timer value clamped from -1.0...1.0
  	// For use in animations, rotations, etc.
  	float timer = 0.0f;
  	// Multiplier for speeding up (or slowing down) the global timer
  	float timerSpeed = 0.25f;
  	
  	bool paused = false;
  
  	// Use to adjust mouse rotation speed
  	float rotationSpeed = 1.0f;
  	// Use to adjust mouse zoom speed
  	float zoomSpeed = 1.0f;
  
  	Camera camera;
  
  	glm::vec3 rotation = glm::vec3();
  	glm::vec3 cameraPos = glm::vec3();
  	glm::vec2 mousePos;
  
  	std::string title = "Vulkan Example";
  	std::string name = "vulkanExample";
  	uint32_t apiVersion = VK_API_VERSION_1_0;
  
  	struct 
  	{
  		VkImage image;
  		VkDeviceMemory mem;
  		VkImageView view;
  	} depthStencil;
  
  	struct {
  		glm::vec2 axisLeft = glm::vec2(0.0f);
  		glm::vec2 axisRight = glm::vec2(0.0f);
  	} gamePadState;
  
  	struct {
  		bool left = false;
  		bool right = false;
  		bool middle = false;
  	} mouseButtons;
  
  	// OS specific 
  #if defined(_WIN32)
  	HWND window;
      HINSTANCE windowInstance;
  #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
  	// true if application has focused, false if moved to background
  	bool focused = false;
  	struct TouchPos {
  		int32_t x;
  		int32_t y;
  	} touchPos;
  	bool touchDown = false;
  	double touchTimer = 0.0;
  	int64_t lastTapTime = 0;
  	/** @brief Product model and manufacturer of the Android device (via android.Product*) */
  	std::string androidProduct;
  #elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
  	void* view;
  #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
  	wl_display *display = nullptr;
  	wl_registry *registry = nullptr;
  	wl_compositor *compositor = nullptr;
  	struct xdg_wm_base *shell = nullptr;
  	wl_seat *seat = nullptr;
  	wl_pointer *pointer = nullptr;
  	wl_keyboard *keyboard = nullptr;
  	wl_surface *surface = nullptr;
  	struct xdg_surface *xdg_surface;
  	struct xdg_toplevel *xdg_toplevel;
  	bool quit = false;
  	bool configured = false;
  
  #elif defined(_DIRECT2DISPLAY)
  	bool quit = false;
  #elif defined(VK_USE_PLATFORM_XCB_KHR)
  	bool quit = false;
  	xcb_connection_t *connection;
  	xcb_screen_t *screen;
  	xcb_window_t window;
  	xcb_intern_atom_reply_t *atom_wm_delete_window;
  #endif
  
  	// Default ctor
  	VulkanExampleBase(bool enableValidation = false);
  
  	// dtor
  	virtual ~VulkanExampleBase();
  
  	// Setup the vulkan instance, enable required extensions and connect to the physical device (GPU)
  	bool initVulkan();
  
  #if defined(_WIN32)
  	void setupConsole(std::string title);
  	void setupDPIAwareness();
  	HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc);
  	void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
  	static int32_t handleAppInput(struct android_app* app, AInputEvent* event);
  	static void handleAppCommand(android_app* app, int32_t cmd);
  #elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
  	void* setupWindow(void* view);
  #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
  	struct xdg_surface *setupWindow();
  	void initWaylandConnection();
  	void setSize(int width, int height);
  	static void registryGlobalCb(void *data, struct wl_registry *registry,
  			uint32_t name, const char *interface, uint32_t version);
  	void registryGlobal(struct wl_registry *registry, uint32_t name,
  			const char *interface, uint32_t version);
  	static void registryGlobalRemoveCb(void *data, struct wl_registry *registry,
  			uint32_t name);
  	static void seatCapabilitiesCb(void *data, wl_seat *seat, uint32_t caps);
  	void seatCapabilities(wl_seat *seat, uint32_t caps);
  	static void pointerEnterCb(void *data, struct wl_pointer *pointer,
  			uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
  			wl_fixed_t sy);
  	static void pointerLeaveCb(void *data, struct wl_pointer *pointer,
  			uint32_t serial, struct wl_surface *surface);
  	static void pointerMotionCb(void *data, struct wl_pointer *pointer,
  			uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
  	void pointerMotion(struct wl_pointer *pointer,
  			uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
  	static void pointerButtonCb(void *data, struct wl_pointer *wl_pointer,
  			uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
  	void pointerButton(struct wl_pointer *wl_pointer,
  			uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
  	static void pointerAxisCb(void *data, struct wl_pointer *wl_pointer,
  			uint32_t time, uint32_t axis, wl_fixed_t value);
  	void pointerAxis(struct wl_pointer *wl_pointer,
  			uint32_t time, uint32_t axis, wl_fixed_t value);
  	static void keyboardKeymapCb(void *data, struct wl_keyboard *keyboard,
  			uint32_t format, int fd, uint32_t size);
  	static void keyboardEnterCb(void *data, struct wl_keyboard *keyboard,
  			uint32_t serial, struct wl_surface *surface, struct wl_array *keys);
  	static void keyboardLeaveCb(void *data, struct wl_keyboard *keyboard,
  			uint32_t serial, struct wl_surface *surface);
  	static void keyboardKeyCb(void *data, struct wl_keyboard *keyboard,
  			uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
  	void keyboardKey(struct wl_keyboard *keyboard,
  			uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
  	static void keyboardModifiersCb(void *data, struct wl_keyboard *keyboard,
  			uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
  			uint32_t mods_locked, uint32_t group);
  
  #elif defined(_DIRECT2DISPLAY)
  //
  #elif defined(VK_USE_PLATFORM_XCB_KHR)
  	xcb_window_t setupWindow();
  	void initxcbConnection();
  	void handleEvent(const xcb_generic_event_t *event);
  #endif
  	/**
  	* Create the application wide Vulkan instance
  	*
  	* @note Virtual, can be overriden by derived example class for custom instance creation
  	*/
  	virtual VkResult createInstance(bool enableValidation);
  
  	// Pure virtual render function (override in derived class)
  	virtual void render() = 0;
  	// Called when view change occurs
  	// Can be overriden in derived class to e.g. update uniform buffers 
  	// Containing view dependant matrices
  	virtual void viewChanged();
  	/** @brief (Virtual) Called after a key was pressed, can be used to do custom key handling */
  	virtual void keyPressed(uint32_t);
  	/** @brief (Virtual) Called after th mouse cursor moved and before internal events (like camera rotation) is handled */
  	virtual void mouseMoved(double x, double y, bool &handled);
  	// Called when the window has been resized
  	// Can be overriden in derived class to recreate or rebuild resources attached to the frame buffer / swapchain
  	virtual void windowResized();
  	// Pure virtual function to be overriden by the dervice class
  	// Called in case of an event where e.g. the framebuffer has to be rebuild and thus
  	// all command buffers that may reference this
  	virtual void buildCommandBuffers();
  
  	void createSynchronizationPrimitives();
  
  	// Creates a new (graphics) command pool object storing command buffers
  	void createCommandPool();
  	// Setup default depth and stencil views
  	virtual void setupDepthStencil();
  	// Create framebuffers for all requested swap chain images
  	// Can be overriden in derived class to setup a custom framebuffer (e.g. for MSAA)
  	virtual void setupFrameBuffer();
  	// Setup a default render pass
  	// Can be overriden in derived class to setup a custom render pass (e.g. for MSAA)
  	virtual void setupRenderPass();
  
  	/** @brief (Virtual) Called after the physical device features have been read, can be used to set features to enable on the device */
  	virtual void getEnabledFeatures();
  
  	// Connect and prepare the swap chain
  	void initSwapchain();
  	// Create swap chain images
  	void setupSwapChain();
  
  	// Check if command buffers are valid (!= VK_NULL_HANDLE)
  	bool checkCommandBuffers();
  	// Create command buffers for drawing commands
  	void createCommandBuffers();
  	// Destroy all command buffers and set their handles to VK_NULL_HANDLE
  	// May be necessary during runtime if options are toggled 
  	void destroyCommandBuffers();
  
  	// Command buffer creation
  	// Creates and returns a new command buffer
  	VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin);
  	// End the command buffer, submit it to the queue and free (if requested)
  	// Note : Waits for the queue to become idle
  	void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free);
  
  	// Create a cache pool for rendering pipelines
  	void createPipelineCache();
  
  	// Prepare commonly used Vulkan functions
  	virtual void prepare();
  
  	// Load a SPIR-V shader
  	VkPipelineShaderStageCreateInfo loadShader(std::string fileName, VkShaderStageFlagBits stage);
  	
  	// Start the main render loop
  	void renderLoop();
  
  	// Render one frame of a render loop on platforms that sync rendering
  	void renderFrame();
  
  	void updateOverlay();
  	void drawUI(const VkCommandBuffer commandBuffer);
  
  	// Prepare the frame for workload submission
  	// - Acquires the next image from the swap chain 
  	// - Sets the default wait and signal semaphores
  	void prepareFrame();
  
  	// Submit the frames' workload 
  	void submitFrame();
  
  	/** @brief (Virtual) Called when the UI overlay is updating, can be used to add custom elements to the overlay */
  	virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay);
  };
  
  // OS specific macros for the example main entry points
  #if defined(_WIN32)
  // Windows entry point
  #define VULKAN_EXAMPLE_MAIN()																		\
  VulkanExample *vulkanExample;																		\
  LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)						\
  {																									\
  	if (vulkanExample != NULL)																		\
  	{																								\
  		vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam);									\
  	}																								\
  	return (DefWindowProc(hWnd, uMsg, wParam, lParam));												\
  }																									\
  int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)									\
  {																									\
  	for (int32_t i = 0; i < __argc; i++) { VulkanExample::args.push_back(__argv[i]); };  			\
  	vulkanExample = new VulkanExample();															\
  	vulkanExample->initVulkan();																	\
  	vulkanExample->setupWindow(hInstance, WndProc);													\
  	vulkanExample->prepare();																		\
  	vulkanExample->renderLoop();																	\
  	delete(vulkanExample);																			\
  	return 0;																						\
  }																									
  #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
  // Android entry point
  #define VULKAN_EXAMPLE_MAIN()																		\
  VulkanExample *vulkanExample;																		\
  void android_main(android_app* state)																\
  {																									\
  	vulkanExample = new VulkanExample();															\
  	state->userData = vulkanExample;																\
  	state->onAppCmd = VulkanExample::handleAppCommand;												\
  	state->onInputEvent = VulkanExample::handleAppInput;											\
  	androidApp = state;																				\
  	vks::android::getDeviceConfig();																\
  	vulkanExample->renderLoop();																	\
  	delete(vulkanExample);																			\
  }
  #elif defined(_DIRECT2DISPLAY)
  // Linux entry point with direct to display wsi
  #define VULKAN_EXAMPLE_MAIN()																		\
  VulkanExample *vulkanExample;																		\
  static void handleEvent()                                											\
  {																									\
  }																									\
  int main(const int argc, const char *argv[])													    \
  {																									\
  	for (size_t i = 0; i < argc; i++) { VulkanExample::args.push_back(argv[i]); };  				\
  	vulkanExample = new VulkanExample();															\
  	vulkanExample->initVulkan();																	\
  	vulkanExample->prepare();																		\
  	vulkanExample->renderLoop();																	\
  	delete(vulkanExample);																			\
  	return 0;																						\
  }
  #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
  #define VULKAN_EXAMPLE_MAIN()																		\
  VulkanExample *vulkanExample;																		\
  int main(const int argc, const char *argv[])													    \
  {																									\
  	for (size_t i = 0; i < argc; i++) { VulkanExample::args.push_back(argv[i]); };  				\
  	vulkanExample = new VulkanExample();															\
  	vulkanExample->initVulkan();																	\
  	vulkanExample->setupWindow();					 												\
  	vulkanExample->prepare();																		\
  	vulkanExample->renderLoop();																	\
  	delete(vulkanExample);																			\
  	return 0;																						\
  }
  #elif defined(VK_USE_PLATFORM_XCB_KHR)
  #define VULKAN_EXAMPLE_MAIN()																		\
  VulkanExample *vulkanExample;																		\
  static void handleEvent(const xcb_generic_event_t *event)											\
  {																									\
  	if (vulkanExample != NULL)																		\
  	{																								\
  		vulkanExample->handleEvent(event);															\
  	}																								\
  }																									\
  int main(const int argc, const char *argv[])													    \
  {																									\
  	for (size_t i = 0; i < argc; i++) { VulkanExample::args.push_back(argv[i]); };  				\
  	vulkanExample = new VulkanExample();															\
  	vulkanExample->initVulkan();																	\
  	vulkanExample->setupWindow();					 												\
  	vulkanExample->prepare();																		\
  	vulkanExample->renderLoop();																	\
  	delete(vulkanExample);																			\
  	return 0;																						\
  }
  #elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
  #define VULKAN_EXAMPLE_MAIN()
  #endif
  