first commit
This commit is contained in:
918
src/Program.bf
Normal file
918
src/Program.bf
Normal file
@@ -0,0 +1,918 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Vulkan;
|
||||
using Vulkan.Loader;
|
||||
using Vulkan.Metadata;
|
||||
|
||||
using Glfw;
|
||||
using VulkanMemoryAllocator;
|
||||
|
||||
namespace Vulkan.Example;
|
||||
|
||||
class Program
|
||||
{
|
||||
public static void CheckResult(VkResult result)
|
||||
{
|
||||
if (result != .VkSuccess)
|
||||
Internal.FatalError(scope $"{result}");
|
||||
}
|
||||
|
||||
public static Glfw.Window* window;
|
||||
public static VkSurfaceKHR surface;
|
||||
public static VulkanLoadedFunctions vkPfns;
|
||||
|
||||
public static VkAllocationCallbacks* vkAlloc;
|
||||
public static VkInstance instance;
|
||||
public static PhysicalDevice physicalDevice ~ delete _;
|
||||
public static VkDevice device;
|
||||
|
||||
public static VkQueue graphicsQueue;
|
||||
public static VkQueue transferQueue;
|
||||
public static VkQueue presentQueue;
|
||||
|
||||
public static VmaAllocator allocator;
|
||||
public static VkBuffer vertexBuffer;
|
||||
public static VkBuffer indexBuffer;
|
||||
|
||||
public static VkSwapchainKHR swapchain;
|
||||
public static VkImage[] images;
|
||||
public static VkImageView[] imageViews;
|
||||
public static VkPipelineLayout pipelineLayout;
|
||||
public static VkPipeline pipeline;
|
||||
|
||||
public const int MaxFramesInFlight = 3;
|
||||
public const uint64 Timeout = uint64.MaxValue;
|
||||
public static VkCommandPool commandPool;
|
||||
public static VkCommandBuffer[MaxFramesInFlight] commandBuffers;
|
||||
public static VkSemaphore[MaxFramesInFlight] imageAvalible;
|
||||
public static VkSemaphore[MaxFramesInFlight] renderFinished;
|
||||
public static VkFence[MaxFramesInFlight] renderFinishedFence;
|
||||
|
||||
typealias Vector = (float x, float y, float z);
|
||||
typealias Vertex = (Vector pos, Vector color);
|
||||
public const Vertex[8] vertices = .
|
||||
(
|
||||
((-1, -1, -1), (1, 0, 0)), // Bottom-left-back (Red)
|
||||
(( 1, -1, -1), (0, 1, 0)), // Bottom-right-back (Green)
|
||||
(( 1, 1, -1), (0, 0, 1)), // Top-right-back (Blue)
|
||||
((-1, 1, -1), (1, 1, 0)), // Top-left-back (Yellow)
|
||||
((-1, -1, 1), (1, 0, 1)), // Bottom-left-front (Cyan)
|
||||
(( 1, -1, 1), (0, 1, 1)), // Bottom-right-front (Magenta)
|
||||
(( 1, 1, 1), (1, 0.5f, 0)), // Top-right-front (Orange)
|
||||
((-1, 1, 1), (0.5f, 0, 0.5f)) // Top-left-front (Purple)
|
||||
);
|
||||
public const uint32[36] indices = .
|
||||
(
|
||||
0, 1, 2, 0, 2, 3, // Back face
|
||||
4, 7, 6, 4, 6, 5, // Front face
|
||||
0, 3, 7, 0, 7, 4, // Left face
|
||||
1, 2, 6, 1, 6, 5, // Right face
|
||||
0, 1, 5, 0, 5, 4, // Bottom face
|
||||
2, 3, 7, 2, 7, 6 // Top face
|
||||
);
|
||||
public struct PushConstants : this(float[16] view);
|
||||
|
||||
public class PhysicalDevice
|
||||
{
|
||||
public const VulkanExtension[?] requiredExtensions = .(.VK_KHR_swapchain);
|
||||
public const VulkanFeature[?] requiredFeatures = .(.dynamicRendering, .fillModeNonSolid);
|
||||
|
||||
public VkPhysicalDevice device;
|
||||
public VulkanApiVersion apiVersion;
|
||||
public VkPhysicalDeviceType kind;
|
||||
public StringView name;
|
||||
public VkSurfaceFormatKHR swapchainFormat;
|
||||
|
||||
public int graphicsFamilyIndex = -1;
|
||||
public int transferFamilyIndex = -1;
|
||||
public int presentFamilyIndex = -1;
|
||||
|
||||
append List<char8*> enabledExtensionNames = .(extensionsFinal.Count);
|
||||
|
||||
public bool Init(VkPhysicalDevice device)
|
||||
{
|
||||
this.device = device;
|
||||
device.GetProperties(var properties);
|
||||
apiVersion = (.)properties.apiVersion;
|
||||
name = .(&properties.deviceName);
|
||||
|
||||
device.GetQueueFamilyProperties_Scope!(let queueFamilies);
|
||||
for (let family in queueFamilies)
|
||||
{
|
||||
if (family.queueFlags.HasFlag(.Transfer))
|
||||
transferFamilyIndex = @family;
|
||||
if (family.queueFlags.HasFlag(.Graphics))
|
||||
graphicsFamilyIndex = @family;
|
||||
if (Glfw.GetPhysicalDevicePresentationSupport((.)instance, (.)device, (.)@family))
|
||||
presentFamilyIndex = @family;
|
||||
}
|
||||
if (graphicsFamilyIndex < 0 || transferFamilyIndex < 0 || presentFamilyIndex < 0)
|
||||
{
|
||||
Debug.WriteLine($"[Vulkan.Example] {name}: Missing queues");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkResult result = device.EnumerateDeviceExtensionProperties_Scope!(null, let extensions);
|
||||
CheckResult(result);
|
||||
findExts: for (let final in extensionsFinal)
|
||||
{
|
||||
let promotedTo = final.PromotedTo;
|
||||
if (promotedTo != 0 && apiVersion >= promotedTo)
|
||||
continue;
|
||||
char8* cstr = final.Name;
|
||||
for (var ext in extensions)
|
||||
if (String.Equals(cstr, &ext.extensionName))
|
||||
{
|
||||
enabledExtensionNames.Add(cstr);
|
||||
continue findExts;
|
||||
}
|
||||
Debug.WriteLine($"[Vulkan.Example] {name}: Missing extension {final}");
|
||||
return false;
|
||||
}
|
||||
|
||||
BumpAllocator featureAlloc = scope .();
|
||||
Dictionary<VkStructureType, VkBaseInStructure*> featureChainBuffer = new:featureAlloc .(8);
|
||||
var featureChainHead = BuildFeatureChain(let featureBools, featureChainBuffer, featureAlloc);
|
||||
|
||||
device.GetFeatures2KHR(out *featureChainHead);
|
||||
for (let featureBool in featureBools)
|
||||
{
|
||||
if (*featureBool) continue;
|
||||
Debug.WriteLine($"[Vulkan.Example] {name}: Missing feature {requiredFeatures[@featureBool]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
device.GetSurfaceFormatsKHR_Scope!(surface, let surfaceFormats);
|
||||
for (let format in surfaceFormats)
|
||||
{
|
||||
if (format.colorSpace != .SrgbNonlinearKHR) continue;
|
||||
if (swapchainFormat.format == .UNDEFINED)
|
||||
swapchainFormat = format;
|
||||
else if (swapchainFormat.format.Size > format.format.Size)
|
||||
swapchainFormat = format;
|
||||
}
|
||||
if (swapchainFormat.format == .UNDEFINED)
|
||||
swapchainFormat = surfaceFormats[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public VkResult CreateDevice(params VkQueue*[3] outQueues)
|
||||
{
|
||||
uint32[outQueues.Count] queueIndices = .();
|
||||
List<VkDeviceQueueCreateInfo> queueCIs = scope .(queueIndices.Count);
|
||||
queueCIs: for (var index in ref queueIndices)
|
||||
{
|
||||
index = (.)-1;
|
||||
switch (@index)
|
||||
{
|
||||
case 0: index = (.)graphicsFamilyIndex;
|
||||
case 1: index = (.)transferFamilyIndex;
|
||||
case 2: index = (.)presentFamilyIndex;
|
||||
}
|
||||
Runtime.Assert(index != (.)-1);
|
||||
for (let queueCI in queueCIs)
|
||||
if (index == queueCI.queueFamilyIndex)
|
||||
continue queueCIs;
|
||||
queueCIs.Add(.(null, 0, index, float[1](1.0f)));
|
||||
}
|
||||
|
||||
BumpAllocator featureAlloc = scope .();
|
||||
Dictionary<VkStructureType, VkBaseInStructure*> featureChainBuffer = new:featureAlloc .(8);
|
||||
void* pNext = BuildFeatureChain(let featureBools, featureChainBuffer, featureAlloc);
|
||||
for (let featureBool in featureBools)
|
||||
*featureBool = true;
|
||||
|
||||
VkResult result = device.CreateDevice(scope .(pNext, 0,
|
||||
queueCreateInfos: queueCIs,
|
||||
enabledExtensionNames: enabledExtensionNames
|
||||
), vkAlloc, out Program.device);
|
||||
if (result != .VkSuccess)
|
||||
return result;
|
||||
|
||||
vkPfns.LoadDevice(Program.device);
|
||||
for (let outQueue in outQueues)
|
||||
Program.device.GetQueue(queueIndices[@outQueue], 0, out *outQueue);
|
||||
|
||||
return .VkSuccess;
|
||||
}
|
||||
|
||||
public void CreateSwapchain()
|
||||
{
|
||||
VkResult result = device.GetSurfaceCapabilitiesKHR(surface, let capabilities);
|
||||
CheckResult(result);
|
||||
|
||||
uint32 minImageCount = capabilities.minImageCount + 1;
|
||||
if (capabilities.maxImageCount != 0 && minImageCount > capabilities.maxImageCount)
|
||||
minImageCount = capabilities.maxImageCount;
|
||||
|
||||
VkExtent2D imageExtent;
|
||||
if (capabilities.currentExtent.width != uint32.MaxValue)
|
||||
imageExtent = capabilities.currentExtent;
|
||||
else
|
||||
{
|
||||
Glfw.GetFramebufferSize(window, let width, let height);
|
||||
|
||||
imageExtent = .(
|
||||
Math.Clamp<uint32>((.)width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width),
|
||||
Math.Clamp<uint32>((.)height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height)
|
||||
);
|
||||
}
|
||||
|
||||
bool sharingExclusive = graphicsFamilyIndex == presentFamilyIndex;
|
||||
|
||||
VkSwapchainKHR oldSwapchain = swapchain;
|
||||
defer oldSwapchain.DestroyKHR(Program.device, vkAlloc);
|
||||
result = Program.device.CreateSwapchainKHR(scope .()
|
||||
{
|
||||
surface = surface,
|
||||
minImageCount = minImageCount,
|
||||
imageFormat = swapchainFormat.format,
|
||||
imageColorSpace = swapchainFormat.colorSpace,
|
||||
imageExtent = imageExtent,
|
||||
imageArrayLayers = 1,
|
||||
imageUsage = .ColorAttachment,
|
||||
imageSharingMode = sharingExclusive ? .Exclusive : .Concurrent,
|
||||
queueFamilyIndices = sharingExclusive ? .() : uint32[?]((.)graphicsFamilyIndex, (.)presentFamilyIndex),
|
||||
preTransform = capabilities.currentTransform,
|
||||
compositeAlpha = .OpaqueKHR,
|
||||
presentMode = .FifoKHR,
|
||||
clipped = true,
|
||||
oldSwapchain = oldSwapchain,
|
||||
}, vkAlloc, out swapchain);
|
||||
CheckResult(result);
|
||||
|
||||
delete images;
|
||||
swapchain.GetImagesKHR_New!(Program.device, out images);
|
||||
if (imageViews != null)
|
||||
{
|
||||
for (let view in imageViews)
|
||||
view.Destroy(Program.device, vkAlloc);
|
||||
if (images.Count != imageViews.Count)
|
||||
{
|
||||
delete imageViews;
|
||||
imageViews = new .[images.Count];
|
||||
}
|
||||
}
|
||||
else
|
||||
imageViews = new .[images.Count];
|
||||
|
||||
for (let image in images)
|
||||
{
|
||||
result = Program.device.CreateImageView(scope .(null, 0,
|
||||
image, ._2d, swapchainFormat.format,
|
||||
components: default,
|
||||
subresourceRange: .(.Color, 0, 1, 0, 1)
|
||||
), vkAlloc, out imageViews[@image]);
|
||||
CheckResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static int operator<=>(Self lhs, Self rhs)
|
||||
{
|
||||
return (lhs.kind == .DiscreteGpu) <=> (rhs.kind == .DiscreteGpu);
|
||||
}
|
||||
|
||||
public VkPhysicalDeviceFeatures2* BuildFeatureChain(out VkBool32*[requiredFeatures.Count] outFeatureBools,
|
||||
Dictionary<VkStructureType, VkBaseInStructure*> chainBuffer, BumpAllocator alloc)
|
||||
{
|
||||
outFeatureBools = default;
|
||||
VkPhysicalDeviceFeatures2* head = new:alloc .();
|
||||
VkBaseInStructure* tail = (.)head;
|
||||
chainBuffer.Add(VkPhysicalDeviceFeatures2.SType, tail);
|
||||
for (let feature in requiredFeatures)
|
||||
{
|
||||
VkStructureType sType;
|
||||
do
|
||||
{
|
||||
let apiVersion = feature.ApiVersion;
|
||||
if (apiVersion != 0 && apiVersion <= this.apiVersion)
|
||||
{
|
||||
sType = apiVersion.FeatureStruct;
|
||||
break;
|
||||
}
|
||||
let ext = feature.Extension;
|
||||
if (ext != 0)
|
||||
{
|
||||
sType = ext.FeatureStruct;
|
||||
break;
|
||||
}
|
||||
Runtime.FatalError();
|
||||
}
|
||||
VkBaseInStructure** valuePtr;
|
||||
switch (chainBuffer.TryAdd(sType))
|
||||
{
|
||||
case .Added(?, out valuePtr):
|
||||
*valuePtr = (.)(new:alloc uint8[sType.Type.Size]).Ptr;
|
||||
(*valuePtr).sType = sType;
|
||||
tail.pNext = *valuePtr;
|
||||
tail = *valuePtr;
|
||||
fallthrough;
|
||||
case .Exists(?, out valuePtr):
|
||||
outFeatureBools[@feature] = GetFeatureBool(feature, *valuePtr);
|
||||
}
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
[Comptime, OnCompile(.TypeInit)]
|
||||
static void OnTypeInit()
|
||||
{
|
||||
HashSet<VulkanExtension> extensions = scope .(16);
|
||||
for (let ext in requiredExtensions)
|
||||
{
|
||||
Runtime.Assert(ext.Kind == .Device);
|
||||
extensions.Add(ext);
|
||||
}
|
||||
for (let feature in requiredFeatures)
|
||||
{
|
||||
let ext = feature.Extension;
|
||||
if (ext == 0) continue;
|
||||
Runtime.Assert(ext.Kind == .Device);
|
||||
extensions.Add(ext);
|
||||
}
|
||||
bool added;
|
||||
repeat
|
||||
{
|
||||
added = false;
|
||||
for (let ext in extensions)
|
||||
for (let dep in ext.Dependencies)
|
||||
if (dep.Kind == .Device)
|
||||
added |= extensions.Add(dep);
|
||||
}
|
||||
while (added);
|
||||
|
||||
String emit = scope .(1024);
|
||||
emit.Append("public const VulkanExtension[?] extensionsFinal = .(");
|
||||
bool comma = false;
|
||||
for (let ext in extensions)
|
||||
{
|
||||
if (comma) emit.Append(", ");
|
||||
comma = true;
|
||||
emit.Append('.');
|
||||
ext.ToString(emit);
|
||||
}
|
||||
emit.Append("""
|
||||
);
|
||||
|
||||
public static VkBool32* GetFeatureBool(VulkanFeature feature, VkBaseInStructure* pStruct)
|
||||
{
|
||||
switch (feature)
|
||||
{
|
||||
|
||||
""");
|
||||
for (let feature in requiredFeatures)
|
||||
{
|
||||
emit.Append("\tcase .");
|
||||
feature.ToString(emit);
|
||||
emit.Append("""
|
||||
:
|
||||
switch (pStruct.sType)
|
||||
{
|
||||
|
||||
""");
|
||||
mixin FeatureStruct(var holder)
|
||||
{
|
||||
if (holder != 0)
|
||||
{
|
||||
VkStructureType featureStruct = holder.FeatureStruct;
|
||||
Runtime.Assert(featureStruct != 0);
|
||||
emit.Append("\t\tcase .");
|
||||
featureStruct.ToString(emit);
|
||||
emit.Append(": return &((");
|
||||
featureStruct.Type.ToString(emit);
|
||||
emit.Append("*)pStruct).");
|
||||
feature.ToString(emit);
|
||||
emit.Append(";\n");
|
||||
}
|
||||
}
|
||||
FeatureStruct!(feature.ApiVersion);
|
||||
FeatureStruct!(feature.Extension);
|
||||
emit.Append("""
|
||||
default: Runtime.FatalError("Invalid struct for feature");
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
emit.Append("""
|
||||
default:
|
||||
Runtime.FatalError("Unhandled feature");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Compiler.EmitTypeBody(typeof(Self), emit);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateAndPushBuffer(out VkBuffer buffer, out VmaAllocation allocation, VkBufferUsageFlags usage, void* ptr, int size)
|
||||
{
|
||||
VkBufferCreateInfo* bufferCI = scope .(null, 0,
|
||||
(.)size, .TransferSrc, .Exclusive
|
||||
);
|
||||
VkResult result = Vma.CreateBuffer(allocator, bufferCI, scope .()
|
||||
{
|
||||
flags = .Mapped | .HostAccessSequentialWrite,
|
||||
usage = .Auto,
|
||||
}, let transferBuffer, let transferBufferAlloc, let transferBufferAllocInfo);
|
||||
CheckResult(result);
|
||||
defer Vma.DestroyBuffer(allocator, transferBuffer, transferBufferAlloc);
|
||||
|
||||
Internal.MemCpy(transferBufferAllocInfo.pMappedData, ptr, size);
|
||||
|
||||
if (physicalDevice.transferFamilyIndex != physicalDevice.graphicsFamilyIndex)
|
||||
{
|
||||
bufferCI.sharingMode = .Concurrent;
|
||||
bufferCI.queueFamilyIndices = uint32[?](
|
||||
(.)physicalDevice.transferFamilyIndex,
|
||||
(.)physicalDevice.graphicsFamilyIndex,
|
||||
);
|
||||
}
|
||||
bufferCI.usage = usage | .TransferDst;
|
||||
result = Vma.CreateBuffer(allocator, bufferCI, scope .()
|
||||
{
|
||||
usage = .Auto,
|
||||
}, out buffer, out allocation, ?);
|
||||
CheckResult(result);
|
||||
|
||||
result = device.CreateCommandPool(scope .(null,
|
||||
.Transient, (.)physicalDevice.transferFamilyIndex
|
||||
), vkAlloc, let cmdPool);
|
||||
CheckResult(result);
|
||||
defer cmdPool.Destroy(device, vkAlloc);
|
||||
|
||||
result = device.AllocateCommandBuffers(scope .(null,
|
||||
cmdPool, .Primary, 1
|
||||
), var cmd);
|
||||
CheckResult(result);
|
||||
defer cmdPool.FreeCommandBuffers(device, .(&cmd, 1));
|
||||
|
||||
result = cmd.Begin(scope .(null, .OneTimeSubmit, null));
|
||||
CheckResult(result);
|
||||
cmd.CopyBuffer(transferBuffer, buffer, VkBufferCopy[?](
|
||||
.(0, 0, (.)size)
|
||||
));
|
||||
result = cmd.End();
|
||||
CheckResult(result);
|
||||
|
||||
result = transferQueue.Submit(VkSubmitInfo[?](.()
|
||||
{
|
||||
commandBufferCount = 1,
|
||||
pCommandBuffers = &cmd,
|
||||
}));
|
||||
|
||||
result = transferQueue.WaitIdle();
|
||||
CheckResult(result);
|
||||
}
|
||||
|
||||
public static int Main(String[] args)
|
||||
{
|
||||
VkResult result;
|
||||
|
||||
{
|
||||
Glfw.Init();
|
||||
defer:: Glfw.Terminate();
|
||||
Glfw.SetErrorCallback((error_code, description)
|
||||
=> Runtime.FatalError(scope $"Glfw.{(Glfw.Error)error_code}: {StringView(description)}"));
|
||||
|
||||
Glfw.WindowHint(.ClientApi, Glfw.NoApi);
|
||||
Glfw.WindowHint(.Resizable, Glfw.True);
|
||||
|
||||
window = Glfw.CreateWindow(1024, 800, "Vulkan-Beef Example", null, null);
|
||||
defer:: Glfw.DestroyWindow(window);
|
||||
|
||||
Runtime.Assert(Glfw.VulkanSupported());
|
||||
Glfw.InitVulkanLoader(null);
|
||||
void* getProc = Glfw.GetInstanceProcAddress(0, "vkGetInstanceProcAddr");
|
||||
vkPfns.Load((.)getProc);
|
||||
VulkanLoadedFunctions.current = &vkPfns;
|
||||
|
||||
result = vkEnumerateInstanceVersion(let instanceFullVersion);
|
||||
CheckResult(result);
|
||||
VulkanApiVersion instanceApiVersion = (.)VK_MAKE_API_VERSION(0,
|
||||
VK_API_VERSION_MAJOR(instanceFullVersion), VK_API_VERSION_MINOR(instanceFullVersion), 0);
|
||||
|
||||
char8** glfwExts = Glfw.GetRequiredInstanceExtensions(let glfwExtCount);
|
||||
List<char8*> extensions = scope .(Span<char8*>(glfwExts, glfwExtCount));
|
||||
extensions.Add(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||
#if DEBUG
|
||||
extensions.Add(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
#endif
|
||||
{
|
||||
result = vkEnumerateInstanceExtensionProperties_Scope!(null, let properties);
|
||||
CheckResult(result);
|
||||
findExt: for (let ext in extensions)
|
||||
{
|
||||
for (var property in properties)
|
||||
{
|
||||
if (String.Equals(ext, &property.extensionName))
|
||||
continue findExt;
|
||||
}
|
||||
Runtime.FatalError(scope $"Missing instance extension {StringView(ext)}");
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
VkDebugUtilsMessengerCreateInfoEXT* debugCI = scope .(null, 0,
|
||||
.VerboseEXT | .ErrorEXT | .WarningEXT,
|
||||
.GeneralEXT | .ValidationEXT | .PerformanceEXT,
|
||||
(messageSeverity, messageTypes, pCallbackData, pUserData) =>
|
||||
{
|
||||
Debug.WriteLine($"[Vulkan] {StringView(pCallbackData.pMessage)}");
|
||||
return false;
|
||||
});
|
||||
#endif
|
||||
|
||||
result = vkCreateInstance(scope .()
|
||||
{
|
||||
#if DEBUG
|
||||
pNext = debugCI,
|
||||
#endif
|
||||
flags = 0,
|
||||
pApplicationInfo = scope .()
|
||||
{
|
||||
pApplicationName = "Vulkan-Beef Example",
|
||||
applicationVersion = VK_MAKE_VERSION(0, 0, 1),
|
||||
apiVersion = (.)instanceApiVersion,
|
||||
},
|
||||
enabledLayerNames = char8*[?](
|
||||
#if DEBUG
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
#endif
|
||||
),
|
||||
enabledExtensionNames = extensions,
|
||||
}, vkAlloc, out instance);
|
||||
CheckResult(result);
|
||||
vkPfns.LoadInstance(instance);
|
||||
defer:: instance.Destroy(vkAlloc);
|
||||
|
||||
#if DEBUG
|
||||
result = instance.CreateDebugUtilsMessengerEXT(debugCI, vkAlloc, let messegner);
|
||||
CheckResult(result);
|
||||
defer:: messegner.DestroyEXT(instance, vkAlloc);
|
||||
#endif
|
||||
|
||||
result = (.)Glfw.CreateWindowSurface((.)instance, window, (.)vkAlloc, out *(Glfw.VkSurfaceKHR*)&surface);
|
||||
CheckResult(result);
|
||||
defer:: surface.DestroyKHR(instance, vkAlloc);
|
||||
}
|
||||
|
||||
{
|
||||
result = instance.EnumeratePhysicalDevices_Scope!(let devices);
|
||||
CheckResult(result);
|
||||
List<PhysicalDevice> options = new .(devices.Count);
|
||||
for (let device in devices)
|
||||
{
|
||||
PhysicalDevice info = new .();
|
||||
if (!info.Init(device))
|
||||
{
|
||||
delete info;
|
||||
continue;
|
||||
}
|
||||
options.Add(info);
|
||||
}
|
||||
Runtime.Assert(!options.IsEmpty, "No suitable GPUs found");
|
||||
options.Sort();
|
||||
physicalDevice = options.PopFront();
|
||||
Debug.WriteLine($"[Vulkan.Example] Selected GPU: {physicalDevice.name}");
|
||||
result = physicalDevice.CreateDevice(&graphicsQueue, &transferQueue, &presentQueue);
|
||||
CheckResult(result);
|
||||
defer:: device.Destroy(vkAlloc);
|
||||
DeleteContainerAndItems!(options);
|
||||
|
||||
physicalDevice.CreateSwapchain();
|
||||
defer::
|
||||
{
|
||||
for (let view in imageViews)
|
||||
view.Destroy(Program.device, vkAlloc);
|
||||
delete imageViews;
|
||||
delete images;
|
||||
swapchain.DestroyKHR(device, vkAlloc);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
VmaVulkanFunctions vulkanPfns = default;
|
||||
[Comptime] static void EmitFill()
|
||||
{
|
||||
String emit = scope .(1024);
|
||||
for (let field in typeof(VmaVulkanFunctions).GetFields())
|
||||
emit.Append("vulkanPfns.", field.Name, " = vkPfns.", field.Name, ";\n");
|
||||
Compiler.MixinRoot(emit);
|
||||
}
|
||||
EmitFill();
|
||||
|
||||
result = Vma.CreateAllocator(scope .()
|
||||
{
|
||||
physicalDevice = physicalDevice.device,
|
||||
device = device,
|
||||
pAllocationCallbacks = vkAlloc,
|
||||
pVulkanFunctions = &vulkanPfns,
|
||||
instance = instance
|
||||
}, out allocator);
|
||||
CheckResult(result);
|
||||
defer:: Vma.DestroyAllocator(allocator);
|
||||
}
|
||||
|
||||
{
|
||||
var vertices = vertices;
|
||||
CreateAndPushBuffer(out vertexBuffer, let vertexBufferAllocation, .VertexBuffer, &vertices, sizeof(decltype(vertices)));
|
||||
defer:: Vma.DestroyBuffer(allocator, vertexBuffer, vertexBufferAllocation);
|
||||
|
||||
var indices = indices;
|
||||
CreateAndPushBuffer(out indexBuffer, let indexBufferAllocation, .IndexBuffer, &indices, sizeof(decltype(indices)));
|
||||
defer:: Vma.DestroyBuffer(allocator, indexBuffer, indexBufferAllocation);
|
||||
}
|
||||
|
||||
{
|
||||
mixin ShaderModule(String path)
|
||||
{
|
||||
let spirvBuf = Compiler.ReadBinary(path);
|
||||
Span<uint8> spirv = spirvBuf;
|
||||
result = device.CreateShaderModule(scope .()
|
||||
{
|
||||
codeSize = (.)spirv.Length,
|
||||
pCode = (.)spirv.Ptr,
|
||||
}, vkAlloc, let module);
|
||||
CheckResult(result);
|
||||
defer:: module.Destroy(device, vkAlloc);
|
||||
module
|
||||
}
|
||||
|
||||
let vertModule = ShaderModule!(Compiler.ProjectDir + "/src/shader.vert.spv");
|
||||
let fragModule = ShaderModule!(Compiler.ProjectDir + "/src/shader.frag.spv");
|
||||
|
||||
result = device.CreatePipelineLayout(scope .(
|
||||
pushConstantRanges: VkPushConstantRange[?](.(.Vertex, 0, sizeof(PushConstants)))
|
||||
), vkAlloc, out pipelineLayout);
|
||||
CheckResult(result);
|
||||
defer:: pipelineLayout.Destroy(device, vkAlloc);
|
||||
|
||||
result = default(VkPipelineCache).CreateGraphicsPipelines(device, 1, scope .()
|
||||
{
|
||||
pNext = scope VkPipelineRenderingCreateInfo()
|
||||
{
|
||||
colorAttachmentFormats = VkFormat[?](physicalDevice.swapchainFormat.format),
|
||||
},
|
||||
stages = VkPipelineShaderStageCreateInfo[?](
|
||||
.(null, 0, .Vertex, vertModule, "main"),
|
||||
.(null, 0, .Fragment, fragModule, "main"),
|
||||
),
|
||||
pVertexInputState = scope .(null, 0,
|
||||
VkVertexInputBindingDescription[?](
|
||||
.(0, strideof(Vertex), .Vertex)
|
||||
),
|
||||
VkVertexInputAttributeDescription[?](
|
||||
.(0, 0, .R32G32B32_SFLOAT, offsetof(Vertex, pos)),
|
||||
.(1, 0, .R32G32B32_SFLOAT, offsetof(Vertex, color)),
|
||||
)
|
||||
),
|
||||
pInputAssemblyState = scope .(null, 0, .TriangleList, false),
|
||||
pViewportState = scope .()
|
||||
{
|
||||
viewportCount = 1,
|
||||
scissorCount = 1,
|
||||
},
|
||||
pRasterizationState = scope .()
|
||||
{
|
||||
depthClampEnable = false,
|
||||
rasterizerDiscardEnable = false,
|
||||
polygonMode = .Line,
|
||||
cullMode = .None,
|
||||
depthBiasEnable = false,
|
||||
lineWidth = 1.0f,
|
||||
},
|
||||
pMultisampleState = scope .()
|
||||
{
|
||||
rasterizationSamples = ._1,
|
||||
sampleShadingEnable = false,
|
||||
alphaToCoverageEnable = false,
|
||||
alphaToOneEnable = false,
|
||||
},
|
||||
pColorBlendState = scope .()
|
||||
{
|
||||
logicOpEnable = false,
|
||||
logicOp = .Copy,
|
||||
attachmentCount = 1,
|
||||
pAttachments = scope .()
|
||||
{
|
||||
blendEnable = false,
|
||||
srcColorBlendFactor = .One,
|
||||
dstColorBlendFactor = .Zero,
|
||||
colorBlendOp = .Add,
|
||||
srcAlphaBlendFactor = .One,
|
||||
dstAlphaBlendFactor = .Zero,
|
||||
alphaBlendOp = .Add,
|
||||
colorWriteMask = .R | .G | .B | .A,
|
||||
},
|
||||
blendConstants = .(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
},
|
||||
pDynamicState = scope .(null, 0, VkDynamicState[?](
|
||||
.Viewport,
|
||||
.Scissor,
|
||||
)),
|
||||
layout = pipelineLayout,
|
||||
}, vkAlloc, out pipeline);
|
||||
CheckResult(result);
|
||||
defer:: pipeline.Destroy(device, vkAlloc);
|
||||
}
|
||||
|
||||
{
|
||||
result = device.CreateCommandPool(scope .(null,
|
||||
.ResetCommandBuffer,
|
||||
(.)physicalDevice.graphicsFamilyIndex
|
||||
), vkAlloc, out commandPool);
|
||||
CheckResult(result);
|
||||
defer:: commandPool.Destroy(device, vkAlloc);
|
||||
|
||||
for (int i < MaxFramesInFlight)
|
||||
{
|
||||
result = device.CreateSemaphore(scope .(null, 0), vkAlloc, out imageAvalible[i]);
|
||||
CheckResult(result);
|
||||
result = device.CreateSemaphore(scope .(null, 0), vkAlloc, out renderFinished[i]);
|
||||
CheckResult(result);
|
||||
result = device.CreateFence(scope .(null, .Signaled), vkAlloc, out renderFinishedFence[i]);
|
||||
CheckResult(result);
|
||||
}
|
||||
defer::
|
||||
{
|
||||
for (let semaphore in imageAvalible)
|
||||
semaphore.Destroy(device, vkAlloc);
|
||||
for (let semaphore in renderFinished)
|
||||
semaphore.Destroy(device, vkAlloc);
|
||||
for (let fence in renderFinishedFence)
|
||||
fence.Destroy(device, vkAlloc);
|
||||
}
|
||||
|
||||
result = device.AllocateCommandBuffers(scope .(null,
|
||||
commandPool, .Primary, commandBuffers.Count
|
||||
), out commandBuffers[0]);
|
||||
CheckResult(result);
|
||||
defer:: commandPool.FreeCommandBuffers(device, commandBuffers);
|
||||
}
|
||||
|
||||
Glfw.GetFramebufferSize(window, var framebufferWidth, var framebufferHeight);
|
||||
int frameIndex = 0;
|
||||
float rotationAngle = 0;
|
||||
Stopwatch deltaWatch = scope .()..Start();
|
||||
|
||||
while (Glfw.WindowShouldClose(window) == 0)
|
||||
{
|
||||
result = device.WaitForFences(.(&renderFinishedFence[frameIndex], 1), false, Timeout);
|
||||
CheckResult(result);
|
||||
result = device.ResetFences(.(&renderFinishedFence[frameIndex], 1));
|
||||
CheckResult(result);
|
||||
|
||||
Glfw.GetFramebufferSize(window, let newWidth, let newHeight);
|
||||
if (framebufferWidth != newWidth || framebufferHeight != newHeight)
|
||||
{
|
||||
framebufferWidth = newWidth;
|
||||
framebufferHeight = newHeight;
|
||||
physicalDevice.CreateSwapchain();
|
||||
}
|
||||
|
||||
uint32 imageIndex;
|
||||
while (true)
|
||||
{
|
||||
result = swapchain.AcquireNextImageKHR(device, Timeout, imageAvalible[frameIndex], null, out imageIndex);
|
||||
switch (result)
|
||||
{
|
||||
case .VkSuboptimalKHR:
|
||||
physicalDevice.CreateSwapchain();
|
||||
continue;
|
||||
case .VkSuccess:
|
||||
default:
|
||||
CheckResult(result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
PushConstants consts = default;
|
||||
rotationAngle += (.)deltaWatch.Elapsed.TotalSeconds;
|
||||
consts.view = .
|
||||
(
|
||||
Math.Cos(rotationAngle), 0, Math.Sin(rotationAngle), 0,
|
||||
0, 1, 0, 0,
|
||||
-Math.Sin(rotationAngle), 0, Math.Cos(rotationAngle), -5,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
deltaWatch.Restart();
|
||||
|
||||
VkCommandBuffer cmd = commandBuffers[frameIndex];
|
||||
result = cmd.Reset();
|
||||
CheckResult(result);
|
||||
result = cmd.Begin(scope .(null, .OneTimeSubmit, null));
|
||||
CheckResult(result);
|
||||
{
|
||||
cmd.PipelineBarrier(.TopOfPipe, .ColorAttachmentOutput, 0, .(), .(), VkImageMemoryBarrier[?](.()
|
||||
{
|
||||
oldLayout = .Undefined,
|
||||
newLayout = .ColorAttachmentOptimal,
|
||||
srcAccessMask = 0,
|
||||
dstAccessMask = .ColorAttachmentWrite,
|
||||
srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
image = images[imageIndex],
|
||||
subresourceRange = .()
|
||||
{
|
||||
aspectMask = .Color,
|
||||
baseMipLevel = 0,
|
||||
levelCount = 1,
|
||||
baseArrayLayer = 0,
|
||||
layerCount = 1,
|
||||
},
|
||||
}));
|
||||
|
||||
VkRect2D renderArea = .(.(0, 0), .((.)framebufferWidth, (.)framebufferHeight));
|
||||
cmd.BeginRendering(scope .(null, 0,
|
||||
renderArea: renderArea,
|
||||
layerCount: 1,
|
||||
viewMask: 0,
|
||||
colorAttachments: VkRenderingAttachmentInfo[?](.()
|
||||
{
|
||||
imageView = imageViews[imageIndex],
|
||||
imageLayout = .ColorAttachmentOptimal,
|
||||
resolveMode = .None,
|
||||
resolveImageView = imageViews[imageIndex],
|
||||
resolveImageLayout = .PresentSrcKHR,
|
||||
loadOp = .Clear,
|
||||
storeOp = .Store,
|
||||
clearValue = .(
|
||||
color: .() { float32 = .(0, 0, 0, 1f) },
|
||||
depthStencil: default
|
||||
)
|
||||
})
|
||||
));
|
||||
|
||||
cmd.BindPipeline(.Graphics, pipeline);
|
||||
cmd.SetViewport(0, VkViewport[?](.(0, 0, framebufferWidth, framebufferHeight, 0f, 1f)));
|
||||
cmd.SetScissor(0, .(&renderArea, 1));
|
||||
cmd.BindVertexBuffers(0, 1, &vertexBuffer, scope uint64());
|
||||
cmd.BindIndexBuffer(indexBuffer, 0, .Uint32);
|
||||
cmd.PushConstants(pipelineLayout, .Vertex, 0, .(&consts, sizeof(PushConstants)));
|
||||
cmd.DrawIndexed(indices.Count, 1, 0, 0, 0);
|
||||
|
||||
cmd.EndRendering();
|
||||
cmd.PipelineBarrier(.ColorAttachmentOutput, .BottomOfPipe, 0, .(), .(), VkImageMemoryBarrier[?](.()
|
||||
{
|
||||
oldLayout = .ColorAttachmentOptimal,
|
||||
newLayout = .PresentSrcKHR,
|
||||
srcAccessMask = .ColorAttachmentWrite,
|
||||
dstAccessMask = 0,
|
||||
srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
image = images[imageIndex],
|
||||
subresourceRange = .()
|
||||
{
|
||||
aspectMask = .Color,
|
||||
baseMipLevel = 0,
|
||||
levelCount = 1,
|
||||
baseArrayLayer = 0,
|
||||
layerCount = 1,
|
||||
},
|
||||
}));
|
||||
}
|
||||
result = cmd.End();
|
||||
CheckResult(result);
|
||||
|
||||
VkPipelineStageFlags waitDstStageMaks = .ColorAttachmentOutput;
|
||||
result = graphicsQueue.Submit(VkSubmitInfo[?](.()
|
||||
{
|
||||
pNext = scope VkTimelineSemaphoreSubmitInfo(null,
|
||||
waitSemaphoreValues: uint64[?](1),
|
||||
signalSemaphoreValues: uint64[?](2)
|
||||
),
|
||||
waitSemaphoreCount = 1,
|
||||
pWaitSemaphores = &imageAvalible[frameIndex],
|
||||
pWaitDstStageMask = &waitDstStageMaks,
|
||||
commandBufferCount = 1,
|
||||
pCommandBuffers = &cmd,
|
||||
signalSemaphoreCount = 1,
|
||||
pSignalSemaphores = &renderFinished[frameIndex],
|
||||
}
|
||||
), renderFinishedFence[frameIndex]);
|
||||
CheckResult(result);
|
||||
|
||||
result = presentQueue.PresentKHR(scope .()
|
||||
{
|
||||
waitSemaphoreCount = 1,
|
||||
pWaitSemaphores = &renderFinished[frameIndex],
|
||||
swapchainCount = 1,
|
||||
pSwapchains = &swapchain,
|
||||
pImageIndices = &imageIndex,
|
||||
});
|
||||
CheckResult(result);
|
||||
|
||||
frameIndex = (frameIndex + 1) % MaxFramesInFlight;
|
||||
Glfw.PollEvents();
|
||||
}
|
||||
|
||||
result = device.WaitIdle();
|
||||
CheckResult(result);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user