Пример #1
0
        // 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}.");
        }
Пример #4
0
        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();
        }
Пример #5
0
        // 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));
        }