// Selects and opens the device
        private void openVulkanDevice(Vk.Instance instance, out Vk.PhysicalDevice pDevice, out Vk.Device lDevice,
                                      out DeviceFeatures features, out DeviceLimits limits, out DeviceInfo info, out DeviceQueues queues,
                                      out Vk.PhysicalDeviceMemoryProperties memory)
        {
            // Enumerate the physical devices, and score and sort them, then remove invalid ones
            var devices = instance.EnumeratePhysicalDevices()
                          .Select(dev => {
                var score = scoreDevice(dev,
                                        out Vk.PhysicalDeviceProperties props,
                                        out Vk.PhysicalDeviceFeatures feats,
                                        out Vk.PhysicalDeviceMemoryProperties memProps,
                                        out Vk.QueueFamilyProperties[] qfams);
                return(device: dev, props, feats, memProps, queues: qfams, score);
            })
                          .OrderByDescending(dev => dev.score)
                          .ToList();

            devices.RemoveAll(dev => {
                if (dev.score == 0)
                {
                    LDEBUG($"Ignoring invalid physical device: {dev.props.DeviceName}.");
                    return(true);
                }
                return(false);
            });
            if (devices.Count == 0)
            {
                throw new PlatformNotSupportedException("This system does not have any valid physical devices.");
            }
            var bestDev = devices[0];

            // Ensure extension support
            var aExts = bestDev.device.EnumerateExtensionProperties().Select(ext => ext.ExtensionName).ToArray();
            var rExts = new List <string> {
                Vk.Constant.DeviceExtension.KhrSwapchain
            };

            {
                var missing = rExts.FindAll(ext => !aExts.Contains(ext));
                if (missing.Count > 0)
                {
                    string msg = $"Required Vulkan device extensions are missing: {String.Join(", ", missing)}";
                    LFATAL(msg);
                    throw new PlatformNotSupportedException(msg);
                }
            }

            // Prepare the queue families (we need to ensure a single queue for graphics and present)
            // In the future, we will operate with a separate transfer queue, if possible, as well as a separate compute queue
            Vk.DeviceQueueCreateInfo[] qInfos;
            bool sepTrans = false;             // If there is a separate transfer queue

            {
                var qfams = bestDev.queues
                            .Select((queue, idx) => (queue, present: Glfw.GetPhysicalDevicePresentationSupport(instance, bestDev.device, (uint)idx), family: idx))
                            .ToArray();
                var gFam = qfams.FirstOrDefault(fam => (fam.queue.QueueFlags & Vk.Queues.Graphics) > 0 && fam.present);
                if (gFam.queue.QueueCount == 0 && gFam.family == 0)
                {
                    throw new PlatformNotSupportedException("The selected device does not support a graphics queue with present capabilities.");
                }
                sepTrans = gFam.queue.QueueCount > 1;
                qInfos   = new Vk.DeviceQueueCreateInfo[] {
                    new Vk.DeviceQueueCreateInfo(gFam.family, sepTrans ? 2 : 1, new[] { 1.0f, 0.5f })
                };
            }

            // Populate the limits
            limits = default;
            limits.MaxTextureSize1D = (uint)bestDev.props.Limits.MaxImageDimension1D;
            limits.MaxTextureSize2D = (uint)bestDev.props.Limits.MaxImageDimension2D;
            limits.MaxTextureSize3D = (uint)bestDev.props.Limits.MaxImageDimension3D;
            limits.MaxTextureLayers = (uint)bestDev.props.Limits.MaxImageArrayLayers;

            // Populate the features
            features = default;
            Vk.PhysicalDeviceFeatures enFeats = default;
            var rFeats = Application.AppParameters.EnabledGraphicsFeatures;
            var strict = Application.AppParameters.StrictGraphicsFeatures;

            bool _enableFeature(bool avail, string name)
            {
                if (!avail)
                {
                    if (strict)
                    {
                        throw new PlatformNotSupportedException($"The required device feature '{name}' is not available.");
                    }
                    else
                    {
                        LERROR($"The requested device feature '{name}' is not available.");
                    }
                    return(false);
                }
                return(true);
            }

            if (rFeats.FillModeNonSolid)
            {
                enFeats.FillModeNonSolid = features.FillModeNonSolid = _enableFeature(bestDev.feats.FillModeNonSolid, "FillModeNonSolid");
            }
            if (rFeats.WideLines)
            {
                enFeats.WideLines = features.WideLines = _enableFeature(bestDev.feats.WideLines, "WideLines");
            }
            if (rFeats.DepthClamp)
            {
                enFeats.DepthClamp = features.DepthClamp = _enableFeature(bestDev.feats.DepthClamp, "DepthClamp");
            }
            if (rFeats.AnisotropicFiltering)
            {
                enFeats.SamplerAnisotropy = features.AnisotropicFiltering = _enableFeature(bestDev.feats.SamplerAnisotropy, "AnisotropicFiltering");
            }

            // Create the device
            Vk.DeviceCreateInfo dInfo = new Vk.DeviceCreateInfo(
                qInfos,
                rExts.ToArray(),
                enFeats,
                IntPtr.Zero
                );
            pDevice = bestDev.device;
            lDevice = pDevice.CreateDevice(dInfo, null);
            LINFO($"Created Vulkan logical device.");
            info = new DeviceInfo
            {
                Name          = bestDev.props.DeviceName,
                IsDiscrete    = (bestDev.props.DeviceType == Vk.PhysicalDeviceType.DiscreteGpu),
                VendorName    = VENDOR_ID_LIST.ContainsKey(bestDev.props.VendorId) ? VENDOR_ID_LIST[bestDev.props.VendorId] : "unknown",
                DriverVersion = new AppVersion(bestDev.props.DriverVersion)
            };

            // Retrieve the queues
            queues.Graphics = lDevice.GetQueue(qInfos[0].QueueFamilyIndex, 0);
            queues.Transfer = sepTrans ? lDevice.GetQueue(qInfos[0].QueueFamilyIndex, 1) : queues.Graphics;

            // Save the memory info
            memory = bestDev.memProps;

            LINFO($"Device Info: {info.Name} (S:{bestDev.score} D:{info.IsDiscrete} V:{info.VendorName} DV:{info.DriverVersion.ToString()}).");
            LINFO($"Queue Info: G={queues.Graphics.FamilyIndex}:{queues.Graphics.Index} T={queues.Transfer.FamilyIndex}:{queues.Transfer.Index}.");
        }
        internal (Device logicalDevice, Queue graphicsQueue, Queue presentQueue, IList <string>) CreateLogicalDevice(
            SurfaceKhr surface, HostDeviceRequirements deviceRequirements)
        {
            if (!AreRequirementsMet(deviceRequirements))
            {
                throw new Exception(
                          $"[{nameof(HostDevice)}] Device '{Name}' does not support all device-requirements");
            }

            string[] requiredExtensions = GetRequiredExtensions(surfaceType);
            if (!AreExtensionsAvailable(requiredExtensions))
            {
                throw new Exception(
                          $"[{nameof(HostDevice)}] Device '{Name}' does not support required extensions");
            }
            var extensionsToEnable = new List <string>(requiredExtensions);

            //Add any optional extensions IF its supported by this host
            string[] optionalExtensions = GetOptionalExtensions(surfaceType);
            for (int i = 0; i < optionalExtensions.Length; i++)
            {
                if (IsExtensionAvailable(optionalExtensions[i]))
                {
                    extensionsToEnable.Add(optionalExtensions[i]);
                }
            }

            int?graphicsQueueFamilyIndex = GetGraphicsQueueFamilyIndex();

            if (graphicsQueueFamilyIndex == null)
            {
                throw new Exception(
                          $"[{nameof(HostDevice)}] Device '{Name}' does not support graphics");
            }

            int?presentQueueFamilyIndex = GetPresentQueueFamilyIndex(surface);

            if (presentQueueFamilyIndex == null)
            {
                throw new Exception(
                          $"[{nameof(HostDevice)}] Device '{Name}' does not support presenting to the given surface");
            }

            List <VulkanCore.DeviceQueueCreateInfo> queueCreateInfos = new List <DeviceQueueCreateInfo>();

            queueCreateInfos.Add(new VulkanCore.DeviceQueueCreateInfo(
                                     graphicsQueueFamilyIndex.Value, queueCount: 1, queuePriorities: 1f));
            //If the present queue and graphics queues are not the same we also need to create a present queue
            if (graphicsQueueFamilyIndex.Value != presentQueueFamilyIndex.Value)
            {
                queueCreateInfos.Add(new VulkanCore.DeviceQueueCreateInfo(
                                         presentQueueFamilyIndex.Value, queueCount: 1, queuePriorities: 1f));
            }

            VulkanCore.DeviceCreateInfo createInfo = new VulkanCore.DeviceCreateInfo(
                queueCreateInfos: queueCreateInfos.ToArray(),
                enabledExtensionNames: extensionsToEnable.ToArray(),
                enabledFeatures: deviceRequirements.GetRequiredFeatures()
                );
            Device logicalDevice = physicalDevice.CreateDevice(createInfo);
            Queue  graphicsQueue = logicalDevice.GetQueue(graphicsQueueFamilyIndex.Value, queueIndex: 0);
            Queue  presentQueue  = logicalDevice.GetQueue(presentQueueFamilyIndex.Value, queueIndex: 0);

            logger?.Log(nameof(HostDevice), $"Created logical-device ({Name})");
            logger?.LogList(nameof(HostDevice), $"Enabled extensions for logical-device:", extensionsToEnable);

            //Note: If we are running on molten-vk then we can set some specific mvk device config
            if (extensionsToEnable.Contains("VK_MVK_moltenvk"))
            {
                var deviceConfig = logicalDevice.GetMVKDeviceConfiguration();
                deviceConfig.DebugMode                    = DebugUtils.IS_DEBUG;
                deviceConfig.PerformanceTracking          = DebugUtils.IS_DEBUG;
                deviceConfig.PerformanceLoggingFrameCount = DebugUtils.IS_DEBUG ? 300 : 0;
                logicalDevice.SetMVKDeviceConfiguration(deviceConfig);
            }

            return(logicalDevice, graphicsQueue, presentQueue, extensionsToEnable);
        }