dotnet / Silk.NET

The high-speed OpenGL, OpenCL, OpenAL, OpenXR, GLFW, SDL, Vulkan, Assimp, WebGPU, and DirectX bindings library your mother warned you about.
https://dotnet.github.io/Silk.NET
MIT License
3.89k stars 378 forks source link

Native symbol not found (Symbol: vkCreateSwapchainKHR) #2179

Closed AnanasKiviTV closed 1 month ago

AnanasKiviTV commented 1 month ago

Discussed in https://github.com/dotnet/Silk.NET/discussions/843

Originally posted by **Fullbrik** February 22, 2022 # Summary I'm following the tutorial over at [vulkan-tutorial.com](vulkan-tutorial.com), but in c# with Silk.Net.Vulkan and Silk.Net.GLFW. Most of the tutorial translated well, except for GetPhysicalDeviceSurfaceSupport which throws Silk.NET.Core.Loader.SymbolLoadingException: Native symbol not found (Symbol: vkGetPhysicalDeviceSurfaceSupportKHR). # Steps to reproduce - Platform: Desktop (PopOS/Linux) - Framework Version: .NET 6 - API: Vulkan - API Version: Vulkan 1.2 1. Initialize vulkan instance with GLFW extensions 2. Enumerate over GetPhysicalDeviceQueueFamilyProperties 3. Check if a family index returns true when using GetPhysicalDeviceSurfaceSupport 4. Crash with the exception above # Comments I am new to vulkan, but decently know c#. I have tested the tutorial's c++ example, it compiled an ran, so the function does exist on my computer.

I have a similar problem. image

I'm currently following this Tutorial: https://github.com/dfkeenan/SilkVulkanTutorial/tree/main/Source/06_SwapChainCreation

Vulkan Version: 1.3.280 NVIDIA-Driver: 552.44 Graphics-Card: GTX 1070 OS: Window 10 Pro 22H2 Build 19045.4291 .Net: 8.0.204 vkcube is working csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Silk.NET.GLFW" Version="2.21.0" />
    <PackageReference Include="Silk.NET.OpenAL" Version="2.21.0" />
    <PackageReference Include="Silk.NET.Vulkan" Version="2.21.0" />
    <PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
  </ItemGroup>

</Project>

The whole File:

internal class VulkanSurface : IDisposable
{
    internal readonly KhrSurface khrSurface;
    internal readonly SurfaceKHR surfaceKHR;
    internal readonly Instance instance;

    internal VulkanSurface(Instance instance, KhrSurface khrSurface, SurfaceKHR surfaceKHR)
    {
        this.instance = instance;
        this.khrSurface = khrSurface;
        this.surfaceKHR = surfaceKHR;
    }

    private unsafe SwapChainSupportDetails QuerySwapChainSupport(VulkanPhysicalDevice vulkanPhysicalDevice)
    {
        var details = new SwapChainSupportDetails();

        var physicalDevice = vulkanPhysicalDevice.physicalDevice;

        khrSurface.GetPhysicalDeviceSurfaceCapabilities(physicalDevice, surfaceKHR, out details.Capabilities);

        uint formatCount = 0;
        khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surfaceKHR, ref formatCount, null);

