// Helper function to create a renderpass (and fill a list) from a set of render targets // The color attachments will always come first, in the order they are specified private static Vk.RenderPass CreateRenderPass(Vk.Device device, PipelineDescription desc, List <RenderTarget> rts) { // Fill the list rts.Clear(); if (desc.HasColorTargets) { rts.AddRange(desc.ColorTargets); } if (desc.HasDepthTarget) { rts.Add(desc.DepthTarget); } // Collect the attachment descriptions and create the references Vk.AttachmentDescription[] atts = new Vk.AttachmentDescription[desc.TargetCount]; Vk.AttachmentReference[] crefs = new Vk.AttachmentReference[desc.TargetCount - (desc.HasDepthTarget ? 1 : 0)]; if (desc.HasColorTargets) { for (int i = 0; i < desc._colorAIs.Length; ++i) { atts[i] = desc._colorAIs[i]; crefs[i] = new Vk.AttachmentReference(i, desc.ColorTargets[i].DefaultImageLayout); } } if (desc.HasDepthTarget) { atts[atts.Length - 1] = desc._depthAI.Value; } // Specify the lone subpass Vk.SubpassDescription subpass = new Vk.SubpassDescription( colorAttachments: crefs, depthStencilAttachment: desc.HasDepthTarget ? new Vk.AttachmentReference(atts.Length - 1, desc.DepthTarget.DefaultImageLayout) : (Vk.AttachmentReference?)null ); // Create the renderpass var rpci = new Vk.RenderPassCreateInfo( new[] { subpass }, attachments: atts, dependencies: new[] { // Create a two dependencies so that the color and depth buffers do not overwrite each other // TODO: Validate the flags used in these new Vk.SubpassDependency(Vk.Constant.SubpassExternal, 0, Vk.PipelineStages.ColorAttachmentOutput, Vk.PipelineStages.ColorAttachmentOutput, Vk.Accesses.None, Vk.Accesses.ColorAttachmentRead, Vk.Dependencies.ByRegion), new Vk.SubpassDependency(Vk.Constant.SubpassExternal, 0, Vk.PipelineStages.LateFragmentTests, Vk.PipelineStages.EarlyFragmentTests, Vk.Accesses.None, Vk.Accesses.DepthStencilAttachmentRead, Vk.Dependencies.ByRegion) } ); return(device.CreateRenderPass(rpci)); }
// 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."); }
// 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 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(); }
// Helper function to create a pipeline layout private static Vk.PipelineLayout CreateLayout(Vk.Device device) { var lci = new Vk.PipelineLayoutCreateInfo(null, null); // TODO: Check shader to get layout return(device.CreatePipelineLayout(lci)); }