├── .gitignore ├── Assets ├── texture.jpg ├── viking_room.obj └── viking_room.png ├── README.MD └── Source ├── .config └── dotnet-tools.json ├── .vscode └── launch.json ├── 00_BaseCode ├── 00_BaseCode.csproj └── Program.cs ├── 01_InstanceCreation ├── 01_InstanceCreation.csproj └── Program.cs ├── 02_ValidationLayers ├── 02_ValidationLayers.csproj └── Program.cs ├── 03_PhysicalDeviceSelection ├── 03_PhysicalDeviceSelection.csproj └── Program.cs ├── 04_LogicalDevice ├── 04_LogicalDevice.csproj └── Program.cs ├── 05_WindowSurface ├── 05_WindowSurface.csproj └── Program.cs ├── 06_SwapChainCreation ├── 06_SwapChainCreation.csproj └── Program.cs ├── 07_ImageViews ├── 07_ImageViews.csproj └── Program.cs ├── 08_GraphicsPipeline ├── 08_GraphicsPipeline.csproj └── Program.cs ├── 09_ShaderModules ├── 09_ShaderModules.csproj ├── 09_shader_base.frag ├── 09_shader_base.vert └── Program.cs ├── 10_FixedFunctions ├── 10_FixedFunctions.csproj └── Program.cs ├── 11_RenderPasses ├── 11_RenderPasses.csproj └── Program.cs ├── 12_GraphicsPipelineComplete ├── 12_GraphicsPipelineComplete.csproj └── Program.cs ├── 13_FrameBuffers ├── 13_FrameBuffers.csproj └── Program.cs ├── 14_CommandBuffers ├── 14_CommandBuffers.csproj └── Program.cs ├── 15_HelloTriangle ├── 15_HelloTriangle.csproj └── Program.cs ├── 17_SwapChainRecreation ├── 17_SwapChainRecreation.csproj └── Program.cs ├── 18_VertexInput ├── 18_VertexInput.csproj ├── 18_shader_vertexbuffer.frag ├── 18_shader_vertexbuffer.vert └── Program.cs ├── 19_VertexBuffer ├── 19_VertexBuffer.csproj └── Program.cs ├── 20_StagingBuffer ├── 20_StagingBuffer.csproj └── Program.cs ├── 21_IndexBuffer ├── 21_IndexBuffer.csproj └── Program.cs ├── 22_DescriptorSetLayout ├── 22_DescriptorSetLayout.csproj ├── 22_shader_ubo.frag ├── 22_shader_ubo.vert └── Program.cs ├── 23_DescriptorSets ├── 23_DescriptorSets.csproj └── Program.cs ├── 24_TextureImage ├── 24_TextureImage.csproj └── Program.cs ├── 25_Sampler ├── 25_Sampler.csproj └── Program.cs ├── 26_TextureMapping ├── 26_TextureMapping.csproj ├── 26_shader_textures.frag ├── 26_shader_textures.vert └── Program.cs ├── 27_DepthBuffering ├── 27_DepthBuffering.csproj ├── 27_shader_depth.frag ├── 27_shader_depth.vert └── Program.cs ├── 28_ModelLoading ├── 28_ModelLoading.csproj └── Program.cs ├── 29_MipMapping ├── 29_MipMapping.csproj └── Program.cs ├── 30_Multisampling ├── 30_Multisampling.csproj └── Program.cs ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── NewChapter.csx ├── NewChapter.ps1 ├── SilkVulkanTutorial.sln └── omnisharp.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | [Dd]ebug/ 3 | [Dd]ebugPublic/ 4 | [Rr]elease/ 5 | [Rr]eleases/ 6 | x64/ 7 | x86/ 8 | bld/ 9 | [Bb]in/ 10 | [Oo]bj/ 11 | [Ll]og/ 12 | 13 | # Visual Studio 2015/2017 cache/options directory 14 | .vs/ 15 | -------------------------------------------------------------------------------- /Assets/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfkeenan/SilkVulkanTutorial/13074670437c810c50c320bcd69121790dc97704/Assets/texture.jpg -------------------------------------------------------------------------------- /Assets/viking_room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfkeenan/SilkVulkanTutorial/13074670437c810c50c320bcd69121790dc97704/Assets/viking_room.png -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Silk .NET Vulkan Tutorial 2 | 3 | C# [Silk.NET](https://github.com/dotnet/Silk.NET) port of the [great tutorial by Alexander Overvoorde](https://vulkan-tutorial.com/). The original code can be found [here](https://github.com/Overv/VulkanTutorial). 4 | 5 | ## Build requirements 6 | 7 | The projects require the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) to build/run. The SDK provides the Vulkan validation layers as well as the command line tools to compile the shaders. The projects include a targets file that compiles the shaders. 8 | 9 | ## Licenses 10 | 11 | The code listings in the `Source` directory are licensed as [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). 12 | By contributing to that directory, you agree to license your contributions to 13 | the public under that same public domain-like license. 14 | 15 | This repository includes the same assets as the original tutorial assets. They are located in the `Assets/` folder. 16 | 17 | The following [CC0 licensed image](https://pixabay.com/en/statue-sculpture-fig-historically-1275469/) resized to 512 x 512 pixels 18 | 19 | * `Assets/texture.jpg` 20 | 21 | The [Viking room](https://sketchfab.com/3d-models/viking-room-a49f1b8e4f5c4ecf9e1fe7d81915ad38) model by [nigelgoh](https://sketchfab.com/nigelgoh) ([CC BY 4.0](https://web.archive.org/web/20200428202538/https://sketchfab.com/3d-models/viking-room-a49f1b8e4f5c4ecf9e1fe7d81915ad38)). 22 | 23 | * `Assets/viking_room.obj` 24 | * `Assets/viking_room.png` -------------------------------------------------------------------------------- /Source/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-script": { 6 | "version": "1.3.1", 7 | "commands": [ 8 | "dotnet-script" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Source/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Script Debug", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "program": "dotnet", 9 | "args": [ 10 | "exec", 11 | "C:/Users/dfkee/.nuget/packages/dotnet-script/1.3.1/tools/net6.0/any/dotnet-script.dll", 12 | "${file}" 13 | ], 14 | "cwd": "${workspaceRoot}", 15 | "stopAtEntry": false 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /Source/00_BaseCode/00_BaseCode.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Source/00_BaseCode/Program.cs: -------------------------------------------------------------------------------- 1 | using Silk.NET.Maths; 2 | using Silk.NET.Windowing; 3 | 4 | 5 | var app = new HelloTriangleApplication(); 6 | app.Run(); 7 | 8 | unsafe class HelloTriangleApplication 9 | { 10 | const int WIDTH = 800; 11 | const int HEIGHT = 600; 12 | 13 | private IWindow? window; 14 | 15 | public void Run() 16 | { 17 | InitWindow(); 18 | InitVulkan(); 19 | MainLoop(); 20 | CleanUp(); 21 | } 22 | 23 | private void InitWindow() 24 | { 25 | //Create a window. 26 | var options = WindowOptions.DefaultVulkan with 27 | { 28 | Size = new Vector2D(WIDTH, HEIGHT), 29 | Title = "Vulkan" 30 | }; 31 | 32 | window = Window.Create(options); 33 | window.Initialize(); 34 | 35 | if (window.VkSurface is null) 36 | { 37 | throw new Exception("Windowing platform doesn't support Vulkan."); 38 | } 39 | } 40 | 41 | private void InitVulkan() 42 | { 43 | 44 | } 45 | 46 | private void MainLoop() 47 | { 48 | window!.Run(); 49 | } 50 | 51 | private void CleanUp() 52 | { 53 | window?.Dispose(); 54 | } 55 | } -------------------------------------------------------------------------------- /Source/01_InstanceCreation/01_InstanceCreation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Source/01_InstanceCreation/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Silk.NET.Core; 3 | using Silk.NET.Maths; 4 | using Silk.NET.Vulkan; 5 | using Silk.NET.Windowing; 6 | 7 | 8 | var app = new HelloTriangleApplication(); 9 | app.Run(); 10 | 11 | unsafe class HelloTriangleApplication 12 | { 13 | const int WIDTH = 800; 14 | const int HEIGHT = 600; 15 | 16 | private IWindow? window; 17 | private Vk? vk; 18 | 19 | private Instance instance; 20 | 21 | public void Run() 22 | { 23 | InitWindow(); 24 | InitVulkan(); 25 | MainLoop(); 26 | CleanUp(); 27 | } 28 | 29 | private void InitWindow() 30 | { 31 | //Create a window. 32 | var options = WindowOptions.DefaultVulkan with 33 | { 34 | Size = new Vector2D(WIDTH, HEIGHT), 35 | Title = "Vulkan", 36 | }; 37 | 38 | window = Window.Create(options); 39 | window.Initialize(); 40 | 41 | if (window.VkSurface is null) 42 | { 43 | throw new Exception("Windowing platform doesn't support Vulkan."); 44 | } 45 | } 46 | 47 | private void InitVulkan() 48 | { 49 | CreateInstance(); 50 | } 51 | 52 | private void MainLoop() 53 | { 54 | window!.Run(); 55 | } 56 | 57 | private void CleanUp() 58 | { 59 | vk!.DestroyInstance(instance, null); 60 | vk!.Dispose(); 61 | 62 | window?.Dispose(); 63 | } 64 | 65 | private void CreateInstance() 66 | { 67 | vk = Vk.GetApi(); 68 | 69 | ApplicationInfo appInfo = new() 70 | { 71 | SType = StructureType.ApplicationInfo, 72 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 73 | ApplicationVersion = new Version32(1, 0, 0), 74 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 75 | EngineVersion = new Version32(1, 0, 0), 76 | ApiVersion = Vk.Version12 77 | }; 78 | 79 | InstanceCreateInfo createInfo = new() 80 | { 81 | SType = StructureType.InstanceCreateInfo, 82 | PApplicationInfo = &appInfo 83 | }; 84 | 85 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 86 | 87 | createInfo.EnabledExtensionCount = glfwExtensionCount; 88 | createInfo.PpEnabledExtensionNames = glfwExtensions; 89 | createInfo.EnabledLayerCount = 0; 90 | 91 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 92 | { 93 | throw new Exception("failed to create instance!"); 94 | } 95 | 96 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 97 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 98 | } 99 | } -------------------------------------------------------------------------------- /Source/02_ValidationLayers/02_ValidationLayers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/02_ValidationLayers/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Silk.NET.Core; 3 | using Silk.NET.Core.Native; 4 | using Silk.NET.Maths; 5 | using Silk.NET.Vulkan; 6 | using Silk.NET.Vulkan.Extensions.EXT; 7 | using Silk.NET.Windowing; 8 | 9 | 10 | var app = new HelloTriangleApplication(); 11 | app.Run(); 12 | 13 | unsafe class HelloTriangleApplication 14 | { 15 | const int WIDTH = 800; 16 | const int HEIGHT = 600; 17 | 18 | bool EnableValidationLayers = true; 19 | 20 | private readonly string[] validationLayers = new[] 21 | { 22 | "VK_LAYER_KHRONOS_validation" 23 | }; 24 | 25 | private IWindow? window; 26 | private Vk? vk; 27 | 28 | private Instance instance; 29 | 30 | private ExtDebugUtils? debugUtils; 31 | private DebugUtilsMessengerEXT debugMessenger; 32 | 33 | public void Run() 34 | { 35 | InitWindow(); 36 | InitVulkan(); 37 | MainLoop(); 38 | CleanUp(); 39 | } 40 | 41 | private void InitWindow() 42 | { 43 | //Create a window. 44 | var options = WindowOptions.DefaultVulkan with 45 | { 46 | Size = new Vector2D(WIDTH, HEIGHT), 47 | Title = "Vulkan", 48 | }; 49 | 50 | window = Window.Create(options); 51 | window.Initialize(); 52 | 53 | if (window.VkSurface is null) 54 | { 55 | throw new Exception("Windowing platform doesn't support Vulkan."); 56 | } 57 | } 58 | 59 | private void InitVulkan() 60 | { 61 | CreateInstance(); 62 | SetupDebugMessenger(); 63 | } 64 | 65 | private void MainLoop() 66 | { 67 | window!.Run(); 68 | } 69 | 70 | private void CleanUp() 71 | { 72 | if (EnableValidationLayers) 73 | { 74 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 75 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 76 | } 77 | 78 | vk!.DestroyInstance(instance, null); 79 | vk!.Dispose(); 80 | 81 | window?.Dispose(); 82 | } 83 | 84 | private void CreateInstance() 85 | { 86 | vk = Vk.GetApi(); 87 | 88 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 89 | { 90 | throw new Exception("validation layers requested, but not available!"); 91 | } 92 | 93 | ApplicationInfo appInfo = new() 94 | { 95 | SType = StructureType.ApplicationInfo, 96 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 97 | ApplicationVersion = new Version32(1, 0, 0), 98 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 99 | EngineVersion = new Version32(1, 0, 0), 100 | ApiVersion = Vk.Version12 101 | }; 102 | 103 | InstanceCreateInfo createInfo = new() 104 | { 105 | SType = StructureType.InstanceCreateInfo, 106 | PApplicationInfo = &appInfo 107 | }; 108 | 109 | var extensions = GetRequiredExtensions(); 110 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 111 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 112 | 113 | if (EnableValidationLayers) 114 | { 115 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 116 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 117 | 118 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 119 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 120 | createInfo.PNext = &debugCreateInfo; 121 | } 122 | else 123 | { 124 | createInfo.EnabledLayerCount = 0; 125 | createInfo.PNext = null; 126 | } 127 | 128 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 129 | { 130 | throw new Exception("failed to create instance!"); 131 | } 132 | 133 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 134 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 135 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 136 | 137 | if (EnableValidationLayers) 138 | { 139 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 140 | } 141 | } 142 | 143 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 144 | { 145 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 146 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 147 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 148 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 149 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 150 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 151 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 152 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 153 | } 154 | 155 | private void SetupDebugMessenger() 156 | { 157 | if (!EnableValidationLayers) return; 158 | 159 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 160 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 161 | 162 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 163 | PopulateDebugMessengerCreateInfo(ref createInfo); 164 | 165 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 166 | { 167 | throw new Exception("failed to set up debug messenger!"); 168 | } 169 | } 170 | 171 | private string[] GetRequiredExtensions() 172 | { 173 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 174 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 175 | 176 | if (EnableValidationLayers) 177 | { 178 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 179 | } 180 | 181 | return extensions; 182 | } 183 | 184 | private bool CheckValidationLayerSupport() 185 | { 186 | uint layerCount = 0; 187 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 188 | var availableLayers = new LayerProperties[layerCount]; 189 | fixed (LayerProperties* availableLayersPtr = availableLayers) 190 | { 191 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 192 | } 193 | 194 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 195 | 196 | return validationLayers.All(availableLayerNames.Contains); 197 | } 198 | 199 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 200 | { 201 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 202 | 203 | return Vk.False; 204 | } 205 | } -------------------------------------------------------------------------------- /Source/03_PhysicalDeviceSelection/03_PhysicalDeviceSelection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/03_PhysicalDeviceSelection/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Silk.NET.Core; 3 | using Silk.NET.Core.Native; 4 | using Silk.NET.Maths; 5 | using Silk.NET.Vulkan; 6 | using Silk.NET.Vulkan.Extensions.EXT; 7 | using Silk.NET.Windowing; 8 | 9 | 10 | var app = new HelloTriangleApplication(); 11 | app.Run(); 12 | 13 | struct QueueFamilyIndices 14 | { 15 | public uint? GraphicsFamily { get; set; } 16 | public bool IsComplete() 17 | { 18 | return GraphicsFamily.HasValue; 19 | } 20 | } 21 | 22 | unsafe class HelloTriangleApplication 23 | { 24 | const int WIDTH = 800; 25 | const int HEIGHT = 600; 26 | 27 | bool EnableValidationLayers = true; 28 | 29 | private readonly string[] validationLayers = new[] 30 | { 31 | "VK_LAYER_KHRONOS_validation" 32 | }; 33 | 34 | private IWindow? window; 35 | private Vk? vk; 36 | 37 | private Instance instance; 38 | 39 | private ExtDebugUtils? debugUtils; 40 | private DebugUtilsMessengerEXT debugMessenger; 41 | 42 | private PhysicalDevice physicalDevice; 43 | 44 | public void Run() 45 | { 46 | InitWindow(); 47 | InitVulkan(); 48 | MainLoop(); 49 | CleanUp(); 50 | } 51 | 52 | private void InitWindow() 53 | { 54 | //Create a window. 55 | var options = WindowOptions.DefaultVulkan with 56 | { 57 | Size = new Vector2D(WIDTH, HEIGHT), 58 | Title = "Vulkan", 59 | }; 60 | 61 | window = Window.Create(options); 62 | window.Initialize(); 63 | 64 | if (window.VkSurface is null) 65 | { 66 | throw new Exception("Windowing platform doesn't support Vulkan."); 67 | } 68 | } 69 | 70 | private void InitVulkan() 71 | { 72 | CreateInstance(); 73 | SetupDebugMessenger(); 74 | PickPhysicalDevice(); 75 | } 76 | 77 | private void MainLoop() 78 | { 79 | window!.Run(); 80 | } 81 | 82 | private void CleanUp() 83 | { 84 | if (EnableValidationLayers) 85 | { 86 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 87 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 88 | } 89 | 90 | vk!.DestroyInstance(instance, null); 91 | vk!.Dispose(); 92 | 93 | window?.Dispose(); 94 | } 95 | 96 | private void CreateInstance() 97 | { 98 | vk = Vk.GetApi(); 99 | 100 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 101 | { 102 | throw new Exception("validation layers requested, but not available!"); 103 | } 104 | 105 | ApplicationInfo appInfo = new() 106 | { 107 | SType = StructureType.ApplicationInfo, 108 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 109 | ApplicationVersion = new Version32(1, 0, 0), 110 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 111 | EngineVersion = new Version32(1, 0, 0), 112 | ApiVersion = Vk.Version12 113 | }; 114 | 115 | InstanceCreateInfo createInfo = new() 116 | { 117 | SType = StructureType.InstanceCreateInfo, 118 | PApplicationInfo = &appInfo 119 | }; 120 | 121 | var extensions = GetRequiredExtensions(); 122 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 123 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 124 | 125 | if (EnableValidationLayers) 126 | { 127 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 128 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 129 | 130 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 131 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 132 | createInfo.PNext = &debugCreateInfo; 133 | } 134 | else 135 | { 136 | createInfo.EnabledLayerCount = 0; 137 | createInfo.PNext = null; 138 | } 139 | 140 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 141 | { 142 | throw new Exception("failed to create instance!"); 143 | } 144 | 145 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 146 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 147 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 148 | 149 | if (EnableValidationLayers) 150 | { 151 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 152 | } 153 | } 154 | 155 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 156 | { 157 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 158 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 159 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 160 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 161 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 162 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 163 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 164 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 165 | } 166 | 167 | private void SetupDebugMessenger() 168 | { 169 | if (!EnableValidationLayers) return; 170 | 171 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 172 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 173 | 174 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 175 | PopulateDebugMessengerCreateInfo(ref createInfo); 176 | 177 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 178 | { 179 | throw new Exception("failed to set up debug messenger!"); 180 | } 181 | } 182 | 183 | private void PickPhysicalDevice() 184 | { 185 | var devices = vk!.GetPhysicalDevices(instance); 186 | 187 | foreach (var device in devices) 188 | { 189 | if (IsDeviceSuitable(device)) 190 | { 191 | physicalDevice = device; 192 | break; 193 | } 194 | } 195 | 196 | if (physicalDevice.Handle == 0) 197 | { 198 | throw new Exception("failed to find a suitable GPU!"); 199 | } 200 | } 201 | 202 | private bool IsDeviceSuitable(PhysicalDevice device) 203 | { 204 | var indices = FindQueueFamilies(device); 205 | 206 | return indices.IsComplete(); 207 | } 208 | 209 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 210 | { 211 | var indices = new QueueFamilyIndices(); 212 | 213 | uint queueFamilityCount = 0; 214 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 215 | 216 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 217 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 218 | { 219 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 220 | } 221 | 222 | 223 | uint i = 0; 224 | foreach (var queueFamily in queueFamilies) 225 | { 226 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 227 | { 228 | indices.GraphicsFamily = i; 229 | } 230 | 231 | if (indices.IsComplete()) 232 | { 233 | break; 234 | } 235 | 236 | i++; 237 | } 238 | 239 | return indices; 240 | } 241 | 242 | private string[] GetRequiredExtensions() 243 | { 244 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 245 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 246 | 247 | if (EnableValidationLayers) 248 | { 249 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 250 | } 251 | 252 | return extensions; 253 | } 254 | 255 | private bool CheckValidationLayerSupport() 256 | { 257 | uint layerCount = 0; 258 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 259 | var availableLayers = new LayerProperties[layerCount]; 260 | fixed (LayerProperties* availableLayersPtr = availableLayers) 261 | { 262 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 263 | } 264 | 265 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 266 | 267 | return validationLayers.All(availableLayerNames.Contains); 268 | } 269 | 270 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 271 | { 272 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 273 | 274 | return Vk.False; 275 | } 276 | } -------------------------------------------------------------------------------- /Source/04_LogicalDevice/04_LogicalDevice.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/04_LogicalDevice/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Silk.NET.Core; 3 | using Silk.NET.Core.Native; 4 | using Silk.NET.Maths; 5 | using Silk.NET.Vulkan; 6 | using Silk.NET.Vulkan.Extensions.EXT; 7 | using Silk.NET.Windowing; 8 | 9 | 10 | var app = new HelloTriangleApplication(); 11 | app.Run(); 12 | 13 | struct QueueFamilyIndices 14 | { 15 | public uint? GraphicsFamily { get; set; } 16 | public bool IsComplete() 17 | { 18 | return GraphicsFamily.HasValue; 19 | } 20 | } 21 | 22 | unsafe class HelloTriangleApplication 23 | { 24 | const int WIDTH = 800; 25 | const int HEIGHT = 600; 26 | 27 | bool EnableValidationLayers = true; 28 | 29 | private readonly string[] validationLayers = new[] 30 | { 31 | "VK_LAYER_KHRONOS_validation" 32 | }; 33 | 34 | private IWindow? window; 35 | private Vk? vk; 36 | 37 | private Instance instance; 38 | 39 | private ExtDebugUtils? debugUtils; 40 | private DebugUtilsMessengerEXT debugMessenger; 41 | 42 | private PhysicalDevice physicalDevice; 43 | private Device device; 44 | 45 | private Queue graphicsQueue; 46 | 47 | public void Run() 48 | { 49 | InitWindow(); 50 | InitVulkan(); 51 | MainLoop(); 52 | CleanUp(); 53 | } 54 | 55 | private void InitWindow() 56 | { 57 | //Create a window. 58 | var options = WindowOptions.DefaultVulkan with 59 | { 60 | Size = new Vector2D(WIDTH, HEIGHT), 61 | Title = "Vulkan", 62 | }; 63 | 64 | window = Window.Create(options); 65 | window.Initialize(); 66 | 67 | if (window.VkSurface is null) 68 | { 69 | throw new Exception("Windowing platform doesn't support Vulkan."); 70 | } 71 | } 72 | 73 | private void InitVulkan() 74 | { 75 | CreateInstance(); 76 | SetupDebugMessenger(); 77 | PickPhysicalDevice(); 78 | CreateLogicalDevice(); 79 | } 80 | 81 | private void MainLoop() 82 | { 83 | window!.Run(); 84 | } 85 | 86 | private void CleanUp() 87 | { 88 | vk!.DestroyDevice(device, null); 89 | 90 | if (EnableValidationLayers) 91 | { 92 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 93 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 94 | } 95 | 96 | vk!.DestroyInstance(instance, null); 97 | vk!.Dispose(); 98 | 99 | window?.Dispose(); 100 | } 101 | 102 | private void CreateInstance() 103 | { 104 | vk = Vk.GetApi(); 105 | 106 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 107 | { 108 | throw new Exception("validation layers requested, but not available!"); 109 | } 110 | 111 | ApplicationInfo appInfo = new() 112 | { 113 | SType = StructureType.ApplicationInfo, 114 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 115 | ApplicationVersion = new Version32(1, 0, 0), 116 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 117 | EngineVersion = new Version32(1, 0, 0), 118 | ApiVersion = Vk.Version12 119 | }; 120 | 121 | InstanceCreateInfo createInfo = new() 122 | { 123 | SType = StructureType.InstanceCreateInfo, 124 | PApplicationInfo = &appInfo 125 | }; 126 | 127 | var extensions = GetRequiredExtensions(); 128 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 129 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 130 | 131 | if (EnableValidationLayers) 132 | { 133 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 134 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 135 | 136 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 137 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 138 | createInfo.PNext = &debugCreateInfo; 139 | } 140 | else 141 | { 142 | createInfo.EnabledLayerCount = 0; 143 | createInfo.PNext = null; 144 | } 145 | 146 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 147 | { 148 | throw new Exception("failed to create instance!"); 149 | } 150 | 151 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 152 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 153 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 154 | 155 | if (EnableValidationLayers) 156 | { 157 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 158 | } 159 | } 160 | 161 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 162 | { 163 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 164 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 165 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 166 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 167 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 168 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 169 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 170 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 171 | } 172 | 173 | private void SetupDebugMessenger() 174 | { 175 | if (!EnableValidationLayers) return; 176 | 177 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 178 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 179 | 180 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 181 | PopulateDebugMessengerCreateInfo(ref createInfo); 182 | 183 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 184 | { 185 | throw new Exception("failed to set up debug messenger!"); 186 | } 187 | } 188 | 189 | private void PickPhysicalDevice() 190 | { 191 | var devices = vk!.GetPhysicalDevices(instance); 192 | 193 | foreach (var device in devices) 194 | { 195 | if (IsDeviceSuitable(device)) 196 | { 197 | physicalDevice = device; 198 | break; 199 | } 200 | } 201 | 202 | if (physicalDevice.Handle == 0) 203 | { 204 | throw new Exception("failed to find a suitable GPU!"); 205 | } 206 | } 207 | 208 | private void CreateLogicalDevice() 209 | { 210 | var indices = FindQueueFamilies(physicalDevice); 211 | 212 | DeviceQueueCreateInfo queueCreateInfo = new() 213 | { 214 | SType = StructureType.DeviceQueueCreateInfo, 215 | QueueFamilyIndex = indices.GraphicsFamily!.Value, 216 | QueueCount = 1 217 | }; 218 | 219 | float queuePriority = 1.0f; 220 | queueCreateInfo.PQueuePriorities = &queuePriority; 221 | 222 | PhysicalDeviceFeatures deviceFeatures = new(); 223 | 224 | DeviceCreateInfo createInfo = new() 225 | { 226 | SType = StructureType.DeviceCreateInfo, 227 | QueueCreateInfoCount = 1, 228 | PQueueCreateInfos = &queueCreateInfo, 229 | 230 | PEnabledFeatures = &deviceFeatures, 231 | 232 | EnabledExtensionCount = 0 233 | }; 234 | 235 | if (EnableValidationLayers) 236 | { 237 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 238 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 239 | } 240 | else 241 | { 242 | createInfo.EnabledLayerCount = 0; 243 | } 244 | 245 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 246 | { 247 | throw new Exception("failed to create logical device!"); 248 | } 249 | 250 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 251 | 252 | if (EnableValidationLayers) 253 | { 254 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 255 | } 256 | } 257 | 258 | private bool IsDeviceSuitable(PhysicalDevice device) 259 | { 260 | var indices = FindQueueFamilies(device); 261 | 262 | return indices.IsComplete(); 263 | } 264 | 265 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 266 | { 267 | var indices = new QueueFamilyIndices(); 268 | 269 | uint queueFamilityCount = 0; 270 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 271 | 272 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 273 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 274 | { 275 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 276 | } 277 | 278 | 279 | uint i = 0; 280 | foreach (var queueFamily in queueFamilies) 281 | { 282 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 283 | { 284 | indices.GraphicsFamily = i; 285 | } 286 | 287 | if (indices.IsComplete()) 288 | { 289 | break; 290 | } 291 | 292 | i++; 293 | } 294 | 295 | return indices; 296 | } 297 | 298 | private string[] GetRequiredExtensions() 299 | { 300 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 301 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 302 | 303 | if (EnableValidationLayers) 304 | { 305 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 306 | } 307 | 308 | return extensions; 309 | } 310 | 311 | private bool CheckValidationLayerSupport() 312 | { 313 | uint layerCount = 0; 314 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 315 | var availableLayers = new LayerProperties[layerCount]; 316 | fixed (LayerProperties* availableLayersPtr = availableLayers) 317 | { 318 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 319 | } 320 | 321 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 322 | 323 | return validationLayers.All(availableLayerNames.Contains); 324 | } 325 | 326 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 327 | { 328 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 329 | 330 | return Vk.False; 331 | } 332 | } -------------------------------------------------------------------------------- /Source/05_WindowSurface/05_WindowSurface.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/05_WindowSurface/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using Silk.NET.Core; 4 | using Silk.NET.Core.Native; 5 | using Silk.NET.Maths; 6 | using Silk.NET.Vulkan; 7 | using Silk.NET.Vulkan.Extensions.EXT; 8 | using Silk.NET.Vulkan.Extensions.KHR; 9 | using Silk.NET.Windowing; 10 | 11 | 12 | var app = new HelloTriangleApplication(); 13 | app.Run(); 14 | 15 | struct QueueFamilyIndices 16 | { 17 | public uint? GraphicsFamily { get; set; } 18 | public uint? PresentFamily { get; set; } 19 | 20 | public bool IsComplete() 21 | { 22 | return GraphicsFamily.HasValue && PresentFamily.HasValue; 23 | } 24 | } 25 | 26 | unsafe class HelloTriangleApplication 27 | { 28 | const int WIDTH = 800; 29 | const int HEIGHT = 600; 30 | 31 | bool EnableValidationLayers = true; 32 | 33 | private readonly string[] validationLayers = new[] 34 | { 35 | "VK_LAYER_KHRONOS_validation" 36 | }; 37 | 38 | private IWindow? window; 39 | private Vk? vk; 40 | 41 | private Instance instance; 42 | 43 | private ExtDebugUtils? debugUtils; 44 | private DebugUtilsMessengerEXT debugMessenger; 45 | private KhrSurface? khrSurface; 46 | private SurfaceKHR surface; 47 | 48 | private PhysicalDevice physicalDevice; 49 | private Device device; 50 | 51 | private Queue graphicsQueue; 52 | private Queue presentQueue; 53 | 54 | public void Run() 55 | { 56 | InitWindow(); 57 | InitVulkan(); 58 | MainLoop(); 59 | CleanUp(); 60 | } 61 | 62 | private void InitWindow() 63 | { 64 | //Create a window. 65 | var options = WindowOptions.DefaultVulkan with 66 | { 67 | Size = new Vector2D(WIDTH, HEIGHT), 68 | Title = "Vulkan", 69 | }; 70 | 71 | window = Window.Create(options); 72 | window.Initialize(); 73 | 74 | if (window.VkSurface is null) 75 | { 76 | throw new Exception("Windowing platform doesn't support Vulkan."); 77 | } 78 | } 79 | 80 | private void InitVulkan() 81 | { 82 | CreateInstance(); 83 | SetupDebugMessenger(); 84 | CreateSurface(); 85 | PickPhysicalDevice(); 86 | CreateLogicalDevice(); 87 | } 88 | 89 | private void MainLoop() 90 | { 91 | window!.Run(); 92 | } 93 | 94 | private void CleanUp() 95 | { 96 | vk!.DestroyDevice(device, null); 97 | 98 | if (EnableValidationLayers) 99 | { 100 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 101 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 102 | } 103 | 104 | khrSurface!.DestroySurface(instance, surface, null); 105 | vk!.DestroyInstance(instance, null); 106 | vk!.Dispose(); 107 | 108 | window?.Dispose(); 109 | } 110 | 111 | private void CreateInstance() 112 | { 113 | vk = Vk.GetApi(); 114 | 115 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 116 | { 117 | throw new Exception("validation layers requested, but not available!"); 118 | } 119 | 120 | ApplicationInfo appInfo = new() 121 | { 122 | SType = StructureType.ApplicationInfo, 123 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 124 | ApplicationVersion = new Version32(1, 0, 0), 125 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 126 | EngineVersion = new Version32(1, 0, 0), 127 | ApiVersion = Vk.Version12 128 | }; 129 | 130 | InstanceCreateInfo createInfo = new() 131 | { 132 | SType = StructureType.InstanceCreateInfo, 133 | PApplicationInfo = &appInfo 134 | }; 135 | 136 | var extensions = GetRequiredExtensions(); 137 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 138 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 139 | 140 | if (EnableValidationLayers) 141 | { 142 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 143 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 144 | 145 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 146 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 147 | createInfo.PNext = &debugCreateInfo; 148 | } 149 | else 150 | { 151 | createInfo.EnabledLayerCount = 0; 152 | createInfo.PNext = null; 153 | } 154 | 155 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 156 | { 157 | throw new Exception("failed to create instance!"); 158 | } 159 | 160 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 161 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 162 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 163 | 164 | if (EnableValidationLayers) 165 | { 166 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 167 | } 168 | } 169 | 170 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 171 | { 172 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 173 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 174 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 175 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 176 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 177 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 178 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 179 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 180 | } 181 | 182 | private void SetupDebugMessenger() 183 | { 184 | if (!EnableValidationLayers) return; 185 | 186 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 187 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 188 | 189 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 190 | PopulateDebugMessengerCreateInfo(ref createInfo); 191 | 192 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 193 | { 194 | throw new Exception("failed to set up debug messenger!"); 195 | } 196 | } 197 | 198 | private void CreateSurface() 199 | { 200 | if (!vk!.TryGetInstanceExtension(instance, out khrSurface)) 201 | { 202 | throw new NotSupportedException("KHR_surface extension not found."); 203 | } 204 | 205 | surface = window!.VkSurface!.Create(instance.ToHandle(), null).ToSurface(); 206 | } 207 | 208 | private void PickPhysicalDevice() 209 | { 210 | var devices = vk!.GetPhysicalDevices(instance); 211 | 212 | foreach (var device in devices) 213 | { 214 | if (IsDeviceSuitable(device)) 215 | { 216 | physicalDevice = device; 217 | break; 218 | } 219 | } 220 | 221 | if (physicalDevice.Handle == 0) 222 | { 223 | throw new Exception("failed to find a suitable GPU!"); 224 | } 225 | } 226 | 227 | private void CreateLogicalDevice() 228 | { 229 | var indices = FindQueueFamilies(physicalDevice); 230 | 231 | var uniqueQueueFamilies = new[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 232 | uniqueQueueFamilies = uniqueQueueFamilies.Distinct().ToArray(); 233 | 234 | using var mem = GlobalMemory.Allocate(uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo)); 235 | var queueCreateInfos = (DeviceQueueCreateInfo*)Unsafe.AsPointer(ref mem.GetPinnableReference()); 236 | 237 | float queuePriority = 1.0f; 238 | for (int i = 0; i < uniqueQueueFamilies.Length; i++) 239 | { 240 | queueCreateInfos[i] = new() 241 | { 242 | SType = StructureType.DeviceQueueCreateInfo, 243 | QueueFamilyIndex = uniqueQueueFamilies[i], 244 | QueueCount = 1, 245 | PQueuePriorities = &queuePriority 246 | }; 247 | } 248 | 249 | PhysicalDeviceFeatures deviceFeatures = new(); 250 | 251 | DeviceCreateInfo createInfo = new() 252 | { 253 | SType = StructureType.DeviceCreateInfo, 254 | QueueCreateInfoCount = (uint)uniqueQueueFamilies.Length, 255 | PQueueCreateInfos = queueCreateInfos, 256 | 257 | PEnabledFeatures = &deviceFeatures, 258 | 259 | EnabledExtensionCount = 0 260 | }; 261 | 262 | if (EnableValidationLayers) 263 | { 264 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 265 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 266 | } 267 | else 268 | { 269 | createInfo.EnabledLayerCount = 0; 270 | } 271 | 272 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 273 | { 274 | throw new Exception("failed to create logical device!"); 275 | } 276 | 277 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 278 | vk!.GetDeviceQueue(device, indices.PresentFamily!.Value, 0, out presentQueue); 279 | 280 | if (EnableValidationLayers) 281 | { 282 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 283 | } 284 | } 285 | 286 | private bool IsDeviceSuitable(PhysicalDevice device) 287 | { 288 | var indices = FindQueueFamilies(device); 289 | 290 | return indices.IsComplete(); 291 | } 292 | 293 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 294 | { 295 | var indices = new QueueFamilyIndices(); 296 | 297 | uint queueFamilityCount = 0; 298 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 299 | 300 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 301 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 302 | { 303 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 304 | } 305 | 306 | 307 | uint i = 0; 308 | foreach (var queueFamily in queueFamilies) 309 | { 310 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 311 | { 312 | indices.GraphicsFamily = i; 313 | } 314 | 315 | khrSurface!.GetPhysicalDeviceSurfaceSupport(device, i, surface, out var presentSupport); 316 | 317 | if (presentSupport) 318 | { 319 | indices.PresentFamily = i; 320 | } 321 | 322 | if (indices.IsComplete()) 323 | { 324 | break; 325 | } 326 | 327 | i++; 328 | } 329 | 330 | return indices; 331 | } 332 | 333 | private string[] GetRequiredExtensions() 334 | { 335 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 336 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 337 | 338 | if (EnableValidationLayers) 339 | { 340 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 341 | } 342 | 343 | return extensions; 344 | } 345 | 346 | private bool CheckValidationLayerSupport() 347 | { 348 | uint layerCount = 0; 349 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 350 | var availableLayers = new LayerProperties[layerCount]; 351 | fixed (LayerProperties* availableLayersPtr = availableLayers) 352 | { 353 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 354 | } 355 | 356 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 357 | 358 | return validationLayers.All(availableLayerNames.Contains); 359 | } 360 | 361 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 362 | { 363 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 364 | 365 | return Vk.False; 366 | } 367 | } -------------------------------------------------------------------------------- /Source/06_SwapChainCreation/06_SwapChainCreation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/06_SwapChainCreation/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using Silk.NET.Core; 4 | using Silk.NET.Core.Native; 5 | using Silk.NET.Maths; 6 | using Silk.NET.Vulkan; 7 | using Silk.NET.Vulkan.Extensions.EXT; 8 | using Silk.NET.Vulkan.Extensions.KHR; 9 | using Silk.NET.Windowing; 10 | 11 | 12 | var app = new HelloTriangleApplication(); 13 | app.Run(); 14 | 15 | struct QueueFamilyIndices 16 | { 17 | public uint? GraphicsFamily { get; set; } 18 | public uint? PresentFamily { get; set; } 19 | 20 | public bool IsComplete() 21 | { 22 | return GraphicsFamily.HasValue && PresentFamily.HasValue; 23 | } 24 | } 25 | 26 | struct SwapChainSupportDetails 27 | { 28 | public SurfaceCapabilitiesKHR Capabilities; 29 | public SurfaceFormatKHR[] Formats; 30 | public PresentModeKHR[] PresentModes; 31 | } 32 | 33 | unsafe class HelloTriangleApplication 34 | { 35 | const int WIDTH = 800; 36 | const int HEIGHT = 600; 37 | 38 | bool EnableValidationLayers = true; 39 | 40 | private readonly string[] validationLayers = new[] 41 | { 42 | "VK_LAYER_KHRONOS_validation" 43 | }; 44 | 45 | private readonly string[] deviceExtensions = new[] 46 | { 47 | KhrSwapchain.ExtensionName 48 | }; 49 | 50 | private IWindow? window; 51 | private Vk? vk; 52 | 53 | private Instance instance; 54 | 55 | private ExtDebugUtils? debugUtils; 56 | private DebugUtilsMessengerEXT debugMessenger; 57 | private KhrSurface? khrSurface; 58 | private SurfaceKHR surface; 59 | 60 | private PhysicalDevice physicalDevice; 61 | private Device device; 62 | 63 | private Queue graphicsQueue; 64 | private Queue presentQueue; 65 | 66 | private KhrSwapchain? khrSwapChain; 67 | private SwapchainKHR swapChain; 68 | private Image[]? swapChainImages; 69 | private Format swapChainImageFormat; 70 | private Extent2D swapChainExtent; 71 | 72 | public void Run() 73 | { 74 | InitWindow(); 75 | InitVulkan(); 76 | MainLoop(); 77 | CleanUp(); 78 | } 79 | 80 | private void InitWindow() 81 | { 82 | //Create a window. 83 | var options = WindowOptions.DefaultVulkan with 84 | { 85 | Size = new Vector2D(WIDTH, HEIGHT), 86 | Title = "Vulkan", 87 | }; 88 | 89 | window = Window.Create(options); 90 | window.Initialize(); 91 | 92 | if (window.VkSurface is null) 93 | { 94 | throw new Exception("Windowing platform doesn't support Vulkan."); 95 | } 96 | } 97 | 98 | private void InitVulkan() 99 | { 100 | CreateInstance(); 101 | SetupDebugMessenger(); 102 | CreateSurface(); 103 | PickPhysicalDevice(); 104 | CreateLogicalDevice(); 105 | CreateSwapChain(); 106 | } 107 | 108 | private void MainLoop() 109 | { 110 | window!.Run(); 111 | } 112 | 113 | private void CleanUp() 114 | { 115 | khrSwapChain!.DestroySwapchain(device, swapChain, null); 116 | 117 | vk!.DestroyDevice(device, null); 118 | 119 | if (EnableValidationLayers) 120 | { 121 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 122 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 123 | } 124 | 125 | khrSurface!.DestroySurface(instance, surface, null); 126 | vk!.DestroyInstance(instance, null); 127 | vk!.Dispose(); 128 | 129 | window?.Dispose(); 130 | } 131 | 132 | private void CreateInstance() 133 | { 134 | vk = Vk.GetApi(); 135 | 136 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 137 | { 138 | throw new Exception("validation layers requested, but not available!"); 139 | } 140 | 141 | ApplicationInfo appInfo = new() 142 | { 143 | SType = StructureType.ApplicationInfo, 144 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 145 | ApplicationVersion = new Version32(1, 0, 0), 146 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 147 | EngineVersion = new Version32(1, 0, 0), 148 | ApiVersion = Vk.Version12 149 | }; 150 | 151 | InstanceCreateInfo createInfo = new() 152 | { 153 | SType = StructureType.InstanceCreateInfo, 154 | PApplicationInfo = &appInfo 155 | }; 156 | 157 | var extensions = GetRequiredExtensions(); 158 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 159 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 160 | 161 | if (EnableValidationLayers) 162 | { 163 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 164 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 165 | 166 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 167 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 168 | createInfo.PNext = &debugCreateInfo; 169 | } 170 | else 171 | { 172 | createInfo.EnabledLayerCount = 0; 173 | createInfo.PNext = null; 174 | } 175 | 176 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 177 | { 178 | throw new Exception("failed to create instance!"); 179 | } 180 | 181 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 182 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 183 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 184 | 185 | if (EnableValidationLayers) 186 | { 187 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 188 | } 189 | } 190 | 191 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 192 | { 193 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 194 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 195 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 196 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 197 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 198 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 199 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 200 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 201 | } 202 | 203 | private void SetupDebugMessenger() 204 | { 205 | if (!EnableValidationLayers) return; 206 | 207 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 208 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 209 | 210 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 211 | PopulateDebugMessengerCreateInfo(ref createInfo); 212 | 213 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 214 | { 215 | throw new Exception("failed to set up debug messenger!"); 216 | } 217 | } 218 | 219 | private void CreateSurface() 220 | { 221 | if (!vk!.TryGetInstanceExtension(instance, out khrSurface)) 222 | { 223 | throw new NotSupportedException("KHR_surface extension not found."); 224 | } 225 | 226 | surface = window!.VkSurface!.Create(instance.ToHandle(), null).ToSurface(); 227 | } 228 | 229 | private void PickPhysicalDevice() 230 | { 231 | var devices = vk!.GetPhysicalDevices(instance); 232 | 233 | foreach (var device in devices) 234 | { 235 | if (IsDeviceSuitable(device)) 236 | { 237 | physicalDevice = device; 238 | break; 239 | } 240 | } 241 | 242 | if (physicalDevice.Handle == 0) 243 | { 244 | throw new Exception("failed to find a suitable GPU!"); 245 | } 246 | } 247 | 248 | private void CreateLogicalDevice() 249 | { 250 | var indices = FindQueueFamilies(physicalDevice); 251 | 252 | var uniqueQueueFamilies = new[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 253 | uniqueQueueFamilies = uniqueQueueFamilies.Distinct().ToArray(); 254 | 255 | using var mem = GlobalMemory.Allocate(uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo)); 256 | var queueCreateInfos = (DeviceQueueCreateInfo*)Unsafe.AsPointer(ref mem.GetPinnableReference()); 257 | 258 | float queuePriority = 1.0f; 259 | for (int i = 0; i < uniqueQueueFamilies.Length; i++) 260 | { 261 | queueCreateInfos[i] = new() 262 | { 263 | SType = StructureType.DeviceQueueCreateInfo, 264 | QueueFamilyIndex = uniqueQueueFamilies[i], 265 | QueueCount = 1, 266 | PQueuePriorities = &queuePriority 267 | }; 268 | } 269 | 270 | PhysicalDeviceFeatures deviceFeatures = new(); 271 | 272 | DeviceCreateInfo createInfo = new() 273 | { 274 | SType = StructureType.DeviceCreateInfo, 275 | QueueCreateInfoCount = (uint)uniqueQueueFamilies.Length, 276 | PQueueCreateInfos = queueCreateInfos, 277 | 278 | PEnabledFeatures = &deviceFeatures, 279 | 280 | EnabledExtensionCount = (uint)deviceExtensions.Length, 281 | PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(deviceExtensions) 282 | }; 283 | 284 | if (EnableValidationLayers) 285 | { 286 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 287 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 288 | } 289 | else 290 | { 291 | createInfo.EnabledLayerCount = 0; 292 | } 293 | 294 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 295 | { 296 | throw new Exception("failed to create logical device!"); 297 | } 298 | 299 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 300 | vk!.GetDeviceQueue(device, indices.PresentFamily!.Value, 0, out presentQueue); 301 | 302 | if (EnableValidationLayers) 303 | { 304 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 305 | } 306 | 307 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 308 | 309 | } 310 | 311 | private void CreateSwapChain() 312 | { 313 | var swapChainSupport = QuerySwapChainSupport(physicalDevice); 314 | 315 | var surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.Formats); 316 | var presentMode = ChoosePresentMode(swapChainSupport.PresentModes); 317 | var extent = ChooseSwapExtent(swapChainSupport.Capabilities); 318 | 319 | var imageCount = swapChainSupport.Capabilities.MinImageCount + 1; 320 | if (swapChainSupport.Capabilities.MaxImageCount > 0 && imageCount > swapChainSupport.Capabilities.MaxImageCount) 321 | { 322 | imageCount = swapChainSupport.Capabilities.MaxImageCount; 323 | } 324 | 325 | SwapchainCreateInfoKHR creatInfo = new() 326 | { 327 | SType = StructureType.SwapchainCreateInfoKhr, 328 | Surface = surface, 329 | 330 | MinImageCount = imageCount, 331 | ImageFormat = surfaceFormat.Format, 332 | ImageColorSpace = surfaceFormat.ColorSpace, 333 | ImageExtent = extent, 334 | ImageArrayLayers = 1, 335 | ImageUsage = ImageUsageFlags.ColorAttachmentBit, 336 | }; 337 | 338 | var indices = FindQueueFamilies(physicalDevice); 339 | var queueFamilyIndices = stackalloc[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 340 | 341 | if (indices.GraphicsFamily != indices.PresentFamily) 342 | { 343 | creatInfo = creatInfo with 344 | { 345 | ImageSharingMode = SharingMode.Concurrent, 346 | QueueFamilyIndexCount = 2, 347 | PQueueFamilyIndices = queueFamilyIndices, 348 | }; 349 | } 350 | else 351 | { 352 | creatInfo.ImageSharingMode = SharingMode.Exclusive; 353 | } 354 | 355 | creatInfo = creatInfo with 356 | { 357 | PreTransform = swapChainSupport.Capabilities.CurrentTransform, 358 | CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, 359 | PresentMode = presentMode, 360 | Clipped = true, 361 | 362 | OldSwapchain = default 363 | }; 364 | 365 | if (!vk!.TryGetDeviceExtension(instance, device, out khrSwapChain)) 366 | { 367 | throw new NotSupportedException("VK_KHR_swapchain extension not found."); 368 | } 369 | 370 | if (khrSwapChain!.CreateSwapchain(device, in creatInfo, null, out swapChain) != Result.Success) 371 | { 372 | throw new Exception("failed to create swap chain!"); 373 | } 374 | 375 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, null); 376 | swapChainImages = new Image[imageCount]; 377 | fixed (Image* swapChainImagesPtr = swapChainImages) 378 | { 379 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, swapChainImagesPtr); 380 | } 381 | 382 | swapChainImageFormat = surfaceFormat.Format; 383 | swapChainExtent = extent; 384 | } 385 | 386 | private SurfaceFormatKHR ChooseSwapSurfaceFormat(IReadOnlyList availableFormats) 387 | { 388 | foreach (var availableFormat in availableFormats) 389 | { 390 | if (availableFormat.Format == Format.B8G8R8A8Srgb && availableFormat.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr) 391 | { 392 | return availableFormat; 393 | } 394 | } 395 | 396 | return availableFormats[0]; 397 | } 398 | 399 | private PresentModeKHR ChoosePresentMode(IReadOnlyList availablePresentModes) 400 | { 401 | foreach (var availablePresentMode in availablePresentModes) 402 | { 403 | if (availablePresentMode == PresentModeKHR.MailboxKhr) 404 | { 405 | return availablePresentMode; 406 | } 407 | } 408 | 409 | return PresentModeKHR.FifoKhr; 410 | } 411 | 412 | private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities) 413 | { 414 | if (capabilities.CurrentExtent.Width != uint.MaxValue) 415 | { 416 | return capabilities.CurrentExtent; 417 | } 418 | else 419 | { 420 | var framebufferSize = window!.FramebufferSize; 421 | 422 | Extent2D actualExtent = new() 423 | { 424 | Width = (uint)framebufferSize.X, 425 | Height = (uint)framebufferSize.Y 426 | }; 427 | 428 | actualExtent.Width = Math.Clamp(actualExtent.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width); 429 | actualExtent.Height = Math.Clamp(actualExtent.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height); 430 | 431 | return actualExtent; 432 | } 433 | } 434 | 435 | private SwapChainSupportDetails QuerySwapChainSupport(PhysicalDevice physicalDevice) 436 | { 437 | var details = new SwapChainSupportDetails(); 438 | 439 | khrSurface!.GetPhysicalDeviceSurfaceCapabilities(physicalDevice, surface, out details.Capabilities); 440 | 441 | uint formatCount = 0; 442 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, null); 443 | 444 | if (formatCount != 0) 445 | { 446 | details.Formats = new SurfaceFormatKHR[formatCount]; 447 | fixed (SurfaceFormatKHR* formatsPtr = details.Formats) 448 | { 449 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, formatsPtr); 450 | } 451 | } 452 | else 453 | { 454 | details.Formats = Array.Empty(); 455 | } 456 | 457 | uint presentModeCount = 0; 458 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, null); 459 | 460 | if (presentModeCount != 0) 461 | { 462 | details.PresentModes = new PresentModeKHR[presentModeCount]; 463 | fixed (PresentModeKHR* formatsPtr = details.PresentModes) 464 | { 465 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, formatsPtr); 466 | } 467 | 468 | } 469 | else 470 | { 471 | details.PresentModes = Array.Empty(); 472 | } 473 | 474 | return details; 475 | } 476 | 477 | private bool IsDeviceSuitable(PhysicalDevice device) 478 | { 479 | var indices = FindQueueFamilies(device); 480 | 481 | bool extensionsSupported = CheckDeviceExtensionsSupport(device); 482 | 483 | bool swapChainAdequate = false; 484 | if (extensionsSupported) 485 | { 486 | var swapChainSupport = QuerySwapChainSupport(device); 487 | swapChainAdequate = swapChainSupport.Formats.Any() && swapChainSupport.PresentModes.Any(); 488 | } 489 | 490 | return indices.IsComplete() && extensionsSupported && swapChainAdequate; 491 | } 492 | 493 | private bool CheckDeviceExtensionsSupport(PhysicalDevice device) 494 | { 495 | uint extentionsCount = 0; 496 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, null); 497 | 498 | var availableExtensions = new ExtensionProperties[extentionsCount]; 499 | fixed (ExtensionProperties* availableExtensionsPtr = availableExtensions) 500 | { 501 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, availableExtensionsPtr); 502 | } 503 | 504 | var availableExtensionNames = availableExtensions.Select(extension => Marshal.PtrToStringAnsi((IntPtr)extension.ExtensionName)).ToHashSet(); 505 | 506 | return deviceExtensions.All(availableExtensionNames.Contains); 507 | 508 | } 509 | 510 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 511 | { 512 | var indices = new QueueFamilyIndices(); 513 | 514 | uint queueFamilityCount = 0; 515 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 516 | 517 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 518 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 519 | { 520 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 521 | } 522 | 523 | 524 | uint i = 0; 525 | foreach (var queueFamily in queueFamilies) 526 | { 527 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 528 | { 529 | indices.GraphicsFamily = i; 530 | } 531 | 532 | khrSurface!.GetPhysicalDeviceSurfaceSupport(device, i, surface, out var presentSupport); 533 | 534 | if (presentSupport) 535 | { 536 | indices.PresentFamily = i; 537 | } 538 | 539 | if (indices.IsComplete()) 540 | { 541 | break; 542 | } 543 | 544 | i++; 545 | } 546 | 547 | return indices; 548 | } 549 | 550 | private string[] GetRequiredExtensions() 551 | { 552 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 553 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 554 | 555 | if (EnableValidationLayers) 556 | { 557 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 558 | } 559 | 560 | return extensions; 561 | } 562 | 563 | private bool CheckValidationLayerSupport() 564 | { 565 | uint layerCount = 0; 566 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 567 | var availableLayers = new LayerProperties[layerCount]; 568 | fixed (LayerProperties* availableLayersPtr = availableLayers) 569 | { 570 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 571 | } 572 | 573 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 574 | 575 | return validationLayers.All(availableLayerNames.Contains); 576 | } 577 | 578 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 579 | { 580 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 581 | 582 | return Vk.False; 583 | } 584 | } -------------------------------------------------------------------------------- /Source/07_ImageViews/07_ImageViews.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/07_ImageViews/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using Silk.NET.Core; 4 | using Silk.NET.Core.Native; 5 | using Silk.NET.Maths; 6 | using Silk.NET.Vulkan; 7 | using Silk.NET.Vulkan.Extensions.EXT; 8 | using Silk.NET.Vulkan.Extensions.KHR; 9 | using Silk.NET.Windowing; 10 | 11 | 12 | var app = new HelloTriangleApplication(); 13 | app.Run(); 14 | 15 | struct QueueFamilyIndices 16 | { 17 | public uint? GraphicsFamily { get; set; } 18 | public uint? PresentFamily { get; set; } 19 | 20 | public bool IsComplete() 21 | { 22 | return GraphicsFamily.HasValue && PresentFamily.HasValue; 23 | } 24 | } 25 | 26 | struct SwapChainSupportDetails 27 | { 28 | public SurfaceCapabilitiesKHR Capabilities; 29 | public SurfaceFormatKHR[] Formats; 30 | public PresentModeKHR[] PresentModes; 31 | } 32 | 33 | unsafe class HelloTriangleApplication 34 | { 35 | const int WIDTH = 800; 36 | const int HEIGHT = 600; 37 | 38 | bool EnableValidationLayers = true; 39 | 40 | private readonly string[] validationLayers = new[] 41 | { 42 | "VK_LAYER_KHRONOS_validation" 43 | }; 44 | 45 | private readonly string[] deviceExtensions = new[] 46 | { 47 | KhrSwapchain.ExtensionName 48 | }; 49 | 50 | private IWindow? window; 51 | private Vk? vk; 52 | 53 | private Instance instance; 54 | 55 | private ExtDebugUtils? debugUtils; 56 | private DebugUtilsMessengerEXT debugMessenger; 57 | private KhrSurface? khrSurface; 58 | private SurfaceKHR surface; 59 | 60 | private PhysicalDevice physicalDevice; 61 | private Device device; 62 | 63 | private Queue graphicsQueue; 64 | private Queue presentQueue; 65 | 66 | private KhrSwapchain? khrSwapChain; 67 | private SwapchainKHR swapChain; 68 | private Image[]? swapChainImages; 69 | private Format swapChainImageFormat; 70 | private Extent2D swapChainExtent; 71 | private ImageView[]? swapChainImageViews; 72 | 73 | public void Run() 74 | { 75 | InitWindow(); 76 | InitVulkan(); 77 | MainLoop(); 78 | CleanUp(); 79 | } 80 | 81 | private void InitWindow() 82 | { 83 | //Create a window. 84 | var options = WindowOptions.DefaultVulkan with 85 | { 86 | Size = new Vector2D(WIDTH, HEIGHT), 87 | Title = "Vulkan", 88 | }; 89 | 90 | window = Window.Create(options); 91 | window.Initialize(); 92 | 93 | if (window.VkSurface is null) 94 | { 95 | throw new Exception("Windowing platform doesn't support Vulkan."); 96 | } 97 | } 98 | 99 | private void InitVulkan() 100 | { 101 | CreateInstance(); 102 | SetupDebugMessenger(); 103 | CreateSurface(); 104 | PickPhysicalDevice(); 105 | CreateLogicalDevice(); 106 | CreateSwapChain(); 107 | CreateImageViews(); 108 | } 109 | 110 | private void MainLoop() 111 | { 112 | window!.Run(); 113 | } 114 | 115 | private void CleanUp() 116 | { 117 | foreach (var imageView in swapChainImageViews!) 118 | { 119 | vk!.DestroyImageView(device, imageView, null); 120 | } 121 | 122 | khrSwapChain!.DestroySwapchain(device, swapChain, null); 123 | 124 | vk!.DestroyDevice(device, null); 125 | 126 | if (EnableValidationLayers) 127 | { 128 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 129 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 130 | } 131 | 132 | khrSurface!.DestroySurface(instance, surface, null); 133 | vk!.DestroyInstance(instance, null); 134 | vk!.Dispose(); 135 | 136 | window?.Dispose(); 137 | } 138 | 139 | private void CreateInstance() 140 | { 141 | vk = Vk.GetApi(); 142 | 143 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 144 | { 145 | throw new Exception("validation layers requested, but not available!"); 146 | } 147 | 148 | ApplicationInfo appInfo = new() 149 | { 150 | SType = StructureType.ApplicationInfo, 151 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 152 | ApplicationVersion = new Version32(1, 0, 0), 153 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 154 | EngineVersion = new Version32(1, 0, 0), 155 | ApiVersion = Vk.Version12 156 | }; 157 | 158 | InstanceCreateInfo createInfo = new() 159 | { 160 | SType = StructureType.InstanceCreateInfo, 161 | PApplicationInfo = &appInfo 162 | }; 163 | 164 | var extensions = GetRequiredExtensions(); 165 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 166 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 167 | 168 | if (EnableValidationLayers) 169 | { 170 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 171 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 172 | 173 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 174 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 175 | createInfo.PNext = &debugCreateInfo; 176 | } 177 | else 178 | { 179 | createInfo.EnabledLayerCount = 0; 180 | createInfo.PNext = null; 181 | } 182 | 183 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 184 | { 185 | throw new Exception("failed to create instance!"); 186 | } 187 | 188 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 189 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 190 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 191 | 192 | if (EnableValidationLayers) 193 | { 194 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 195 | } 196 | } 197 | 198 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 199 | { 200 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 201 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 202 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 203 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 204 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 205 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 206 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 207 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 208 | } 209 | 210 | private void SetupDebugMessenger() 211 | { 212 | if (!EnableValidationLayers) return; 213 | 214 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 215 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 216 | 217 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 218 | PopulateDebugMessengerCreateInfo(ref createInfo); 219 | 220 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 221 | { 222 | throw new Exception("failed to set up debug messenger!"); 223 | } 224 | } 225 | 226 | private void CreateSurface() 227 | { 228 | if (!vk!.TryGetInstanceExtension(instance, out khrSurface)) 229 | { 230 | throw new NotSupportedException("KHR_surface extension not found."); 231 | } 232 | 233 | surface = window!.VkSurface!.Create(instance.ToHandle(), null).ToSurface(); 234 | } 235 | 236 | private void PickPhysicalDevice() 237 | { 238 | var devices = vk!.GetPhysicalDevices(instance); 239 | 240 | foreach (var device in devices) 241 | { 242 | if (IsDeviceSuitable(device)) 243 | { 244 | physicalDevice = device; 245 | break; 246 | } 247 | } 248 | 249 | if (physicalDevice.Handle == 0) 250 | { 251 | throw new Exception("failed to find a suitable GPU!"); 252 | } 253 | } 254 | 255 | private void CreateLogicalDevice() 256 | { 257 | var indices = FindQueueFamilies(physicalDevice); 258 | 259 | var uniqueQueueFamilies = new[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 260 | uniqueQueueFamilies = uniqueQueueFamilies.Distinct().ToArray(); 261 | 262 | using var mem = GlobalMemory.Allocate(uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo)); 263 | var queueCreateInfos = (DeviceQueueCreateInfo*)Unsafe.AsPointer(ref mem.GetPinnableReference()); 264 | 265 | float queuePriority = 1.0f; 266 | for (int i = 0; i < uniqueQueueFamilies.Length; i++) 267 | { 268 | queueCreateInfos[i] = new() 269 | { 270 | SType = StructureType.DeviceQueueCreateInfo, 271 | QueueFamilyIndex = uniqueQueueFamilies[i], 272 | QueueCount = 1, 273 | PQueuePriorities = &queuePriority 274 | }; 275 | } 276 | 277 | PhysicalDeviceFeatures deviceFeatures = new(); 278 | 279 | DeviceCreateInfo createInfo = new() 280 | { 281 | SType = StructureType.DeviceCreateInfo, 282 | QueueCreateInfoCount = (uint)uniqueQueueFamilies.Length, 283 | PQueueCreateInfos = queueCreateInfos, 284 | 285 | PEnabledFeatures = &deviceFeatures, 286 | 287 | EnabledExtensionCount = (uint)deviceExtensions.Length, 288 | PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(deviceExtensions) 289 | }; 290 | 291 | if (EnableValidationLayers) 292 | { 293 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 294 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 295 | } 296 | else 297 | { 298 | createInfo.EnabledLayerCount = 0; 299 | } 300 | 301 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 302 | { 303 | throw new Exception("failed to create logical device!"); 304 | } 305 | 306 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 307 | vk!.GetDeviceQueue(device, indices.PresentFamily!.Value, 0, out presentQueue); 308 | 309 | if (EnableValidationLayers) 310 | { 311 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 312 | } 313 | 314 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 315 | 316 | } 317 | 318 | private void CreateSwapChain() 319 | { 320 | var swapChainSupport = QuerySwapChainSupport(physicalDevice); 321 | 322 | var surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.Formats); 323 | var presentMode = ChoosePresentMode(swapChainSupport.PresentModes); 324 | var extent = ChooseSwapExtent(swapChainSupport.Capabilities); 325 | 326 | var imageCount = swapChainSupport.Capabilities.MinImageCount + 1; 327 | if (swapChainSupport.Capabilities.MaxImageCount > 0 && imageCount > swapChainSupport.Capabilities.MaxImageCount) 328 | { 329 | imageCount = swapChainSupport.Capabilities.MaxImageCount; 330 | } 331 | 332 | SwapchainCreateInfoKHR creatInfo = new() 333 | { 334 | SType = StructureType.SwapchainCreateInfoKhr, 335 | Surface = surface, 336 | 337 | MinImageCount = imageCount, 338 | ImageFormat = surfaceFormat.Format, 339 | ImageColorSpace = surfaceFormat.ColorSpace, 340 | ImageExtent = extent, 341 | ImageArrayLayers = 1, 342 | ImageUsage = ImageUsageFlags.ColorAttachmentBit, 343 | }; 344 | 345 | var indices = FindQueueFamilies(physicalDevice); 346 | var queueFamilyIndices = stackalloc[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 347 | 348 | if (indices.GraphicsFamily != indices.PresentFamily) 349 | { 350 | creatInfo = creatInfo with 351 | { 352 | ImageSharingMode = SharingMode.Concurrent, 353 | QueueFamilyIndexCount = 2, 354 | PQueueFamilyIndices = queueFamilyIndices, 355 | }; 356 | } 357 | else 358 | { 359 | creatInfo.ImageSharingMode = SharingMode.Exclusive; 360 | } 361 | 362 | creatInfo = creatInfo with 363 | { 364 | PreTransform = swapChainSupport.Capabilities.CurrentTransform, 365 | CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, 366 | PresentMode = presentMode, 367 | Clipped = true, 368 | 369 | OldSwapchain = default 370 | }; 371 | 372 | if (!vk!.TryGetDeviceExtension(instance, device, out khrSwapChain)) 373 | { 374 | throw new NotSupportedException("VK_KHR_swapchain extension not found."); 375 | } 376 | 377 | if (khrSwapChain!.CreateSwapchain(device, in creatInfo, null, out swapChain) != Result.Success) 378 | { 379 | throw new Exception("failed to create swap chain!"); 380 | } 381 | 382 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, null); 383 | swapChainImages = new Image[imageCount]; 384 | fixed (Image* swapChainImagesPtr = swapChainImages) 385 | { 386 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, swapChainImagesPtr); 387 | } 388 | 389 | swapChainImageFormat = surfaceFormat.Format; 390 | swapChainExtent = extent; 391 | } 392 | 393 | private void CreateImageViews() 394 | { 395 | swapChainImageViews = new ImageView[swapChainImages!.Length]; 396 | 397 | for (int i = 0; i < swapChainImages.Length; i++) 398 | { 399 | ImageViewCreateInfo createInfo = new() 400 | { 401 | SType = StructureType.ImageViewCreateInfo, 402 | Image = swapChainImages[i], 403 | ViewType = ImageViewType.Type2D, 404 | Format = swapChainImageFormat, 405 | Components = 406 | { 407 | R = ComponentSwizzle.Identity, 408 | G = ComponentSwizzle.Identity, 409 | B = ComponentSwizzle.Identity, 410 | A = ComponentSwizzle.Identity, 411 | }, 412 | SubresourceRange = 413 | { 414 | AspectMask = ImageAspectFlags.ColorBit, 415 | BaseMipLevel = 0, 416 | LevelCount = 1, 417 | BaseArrayLayer = 0, 418 | LayerCount = 1, 419 | } 420 | 421 | }; 422 | 423 | if (vk!.CreateImageView(device, in createInfo, null, out swapChainImageViews[i]) != Result.Success) 424 | { 425 | throw new Exception("failed to create image views!"); 426 | } 427 | } 428 | } 429 | 430 | private SurfaceFormatKHR ChooseSwapSurfaceFormat(IReadOnlyList availableFormats) 431 | { 432 | foreach (var availableFormat in availableFormats) 433 | { 434 | if (availableFormat.Format == Format.B8G8R8A8Srgb && availableFormat.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr) 435 | { 436 | return availableFormat; 437 | } 438 | } 439 | 440 | return availableFormats[0]; 441 | } 442 | 443 | private PresentModeKHR ChoosePresentMode(IReadOnlyList availablePresentModes) 444 | { 445 | foreach (var availablePresentMode in availablePresentModes) 446 | { 447 | if (availablePresentMode == PresentModeKHR.MailboxKhr) 448 | { 449 | return availablePresentMode; 450 | } 451 | } 452 | 453 | return PresentModeKHR.FifoKhr; 454 | } 455 | 456 | private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities) 457 | { 458 | if (capabilities.CurrentExtent.Width != uint.MaxValue) 459 | { 460 | return capabilities.CurrentExtent; 461 | } 462 | else 463 | { 464 | var framebufferSize = window!.FramebufferSize; 465 | 466 | Extent2D actualExtent = new() 467 | { 468 | Width = (uint)framebufferSize.X, 469 | Height = (uint)framebufferSize.Y 470 | }; 471 | 472 | actualExtent.Width = Math.Clamp(actualExtent.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width); 473 | actualExtent.Height = Math.Clamp(actualExtent.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height); 474 | 475 | return actualExtent; 476 | } 477 | } 478 | 479 | private SwapChainSupportDetails QuerySwapChainSupport(PhysicalDevice physicalDevice) 480 | { 481 | var details = new SwapChainSupportDetails(); 482 | 483 | khrSurface!.GetPhysicalDeviceSurfaceCapabilities(physicalDevice, surface, out details.Capabilities); 484 | 485 | uint formatCount = 0; 486 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, null); 487 | 488 | if (formatCount != 0) 489 | { 490 | details.Formats = new SurfaceFormatKHR[formatCount]; 491 | fixed (SurfaceFormatKHR* formatsPtr = details.Formats) 492 | { 493 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, formatsPtr); 494 | } 495 | } 496 | else 497 | { 498 | details.Formats = Array.Empty(); 499 | } 500 | 501 | uint presentModeCount = 0; 502 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, null); 503 | 504 | if (presentModeCount != 0) 505 | { 506 | details.PresentModes = new PresentModeKHR[presentModeCount]; 507 | fixed (PresentModeKHR* formatsPtr = details.PresentModes) 508 | { 509 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, formatsPtr); 510 | } 511 | 512 | } 513 | else 514 | { 515 | details.PresentModes = Array.Empty(); 516 | } 517 | 518 | return details; 519 | } 520 | 521 | private bool IsDeviceSuitable(PhysicalDevice device) 522 | { 523 | var indices = FindQueueFamilies(device); 524 | 525 | bool extensionsSupported = CheckDeviceExtensionsSupport(device); 526 | 527 | bool swapChainAdequate = false; 528 | if (extensionsSupported) 529 | { 530 | var swapChainSupport = QuerySwapChainSupport(device); 531 | swapChainAdequate = swapChainSupport.Formats.Any() && swapChainSupport.PresentModes.Any(); 532 | } 533 | 534 | return indices.IsComplete() && extensionsSupported && swapChainAdequate; 535 | } 536 | 537 | private bool CheckDeviceExtensionsSupport(PhysicalDevice device) 538 | { 539 | uint extentionsCount = 0; 540 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, null); 541 | 542 | var availableExtensions = new ExtensionProperties[extentionsCount]; 543 | fixed (ExtensionProperties* availableExtensionsPtr = availableExtensions) 544 | { 545 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, availableExtensionsPtr); 546 | } 547 | 548 | var availableExtensionNames = availableExtensions.Select(extension => Marshal.PtrToStringAnsi((IntPtr)extension.ExtensionName)).ToHashSet(); 549 | 550 | return deviceExtensions.All(availableExtensionNames.Contains); 551 | 552 | } 553 | 554 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 555 | { 556 | var indices = new QueueFamilyIndices(); 557 | 558 | uint queueFamilityCount = 0; 559 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 560 | 561 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 562 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 563 | { 564 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 565 | } 566 | 567 | 568 | uint i = 0; 569 | foreach (var queueFamily in queueFamilies) 570 | { 571 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 572 | { 573 | indices.GraphicsFamily = i; 574 | } 575 | 576 | khrSurface!.GetPhysicalDeviceSurfaceSupport(device, i, surface, out var presentSupport); 577 | 578 | if (presentSupport) 579 | { 580 | indices.PresentFamily = i; 581 | } 582 | 583 | if (indices.IsComplete()) 584 | { 585 | break; 586 | } 587 | 588 | i++; 589 | } 590 | 591 | return indices; 592 | } 593 | 594 | private string[] GetRequiredExtensions() 595 | { 596 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 597 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 598 | 599 | if (EnableValidationLayers) 600 | { 601 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 602 | } 603 | 604 | return extensions; 605 | } 606 | 607 | private bool CheckValidationLayerSupport() 608 | { 609 | uint layerCount = 0; 610 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 611 | var availableLayers = new LayerProperties[layerCount]; 612 | fixed (LayerProperties* availableLayersPtr = availableLayers) 613 | { 614 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 615 | } 616 | 617 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 618 | 619 | return validationLayers.All(availableLayerNames.Contains); 620 | } 621 | 622 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 623 | { 624 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 625 | 626 | return Vk.False; 627 | } 628 | } -------------------------------------------------------------------------------- /Source/08_GraphicsPipeline/08_GraphicsPipeline.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/08_GraphicsPipeline/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using Silk.NET.Core; 4 | using Silk.NET.Core.Native; 5 | using Silk.NET.Maths; 6 | using Silk.NET.Vulkan; 7 | using Silk.NET.Vulkan.Extensions.EXT; 8 | using Silk.NET.Vulkan.Extensions.KHR; 9 | using Silk.NET.Windowing; 10 | 11 | 12 | var app = new HelloTriangleApplication(); 13 | app.Run(); 14 | 15 | struct QueueFamilyIndices 16 | { 17 | public uint? GraphicsFamily { get; set; } 18 | public uint? PresentFamily { get; set; } 19 | 20 | public bool IsComplete() 21 | { 22 | return GraphicsFamily.HasValue && PresentFamily.HasValue; 23 | } 24 | } 25 | 26 | struct SwapChainSupportDetails 27 | { 28 | public SurfaceCapabilitiesKHR Capabilities; 29 | public SurfaceFormatKHR[] Formats; 30 | public PresentModeKHR[] PresentModes; 31 | } 32 | 33 | unsafe class HelloTriangleApplication 34 | { 35 | const int WIDTH = 800; 36 | const int HEIGHT = 600; 37 | 38 | bool EnableValidationLayers = true; 39 | 40 | private readonly string[] validationLayers = new[] 41 | { 42 | "VK_LAYER_KHRONOS_validation" 43 | }; 44 | 45 | private readonly string[] deviceExtensions = new[] 46 | { 47 | KhrSwapchain.ExtensionName 48 | }; 49 | 50 | private IWindow? window; 51 | private Vk? vk; 52 | 53 | private Instance instance; 54 | 55 | private ExtDebugUtils? debugUtils; 56 | private DebugUtilsMessengerEXT debugMessenger; 57 | private KhrSurface? khrSurface; 58 | private SurfaceKHR surface; 59 | 60 | private PhysicalDevice physicalDevice; 61 | private Device device; 62 | 63 | private Queue graphicsQueue; 64 | private Queue presentQueue; 65 | 66 | private KhrSwapchain? khrSwapChain; 67 | private SwapchainKHR swapChain; 68 | private Image[]? swapChainImages; 69 | private Format swapChainImageFormat; 70 | private Extent2D swapChainExtent; 71 | private ImageView[]? swapChainImageViews; 72 | 73 | public void Run() 74 | { 75 | InitWindow(); 76 | InitVulkan(); 77 | MainLoop(); 78 | CleanUp(); 79 | } 80 | 81 | private void InitWindow() 82 | { 83 | //Create a window. 84 | var options = WindowOptions.DefaultVulkan with 85 | { 86 | Size = new Vector2D(WIDTH, HEIGHT), 87 | Title = "Vulkan", 88 | }; 89 | 90 | window = Window.Create(options); 91 | window.Initialize(); 92 | 93 | if (window.VkSurface is null) 94 | { 95 | throw new Exception("Windowing platform doesn't support Vulkan."); 96 | } 97 | } 98 | 99 | private void InitVulkan() 100 | { 101 | CreateInstance(); 102 | SetupDebugMessenger(); 103 | CreateSurface(); 104 | PickPhysicalDevice(); 105 | CreateLogicalDevice(); 106 | CreateSwapChain(); 107 | CreateImageViews(); 108 | CreateGraphicsPipeline(); 109 | } 110 | 111 | private void MainLoop() 112 | { 113 | window!.Run(); 114 | } 115 | 116 | private void CleanUp() 117 | { 118 | foreach (var imageView in swapChainImageViews!) 119 | { 120 | vk!.DestroyImageView(device, imageView, null); 121 | } 122 | 123 | khrSwapChain!.DestroySwapchain(device, swapChain, null); 124 | 125 | vk!.DestroyDevice(device, null); 126 | 127 | if (EnableValidationLayers) 128 | { 129 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 130 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 131 | } 132 | 133 | khrSurface!.DestroySurface(instance, surface, null); 134 | vk!.DestroyInstance(instance, null); 135 | vk!.Dispose(); 136 | 137 | window?.Dispose(); 138 | } 139 | 140 | private void CreateInstance() 141 | { 142 | vk = Vk.GetApi(); 143 | 144 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 145 | { 146 | throw new Exception("validation layers requested, but not available!"); 147 | } 148 | 149 | ApplicationInfo appInfo = new() 150 | { 151 | SType = StructureType.ApplicationInfo, 152 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 153 | ApplicationVersion = new Version32(1, 0, 0), 154 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 155 | EngineVersion = new Version32(1, 0, 0), 156 | ApiVersion = Vk.Version12 157 | }; 158 | 159 | InstanceCreateInfo createInfo = new() 160 | { 161 | SType = StructureType.InstanceCreateInfo, 162 | PApplicationInfo = &appInfo 163 | }; 164 | 165 | var extensions = GetRequiredExtensions(); 166 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 167 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 168 | 169 | if (EnableValidationLayers) 170 | { 171 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 172 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 173 | 174 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 175 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 176 | createInfo.PNext = &debugCreateInfo; 177 | } 178 | else 179 | { 180 | createInfo.EnabledLayerCount = 0; 181 | createInfo.PNext = null; 182 | } 183 | 184 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 185 | { 186 | throw new Exception("failed to create instance!"); 187 | } 188 | 189 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 190 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 191 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 192 | 193 | if (EnableValidationLayers) 194 | { 195 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 196 | } 197 | } 198 | 199 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 200 | { 201 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 202 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 203 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 204 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 205 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 206 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 207 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 208 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 209 | } 210 | 211 | private void SetupDebugMessenger() 212 | { 213 | if (!EnableValidationLayers) return; 214 | 215 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 216 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 217 | 218 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 219 | PopulateDebugMessengerCreateInfo(ref createInfo); 220 | 221 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 222 | { 223 | throw new Exception("failed to set up debug messenger!"); 224 | } 225 | } 226 | 227 | private void CreateSurface() 228 | { 229 | if (!vk!.TryGetInstanceExtension(instance, out khrSurface)) 230 | { 231 | throw new NotSupportedException("KHR_surface extension not found."); 232 | } 233 | 234 | surface = window!.VkSurface!.Create(instance.ToHandle(), null).ToSurface(); 235 | } 236 | 237 | private void PickPhysicalDevice() 238 | { 239 | var devices = vk!.GetPhysicalDevices(instance); 240 | 241 | foreach (var device in devices) 242 | { 243 | if (IsDeviceSuitable(device)) 244 | { 245 | physicalDevice = device; 246 | break; 247 | } 248 | } 249 | 250 | if (physicalDevice.Handle == 0) 251 | { 252 | throw new Exception("failed to find a suitable GPU!"); 253 | } 254 | } 255 | 256 | private void CreateLogicalDevice() 257 | { 258 | var indices = FindQueueFamilies(physicalDevice); 259 | 260 | var uniqueQueueFamilies = new[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 261 | uniqueQueueFamilies = uniqueQueueFamilies.Distinct().ToArray(); 262 | 263 | using var mem = GlobalMemory.Allocate(uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo)); 264 | var queueCreateInfos = (DeviceQueueCreateInfo*)Unsafe.AsPointer(ref mem.GetPinnableReference()); 265 | 266 | float queuePriority = 1.0f; 267 | for (int i = 0; i < uniqueQueueFamilies.Length; i++) 268 | { 269 | queueCreateInfos[i] = new() 270 | { 271 | SType = StructureType.DeviceQueueCreateInfo, 272 | QueueFamilyIndex = uniqueQueueFamilies[i], 273 | QueueCount = 1, 274 | PQueuePriorities = &queuePriority 275 | }; 276 | } 277 | 278 | PhysicalDeviceFeatures deviceFeatures = new(); 279 | 280 | DeviceCreateInfo createInfo = new() 281 | { 282 | SType = StructureType.DeviceCreateInfo, 283 | QueueCreateInfoCount = (uint)uniqueQueueFamilies.Length, 284 | PQueueCreateInfos = queueCreateInfos, 285 | 286 | PEnabledFeatures = &deviceFeatures, 287 | 288 | EnabledExtensionCount = (uint)deviceExtensions.Length, 289 | PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(deviceExtensions) 290 | }; 291 | 292 | if (EnableValidationLayers) 293 | { 294 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 295 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 296 | } 297 | else 298 | { 299 | createInfo.EnabledLayerCount = 0; 300 | } 301 | 302 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 303 | { 304 | throw new Exception("failed to create logical device!"); 305 | } 306 | 307 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 308 | vk!.GetDeviceQueue(device, indices.PresentFamily!.Value, 0, out presentQueue); 309 | 310 | if (EnableValidationLayers) 311 | { 312 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 313 | } 314 | 315 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 316 | 317 | } 318 | 319 | private void CreateSwapChain() 320 | { 321 | var swapChainSupport = QuerySwapChainSupport(physicalDevice); 322 | 323 | var surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.Formats); 324 | var presentMode = ChoosePresentMode(swapChainSupport.PresentModes); 325 | var extent = ChooseSwapExtent(swapChainSupport.Capabilities); 326 | 327 | var imageCount = swapChainSupport.Capabilities.MinImageCount + 1; 328 | if (swapChainSupport.Capabilities.MaxImageCount > 0 && imageCount > swapChainSupport.Capabilities.MaxImageCount) 329 | { 330 | imageCount = swapChainSupport.Capabilities.MaxImageCount; 331 | } 332 | 333 | SwapchainCreateInfoKHR creatInfo = new() 334 | { 335 | SType = StructureType.SwapchainCreateInfoKhr, 336 | Surface = surface, 337 | 338 | MinImageCount = imageCount, 339 | ImageFormat = surfaceFormat.Format, 340 | ImageColorSpace = surfaceFormat.ColorSpace, 341 | ImageExtent = extent, 342 | ImageArrayLayers = 1, 343 | ImageUsage = ImageUsageFlags.ColorAttachmentBit, 344 | }; 345 | 346 | var indices = FindQueueFamilies(physicalDevice); 347 | var queueFamilyIndices = stackalloc[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 348 | 349 | if (indices.GraphicsFamily != indices.PresentFamily) 350 | { 351 | creatInfo = creatInfo with 352 | { 353 | ImageSharingMode = SharingMode.Concurrent, 354 | QueueFamilyIndexCount = 2, 355 | PQueueFamilyIndices = queueFamilyIndices, 356 | }; 357 | } 358 | else 359 | { 360 | creatInfo.ImageSharingMode = SharingMode.Exclusive; 361 | } 362 | 363 | creatInfo = creatInfo with 364 | { 365 | PreTransform = swapChainSupport.Capabilities.CurrentTransform, 366 | CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, 367 | PresentMode = presentMode, 368 | Clipped = true, 369 | 370 | OldSwapchain = default 371 | }; 372 | 373 | if (!vk!.TryGetDeviceExtension(instance, device, out khrSwapChain)) 374 | { 375 | throw new NotSupportedException("VK_KHR_swapchain extension not found."); 376 | } 377 | 378 | if (khrSwapChain!.CreateSwapchain(device, in creatInfo, null, out swapChain) != Result.Success) 379 | { 380 | throw new Exception("failed to create swap chain!"); 381 | } 382 | 383 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, null); 384 | swapChainImages = new Image[imageCount]; 385 | fixed (Image* swapChainImagesPtr = swapChainImages) 386 | { 387 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, swapChainImagesPtr); 388 | } 389 | 390 | swapChainImageFormat = surfaceFormat.Format; 391 | swapChainExtent = extent; 392 | } 393 | 394 | private void CreateImageViews() 395 | { 396 | swapChainImageViews = new ImageView[swapChainImages!.Length]; 397 | 398 | for (int i = 0; i < swapChainImages.Length; i++) 399 | { 400 | ImageViewCreateInfo createInfo = new() 401 | { 402 | SType = StructureType.ImageViewCreateInfo, 403 | Image = swapChainImages[i], 404 | ViewType = ImageViewType.Type2D, 405 | Format = swapChainImageFormat, 406 | Components = 407 | { 408 | R = ComponentSwizzle.Identity, 409 | G = ComponentSwizzle.Identity, 410 | B = ComponentSwizzle.Identity, 411 | A = ComponentSwizzle.Identity, 412 | }, 413 | SubresourceRange = 414 | { 415 | AspectMask = ImageAspectFlags.ColorBit, 416 | BaseMipLevel = 0, 417 | LevelCount = 1, 418 | BaseArrayLayer = 0, 419 | LayerCount = 1, 420 | } 421 | 422 | }; 423 | 424 | if (vk!.CreateImageView(device, in createInfo, null, out swapChainImageViews[i]) != Result.Success) 425 | { 426 | throw new Exception("failed to create image views!"); 427 | } 428 | } 429 | } 430 | 431 | private void CreateGraphicsPipeline() 432 | { 433 | 434 | } 435 | 436 | private SurfaceFormatKHR ChooseSwapSurfaceFormat(IReadOnlyList availableFormats) 437 | { 438 | foreach (var availableFormat in availableFormats) 439 | { 440 | if (availableFormat.Format == Format.B8G8R8A8Srgb && availableFormat.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr) 441 | { 442 | return availableFormat; 443 | } 444 | } 445 | 446 | return availableFormats[0]; 447 | } 448 | 449 | private PresentModeKHR ChoosePresentMode(IReadOnlyList availablePresentModes) 450 | { 451 | foreach (var availablePresentMode in availablePresentModes) 452 | { 453 | if (availablePresentMode == PresentModeKHR.MailboxKhr) 454 | { 455 | return availablePresentMode; 456 | } 457 | } 458 | 459 | return PresentModeKHR.FifoKhr; 460 | } 461 | 462 | private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities) 463 | { 464 | if (capabilities.CurrentExtent.Width != uint.MaxValue) 465 | { 466 | return capabilities.CurrentExtent; 467 | } 468 | else 469 | { 470 | var framebufferSize = window!.FramebufferSize; 471 | 472 | Extent2D actualExtent = new() 473 | { 474 | Width = (uint)framebufferSize.X, 475 | Height = (uint)framebufferSize.Y 476 | }; 477 | 478 | actualExtent.Width = Math.Clamp(actualExtent.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width); 479 | actualExtent.Height = Math.Clamp(actualExtent.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height); 480 | 481 | return actualExtent; 482 | } 483 | } 484 | 485 | private SwapChainSupportDetails QuerySwapChainSupport(PhysicalDevice physicalDevice) 486 | { 487 | var details = new SwapChainSupportDetails(); 488 | 489 | khrSurface!.GetPhysicalDeviceSurfaceCapabilities(physicalDevice, surface, out details.Capabilities); 490 | 491 | uint formatCount = 0; 492 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, null); 493 | 494 | if (formatCount != 0) 495 | { 496 | details.Formats = new SurfaceFormatKHR[formatCount]; 497 | fixed (SurfaceFormatKHR* formatsPtr = details.Formats) 498 | { 499 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, formatsPtr); 500 | } 501 | } 502 | else 503 | { 504 | details.Formats = Array.Empty(); 505 | } 506 | 507 | uint presentModeCount = 0; 508 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, null); 509 | 510 | if (presentModeCount != 0) 511 | { 512 | details.PresentModes = new PresentModeKHR[presentModeCount]; 513 | fixed (PresentModeKHR* formatsPtr = details.PresentModes) 514 | { 515 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, formatsPtr); 516 | } 517 | 518 | } 519 | else 520 | { 521 | details.PresentModes = Array.Empty(); 522 | } 523 | 524 | return details; 525 | } 526 | 527 | private bool IsDeviceSuitable(PhysicalDevice device) 528 | { 529 | var indices = FindQueueFamilies(device); 530 | 531 | bool extensionsSupported = CheckDeviceExtensionsSupport(device); 532 | 533 | bool swapChainAdequate = false; 534 | if (extensionsSupported) 535 | { 536 | var swapChainSupport = QuerySwapChainSupport(device); 537 | swapChainAdequate = swapChainSupport.Formats.Any() && swapChainSupport.PresentModes.Any(); 538 | } 539 | 540 | return indices.IsComplete() && extensionsSupported && swapChainAdequate; 541 | } 542 | 543 | private bool CheckDeviceExtensionsSupport(PhysicalDevice device) 544 | { 545 | uint extentionsCount = 0; 546 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, null); 547 | 548 | var availableExtensions = new ExtensionProperties[extentionsCount]; 549 | fixed (ExtensionProperties* availableExtensionsPtr = availableExtensions) 550 | { 551 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, availableExtensionsPtr); 552 | } 553 | 554 | var availableExtensionNames = availableExtensions.Select(extension => Marshal.PtrToStringAnsi((IntPtr)extension.ExtensionName)).ToHashSet(); 555 | 556 | return deviceExtensions.All(availableExtensionNames.Contains); 557 | 558 | } 559 | 560 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 561 | { 562 | var indices = new QueueFamilyIndices(); 563 | 564 | uint queueFamilityCount = 0; 565 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 566 | 567 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 568 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 569 | { 570 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 571 | } 572 | 573 | 574 | uint i = 0; 575 | foreach (var queueFamily in queueFamilies) 576 | { 577 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 578 | { 579 | indices.GraphicsFamily = i; 580 | } 581 | 582 | khrSurface!.GetPhysicalDeviceSurfaceSupport(device, i, surface, out var presentSupport); 583 | 584 | if (presentSupport) 585 | { 586 | indices.PresentFamily = i; 587 | } 588 | 589 | if (indices.IsComplete()) 590 | { 591 | break; 592 | } 593 | 594 | i++; 595 | } 596 | 597 | return indices; 598 | } 599 | 600 | private string[] GetRequiredExtensions() 601 | { 602 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 603 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 604 | 605 | if (EnableValidationLayers) 606 | { 607 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 608 | } 609 | 610 | return extensions; 611 | } 612 | 613 | private bool CheckValidationLayerSupport() 614 | { 615 | uint layerCount = 0; 616 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 617 | var availableLayers = new LayerProperties[layerCount]; 618 | fixed (LayerProperties* availableLayersPtr = availableLayers) 619 | { 620 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 621 | } 622 | 623 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 624 | 625 | return validationLayers.All(availableLayerNames.Contains); 626 | } 627 | 628 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 629 | { 630 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 631 | 632 | return Vk.False; 633 | } 634 | } -------------------------------------------------------------------------------- /Source/09_ShaderModules/09_ShaderModules.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/09_ShaderModules/09_shader_base.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /Source/09_ShaderModules/09_shader_base.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) out vec3 fragColor; 4 | 5 | vec2 positions[3] = vec2[]( 6 | vec2(0.0, -0.5), 7 | vec2(0.5, 0.5), 8 | vec2(-0.5, 0.5) 9 | ); 10 | 11 | vec3 colors[3] = vec3[]( 12 | vec3(1.0, 0.0, 0.0), 13 | vec3(0.0, 1.0, 0.0), 14 | vec3(0.0, 0.0, 1.0) 15 | ); 16 | 17 | void main() { 18 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 19 | fragColor = colors[gl_VertexIndex]; 20 | } 21 | -------------------------------------------------------------------------------- /Source/09_ShaderModules/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using Silk.NET.Core; 4 | using Silk.NET.Core.Native; 5 | using Silk.NET.Maths; 6 | using Silk.NET.Vulkan; 7 | using Silk.NET.Vulkan.Extensions.EXT; 8 | using Silk.NET.Vulkan.Extensions.KHR; 9 | using Silk.NET.Windowing; 10 | 11 | 12 | var app = new HelloTriangleApplication(); 13 | app.Run(); 14 | 15 | struct QueueFamilyIndices 16 | { 17 | public uint? GraphicsFamily { get; set; } 18 | public uint? PresentFamily { get; set; } 19 | 20 | public bool IsComplete() 21 | { 22 | return GraphicsFamily.HasValue && PresentFamily.HasValue; 23 | } 24 | } 25 | 26 | struct SwapChainSupportDetails 27 | { 28 | public SurfaceCapabilitiesKHR Capabilities; 29 | public SurfaceFormatKHR[] Formats; 30 | public PresentModeKHR[] PresentModes; 31 | } 32 | 33 | unsafe class HelloTriangleApplication 34 | { 35 | const int WIDTH = 800; 36 | const int HEIGHT = 600; 37 | 38 | bool EnableValidationLayers = true; 39 | 40 | private readonly string[] validationLayers = new[] 41 | { 42 | "VK_LAYER_KHRONOS_validation" 43 | }; 44 | 45 | private readonly string[] deviceExtensions = new[] 46 | { 47 | KhrSwapchain.ExtensionName 48 | }; 49 | 50 | private IWindow? window; 51 | private Vk? vk; 52 | 53 | private Instance instance; 54 | 55 | private ExtDebugUtils? debugUtils; 56 | private DebugUtilsMessengerEXT debugMessenger; 57 | private KhrSurface? khrSurface; 58 | private SurfaceKHR surface; 59 | 60 | private PhysicalDevice physicalDevice; 61 | private Device device; 62 | 63 | private Queue graphicsQueue; 64 | private Queue presentQueue; 65 | 66 | private KhrSwapchain? khrSwapChain; 67 | private SwapchainKHR swapChain; 68 | private Image[]? swapChainImages; 69 | private Format swapChainImageFormat; 70 | private Extent2D swapChainExtent; 71 | private ImageView[]? swapChainImageViews; 72 | 73 | public void Run() 74 | { 75 | InitWindow(); 76 | InitVulkan(); 77 | MainLoop(); 78 | CleanUp(); 79 | } 80 | 81 | private void InitWindow() 82 | { 83 | //Create a window. 84 | var options = WindowOptions.DefaultVulkan with 85 | { 86 | Size = new Vector2D(WIDTH, HEIGHT), 87 | Title = "Vulkan", 88 | }; 89 | 90 | window = Window.Create(options); 91 | window.Initialize(); 92 | 93 | if (window.VkSurface is null) 94 | { 95 | throw new Exception("Windowing platform doesn't support Vulkan."); 96 | } 97 | } 98 | 99 | private void InitVulkan() 100 | { 101 | CreateInstance(); 102 | SetupDebugMessenger(); 103 | CreateSurface(); 104 | PickPhysicalDevice(); 105 | CreateLogicalDevice(); 106 | CreateSwapChain(); 107 | CreateImageViews(); 108 | CreateGraphicsPipeline(); 109 | } 110 | 111 | private void MainLoop() 112 | { 113 | window!.Run(); 114 | } 115 | 116 | private void CleanUp() 117 | { 118 | foreach (var imageView in swapChainImageViews!) 119 | { 120 | vk!.DestroyImageView(device, imageView, null); 121 | } 122 | 123 | khrSwapChain!.DestroySwapchain(device, swapChain, null); 124 | 125 | vk!.DestroyDevice(device, null); 126 | 127 | if (EnableValidationLayers) 128 | { 129 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 130 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 131 | } 132 | 133 | khrSurface!.DestroySurface(instance, surface, null); 134 | vk!.DestroyInstance(instance, null); 135 | vk!.Dispose(); 136 | 137 | window?.Dispose(); 138 | } 139 | 140 | private void CreateInstance() 141 | { 142 | vk = Vk.GetApi(); 143 | 144 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 145 | { 146 | throw new Exception("validation layers requested, but not available!"); 147 | } 148 | 149 | ApplicationInfo appInfo = new() 150 | { 151 | SType = StructureType.ApplicationInfo, 152 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 153 | ApplicationVersion = new Version32(1, 0, 0), 154 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 155 | EngineVersion = new Version32(1, 0, 0), 156 | ApiVersion = Vk.Version12 157 | }; 158 | 159 | InstanceCreateInfo createInfo = new() 160 | { 161 | SType = StructureType.InstanceCreateInfo, 162 | PApplicationInfo = &appInfo 163 | }; 164 | 165 | var extensions = GetRequiredExtensions(); 166 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 167 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 168 | 169 | if (EnableValidationLayers) 170 | { 171 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 172 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 173 | 174 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 175 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 176 | createInfo.PNext = &debugCreateInfo; 177 | } 178 | else 179 | { 180 | createInfo.EnabledLayerCount = 0; 181 | createInfo.PNext = null; 182 | } 183 | 184 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 185 | { 186 | throw new Exception("failed to create instance!"); 187 | } 188 | 189 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 190 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 191 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 192 | 193 | if (EnableValidationLayers) 194 | { 195 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 196 | } 197 | } 198 | 199 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 200 | { 201 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 202 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 203 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 204 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 205 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 206 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 207 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 208 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 209 | } 210 | 211 | private void SetupDebugMessenger() 212 | { 213 | if (!EnableValidationLayers) return; 214 | 215 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 216 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 217 | 218 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 219 | PopulateDebugMessengerCreateInfo(ref createInfo); 220 | 221 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 222 | { 223 | throw new Exception("failed to set up debug messenger!"); 224 | } 225 | } 226 | 227 | private void CreateSurface() 228 | { 229 | if (!vk!.TryGetInstanceExtension(instance, out khrSurface)) 230 | { 231 | throw new NotSupportedException("KHR_surface extension not found."); 232 | } 233 | 234 | surface = window!.VkSurface!.Create(instance.ToHandle(), null).ToSurface(); 235 | } 236 | 237 | private void PickPhysicalDevice() 238 | { 239 | var devices = vk!.GetPhysicalDevices(instance); 240 | 241 | foreach (var device in devices) 242 | { 243 | if (IsDeviceSuitable(device)) 244 | { 245 | physicalDevice = device; 246 | break; 247 | } 248 | } 249 | 250 | if (physicalDevice.Handle == 0) 251 | { 252 | throw new Exception("failed to find a suitable GPU!"); 253 | } 254 | } 255 | 256 | private void CreateLogicalDevice() 257 | { 258 | var indices = FindQueueFamilies(physicalDevice); 259 | 260 | var uniqueQueueFamilies = new[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 261 | uniqueQueueFamilies = uniqueQueueFamilies.Distinct().ToArray(); 262 | 263 | using var mem = GlobalMemory.Allocate(uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo)); 264 | var queueCreateInfos = (DeviceQueueCreateInfo*)Unsafe.AsPointer(ref mem.GetPinnableReference()); 265 | 266 | float queuePriority = 1.0f; 267 | for (int i = 0; i < uniqueQueueFamilies.Length; i++) 268 | { 269 | queueCreateInfos[i] = new() 270 | { 271 | SType = StructureType.DeviceQueueCreateInfo, 272 | QueueFamilyIndex = uniqueQueueFamilies[i], 273 | QueueCount = 1, 274 | PQueuePriorities = &queuePriority 275 | }; 276 | } 277 | 278 | PhysicalDeviceFeatures deviceFeatures = new(); 279 | 280 | DeviceCreateInfo createInfo = new() 281 | { 282 | SType = StructureType.DeviceCreateInfo, 283 | QueueCreateInfoCount = (uint)uniqueQueueFamilies.Length, 284 | PQueueCreateInfos = queueCreateInfos, 285 | 286 | PEnabledFeatures = &deviceFeatures, 287 | 288 | EnabledExtensionCount = (uint)deviceExtensions.Length, 289 | PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(deviceExtensions) 290 | }; 291 | 292 | if (EnableValidationLayers) 293 | { 294 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 295 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 296 | } 297 | else 298 | { 299 | createInfo.EnabledLayerCount = 0; 300 | } 301 | 302 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 303 | { 304 | throw new Exception("failed to create logical device!"); 305 | } 306 | 307 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 308 | vk!.GetDeviceQueue(device, indices.PresentFamily!.Value, 0, out presentQueue); 309 | 310 | if (EnableValidationLayers) 311 | { 312 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 313 | } 314 | 315 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 316 | 317 | } 318 | 319 | private void CreateSwapChain() 320 | { 321 | var swapChainSupport = QuerySwapChainSupport(physicalDevice); 322 | 323 | var surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.Formats); 324 | var presentMode = ChoosePresentMode(swapChainSupport.PresentModes); 325 | var extent = ChooseSwapExtent(swapChainSupport.Capabilities); 326 | 327 | var imageCount = swapChainSupport.Capabilities.MinImageCount + 1; 328 | if (swapChainSupport.Capabilities.MaxImageCount > 0 && imageCount > swapChainSupport.Capabilities.MaxImageCount) 329 | { 330 | imageCount = swapChainSupport.Capabilities.MaxImageCount; 331 | } 332 | 333 | SwapchainCreateInfoKHR creatInfo = new() 334 | { 335 | SType = StructureType.SwapchainCreateInfoKhr, 336 | Surface = surface, 337 | 338 | MinImageCount = imageCount, 339 | ImageFormat = surfaceFormat.Format, 340 | ImageColorSpace = surfaceFormat.ColorSpace, 341 | ImageExtent = extent, 342 | ImageArrayLayers = 1, 343 | ImageUsage = ImageUsageFlags.ColorAttachmentBit, 344 | }; 345 | 346 | var indices = FindQueueFamilies(physicalDevice); 347 | var queueFamilyIndices = stackalloc[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 348 | 349 | if (indices.GraphicsFamily != indices.PresentFamily) 350 | { 351 | creatInfo = creatInfo with 352 | { 353 | ImageSharingMode = SharingMode.Concurrent, 354 | QueueFamilyIndexCount = 2, 355 | PQueueFamilyIndices = queueFamilyIndices, 356 | }; 357 | } 358 | else 359 | { 360 | creatInfo.ImageSharingMode = SharingMode.Exclusive; 361 | } 362 | 363 | creatInfo = creatInfo with 364 | { 365 | PreTransform = swapChainSupport.Capabilities.CurrentTransform, 366 | CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, 367 | PresentMode = presentMode, 368 | Clipped = true, 369 | 370 | OldSwapchain = default 371 | }; 372 | 373 | if (!vk!.TryGetDeviceExtension(instance, device, out khrSwapChain)) 374 | { 375 | throw new NotSupportedException("VK_KHR_swapchain extension not found."); 376 | } 377 | 378 | if (khrSwapChain!.CreateSwapchain(device, in creatInfo, null, out swapChain) != Result.Success) 379 | { 380 | throw new Exception("failed to create swap chain!"); 381 | } 382 | 383 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, null); 384 | swapChainImages = new Image[imageCount]; 385 | fixed (Image* swapChainImagesPtr = swapChainImages) 386 | { 387 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, swapChainImagesPtr); 388 | } 389 | 390 | swapChainImageFormat = surfaceFormat.Format; 391 | swapChainExtent = extent; 392 | } 393 | 394 | private void CreateImageViews() 395 | { 396 | swapChainImageViews = new ImageView[swapChainImages!.Length]; 397 | 398 | for (int i = 0; i < swapChainImages.Length; i++) 399 | { 400 | ImageViewCreateInfo createInfo = new() 401 | { 402 | SType = StructureType.ImageViewCreateInfo, 403 | Image = swapChainImages[i], 404 | ViewType = ImageViewType.Type2D, 405 | Format = swapChainImageFormat, 406 | Components = 407 | { 408 | R = ComponentSwizzle.Identity, 409 | G = ComponentSwizzle.Identity, 410 | B = ComponentSwizzle.Identity, 411 | A = ComponentSwizzle.Identity, 412 | }, 413 | SubresourceRange = 414 | { 415 | AspectMask = ImageAspectFlags.ColorBit, 416 | BaseMipLevel = 0, 417 | LevelCount = 1, 418 | BaseArrayLayer = 0, 419 | LayerCount = 1, 420 | } 421 | 422 | }; 423 | 424 | if (vk!.CreateImageView(device, in createInfo, null, out swapChainImageViews[i]) != Result.Success) 425 | { 426 | throw new Exception("failed to create image views!"); 427 | } 428 | } 429 | } 430 | 431 | private void CreateGraphicsPipeline() 432 | { 433 | var vertShaderCode = File.ReadAllBytes("shaders/vert.spv"); 434 | var fragShaderCode = File.ReadAllBytes("shaders/frag.spv"); 435 | 436 | var vertShaderModule = CreateShaderModule(vertShaderCode); 437 | var fragShaderModule = CreateShaderModule(fragShaderCode); 438 | 439 | PipelineShaderStageCreateInfo vertShaderStageInfo = new() 440 | { 441 | SType = StructureType.PipelineShaderStageCreateInfo, 442 | Stage = ShaderStageFlags.VertexBit, 443 | Module = vertShaderModule, 444 | PName = (byte*)SilkMarshal.StringToPtr("main") 445 | }; 446 | 447 | PipelineShaderStageCreateInfo fragShaderStageInfo = new() 448 | { 449 | SType = StructureType.PipelineShaderStageCreateInfo, 450 | Stage = ShaderStageFlags.FragmentBit, 451 | Module = fragShaderModule, 452 | PName = (byte*)SilkMarshal.StringToPtr("main") 453 | }; 454 | 455 | var shaderStages = stackalloc[] 456 | { 457 | vertShaderStageInfo, 458 | fragShaderStageInfo 459 | }; 460 | 461 | vk!.DestroyShaderModule(device, fragShaderModule, null); 462 | vk!.DestroyShaderModule(device, vertShaderModule, null); 463 | 464 | SilkMarshal.Free((nint)vertShaderStageInfo.PName); 465 | SilkMarshal.Free((nint)fragShaderStageInfo.PName); 466 | } 467 | 468 | private ShaderModule CreateShaderModule(byte[] code) 469 | { 470 | ShaderModuleCreateInfo createInfo = new() 471 | { 472 | SType = StructureType.ShaderModuleCreateInfo, 473 | CodeSize = (nuint)code.Length, 474 | }; 475 | 476 | ShaderModule shaderModule; 477 | 478 | fixed (byte* codePtr = code) 479 | { 480 | createInfo.PCode = (uint*)codePtr; 481 | 482 | if (vk!.CreateShaderModule(device, in createInfo, null, out shaderModule) != Result.Success) 483 | { 484 | throw new Exception(); 485 | } 486 | } 487 | 488 | return shaderModule; 489 | 490 | } 491 | 492 | private SurfaceFormatKHR ChooseSwapSurfaceFormat(IReadOnlyList availableFormats) 493 | { 494 | foreach (var availableFormat in availableFormats) 495 | { 496 | if (availableFormat.Format == Format.B8G8R8A8Srgb && availableFormat.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr) 497 | { 498 | return availableFormat; 499 | } 500 | } 501 | 502 | return availableFormats[0]; 503 | } 504 | 505 | private PresentModeKHR ChoosePresentMode(IReadOnlyList availablePresentModes) 506 | { 507 | foreach (var availablePresentMode in availablePresentModes) 508 | { 509 | if (availablePresentMode == PresentModeKHR.MailboxKhr) 510 | { 511 | return availablePresentMode; 512 | } 513 | } 514 | 515 | return PresentModeKHR.FifoKhr; 516 | } 517 | 518 | private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities) 519 | { 520 | if (capabilities.CurrentExtent.Width != uint.MaxValue) 521 | { 522 | return capabilities.CurrentExtent; 523 | } 524 | else 525 | { 526 | var framebufferSize = window!.FramebufferSize; 527 | 528 | Extent2D actualExtent = new() 529 | { 530 | Width = (uint)framebufferSize.X, 531 | Height = (uint)framebufferSize.Y 532 | }; 533 | 534 | actualExtent.Width = Math.Clamp(actualExtent.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width); 535 | actualExtent.Height = Math.Clamp(actualExtent.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height); 536 | 537 | return actualExtent; 538 | } 539 | } 540 | 541 | private SwapChainSupportDetails QuerySwapChainSupport(PhysicalDevice physicalDevice) 542 | { 543 | var details = new SwapChainSupportDetails(); 544 | 545 | khrSurface!.GetPhysicalDeviceSurfaceCapabilities(physicalDevice, surface, out details.Capabilities); 546 | 547 | uint formatCount = 0; 548 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, null); 549 | 550 | if (formatCount != 0) 551 | { 552 | details.Formats = new SurfaceFormatKHR[formatCount]; 553 | fixed (SurfaceFormatKHR* formatsPtr = details.Formats) 554 | { 555 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, formatsPtr); 556 | } 557 | } 558 | else 559 | { 560 | details.Formats = Array.Empty(); 561 | } 562 | 563 | uint presentModeCount = 0; 564 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, null); 565 | 566 | if (presentModeCount != 0) 567 | { 568 | details.PresentModes = new PresentModeKHR[presentModeCount]; 569 | fixed (PresentModeKHR* formatsPtr = details.PresentModes) 570 | { 571 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, formatsPtr); 572 | } 573 | 574 | } 575 | else 576 | { 577 | details.PresentModes = Array.Empty(); 578 | } 579 | 580 | return details; 581 | } 582 | 583 | private bool IsDeviceSuitable(PhysicalDevice device) 584 | { 585 | var indices = FindQueueFamilies(device); 586 | 587 | bool extensionsSupported = CheckDeviceExtensionsSupport(device); 588 | 589 | bool swapChainAdequate = false; 590 | if (extensionsSupported) 591 | { 592 | var swapChainSupport = QuerySwapChainSupport(device); 593 | swapChainAdequate = swapChainSupport.Formats.Any() && swapChainSupport.PresentModes.Any(); 594 | } 595 | 596 | return indices.IsComplete() && extensionsSupported && swapChainAdequate; 597 | } 598 | 599 | private bool CheckDeviceExtensionsSupport(PhysicalDevice device) 600 | { 601 | uint extentionsCount = 0; 602 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, null); 603 | 604 | var availableExtensions = new ExtensionProperties[extentionsCount]; 605 | fixed (ExtensionProperties* availableExtensionsPtr = availableExtensions) 606 | { 607 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, availableExtensionsPtr); 608 | } 609 | 610 | var availableExtensionNames = availableExtensions.Select(extension => Marshal.PtrToStringAnsi((IntPtr)extension.ExtensionName)).ToHashSet(); 611 | 612 | return deviceExtensions.All(availableExtensionNames.Contains); 613 | 614 | } 615 | 616 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 617 | { 618 | var indices = new QueueFamilyIndices(); 619 | 620 | uint queueFamilityCount = 0; 621 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 622 | 623 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 624 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 625 | { 626 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 627 | } 628 | 629 | 630 | uint i = 0; 631 | foreach (var queueFamily in queueFamilies) 632 | { 633 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 634 | { 635 | indices.GraphicsFamily = i; 636 | } 637 | 638 | khrSurface!.GetPhysicalDeviceSurfaceSupport(device, i, surface, out var presentSupport); 639 | 640 | if (presentSupport) 641 | { 642 | indices.PresentFamily = i; 643 | } 644 | 645 | if (indices.IsComplete()) 646 | { 647 | break; 648 | } 649 | 650 | i++; 651 | } 652 | 653 | return indices; 654 | } 655 | 656 | private string[] GetRequiredExtensions() 657 | { 658 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 659 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 660 | 661 | if (EnableValidationLayers) 662 | { 663 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 664 | } 665 | 666 | return extensions; 667 | } 668 | 669 | private bool CheckValidationLayerSupport() 670 | { 671 | uint layerCount = 0; 672 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 673 | var availableLayers = new LayerProperties[layerCount]; 674 | fixed (LayerProperties* availableLayersPtr = availableLayers) 675 | { 676 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 677 | } 678 | 679 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 680 | 681 | return validationLayers.All(availableLayerNames.Contains); 682 | } 683 | 684 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 685 | { 686 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 687 | 688 | return Vk.False; 689 | } 690 | } 691 | -------------------------------------------------------------------------------- /Source/10_FixedFunctions/10_FixedFunctions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/10_FixedFunctions/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using Silk.NET.Core; 4 | using Silk.NET.Core.Native; 5 | using Silk.NET.Maths; 6 | using Silk.NET.Vulkan; 7 | using Silk.NET.Vulkan.Extensions.EXT; 8 | using Silk.NET.Vulkan.Extensions.KHR; 9 | using Silk.NET.Windowing; 10 | 11 | 12 | var app = new HelloTriangleApplication(); 13 | app.Run(); 14 | 15 | struct QueueFamilyIndices 16 | { 17 | public uint? GraphicsFamily { get; set; } 18 | public uint? PresentFamily { get; set; } 19 | 20 | public bool IsComplete() 21 | { 22 | return GraphicsFamily.HasValue && PresentFamily.HasValue; 23 | } 24 | } 25 | 26 | struct SwapChainSupportDetails 27 | { 28 | public SurfaceCapabilitiesKHR Capabilities; 29 | public SurfaceFormatKHR[] Formats; 30 | public PresentModeKHR[] PresentModes; 31 | } 32 | 33 | unsafe class HelloTriangleApplication 34 | { 35 | const int WIDTH = 800; 36 | const int HEIGHT = 600; 37 | 38 | bool EnableValidationLayers = true; 39 | 40 | private readonly string[] validationLayers = new[] 41 | { 42 | "VK_LAYER_KHRONOS_validation" 43 | }; 44 | 45 | private readonly string[] deviceExtensions = new[] 46 | { 47 | KhrSwapchain.ExtensionName 48 | }; 49 | 50 | private IWindow? window; 51 | private Vk? vk; 52 | 53 | private Instance instance; 54 | 55 | private ExtDebugUtils? debugUtils; 56 | private DebugUtilsMessengerEXT debugMessenger; 57 | private KhrSurface? khrSurface; 58 | private SurfaceKHR surface; 59 | 60 | private PhysicalDevice physicalDevice; 61 | private Device device; 62 | 63 | private Queue graphicsQueue; 64 | private Queue presentQueue; 65 | 66 | private KhrSwapchain? khrSwapChain; 67 | private SwapchainKHR swapChain; 68 | private Image[]? swapChainImages; 69 | private Format swapChainImageFormat; 70 | private Extent2D swapChainExtent; 71 | private ImageView[]? swapChainImageViews; 72 | 73 | private PipelineLayout pipelineLayout; 74 | 75 | public void Run() 76 | { 77 | InitWindow(); 78 | InitVulkan(); 79 | MainLoop(); 80 | CleanUp(); 81 | } 82 | 83 | private void InitWindow() 84 | { 85 | //Create a window. 86 | var options = WindowOptions.DefaultVulkan with 87 | { 88 | Size = new Vector2D(WIDTH, HEIGHT), 89 | Title = "Vulkan", 90 | }; 91 | 92 | window = Window.Create(options); 93 | window.Initialize(); 94 | 95 | if (window.VkSurface is null) 96 | { 97 | throw new Exception("Windowing platform doesn't support Vulkan."); 98 | } 99 | } 100 | 101 | private void InitVulkan() 102 | { 103 | CreateInstance(); 104 | SetupDebugMessenger(); 105 | CreateSurface(); 106 | PickPhysicalDevice(); 107 | CreateLogicalDevice(); 108 | CreateSwapChain(); 109 | CreateImageViews(); 110 | CreateGraphicsPipeline(); 111 | } 112 | 113 | private void MainLoop() 114 | { 115 | window!.Run(); 116 | } 117 | 118 | private void CleanUp() 119 | { 120 | vk!.DestroyPipelineLayout(device, pipelineLayout, null); 121 | 122 | foreach (var imageView in swapChainImageViews!) 123 | { 124 | vk!.DestroyImageView(device, imageView, null); 125 | } 126 | 127 | khrSwapChain!.DestroySwapchain(device, swapChain, null); 128 | 129 | vk!.DestroyDevice(device, null); 130 | 131 | if (EnableValidationLayers) 132 | { 133 | //DestroyDebugUtilsMessenger equivilant to method DestroyDebugUtilsMessengerEXT from original tutorial. 134 | debugUtils!.DestroyDebugUtilsMessenger(instance, debugMessenger, null); 135 | } 136 | 137 | khrSurface!.DestroySurface(instance, surface, null); 138 | vk!.DestroyInstance(instance, null); 139 | vk!.Dispose(); 140 | 141 | window?.Dispose(); 142 | } 143 | 144 | private void CreateInstance() 145 | { 146 | vk = Vk.GetApi(); 147 | 148 | if (EnableValidationLayers && !CheckValidationLayerSupport()) 149 | { 150 | throw new Exception("validation layers requested, but not available!"); 151 | } 152 | 153 | ApplicationInfo appInfo = new() 154 | { 155 | SType = StructureType.ApplicationInfo, 156 | PApplicationName = (byte*)Marshal.StringToHGlobalAnsi("Hello Triangle"), 157 | ApplicationVersion = new Version32(1, 0, 0), 158 | PEngineName = (byte*)Marshal.StringToHGlobalAnsi("No Engine"), 159 | EngineVersion = new Version32(1, 0, 0), 160 | ApiVersion = Vk.Version12 161 | }; 162 | 163 | InstanceCreateInfo createInfo = new() 164 | { 165 | SType = StructureType.InstanceCreateInfo, 166 | PApplicationInfo = &appInfo 167 | }; 168 | 169 | var extensions = GetRequiredExtensions(); 170 | createInfo.EnabledExtensionCount = (uint)extensions.Length; 171 | createInfo.PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(extensions); ; 172 | 173 | if (EnableValidationLayers) 174 | { 175 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 176 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 177 | 178 | DebugUtilsMessengerCreateInfoEXT debugCreateInfo = new(); 179 | PopulateDebugMessengerCreateInfo(ref debugCreateInfo); 180 | createInfo.PNext = &debugCreateInfo; 181 | } 182 | else 183 | { 184 | createInfo.EnabledLayerCount = 0; 185 | createInfo.PNext = null; 186 | } 187 | 188 | if (vk.CreateInstance(in createInfo, null, out instance) != Result.Success) 189 | { 190 | throw new Exception("failed to create instance!"); 191 | } 192 | 193 | Marshal.FreeHGlobal((IntPtr)appInfo.PApplicationName); 194 | Marshal.FreeHGlobal((IntPtr)appInfo.PEngineName); 195 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 196 | 197 | if (EnableValidationLayers) 198 | { 199 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 200 | } 201 | } 202 | 203 | private void PopulateDebugMessengerCreateInfo(ref DebugUtilsMessengerCreateInfoEXT createInfo) 204 | { 205 | createInfo.SType = StructureType.DebugUtilsMessengerCreateInfoExt; 206 | createInfo.MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt | 207 | DebugUtilsMessageSeverityFlagsEXT.WarningBitExt | 208 | DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt; 209 | createInfo.MessageType = DebugUtilsMessageTypeFlagsEXT.GeneralBitExt | 210 | DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt | 211 | DebugUtilsMessageTypeFlagsEXT.ValidationBitExt; 212 | createInfo.PfnUserCallback = (DebugUtilsMessengerCallbackFunctionEXT)DebugCallback; 213 | } 214 | 215 | private void SetupDebugMessenger() 216 | { 217 | if (!EnableValidationLayers) return; 218 | 219 | //TryGetInstanceExtension equivilant to method CreateDebugUtilsMessengerEXT from original tutorial. 220 | if (!vk!.TryGetInstanceExtension(instance, out debugUtils)) return; 221 | 222 | DebugUtilsMessengerCreateInfoEXT createInfo = new(); 223 | PopulateDebugMessengerCreateInfo(ref createInfo); 224 | 225 | if (debugUtils!.CreateDebugUtilsMessenger(instance, in createInfo, null, out debugMessenger) != Result.Success) 226 | { 227 | throw new Exception("failed to set up debug messenger!"); 228 | } 229 | } 230 | 231 | private void CreateSurface() 232 | { 233 | if (!vk!.TryGetInstanceExtension(instance, out khrSurface)) 234 | { 235 | throw new NotSupportedException("KHR_surface extension not found."); 236 | } 237 | 238 | surface = window!.VkSurface!.Create(instance.ToHandle(), null).ToSurface(); 239 | } 240 | 241 | private void PickPhysicalDevice() 242 | { 243 | var devices = vk!.GetPhysicalDevices(instance); 244 | 245 | foreach (var device in devices) 246 | { 247 | if (IsDeviceSuitable(device)) 248 | { 249 | physicalDevice = device; 250 | break; 251 | } 252 | } 253 | 254 | if (physicalDevice.Handle == 0) 255 | { 256 | throw new Exception("failed to find a suitable GPU!"); 257 | } 258 | } 259 | 260 | private void CreateLogicalDevice() 261 | { 262 | var indices = FindQueueFamilies(physicalDevice); 263 | 264 | var uniqueQueueFamilies = new[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 265 | uniqueQueueFamilies = uniqueQueueFamilies.Distinct().ToArray(); 266 | 267 | using var mem = GlobalMemory.Allocate(uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo)); 268 | var queueCreateInfos = (DeviceQueueCreateInfo*)Unsafe.AsPointer(ref mem.GetPinnableReference()); 269 | 270 | float queuePriority = 1.0f; 271 | for (int i = 0; i < uniqueQueueFamilies.Length; i++) 272 | { 273 | queueCreateInfos[i] = new() 274 | { 275 | SType = StructureType.DeviceQueueCreateInfo, 276 | QueueFamilyIndex = uniqueQueueFamilies[i], 277 | QueueCount = 1, 278 | PQueuePriorities = &queuePriority 279 | }; 280 | } 281 | 282 | PhysicalDeviceFeatures deviceFeatures = new(); 283 | 284 | DeviceCreateInfo createInfo = new() 285 | { 286 | SType = StructureType.DeviceCreateInfo, 287 | QueueCreateInfoCount = (uint)uniqueQueueFamilies.Length, 288 | PQueueCreateInfos = queueCreateInfos, 289 | 290 | PEnabledFeatures = &deviceFeatures, 291 | 292 | EnabledExtensionCount = (uint)deviceExtensions.Length, 293 | PpEnabledExtensionNames = (byte**)SilkMarshal.StringArrayToPtr(deviceExtensions) 294 | }; 295 | 296 | if (EnableValidationLayers) 297 | { 298 | createInfo.EnabledLayerCount = (uint)validationLayers.Length; 299 | createInfo.PpEnabledLayerNames = (byte**)SilkMarshal.StringArrayToPtr(validationLayers); 300 | } 301 | else 302 | { 303 | createInfo.EnabledLayerCount = 0; 304 | } 305 | 306 | if (vk!.CreateDevice(physicalDevice, in createInfo, null, out device) != Result.Success) 307 | { 308 | throw new Exception("failed to create logical device!"); 309 | } 310 | 311 | vk!.GetDeviceQueue(device, indices.GraphicsFamily!.Value, 0, out graphicsQueue); 312 | vk!.GetDeviceQueue(device, indices.PresentFamily!.Value, 0, out presentQueue); 313 | 314 | if (EnableValidationLayers) 315 | { 316 | SilkMarshal.Free((nint)createInfo.PpEnabledLayerNames); 317 | } 318 | 319 | SilkMarshal.Free((nint)createInfo.PpEnabledExtensionNames); 320 | 321 | } 322 | 323 | private void CreateSwapChain() 324 | { 325 | var swapChainSupport = QuerySwapChainSupport(physicalDevice); 326 | 327 | var surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.Formats); 328 | var presentMode = ChoosePresentMode(swapChainSupport.PresentModes); 329 | var extent = ChooseSwapExtent(swapChainSupport.Capabilities); 330 | 331 | var imageCount = swapChainSupport.Capabilities.MinImageCount + 1; 332 | if (swapChainSupport.Capabilities.MaxImageCount > 0 && imageCount > swapChainSupport.Capabilities.MaxImageCount) 333 | { 334 | imageCount = swapChainSupport.Capabilities.MaxImageCount; 335 | } 336 | 337 | SwapchainCreateInfoKHR creatInfo = new() 338 | { 339 | SType = StructureType.SwapchainCreateInfoKhr, 340 | Surface = surface, 341 | 342 | MinImageCount = imageCount, 343 | ImageFormat = surfaceFormat.Format, 344 | ImageColorSpace = surfaceFormat.ColorSpace, 345 | ImageExtent = extent, 346 | ImageArrayLayers = 1, 347 | ImageUsage = ImageUsageFlags.ColorAttachmentBit, 348 | }; 349 | 350 | var indices = FindQueueFamilies(physicalDevice); 351 | var queueFamilyIndices = stackalloc[] { indices.GraphicsFamily!.Value, indices.PresentFamily!.Value }; 352 | 353 | if (indices.GraphicsFamily != indices.PresentFamily) 354 | { 355 | creatInfo = creatInfo with 356 | { 357 | ImageSharingMode = SharingMode.Concurrent, 358 | QueueFamilyIndexCount = 2, 359 | PQueueFamilyIndices = queueFamilyIndices, 360 | }; 361 | } 362 | else 363 | { 364 | creatInfo.ImageSharingMode = SharingMode.Exclusive; 365 | } 366 | 367 | creatInfo = creatInfo with 368 | { 369 | PreTransform = swapChainSupport.Capabilities.CurrentTransform, 370 | CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, 371 | PresentMode = presentMode, 372 | Clipped = true, 373 | 374 | OldSwapchain = default 375 | }; 376 | 377 | if (!vk!.TryGetDeviceExtension(instance, device, out khrSwapChain)) 378 | { 379 | throw new NotSupportedException("VK_KHR_swapchain extension not found."); 380 | } 381 | 382 | if (khrSwapChain!.CreateSwapchain(device, in creatInfo, null, out swapChain) != Result.Success) 383 | { 384 | throw new Exception("failed to create swap chain!"); 385 | } 386 | 387 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, null); 388 | swapChainImages = new Image[imageCount]; 389 | fixed (Image* swapChainImagesPtr = swapChainImages) 390 | { 391 | khrSwapChain.GetSwapchainImages(device, swapChain, ref imageCount, swapChainImagesPtr); 392 | } 393 | 394 | swapChainImageFormat = surfaceFormat.Format; 395 | swapChainExtent = extent; 396 | } 397 | 398 | private void CreateImageViews() 399 | { 400 | swapChainImageViews = new ImageView[swapChainImages!.Length]; 401 | 402 | for (int i = 0; i < swapChainImages.Length; i++) 403 | { 404 | ImageViewCreateInfo createInfo = new() 405 | { 406 | SType = StructureType.ImageViewCreateInfo, 407 | Image = swapChainImages[i], 408 | ViewType = ImageViewType.Type2D, 409 | Format = swapChainImageFormat, 410 | Components = 411 | { 412 | R = ComponentSwizzle.Identity, 413 | G = ComponentSwizzle.Identity, 414 | B = ComponentSwizzle.Identity, 415 | A = ComponentSwizzle.Identity, 416 | }, 417 | SubresourceRange = 418 | { 419 | AspectMask = ImageAspectFlags.ColorBit, 420 | BaseMipLevel = 0, 421 | LevelCount = 1, 422 | BaseArrayLayer = 0, 423 | LayerCount = 1, 424 | } 425 | 426 | }; 427 | 428 | if (vk!.CreateImageView(device, in createInfo, null, out swapChainImageViews[i]) != Result.Success) 429 | { 430 | throw new Exception("failed to create image views!"); 431 | } 432 | } 433 | } 434 | 435 | private void CreateGraphicsPipeline() 436 | { 437 | var vertShaderCode = File.ReadAllBytes("shaders/vert.spv"); 438 | var fragShaderCode = File.ReadAllBytes("shaders/frag.spv"); 439 | 440 | var vertShaderModule = CreateShaderModule(vertShaderCode); 441 | var fragShaderModule = CreateShaderModule(fragShaderCode); 442 | 443 | PipelineShaderStageCreateInfo vertShaderStageInfo = new() 444 | { 445 | SType = StructureType.PipelineShaderStageCreateInfo, 446 | Stage = ShaderStageFlags.VertexBit, 447 | Module = vertShaderModule, 448 | PName = (byte*)SilkMarshal.StringToPtr("main") 449 | }; 450 | 451 | PipelineShaderStageCreateInfo fragShaderStageInfo = new() 452 | { 453 | SType = StructureType.PipelineShaderStageCreateInfo, 454 | Stage = ShaderStageFlags.FragmentBit, 455 | Module = fragShaderModule, 456 | PName = (byte*)SilkMarshal.StringToPtr("main") 457 | }; 458 | 459 | var shaderStages = stackalloc[] 460 | { 461 | vertShaderStageInfo, 462 | fragShaderStageInfo 463 | }; 464 | 465 | PipelineVertexInputStateCreateInfo vertexInputInfo = new() 466 | { 467 | SType = StructureType.PipelineVertexInputStateCreateInfo, 468 | VertexBindingDescriptionCount = 0, 469 | VertexAttributeDescriptionCount = 0, 470 | }; 471 | 472 | PipelineInputAssemblyStateCreateInfo inputAssembly = new() 473 | { 474 | SType = StructureType.PipelineInputAssemblyStateCreateInfo, 475 | Topology = PrimitiveTopology.TriangleList, 476 | PrimitiveRestartEnable = false, 477 | }; 478 | 479 | Viewport viewport = new() 480 | { 481 | X = 0, 482 | Y = 0, 483 | Width = swapChainExtent.Width, 484 | Height = swapChainExtent.Height, 485 | MinDepth = 0, 486 | MaxDepth = 1, 487 | }; 488 | 489 | Rect2D scissor = new() 490 | { 491 | Offset = { X = 0, Y = 0 }, 492 | Extent = swapChainExtent, 493 | }; 494 | 495 | PipelineViewportStateCreateInfo viewportState = new() 496 | { 497 | SType = StructureType.PipelineViewportStateCreateInfo, 498 | ViewportCount = 1, 499 | PViewports = &viewport, 500 | ScissorCount = 1, 501 | PScissors = &scissor, 502 | }; 503 | 504 | PipelineRasterizationStateCreateInfo rasterizer = new() 505 | { 506 | SType = StructureType.PipelineRasterizationStateCreateInfo, 507 | DepthClampEnable = false, 508 | RasterizerDiscardEnable = false, 509 | PolygonMode = PolygonMode.Fill, 510 | LineWidth = 1, 511 | CullMode = CullModeFlags.BackBit, 512 | FrontFace = FrontFace.Clockwise, 513 | DepthBiasEnable = false, 514 | }; 515 | 516 | PipelineMultisampleStateCreateInfo multisampling = new() 517 | { 518 | SType = StructureType.PipelineMultisampleStateCreateInfo, 519 | SampleShadingEnable = false, 520 | RasterizationSamples = SampleCountFlags.Count1Bit, 521 | }; 522 | 523 | PipelineColorBlendAttachmentState colorBlendAttachment = new() 524 | { 525 | ColorWriteMask = ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit, 526 | BlendEnable = false, 527 | }; 528 | 529 | PipelineColorBlendStateCreateInfo colorBlending = new() 530 | { 531 | SType = StructureType.PipelineColorBlendStateCreateInfo, 532 | LogicOpEnable = false, 533 | LogicOp = LogicOp.Copy, 534 | AttachmentCount = 1, 535 | PAttachments = &colorBlendAttachment, 536 | }; 537 | 538 | colorBlending.BlendConstants[0] = 0; 539 | colorBlending.BlendConstants[1] = 0; 540 | colorBlending.BlendConstants[2] = 0; 541 | colorBlending.BlendConstants[3] = 0; 542 | 543 | PipelineLayoutCreateInfo pipelineLayoutInfo = new() 544 | { 545 | SType = StructureType.PipelineLayoutCreateInfo, 546 | SetLayoutCount = 0, 547 | PushConstantRangeCount = 0, 548 | }; 549 | 550 | if (vk!.CreatePipelineLayout(device, in pipelineLayoutInfo, null, out pipelineLayout) != Result.Success) 551 | { 552 | throw new Exception("failed to create pipeline layout!"); 553 | } 554 | 555 | 556 | vk!.DestroyShaderModule(device, fragShaderModule, null); 557 | vk!.DestroyShaderModule(device, vertShaderModule, null); 558 | 559 | SilkMarshal.Free((nint)vertShaderStageInfo.PName); 560 | SilkMarshal.Free((nint)fragShaderStageInfo.PName); 561 | } 562 | 563 | private ShaderModule CreateShaderModule(byte[] code) 564 | { 565 | ShaderModuleCreateInfo createInfo = new() 566 | { 567 | SType = StructureType.ShaderModuleCreateInfo, 568 | CodeSize = (nuint)code.Length, 569 | }; 570 | 571 | ShaderModule shaderModule; 572 | 573 | fixed (byte* codePtr = code) 574 | { 575 | createInfo.PCode = (uint*)codePtr; 576 | 577 | if (vk!.CreateShaderModule(device, in createInfo, null, out shaderModule) != Result.Success) 578 | { 579 | throw new Exception(); 580 | } 581 | } 582 | 583 | return shaderModule; 584 | 585 | } 586 | 587 | private SurfaceFormatKHR ChooseSwapSurfaceFormat(IReadOnlyList availableFormats) 588 | { 589 | foreach (var availableFormat in availableFormats) 590 | { 591 | if (availableFormat.Format == Format.B8G8R8A8Srgb && availableFormat.ColorSpace == ColorSpaceKHR.SpaceSrgbNonlinearKhr) 592 | { 593 | return availableFormat; 594 | } 595 | } 596 | 597 | return availableFormats[0]; 598 | } 599 | 600 | private PresentModeKHR ChoosePresentMode(IReadOnlyList availablePresentModes) 601 | { 602 | foreach (var availablePresentMode in availablePresentModes) 603 | { 604 | if (availablePresentMode == PresentModeKHR.MailboxKhr) 605 | { 606 | return availablePresentMode; 607 | } 608 | } 609 | 610 | return PresentModeKHR.FifoKhr; 611 | } 612 | 613 | private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities) 614 | { 615 | if (capabilities.CurrentExtent.Width != uint.MaxValue) 616 | { 617 | return capabilities.CurrentExtent; 618 | } 619 | else 620 | { 621 | var framebufferSize = window!.FramebufferSize; 622 | 623 | Extent2D actualExtent = new() 624 | { 625 | Width = (uint)framebufferSize.X, 626 | Height = (uint)framebufferSize.Y 627 | }; 628 | 629 | actualExtent.Width = Math.Clamp(actualExtent.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width); 630 | actualExtent.Height = Math.Clamp(actualExtent.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height); 631 | 632 | return actualExtent; 633 | } 634 | } 635 | 636 | private SwapChainSupportDetails QuerySwapChainSupport(PhysicalDevice physicalDevice) 637 | { 638 | var details = new SwapChainSupportDetails(); 639 | 640 | khrSurface!.GetPhysicalDeviceSurfaceCapabilities(physicalDevice, surface, out details.Capabilities); 641 | 642 | uint formatCount = 0; 643 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, null); 644 | 645 | if (formatCount != 0) 646 | { 647 | details.Formats = new SurfaceFormatKHR[formatCount]; 648 | fixed (SurfaceFormatKHR* formatsPtr = details.Formats) 649 | { 650 | khrSurface.GetPhysicalDeviceSurfaceFormats(physicalDevice, surface, ref formatCount, formatsPtr); 651 | } 652 | } 653 | else 654 | { 655 | details.Formats = Array.Empty(); 656 | } 657 | 658 | uint presentModeCount = 0; 659 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, null); 660 | 661 | if (presentModeCount != 0) 662 | { 663 | details.PresentModes = new PresentModeKHR[presentModeCount]; 664 | fixed (PresentModeKHR* formatsPtr = details.PresentModes) 665 | { 666 | khrSurface.GetPhysicalDeviceSurfacePresentModes(physicalDevice, surface, ref presentModeCount, formatsPtr); 667 | } 668 | 669 | } 670 | else 671 | { 672 | details.PresentModes = Array.Empty(); 673 | } 674 | 675 | return details; 676 | } 677 | 678 | private bool IsDeviceSuitable(PhysicalDevice device) 679 | { 680 | var indices = FindQueueFamilies(device); 681 | 682 | bool extensionsSupported = CheckDeviceExtensionsSupport(device); 683 | 684 | bool swapChainAdequate = false; 685 | if (extensionsSupported) 686 | { 687 | var swapChainSupport = QuerySwapChainSupport(device); 688 | swapChainAdequate = swapChainSupport.Formats.Any() && swapChainSupport.PresentModes.Any(); 689 | } 690 | 691 | return indices.IsComplete() && extensionsSupported && swapChainAdequate; 692 | } 693 | 694 | private bool CheckDeviceExtensionsSupport(PhysicalDevice device) 695 | { 696 | uint extentionsCount = 0; 697 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, null); 698 | 699 | var availableExtensions = new ExtensionProperties[extentionsCount]; 700 | fixed (ExtensionProperties* availableExtensionsPtr = availableExtensions) 701 | { 702 | vk!.EnumerateDeviceExtensionProperties(device, (byte*)null, ref extentionsCount, availableExtensionsPtr); 703 | } 704 | 705 | var availableExtensionNames = availableExtensions.Select(extension => Marshal.PtrToStringAnsi((IntPtr)extension.ExtensionName)).ToHashSet(); 706 | 707 | return deviceExtensions.All(availableExtensionNames.Contains); 708 | 709 | } 710 | 711 | private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device) 712 | { 713 | var indices = new QueueFamilyIndices(); 714 | 715 | uint queueFamilityCount = 0; 716 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, null); 717 | 718 | var queueFamilies = new QueueFamilyProperties[queueFamilityCount]; 719 | fixed (QueueFamilyProperties* queueFamiliesPtr = queueFamilies) 720 | { 721 | vk!.GetPhysicalDeviceQueueFamilyProperties(device, ref queueFamilityCount, queueFamiliesPtr); 722 | } 723 | 724 | 725 | uint i = 0; 726 | foreach (var queueFamily in queueFamilies) 727 | { 728 | if (queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) 729 | { 730 | indices.GraphicsFamily = i; 731 | } 732 | 733 | khrSurface!.GetPhysicalDeviceSurfaceSupport(device, i, surface, out var presentSupport); 734 | 735 | if (presentSupport) 736 | { 737 | indices.PresentFamily = i; 738 | } 739 | 740 | if (indices.IsComplete()) 741 | { 742 | break; 743 | } 744 | 745 | i++; 746 | } 747 | 748 | return indices; 749 | } 750 | 751 | private string[] GetRequiredExtensions() 752 | { 753 | var glfwExtensions = window!.VkSurface!.GetRequiredExtensions(out var glfwExtensionCount); 754 | var extensions = SilkMarshal.PtrToStringArray((nint)glfwExtensions, (int)glfwExtensionCount); 755 | 756 | if (EnableValidationLayers) 757 | { 758 | return extensions.Append(ExtDebugUtils.ExtensionName).ToArray(); 759 | } 760 | 761 | return extensions; 762 | } 763 | 764 | private bool CheckValidationLayerSupport() 765 | { 766 | uint layerCount = 0; 767 | vk!.EnumerateInstanceLayerProperties(ref layerCount, null); 768 | var availableLayers = new LayerProperties[layerCount]; 769 | fixed (LayerProperties* availableLayersPtr = availableLayers) 770 | { 771 | vk!.EnumerateInstanceLayerProperties(ref layerCount, availableLayersPtr); 772 | } 773 | 774 | var availableLayerNames = availableLayers.Select(layer => Marshal.PtrToStringAnsi((IntPtr)layer.LayerName)).ToHashSet(); 775 | 776 | return validationLayers.All(availableLayerNames.Contains); 777 | } 778 | 779 | private uint DebugCallback(DebugUtilsMessageSeverityFlagsEXT messageSeverity, DebugUtilsMessageTypeFlagsEXT messageTypes, DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) 780 | { 781 | Console.WriteLine($"validation layer:" + Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage)); 782 | 783 | return Vk.False; 784 | } 785 | } -------------------------------------------------------------------------------- /Source/11_RenderPasses/11_RenderPasses.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/12_GraphicsPipelineComplete/12_GraphicsPipelineComplete.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/13_FrameBuffers/13_FrameBuffers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/14_CommandBuffers/14_CommandBuffers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/15_HelloTriangle/15_HelloTriangle.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/17_SwapChainRecreation/17_SwapChainRecreation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/18_VertexInput/18_VertexInput.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Source/18_VertexInput/18_shader_vertexbuffer.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /Source/18_VertexInput/18_shader_vertexbuffer.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 inPosition; 4 | layout(location = 1) in vec3 inColor; 5 | 6 | layout(location = 0) out vec3 fragColor; 7 | 8 | void main() { 9 | gl_Position = vec4(inPosition, 0.0, 1.0); 10 | fragColor = inColor; 11 | } 12 | -------------------------------------------------------------------------------- /Source/19_VertexBuffer/19_VertexBuffer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Source/20_StagingBuffer/20_StagingBuffer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Source/21_IndexBuffer/21_IndexBuffer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Source/22_DescriptorSetLayout/22_DescriptorSetLayout.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Source/22_DescriptorSetLayout/22_shader_ubo.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /Source/22_DescriptorSetLayout/22_shader_ubo.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 proj; 7 | } ubo; 8 | 9 | layout(location = 0) in vec2 inPosition; 10 | layout(location = 1) in vec3 inColor; 11 | 12 | layout(location = 0) out vec3 fragColor; 13 | 14 | void main() { 15 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 16 | fragColor = inColor; 17 | } 18 | -------------------------------------------------------------------------------- /Source/23_DescriptorSets/23_DescriptorSets.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Source/24_TextureImage/24_TextureImage.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Source/25_Sampler/25_Sampler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Source/26_TextureMapping/26_TextureMapping.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Source/26_TextureMapping/26_shader_textures.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 1) uniform sampler2D texSampler; 4 | 5 | layout(location = 0) in vec3 fragColor; 6 | layout(location = 1) in vec2 fragTexCoord; 7 | 8 | layout(location = 0) out vec4 outColor; 9 | 10 | void main() { 11 | outColor = texture(texSampler, fragTexCoord); 12 | } 13 | -------------------------------------------------------------------------------- /Source/26_TextureMapping/26_shader_textures.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 proj; 7 | } ubo; 8 | 9 | layout(location = 0) in vec2 inPosition; 10 | layout(location = 1) in vec3 inColor; 11 | layout(location = 2) in vec2 inTexCoord; 12 | 13 | layout(location = 0) out vec3 fragColor; 14 | layout(location = 1) out vec2 fragTexCoord; 15 | 16 | void main() { 17 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 18 | fragColor = inColor; 19 | fragTexCoord = inTexCoord; 20 | } 21 | -------------------------------------------------------------------------------- /Source/27_DepthBuffering/27_DepthBuffering.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Source/27_DepthBuffering/27_shader_depth.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 1) uniform sampler2D texSampler; 4 | 5 | layout(location = 0) in vec3 fragColor; 6 | layout(location = 1) in vec2 fragTexCoord; 7 | 8 | layout(location = 0) out vec4 outColor; 9 | 10 | void main() { 11 | outColor = texture(texSampler, fragTexCoord); 12 | } 13 | -------------------------------------------------------------------------------- /Source/27_DepthBuffering/27_shader_depth.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 proj; 7 | } ubo; 8 | 9 | layout(location = 0) in vec3 inPosition; 10 | layout(location = 1) in vec3 inColor; 11 | layout(location = 2) in vec2 inTexCoord; 12 | 13 | layout(location = 0) out vec3 fragColor; 14 | layout(location = 1) out vec2 fragTexCoord; 15 | 16 | void main() { 17 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); 18 | fragColor = inColor; 19 | fragTexCoord = inTexCoord; 20 | } 21 | -------------------------------------------------------------------------------- /Source/28_ModelLoading/28_ModelLoading.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Source/29_MipMapping/29_MipMapping.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Source/30_Multisampling/30_Multisampling.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Source/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(VULKAN_SDK)\Bin 4 | Shaders 5 | 6 | -------------------------------------------------------------------------------- /Source/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Source/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Source/NewChapter.csx: -------------------------------------------------------------------------------- 1 | 2 | using System.IO; 3 | using System.Diagnostics; 4 | 5 | var currentChapter = Directory.EnumerateDirectories(Directory.GetCurrentDirectory()).OrderByDescending(d => d).First(); 6 | currentChapter = Path.GetFileName(currentChapter); 7 | var number = currentChapter.Split('_')[0]; 8 | var nextNumber = int.Parse(number) + 1; 9 | var nextChapter = $"{nextNumber}_{Args[0]}"; 10 | 11 | Console.WriteLine($"Creating chapter {nextChapter}"); 12 | 13 | Directory.CreateDirectory(nextChapter); 14 | 15 | var extensions = new HashSet(StringComparer.OrdinalIgnoreCase) 16 | { 17 | ".cs", ".csproj", ".frag", ".vert" 18 | }; 19 | 20 | var files = Directory.EnumerateFiles(currentChapter, "*.*", SearchOption.TopDirectoryOnly) 21 | .Where(s => extensions.Contains(Path.GetExtension(s))); 22 | 23 | string nextProjectName = null; 24 | 25 | foreach(var file in files) 26 | { 27 | var destinationFile = Path.Combine(nextChapter, Path.GetFileName(file)); 28 | 29 | if(string.Equals(Path.GetExtension(file), ".csproj",StringComparison.OrdinalIgnoreCase)) 30 | { 31 | destinationFile = Path.Combine(nextChapter, $"{nextChapter}.csproj"); 32 | nextProjectName = destinationFile; 33 | } 34 | 35 | Console.WriteLine($"Copying file {file} --> {destinationFile}"); 36 | 37 | File.Copy(file, destinationFile); 38 | } 39 | 40 | if(nextProjectName is {}) 41 | { 42 | Process.Start("dotnet", $"sln add \"{nextProjectName}\""); 43 | } -------------------------------------------------------------------------------- /Source/NewChapter.ps1: -------------------------------------------------------------------------------- 1 | $nextChapter = $args[0] 2 | 3 | dotnet script .\NewChapter.csx -- $nextChapter -------------------------------------------------------------------------------- /Source/SilkVulkanTutorial.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "00_BaseCode", "00_BaseCode\00_BaseCode.csproj", "{F687F7D7-BB44-462E-8D76-A94F3BCF82DA}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01_InstanceCreation", "01_InstanceCreation\01_InstanceCreation.csproj", "{56ECAD6A-9228-46C3-8E0C-F4066325A69B}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_ValidationLayers", "02_ValidationLayers\02_ValidationLayers.csproj", "{84592EC9-B68B-45A3-AAA7-4744DDC3E9ED}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03_PhysicalDeviceSelection", "03_PhysicalDeviceSelection\03_PhysicalDeviceSelection.csproj", "{42D8754A-E5DD-42AC-82B6-A463C5BEAB07}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04_LogicalDevice", "04_LogicalDevice\04_LogicalDevice.csproj", "{B64E207A-9B80-4E47-B623-CE0F3EE0F1BD}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05_WindowSurface", "05_WindowSurface\05_WindowSurface.csproj", "{C4218416-ADEF-482E-89C6-BF1FB17B63E7}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "06_SwapChainCreation", "06_SwapChainCreation\06_SwapChainCreation.csproj", "{21C07CC2-1363-4DDE-B8A2-B17916A6027A}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "07_ImageViews", "07_ImageViews\07_ImageViews.csproj", "{C491CD74-99BC-4A54-AC83-338428BCA1BA}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "08_GraphicsPipeline", "08_GraphicsPipeline\08_GraphicsPipeline.csproj", "{2DB184AF-F663-4BA1-8B49-AA424F75D3BC}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "09_ShaderModules", "09_ShaderModules\09_ShaderModules.csproj", "{FAF18F85-4036-4CCB-9533-E24D59E59695}" 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{F7CA3EA9-8818-4D0C-9B1A-598480404AAC}" 27 | ProjectSection(SolutionItems) = preProject 28 | Directory.Build.props = Directory.Build.props 29 | Directory.Build.targets = Directory.Build.targets 30 | Directory.Packages.props = Directory.Packages.props 31 | EndProjectSection 32 | EndProject 33 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "10_FixedFunctions", "10_FixedFunctions\10_FixedFunctions.csproj", "{952CA805-7465-425F-AE44-D41C9FFBC1E7}" 34 | EndProject 35 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "11_RenderPasses", "11_RenderPasses\11_RenderPasses.csproj", "{E612CAF1-6541-4875-9D68-5B5B9A5ECE87}" 36 | EndProject 37 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "12_GraphicsPipelineComplete", "12_GraphicsPipelineComplete\12_GraphicsPipelineComplete.csproj", "{25F60CCE-35C4-4AB8-B339-0D23970B2973}" 38 | EndProject 39 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "13_FrameBuffers", "13_FrameBuffers\13_FrameBuffers.csproj", "{35E5BA99-9D0F-4BEC-8909-B7CE2D28F7A0}" 40 | EndProject 41 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "14_CommandBuffers", "14_CommandBuffers\14_CommandBuffers.csproj", "{76ABCCCC-57A2-4ACC-9006-7EA60DC14CCB}" 42 | EndProject 43 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "15_HelloTriangle", "15_HelloTriangle\15_HelloTriangle.csproj", "{335BCBAB-CD9E-43CB-A9EE-2D771548BBCD}" 44 | EndProject 45 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "17_SwapChainRecreation", "17_SwapChainRecreation\17_SwapChainRecreation.csproj", "{23734D23-D986-4995-BDDB-85F794743E0C}" 46 | EndProject 47 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "18_VertexInput", "18_VertexInput\18_VertexInput.csproj", "{01E52DAC-D4B7-4805-BF26-D941F3C90859}" 48 | EndProject 49 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "19_VertexBuffer", "19_VertexBuffer\19_VertexBuffer.csproj", "{1CFA7B95-F309-4D9D-995F-4B43CC47A689}" 50 | EndProject 51 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "20_StagingBuffer", "20_StagingBuffer\20_StagingBuffer.csproj", "{542563D0-E06A-48DA-88E1-0D2567AEC306}" 52 | EndProject 53 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "21_IndexBuffer", "21_IndexBuffer\21_IndexBuffer.csproj", "{1B2EBFFC-BD61-45BB-8098-992C4728309E}" 54 | EndProject 55 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "22_DescriptorSetLayout", "22_DescriptorSetLayout\22_DescriptorSetLayout.csproj", "{8595A9AA-429F-4712-9821-D8BFB3BC37A4}" 56 | EndProject 57 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "23_DescriptorSets", "23_DescriptorSets\23_DescriptorSets.csproj", "{4A357E48-DE54-44D6-8FDE-1F4A5A8EB6F0}" 58 | EndProject 59 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "24_TextureImage", "24_TextureImage\24_TextureImage.csproj", "{21D3CFC9-4B78-4927-8AD8-B16F5D86C6A9}" 60 | EndProject 61 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "25_Sampler", "25_Sampler\25_Sampler.csproj", "{3946F47E-E767-48E1-AE2B-C642861E53F2}" 62 | EndProject 63 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "26_TextureMapping", "26_TextureMapping\26_TextureMapping.csproj", "{2B5BBB67-EB7D-4812-B756-791347844A98}" 64 | EndProject 65 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "27_DepthBuffering", "27_DepthBuffering\27_DepthBuffering.csproj", "{AE8DE34A-02A3-4D60-8C77-5A9495F31138}" 66 | EndProject 67 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "28_ModelLoading", "28_ModelLoading\28_ModelLoading.csproj", "{13EA8FEE-C14F-4970-B046-BF3295A9B3F3}" 68 | EndProject 69 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "29_MipMapping", "29_MipMapping\29_MipMapping.csproj", "{5F826843-AD20-4CFF-B9E0-6860A4A4865C}" 70 | EndProject 71 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "30_Multisampling", "30_Multisampling\30_Multisampling.csproj", "{051D3FF5-C7B8-4B38-9354-EFEB3D47C5FB}" 72 | EndProject 73 | Global 74 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 75 | Debug|Any CPU = Debug|Any CPU 76 | Release|Any CPU = Release|Any CPU 77 | EndGlobalSection 78 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 79 | {F687F7D7-BB44-462E-8D76-A94F3BCF82DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {F687F7D7-BB44-462E-8D76-A94F3BCF82DA}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {F687F7D7-BB44-462E-8D76-A94F3BCF82DA}.Release|Any CPU.ActiveCfg = Release|Any CPU 82 | {F687F7D7-BB44-462E-8D76-A94F3BCF82DA}.Release|Any CPU.Build.0 = Release|Any CPU 83 | {56ECAD6A-9228-46C3-8E0C-F4066325A69B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 84 | {56ECAD6A-9228-46C3-8E0C-F4066325A69B}.Debug|Any CPU.Build.0 = Debug|Any CPU 85 | {56ECAD6A-9228-46C3-8E0C-F4066325A69B}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {56ECAD6A-9228-46C3-8E0C-F4066325A69B}.Release|Any CPU.Build.0 = Release|Any CPU 87 | {84592EC9-B68B-45A3-AAA7-4744DDC3E9ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 88 | {84592EC9-B68B-45A3-AAA7-4744DDC3E9ED}.Debug|Any CPU.Build.0 = Debug|Any CPU 89 | {84592EC9-B68B-45A3-AAA7-4744DDC3E9ED}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {84592EC9-B68B-45A3-AAA7-4744DDC3E9ED}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {42D8754A-E5DD-42AC-82B6-A463C5BEAB07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 | {42D8754A-E5DD-42AC-82B6-A463C5BEAB07}.Debug|Any CPU.Build.0 = Debug|Any CPU 93 | {42D8754A-E5DD-42AC-82B6-A463C5BEAB07}.Release|Any CPU.ActiveCfg = Release|Any CPU 94 | {42D8754A-E5DD-42AC-82B6-A463C5BEAB07}.Release|Any CPU.Build.0 = Release|Any CPU 95 | {B64E207A-9B80-4E47-B623-CE0F3EE0F1BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 96 | {B64E207A-9B80-4E47-B623-CE0F3EE0F1BD}.Debug|Any CPU.Build.0 = Debug|Any CPU 97 | {B64E207A-9B80-4E47-B623-CE0F3EE0F1BD}.Release|Any CPU.ActiveCfg = Release|Any CPU 98 | {B64E207A-9B80-4E47-B623-CE0F3EE0F1BD}.Release|Any CPU.Build.0 = Release|Any CPU 99 | {C4218416-ADEF-482E-89C6-BF1FB17B63E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 100 | {C4218416-ADEF-482E-89C6-BF1FB17B63E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 101 | {C4218416-ADEF-482E-89C6-BF1FB17B63E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 102 | {C4218416-ADEF-482E-89C6-BF1FB17B63E7}.Release|Any CPU.Build.0 = Release|Any CPU 103 | {21C07CC2-1363-4DDE-B8A2-B17916A6027A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 104 | {21C07CC2-1363-4DDE-B8A2-B17916A6027A}.Debug|Any CPU.Build.0 = Debug|Any CPU 105 | {21C07CC2-1363-4DDE-B8A2-B17916A6027A}.Release|Any CPU.ActiveCfg = Release|Any CPU 106 | {21C07CC2-1363-4DDE-B8A2-B17916A6027A}.Release|Any CPU.Build.0 = Release|Any CPU 107 | {C491CD74-99BC-4A54-AC83-338428BCA1BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 108 | {C491CD74-99BC-4A54-AC83-338428BCA1BA}.Debug|Any CPU.Build.0 = Debug|Any CPU 109 | {C491CD74-99BC-4A54-AC83-338428BCA1BA}.Release|Any CPU.ActiveCfg = Release|Any CPU 110 | {C491CD74-99BC-4A54-AC83-338428BCA1BA}.Release|Any CPU.Build.0 = Release|Any CPU 111 | {2DB184AF-F663-4BA1-8B49-AA424F75D3BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 112 | {2DB184AF-F663-4BA1-8B49-AA424F75D3BC}.Debug|Any CPU.Build.0 = Debug|Any CPU 113 | {2DB184AF-F663-4BA1-8B49-AA424F75D3BC}.Release|Any CPU.ActiveCfg = Release|Any CPU 114 | {2DB184AF-F663-4BA1-8B49-AA424F75D3BC}.Release|Any CPU.Build.0 = Release|Any CPU 115 | {FAF18F85-4036-4CCB-9533-E24D59E59695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 116 | {FAF18F85-4036-4CCB-9533-E24D59E59695}.Debug|Any CPU.Build.0 = Debug|Any CPU 117 | {FAF18F85-4036-4CCB-9533-E24D59E59695}.Release|Any CPU.ActiveCfg = Release|Any CPU 118 | {FAF18F85-4036-4CCB-9533-E24D59E59695}.Release|Any CPU.Build.0 = Release|Any CPU 119 | {952CA805-7465-425F-AE44-D41C9FFBC1E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 120 | {952CA805-7465-425F-AE44-D41C9FFBC1E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 121 | {952CA805-7465-425F-AE44-D41C9FFBC1E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 122 | {952CA805-7465-425F-AE44-D41C9FFBC1E7}.Release|Any CPU.Build.0 = Release|Any CPU 123 | {E612CAF1-6541-4875-9D68-5B5B9A5ECE87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 124 | {E612CAF1-6541-4875-9D68-5B5B9A5ECE87}.Debug|Any CPU.Build.0 = Debug|Any CPU 125 | {E612CAF1-6541-4875-9D68-5B5B9A5ECE87}.Release|Any CPU.ActiveCfg = Release|Any CPU 126 | {E612CAF1-6541-4875-9D68-5B5B9A5ECE87}.Release|Any CPU.Build.0 = Release|Any CPU 127 | {25F60CCE-35C4-4AB8-B339-0D23970B2973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 128 | {25F60CCE-35C4-4AB8-B339-0D23970B2973}.Debug|Any CPU.Build.0 = Debug|Any CPU 129 | {25F60CCE-35C4-4AB8-B339-0D23970B2973}.Release|Any CPU.ActiveCfg = Release|Any CPU 130 | {25F60CCE-35C4-4AB8-B339-0D23970B2973}.Release|Any CPU.Build.0 = Release|Any CPU 131 | {35E5BA99-9D0F-4BEC-8909-B7CE2D28F7A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 132 | {35E5BA99-9D0F-4BEC-8909-B7CE2D28F7A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 133 | {35E5BA99-9D0F-4BEC-8909-B7CE2D28F7A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 134 | {35E5BA99-9D0F-4BEC-8909-B7CE2D28F7A0}.Release|Any CPU.Build.0 = Release|Any CPU 135 | {76ABCCCC-57A2-4ACC-9006-7EA60DC14CCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 136 | {76ABCCCC-57A2-4ACC-9006-7EA60DC14CCB}.Debug|Any CPU.Build.0 = Debug|Any CPU 137 | {76ABCCCC-57A2-4ACC-9006-7EA60DC14CCB}.Release|Any CPU.ActiveCfg = Release|Any CPU 138 | {76ABCCCC-57A2-4ACC-9006-7EA60DC14CCB}.Release|Any CPU.Build.0 = Release|Any CPU 139 | {335BCBAB-CD9E-43CB-A9EE-2D771548BBCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 140 | {335BCBAB-CD9E-43CB-A9EE-2D771548BBCD}.Debug|Any CPU.Build.0 = Debug|Any CPU 141 | {335BCBAB-CD9E-43CB-A9EE-2D771548BBCD}.Release|Any CPU.ActiveCfg = Release|Any CPU 142 | {335BCBAB-CD9E-43CB-A9EE-2D771548BBCD}.Release|Any CPU.Build.0 = Release|Any CPU 143 | {23734D23-D986-4995-BDDB-85F794743E0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 144 | {23734D23-D986-4995-BDDB-85F794743E0C}.Debug|Any CPU.Build.0 = Debug|Any CPU 145 | {23734D23-D986-4995-BDDB-85F794743E0C}.Release|Any CPU.ActiveCfg = Release|Any CPU 146 | {23734D23-D986-4995-BDDB-85F794743E0C}.Release|Any CPU.Build.0 = Release|Any CPU 147 | {01E52DAC-D4B7-4805-BF26-D941F3C90859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 148 | {01E52DAC-D4B7-4805-BF26-D941F3C90859}.Debug|Any CPU.Build.0 = Debug|Any CPU 149 | {01E52DAC-D4B7-4805-BF26-D941F3C90859}.Release|Any CPU.ActiveCfg = Release|Any CPU 150 | {01E52DAC-D4B7-4805-BF26-D941F3C90859}.Release|Any CPU.Build.0 = Release|Any CPU 151 | {1CFA7B95-F309-4D9D-995F-4B43CC47A689}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 152 | {1CFA7B95-F309-4D9D-995F-4B43CC47A689}.Debug|Any CPU.Build.0 = Debug|Any CPU 153 | {1CFA7B95-F309-4D9D-995F-4B43CC47A689}.Release|Any CPU.ActiveCfg = Release|Any CPU 154 | {1CFA7B95-F309-4D9D-995F-4B43CC47A689}.Release|Any CPU.Build.0 = Release|Any CPU 155 | {542563D0-E06A-48DA-88E1-0D2567AEC306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 156 | {542563D0-E06A-48DA-88E1-0D2567AEC306}.Debug|Any CPU.Build.0 = Debug|Any CPU 157 | {542563D0-E06A-48DA-88E1-0D2567AEC306}.Release|Any CPU.ActiveCfg = Release|Any CPU 158 | {542563D0-E06A-48DA-88E1-0D2567AEC306}.Release|Any CPU.Build.0 = Release|Any CPU 159 | {1B2EBFFC-BD61-45BB-8098-992C4728309E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 160 | {1B2EBFFC-BD61-45BB-8098-992C4728309E}.Debug|Any CPU.Build.0 = Debug|Any CPU 161 | {1B2EBFFC-BD61-45BB-8098-992C4728309E}.Release|Any CPU.ActiveCfg = Release|Any CPU 162 | {1B2EBFFC-BD61-45BB-8098-992C4728309E}.Release|Any CPU.Build.0 = Release|Any CPU 163 | {8595A9AA-429F-4712-9821-D8BFB3BC37A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 164 | {8595A9AA-429F-4712-9821-D8BFB3BC37A4}.Debug|Any CPU.Build.0 = Debug|Any CPU 165 | {8595A9AA-429F-4712-9821-D8BFB3BC37A4}.Release|Any CPU.ActiveCfg = Release|Any CPU 166 | {8595A9AA-429F-4712-9821-D8BFB3BC37A4}.Release|Any CPU.Build.0 = Release|Any CPU 167 | {4A357E48-DE54-44D6-8FDE-1F4A5A8EB6F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 168 | {4A357E48-DE54-44D6-8FDE-1F4A5A8EB6F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 169 | {4A357E48-DE54-44D6-8FDE-1F4A5A8EB6F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 170 | {4A357E48-DE54-44D6-8FDE-1F4A5A8EB6F0}.Release|Any CPU.Build.0 = Release|Any CPU 171 | {21D3CFC9-4B78-4927-8AD8-B16F5D86C6A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 172 | {21D3CFC9-4B78-4927-8AD8-B16F5D86C6A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 173 | {21D3CFC9-4B78-4927-8AD8-B16F5D86C6A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 174 | {21D3CFC9-4B78-4927-8AD8-B16F5D86C6A9}.Release|Any CPU.Build.0 = Release|Any CPU 175 | {3946F47E-E767-48E1-AE2B-C642861E53F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 176 | {3946F47E-E767-48E1-AE2B-C642861E53F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 177 | {3946F47E-E767-48E1-AE2B-C642861E53F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 178 | {3946F47E-E767-48E1-AE2B-C642861E53F2}.Release|Any CPU.Build.0 = Release|Any CPU 179 | {2B5BBB67-EB7D-4812-B756-791347844A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 180 | {2B5BBB67-EB7D-4812-B756-791347844A98}.Debug|Any CPU.Build.0 = Debug|Any CPU 181 | {2B5BBB67-EB7D-4812-B756-791347844A98}.Release|Any CPU.ActiveCfg = Release|Any CPU 182 | {2B5BBB67-EB7D-4812-B756-791347844A98}.Release|Any CPU.Build.0 = Release|Any CPU 183 | {AE8DE34A-02A3-4D60-8C77-5A9495F31138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 184 | {AE8DE34A-02A3-4D60-8C77-5A9495F31138}.Debug|Any CPU.Build.0 = Debug|Any CPU 185 | {AE8DE34A-02A3-4D60-8C77-5A9495F31138}.Release|Any CPU.ActiveCfg = Release|Any CPU 186 | {AE8DE34A-02A3-4D60-8C77-5A9495F31138}.Release|Any CPU.Build.0 = Release|Any CPU 187 | {13EA8FEE-C14F-4970-B046-BF3295A9B3F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 188 | {13EA8FEE-C14F-4970-B046-BF3295A9B3F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 189 | {13EA8FEE-C14F-4970-B046-BF3295A9B3F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 190 | {13EA8FEE-C14F-4970-B046-BF3295A9B3F3}.Release|Any CPU.Build.0 = Release|Any CPU 191 | {5F826843-AD20-4CFF-B9E0-6860A4A4865C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 192 | {5F826843-AD20-4CFF-B9E0-6860A4A4865C}.Debug|Any CPU.Build.0 = Debug|Any CPU 193 | {5F826843-AD20-4CFF-B9E0-6860A4A4865C}.Release|Any CPU.ActiveCfg = Release|Any CPU 194 | {5F826843-AD20-4CFF-B9E0-6860A4A4865C}.Release|Any CPU.Build.0 = Release|Any CPU 195 | {051D3FF5-C7B8-4B38-9354-EFEB3D47C5FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 196 | {051D3FF5-C7B8-4B38-9354-EFEB3D47C5FB}.Debug|Any CPU.Build.0 = Debug|Any CPU 197 | {051D3FF5-C7B8-4B38-9354-EFEB3D47C5FB}.Release|Any CPU.ActiveCfg = Release|Any CPU 198 | {051D3FF5-C7B8-4B38-9354-EFEB3D47C5FB}.Release|Any CPU.Build.0 = Release|Any CPU 199 | EndGlobalSection 200 | GlobalSection(SolutionProperties) = preSolution 201 | HideSolutionNode = FALSE 202 | EndGlobalSection 203 | GlobalSection(ExtensibilityGlobals) = postSolution 204 | SolutionGuid = {530C8FB5-18D0-43C0-8166-0E7A05EA927D} 205 | EndGlobalSection 206 | EndGlobal 207 | -------------------------------------------------------------------------------- /Source/omnisharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "script": { 3 | "enableScriptNuGetReferences": true, 4 | "defaultTargetFramework": "net6.0" 5 | } 6 | } --------------------------------------------------------------------------------