-
-
Save shakesoda/28eb50428eb683ce677b8b1903a57473 to your computer and use it in GitHub Desktop.
zig vulkan triangle breaks compiler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ zig build | |
broken LLVM module found: Call parameter type does not match function signature! | |
%35 = getelementptr inbounds { %"[][*]const u8", i16 }, { %"[][*]const u8", i16 }* %0, i32 0, i32 0, !dbg !8239 | |
%"[][*:0]const u8"* call fastcc void @"std.array_list.AlignedArrayList([*:0]const u8,null).toOwnedSlice"(%"[][*]const u8"* sret %35, %"std.array_list.AlignedArrayList([*:0]const u8,null)"* %extensions), !dbg !8239 | |
This is a bug in the Zig compiler. | |
Unable to dump stack trace: debug info stripped | |
how-does-vulkan...The following command exited with error code 3: | |
$ zig version | |
0.5.0+1568470c4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const assert = std.debug.assert; | |
const mem = std.mem; | |
const Allocator = mem.Allocator; | |
const c = @cImport({ | |
@cInclude("vulkan/vulkan.h"); | |
@cInclude("GLFW/glfw3.h"); | |
}); | |
const WIDTH = 800; | |
const HEIGHT = 600; | |
const MAX_FRAMES_IN_FLIGHT = 2; | |
const enableValidationLayers = std.debug.runtime_safety; | |
const validationLayers = [_][*:0]const u8{"VK_LAYER_LUNARG_standard_validation"}; | |
const deviceExtensions = [_][*:0]const u8{c.VK_KHR_SWAPCHAIN_EXTENSION_NAME}; | |
var currentFrame: usize = 0; | |
var instance: c.VkInstance = undefined; | |
var callback: c.VkDebugReportCallbackEXT = undefined; | |
var surface: c.VkSurfaceKHR = undefined; | |
var physicalDevice: c.VkPhysicalDevice = undefined; | |
var global_device: c.VkDevice = undefined; | |
var graphicsQueue: c.VkQueue = undefined; | |
var presentQueue: c.VkQueue = undefined; | |
var swapChainImages: []c.VkImage = undefined; | |
var swapChain: c.VkSwapchainKHR = undefined; | |
var swapChainImageFormat: c.VkFormat = undefined; | |
var swapChainExtent: c.VkExtent2D = undefined; | |
var swapChainImageViews: []c.VkImageView = undefined; | |
var renderPass: c.VkRenderPass = undefined; | |
var pipelineLayout: c.VkPipelineLayout = undefined; | |
var graphicsPipeline: c.VkPipeline = undefined; | |
var swapChainFramebuffers: []c.VkFramebuffer = undefined; | |
var commandPool: c.VkCommandPool = undefined; | |
var commandBuffers: []c.VkCommandBuffer = undefined; | |
var imageAvailableSemaphores: [MAX_FRAMES_IN_FLIGHT]c.VkSemaphore = undefined; | |
var renderFinishedSemaphores: [MAX_FRAMES_IN_FLIGHT]c.VkSemaphore = undefined; | |
var inFlightFences: [MAX_FRAMES_IN_FLIGHT]c.VkFence = undefined; | |
const QueueFamilyIndices = struct { | |
graphicsFamily: ?u32, | |
presentFamily: ?u32, | |
fn init() QueueFamilyIndices { | |
return QueueFamilyIndices{ | |
.graphicsFamily = null, | |
.presentFamily = null, | |
}; | |
} | |
fn isComplete(self: QueueFamilyIndices) bool { | |
return self.graphicsFamily != null and self.presentFamily != null; | |
} | |
}; | |
const SwapChainSupportDetails = struct { | |
capabilities: c.VkSurfaceCapabilitiesKHR, | |
formats: std.ArrayList(c.VkSurfaceFormatKHR), | |
presentModes: std.ArrayList(c.VkPresentModeKHR), | |
fn init(allocator: *Allocator) SwapChainSupportDetails { | |
var result = SwapChainSupportDetails{ | |
.capabilities = std.mem.zeroes(c.VkSurfaceCapabilitiesKHR), | |
.formats = std.ArrayList(c.VkSurfaceFormatKHR).init(allocator), | |
.presentModes = std.ArrayList(c.VkPresentModeKHR).init(allocator), | |
}; | |
// const slice = @sliceToBytes((*[1]c.VkSurfaceCapabilitiesKHR)(&result.capabilities)[0..1]); | |
// std.mem.set(u8, slice, 0); | |
return result; | |
} | |
fn deinit(self: *SwapChainSupportDetails) void { | |
self.formats.deinit(); | |
self.presentModes.deinit(); | |
} | |
}; | |
pub fn main() !void { | |
if (c.glfwInit() == 0) return error.GlfwInitFailed; | |
defer c.glfwTerminate(); | |
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API); | |
c.glfwWindowHint(c.GLFW_RESIZABLE, c.GLFW_FALSE); | |
const window = c.glfwCreateWindow(WIDTH, HEIGHT, "Zig Vulkan Triangle", null, null) orelse return error.GlfwCreateWindowFailed; | |
defer c.glfwDestroyWindow(window); | |
const allocator = std.heap.c_allocator; | |
try initVulkan(allocator, window); | |
while (c.glfwWindowShouldClose(window) == 0) { | |
c.glfwPollEvents(); | |
try drawFrame(); | |
} | |
try checkSuccess(c.vkDeviceWaitIdle(global_device)); | |
cleanup(); | |
} | |
fn cleanup() void { | |
var i: usize = 0; | |
while (i < MAX_FRAMES_IN_FLIGHT) : (i += 1) { | |
c.vkDestroySemaphore(global_device, renderFinishedSemaphores[i], null); | |
c.vkDestroySemaphore(global_device, imageAvailableSemaphores[i], null); | |
c.vkDestroyFence(global_device, inFlightFences[i], null); | |
} | |
c.vkDestroyCommandPool(global_device, commandPool, null); | |
for (swapChainFramebuffers) |framebuffer| { | |
c.vkDestroyFramebuffer(global_device, framebuffer, null); | |
} | |
c.vkDestroyPipeline(global_device, graphicsPipeline, null); | |
c.vkDestroyPipelineLayout(global_device, pipelineLayout, null); | |
c.vkDestroyRenderPass(global_device, renderPass, null); | |
for (swapChainImageViews) |imageView| { | |
c.vkDestroyImageView(global_device, imageView, null); | |
} | |
c.vkDestroySwapchainKHR(global_device, swapChain, null); | |
c.vkDestroyDevice(global_device, null); | |
if (enableValidationLayers) { | |
DestroyDebugReportCallbackEXT(null); | |
} | |
c.vkDestroySurfaceKHR(instance, surface, null); | |
c.vkDestroyInstance(instance, null); | |
} | |
fn initVulkan(allocator: *Allocator, window: *c.GLFWwindow) !void { | |
try createInstance(allocator); | |
try setupDebugCallback(); | |
try createSurface(window); | |
try pickPhysicalDevice(allocator); | |
try createLogicalDevice(allocator); | |
try createSwapChain(allocator); | |
try createImageViews(allocator); | |
try createRenderPass(); | |
try createGraphicsPipeline(allocator); | |
try createFramebuffers(allocator); | |
try createCommandPool(allocator); | |
try createCommandBuffers(allocator); | |
try createSyncObjects(); | |
} | |
// automate setting up these things, it's really error prone. | |
fn VulkanObject(T: var) T { | |
var ret = std.mem.zeroes(T); | |
ret.sType = switch (T) { | |
c.VkDeviceQueueCreateInfo => .VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
c.VkSwapchainCreateInfoKHR => .VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | |
c.VkPresentInfoKHR => .VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
c.VkDebugReportCallbackCreateInfoEXT => .VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, | |
c.VkDeviceCreateInfo => .VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
c.VkImageViewCreateInfo => .VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
c.VkPipelineLayoutCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
c.VkCommandBufferAllocateInfo => .VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
c.VkCommandBufferBeginInfo => .VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
c.VkRenderPassBeginInfo => .VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
c.VkApplicationInfo => .VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
c.VkInstanceCreateInfo => .VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
c.VkPipelineShaderStageCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
c.VkPipelineVertexInputStateCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
c.VkPipelineInputAssemblyStateCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
c.VkPipelineViewportStateCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
c.VkPipelineRasterizationStateCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
c.VkPipelineMultisampleStateCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
c.VkGraphicsPipelineCreateInfo => .VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
c.VkRenderPassCreateInfo => .VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
c.VkSubmitInfo => .VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
c.VkSemaphoreCreateInfo => .VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
c.VkFenceCreateInfo => .VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, | |
c.VkCommandPoolCreateInfo => .VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
c.VkFramebufferCreateInfo => .VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
c.VkShaderModuleCreateInfo => .VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
c.VkPipelineColorBlendStateCreateInfo => .VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
else => unreachable | |
}; | |
return ret; | |
} | |
fn createCommandBuffers(allocator: *Allocator) !void { | |
commandBuffers = try allocator.alloc(c.VkCommandBuffer, swapChainFramebuffers.len); | |
var allocInfo = VulkanObject(c.VkCommandBufferAllocateInfo); | |
allocInfo.commandPool = commandPool; | |
allocInfo.level = .VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
allocInfo.commandBufferCount = @intCast(u32, commandBuffers.len); | |
try checkSuccess(c.vkAllocateCommandBuffers(global_device, &allocInfo, commandBuffers.ptr)); | |
for (commandBuffers) |command_buffer, i| { | |
var beginInfo = VulkanObject(c.VkCommandBufferBeginInfo); | |
beginInfo.flags = c.VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; | |
try checkSuccess(c.vkBeginCommandBuffer(commandBuffers[i], &beginInfo)); | |
const clearColor = c.VkClearValue{ .color = c.VkClearColorValue{ .float32 = [_]f32{ 0.0, 0.0, 0.0, 1.0 } } }; | |
var renderPassInfo = VulkanObject(c.VkRenderPassBeginInfo); | |
renderPassInfo.renderPass = renderPass; | |
renderPassInfo.framebuffer = swapChainFramebuffers[i]; | |
renderPassInfo.renderArea = c.VkRect2D{ | |
.offset = c.VkOffset2D{ .x = 0, .y = 0 }, | |
.extent = swapChainExtent, | |
}; | |
renderPassInfo.clearValueCount = 1; | |
renderPassInfo.pClearValues = &clearColor; | |
c.vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, .VK_SUBPASS_CONTENTS_INLINE); | |
{ | |
c.vkCmdBindPipeline(commandBuffers[i], .VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); | |
c.vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); | |
} | |
c.vkCmdEndRenderPass(commandBuffers[i]); | |
try checkSuccess(c.vkEndCommandBuffer(commandBuffers[i])); | |
} | |
} | |
fn createSyncObjects() !void { | |
const semaphoreInfo = VulkanObject(c.VkSemaphoreCreateInfo); | |
var fenceInfo = VulkanObject(c.VkFenceCreateInfo); | |
fenceInfo.flags = c.VK_FENCE_CREATE_SIGNALED_BIT; | |
var i: usize = 0; | |
while (i < MAX_FRAMES_IN_FLIGHT) : (i += 1) { | |
try checkSuccess(c.vkCreateSemaphore(global_device, &semaphoreInfo, null, &imageAvailableSemaphores[i])); | |
try checkSuccess(c.vkCreateSemaphore(global_device, &semaphoreInfo, null, &renderFinishedSemaphores[i])); | |
try checkSuccess(c.vkCreateFence(global_device, &fenceInfo, null, &inFlightFences[i])); | |
} | |
} | |
fn createCommandPool(allocator: *Allocator) !void { | |
const queueFamilyIndices = try findQueueFamilies(allocator, physicalDevice); | |
var poolInfo = VulkanObject(c.VkCommandPoolCreateInfo); | |
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.?; | |
try checkSuccess(c.vkCreateCommandPool(global_device, &poolInfo, null, &commandPool)); | |
} | |
fn createFramebuffers(allocator: *Allocator) !void { | |
swapChainFramebuffers = try allocator.alloc(c.VkFramebuffer, swapChainImageViews.len); | |
for (swapChainImageViews) |swap_chain_image_view, i| { | |
const attachments = [_]c.VkImageView{swap_chain_image_view}; | |
var framebufferInfo = VulkanObject(c.VkFramebufferCreateInfo); | |
framebufferInfo.renderPass = renderPass; | |
framebufferInfo.attachmentCount = 1; | |
framebufferInfo.pAttachments = &attachments; | |
framebufferInfo.width = swapChainExtent.width; | |
framebufferInfo.height = swapChainExtent.height; | |
framebufferInfo.layers = 1; | |
try checkSuccess(c.vkCreateFramebuffer(global_device, &framebufferInfo, null, &swapChainFramebuffers[i])); | |
} | |
} | |
fn createShaderModule(code: []align(@alignOf(u32)) const u8) !c.VkShaderModule { | |
var createInfo = VulkanObject(c.VkShaderModuleCreateInfo); | |
createInfo.codeSize = code.len; | |
createInfo.pCode = @ptrCast([*:0]const u32, &code[0..:0]); | |
var shaderModule: c.VkShaderModule = undefined; | |
try checkSuccess(c.vkCreateShaderModule(global_device, &createInfo, null, &shaderModule)); | |
return shaderModule; | |
} | |
fn createGraphicsPipeline(allocator: *Allocator) !void { | |
const vertShaderCode = try std.fs.cwd().readFileAllocAligned(allocator, "shaders/vert.spv", std.math.maxInt(u32), @alignOf(u32)); | |
defer allocator.free(vertShaderCode); | |
const fragShaderCode = try std.fs.cwd().readFileAllocAligned(allocator, "shaders/frag.spv", std.math.maxInt(u32), @alignOf(u32)); | |
defer allocator.free(fragShaderCode); | |
const vertShaderModule = try createShaderModule(vertShaderCode); | |
const fragShaderModule = try createShaderModule(fragShaderCode); | |
var vertShaderStageInfo = VulkanObject(c.VkPipelineShaderStageCreateInfo); | |
vertShaderStageInfo.stage = .VK_SHADER_STAGE_VERTEX_BIT; | |
vertShaderStageInfo.module = vertShaderModule; | |
vertShaderStageInfo.pName = "main"; | |
var fragShaderStageInfo = VulkanObject(c.VkPipelineShaderStageCreateInfo); | |
fragShaderStageInfo.stage = .VK_SHADER_STAGE_FRAGMENT_BIT; | |
fragShaderStageInfo.module = fragShaderModule; | |
fragShaderStageInfo.pName = "main"; | |
const shaderStages = [_]c.VkPipelineShaderStageCreateInfo{ vertShaderStageInfo, fragShaderStageInfo }; | |
const vertexInputInfo = VulkanObject(c.VkPipelineVertexInputStateCreateInfo); | |
var inputAssembly = VulkanObject(c.VkPipelineInputAssemblyStateCreateInfo); | |
inputAssembly.topology = .VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | |
inputAssembly.primitiveRestartEnable = c.VK_FALSE; | |
const viewport = [_]c.VkViewport{c.VkViewport{ | |
.x = 0.0, | |
.y = 0.0, | |
.width = @intToFloat(f32, swapChainExtent.width), | |
.height = @intToFloat(f32, swapChainExtent.height), | |
.minDepth = 0.0, | |
.maxDepth = 1.0, | |
}}; | |
const scissor = [_]c.VkRect2D{c.VkRect2D{ | |
.offset = c.VkOffset2D{ .x = 0, .y = 0 }, | |
.extent = swapChainExtent, | |
}}; | |
var viewportState = VulkanObject(c.VkPipelineViewportStateCreateInfo); | |
viewportState.viewportCount = 1; | |
viewportState.pViewports = &viewport; | |
viewportState.scissorCount = 1; | |
viewportState.pScissors = &scissor; | |
var rasterizer = VulkanObject(c.VkPipelineRasterizationStateCreateInfo); | |
rasterizer.depthClampEnable = c.VK_FALSE; | |
rasterizer.rasterizerDiscardEnable = c.VK_FALSE; | |
rasterizer.polygonMode = .VK_POLYGON_MODE_FILL; | |
rasterizer.lineWidth = 1.0; | |
// rasterizer.cullMode = @intCast(u32, @enumToInt(c.VK_CULL_MODE_BACK_BIT)); | |
rasterizer.cullMode = c.VK_CULL_MODE_BACK_BIT; | |
rasterizer.frontFace = .VK_FRONT_FACE_CLOCKWISE; | |
rasterizer.depthBiasEnable = c.VK_FALSE; | |
var multisampling = VulkanObject(c.VkPipelineMultisampleStateCreateInfo); | |
multisampling.sampleShadingEnable = c.VK_FALSE; | |
multisampling.rasterizationSamples = .VK_SAMPLE_COUNT_1_BIT; | |
const colorBlendAttachment = c.VkPipelineColorBlendAttachmentState{ | |
.colorWriteMask = c.VK_COLOR_COMPONENT_R_BIT | c.VK_COLOR_COMPONENT_G_BIT | c.VK_COLOR_COMPONENT_B_BIT | c.VK_COLOR_COMPONENT_A_BIT, | |
.blendEnable = c.VK_FALSE, | |
.srcColorBlendFactor = .VK_BLEND_FACTOR_ZERO, | |
.dstColorBlendFactor = .VK_BLEND_FACTOR_ZERO, | |
.colorBlendOp = .VK_BLEND_OP_ADD, | |
.srcAlphaBlendFactor = .VK_BLEND_FACTOR_ZERO, | |
.dstAlphaBlendFactor = .VK_BLEND_FACTOR_ZERO, | |
.alphaBlendOp = .VK_BLEND_OP_ADD, | |
}; | |
var colorBlending = VulkanObject(c.VkPipelineColorBlendStateCreateInfo); | |
colorBlending.logicOpEnable = c.VK_FALSE; | |
colorBlending.logicOp = .VK_LOGIC_OP_COPY; | |
colorBlending.attachmentCount = 1; | |
colorBlending.pAttachments = &colorBlendAttachment; | |
colorBlending.blendConstants = [_]f32{ 0, 0, 0, 0 }; | |
const pipelineLayoutInfo = VulkanObject(c.VkPipelineLayoutCreateInfo); | |
try checkSuccess(c.vkCreatePipelineLayout(global_device, &pipelineLayoutInfo, null, &pipelineLayout)); | |
var pipelineInfoReal = VulkanObject(c.VkGraphicsPipelineCreateInfo); | |
pipelineInfoReal.stageCount = @intCast(u32, shaderStages.len); | |
pipelineInfoReal.pStages = &shaderStages; | |
pipelineInfoReal.pVertexInputState = &vertexInputInfo; | |
pipelineInfoReal.pInputAssemblyState = &inputAssembly; | |
pipelineInfoReal.pViewportState = &viewportState; | |
pipelineInfoReal.pRasterizationState = &rasterizer; | |
pipelineInfoReal.pMultisampleState = &multisampling; | |
pipelineInfoReal.pColorBlendState = &colorBlending; | |
pipelineInfoReal.layout = pipelineLayout; | |
pipelineInfoReal.renderPass = renderPass; | |
const pipelineInfo = [_]c.VkGraphicsPipelineCreateInfo{ pipelineInfoReal }; | |
try checkSuccess(c.vkCreateGraphicsPipelines( | |
global_device, | |
null, | |
@intCast(u32, pipelineInfo.len), | |
&pipelineInfo, | |
null, | |
&graphicsPipeline, | |
)); | |
c.vkDestroyShaderModule(global_device, fragShaderModule, null); | |
c.vkDestroyShaderModule(global_device, vertShaderModule, null); | |
} | |
fn createRenderPass() !void { | |
const colorAttachment = c.VkAttachmentDescription{ | |
.format = swapChainImageFormat, | |
.samples = .VK_SAMPLE_COUNT_1_BIT, | |
.loadOp = .VK_ATTACHMENT_LOAD_OP_CLEAR, | |
.storeOp = .VK_ATTACHMENT_STORE_OP_STORE, | |
.stencilLoadOp = .VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
.stencilStoreOp = .VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
.initialLayout = .VK_IMAGE_LAYOUT_UNDEFINED, | |
.finalLayout = .VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
.flags = 0, | |
}; | |
const colorAttachmentRef = c.VkAttachmentReference{ | |
.attachment = 0, | |
.layout = .VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
}; | |
const subpass = [_]c.VkSubpassDescription{c.VkSubpassDescription{ | |
.pipelineBindPoint = .VK_PIPELINE_BIND_POINT_GRAPHICS, | |
.colorAttachmentCount = 1, | |
.pColorAttachments = &colorAttachmentRef, | |
.flags = 0, | |
.inputAttachmentCount = 0, | |
.pInputAttachments = null, | |
.pResolveAttachments = null, | |
.pDepthStencilAttachment = null, | |
.preserveAttachmentCount = 0, | |
.pPreserveAttachments = null, | |
}}; | |
const dependency = [_]c.VkSubpassDependency{c.VkSubpassDependency{ | |
.srcSubpass = c.VK_SUBPASS_EXTERNAL, | |
.dstSubpass = 0, | |
.srcStageMask = c.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | |
.srcAccessMask = 0, | |
.dstStageMask = c.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | |
.dstAccessMask = c.VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | c.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | |
.dependencyFlags = 0, | |
}}; | |
var renderPassInfo = VulkanObject(c.VkRenderPassCreateInfo); | |
renderPassInfo.attachmentCount = 1; | |
renderPassInfo.pAttachments = &colorAttachment; | |
renderPassInfo.subpassCount = 1; | |
renderPassInfo.pSubpasses = &subpass; | |
renderPassInfo.dependencyCount = 1; | |
renderPassInfo.pDependencies = &dependency; | |
try checkSuccess(c.vkCreateRenderPass(global_device, &renderPassInfo, null, &renderPass)); | |
} | |
fn createImageViews(allocator: *Allocator) !void { | |
swapChainImageViews = try allocator.alloc(c.VkImageView, swapChainImages.len); | |
errdefer allocator.free(swapChainImageViews); | |
for (swapChainImages) |swap_chain_image, i| { | |
var createInfo = VulkanObject(c.VkImageViewCreateInfo); | |
createInfo.image = swap_chain_image; | |
createInfo.viewType = .VK_IMAGE_VIEW_TYPE_2D; | |
createInfo.format = swapChainImageFormat; | |
createInfo.components = c.VkComponentMapping{ | |
.r = .VK_COMPONENT_SWIZZLE_IDENTITY, | |
.g = .VK_COMPONENT_SWIZZLE_IDENTITY, | |
.b = .VK_COMPONENT_SWIZZLE_IDENTITY, | |
.a = .VK_COMPONENT_SWIZZLE_IDENTITY, | |
}; | |
createInfo.subresourceRange = c.VkImageSubresourceRange{ | |
.aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, | |
.baseMipLevel = 0, | |
.levelCount = 1, | |
.baseArrayLayer = 0, | |
.layerCount = 1, | |
}; | |
try checkSuccess(c.vkCreateImageView(global_device, &createInfo, null, &swapChainImageViews[i])); | |
} | |
} | |
fn chooseSwapSurfaceFormat(availableFormats: []c.VkSurfaceFormatKHR) c.VkSurfaceFormatKHR { | |
if (availableFormats.len == 1 and availableFormats[0].format == .VK_FORMAT_UNDEFINED) { | |
return c.VkSurfaceFormatKHR{ | |
.format = .VK_FORMAT_B8G8R8A8_UNORM, | |
.colorSpace = .VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, | |
}; | |
} | |
for (availableFormats) |availableFormat| { | |
if (availableFormat.format == .VK_FORMAT_B8G8R8A8_UNORM and | |
availableFormat.colorSpace == .VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) | |
{ | |
return availableFormat; | |
} | |
} | |
return availableFormats[0]; | |
} | |
fn chooseSwapPresentMode(availablePresentModes: []c.VkPresentModeKHR) c.VkPresentModeKHR { | |
var bestMode: c.VkPresentModeKHR = .VK_PRESENT_MODE_FIFO_KHR; | |
for (availablePresentModes) |availablePresentMode| { | |
if (availablePresentMode == .VK_PRESENT_MODE_MAILBOX_KHR) { | |
return availablePresentMode; | |
} else if (availablePresentMode == .VK_PRESENT_MODE_IMMEDIATE_KHR) { | |
bestMode = availablePresentMode; | |
} | |
} | |
return bestMode; | |
} | |
fn chooseSwapExtent(capabilities: c.VkSurfaceCapabilitiesKHR) c.VkExtent2D { | |
if (capabilities.currentExtent.width != std.math.maxInt(u32)) { | |
return capabilities.currentExtent; | |
} else { | |
var actualExtent = c.VkExtent2D{ | |
.width = WIDTH, | |
.height = HEIGHT, | |
}; | |
actualExtent.width = std.math.max(capabilities.minImageExtent.width, std.math.min(capabilities.maxImageExtent.width, actualExtent.width)); | |
actualExtent.height = std.math.max(capabilities.minImageExtent.height, std.math.min(capabilities.maxImageExtent.height, actualExtent.height)); | |
return actualExtent; | |
} | |
} | |
fn createSwapChain(allocator: *Allocator) !void { | |
var swapChainSupport = try querySwapChainSupport(allocator, physicalDevice); | |
defer swapChainSupport.deinit(); | |
const surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats.toSlice()); | |
const presentMode = chooseSwapPresentMode(swapChainSupport.presentModes.toSlice()); | |
const extent = chooseSwapExtent(swapChainSupport.capabilities); | |
var imageCount: u32 = swapChainSupport.capabilities.minImageCount + 1; | |
if (swapChainSupport.capabilities.maxImageCount > 0 and | |
imageCount > swapChainSupport.capabilities.maxImageCount) | |
{ | |
imageCount = swapChainSupport.capabilities.maxImageCount; | |
} | |
const indices = try findQueueFamilies(allocator, physicalDevice); | |
const queueFamilyIndices = [_]u32{ indices.graphicsFamily.?, indices.presentFamily.? }; | |
const different_families = indices.graphicsFamily.? != indices.presentFamily.?; | |
var createInfo = VulkanObject(c.VkSwapchainCreateInfoKHR); | |
createInfo.surface = surface; | |
createInfo.minImageCount = imageCount; | |
createInfo.imageFormat = surfaceFormat.format; | |
createInfo.imageColorSpace = surfaceFormat.colorSpace; | |
createInfo.imageExtent = extent; | |
createInfo.imageArrayLayers = 1; | |
createInfo.imageUsage = c.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
createInfo.imageSharingMode = if (different_families) .VK_SHARING_MODE_CONCURRENT else .VK_SHARING_MODE_EXCLUSIVE; | |
createInfo.queueFamilyIndexCount = if (different_families) 2 else 0; | |
createInfo.pQueueFamilyIndices = if (different_families) &queueFamilyIndices else &([_]u32{ 0, 0 }); | |
createInfo.preTransform = swapChainSupport.capabilities.currentTransform; | |
createInfo.compositeAlpha = .VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
createInfo.presentMode = presentMode; | |
createInfo.clipped = c.VK_TRUE; | |
try checkSuccess(c.vkCreateSwapchainKHR(global_device, &createInfo, null, &swapChain)); | |
try checkSuccess(c.vkGetSwapchainImagesKHR(global_device, swapChain, &imageCount, null)); | |
swapChainImages = try allocator.alloc(c.VkImage, imageCount); | |
try checkSuccess(c.vkGetSwapchainImagesKHR(global_device, swapChain, &imageCount, swapChainImages.ptr)); | |
swapChainImageFormat = surfaceFormat.format; | |
swapChainExtent = extent; | |
} | |
fn createLogicalDevice(allocator: *Allocator) !void { | |
const indices = try findQueueFamilies(allocator, physicalDevice); | |
var queueCreateInfos = std.ArrayList(c.VkDeviceQueueCreateInfo).init(allocator); | |
defer queueCreateInfos.deinit(); | |
const all_queue_families = [_]u32{ indices.graphicsFamily.?, indices.presentFamily.? }; | |
const uniqueQueueFamilies = if (indices.graphicsFamily.? == indices.presentFamily.?) | |
all_queue_families[0..1] | |
else | |
all_queue_families[0..2]; | |
var queuePriority: f32 = 1.0; | |
for (uniqueQueueFamilies) |queueFamily| { | |
var queueCreateInfo = VulkanObject(c.VkDeviceQueueCreateInfo); | |
queueCreateInfo.queueFamilyIndex = queueFamily; | |
queueCreateInfo.queueCount = 1; | |
queueCreateInfo.pQueuePriorities = &queuePriority; | |
try queueCreateInfos.append(queueCreateInfo); | |
} | |
const deviceFeatures = c.VkPhysicalDeviceFeatures{ | |
.robustBufferAccess = 0, | |
.fullDrawIndexUint32 = 0, | |
.imageCubeArray = 0, | |
.independentBlend = 0, | |
.geometryShader = 0, | |
.tessellationShader = 0, | |
.sampleRateShading = 0, | |
.dualSrcBlend = 0, | |
.logicOp = 0, | |
.multiDrawIndirect = 0, | |
.drawIndirectFirstInstance = 0, | |
.depthClamp = 0, | |
.depthBiasClamp = 0, | |
.fillModeNonSolid = 0, | |
.depthBounds = 0, | |
.wideLines = 0, | |
.largePoints = 0, | |
.alphaToOne = 0, | |
.multiViewport = 0, | |
.samplerAnisotropy = 0, | |
.textureCompressionETC2 = 0, | |
.textureCompressionASTC_LDR = 0, | |
.textureCompressionBC = 0, | |
.occlusionQueryPrecise = 0, | |
.pipelineStatisticsQuery = 0, | |
.vertexPipelineStoresAndAtomics = 0, | |
.fragmentStoresAndAtomics = 0, | |
.shaderTessellationAndGeometryPointSize = 0, | |
.shaderImageGatherExtended = 0, | |
.shaderStorageImageExtendedFormats = 0, | |
.shaderStorageImageMultisample = 0, | |
.shaderStorageImageReadWithoutFormat = 0, | |
.shaderStorageImageWriteWithoutFormat = 0, | |
.shaderUniformBufferArrayDynamicIndexing = 0, | |
.shaderSampledImageArrayDynamicIndexing = 0, | |
.shaderStorageBufferArrayDynamicIndexing = 0, | |
.shaderStorageImageArrayDynamicIndexing = 0, | |
.shaderClipDistance = 0, | |
.shaderCullDistance = 0, | |
.shaderFloat64 = 0, | |
.shaderInt64 = 0, | |
.shaderInt16 = 0, | |
.shaderResourceResidency = 0, | |
.shaderResourceMinLod = 0, | |
.sparseBinding = 0, | |
.sparseResidencyBuffer = 0, | |
.sparseResidencyImage2D = 0, | |
.sparseResidencyImage3D = 0, | |
.sparseResidency2Samples = 0, | |
.sparseResidency4Samples = 0, | |
.sparseResidency8Samples = 0, | |
.sparseResidency16Samples = 0, | |
.sparseResidencyAliased = 0, | |
.variableMultisampleRate = 0, | |
.inheritedQueries = 0, | |
}; | |
var createInfo = VulkanObject(c.VkDeviceCreateInfo); | |
createInfo.queueCreateInfoCount = @intCast(u32, queueCreateInfos.len); | |
createInfo.pQueueCreateInfos = queueCreateInfos.items.ptr; | |
createInfo.pEnabledFeatures = &deviceFeatures; | |
createInfo.enabledExtensionCount = @intCast(u32, deviceExtensions.len); | |
createInfo.ppEnabledExtensionNames = &deviceExtensions; | |
createInfo.enabledLayerCount = if (enableValidationLayers) @intCast(u32, validationLayers.len) else 0; | |
createInfo.ppEnabledLayerNames = if (enableValidationLayers) &validationLayers else null; | |
try checkSuccess(c.vkCreateDevice(physicalDevice, &createInfo, null, &global_device)); | |
c.vkGetDeviceQueue(global_device, indices.graphicsFamily.?, 0, &graphicsQueue); | |
c.vkGetDeviceQueue(global_device, indices.presentFamily.?, 0, &presentQueue); | |
} | |
fn pickPhysicalDevice(allocator: *Allocator) !void { | |
var deviceCount: u32 = 0; | |
try checkSuccess(c.vkEnumeratePhysicalDevices(instance, &deviceCount, null)); | |
if (deviceCount == 0) { | |
return error.FailedToFindGPUsWithVulkanSupport; | |
} | |
const devices = try allocator.alloc(c.VkPhysicalDevice, deviceCount); | |
defer allocator.free(devices); | |
try checkSuccess(c.vkEnumeratePhysicalDevices(instance, &deviceCount, devices.ptr)); | |
physicalDevice = for (devices) |device| { | |
if (try isDeviceSuitable(allocator, device)) { | |
break device; | |
} | |
} else return error.FailedToFindSuitableGPU; | |
} | |
fn findQueueFamilies(allocator: *Allocator, device: c.VkPhysicalDevice) !QueueFamilyIndices { | |
var indices = QueueFamilyIndices.init(); | |
var queueFamilyCount: u32 = 0; | |
c.vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, null); | |
const queueFamilies = try allocator.alloc(c.VkQueueFamilyProperties, queueFamilyCount); | |
defer allocator.free(queueFamilies); | |
c.vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.ptr); | |
var i: u32 = 0; | |
for (queueFamilies) |queueFamily| { | |
if (queueFamily.queueCount > 0 and | |
queueFamily.queueFlags & @intCast(u32, c.VK_QUEUE_GRAPHICS_BIT) != 0) | |
{ | |
indices.graphicsFamily = i; | |
} | |
var presentSupport: c.VkBool32 = 0; | |
try checkSuccess(c.vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport)); | |
if (queueFamily.queueCount > 0 and presentSupport != 0) { | |
indices.presentFamily = i; | |
} | |
if (indices.isComplete()) { | |
break; | |
} | |
i += 1; | |
} | |
return indices; | |
} | |
fn isDeviceSuitable(allocator: *Allocator, device: c.VkPhysicalDevice) !bool { | |
const indices = try findQueueFamilies(allocator, device); | |
const extensionsSupported = try checkDeviceExtensionSupport(allocator, device); | |
var swapChainAdequate = false; | |
if (extensionsSupported) { | |
var swapChainSupport = try querySwapChainSupport(allocator, device); | |
defer swapChainSupport.deinit(); | |
swapChainAdequate = swapChainSupport.formats.len != 0 and swapChainSupport.presentModes.len != 0; | |
} | |
return indices.isComplete() and extensionsSupported and swapChainAdequate; | |
} | |
fn querySwapChainSupport(allocator: *Allocator, device: c.VkPhysicalDevice) !SwapChainSupportDetails { | |
var details = SwapChainSupportDetails.init(allocator); | |
try checkSuccess(c.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities)); | |
var formatCount: u32 = undefined; | |
try checkSuccess(c.vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, null)); | |
if (formatCount != 0) { | |
try details.formats.resize(formatCount); | |
try checkSuccess(c.vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.items.ptr)); | |
} | |
var presentModeCount: u32 = undefined; | |
try checkSuccess(c.vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, null)); | |
if (presentModeCount != 0) { | |
try details.presentModes.resize(presentModeCount); | |
try checkSuccess(c.vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.items.ptr)); | |
} | |
return details; | |
} | |
fn checkDeviceExtensionSupport(allocator: *Allocator, device: c.VkPhysicalDevice) !bool { | |
var extensionCount: u32 = undefined; | |
try checkSuccess(c.vkEnumerateDeviceExtensionProperties(device, null, &extensionCount, null)); | |
const availableExtensions = try allocator.alloc(c.VkExtensionProperties, extensionCount); | |
defer allocator.free(availableExtensions); | |
try checkSuccess(c.vkEnumerateDeviceExtensionProperties(device, null, &extensionCount, availableExtensions.ptr)); | |
var requiredExtensions = std.HashMap([*:0]const u8, void, hash_cstr, eql_cstr).init(allocator); | |
defer requiredExtensions.deinit(); | |
for (deviceExtensions) |device_ext| { | |
_ = try requiredExtensions.put(device_ext, {}); | |
} | |
for (availableExtensions) |extension| { | |
const name: [*:0]const u8 = @ptrCast([*:0]const u8, &extension.extensionName); | |
_ = requiredExtensions.remove(name); | |
} | |
return requiredExtensions.count() == 0; | |
} | |
fn createSurface(window: *c.GLFWwindow) !void { | |
if (c.glfwCreateWindowSurface(instance, window, null, &surface) != @intToEnum(c.VkResult, c.VK_SUCCESS)) { | |
return error.FailedToCreateWindowSurface; | |
} | |
} | |
// TODO https://github.com/ziglang/zig/issues/661 | |
// Doesn't work on Windows until the above is fixed, because | |
// this function needs to be stdcallcc on Windows. | |
fn debugCallback( | |
flags: c.VkDebugReportFlagsEXT, | |
objType: c.VkDebugReportObjectTypeEXT, | |
obj: u64, | |
location: usize, | |
code: i32, | |
layerPrefix: ?[*:0]const u8, | |
msg: ?[*:0]const u8, | |
userData: ?*c_void, | |
) callconv(.C) c.VkBool32 { | |
std.debug.warn("validation layer: {s}\n", .{msg}); | |
return c.VK_FALSE; | |
} | |
fn setupDebugCallback() error{FailedToSetUpDebugCallback}!void { | |
if (!enableValidationLayers) return; | |
var createInfo = VulkanObject(c.VkDebugReportCallbackCreateInfoEXT); | |
createInfo.flags = c.VK_DEBUG_REPORT_ERROR_BIT_EXT | c.VK_DEBUG_REPORT_WARNING_BIT_EXT; | |
createInfo.pfnCallback = debugCallback; | |
if (CreateDebugReportCallbackEXT(&createInfo, null, &callback) != @intToEnum(c.VkResult, c.VK_SUCCESS)) { | |
return error.FailedToSetUpDebugCallback; | |
} | |
} | |
fn DestroyDebugReportCallbackEXT( | |
pAllocator: ?*const c.VkAllocationCallbacks, | |
) void { | |
const func = @ptrCast(c.PFN_vkDestroyDebugReportCallbackEXT, c.vkGetInstanceProcAddr( | |
instance, | |
"vkDestroyDebugReportCallbackEXT", | |
)) orelse unreachable; | |
func(instance, callback, pAllocator); | |
} | |
fn CreateDebugReportCallbackEXT( | |
pCreateInfo: *const c.VkDebugReportCallbackCreateInfoEXT, | |
pAllocator: ?*const c.VkAllocationCallbacks, | |
pCallback: *c.VkDebugReportCallbackEXT, | |
) c.VkResult { | |
const func = @ptrCast(c.PFN_vkCreateDebugReportCallbackEXT, c.vkGetInstanceProcAddr( | |
instance, | |
"vkCreateDebugReportCallbackEXT", | |
)) orelse return @intToEnum(c.VkResult, c.VK_ERROR_EXTENSION_NOT_PRESENT); | |
return func(instance, pCreateInfo, pAllocator, pCallback); | |
} | |
fn createInstance(allocator: *Allocator) !void { | |
if (enableValidationLayers) { | |
if (!(try checkValidationLayerSupport(allocator))) { | |
return error.ValidationLayerRequestedButNotAvailable; | |
} | |
} | |
var appInfo = VulkanObject(c.VkApplicationInfo); | |
appInfo.pApplicationName = "Hello Triangle"; | |
appInfo.applicationVersion = c.VK_MAKE_VERSION(1, 0, 0); | |
appInfo.pEngineName = "No Engine"; | |
appInfo.engineVersion = c.VK_MAKE_VERSION(1, 0, 0); | |
appInfo.apiVersion = c.VK_API_VERSION_1_0; | |
const extensions = try getRequiredExtensions(allocator); | |
defer allocator.free(extensions); | |
var createInfo = VulkanObject(c.VkInstanceCreateInfo); | |
createInfo.pApplicationInfo = &appInfo; | |
createInfo.enabledExtensionCount = @intCast(u32, extensions.len); | |
createInfo.ppEnabledExtensionNames = extensions.ptr; | |
createInfo.enabledLayerCount = if (enableValidationLayers) @intCast(u32, validationLayers.len) else 0; | |
createInfo.ppEnabledLayerNames = if (enableValidationLayers) &validationLayers else null; | |
try checkSuccess(c.vkCreateInstance(&createInfo, null, &instance)); | |
} | |
/// caller must free returned memory | |
fn getRequiredExtensions(allocator: *Allocator) ![][*]const u8 { | |
var glfwExtensionCount: u32 = 0; | |
const glfwExtensions: [*]const?[*:0]const u8 = c.glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
// var glfwExtensions: [*]const [*:0]const u8 = c.glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
var extensions = std.ArrayList([*:0]const u8).init(allocator); | |
errdefer extensions.deinit(); | |
var i: u32 = 0; | |
while (i < glfwExtensionCount) : (i += 1) { | |
if (glfwExtensions[i]) |ext| { | |
try extensions.append(ext); | |
} | |
} | |
if (enableValidationLayers) { | |
try extensions.append(c.VK_EXT_DEBUG_REPORT_EXTENSION_NAME); | |
} | |
return extensions.toOwnedSlice(); | |
} | |
fn checkSuccess(result: c.VkResult) !void { | |
switch (@enumToInt(result)) { | |
c.VK_SUCCESS => {}, | |
else => return error.Unexpected, | |
} | |
} | |
fn checkValidationLayerSupport(allocator: *Allocator) !bool { | |
var layerCount: u32 = undefined; | |
try checkSuccess(c.vkEnumerateInstanceLayerProperties(&layerCount, null)); | |
var availableLayers = try allocator.alloc(c.VkLayerProperties, layerCount); | |
defer allocator.free(availableLayers); | |
try checkSuccess(c.vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.ptr)); | |
for (validationLayers) |layerName| { | |
var layerFound = false; | |
for (availableLayers) |*layerProperties| { | |
const name: [*:0]const u8 = @ptrCast([*:0]const u8, &layerProperties.layerName); | |
if (std.cstr.cmp(layerName, name) == 0) { | |
layerFound = true; | |
break; | |
} | |
} | |
if (!layerFound) { | |
return false; | |
} | |
} | |
return true; | |
} | |
fn drawFrame() !void { | |
try checkSuccess(c.vkWaitForFences(global_device, 1, &inFlightFences[currentFrame], c.VK_TRUE, std.math.maxInt(u64))); | |
try checkSuccess(c.vkResetFences(global_device, 1, &inFlightFences[currentFrame])); | |
var imageIndex: u32 = undefined; | |
try checkSuccess(c.vkAcquireNextImageKHR(global_device, swapChain, std.math.maxInt(u64), imageAvailableSemaphores[currentFrame], null, &imageIndex)); | |
var waitSemaphores = [_]c.VkSemaphore{imageAvailableSemaphores[currentFrame]}; | |
var waitStages = [_]c.VkPipelineStageFlags{c.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; | |
const signalSemaphores = [_]c.VkSemaphore{renderFinishedSemaphores[currentFrame]}; | |
var submitInfoReal = VulkanObject(c.VkSubmitInfo); | |
submitInfoReal.waitSemaphoreCount = 1; | |
submitInfoReal.pWaitSemaphores = &waitSemaphores; | |
submitInfoReal.pWaitDstStageMask = &waitStages; | |
submitInfoReal.commandBufferCount = 1; | |
submitInfoReal.pCommandBuffers = commandBuffers.ptr + imageIndex; | |
submitInfoReal.signalSemaphoreCount = 1; | |
submitInfoReal.pSignalSemaphores = &signalSemaphores; | |
var submitInfo = [_]c.VkSubmitInfo{submitInfoReal}; | |
try checkSuccess(c.vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame])); | |
const swapChains = [_]c.VkSwapchainKHR{swapChain}; | |
var presentInfo = VulkanObject(c.VkPresentInfoKHR); | |
presentInfo.waitSemaphoreCount = 1; | |
presentInfo.pWaitSemaphores = &signalSemaphores; | |
presentInfo.swapchainCount = 1; | |
presentInfo.pSwapchains = &swapChains; | |
presentInfo.pImageIndices = &imageIndex; | |
try checkSuccess(c.vkQueuePresentKHR(presentQueue, &presentInfo)); | |
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; | |
} | |
fn hash_cstr(a: [*:0]const u8) u32 { | |
// FNV 32-bit hash | |
var h: u32 = 2166136261; | |
var i: usize = 0; | |
while (a[i] != 0) : (i += 1) { | |
h ^= a[i]; | |
h *%= 16777619; | |
} | |
return h; | |
} | |
fn eql_cstr(a: [*:0]const u8, b: [*:0]const u8) bool { | |
return std.cstr.cmp(a, b) == 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment