private void createLogicalDevice()
        {
            QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

            List <VkDeviceQueueCreateInfo> queueCreateInfos = new List <VkDeviceQueueCreateInfo>();

            HashSet <int> uniqueQueueFamilies = new HashSet <int>()
            {
                indices.graphicsFamily, indices.presentFamily
            };

            float[] queuePriorities = new float[] { 1.0f };

            foreach (var queueFamily in uniqueQueueFamilies)
            {
                VkDeviceQueueCreateInfo queueCreateInfo = new VkDeviceQueueCreateInfo();
                queueCreateInfo.sType            = VkStructureType.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
                queueCreateInfo.queueFamilyIndex = queueFamily;
                queueCreateInfo.queueCount       = 1;
                queueCreateInfo.pQueuePriorities = queuePriorities;

                queueCreateInfos.Add(queueCreateInfo);
            }

            VkPhysicalDeviceFeatures deviceFeatures = new VkPhysicalDeviceFeatures();

            VkDeviceCreateInfo createInfo = new VkDeviceCreateInfo();

            createInfo.sType = VkStructureType.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;

            createInfo.queueCreateInfoCount = queueCreateInfos.Count;
            createInfo.pQueueCreateInfos    = queueCreateInfos.ToArray();

            createInfo.pEnabledFeatures = new VkPhysicalDeviceFeatures[] { deviceFeatures };

            createInfo.enabledExtensionCount   = deviceExtensions.Length;
            createInfo.ppEnabledExtensionNames = deviceExtensions;

            if (enableValidationLayers)
            {
                createInfo.enabledLayerCount   = validationLayers.Length;
                createInfo.ppEnabledLayerNames = validationLayers;
            }
            else
            {
                createInfo.enabledLayerCount = 0;
            }

            VkResult result = Vulkan.vkCreateDevice(physicalDevice, createInfo, null, out device);

            if (result != VkResult.VK_SUCCESS)
            {
                throw Program.Throw("failed to create logical device!", result);
            }

            Vulkan.vkGetDeviceQueue(device, indices.graphicsFamily, 0, out graphicsQueue);
            Vulkan.vkGetDeviceQueue(device, indices.presentFamily, 0, out presentQueue);
        }
        private bool isDeviceSuitable(VkPhysicalDevice physicalDevice)
        {
            QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

            bool extensionsSupported = checkDeviceExtensionsSupport(physicalDevice);

            bool swapChainAdequate = false;

            if (extensionsSupported)
            {
                SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
                swapChainAdequate = swapChainSupport.formats.Any() && swapChainSupport.presentModes.Any();
            }

            return(indices.isComplete() && extensionsSupported && swapChainAdequate);
        }
        private void createCommandPool()
        {
            QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);

            VkCommandPoolCreateInfo poolInfo = new VkCommandPoolCreateInfo();

            poolInfo.sType            = VkStructureType.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
            poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;

            VkResult result = Vulkan.vkCreateCommandPool(device, poolInfo, null, out commandPool);

            if (result != VkResult.VK_SUCCESS)
            {
                throw Program.Throw("failed to create command pool!", result);
            }
        }
        private QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device)
        {
            QueueFamilyIndices indices = new QueueFamilyIndices();

            int queueFamilyCount = 0;

            Vulkan.vkGetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilyCount, null);
            VkQueueFamilyProperties[] queueFamilies = new VkQueueFamilyProperties[queueFamilyCount];
            Vulkan.vkGetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilyCount, queueFamilies);

            int i = 0;

            foreach (var queueFamily in queueFamilies)
            {
                if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & VkQueueFlagBits.VK_QUEUE_GRAPHICS_BIT) > 0)
                {
                    indices.graphicsFamily = i;
                }

                bool presentSupport = false;
                Vulkan.vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, out presentSupport);

                if (queueFamily.queueCount > 0 && presentSupport)
                {
                    indices.presentFamily = i;
                }

                if (indices.isComplete())
                {
                    break;
                }

                i++;
            }

            return(indices);
        }
        private void createSwapChain()
        {
            SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);

            VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
            VkPresentModeKHR   presentMode   = chooseSwapPresentMode(swapChainSupport.presentModes);
            VkExtent2D         extent        = chooseSwapExtent(swapChainSupport.capabilities);

            int imageCount = swapChainSupport.capabilities.minImageCount + 1;

            if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
            {
                imageCount = swapChainSupport.capabilities.maxImageCount;
            }

            VkSwapchainCreateInfoKHR createInfo = new VkSwapchainCreateInfoKHR();

            createInfo.sType   = VkStructureType.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
            createInfo.surface = surface;

            createInfo.minImageCount    = imageCount;
            createInfo.imageFormat      = surfaceFormat.format;
            createInfo.imageColorSpace  = surfaceFormat.colorSpace;
            createInfo.imageExtent      = extent;
            createInfo.imageArrayLayers = 1;
            createInfo.imageUsage       = VkImageUsageFlags.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

            QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
            var queueFamilyIndices     = new List <int>()
            {
                indices.graphicsFamily, indices.presentFamily
            };

            if (indices.graphicsFamily != indices.presentFamily)
            {
                createInfo.imageSharingMode      = VkSharingMode.VK_SHARING_MODE_CONCURRENT;
                createInfo.queueFamilyIndexCount = queueFamilyIndices.Count;
                createInfo.pQueueFamilyIndices   = queueFamilyIndices.ToArray();
            }
            else
            {
                createInfo.imageSharingMode = VkSharingMode.VK_SHARING_MODE_EXCLUSIVE;
            }

            createInfo.preTransform   = (VkSurfaceTransformFlagBitsKHR)swapChainSupport.capabilities.currentTransform;
            createInfo.compositeAlpha = VkCompositeAlphaFlagBitsKHR.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
            createInfo.presentMode    = presentMode;
            createInfo.clipped        = VkBool32.VK_TRUE;

            createInfo.oldSwapchain = null;

            VkResult result = Vulkan.vkCreateSwapchainKHR(device, createInfo, null, out swapChain);

            if (result != VkResult.VK_SUCCESS)
            {
                throw Program.Throw("failed to create swap chain!", result);
            }

            swapChainImages = new VkImage[imageCount];
            Vulkan.vkGetSwapchainImagesKHR(device, swapChain, ref imageCount, swapChainImages);

            swapChainImageFormat = surfaceFormat.format;
            swapChainExtent      = extent;
        }