Skip to content

Instantly share code, notes, and snippets.

@flysand7
Created November 4, 2024 00:33
Show Gist options
  • Save flysand7/9583abdb45176ed07596accdd911827c to your computer and use it in GitHub Desktop.
Save flysand7/9583abdb45176ed07596accdd911827c to your computer and use it in GitHub Desktop.
One file example of vulkan triangle
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
#version 450
layout(location = 0) out vec3 fragColor;
vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}
package game
import "core:time"
import "core:mem"
import "core:fmt"
import "core:log"
import "core:os"
import "core:dynlib"
import "core:slice"
import "base:runtime"
import "vendor:glfw"
import vk "vendor:vulkan"
VK_APP_NAME :: "Game"
VK_SO_NAME :: "vulkan-1.dll"
main :: proc() {
tracking_allocator: mem.Tracking_Allocator
mem.tracking_allocator_init(&tracking_allocator, context.allocator)
defer mem.tracking_allocator_destroy(&tracking_allocator)
context.allocator = mem.tracking_allocator(&tracking_allocator)
defer {
for _, leak in tracking_allocator.allocation_map {
log.errorf("[LEAK] %v leaked %m", leak.location, leak.size)
}
for bad_free in tracking_allocator.bad_free_array {
log.errorf("[BAD FREE] %v allocation %p was freed badly", bad_free.location, bad_free.memory)
}
}
context.logger = log.Logger {
data = nil,
lowest_level = .Debug,
procedure = proc(
data: rawptr,
level: log.Level,
text: string,
options: log.Options,
location := #caller_location,
) {
level_str: string = ---
color_seq: string = ---
switch level {
case .Debug:
level_str = "D"
color_seq = "\e[38;5;241m"
case .Info:
level_str = "I"
color_seq = "\e[38;5;78m"
case .Warning:
level_str = "W"
color_seq = "\e[38;5;220m"
case .Error:
level_str = "E"
color_seq = "\e[38;5;203m"
case .Fatal:
level_str = "F"
color_seq = "\e[38;5;165m"
}
dt, _ := time.time_to_compound(time.now())
fmt.printfln(
"%s[%02d:%02d:%02d.%04d] [%s] %s\e[0m",
color_seq,
dt.hour, dt.minute, dt.second, dt.nano/1_000_00,
level_str,
text,
)
},
}
glfw.SetErrorCallback(proc "c" (error: i32, description: cstring) {
context = {}
fmt.printfln("[glfw] Error (%d): %s", error, description)
})
if !glfw.Init() {
log.fatalf("Unable to initialize GLFW.")
os.exit(1)
}
defer glfw.Terminate()
glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API)
glfw.WindowHint(glfw.RESIZABLE, glfw.FALSE);
window := glfw.CreateWindow(1280, 720, "Game", nil, nil)
if window == nil {
log.fatalf("Unable to create a GLFW window")
os.exit(1)
}
defer glfw.DestroyWindow(window)
{
log.debugf("[vk] Loading vulkan.")
vulkan_lib, load_ok := dynlib.load_library(VK_SO_NAME)
if !load_ok {
log.fatalf("Unable to load library: %s", VK_SO_NAME)
os.exit(1)
}
log.debugf("[vk] Loading vulkan procedures")
get_instance_proc := auto_cast dynlib.symbol_address(vulkan_lib, "vkGetInstanceProcAddr")
if get_instance_proc == nil {
log.fatalf("Unable to find symbol '%s' in library: %s", "vkGetInstanceProcAddr", VK_SO_NAME)
os.exit(1)
}
vk.load_proc_addresses_global(get_instance_proc)
log.infof("[vk] Vulkan procedures loaded")
}
req_layers: [dynamic]cstring
append(&req_layers, "VK_LAYER_KHRONOS_validation")
log.debugf("[vk] Required layers:")
for layer in req_layers {
log.debugf("[vk] - %s", layer)
}
{
log.debugf("[vk] Checking required layers.")
vk_layer_count: u32 = ---
vk.EnumerateInstanceLayerProperties(&vk_layer_count, nil)
log.debugf("[vk] Found %d layers.", vk_layer_count)
vk_layers := make([]vk.LayerProperties, vk_layer_count)
defer delete(vk_layers)
vk.EnumerateInstanceLayerProperties(&vk_layer_count, &vk_layers[0])
for &layer in vk_layers {
layer_name := cstring(&layer.layerName[0])
log.debugf("[vk] - Available layer: %s", layer_name)
}
next_layer: for req_layer in req_layers {
for &layer in vk_layers {
if req_layer == cstring(&layer.layerName[0]) {
continue next_layer
}
}
log.fatalf("Required vulkan layer '%s' not supported", req_layer)
os.exit(1)
}
log.infof("[vk] All required layers found!")
}
req_exts: [dynamic]cstring
append(&req_exts, ..glfw.GetRequiredInstanceExtensions())
append(&req_exts, "VK_EXT_debug_utils")
log.debugf("[vk] Required extensions:")
for ext in req_exts {
log.debugf("[vk] - %s", ext)
}
{
log.debugf("[vk] Checking required extensions.")
vk_ext_count: u32 = ---
vk.EnumerateInstanceExtensionProperties(nil, &vk_ext_count, nil)
log.debugf("[vk] Found %d extensions.", vk_ext_count)
vk_exts := make([]vk.ExtensionProperties, vk_ext_count)
defer delete(vk_exts)
vk.EnumerateInstanceExtensionProperties(nil, &vk_ext_count, &vk_exts[0])
for &ext in vk_exts {
log.debugf("[vk] - Available extension: %s", cstring(&ext.extensionName[0]))
}
next_ext: for req_ext in req_exts {
for &ext in vk_exts {
if req_ext == cstring(&ext.extensionName[0]) {
continue next_ext
}
}
log.fatal("Required extension '%s' Not found.", req_ext)
os.exit(1)
}
log.infof("[vk] All required extensions found!")
}
log.debugf("[vk] Creating a vulkan inst.")
inst: vk.Instance = ---
inst_ci := vk.InstanceCreateInfo {
sType = .INSTANCE_CREATE_INFO,
pApplicationInfo = &vk.ApplicationInfo {
sType = .APPLICATION_INFO,
pApplicationName = VK_APP_NAME,
applicationVersion = vk.MAKE_VERSION(1, 0, 0),
pEngineName = "No Engine",
engineVersion = vk.MAKE_VERSION(1, 0, 0),
apiVersion = vk.API_VERSION_1_0,
},
enabledExtensionCount = u32(len(req_exts)),
ppEnabledExtensionNames = raw_data(req_exts),
enabledLayerCount = u32(len(req_layers)),
ppEnabledLayerNames = raw_data(req_layers),
}
if res := vk.CreateInstance(&inst_ci, nil, &inst); res != .SUCCESS {
assert(res != .ERROR_LAYER_NOT_PRESENT)
log.fatalf("Unable to create vulkan inst: %v", res)
os.exit(1)
}
defer vk.DestroyInstance(inst, nil)
vk.load_proc_addresses_instance(inst)
delete(req_exts)
delete(req_layers)
log.infof("[vk] Instance created!")
log.debugf("[vk] Creating vulkan debug messenger.")
VK_DBG_Capture :: struct {
ctx: runtime.Context,
}
capture := VK_DBG_Capture {
ctx = context,
}
dbg_messenger_ci := vk.DebugUtilsMessengerCreateInfoEXT {
sType = .DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
messageSeverity = { .VERBOSE, .INFO, .WARNING, .ERROR },
messageType = { .DEVICE_ADDRESS_BINDING, .GENERAL, .PERFORMANCE, .VALIDATION },
pfnUserCallback = proc "system" (
messageSeverity: vk.DebugUtilsMessageSeverityFlagsEXT,
messageTypes: vk.DebugUtilsMessageTypeFlagsEXT,
pCallbackData: ^vk.DebugUtilsMessengerCallbackDataEXT,
pUserData: rawptr,
) -> b32 {
capture := cast(^VK_DBG_Capture) pUserData
context = capture.ctx
type: string = ---
msg := pCallbackData.pMessage
switch messageTypes {
case {.DEVICE_ADDRESS_BINDING}: type = "D"
case {.GENERAL}: type = "G"
case {.PERFORMANCE}: type = "P"
case {.VALIDATION}: type = "V"
case: return false
}
switch messageSeverity {
case {.VERBOSE}: log.debugf("[vk/dbg] [%s] %s", type, msg)
case {.INFO}: log.infof("[vk/dbg] [%s] %s", type, msg)
case {.WARNING}: log.warnf("[vk/dbg] [%s] %s", type, msg)
case {.ERROR}: log.errorf("[vk/dbg] [%s] %s", type, msg)
case:
panic("Unhandled message severity")
}
return true
},
pUserData = &capture,
}
dbg_messenger: vk.DebugUtilsMessengerEXT = ---
vk.CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_ci, nil, &dbg_messenger)
defer vk.DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nil)
log.infof("[vk] Debug messenger created!")
surface: vk.SurfaceKHR = ---
log.debugf("[glfw] Creating window surface.")
if res := glfw.CreateWindowSurface(inst, window, nil, &surface); res != .SUCCESS {
log.fatalf("[glfw] Unable to create window surface: %v", res)
os.exit(1)
}
log.infof("[glfw] Created window surface!")
defer vk.DestroySurfaceKHR(inst, surface, nil)
PH_Dev_Info :: struct {
vk_dev: vk.PhysicalDevice,
props: vk.PhysicalDeviceProperties,
features: vk.PhysicalDeviceFeatures,
graphics_qf: u32,
present_qf: u32,
}
log.debugf("[vk] Searching for viable physical devices.")
req_dev_exts := make([dynamic]cstring)
append(&req_dev_exts, "VK_KHR_swapchain")
ph_devs := make([dynamic]PH_Dev_Info, context.allocator)
{
vk_ph_dev_count: u32 = ---
vk.EnumeratePhysicalDevices(inst, &vk_ph_dev_count, nil)
if vk_ph_dev_count == 0 {
log.fatalf("[vk] No GPU's found on this machine!")
os.exit(1)
}
log.debugf("[vk] Found %d devices.", vk_ph_dev_count)
vk_ph_devs := make([]vk.PhysicalDevice, vk_ph_dev_count)
defer delete(vk_ph_devs)
vk.EnumeratePhysicalDevices(inst, &vk_ph_dev_count, &vk_ph_devs[0])
next_device: for dev in vk_ph_devs {
props: vk.PhysicalDeviceProperties = ---
features: vk.PhysicalDeviceFeatures = ---
vk.GetPhysicalDeviceProperties(dev, &props)
vk.GetPhysicalDeviceFeatures(dev, &features)
ext_count: u32 = ---
vk.EnumerateDeviceExtensionProperties(dev, nil, &ext_count, nil)
exts := make([]vk.ExtensionProperties, ext_count)
defer delete(exts)
vk.EnumerateDeviceExtensionProperties(dev, nil, &ext_count, raw_data(exts))
for chk_ext in req_dev_exts {
has_ext := false
for &ext in exts {
if cstring(&ext.extensionName[0]) == chk_ext {
has_ext = true
break
}
}
if !has_ext {
continue next_device
}
}
graphics_qf := u32(0)
present_qf := u32(0)
g_qf_present := false
p_qf_present := false
dev_qf_n: u32 = ---
vk.GetPhysicalDeviceQueueFamilyProperties(dev, &dev_qf_n, nil)
dev_qfs := make([]vk.QueueFamilyProperties, dev_qf_n)
defer delete(dev_qfs)
vk.GetPhysicalDeviceQueueFamilyProperties(dev, &dev_qf_n, &dev_qfs[0])
for qf, i in dev_qfs {
supports_presentation: b32
vk.GetPhysicalDeviceSurfaceSupportKHR(dev, u32(i), surface, &supports_presentation)
if supports_presentation {
present_qf = u32(i)
p_qf_present = true
}
if qf.queueFlags >= {.GRAPHICS} {
graphics_qf = u32(i)
g_qf_present = true
}
}
if !g_qf_present || !p_qf_present {
continue next_device
}
caps: vk.SurfaceCapabilitiesKHR = ---
vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface, &caps)
format_count: u32 = ---
vk.GetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, nil)
if format_count == 0 {
continue next_device
}
mode_count: u32 = ---
vk.GetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &mode_count, nil)
if mode_count == 0 {
continue next_device
}
append(&ph_devs, PH_Dev_Info {
vk_dev = dev,
props = props,
features = features,
graphics_qf = graphics_qf,
present_qf = present_qf,
})
}
}
log.infof("[vk] Found %d viable physical devices!", len(ph_devs))
ph_dev: vk.PhysicalDevice
graphics_qf: u32
present_qf: u32
log.debugf("[vk] Picking a physical device.")
for &dev in ph_devs {
log.debugf("[vk] - Viable device: %s", cstring(&dev.props.deviceName[0]))
ph_dev = dev.vk_dev
graphics_qf = dev.graphics_qf
present_qf = dev.present_qf
}
delete(ph_devs)
log.infof("[vk] Picked physical device!")
log.debugf("[vk] Creating a logical device")
q_infos := make([dynamic]vk.DeviceQueueCreateInfo)
append(&q_infos, vk.DeviceQueueCreateInfo {
sType = .DEVICE_QUEUE_CREATE_INFO,
queueFamilyIndex = graphics_qf,
queueCount = 1,
pQueuePriorities = raw_data([]f32 { 1.0 }),
})
append(&q_infos, vk.DeviceQueueCreateInfo {
sType = .DEVICE_QUEUE_CREATE_INFO,
queueFamilyIndex = present_qf,
queueCount = 1,
pQueuePriorities = raw_data([]f32 { 1.0 }),
})
// Note(flysand): We've just added queues of different matching types into
// the dynamic array of queues, but there's no guarantee that we've put
// each queue family index once. Which is why we remove each duplicate queue
// family from the array before creating the logical device. Each queue
// creation info will then get the biggest queue count from the two.
for i := 0; i < len(q_infos); {
duplicate_found := false
for j := i+1; j < len(q_infos); j += 1 {
if q_infos[i].queueFamilyIndex == q_infos[j].queueFamilyIndex {
duplicate_found = true
largest_qc := max(q_infos[i].queueCount, q_infos[j].queueCount)
if q_infos[i].queueCount == largest_qc {
q_infos[j].pQueuePriorities = q_infos[i].pQueuePriorities
}
q_infos[j].queueCount = largest_qc
break
}
}
if duplicate_found {
ordered_remove(&q_infos, i)
} else {
i += 1
}
}
dev_ci := vk.DeviceCreateInfo {
sType = .DEVICE_CREATE_INFO,
pEnabledFeatures = &vk.PhysicalDeviceFeatures {},
queueCreateInfoCount = u32(len(q_infos)),
pQueueCreateInfos = raw_data(q_infos),
enabledExtensionCount = u32(len(req_dev_exts)),
ppEnabledExtensionNames = raw_data(req_dev_exts),
}
dev: vk.Device = ---
if err := vk.CreateDevice(ph_dev, &dev_ci, nil, &dev); err != .SUCCESS {
log.fatalf("[vk] Unable to create logical device: %v", err)
os.exit(1)
}
defer vk.DestroyDevice(dev, nil)
log.infof("[vk] Created a logical device")
delete(q_infos)
delete(req_dev_exts)
present_q: vk.Queue = ---
graphics_q: vk.Queue = ---
vk.GetDeviceQueue(dev, present_qf, 0, &present_q)
vk.GetDeviceQueue(dev, graphics_qf, 0, &graphics_q)
log.debugf("[vk] Creating a swap chain")
caps: vk.SurfaceCapabilitiesKHR = ---
vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(ph_dev, surface, &caps)
format_count: u32 = ---
vk.GetPhysicalDeviceSurfaceFormatsKHR(ph_dev, surface, &format_count, nil)
formats := make([]vk.SurfaceFormatKHR, format_count)
vk.GetPhysicalDeviceSurfaceFormatsKHR(ph_dev, surface, &format_count, raw_data(formats))
mode_count: u32 = ---
vk.GetPhysicalDeviceSurfacePresentModesKHR(ph_dev, surface, &mode_count, nil)
modes := make([]vk.PresentModeKHR, mode_count)
vk.GetPhysicalDeviceSurfacePresentModesKHR(ph_dev, surface, &mode_count, raw_data(modes))
mode := modes[0]
for m in modes {
if m == .FIFO {
mode = m
break
}
}
format := formats[0]
for f in formats {
if f.colorSpace == .SRGB_NONLINEAR && f.format == .R8G8B8A8_SRGB {
format = f
break
}
}
swap_extent: vk.Extent2D
if caps.currentExtent.width != max(u32) {
swap_extent = caps.currentExtent
} else {
width_i, height_i := glfw.GetFramebufferSize(window)
swap_extent.width = clamp(u32(width_i), caps.minImageExtent.width, caps.maxImageExtent.width)
swap_extent.height = clamp(u32(height_i), caps.minImageExtent.height, caps.maxImageExtent.height)
}
image_count := caps.minImageCount + 1
if caps.maxImageCount > 0 {
image_count = min(image_count, caps.maxImageCount)
}
swapchain_ci := vk.SwapchainCreateInfoKHR {
sType = .SWAPCHAIN_CREATE_INFO_KHR,
surface = surface,
minImageCount = image_count,
imageFormat = format.format,
imageColorSpace = format.colorSpace,
imageExtent = swap_extent,
imageArrayLayers = 1,
imageUsage = {.COLOR_ATTACHMENT},
preTransform = caps.currentTransform,
compositeAlpha = { .OPAQUE },
presentMode = mode,
clipped = true,
}
if graphics_qf != present_qf {
swapchain_ci.imageSharingMode = .CONCURRENT
swapchain_ci.queueFamilyIndexCount = 2
swapchain_ci.pQueueFamilyIndices = raw_data([]u32 { graphics_qf, present_qf })
} else {
swapchain_ci.imageSharingMode = .EXCLUSIVE
swapchain_ci.queueFamilyIndexCount = 0
swapchain_ci.pQueueFamilyIndices = nil
}
swapchain: vk.SwapchainKHR = ---
if res := vk.CreateSwapchainKHR(dev, &swapchain_ci, nil, &swapchain); res != .SUCCESS {
log.fatalf("[vk] Failed to create swapchain!")
os.exit(1)
}
delete(modes)
delete(formats)
defer vk.DestroySwapchainKHR(dev, swapchain, nil)
log.infof("[vk] Swap chain created!")
log.debugf("[vk] Creating vulkan image views")
images_count: u32 = ---
vk.GetSwapchainImagesKHR(dev, swapchain, &images_count, nil)
images := make([]vk.Image, images_count)
vk.GetSwapchainImagesKHR(dev, swapchain, &images_count, raw_data(images))
defer delete(images)
image_views := make([]vk.ImageView, images_count)
for image, i in images {
image_view_info := vk.ImageViewCreateInfo {
sType = .IMAGE_VIEW_CREATE_INFO,
image = image,
viewType = .D2,
format = format.format,
components = { .IDENTITY, .IDENTITY, .IDENTITY, .IDENTITY },
subresourceRange = {
aspectMask = { .COLOR },
baseMipLevel = 0,
levelCount = 1,
baseArrayLayer = 0,
layerCount = 1,
},
}
if res := vk.CreateImageView(dev, &image_view_info, nil, &image_views[i]); res != .SUCCESS {
log.fatalf("[vk] Unable to create image view at index %d", i)
os.exit(1)
}
}
defer {
for view in image_views {
vk.DestroyImageView(dev, view, nil)
}
delete(image_views)
}
log.infof("[vk] Swapchain image views created!")
log.debugf("[vk] Creating shaders.")
vert_shader: vk.ShaderModule = ---
{
VERT_SHADER_PATH :: "assets/shader-vert.spv"
vert_shader_code, vert_ok := os.read_entire_file(VERT_SHADER_PATH)
if !vert_ok {
log.fatal("[vk] Unable to load vertex shader: %s", VERT_SHADER_PATH)
os.exit(1)
}
shader_ci := vk.ShaderModuleCreateInfo {
sType = .SHADER_MODULE_CREATE_INFO,
codeSize = len(vert_shader_code),
pCode = transmute(^u32) raw_data(vert_shader_code),
}
if res := vk.CreateShaderModule(dev, &shader_ci, nil, &vert_shader); res != .SUCCESS {
log.fatalf("[vk] Unable to create vertex shader module: %v", res)
os.exit(1)
}
delete(vert_shader_code)
}
defer vk.DestroyShaderModule(dev, vert_shader, nil)
frag_shader: vk.ShaderModule = ---
{
FRAG_SHADER_PATH :: "assets/shader-frag.spv"
frag_shader_code, frag_ok := os.read_entire_file(FRAG_SHADER_PATH)
if !frag_ok {
log.fatal("[vk] Unable to load fragment shader: %s", FRAG_SHADER_PATH)
os.exit(1)
}
shader_ci := vk.ShaderModuleCreateInfo {
sType = .SHADER_MODULE_CREATE_INFO,
codeSize = len(frag_shader_code),
pCode = transmute(^u32) raw_data(frag_shader_code),
}
if res := vk.CreateShaderModule(dev, &shader_ci, nil, &frag_shader); res != .SUCCESS {
log.fatalf("[vk] Unable to create fragment shader module: %v", res)
os.exit(1)
}
delete(frag_shader_code)
}
defer vk.DestroyShaderModule(dev, frag_shader, nil)
log.infof("[vk] Shaders created!")
shader_stages := []vk.PipelineShaderStageCreateInfo {
{
sType = .PIPELINE_SHADER_STAGE_CREATE_INFO,
stage = { .VERTEX },
module = vert_shader,
pName = "main",
},
{
sType = .PIPELINE_SHADER_STAGE_CREATE_INFO,
stage = { .FRAGMENT },
module = frag_shader,
pName = "main",
},
}
pipeline_layout_ci := vk.PipelineLayoutCreateInfo {
sType = .PIPELINE_LAYOUT_CREATE_INFO,
setLayoutCount = 0,
pSetLayouts = nil,
pushConstantRangeCount = 0,
pPushConstantRanges = nil,
}
pipeline_layout: vk.PipelineLayout = ---
if res := vk.CreatePipelineLayout(dev, &pipeline_layout_ci, nil, &pipeline_layout); res != .SUCCESS {
log.fatal("Unable to create pipeline layout: %v", res)
os.exit(1)
}
defer vk.DestroyPipelineLayout(dev, pipeline_layout, nil)
log.debugf("[vk] Creating a render pass.")
render_pass_ci := vk.RenderPassCreateInfo {
sType = .RENDER_PASS_CREATE_INFO,
attachmentCount = 1,
pAttachments = &vk.AttachmentDescription {
format = format.format,
samples = { ._1 },
loadOp = .CLEAR,
storeOp = .STORE,
stencilLoadOp = .DONT_CARE,
stencilStoreOp = .DONT_CARE,
initialLayout = .UNDEFINED,
finalLayout = .PRESENT_SRC_KHR,
},
subpassCount = 1,
pSubpasses = &vk.SubpassDescription {
pipelineBindPoint = .GRAPHICS,
colorAttachmentCount = 1,
pColorAttachments = &vk.AttachmentReference {
attachment = 0,
layout = .COLOR_ATTACHMENT_OPTIMAL,
},
},
dependencyCount = 1,
pDependencies = &vk.SubpassDependency {
srcSubpass = vk.SUBPASS_EXTERNAL,
srcStageMask = { .COLOR_ATTACHMENT_OUTPUT },
srcAccessMask = {},
dstSubpass = 0,
dstStageMask = { .COLOR_ATTACHMENT_OUTPUT },
dstAccessMask = { .COLOR_ATTACHMENT_WRITE, },
},
}
render_pass: vk.RenderPass = ---
if res := vk.CreateRenderPass(dev, &render_pass_ci, nil, &render_pass); res != .SUCCESS {
log.fatal("[vk] Unable to create the render pass: %v", res)
os.exit(1)
}
defer vk.DestroyRenderPass(dev, render_pass, nil)
log.infof("[vk] Render pass created!")
log.debugf("[vk] Creating a graphics pipeline")
pipeline_ci := vk.GraphicsPipelineCreateInfo {
sType = .GRAPHICS_PIPELINE_CREATE_INFO,
stageCount = 2,
pStages = raw_data(shader_stages),
pVertexInputState = &vk.PipelineVertexInputStateCreateInfo {
sType = .PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
vertexBindingDescriptionCount = 0,
pVertexBindingDescriptions = nil,
vertexAttributeDescriptionCount = 0,
pVertexAttributeDescriptions = nil,
},
pInputAssemblyState = &vk.PipelineInputAssemblyStateCreateInfo {
sType = .PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
topology = .TRIANGLE_LIST,
primitiveRestartEnable = false,
},
pViewportState = &vk.PipelineViewportStateCreateInfo {
sType = .PIPELINE_VIEWPORT_STATE_CREATE_INFO,
viewportCount = 1,
pViewports = &vk.Viewport {
x = 0,
y = 0,
width = f32(swap_extent.width),
height = f32(swap_extent.height),
minDepth = 0.0,
maxDepth = 1.0,
},
scissorCount = 1,
pScissors = &vk.Rect2D {
offset = {0, 0},
extent = swap_extent,
},
},
pRasterizationState = &vk.PipelineRasterizationStateCreateInfo {
sType = .PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
depthClampEnable = false,
rasterizerDiscardEnable = false,
polygonMode = .FILL,
lineWidth = 1.0,
cullMode = { .BACK },
frontFace = .CLOCKWISE,
depthBiasEnable = false,
depthBiasClamp = 0,
depthBiasConstantFactor = 0,
depthBiasSlopeFactor = 0,
},
pMultisampleState = &vk.PipelineMultisampleStateCreateInfo {
sType = .PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
sampleShadingEnable = false,
rasterizationSamples = { ._1 },
minSampleShading = 1.0,
pSampleMask = nil,
alphaToCoverageEnable = false,
alphaToOneEnable = false,
},
pDepthStencilState = nil,
pColorBlendState = &vk.PipelineColorBlendStateCreateInfo {
sType = .PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
logicOpEnable = false,
logicOp = .COPY,
attachmentCount = 1,
pAttachments = &vk.PipelineColorBlendAttachmentState {
colorWriteMask = { .R, .G, .B, .A },
blendEnable = false,
srcColorBlendFactor = .ONE,
dstColorBlendFactor = .ZERO,
colorBlendOp = .ADD,
srcAlphaBlendFactor = .ONE,
dstAlphaBlendFactor = .ZERO,
alphaBlendOp = .ADD,
},
blendConstants = { 0, 0, 0, 0 },
},
pDynamicState = &vk.PipelineDynamicStateCreateInfo {
sType = .PIPELINE_DYNAMIC_STATE_CREATE_INFO,
dynamicStateCount = 2,
pDynamicStates = raw_data([]vk.DynamicState { .VIEWPORT, .SCISSOR }),
},
layout = pipeline_layout,
renderPass = render_pass,
basePipelineHandle = 0,
basePipelineIndex = -1,
}
pipeline: vk.Pipeline = ---
if res := vk.CreateGraphicsPipelines(dev, 0, 1, &pipeline_ci, nil, &pipeline); res != .SUCCESS {
log.fatalf("[vk] Unable to create a graphics pipeline")
os.exit(1)
}
defer vk.DestroyPipeline(dev, pipeline, nil)
log.info("[vk] Graphics pipeline created!")
log.debugf("[vk] Creating framebuffers.")
framebuffers := make([]vk.Framebuffer, len(image_views))
defer {
for fb in framebuffers {
vk.DestroyFramebuffer(dev, fb, nil)
}
delete(framebuffers)
}
for &image_view, i in image_views {
framebuffer_ci := vk.FramebufferCreateInfo {
sType = .FRAMEBUFFER_CREATE_INFO,
renderPass = render_pass,
attachmentCount = 1,
pAttachments = &image_view,
width = swap_extent.width,
height = swap_extent.height,
layers = 1,
}
if res := vk.CreateFramebuffer(dev, &framebuffer_ci, nil, &framebuffers[i]); res != .SUCCESS {
log.fatalf("[vk] Unable to create framebuffer #%d", i)
os.exit(1)
}
}
log.infof("[vk] Framebuffers created!")
log.debugf("[vk] Creating command pool.")
command_pool: vk.CommandPool = ---
command_pool_ci := vk.CommandPoolCreateInfo {
sType = .COMMAND_POOL_CREATE_INFO,
flags = { .RESET_COMMAND_BUFFER },
queueFamilyIndex = graphics_qf,
}
if res := vk.CreateCommandPool(dev, &command_pool_ci, nil, &command_pool); res != .SUCCESS {
log.fatalf("[vk] Unable to create command pool: %v", res)
os.exit(1)
}
defer vk.DestroyCommandPool(dev, command_pool, nil)
log.infof("[vk] Command pool created!")
log.debugf("[vk] Allocating command buffers.")
command_buffer: vk.CommandBuffer = ---
command_buffer_ci := vk.CommandBufferAllocateInfo {
sType = .COMMAND_BUFFER_ALLOCATE_INFO,
commandPool = command_pool,
level = .PRIMARY,
commandBufferCount = 1,
}
if res := vk.AllocateCommandBuffers(dev, &command_buffer_ci, &command_buffer); res != .SUCCESS {
log.fatalf("[vk] Unable to allocate command buffer: %v", res)
os.exit(1)
}
log.infof("[vk] Command buffer allocated!")
image_semaphore: vk.Semaphore = ---
render_semaphore: vk.Semaphore = ---
in_flight_fence: vk.Fence = ---
semaphore_ci := vk.SemaphoreCreateInfo {
sType = .SEMAPHORE_CREATE_INFO,
}
fence_ci := vk.FenceCreateInfo {
sType = .FENCE_CREATE_INFO,
flags = {.SIGNALED},
}
if res := vk.CreateSemaphore(dev, &semaphore_ci, nil, &image_semaphore); res != .SUCCESS {
log.fatalf("[vk] Unable to create image semaphore: %v", res)
os.exit(1)
}
defer vk.DestroySemaphore(dev, image_semaphore, nil)
if res := vk.CreateSemaphore(dev, &semaphore_ci, nil, &render_semaphore); res != .SUCCESS {
log.fatalf("[vk] Unable to create render semaphore: %v", res)
os.exit(1)
}
defer vk.DestroySemaphore(dev, render_semaphore, nil)
if res := vk.CreateFence(dev, &fence_ci, nil, &in_flight_fence); res != .SUCCESS {
log.fatalf("[vk] Unable to create in-flight fence: %v", res)
os.exit(1)
}
defer vk.DestroyFence(dev, in_flight_fence, nil)
for !glfw.WindowShouldClose(window) {
glfw.PollEvents()
vk.WaitForFences(dev, 1, &in_flight_fence, true, max(u64))
vk.ResetFences(dev, 1, &in_flight_fence)
image_index: u32
vk.AcquireNextImageKHR(dev, swapchain, max(u64), image_semaphore, 0, &image_index)
vk.ResetCommandBuffer(command_buffer, {})
command_buffer_bi := vk.CommandBufferBeginInfo {
sType = .COMMAND_BUFFER_BEGIN_INFO,
flags = {},
pInheritanceInfo = nil,
}
if res := vk.BeginCommandBuffer(command_buffer, &command_buffer_bi); res != .SUCCESS {
log.fatalf("[vk] Unable to begin recording command buffer: %v", res)
os.exit(1)
}
render_pass_bi := vk.RenderPassBeginInfo {
sType = .RENDER_PASS_BEGIN_INFO,
renderPass = render_pass,
framebuffer = framebuffers[image_index],
renderArea = {
offset = {0, 0},
extent = swap_extent,
},
clearValueCount = 1,
pClearValues = &vk.ClearValue {
color = { float32 = { 0, 0, 0, 1 }},
},
}
vk.CmdBeginRenderPass(command_buffer, &render_pass_bi, .INLINE)
vk.CmdBindPipeline(command_buffer, .GRAPHICS, pipeline)
vk.CmdSetViewport(command_buffer, 0, 1, &vk.Viewport {
x = 0,
y = 0,
width = f32(swap_extent.width),
height = f32(swap_extent.height),
minDepth = 0,
maxDepth = 1,
})
vk.CmdSetScissor(command_buffer, 0, 1, &vk.Rect2D {
offset = {0, 0},
extent = swap_extent,
})
vk.CmdDraw(command_buffer, 3, 1, 0, 0)
vk.CmdEndRenderPass(command_buffer)
if res := vk.EndCommandBuffer(command_buffer); res != .SUCCESS {
log.fatalf("[vk] Error recording the command buffer: %v", res)
os.exit(1)
}
submit_info := vk.SubmitInfo {
sType = .SUBMIT_INFO,
waitSemaphoreCount = 1,
pWaitSemaphores = raw_data([]vk.Semaphore { image_semaphore }),
pWaitDstStageMask = raw_data([]vk.PipelineStageFlags {
{ .COLOR_ATTACHMENT_OUTPUT },
}),
commandBufferCount = 1,
pCommandBuffers = &command_buffer,
signalSemaphoreCount = 1,
pSignalSemaphores = &render_semaphore,
}
if res := vk.QueueSubmit(graphics_q, 1, &submit_info, in_flight_fence); res != .SUCCESS {
log.fatalf("[vk] Unable to submit command buffer: %v", res)
os.exit(1)
}
present_info := vk.PresentInfoKHR {
sType = .PRESENT_INFO_KHR,
waitSemaphoreCount = 1,
pWaitSemaphores = &render_semaphore,
swapchainCount = 1,
pSwapchains = &swapchain,
pImageIndices = &image_index,
pResults = nil,
}
vk.QueuePresentKHR(present_q, &present_info)
}
vk.DeviceWaitIdle(dev)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment