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