        if (formatCount != 0)
        {
            details.Formats = new SurfaceFormatKHR[formatCount];
            fixed (SurfaceFormatKHR* formatsPtr = details.Formats)
            {
                khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surfaceKHR, ref formatCount, formatsPtr);
            }
        }
        else
        {
            details.Formats = Array.Empty<SurfaceFormatKHR>();
        }

        uint presentModeCount = 0;
        khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surfaceKHR, ref presentModeCount, null);

        if (presentModeCount != 0)
        {
            details.PresentModes = new PresentModeKHR[presentModeCount];
            fixed (PresentModeKHR* formatsPtr = details.PresentModes)
            {
                khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surfaceKHR, ref presentModeCount, formatsPtr);
            }

        }
        else
        {
            details.PresentModes = Array.Empty<PresentModeKHR>();
        }

        return details;
    }

    private SurfaceFormatKHR ChooseSwapSurfaceFormat(IReadOnlyList<SurfaceFormatKHR> availableFormats)
    {
        foreach (var availableFormat in availableFormats)
        {
            if (availableFormat.Format == Format.B8G8R8A8Srgb && availableFormat.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr)
            {
                return availableFormat;
            }
        }

        return availableFormats[0];
    }

    private PresentModeKHR ChoosePresentMode(IReadOnlyList<PresentModeKHR> availablePresentModes)
    {
        foreach (var availablePresentMode in availablePresentModes)
        {
            if (availablePresentMode == PresentModeKHR.MailboxKhr)
            {
                return availablePresentMode;
            }
        }

        return PresentModeKHR.FifoKhr;
    }

    private Extent2D ChooseSwapExtent(Vector2 framebufferSize, SurfaceCapabilitiesKHR capabilities)
    {
        if (capabilities.CurrentExtent.Width != uint.MaxValue)
        {
            return capabilities.CurrentExtent;
        }
        else
        {
            Extent2D actualExtent = new()
            {
                Width = (uint)framebufferSize.X,
                Height = (uint)framebufferSize.Y
            };

            actualExtent.Width = Math.Clamp(actualExtent.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width);
            actualExtent.Height = Math.Clamp(actualExtent.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height);

            return actualExtent;
        }
    }

    public unsafe bool CreateSwapchain(out VulkanSwapchain? vulkanSwapchain, Vector2 framebufferSize, VulkanDevice logicalDevice)
    {
        vulkanSwapchain = default;
        var vulkan = Vk.GetApi();

        var vulkanPhysicalDevice = logicalDevice.vulkanPhysicalDevice;

        var swapChainSupport = QuerySwapChainSupport(vulkanPhysicalDevice);

        var surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.Formats);
        var presentMode = ChoosePresentMode(swapChainSupport.PresentModes);
        var extent = ChooseSwapExtent(framebufferSize, swapChainSupport.Capabilities);

        var imageCount = swapChainSupport.Capabilities.MinImageCount + 1;
        if (swapChainSupport.Capabilities.MaxImageCount > 0 && imageCount > swapChainSupport.Capabilities.MaxImageCount)
        {
            imageCount = swapChainSupport.Capabilities.MaxImageCount;
        }

        SwapchainCreateInfoKHR creatInfo = new()
        {
            SType = StructureType.SwapchainCreateInfoKhr,
            Surface = this.surfaceKHR,

            MinImageCount = imageCount,
            ImageFormat = surfaceFormat.Format,
            ImageColorSpace = surfaceFormat.ColorSpace,
            ImageExtent = extent,
            ImageArrayLayers = 1,
            ImageUsage = ImageUsageFlags.ColorAttachmentBit,
        };

        var indices = vulkanPhysicalDevice.FindQueueFamilies(this);
        var queueFamilyIndices = stackalloc[] { indices.graphicsFamily!.Value, indices.presentFamily!.Value };

        if (indices.graphicsFamily != indices.presentFamily)
        {
            creatInfo = creatInfo with
            {
                ImageSharingMode = SharingMode.Concurrent,
                QueueFamilyIndexCount = 2,
                PQueueFamilyIndices = queueFamilyIndices,
            };
        }
        else
        {
            creatInfo.ImageSharingMode = SharingMode.Exclusive;
        }

        creatInfo = creatInfo with
        {
            PreTransform = swapChainSupport.Capabilities.CurrentTransform,
            CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr,
            PresentMode = presentMode,
            Clipped = true,

            OldSwapchain = default
        };

        if (!vulkan.TryGetDeviceExtension<KhrSwapchain>(instance, logicalDevice.device, out var khrSwapChain))
        {
            Debugger.Break();
            return false;
        }

        if (khrSwapChain.CreateSwapchain(logicalDevice.device, ref creatInfo, null, out var swapChain) != Result.Success) // Code break's here
        {
            Debugger.Break();
            return false;
        }

        khrSwapChain.GetSwapchainImages(logicalDevice.device, swapChain, ref imageCount, null);
        Image[] swapChainImages = new Image[imageCount];
        fixed (Image* swapChainImagesPtr = swapChainImages)
        {
            khrSwapChain.GetSwapchainImages(logicalDevice.device, swapChain, ref imageCount, swapChainImagesPtr);
        }

        var swapChainImageFormat = surfaceFormat.Format;
        var swapChainExtent = extent;

        vulkanSwapchain = new VulkanSwapchain(khrSwapChain, swapChain, swapChainImages, swapChainImageFormat, swapChainExtent, logicalDevice);
        return true;
    }

    public unsafe void Dispose()
    {
        this.khrSurface.DestroySurface(this.instance, surfaceKHR, null);
        this.khrSurface.Dispose();
    }

    private struct SwapChainSupportDetails
    {
        public SurfaceCapabilitiesKHR Capabilities;
        public SurfaceFormatKHR[] Formats;
        public PresentModeKHR[] PresentModes;
    }
}

ask if you need something else ;)

Perksey commented 1 month ago

Have you added VK_KHR_swapchain to your list of extensions in InstanceCreateInfo?

AnanasKiviTV commented 1 month ago

Have you added VK_KHR_swapchain to your list of extensions in InstanceCreateInfo? Yesn't. My code added the VK_KHR_swapchain extension along with the glfw required extensions to the DeviceCreateInfo. The creation of the device failed in ErrorExtensionNotPresent. It failed because the glfw extensions are instance extensions and not device extensions.

My code moved further until the creation of the swapchain which failed in that error. Most likely because of an faulty pointer to the device.

Thank you :)