public GraphicsDevice(bool enableValidation)
        {
            VkResult err;

            err = CreateInstance("VulkanDevice", enableValidation);
            if (err != VkResult.Success)
            {
                throw new InvalidOperationException("Could not create Vulkan instance. Error: " + err);
            }

            // Physical Device
            uint gpuCount = 0;

            Util.CheckResult(vkEnumeratePhysicalDevices(Instance, &gpuCount, null));
            Debug.Assert(gpuCount > 0);

            // Enumerate devices
            IntPtr *physicalDevices = stackalloc IntPtr[(int)gpuCount];

            err = vkEnumeratePhysicalDevices(Instance, &gpuCount, (VkPhysicalDevice *)physicalDevices);
            if (err != VkResult.Success)
            {
                throw new InvalidOperationException("Could not enumerate physical devices.");
            }

            // GPU selection

            uint selectedDevice           = 0;
            VkPhysicalDeviceType bestType = VkPhysicalDeviceType.Other;
            ulong bestDeviceLocalMemSize  = 0;

            for (uint i = 0; i < gpuCount; i++)
            {
                VkPhysicalDevice           device = ((VkPhysicalDevice *)physicalDevices)[i];
                VkPhysicalDeviceProperties devProps;
                vkGetPhysicalDeviceProperties(device, &devProps);


                if (devProps.deviceType == VkPhysicalDeviceType.IntegratedGpu &&
                    bestType != VkPhysicalDeviceType.IntegratedGpu &&
                    bestType != VkPhysicalDeviceType.DiscreteGpu)
                {
                    selectedDevice         = i;
                    bestType               = VkPhysicalDeviceType.IntegratedGpu;
                    bestDeviceLocalMemSize = GetDeviceLocalMemorySize(device);
                }
                else if (devProps.deviceType == VkPhysicalDeviceType.DiscreteGpu &&
                         bestType != VkPhysicalDeviceType.DiscreteGpu)
                {
                    //Found a discrete GPU, we can choose it.
                    selectedDevice         = i;
                    bestType               = VkPhysicalDeviceType.DiscreteGpu;
                    bestDeviceLocalMemSize = GetDeviceLocalMemorySize(device);
                }
                else if (devProps.deviceType == VkPhysicalDeviceType.DiscreteGpu &&
                         bestType == VkPhysicalDeviceType.DiscreteGpu)
                {
                    ulong memSize = GetDeviceLocalMemorySize(device);
                    if (memSize > bestDeviceLocalMemSize)
                    {
                    }
                    selectedDevice         = i;
                    bestType               = VkPhysicalDeviceType.DiscreteGpu;
                    bestDeviceLocalMemSize = memSize;
                }
            }

            Console.WriteLine($"Found a vulkan capable GPU of type {bestType} at location {selectedDevice}. With {bestDeviceLocalMemSize / (1024 * 1024)}MiB of memory.");
            // Select physical Device to be used for the Vulkan example
            // Defaults to the first Device unless specified by command line


            // TODO: Implement arg parsing, etc.



            physicalDevice = ((VkPhysicalDevice *)physicalDevices)[selectedDevice];

            Console.WriteLine(PrintDeviceMemoryData(physicalDevice));


            // Store properties (including limits) and features of the phyiscal Device
            // So examples can check against them and see if a feature is actually supported
            VkPhysicalDeviceProperties deviceProperties;

            vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
            DeviceProperties = deviceProperties;

            VkPhysicalDeviceFeatures deviceFeatures;

            vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures);
            DeviceFeatures = deviceFeatures;

            // Gather physical Device memory properties
            VkPhysicalDeviceMemoryProperties deviceMemoryProperties;

            vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
            DeviceMemoryProperties = deviceMemoryProperties;

            // Derived examples can override this to set actual features (based on above readings) to enable for logical device creation
            //getEnabledFeatures();

            // Vulkan Device creation
            // This is handled by a separate class that gets a logical Device representation
            // and encapsulates functions related to a Device
            vulkanDevice = new vksVulkanDevice(physicalDevice);
            VkResult res = vulkanDevice.CreateLogicalDevice(enabledFeatures, EnabledExtensions);

            if (res != VkResult.Success)
            {
                throw new InvalidOperationException("Could not create Vulkan Device.");
            }
            device = vulkanDevice.LogicalDevice;

            // Get a graphics queue from the Device
            VkQueue queue;

            vkGetDeviceQueue(device, vulkanDevice.QFIndices.Graphics, 0, &queue);
            this.queue = queue;

            // Find a suitable depth format
            VkFormat depthFormat;
            uint     validDepthFormat = Tools.GetSupportedDepthFormat(physicalDevice, &depthFormat);

            Debug.Assert(validDepthFormat == True);
            DepthFormat = depthFormat;

            mainSwapchain = new Swapchain(this);
            mainSwapchain.Connect(Instance, physicalDevice, device);

            // Create synchronization objects
            VkSemaphoreCreateInfo semaphoreCreateInfo = Initializers.semaphoreCreateInfo();

            // Create a semaphore used to synchronize image presentation
            // Ensures that the image is displayed before we start submitting new commands to the queu
            Util.CheckResult(vkCreateSemaphore(device, &semaphoreCreateInfo, null, &GetSemaphoresPtr()->PresentComplete));
            // Create a semaphore used to synchronize command submission
            // Ensures that the image is not presented until all commands have been sumbitted and executed
            Util.CheckResult(vkCreateSemaphore(device, &semaphoreCreateInfo, null, &GetSemaphoresPtr()->RenderComplete));
            // Create a semaphore used to synchronize command submission
            // Ensures that the image is not presented until all commands for the text overlay have been sumbitted and executed
            // Will be inserted after the render complete semaphore if the text overlay is enabled
            Util.CheckResult(vkCreateSemaphore(device, &semaphoreCreateInfo, null, &GetSemaphoresPtr()->TextOverlayComplete));

            // Set up submit info structure
            // Semaphores will stay the same during application lifetime
            // Command buffer submission info is set by each example
            submitInfo = Initializers.SubmitInfo();
            submitInfo.pWaitDstStageMask    = (VkPipelineStageFlags *)submitPipelineStages.Data;
            submitInfo.waitSemaphoreCount   = 1;
            submitInfo.pWaitSemaphores      = &GetSemaphoresPtr()->PresentComplete;
            submitInfo.signalSemaphoreCount = 1;
            submitInfo.pSignalSemaphores    = &GetSemaphoresPtr()->RenderComplete;

            memoryAllocator = new VulkanMemoryAllocator(this);
        }