// 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);
        }
Example #2
0
 public static bool GetPhysicalDevicePresentationSupport(Vk.Instance inst, Vk.PhysicalDevice dev, uint fam)
 => (_glfwGetPhysicalDevicePresentationSupport(inst.Handle, dev.Handle, fam) == 1);
        // 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}.");
        }
Example #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();
        }