├── .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 | }
--------------------------------------------------------------------------------