public static long CreateWindowSurface(Vk.Instance inst, IntPtr window) { Vk.Result res = (Vk.Result)_glfwCreateWindowSurface(inst.Handle, window, IntPtr.Zero, out IntPtr surface); if (res != Vk.Result.Success) { throw new Vk.VulkanException(res, "Could not create Vulkan surface from GLFW."); } return(surface.ToInt64()); }
// Destroys the global vulkan objects private void destroyGlobalVulkanObjects(Vk.Instance inst, VkExt.DebugReportCallbackExt debugReport, Vk.Device device) { device?.Dispose(); LINFO("Destroyed Vulkan device."); if (debugReport != null) { debugReport.Dispose(); LINFO("Destroyed Vulkan debug report callback."); } inst.Dispose(); LINFO("Destroyed Vulkan instance."); }
internal Instance(string appName, string engName, VER appVer, VER engVer, VER apiVer, bool bDebug) { string vlName = ""; if (bDebug) { if (!bCheckValidation(out vlName)) { return; } } ApplicationInfo ai = new ApplicationInfo(); ai.ApplicationName = appName; ai.ApplicationVersion = appVer; ai.EngineName = engName; ai.EngineVersion = engVer; ai.ApiVersion = apiVer; InstanceCreateInfo ici = new InstanceCreateInfo(); ici.Next = IntPtr.Zero; ici.ApplicationInfo = ai; List <string> extensions = new List <string>(); extensions.AddRange(Vulkan.GetRequiredInstanceExtensions()); if (bDebug) { extensions.Add(Constant.InstanceExtension.ExtDebugReport); } //get extension stuff from glfw ici.EnabledExtensionNames = extensions.ToArray(); if (bDebug) { ici.EnabledLayerNames = new string[1] { vlName }; } mInstance = new INST(ici, null); }
bool bCheckValidation(out string vlName) { LayerProperties [] lps = INST.EnumerateLayerProperties(); bool bFound = false; vlName = ""; foreach (LayerProperties lp in lps) { if (lp.Description.Contains("Standard Validation")) { bFound = true; vlName = lp.LayerName; } } if (!bFound) { Console.WriteLine("Missing standard validation layer..."); return(false); } return(true); }
// Creates a VkInstance private void createVulkanInstance(out Vk.Instance instance, out VkExt.DebugReportCallbackExt debugReport) { var appVersion = Application.AppParameters.Version; var engVersion = Assembly.GetExecutingAssembly().GetName().Version; // Build the app info Vk.ApplicationInfo aInfo = new Vk.ApplicationInfo( Application.AppParameters.Name, appVersion.ToVkVersion(), "Spectrum", new Vk.Version(engVersion.Major, engVersion.Minor, engVersion.Revision), new Vk.Version(1, 0, 0) ); // Get available instance extensions, and ensure the required ones are present var availExt = Vk.Instance.EnumerateExtensionProperties().Select(ext => ext.ExtensionName).ToArray(); List <string> reqExt = new List <string>(); reqExt.Add(Vk.Constant.InstanceExtension.KhrSurface); switch (Platform.OS) { case PlatformOS.Windows: reqExt.Add(Vk.Constant.InstanceExtension.KhrWin32Surface); break; case PlatformOS.Linux: reqExt.Add(Vk.Constant.InstanceExtension.KhrXlibSurface); break; case PlatformOS.OSX: reqExt.Add(Vk.Constant.InstanceExtension.MvkMacOSSurface); break; } { var missingExts = reqExt.FindAll(extName => !availExt.Contains(extName)); if (missingExts.Count > 0) { string msg = $"Required Vulkan extensions are missing: {String.Join(", ", missingExts)}"; LFATAL(msg); throw new PlatformNotSupportedException(msg); } } // Check for validation layers, if requested bool hasDebug = false; if (Application.AppParameters.EnableValidationLayers) { if (availExt.Contains(Vk.Constant.InstanceExtension.ExtDebugReport)) { var availLay = Vk.Instance.EnumerateLayerProperties().Select(layer => layer.LayerName).ToArray(); hasDebug = availLay.Contains("VK_LAYER_KHRONOS_validation"); if (hasDebug) { reqExt.Add(Vk.Constant.InstanceExtension.ExtDebugReport); } else { LERROR("Application requested Vulkan validation layers, but the standard layers are not available."); } } else { LERROR("Application requested Vulkan validation layers, but the debug report extension is not available."); } } // Create the instance Vk.InstanceCreateInfo iInfo = new Vk.InstanceCreateInfo( aInfo, hasDebug ? new[] { "VK_LAYER_KHRONOS_validation" } : null, reqExt.ToArray(), IntPtr.Zero ); instance = new Vk.Instance(iInfo, null); LINFO("Created Vulkan instance."); // Create the debug callback if needed debugReport = null; if (hasDebug) { VkExt.DebugReportCallbackCreateInfoExt dbInfo = new VkExt.DebugReportCallbackCreateInfoExt( (VkExt.DebugReportFlagsExt.PerformanceWarning | VkExt.DebugReportFlagsExt.Warning | VkExt.DebugReportFlagsExt.Error), _DebugReportCallback, IntPtr.Zero ); debugReport = VkExt.InstanceExtensions.CreateDebugReportCallbackExt(instance, dbInfo, null); LINFO("Created Vulkan debug report callback."); } }
// 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}."); }
public static bool GetPhysicalDevicePresentationSupport(Vk.Instance inst, Vk.PhysicalDevice dev, uint fam) => (_glfwGetPhysicalDevicePresentationSupport(inst.Handle, dev.Handle, fam) == 1);
public Swapchain(GraphicsDevice gdevice, Vk.Instance instance, Vk.PhysicalDevice pDevice, Vk.Device device) { Device = gdevice; _window = gdevice.Application.Window; _vkInstance = instance; _vkPhysicalDevice = pDevice; _vkDevice = device; // Create the surface long surfHandle = Glfw.CreateWindowSurface(instance, _window.Handle); Vk.AllocationCallbacks?acb = null; Surface = new VkKhr.SurfaceKhr(instance, ref acb, surfHandle); if (VkKhr.PhysicalDeviceExtensions.GetSurfaceSupportKhr(pDevice, gdevice.Queues.Graphics.FamilyIndex, Surface) == Vk.Constant.False) { LFATAL($"The physical device '{gdevice.Info.Name}' does not support surface presentation."); throw new PlatformNotSupportedException("Physical device does not support surface presentation."); } LINFO("Created Vulkan presentation surface."); // Check the surface for swapchain support levels var sFmts = VkKhr.PhysicalDeviceExtensions.GetSurfaceFormatsKhr(pDevice, Surface); var pModes = VkKhr.PhysicalDeviceExtensions.GetSurfacePresentModesKhr(pDevice, Surface); if (sFmts.Length == 0) { throw new PlatformNotSupportedException("The chosen device does not support any presentation formats."); } if (pModes.Length == 0) { throw new PlatformNotSupportedException("The chosen device does not support any presentation modes."); } // Choose the best available surface format if (sFmts.Length == 1 && sFmts[0].Format == Vk.Format.Undefined) // We are allowed to pick! { _surfaceFormat = PREFERRED_SURFACE_FORMATS[0]; } else // Check if one of the preferred formats is available, otherwise just use the first format given { var sfmt = PREFERRED_SURFACE_FORMATS.FirstOrDefault(fmt => sFmts.Contains(fmt)); if (sfmt.Format == 0 && sfmt.ColorSpace == 0) { _surfaceFormat = sFmts[0]; } else { _surfaceFormat = sfmt; } } // Choose the presentation mode (prefer mailbox -> fifo -> imm) _presentMode = pModes.Contains(VkKhr.PresentModeKhr.Mailbox) ? VkKhr.PresentModeKhr.Mailbox : pModes.Contains(VkKhr.PresentModeKhr.Fifo) ? VkKhr.PresentModeKhr.Fifo : VkKhr.PresentModeKhr.Immediate; // Prepare the synchronization objects _syncObjects.ImageAvailable = new Vk.Semaphore[MAX_INFLIGHT_FRAMES]; _syncObjects.BlitComplete = new Vk.Semaphore[MAX_INFLIGHT_FRAMES]; for (int i = 0; i < MAX_INFLIGHT_FRAMES; ++i) { _syncObjects.ImageAvailable[i] = device.CreateSemaphore(); _syncObjects.BlitComplete[i] = device.CreateSemaphore(); } // Setup the command buffers var cpci = new Vk.CommandPoolCreateInfo(_presentQueue.FamilyIndex, Vk.CommandPoolCreateFlags.Transient | Vk.CommandPoolCreateFlags.ResetCommandBuffer); _commandPool = device.CreateCommandPool(cpci); var cbai = new Vk.CommandBufferAllocateInfo(Vk.CommandBufferLevel.Primary, 1); _commandBuffer = _commandPool.AllocateBuffers(cbai)[0]; _blitFence = device.CreateFence(); // Do NOT start this signalled, as it is needed in rebuildSwapchain() below _rtTransferBarrier = new Vk.ImageMemoryBarrier( null, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.ColorAttachmentWrite, Vk.Accesses.TransferRead, Vk.ImageLayout.ColorAttachmentOptimal, Vk.ImageLayout.TransferSrcOptimal ); _rtAttachBarrier = new Vk.ImageMemoryBarrier( null, new Vk.ImageSubresourceRange(Vk.ImageAspects.Color, 0, 1, 0, 1), Vk.Accesses.TransferRead, Vk.Accesses.ColorAttachmentWrite, Vk.ImageLayout.TransferSrcOptimal, Vk.ImageLayout.ColorAttachmentOptimal ); // Build the swapchain rebuildSwapchain(); }