// Scores a physical device (somewhat arbitrarily, make this better later), score of zero is unsuitable private uint scoreDevice(Vk.PhysicalDevice device, out Vk.PhysicalDeviceProperties props, out Vk.PhysicalDeviceFeatures feats, out Vk.PhysicalDeviceMemoryProperties memProps, out Vk.QueueFamilyProperties[] queues) { props = device.GetProperties(); feats = device.GetFeatures(); memProps = device.GetMemoryProperties(); queues = device.GetQueueFamilyProperties(); uint score = 0; // Strongly prefer discrete GPUS if (props.DeviceType == Vk.PhysicalDeviceType.DiscreteGpu) { score += 10000; } return(score); }
// 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}."); }