Created
November 4, 2024 00:33
-
-
Save flysand7/9583abdb45176ed07596accdd911827c to your computer and use it in GitHub Desktop.
One file example of vulkan triangle
This file contains hidden or 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
#version 450 | |
layout(location = 0) in vec3 fragColor; | |
layout(location = 0) out vec4 outColor; | |
void main() { | |
outColor = vec4(fragColor, 1.0); | |
} |
This file contains hidden or 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
#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]; | |
} |
This file contains hidden or 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
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