Skip to content

Commit 6351890

Browse files
authored
Enhanced vulkan triangle app (#475)
* Start of enhanced vulkan triangle app * Ignore debug messages in the validation layer, because they produce too much output * Fix validation error which was caused by creating device queues with the same index more than once * Resizing of the window is working now; caching values in two places needed to be disabled otherwise the cached values are out of date and don't allow for successful resize * Enable rendering while resizing * Added a switch to allow event based rendering; if no inputs are received at all no new images will be rendered * Use var in foreach for variable type
1 parent 510de97 commit 6351890

File tree

1 file changed

+144
-77
lines changed

1 file changed

+144
-77
lines changed

src/Lab/VulkanTriangle/HelloTriangleApplication.cs

Lines changed: 144 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Runtime.InteropServices;
1111
using Silk.NET.Core;
1212
using Silk.NET.Core.Native;
13+
using Silk.NET.Maths;
1314
using Silk.NET.Vulkan;
1415
using Silk.NET.Vulkan.Extensions.EXT;
1516
using Silk.NET.Vulkan.Extensions.KHR;
@@ -22,6 +23,7 @@ public class HelloTriangleApplication
2223
{
2324
public const bool EnableValidationLayers = true;
2425
public const int MaxFramesInFlight = 8;
26+
public const bool EventBasedRendering = false;
2527

2628
public void Run()
2729
{
@@ -63,6 +65,8 @@ public void Run()
6365
private Fence[] _imagesInFlight;
6466
private uint _currentFrame;
6567

68+
private bool _framebufferResized = false;
69+
6670
private Vk _vk;
6771
private KhrSurface _vkSurface;
6872
private KhrSwapchain _vkSwapchain;
@@ -74,13 +78,22 @@ public void Run()
7478
private void InitWindow()
7579
{
7680
var opts = WindowOptions.DefaultVulkan;
81+
opts.IsEventDriven = EventBasedRendering;
7782
_window = Window.Create(opts);
7883
if (_window?.VkSurface is null)
7984
{
8085
throw new NotSupportedException("Windowing platform doesn't support Vulkan.");
8186
}
8287

8388
_window.Initialize();
89+
_window.FramebufferResize += OnFramebufferResize;
90+
}
91+
92+
private void OnFramebufferResize(Vector2D<int> size)
93+
{
94+
_framebufferResized = true;
95+
RecreateSwapChain();
96+
_window.DoRender();
8497
}
8598

8699
private void InitVulkan()
@@ -113,9 +126,19 @@ private unsafe void DrawFrame(double obj)
113126
_vk.WaitForFences(_device, 1, in fence, Vk.True, ulong.MaxValue);
114127

115128
uint imageIndex;
116-
_vkSwapchain.AcquireNextImage
129+
Result result =_vkSwapchain.AcquireNextImage
117130
(_device, _swapchain, ulong.MaxValue, _imageAvailableSemaphores[_currentFrame], default, &imageIndex);
118131

132+
if (result == Result.ErrorOutOfDateKhr)
133+
{
134+
RecreateSwapChain();
135+
return;
136+
}
137+
else if (result != Result.Success && result != Result.SuboptimalKhr)
138+
{
139+
throw new Exception("failed to acquire swap chain image!");
140+
}
141+
119142
if (_imagesInFlight[imageIndex].Handle != 0)
120143
{
121144
_vk.WaitForFences(_device, 1, in _imagesInFlight[imageIndex], Vk.True, ulong.MaxValue);
@@ -165,14 +188,26 @@ private unsafe void DrawFrame(double obj)
165188
PImageIndices = &imageIndex
166189
};
167190

168-
_vkSwapchain.QueuePresent(_presentQueue, &presentInfo);
191+
result = _vkSwapchain.QueuePresent(_presentQueue, &presentInfo);
192+
}
193+
194+
if (result == Result.ErrorOutOfDateKhr || result == Result.SuboptimalKhr || _framebufferResized)
195+
{
196+
_framebufferResized = false;
197+
RecreateSwapChain();
198+
}
199+
else if (result != Result.Success)
200+
{
201+
throw new Exception("failed to present swap chain image!");
169202
}
170203

171204
_currentFrame = (_currentFrame + 1) % MaxFramesInFlight;
172205
}
173206

174207
private unsafe void Cleanup()
175208
{
209+
CleanupSwapchain();
210+
176211
for (var i = 0; i < MaxFramesInFlight; i++)
177212
{
178213
_vk.DestroySemaphore(_device, _renderFinishedSemaphores[i], null);
@@ -182,11 +217,29 @@ private unsafe void Cleanup()
182217

183218
_vk.DestroyCommandPool(_device, _commandPool, null);
184219

220+
_vk.DestroyDevice(_device, null);
221+
222+
if (EnableValidationLayers)
223+
{
224+
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugMessenger, null);
225+
}
226+
227+
_vkSurface.DestroySurface(_instance, _surface, null);
228+
_vk.DestroyInstance(_instance, null);
229+
}
230+
231+
private unsafe void CleanupSwapchain()
232+
{
185233
foreach (var framebuffer in _swapchainFramebuffers)
186234
{
187235
_vk.DestroyFramebuffer(_device, framebuffer, null);
188236
}
189237

238+
fixed (CommandBuffer* buffers = _commandBuffers)
239+
{
240+
_vk.FreeCommandBuffers(_device, _commandPool, (uint) _commandBuffers.Length, buffers);
241+
}
242+
190243
_vk.DestroyPipeline(_device, _graphicsPipeline, null);
191244
_vk.DestroyPipelineLayout(_device, _pipelineLayout, null);
192245
_vk.DestroyRenderPass(_device, _renderPass, null);
@@ -197,15 +250,6 @@ private unsafe void Cleanup()
197250
}
198251

199252
_vkSwapchain.DestroySwapchain(_device, _swapchain, null);
200-
_vk.DestroyDevice(_device, null);
201-
202-
if (EnableValidationLayers)
203-
{
204-
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugMessenger, null);
205-
}
206-
207-
_vkSurface.DestroySurface(_instance, _surface, null);
208-
_vk.DestroyInstance(_instance, null);
209253
}
210254

211255
private unsafe void CreateInstance()
@@ -324,8 +368,13 @@ private unsafe uint DebugCallback
324368
void* pUserData
325369
)
326370
{
327-
Console.WriteLine
328-
($"{messageSeverity} {messageTypes}" + Marshal.PtrToStringAnsi((nint) pCallbackData->PMessage));
371+
if (messageSeverity > DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt)
372+
{
373+
Console.WriteLine
374+
($"{messageSeverity} {messageTypes}" + Marshal.PtrToStringAnsi((nint) pCallbackData->PMessage));
375+
376+
}
377+
329378
return Vk.False;
330379
}
331380

@@ -363,101 +412,93 @@ private unsafe void PickPhysicalDevice()
363412
throw new Exception("No suitable device.");
364413
}
365414

366-
private static readonly ConcurrentDictionary<PhysicalDevice, SwapChainSupportDetails> _swapChainSupportDetailsCache
367-
= new ConcurrentDictionary<PhysicalDevice, SwapChainSupportDetails>();
415+
// Caching the returned values breaks the ability for resizing the window
368416
private unsafe SwapChainSupportDetails QuerySwapChainSupport(PhysicalDevice device)
369417
{
370-
return _swapChainSupportDetailsCache.GetOrAdd(device, d =>
371-
{
372-
var details = new SwapChainSupportDetails();
373-
_vkSurface.GetPhysicalDeviceSurfaceCapabilities(d, _surface, out var surfaceCapabilities);
374-
details.Capabilities = surfaceCapabilities;
418+
var details = new SwapChainSupportDetails();
419+
_vkSurface.GetPhysicalDeviceSurfaceCapabilities(device, _surface, out var surfaceCapabilities);
420+
details.Capabilities = surfaceCapabilities;
375421

376-
var formatCount = 0u;
377-
_vkSurface.GetPhysicalDeviceSurfaceFormats(d, _surface, &formatCount, null);
422+
var formatCount = 0u;
423+
_vkSurface.GetPhysicalDeviceSurfaceFormats(device, _surface, &formatCount, null);
378424

379-
if (formatCount != 0)
380-
{
381-
details.Formats = new SurfaceFormatKHR[formatCount];
425+
if (formatCount != 0)
426+
{
427+
details.Formats = new SurfaceFormatKHR[formatCount];
382428

383-
using var mem = GlobalMemory.Allocate((int) formatCount * sizeof(SurfaceFormatKHR));
384-
var formats = (SurfaceFormatKHR*) Unsafe.AsPointer(ref mem.GetPinnableReference());
429+
using var mem = GlobalMemory.Allocate((int) formatCount * sizeof(SurfaceFormatKHR));
430+
var formats = (SurfaceFormatKHR*) Unsafe.AsPointer(ref mem.GetPinnableReference());
385431

386-
_vkSurface.GetPhysicalDeviceSurfaceFormats(d, _surface, &formatCount, formats);
432+
_vkSurface.GetPhysicalDeviceSurfaceFormats(device, _surface, &formatCount, formats);
387433

388-
for (var i = 0; i < formatCount; i++)
389-
{
390-
details.Formats[i] = formats[i];
391-
}
434+
for (var i = 0; i < formatCount; i++)
435+
{
436+
details.Formats[i] = formats[i];
392437
}
438+
}
393439

394-
var presentModeCount = 0u;
395-
_vkSurface.GetPhysicalDeviceSurfacePresentModes(d, _surface, &presentModeCount, null);
440+
var presentModeCount = 0u;
441+
_vkSurface.GetPhysicalDeviceSurfacePresentModes(device, _surface, &presentModeCount, null);
396442

397-
if (presentModeCount != 0)
398-
{
399-
details.PresentModes = new PresentModeKHR[presentModeCount];
443+
if (presentModeCount != 0)
444+
{
445+
details.PresentModes = new PresentModeKHR[presentModeCount];
400446

401-
using var mem = GlobalMemory.Allocate((int) presentModeCount * sizeof(PresentModeKHR));
402-
var modes = (PresentModeKHR*) Unsafe.AsPointer(ref mem.GetPinnableReference());
447+
using var mem = GlobalMemory.Allocate((int) presentModeCount * sizeof(PresentModeKHR));
448+
var modes = (PresentModeKHR*) Unsafe.AsPointer(ref mem.GetPinnableReference());
403449

404-
_vkSurface.GetPhysicalDeviceSurfacePresentModes(d, _surface, &presentModeCount, modes);
450+
_vkSurface.GetPhysicalDeviceSurfacePresentModes(device, _surface, &presentModeCount, modes);
405451

406-
for (var i = 0; i < presentModeCount; i++)
407-
{
408-
details.PresentModes[i] = modes[i];
409-
}
452+
for (var i = 0; i < presentModeCount; i++)
453+
{
454+
details.PresentModes[i] = modes[i];
410455
}
456+
}
411457

412-
return details;
413-
});
458+
return details;
414459
}
415460

416461
private unsafe bool CheckDeviceExtensionSupport(PhysicalDevice device)
417462
{
418463
return _deviceExtensions.All(ext => _vk.IsDeviceExtensionPresent(device, ext));
419464
}
420465

421-
private static readonly ConcurrentDictionary<PhysicalDevice, QueueFamilyIndices> _queueFamilyIndicesCache
422-
= new ConcurrentDictionary<PhysicalDevice, QueueFamilyIndices>();
466+
// Caching these values might have unintended side effects
423467
private unsafe QueueFamilyIndices FindQueueFamilies(PhysicalDevice device)
424468
{
425-
return _queueFamilyIndicesCache.GetOrAdd(device, d =>
426-
{
427-
var indices = new QueueFamilyIndices();
469+
var indices = new QueueFamilyIndices();
428470

429-
uint queryFamilyCount = 0;
430-
_vk.GetPhysicalDeviceQueueFamilyProperties(d, &queryFamilyCount, null);
471+
uint queryFamilyCount = 0;
472+
_vk.GetPhysicalDeviceQueueFamilyProperties(device, &queryFamilyCount, null);
431473

432-
using var mem = GlobalMemory.Allocate((int) queryFamilyCount * sizeof(QueueFamilyProperties));
433-
var queueFamilies = (QueueFamilyProperties*) Unsafe.AsPointer(ref mem.GetPinnableReference());
474+
using var mem = GlobalMemory.Allocate((int) queryFamilyCount * sizeof(QueueFamilyProperties));
475+
var queueFamilies = (QueueFamilyProperties*) Unsafe.AsPointer(ref mem.GetPinnableReference());
434476

435-
_vk.GetPhysicalDeviceQueueFamilyProperties(d, &queryFamilyCount, queueFamilies);
436-
for (var i = 0u; i < queryFamilyCount; i++)
477+
_vk.GetPhysicalDeviceQueueFamilyProperties(device, &queryFamilyCount, queueFamilies);
478+
for (var i = 0u; i < queryFamilyCount; i++)
479+
{
480+
var queueFamily = queueFamilies[i];
481+
// note: HasFlag is slow on .NET Core 2.1 and below.
482+
// if you're targeting these versions, use ((queueFamily.QueueFlags & QueueFlags.QueueGraphicsBit) != 0)
483+
if (queueFamily.QueueFlags.HasFlag(QueueFlags.QueueGraphicsBit))
437484
{
438-
var queueFamily = queueFamilies[i];
439-
// note: HasFlag is slow on .NET Core 2.1 and below.
440-
// if you're targeting these versions, use ((queueFamily.QueueFlags & QueueFlags.QueueGraphicsBit) != 0)
441-
if (queueFamily.QueueFlags.HasFlag(QueueFlags.QueueGraphicsBit))
442-
{
443-
indices.GraphicsFamily = i;
444-
}
485+
indices.GraphicsFamily = i;
486+
}
445487

446-
_vkSurface.GetPhysicalDeviceSurfaceSupport(d, i, _surface, out var presentSupport);
488+
_vkSurface.GetPhysicalDeviceSurfaceSupport(device, i, _surface, out var presentSupport);
447489

448-
if (presentSupport == Vk.True)
449-
{
450-
indices.PresentFamily = i;
451-
}
490+
if (presentSupport == Vk.True)
491+
{
492+
indices.PresentFamily = i;
493+
}
452494

453-
if (indices.IsComplete())
454-
{
455-
break;
456-
}
495+
if (indices.IsComplete())
496+
{
497+
break;
457498
}
499+
}
458500

459-
return indices;
460-
});
501+
return indices;
461502
}
462503

463504
public struct QueueFamilyIndices
@@ -481,7 +522,9 @@ public struct SwapChainSupportDetails
481522
private unsafe void CreateLogicalDevice()
482523
{
483524
var indices = FindQueueFamilies(_physicalDevice);
484-
var uniqueQueueFamilies = new[] { indices.GraphicsFamily.Value, indices.PresentFamily.Value };
525+
var uniqueQueueFamilies = indices.GraphicsFamily.Value == indices.PresentFamily.Value
526+
? new[] { indices.GraphicsFamily.Value }
527+
: new[] { indices.GraphicsFamily.Value, indices.PresentFamily.Value };
485528

486529
using var mem = GlobalMemory.Allocate((int) uniqueQueueFamilies.Length * sizeof(DeviceQueueCreateInfo));
487530
var queueCreateInfos = (DeviceQueueCreateInfo*) Unsafe.AsPointer(ref mem.GetPinnableReference());
@@ -624,6 +667,30 @@ private unsafe void CreateSwapChain()
624667
_swapchainExtent = extent;
625668
}
626669

670+
private unsafe void RecreateSwapChain()
671+
{
672+
Vector2D<int> framebufferSize = _window.FramebufferSize;
673+
674+
while (framebufferSize.X == 0 || framebufferSize.Y == 0)
675+
{
676+
framebufferSize = _window.FramebufferSize;
677+
_window.DoEvents();
678+
}
679+
680+
_ = _vk.DeviceWaitIdle(_device);
681+
682+
CleanupSwapchain();
683+
684+
CreateSwapChain();
685+
CreateImageViews();
686+
CreateRenderPass();
687+
CreateGraphicsPipeline();
688+
CreateFramebuffers();
689+
CreateCommandBuffers();
690+
691+
_imagesInFlight = new Fence[_swapchainImages.Length];
692+
}
693+
627694
private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities)
628695
{
629696
if (capabilities.CurrentExtent.Width != uint.MaxValue)
@@ -632,7 +699,7 @@ private Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities)
632699
}
633700

634701
var actualExtent = new Extent2D
635-
{ Height = (uint) _window.Size.Y, Width = (uint) _window.Size.X };
702+
{ Height = (uint) _window.FramebufferSize.Y, Width = (uint) _window.FramebufferSize.X };
636703
actualExtent.Width = new[]
637704
{
638705
capabilities.MinImageExtent.Width,

0 commit comments

Comments
 (0)