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); }