Skip to content

Instantly share code, notes, and snippets.

@miaoles
Last active October 28, 2024 12:16
Show Gist options
  • Save miaoles/a88995378271c2ec8ec1d9a87221d7d6 to your computer and use it in GitHub Desktop.
Save miaoles/a88995378271c2ec8ec1d9a87221d7d6 to your computer and use it in GitHub Desktop.
vulkan-tutorial.com Triangle with Odin
// vulkan-tutorial.com Triangle with Odin
// also has personal tweaks/additions, explicit naming, entire enum names
package main
import fmt "core:fmt"
import math "core:math"
import os "core:os"
import str "core:strings"
import glfw "vendor:glfw"
import vk "vendor:vulkan"
when ODIN_DEBUG == true {
ENABLE_VALIDATION_LAYERS :: true
} else {
ENABLE_VALIDATION_LAYERS :: false
}
WIDTH :: 1280
HEIGHT :: 720
TITLE :: "Vulkan Engine"
VALIDATION_LAYERS := []cstring{"VK_LAYER_KHRONOS_validation"}
DEVICE_EXTENSIONS := []cstring{"VK_KHR_swapchain"}
Context :: struct {
instance: vk.Instance,
window: glfw.WindowHandle,
debug_messenger: vk.DebugUtilsMessengerEXT,
physical_device: vk.PhysicalDevice,
logical_device: vk.Device,
graphics_queue: vk.Queue,
surface: vk.SurfaceKHR,
present_queue: vk.Queue,
swap_chain: vk.SwapchainKHR,
swap_chain_images: [dynamic]vk.Image,
swap_chain_image_format: vk.Format,
swap_chain_extent: vk.Extent2D,
swap_chain_image_views: [dynamic]vk.ImageView,
render_pass: vk.RenderPass,
pipeline_layout: vk.PipelineLayout,
graphics_pipeline: vk.Pipeline,
swap_chain_framebuffers: [dynamic]vk.Framebuffer,
command_pool: vk.CommandPool,
command_buffer: vk.CommandBuffer,
image_available_semaphore: vk.Semaphore,
render_finished_sempahore: vk.Semaphore,
in_flight_fence: vk.Fence,
}
Queue_Family_Flags :: enum {
Graphics_Family,
Present_Family,
}
Queue_Family_Indices :: struct {
graphics_family: Maybe(u32),
present_family: Maybe(u32),
}
Swap_Chain_Support_Details :: struct {
capabilities: vk.SurfaceCapabilitiesKHR,
formats: [dynamic]vk.SurfaceFormatKHR,
present_modes: [dynamic]vk.PresentModeKHR,
}
main :: proc() {
using ctx: Context
if !run_app(&ctx) do return
return
}
run_app :: proc(using ctx: ^Context) -> b32 {
fmt.println("init_window")
init_window(ctx)
fmt.println("init_vulkan")
init_vulkan(ctx)
fmt.println("main_loop")
main_loop(ctx)
fmt.println("end_app")
end_app(ctx)
return true
}
end_app :: proc(using ctx: ^Context) {
fmt.println("term_vulkan")
term_vulkan(ctx)
fmt.println("term_window")
term_window(ctx)
}
init_window :: proc(using ctx: ^Context) {
fmt.println("glfw.Init")
glfw.Init()
glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API)
glfw.WindowHint(glfw.RESIZABLE, glfw.FALSE)
fmt.println("glfw.CreateWindow")
window = glfw.CreateWindow(WIDTH, HEIGHT, TITLE, nil, nil)
}
term_window :: proc(using ctx: ^Context) {
fmt.println("glfw.DestroyWindow")
glfw.DestroyWindow(window)
fmt.println("glfw.Terminate")
glfw.Terminate()
}
init_vulkan :: proc(using ctx: ^Context) {
fmt.println("create_instance")
create_instance(ctx)
fmt.println("create_debug_messenger")
setup_debug_messenger(ctx)
fmt.println("create_surface")
create_surface(ctx)
fmt.println("choose_physical_device")
choose_physical_device(ctx)
fmt.println("create_logical_device")
create_logical_device(ctx)
fmt.println("create_swap_chain")
create_swap_chain(ctx)
fmt.println("create_image_views")
create_image_views(ctx)
fmt.println("create_render_pass")
create_render_pass(ctx)
fmt.println("create_graphics_pipeline")
create_graphics_pipeline(ctx)
fmt.println("create_framebuffers")
create_framebuffers(ctx)
fmt.println("create_command_pool")
create_command_pool(ctx)
fmt.println("create_command_buffer")
create_command_buffer(ctx)
fmt.println("create_sync_objects")
create_sync_objects(ctx)
}
term_vulkan :: proc(using ctx: ^Context) {
fmt.println("vk.DestroySemaphore(s)")
vk.DestroySemaphore(logical_device, image_available_semaphore, nil)
vk.DestroySemaphore(logical_device, render_finished_sempahore, nil)
fmt.println("vk.Fence(s)")
vk.DestroyFence(logical_device, in_flight_fence, nil)
fmt.println("vk.DestroyCommandPool")
vk.DestroyCommandPool(logical_device, command_pool, nil)
fmt.println("vk.DestroyFramebuffer(s)")
for framebuffer in swap_chain_framebuffers {
vk.DestroyFramebuffer(logical_device, framebuffer, nil)
}
fmt.println("vk.DestroyPipeline")
vk.DestroyPipeline(logical_device, graphics_pipeline, nil)
fmt.println("vk.DestroyPipelineLayout")
vk.DestroyPipelineLayout(logical_device, pipeline_layout, nil)
fmt.println("vk.DestroyRenderPass")
vk.DestroyRenderPass(logical_device, render_pass, nil)
fmt.println("vk.DestroyImageView(s)")
for image_view in swap_chain_image_views {
vk.DestroyImageView(logical_device, image_view, nil)
}
fmt.println("vk.DestroySwapchainKHR")
vk.DestroySwapchainKHR(logical_device, swap_chain, nil)
fmt.println("vk.DestroyDevice")
vk.DestroyDevice(logical_device, nil)
fmt.println("vk.DestroySurfaceKHR")
vk.DestroySurfaceKHR(instance, surface, nil)
if ENABLE_VALIDATION_LAYERS {
fmt.println("destroy_debug_utils_messenger")
destroy_debug_utils_messenger(instance, debug_messenger, nil)
}
fmt.println("vk.DestroyInstance")
vk.DestroyInstance(instance, nil)
}
main_loop :: proc(using ctx: ^Context) {
for (!glfw.WindowShouldClose(window)) {
glfw.PollEvents()
draw_frame(ctx)
}
vk.DeviceWaitIdle(logical_device)
}
create_instance :: proc(using ctx: ^Context) {
vk.load_proc_addresses(rawptr(glfw.GetInstanceProcAddress))
if ENABLE_VALIDATION_LAYERS {
if check_validation_layer_support() {
fmt.println("Validation layers requested and found.")
} else {
fmt.eprintln("Validation layers requested and not found.")
}
}
app_info: vk.ApplicationInfo
app_info.sType = vk.StructureType.APPLICATION_INFO
app_info.pApplicationName = "Hellope Triangle"
app_info.applicationVersion = vk.MAKE_VERSION(1, 0, 0)
app_info.pEngineName = TITLE
app_info.engineVersion = vk.MAKE_VERSION(1, 0, 0)
app_info.apiVersion = vk.API_VERSION_1_0
info: vk.InstanceCreateInfo
info.sType = vk.StructureType.INSTANCE_CREATE_INFO
info.pApplicationInfo = &app_info
extensions := get_required_extensions()
info.enabledExtensionCount = u32(len(extensions))
info.ppEnabledExtensionNames = raw_data(extensions)
debug_info: vk.DebugUtilsMessengerCreateInfoEXT
if (ENABLE_VALIDATION_LAYERS) {
info.enabledLayerCount = u32(len(VALIDATION_LAYERS))
info.ppEnabledLayerNames = raw_data(VALIDATION_LAYERS)
populate_debug_messenger_info(&debug_info)
info.pNext = cast(^vk.DebugUtilsMessengerCreateInfoEXT)&debug_info
} else {
info.enabledLayerCount = 0
info.pNext = nil
}
result: vk.Result = vk.CreateInstance(&info, nil, &instance)
if result != vk.Result.SUCCESS {
fmt.eprintln("Failed to create Vulkan instance.")
} else {
fmt.println("Successfully created Vulkan instance.")
}
vk.load_proc_addresses_instance(instance)
}
debug_callback :: proc(
message_severity: vk.DebugUtilsMessageSeverityFlagEXT,
message_type: vk.DebugUtilsMessageTypeFlagEXT,
p_callback_data: ^vk.DebugUtilsMessengerCallbackDataEXT,
p_user_data: rawptr,
) -> b32 {
fmt.eprintln("Validation Layer: ", p_callback_data.pMessage)
return false
}
check_validation_layer_support :: proc() -> b32 {
layer_count: u32
vk.EnumerateInstanceLayerProperties(&layer_count, nil)
available_layers := make([]vk.LayerProperties, layer_count)
vk.EnumerateInstanceLayerProperties(&layer_count, raw_data(available_layers))
wanted_layer_found: b32 = false
for wanted_layer_cstring in VALIDATION_LAYERS {
for available_layer_properties in available_layers {
available_layer_string := convert_bytes_to_string(available_layer_properties.layerName)
if str.compare(string(wanted_layer_cstring), available_layer_string) == 0 {
wanted_layer_found = true
break
}
}
}
return wanted_layer_found
}
convert_bytes_to_string :: proc(bytes: [256]u8) -> string {
bytes_clone := bytes
builder := str.clone_from_bytes(bytes_clone[:])
cstring := str.clone_to_cstring(builder)
return string(cstring)
}
get_required_extensions :: proc() -> []cstring {
glfw_extensions := glfw.GetRequiredInstanceExtensions()
extensions := make([dynamic]string, len(glfw_extensions))
for index in 0 ..< len(glfw_extensions) {
extensions[index] = string(glfw_extensions[index])
}
if (ENABLE_VALIDATION_LAYERS) {
append(&extensions, string(vk.EXT_DEBUG_UTILS_EXTENSION_NAME))
}
result := make([]cstring, len(extensions))
for index in 0 ..< len(extensions) {
result[index] = str.clone_to_cstring(extensions[index])
}
fmt.println("Extensions Result: ", result)
return result
}
setup_debug_messenger :: proc(using ctx: ^Context) {
if !ENABLE_VALIDATION_LAYERS do return
info: vk.DebugUtilsMessengerCreateInfoEXT
populate_debug_messenger_info(&info)
if (create_debug_utils_messenger(instance, &info, nil, &debug_messenger) != vk.Result.SUCCESS) {
fmt.eprintln("Failed to set up debug messenger.")
}
info.pUserData = nil
}
populate_debug_messenger_info :: proc(info: ^vk.DebugUtilsMessengerCreateInfoEXT) {
info.sType = vk.StructureType.DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
info.messageSeverity = vk.DebugUtilsMessageSeverityFlagsEXT{.VERBOSE, .WARNING, .ERROR}
info.messageType = vk.DebugUtilsMessageTypeFlagsEXT{.GENERAL, .VALIDATION, .PERFORMANCE}
info.pfnUserCallback = vk.ProcDebugUtilsMessengerCallbackEXT(debug_callback)
}
create_debug_utils_messenger :: proc(
instance: vk.Instance,
p_info: ^vk.DebugUtilsMessengerCreateInfoEXT,
p_allocator: ^vk.AllocationCallbacks,
p_debug_messenger: ^vk.DebugUtilsMessengerEXT,
) -> vk.Result {
procedure := auto_cast vk.ProcCreateDebugUtilsMessengerEXT(vk.GetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"))
if procedure != nil {
return procedure(instance, p_info, p_allocator, p_debug_messenger)
} else {
return vk.Result.ERROR_EXTENSION_NOT_PRESENT
}
}
destroy_debug_utils_messenger :: proc(instance: vk.Instance, debug_messenger: vk.DebugUtilsMessengerEXT, p_allocator: ^vk.AllocationCallbacks) {
procedure := auto_cast vk.ProcDestroyDebugUtilsMessengerEXT(vk.GetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"))
if procedure != nil {
procedure(instance, debug_messenger, p_allocator)
}
}
create_surface :: proc(using ctx: ^Context) {
if (glfw.CreateWindowSurface(instance, window, nil, &surface)) != .SUCCESS {
fmt.eprintln("Failed to create window surface.")
}
}
choose_physical_device :: proc(using ctx: ^Context) {
device_count: u32 = 0
vk.EnumeratePhysicalDevices(instance, &device_count, nil)
if device_count == 0 {
fmt.eprintln("Failed to find GPUs with Vulkan support.")
}
physical_devices := make([]vk.PhysicalDevice, device_count)
vk.EnumeratePhysicalDevices(instance, &device_count, raw_data(physical_devices))
for device in physical_devices {
if is_device_suitable(ctx, device) {
physical_device = device
break
}
}
if physical_device == nil {
fmt.eprintln("Failed to find suitable GPU.")
}
device_properties: vk.PhysicalDeviceProperties
vk.GetPhysicalDeviceProperties(physical_device, &device_properties)
device_features: vk.PhysicalDeviceFeatures
vk.GetPhysicalDeviceFeatures(physical_device, &device_features)
}
create_logical_device :: proc(using ctx: ^Context) {
indices: Queue_Family_Indices = find_queue_families(ctx, physical_device)
queue_infos := make([dynamic]vk.DeviceQueueCreateInfo)
queue_families_unique := bit_set[Queue_Family_Flags]{.Graphics_Family, .Present_Family}
queue_priority: f32 = 1.0
for queue_family in queue_families_unique {
queue_info: vk.DeviceQueueCreateInfo
queue_info.sType = vk.StructureType.DEVICE_QUEUE_CREATE_INFO
queue_info.queueFamilyIndex = u32(queue_family)
queue_info.queueCount = 1
queue_info.pQueuePriorities = &queue_priority
append(&queue_infos, queue_info)
}
device_features: vk.PhysicalDeviceFeatures
info: vk.DeviceCreateInfo
info.sType = vk.StructureType.DEVICE_CREATE_INFO
info.queueCreateInfoCount = u32(len(queue_infos))
info.pQueueCreateInfos = raw_data(queue_infos)
info.pEnabledFeatures = &device_features
info.enabledExtensionCount = u32(len(DEVICE_EXTENSIONS))
info.ppEnabledExtensionNames = raw_data(DEVICE_EXTENSIONS)
if ENABLE_VALIDATION_LAYERS {
info.enabledLayerCount = u32(len(VALIDATION_LAYERS))
info.ppEnabledLayerNames = raw_data(VALIDATION_LAYERS)
} else {
info.enabledLayerCount = 0
}
if vk.CreateDevice(physical_device, &info, nil, &logical_device) != .SUCCESS {
fmt.eprintln("Failed to create logical device.")
}
vk.GetDeviceQueue(logical_device, indices.graphics_family.(u32), 0, &graphics_queue)
vk.GetDeviceQueue(logical_device, indices.present_family.(u32), 0, &present_queue)
}
is_device_suitable :: proc(using ctx: ^Context, device: vk.PhysicalDevice) -> b32 {
indices: Queue_Family_Indices = find_queue_families(ctx, device)
extensions_supported := check_device_extension_support(device)
swap_chain_adequate: b32 = false
if extensions_supported {
swap_chain_support := query_swap_chain_support(ctx, device)
swap_chain_adequate = !(len(swap_chain_support.formats) == 0) && !(len(swap_chain_support.present_modes) == 0)
}
return (indices.graphics_family != nil) && extensions_supported && swap_chain_adequate
}
find_queue_families :: proc(using ctx: ^Context, device: vk.PhysicalDevice) -> Queue_Family_Indices {
indices: Queue_Family_Indices
queue_family_count: u32 = 0
vk.GetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nil)
queue_families := make([]vk.QueueFamilyProperties, queue_family_count)
queue_families_raw := raw_data(queue_families)
vk.GetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families_raw)
i: int = 0
for queue_family in queue_families {
if vk.QueueFlag.GRAPHICS in queue_family.queueFlags {
indices.graphics_family = u32(i)
}
present_support: b32 = false
vk.GetPhysicalDeviceSurfaceSupportKHR(device, u32(i), surface, &present_support)
if present_support do indices.present_family = u32(i)
if indices.graphics_family != nil && present_support {
break
}
i += 1
}
return indices
}
check_device_extension_support :: proc(device: vk.PhysicalDevice) -> b32 {
extension_count: u32
vk.EnumerateDeviceExtensionProperties(device, nil, &extension_count, nil)
available_extensions := make([]vk.ExtensionProperties, extension_count)
vk.EnumerateDeviceExtensionProperties(device, nil, &extension_count, raw_data(available_extensions))
required_extensions := make(map[string]b32, len(DEVICE_EXTENSIONS))
for extension in DEVICE_EXTENSIONS {
fmt.println("Required Extension: ", extension)
required_extensions[string(extension)] = true
}
for extension in available_extensions {
delete_key(&required_extensions, convert_bytes_to_string(extension.extensionName))
}
return len(required_extensions) == 0
}
create_swap_chain :: proc(using ctx: ^Context) {
swap_chain_support: Swap_Chain_Support_Details = query_swap_chain_support(ctx, physical_device)
surface_format: vk.SurfaceFormatKHR = choose_swap_surface_format(swap_chain_support.formats[:])
present_mode: vk.PresentModeKHR = choose_swap_present_mode(swap_chain_support.present_modes[:])
extent: vk.Extent2D = choose_swap_extent(ctx, swap_chain_support.capabilities)
image_count: u32 = swap_chain_support.capabilities.minImageCount + 1
if (swap_chain_support.capabilities.maxImageCount > 0) && (image_count > swap_chain_support.capabilities.maxImageCount) {
image_count = swap_chain_support.capabilities.maxImageCount
}
info: vk.SwapchainCreateInfoKHR
info.sType = vk.StructureType.SWAPCHAIN_CREATE_INFO_KHR
info.surface = surface
info.minImageCount = image_count
info.imageFormat = surface_format.format
info.imageColorSpace = surface_format.colorSpace
info.imageExtent = extent
info.imageArrayLayers = 1
info.imageUsage = {vk.ImageUsageFlag.COLOR_ATTACHMENT}
indices: Queue_Family_Indices = find_queue_families(ctx, physical_device)
queue_family_indices: []u32 = {indices.graphics_family.(u32), indices.present_family.(u32)}
if indices.graphics_family != indices.present_family {
info.imageSharingMode = vk.SharingMode.CONCURRENT
info.queueFamilyIndexCount = 2
info.pQueueFamilyIndices = raw_data(queue_family_indices)
} else {
info.imageSharingMode = vk.SharingMode.EXCLUSIVE
info.queueFamilyIndexCount = 0
info.pQueueFamilyIndices = nil
}
info.preTransform = swap_chain_support.capabilities.currentTransform
info.compositeAlpha = {vk.CompositeAlphaFlagsKHR.OPAQUE}
info.presentMode = present_mode
info.clipped = true
info.oldSwapchain = {}
if vk.CreateSwapchainKHR(logical_device, &info, nil, &swap_chain) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create swap chain.")
}
vk.GetSwapchainImagesKHR(logical_device, swap_chain, &image_count, nil)
resize(&swap_chain_images, int(image_count))
vk.GetSwapchainImagesKHR(logical_device, swap_chain, &image_count, raw_data(swap_chain_images))
swap_chain_image_format = surface_format.format
swap_chain_extent = extent
}
query_swap_chain_support :: proc(using ctx: ^Context, device: vk.PhysicalDevice) -> Swap_Chain_Support_Details {
details: Swap_Chain_Support_Details
vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities)
format_count: u32
vk.GetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, nil)
if format_count != 0 {
resize(&details.formats, int(format_count))
vk.GetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, raw_data(details.formats))
}
present_mode_count: u32
vk.GetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_mode_count, nil)
if present_mode_count != 0 {
resize(&details.present_modes, int(present_mode_count))
vk.GetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_mode_count, raw_data(details.present_modes))
}
return details
}
choose_swap_surface_format :: proc(available_formats: []vk.SurfaceFormatKHR) -> vk.SurfaceFormatKHR {
for available_format in available_formats {
if available_format.format == vk.Format.B8G8R8A8_SRGB &&
available_format.colorSpace == (vk.ColorSpaceKHR.COLORSPACE_SRGB_NONLINEAR | vk.ColorSpaceKHR.SRGB_NONLINEAR) {
return available_format
}
}
return available_formats[0]
}
choose_swap_present_mode :: proc(available_present_modes: []vk.PresentModeKHR) -> vk.PresentModeKHR {
for available_present_mode in available_present_modes {
if available_present_mode == vk.PresentModeKHR.MAILBOX {
return available_present_mode
}
}
return vk.PresentModeKHR.FIFO
}
choose_swap_extent :: proc(using ctx: ^Context, capabilities: vk.SurfaceCapabilitiesKHR) -> vk.Extent2D {
if (capabilities.currentExtent.width) != math.max(u32) {
return capabilities.currentExtent
} else {
framebuffer_width, framebuffer_height := glfw.GetFramebufferSize(window)
actual_extent: vk.Extent2D = {u32(framebuffer_width), u32(framebuffer_height)}
actual_extent.width = math.clamp(actual_extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width)
actual_extent.height = math.clamp(actual_extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height)
return actual_extent
}
}
create_image_views :: proc(using ctx: ^Context) {
resize(&swap_chain_image_views, len(swap_chain_images))
for index in 0 ..< len(swap_chain_images) {
info: vk.ImageViewCreateInfo
info.sType = vk.StructureType.IMAGE_VIEW_CREATE_INFO
info.image = swap_chain_images[index]
info.viewType = vk.ImageViewType.D2
info.format = swap_chain_image_format
info.components.r = vk.ComponentSwizzle.IDENTITY
info.components.g = vk.ComponentSwizzle.IDENTITY
info.components.b = vk.ComponentSwizzle.IDENTITY
info.components.a = vk.ComponentSwizzle.IDENTITY
info.subresourceRange.aspectMask = {vk.ImageAspectFlag.COLOR}
info.subresourceRange.baseMipLevel = 0
info.subresourceRange.levelCount = 1
info.subresourceRange.baseArrayLayer = 0
info.subresourceRange.layerCount = 1
if (vk.CreateImageView(logical_device, &info, nil, &swap_chain_image_views[index])) != vk.Result.SUCCESS {
fmt.println("Failed to create image views.")
}
}
}
create_graphics_pipeline :: proc(using ctx: ^Context) {
vertex_shader_code, vertex_shader_code_success := read_file_bytes("vert.spv")
if vertex_shader_code_success {
fmt.println("Successfully read vertex shader code.")
} else {
fmt.eprintln("Failed to read vertex shader code.")
}
fragment_shader_code, fragment_shader_code_success := read_file_bytes("frag.spv")
if fragment_shader_code_success {
fmt.println("Successfully read fragment shader code.")
} else {
fmt.eprintln("Failed to read fragment shader code.")
}
vertex_shader_module, vertex_shader_module_success := create_shader_module(ctx, vertex_shader_code)
defer vk.DestroyShaderModule(logical_device, vertex_shader_module, nil)
fragment_shader_module, fragment_shader_module_success := create_shader_module(ctx, fragment_shader_code)
defer vk.DestroyShaderModule(logical_device, fragment_shader_module, nil)
vertex_shader_stage_info: vk.PipelineShaderStageCreateInfo
vertex_shader_stage_info.sType = vk.StructureType.PIPELINE_SHADER_STAGE_CREATE_INFO
vertex_shader_stage_info.stage = {vk.ShaderStageFlag.VERTEX}
vertex_shader_stage_info.module = vertex_shader_module
vertex_shader_stage_info.pName = "main"
vertex_shader_stage_info.pSpecializationInfo = nil
fragment_shader_stage_info: vk.PipelineShaderStageCreateInfo
fragment_shader_stage_info.sType = vk.StructureType.PIPELINE_SHADER_STAGE_CREATE_INFO
fragment_shader_stage_info.stage = {vk.ShaderStageFlag.FRAGMENT}
fragment_shader_stage_info.module = fragment_shader_module
fragment_shader_stage_info.pName = "main"
fragment_shader_stage_info.pSpecializationInfo = nil
shader_stages: []vk.PipelineShaderStageCreateInfo = {vertex_shader_stage_info, fragment_shader_stage_info}
vertex_input_info: vk.PipelineVertexInputStateCreateInfo
vertex_input_info.sType = vk.StructureType.PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
vertex_input_info.vertexBindingDescriptionCount = 0
vertex_input_info.pVertexBindingDescriptions = nil
vertex_input_info.vertexAttributeDescriptionCount = 0
vertex_input_info.pVertexAttributeDescriptions = nil
input_assembly_info: vk.PipelineInputAssemblyStateCreateInfo
input_assembly_info.sType = vk.StructureType.PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO
input_assembly_info.topology = vk.PrimitiveTopology.TRIANGLE_LIST
input_assembly_info.primitiveRestartEnable = false // vk.FALSE
viewport: vk.Viewport = {}
viewport.x = 0.0
viewport.y = 0.0
viewport.width = f32(swap_chain_extent.width)
viewport.height = f32(swap_chain_extent.height)
viewport.minDepth = 0.0
viewport.maxDepth = 1.0
scissor: vk.Rect2D
scissor.offset = {0, 0}
scissor.extent = swap_chain_extent
dynamic_states: []vk.DynamicState = {vk.DynamicState.VIEWPORT, vk.DynamicState.SCISSOR}
dynamic_state_info: vk.PipelineDynamicStateCreateInfo
dynamic_state_info.sType = vk.StructureType.PIPELINE_DYNAMIC_STATE_CREATE_INFO
dynamic_state_info.dynamicStateCount = u32(len(dynamic_states))
dynamic_state_info.pDynamicStates = raw_data(dynamic_states)
viewport_state_info: vk.PipelineViewportStateCreateInfo
viewport_state_info.sType = vk.StructureType.PIPELINE_VIEWPORT_STATE_CREATE_INFO
viewport_state_info.viewportCount = 1
//viewport_state_info.pViewports = &viewport
viewport_state_info.scissorCount = 1
//viewport_state_info.pScissors = &scissor
rasterizer_info: vk.PipelineRasterizationStateCreateInfo
rasterizer_info.sType = vk.StructureType.PIPELINE_RASTERIZATION_STATE_CREATE_INFO
rasterizer_info.depthClampEnable = false // vk.FALSE
rasterizer_info.rasterizerDiscardEnable = false // vk.FALSE
rasterizer_info.polygonMode = vk.PolygonMode.FILL
rasterizer_info.lineWidth = 1.0
rasterizer_info.cullMode = {vk.CullModeFlag.BACK}
rasterizer_info.frontFace = vk.FrontFace.CLOCKWISE
rasterizer_info.depthBiasEnable = false // vk.FALSE
rasterizer_info.depthBiasConstantFactor = 0.0
rasterizer_info.depthBiasClamp = 0.0
rasterizer_info.depthBiasSlopeFactor = 0.0
multisampling_info: vk.PipelineMultisampleStateCreateInfo
multisampling_info.sType = vk.StructureType.PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
multisampling_info.sampleShadingEnable = false // vk.FALSE
multisampling_info.rasterizationSamples = {vk.SampleCountFlag._1}
multisampling_info.minSampleShading = 1.0
multisampling_info.pSampleMask = nil
multisampling_info.alphaToCoverageEnable = false // vk.FALSE
multisampling_info.alphaToOneEnable = false // vk.FALSE
color_blend_attachment_state: vk.PipelineColorBlendAttachmentState
color_blend_attachment_state.colorWriteMask = {vk.ColorComponentFlag.R, vk.ColorComponentFlag.G, vk.ColorComponentFlag.B, vk.ColorComponentFlag.A}
//color_blend_attachment_state.blendEnable = false // vk.FALSE
//color_blend_attachment_state.srcColorBlendFactor = vk.BlendFactor.ONE
//color_blend_attachment_state.dstColorBlendFactor = vk.BlendFactor.ZERO
color_blend_attachment_state.blendEnable = true // vk.TRUE
color_blend_attachment_state.srcColorBlendFactor = vk.BlendFactor.SRC_ALPHA
color_blend_attachment_state.dstColorBlendFactor = vk.BlendFactor.ONE_MINUS_SRC_ALPHA
color_blend_attachment_state.colorBlendOp = vk.BlendOp.ADD
color_blend_attachment_state.srcAlphaBlendFactor = vk.BlendFactor.ONE
color_blend_attachment_state.dstAlphaBlendFactor = vk.BlendFactor.ZERO
color_blend_attachment_state.alphaBlendOp = vk.BlendOp.ADD
color_blend_state_info: vk.PipelineColorBlendStateCreateInfo
color_blend_state_info.sType = vk.StructureType.PIPELINE_COLOR_BLEND_STATE_CREATE_INFO
color_blend_state_info.logicOpEnable = false // vk.FALSE
color_blend_state_info.logicOp = vk.LogicOp.COPY
color_blend_state_info.attachmentCount = 1
color_blend_state_info.pAttachments = &color_blend_attachment_state
color_blend_state_info.blendConstants[0] = 0.0
color_blend_state_info.blendConstants[1] = 0.0
color_blend_state_info.blendConstants[2] = 0.0
color_blend_state_info.blendConstants[3] = 0.0
pipeline_layout_info: vk.PipelineLayoutCreateInfo
pipeline_layout_info.sType = vk.StructureType.PIPELINE_LAYOUT_CREATE_INFO
pipeline_layout_info.setLayoutCount = 0
pipeline_layout_info.pSetLayouts = nil
pipeline_layout_info.pushConstantRangeCount = 0
pipeline_layout_info.pPushConstantRanges = nil
if vk.CreatePipelineLayout(logical_device, &pipeline_layout_info, nil, &pipeline_layout) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create pipeline layout.")
}
pipeline_info: vk.GraphicsPipelineCreateInfo
pipeline_info.sType = vk.StructureType.GRAPHICS_PIPELINE_CREATE_INFO
pipeline_info.stageCount = 2
pipeline_info.pStages = raw_data(shader_stages)
pipeline_info.pVertexInputState = &vertex_input_info
pipeline_info.pInputAssemblyState = &input_assembly_info
pipeline_info.pViewportState = &viewport_state_info
pipeline_info.pRasterizationState = &rasterizer_info
pipeline_info.pMultisampleState = &multisampling_info
pipeline_info.pDepthStencilState = nil
pipeline_info.pColorBlendState = &color_blend_state_info
pipeline_info.pDynamicState = &dynamic_state_info
pipeline_info.layout = pipeline_layout
pipeline_info.renderPass = render_pass
pipeline_info.subpass = 0
pipeline_info.basePipelineHandle = {}
pipeline_info.basePipelineIndex = -1
if vk.CreateGraphicsPipelines(logical_device, {}, 1, &pipeline_info, nil, &graphics_pipeline) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create graphics pipeline.")
}
}
read_file_bytes :: proc(file_name: string) -> ([]byte, bool) {
file, file_error := os.open(file_name)
if file_error != os.ERROR_NONE {
fmt.eprintln("Failed to open file.")
return nil, false
}
defer os.close(file)
file_size, _ := os.file_size(file)
buffer := make([]byte, file_size)
os.seek(file, 0, os.SEEK_SET)
bytes_read, bytes_read_error := os.read(file, buffer)
if (bytes_read_error != os.ERROR_NONE) || (bytes_read != int(file_size)) {
fmt.eprintln("Failed to read file.")
return nil, false
}
return buffer, true
}
create_shader_module :: proc(using ctx: ^Context, code: []byte) -> (vk.ShaderModule, bool) {
info: vk.ShaderModuleCreateInfo
info.sType = vk.StructureType.SHADER_MODULE_CREATE_INFO
info.codeSize = len(code)
info.pCode = auto_cast raw_data(code)
shader_module: vk.ShaderModule
if (vk.CreateShaderModule(logical_device, &info, nil, &shader_module) != vk.Result.SUCCESS) {
fmt.eprintln("Failed to create shader module.")
return shader_module, false
}
return shader_module, true
}
create_render_pass :: proc(using ctx: ^Context) {
color_attachment_desc: vk.AttachmentDescription
color_attachment_desc.format = swap_chain_image_format
color_attachment_desc.samples = {vk.SampleCountFlag._1}
color_attachment_desc.loadOp = vk.AttachmentLoadOp.CLEAR
color_attachment_desc.storeOp = vk.AttachmentStoreOp.STORE
color_attachment_desc.stencilLoadOp = vk.AttachmentLoadOp.DONT_CARE
color_attachment_desc.stencilStoreOp = vk.AttachmentStoreOp.DONT_CARE
color_attachment_desc.initialLayout = vk.ImageLayout.UNDEFINED
color_attachment_desc.finalLayout = vk.ImageLayout.PRESENT_SRC_KHR
color_attachment_ref: vk.AttachmentReference
color_attachment_ref.attachment = 0
color_attachment_ref.layout = vk.ImageLayout.COLOR_ATTACHMENT_OPTIMAL
subpass_desc: vk.SubpassDescription
subpass_desc.pipelineBindPoint = vk.PipelineBindPoint.GRAPHICS
subpass_desc.colorAttachmentCount = 1
subpass_desc.pColorAttachments = &color_attachment_ref
subpass_dependency: vk.SubpassDependency
subpass_dependency.srcSubpass = vk.SUBPASS_EXTERNAL
subpass_dependency.dstSubpass = 0
subpass_dependency.srcStageMask = {vk.PipelineStageFlag.COLOR_ATTACHMENT_OUTPUT}
subpass_dependency.srcAccessMask = {}
subpass_dependency.dstStageMask = {vk.PipelineStageFlag.COLOR_ATTACHMENT_OUTPUT}
subpass_dependency.dstAccessMask = {vk.AccessFlag.COLOR_ATTACHMENT_WRITE}
render_pass_info: vk.RenderPassCreateInfo
render_pass_info.sType = vk.StructureType.RENDER_PASS_CREATE_INFO
render_pass_info.attachmentCount = 1
render_pass_info.pAttachments = &color_attachment_desc
render_pass_info.subpassCount = 1
render_pass_info.pSubpasses = &subpass_desc
render_pass_info.dependencyCount = 1
render_pass_info.pDependencies = &subpass_dependency
if vk.CreateRenderPass(logical_device, &render_pass_info, nil, &render_pass) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create render pass.")
}
}
create_framebuffers :: proc(using ctx: ^Context) {
resize(&swap_chain_framebuffers, len(swap_chain_image_views))
for index in 0 ..< len(swap_chain_image_views) {
attachments: vk.ImageView = swap_chain_image_views[index]
framebuffer_info: vk.FramebufferCreateInfo
framebuffer_info.sType = vk.StructureType.FRAMEBUFFER_CREATE_INFO
framebuffer_info.renderPass = render_pass
framebuffer_info.attachmentCount = 1
framebuffer_info.pAttachments = &attachments
framebuffer_info.width = swap_chain_extent.width
framebuffer_info.height = swap_chain_extent.height
framebuffer_info.layers = 1
if vk.CreateFramebuffer(logical_device, &framebuffer_info, nil, &swap_chain_framebuffers[index]) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create framebuffer #", index, ".")
}
}
}
create_command_pool :: proc(using ctx: ^Context) {
queue_family_indices: Queue_Family_Indices = find_queue_families(ctx, physical_device)
command_pool_info: vk.CommandPoolCreateInfo
command_pool_info.sType = vk.StructureType.COMMAND_POOL_CREATE_INFO
command_pool_info.flags = {vk.CommandPoolCreateFlag.RESET_COMMAND_BUFFER}
command_pool_info.queueFamilyIndex = queue_family_indices.graphics_family.(u32)
if vk.CreateCommandPool(logical_device, &command_pool_info, nil, &command_pool) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create command pool.")
}
}
create_command_buffer :: proc(using ctx: ^Context) {
commannd_buffer_allocate_info: vk.CommandBufferAllocateInfo
commannd_buffer_allocate_info.sType = vk.StructureType.COMMAND_BUFFER_ALLOCATE_INFO
commannd_buffer_allocate_info.commandPool = command_pool
commannd_buffer_allocate_info.level = vk.CommandBufferLevel.PRIMARY
commannd_buffer_allocate_info.commandBufferCount = 1
if vk.AllocateCommandBuffers(logical_device, &commannd_buffer_allocate_info, &command_buffer) != vk.Result.SUCCESS {
fmt.eprintln("Failed to allocated command buffers.")
}
}
record_command_buffer :: proc(using ctx: ^Context, image_index: u32) {
command_buffer_begin_info: vk.CommandBufferBeginInfo
command_buffer_begin_info.sType = vk.StructureType.COMMAND_BUFFER_BEGIN_INFO
command_buffer_begin_info.flags = {}
command_buffer_begin_info.pInheritanceInfo = nil
if vk.BeginCommandBuffer(command_buffer, &command_buffer_begin_info) != vk.Result.SUCCESS {
fmt.eprintln("Failed to begin recording command buffer.")
}
render_pass_begin_info: vk.RenderPassBeginInfo
render_pass_begin_info.sType = vk.StructureType.RENDER_PASS_BEGIN_INFO
render_pass_begin_info.renderPass = render_pass
render_pass_begin_info.framebuffer = swap_chain_framebuffers[image_index]
render_pass_begin_info.renderArea.offset = {0, 0}
render_pass_begin_info.renderArea.extent = swap_chain_extent
clear_color: vk.ClearValue = {
color = {float32 = {0.0, 0.0, 0.0, 1.0}},
}
render_pass_begin_info.clearValueCount = 1
render_pass_begin_info.pClearValues = &clear_color
vk.CmdBeginRenderPass(command_buffer, &render_pass_begin_info, vk.SubpassContents.INLINE)
vk.CmdBindPipeline(command_buffer, vk.PipelineBindPoint.GRAPHICS, graphics_pipeline)
viewport: vk.Viewport
viewport.x = 0.0
viewport.y = 0.0
viewport.width = f32(swap_chain_extent.width)
viewport.height = f32(swap_chain_extent.height)
viewport.minDepth = 0.0
viewport.maxDepth = 1.0
vk.CmdSetViewport(command_buffer, 0, 1, &viewport)
scissor: vk.Rect2D
scissor.offset = {0, 0}
scissor.extent = swap_chain_extent
vk.CmdSetScissor(command_buffer, 0, 1, &scissor)
vk.CmdDraw(command_buffer, 3, 1, 0, 0)
vk.CmdEndRenderPass(command_buffer)
if vk.EndCommandBuffer(command_buffer) != vk.Result.SUCCESS {
fmt.eprintln("Failed to record command buffer.")
}
}
draw_frame :: proc(using ctx: ^Context) {
vk.WaitForFences(logical_device, 1, &in_flight_fence, true, math.max(u64))
vk.ResetFences(logical_device, 1, &in_flight_fence)
image_index: u32
vk.AcquireNextImageKHR(logical_device, swap_chain, max(u64), image_available_semaphore, {}, &image_index)
vk.ResetCommandBuffer(command_buffer, {})
record_command_buffer(ctx, image_index)
submit_info: vk.SubmitInfo
submit_info.sType = vk.StructureType.SUBMIT_INFO
wait_semaphores: []vk.Semaphore = {image_available_semaphore}
wait_stages: []vk.PipelineStageFlags = {{vk.PipelineStageFlag.COLOR_ATTACHMENT_OUTPUT}}
submit_info.waitSemaphoreCount = 1
submit_info.pWaitSemaphores = raw_data(wait_semaphores)
submit_info.pWaitDstStageMask = raw_data(wait_stages)
submit_info.commandBufferCount = 1
submit_info.pCommandBuffers = &command_buffer
signal_semaphores: []vk.Semaphore = {render_finished_sempahore}
submit_info.signalSemaphoreCount = 1
submit_info.pSignalSemaphores = raw_data(signal_semaphores)
if vk.QueueSubmit(graphics_queue, 1, &submit_info, in_flight_fence) != vk.Result.SUCCESS {
fmt.eprintln("Failed to submit draw command buffer.")
}
present_info: vk.PresentInfoKHR
present_info.sType = vk.StructureType.PRESENT_INFO_KHR
present_info.waitSemaphoreCount = 1
present_info.pWaitSemaphores = raw_data(signal_semaphores)
swap_chains: []vk.SwapchainKHR = {swap_chain}
present_info.swapchainCount = 1
present_info.pSwapchains = raw_data(swap_chains)
present_info.pImageIndices = &image_index
present_info.pResults = nil
vk.QueuePresentKHR(present_queue, &present_info)
}
create_sync_objects :: proc(using ctx: ^Context) {
semaphore_info: vk.SemaphoreCreateInfo
semaphore_info.sType = vk.StructureType.SEMAPHORE_CREATE_INFO
fence_info: vk.FenceCreateInfo
fence_info.sType = vk.StructureType.FENCE_CREATE_INFO
fence_info.flags = {vk.FenceCreateFlag.SIGNALED}
if vk.CreateSemaphore(logical_device, &semaphore_info, nil, &image_available_semaphore) != vk.Result.SUCCESS ||
vk.CreateSemaphore(logical_device, &semaphore_info, nil, &render_finished_sempahore) != vk.Result.SUCCESS ||
vk.CreateFence(logical_device, &fence_info, nil, &in_flight_fence) != vk.Result.SUCCESS {
fmt.eprintln("Failed to create sempahores and fences.")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment