-
-
Save jockus/61a90bef44048a26e056f2d538e67886 to your computer and use it in GitHub Desktop.
minimal_modern_vulkan
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package vulkan_tutorial | |
import "core:fmt" | |
import "core:dynlib" | |
import "core:mem" | |
import "vendor:glfw" | |
import vk "vendor:vulkan" | |
DB :: "DEBUG:" | |
DBVB :: "DEBUG (VERBOSE):" | |
ERROR :: "ERROR:" | |
WARNING :: "WARNING:" | |
vertex_shader_bytecode :: #load("./shaders/vertex.spv") | |
fragment_shader_bytecode :: #load("./shaders/fragment.spv") | |
vkok :: proc(result : vk.Result, loc := #caller_location) { | |
#partial switch result { | |
case .SUCCESS: | |
case .ERROR_OUT_OF_HOST_MEMORY: | |
fmt.eprintln(ERROR, "Out of host memory.") | |
case .ERROR_OUT_OF_DEVICE_MEMORY: | |
fmt.eprintln(ERROR, "Out of device memory.") | |
case .ERROR_INITIALIZATION_FAILED: | |
fmt.eprintln(ERROR, "Initialization failed.") | |
case .ERROR_LAYER_NOT_PRESENT: | |
fmt.eprintln(ERROR, "Layer not present.") | |
case .ERROR_EXTENSION_NOT_PRESENT: | |
fmt.eprintln(ERROR, "Extension not present.") | |
case .ERROR_INCOMPATIBLE_DRIVER: | |
fmt.eprintln(ERROR, "Incompatible driver.") | |
case: | |
fmt.eprintln(ERROR, "Other Spec 1.3 Error!") | |
} | |
if result != .SUCCESS { | |
fmt.println(ERROR, "vk function result was not .SUCCESS") | |
fmt.println( "Result was instead:", result) | |
fmt.println(" Error at:") | |
fmt.println(" ", loc) | |
assert(false) | |
} | |
} | |
main :: proc() { | |
when ODIN_DEBUG { | |
track: mem.Tracking_Allocator | |
mem.tracking_allocator_init(&track, context.allocator) | |
context.allocator = mem.tracking_allocator(&track) | |
defer { | |
if len(track.allocation_map) > 0 { | |
fmt.eprintf("=== %v allocations not freed: ===\n", len(track.allocation_map)) | |
for _, entry in track.allocation_map { | |
fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location) | |
} | |
} | |
if len(track.bad_free_array) > 0 { | |
fmt.eprintf("=== %v incorrect frees: ===\n", len(track.bad_free_array)) | |
for entry in track.bad_free_array { | |
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location) | |
} | |
} | |
mem.tracking_allocator_destroy(&track) | |
} | |
} | |
glfw.Init() | |
defer glfw.Terminate() | |
glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API) | |
glfw.WindowHint(glfw.RESIZABLE, glfw.TRUE) | |
glfw.WindowHint(glfw.VISIBLE, glfw.FALSE); | |
window_handle := glfw.CreateWindow(1000, 1000, "Vulkan Window", nil, nil) | |
assert(window_handle != nil) | |
defer glfw.DestroyWindow(window_handle) | |
instance := instance_create() | |
defer instance_destroy(&instance) | |
surface := surface_create(instance, window_handle) | |
defer surface_destroy(instance, &surface) | |
device := device_create(instance, surface) | |
defer device_destroy(&device) | |
swapchain := swapchain_create(device, surface) | |
defer swapchain_destroy(device, &swapchain) | |
rendering := rendering_create(device) | |
defer rendering_destroy(device, &rendering) | |
defer vk.DeviceWaitIdle(device.device) | |
first_present := true | |
for !glfw.WindowShouldClose(window_handle) { | |
glfw.PollEvents() | |
render_frame(device, surface, &swapchain, &rendering) | |
if first_present { | |
first_present = false | |
glfw.ShowWindow(window_handle) | |
} | |
} | |
} | |
Instance :: struct { | |
instance : vk.Instance | |
} | |
instance_create :: proc() -> (instance: Instance) { | |
vulkan_lib, loaded := dynlib.load_library("vulkan-1.dll") | |
assert(loaded) | |
vkGetInstanceProcAddr, found := dynlib.symbol_address(vulkan_lib, "vkGetInstanceProcAddr") | |
assert(found) | |
vk.load_proc_addresses_global(vkGetInstanceProcAddr) | |
required_layers : [dynamic] cstring | |
defer delete(required_layers) | |
when ODIN_DEBUG { | |
append(&required_layers, "VK_LAYER_KHRONOS_validation") | |
// append(&required_layers, "VK_LAYER_KHRONOS_shader_object") | |
} | |
// Ensure layers | |
supported_layer_count : u32 | |
vkok(vk.EnumerateInstanceLayerProperties(&supported_layer_count, nil)) | |
supported_layers := make([] vk.LayerProperties, supported_layer_count) | |
defer delete(supported_layers) | |
vkok(vk.EnumerateInstanceLayerProperties(&supported_layer_count, raw_data(supported_layers))) | |
for required_layer in required_layers { | |
layer_found : bool | |
for &supported_layer in supported_layers { | |
if required_layer == cstring(&supported_layer.layerName[0]) { | |
layer_found = true | |
break | |
} | |
} | |
if !layer_found { | |
fmt.println(ERROR, "A required layer:", required_layer, "is missing!") | |
assert(false) | |
} | |
} | |
// Ensure extensions | |
required_extensions : [dynamic]cstring | |
defer delete(required_extensions) | |
for ext in glfw.GetRequiredInstanceExtensions() { | |
append(&required_extensions, ext) | |
} | |
supported_extension_count : u32 | |
vkok(vk.EnumerateInstanceExtensionProperties(nil, &supported_extension_count, nil)) | |
supported_extensions := make([]vk.ExtensionProperties, supported_extension_count) | |
defer delete(supported_extensions) | |
vkok(vk.EnumerateInstanceExtensionProperties(nil, &supported_extension_count, raw_data(supported_extensions))) | |
for required_extension in required_extensions { | |
extension_found : bool | |
for &supported_extension in supported_extensions { | |
if required_extension == cstring(&supported_extension.extensionName[0]) { | |
extension_found = true | |
break | |
} | |
} | |
if !extension_found { | |
fmt.println(ERROR, "A required extension:", required_extension, "is missing!") | |
assert(false) | |
} | |
} | |
appinfo := vk.ApplicationInfo { | |
sType = vk.StructureType.APPLICATION_INFO, | |
pApplicationName = "Minimal Modern Vulkan", | |
applicationVersion = vk.MAKE_VERSION(1,0,0), | |
pEngineName = "Minimal Modern Vulkan", | |
engineVersion = vk.MAKE_VERSION(1,0,0), | |
apiVersion = vk.API_VERSION_1_3, | |
} | |
icreateinfo := vk.InstanceCreateInfo { | |
sType = vk.StructureType.INSTANCE_CREATE_INFO, | |
pApplicationInfo = &appinfo, | |
enabledLayerCount = u32(len(required_layers)), | |
ppEnabledLayerNames = raw_data(required_layers), | |
enabledExtensionCount = u32(len(required_extensions)), | |
ppEnabledExtensionNames = raw_data(required_extensions), | |
} | |
vkok(vk.CreateInstance(&icreateinfo, nil, &instance.instance)) | |
vk.load_proc_addresses(instance.instance) | |
return | |
} | |
instance_destroy :: proc(instance : ^Instance) { | |
vk.DestroyInstance(instance.instance, nil) | |
} | |
Surface :: struct { | |
surface : vk.SurfaceKHR | |
} | |
surface_create :: proc(instance : Instance, window_handle : glfw.WindowHandle) -> (surface: Surface) { | |
vkok(glfw.CreateWindowSurface(instance.instance, window_handle, nil, &surface.surface)) | |
return | |
} | |
surface_destroy :: proc(instance : Instance, surface : ^Surface) { | |
vk.DestroySurfaceKHR(instance.instance, surface.surface, nil) | |
} | |
Device :: struct { | |
physical_device : vk.PhysicalDevice, | |
device : vk.Device, | |
graphics_queue : vk.Queue, | |
graphics_family_queue_index : u32, | |
} | |
device_create :: proc(instance : Instance, surface : Surface) -> (device: Device) { | |
pds_count : u32 | |
vkok(vk.EnumeratePhysicalDevices(instance.instance, &pds_count, nil)) | |
physical_devices := make([]vk.PhysicalDevice, pds_count) | |
defer delete(physical_devices) | |
vkok(vk.EnumeratePhysicalDevices(instance.instance, &pds_count, raw_data(physical_devices))) | |
pdevice_required_extensions := []cstring{ | |
vk.KHR_SWAPCHAIN_EXTENSION_NAME, | |
vk.KHR_DYNAMIC_RENDERING_EXTENSION_NAME, | |
vk.EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, | |
vk.EXT_SHADER_OBJECT_EXTENSION_NAME, | |
} | |
at_least_one_pdevice_okay : bool | |
okay_pdevice : vk.PhysicalDevice | |
graphics_family_queue_index : u32 | |
for pdevice in physical_devices { | |
pdevice_supported_extensions_count : u32 | |
vkok(vk.EnumerateDeviceExtensionProperties(pdevice, nil, &pdevice_supported_extensions_count, nil)) | |
pdevice_supported_extensions := make([]vk.ExtensionProperties, pdevice_supported_extensions_count) | |
defer delete(pdevice_supported_extensions) | |
vkok(vk.EnumerateDeviceExtensionProperties(pdevice, nil, &pdevice_supported_extensions_count, raw_data(pdevice_supported_extensions))) | |
has_required_extensions := true | |
for required_extension in pdevice_required_extensions { | |
found : bool | |
for &supported_extension in pdevice_supported_extensions { | |
if required_extension == cstring(&supported_extension.extensionName[0]) { | |
found = true | |
break | |
} | |
} | |
if !found { | |
has_required_extensions = false | |
break | |
} | |
} | |
has_required_queue_family : bool | |
queue_count : u32 | |
vk.GetPhysicalDeviceQueueFamilyProperties(pdevice, &queue_count, nil) | |
qf_properties := make([]vk.QueueFamilyProperties, queue_count) | |
defer delete(qf_properties) | |
vk.GetPhysicalDeviceQueueFamilyProperties(pdevice, &queue_count, raw_data(qf_properties)) | |
for queue_family, queue_family_index in qf_properties { | |
supports_present : b32 | |
vkok(vk.GetPhysicalDeviceSurfaceSupportKHR(pdevice, u32(queue_family_index), surface.surface, &supports_present)) | |
if vk.QueueFlag.GRAPHICS in queue_family.queueFlags && supports_present { | |
graphics_family_queue_index = u32(queue_family_index) | |
has_required_queue_family = true | |
break | |
} | |
} | |
// TODO: Ensure wanted surface format is available? | |
if has_required_extensions && has_required_queue_family { | |
at_least_one_pdevice_okay = true | |
okay_pdevice = pdevice | |
} | |
} | |
if !at_least_one_pdevice_okay { | |
fmt.eprintln("No physical devices have all the required extensions") | |
assert(false) | |
} | |
device.physical_device = okay_pdevice | |
device.graphics_family_queue_index = graphics_family_queue_index | |
queue_priority_graphics : f32 = 1 | |
qci_graphics := vk.DeviceQueueCreateInfo { | |
sType = vk.StructureType.DEVICE_QUEUE_CREATE_INFO, | |
queueFamilyIndex = graphics_family_queue_index, | |
queueCount = 1.0, | |
pQueuePriorities = &queue_priority_graphics, | |
} | |
enabledShaderObjectFeaturesEXT := vk.PhysicalDeviceShaderObjectFeaturesEXT{ | |
sType = vk.StructureType.PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, | |
shaderObject = true, | |
} | |
dynamic_rendering_feature := vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ | |
sType = vk.StructureType.PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR, | |
dynamicRendering = true, | |
pNext = &enabledShaderObjectFeaturesEXT | |
} | |
device_features : vk.PhysicalDeviceFeatures | |
dci := vk.DeviceCreateInfo { | |
sType = vk.StructureType.DEVICE_CREATE_INFO, | |
queueCreateInfoCount = 1, | |
pQueueCreateInfos = &qci_graphics, | |
pEnabledFeatures = &device_features, | |
enabledExtensionCount = u32(len(pdevice_required_extensions)), | |
ppEnabledExtensionNames = raw_data(pdevice_required_extensions), | |
pNext = &dynamic_rendering_feature, | |
} | |
vkok(vk.CreateDevice(device.physical_device, &dci, nil, &device.device)) | |
vk.GetDeviceQueue(device.device, device.graphics_family_queue_index, 0, &device.graphics_queue) | |
return | |
} | |
device_destroy :: proc(device : ^Device) { | |
vk.DestroyDevice(device.device, nil) | |
} | |
Swapchain :: struct { | |
swapchain : vk.SwapchainKHR, | |
images : []vk.Image, | |
image_views : []vk.ImageView, | |
extent : vk.Extent2D, | |
format : vk.Format, | |
} | |
swapchain_create :: proc(device : Device, surface : Surface) -> (swapchain: Swapchain) { | |
supported_surface_formats_count : u32 | |
vkok(vk.GetPhysicalDeviceSurfaceFormatsKHR(device.physical_device, surface.surface, &supported_surface_formats_count, nil)) | |
supported_surface_formats := make([]vk.SurfaceFormatKHR, supported_surface_formats_count) | |
defer delete(supported_surface_formats) | |
vkok(vk.GetPhysicalDeviceSurfaceFormatsKHR(device.physical_device, surface.surface, &supported_surface_formats_count, raw_data(supported_surface_formats))) | |
surface_format := supported_surface_formats[0] | |
desired_surface_format := vk.SurfaceFormatKHR{ | |
format = .B8G8R8A8_SRGB, | |
colorSpace = .SRGB_NONLINEAR, | |
} | |
for sf in supported_surface_formats { | |
if sf == desired_surface_format { | |
surface_format = desired_surface_format | |
break | |
} | |
} | |
supported_present_modes_count : u32 | |
vkok(vk.GetPhysicalDeviceSurfacePresentModesKHR(device.physical_device, surface.surface, &supported_present_modes_count, nil)) | |
supported_present_modes := make([]vk.PresentModeKHR, supported_present_modes_count) | |
defer delete(supported_present_modes) | |
vkok(vk.GetPhysicalDeviceSurfacePresentModesKHR(device.physical_device, surface.surface, &supported_present_modes_count, raw_data(supported_present_modes))) | |
present_mode := vk.PresentModeKHR.FIFO | |
desired_present_mode := vk.PresentModeKHR.MAILBOX | |
for pm in supported_present_modes { | |
if pm == desired_present_mode { | |
present_mode = desired_present_mode | |
} | |
} | |
// TODO: Just handle double/triple buffer | |
surface_capabilities : vk.SurfaceCapabilitiesKHR | |
vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(device.physical_device, surface.surface, &surface_capabilities) | |
surface_image_count := min(surface_capabilities.minImageCount + 1, surface_capabilities.maxImageCount) | |
extent_special_value := vk.Extent2D{max(u32), max(u32)} | |
swapchain.extent = surface_capabilities.currentExtent | |
if swapchain.extent == extent_special_value { | |
swapchain.extent = surface_capabilities.minImageExtent | |
} | |
swpcnci := vk.SwapchainCreateInfoKHR { | |
sType = .SWAPCHAIN_CREATE_INFO_KHR, | |
surface = surface.surface, | |
minImageCount = surface_image_count, | |
imageFormat = surface_format.format, | |
imageColorSpace = surface_format.colorSpace, | |
imageExtent = swapchain.extent, | |
imageArrayLayers = 1, | |
imageUsage = { vk.ImageUsageFlag.COLOR_ATTACHMENT }, | |
imageSharingMode = .EXCLUSIVE, | |
preTransform = surface_capabilities.currentTransform, | |
compositeAlpha = { vk.CompositeAlphaFlagKHR.OPAQUE }, | |
presentMode = present_mode, | |
clipped = true, | |
} | |
vkok(vk.CreateSwapchainKHR(device.device, &swpcnci, nil, &swapchain.swapchain)) | |
device_image_count : u32 | |
vkok(vk.GetSwapchainImagesKHR(device.device, swapchain.swapchain, &device_image_count, nil)) | |
swapchain.images = make([]vk.Image, device_image_count) | |
vkok(vk.GetSwapchainImagesKHR(device.device, swapchain.swapchain, &device_image_count, raw_data(swapchain.images))) | |
swapchain.image_views = make([]vk.ImageView, device_image_count) | |
for i in 0..<device_image_count { | |
imvci := vk.ImageViewCreateInfo { | |
sType = .IMAGE_VIEW_CREATE_INFO, | |
image = swapchain.images[i], | |
viewType = vk.ImageViewType.D2, | |
format = surface_format.format, | |
components = { | |
r = .IDENTITY, | |
g = .IDENTITY, | |
b = .IDENTITY, | |
a = .IDENTITY, | |
}, | |
subresourceRange = { | |
aspectMask = { .COLOR }, | |
baseMipLevel = 0, | |
levelCount = 1, | |
baseArrayLayer = 0, | |
layerCount = 1, | |
}, | |
} | |
vkok(vk.CreateImageView(device.device, &imvci, nil, &swapchain.image_views[i])) | |
} | |
return | |
} | |
swapchain_destroy :: proc(device : Device, swapchain : ^Swapchain) { | |
for image_view in swapchain.image_views { | |
vk.DestroyImageView(device.device, image_view, nil); | |
} | |
delete(swapchain.image_views) | |
delete(swapchain.images) | |
vk.DestroySwapchainKHR(device.device, swapchain.swapchain, nil); | |
} | |
swapchain_recreate :: proc(device : Device, surface : Surface, swapchain : ^Swapchain) { | |
vk.DeviceWaitIdle(device.device) | |
swapchain_destroy(device, swapchain) | |
swapchain^ = swapchain_create(device, surface) | |
} | |
Rendering :: struct { | |
shaders : [2]vk.ShaderEXT, | |
command_pool : vk.CommandPool, | |
command_buffer : vk.CommandBuffer, | |
image_available_semaphore : vk.Semaphore, | |
render_finished_semaphore : vk.Semaphore, | |
in_flight_fence : vk.Fence, | |
} | |
rendering_create :: proc(device : Device) -> (rendering: Rendering) { | |
// Ensure source is 4 bytes aligned | |
vertex, _ := mem.alloc_bytes(len(vertex_shader_bytecode), 4) | |
mem.copy(raw_data(vertex), raw_data(vertex_shader_bytecode), len(vertex_shader_bytecode)) | |
defer delete(vertex) | |
frag, _ := mem.alloc_bytes(len(fragment_shader_bytecode), 4) | |
mem.copy(raw_data(frag), raw_data(fragment_shader_bytecode), len(fragment_shader_bytecode)) | |
defer delete(frag) | |
shaderCreateInfos := [2]vk.ShaderCreateInfoEXT { | |
{ | |
sType = vk.StructureType.SHADER_CREATE_INFO_EXT, | |
flags = {vk.ShaderCreateFlagEXT.LINK_STAGE}, | |
stage = {vk.ShaderStageFlag.VERTEX}, | |
nextStage = {vk.ShaderStageFlag.FRAGMENT}, | |
codeType = vk.ShaderCodeTypeEXT.SPIRV, | |
pCode = raw_data(vertex), | |
codeSize = len(vertex), | |
pName = "main", | |
setLayoutCount = 0, | |
// pSetLayouts = &descriptorSetLayout, | |
}, | |
{ | |
sType = vk.StructureType.SHADER_CREATE_INFO_EXT, | |
flags = {vk.ShaderCreateFlagEXT.LINK_STAGE}, | |
stage = {vk.ShaderStageFlag.FRAGMENT}, | |
nextStage = {}, | |
codeType = vk.ShaderCodeTypeEXT.SPIRV, | |
pCode = raw_data(frag), | |
codeSize = len(frag), | |
pName = "main", | |
setLayoutCount = 0, | |
// pSetLayouts = &descriptorSetLayout, | |
} | |
} | |
vkok(vk.CreateShadersEXT(device.device, 2, &shaderCreateInfos[0], nil, &rendering.shaders[0])) | |
cpci := vk.CommandPoolCreateInfo { | |
sType = .COMMAND_POOL_CREATE_INFO, | |
flags = { vk.CommandPoolCreateFlags.RESET_COMMAND_BUFFER }, | |
queueFamilyIndex = device.graphics_family_queue_index, | |
} | |
vkok(vk.CreateCommandPool(device.device, &cpci, nil, &rendering.command_pool)) | |
cbai := vk.CommandBufferAllocateInfo { | |
sType = .COMMAND_BUFFER_ALLOCATE_INFO, | |
commandPool = rendering.command_pool, | |
level = .PRIMARY, | |
commandBufferCount = 1, | |
} | |
vkok(vk.AllocateCommandBuffers(device.device, &cbai, &rendering.command_buffer)) | |
sci := vk.SemaphoreCreateInfo { | |
sType = .SEMAPHORE_CREATE_INFO | |
} | |
vkok(vk.CreateSemaphore(device.device, &sci, nil, &rendering.image_available_semaphore)) | |
vkok(vk.CreateSemaphore(device.device, &sci, nil, &rendering.render_finished_semaphore)) | |
fci := vk.FenceCreateInfo { | |
sType = .FENCE_CREATE_INFO, | |
flags = { .SIGNALED }, | |
} | |
vkok(vk.CreateFence(device.device, &fci, nil, &rendering.in_flight_fence)) | |
return | |
} | |
rendering_destroy :: proc(device : Device, rendering : ^Rendering) { | |
vk.DestroyShaderEXT(device.device, rendering.shaders[0], nil) | |
vk.DestroyShaderEXT(device.device, rendering.shaders[1], nil) | |
vk.FreeCommandBuffers(device.device, rendering.command_pool, 1, &rendering.command_buffer) | |
vk.DestroyCommandPool(device.device, rendering.command_pool, nil) | |
vk.DestroySemaphore(device.device, rendering.image_available_semaphore, nil) | |
vk.DestroySemaphore(device.device, rendering.render_finished_semaphore, nil) | |
vk.DestroyFence(device.device, rendering.in_flight_fence, nil) | |
} | |
swapchain_barrier :: proc(command_buffer : vk.CommandBuffer, image : vk.Image, top : bool) { | |
image_memory_barrier := vk.ImageMemoryBarrier{ | |
sType = vk.StructureType.IMAGE_MEMORY_BARRIER, | |
dstAccessMask = top ? {vk.AccessFlag.COLOR_ATTACHMENT_WRITE} : {}, | |
srcAccessMask = top ? {} : {vk.AccessFlag.COLOR_ATTACHMENT_WRITE}, | |
oldLayout = top ? vk.ImageLayout.UNDEFINED : vk.ImageLayout.COLOR_ATTACHMENT_OPTIMAL, | |
newLayout = top ? vk.ImageLayout.COLOR_ATTACHMENT_OPTIMAL : vk.ImageLayout.PRESENT_SRC_KHR, | |
image = image, | |
subresourceRange = { | |
aspectMask = {vk.ImageAspectFlag.COLOR}, | |
baseMipLevel = 0, | |
levelCount = 1, | |
baseArrayLayer = 0, | |
layerCount = 1, | |
} | |
} | |
vk.CmdPipelineBarrier( | |
command_buffer, | |
top ? {vk.PipelineStageFlag.TOP_OF_PIPE} : {vk.PipelineStageFlag.COLOR_ATTACHMENT_OUTPUT}, // srcStageMask | |
top ? {vk.PipelineStageFlag.COLOR_ATTACHMENT_OUTPUT} : {vk.PipelineStageFlag.BOTTOM_OF_PIPE}, // dstStageMask | |
{}, | |
{}, | |
nil, | |
{}, | |
nil, | |
1, // imageMemoryBarrierCount | |
&image_memory_barrier // pImageMemoryBarriers | |
) | |
} | |
record_command_buffer :: proc(swapchain : Swapchain, rendering : ^Rendering, image_index : u32) { | |
cbbi := vk.CommandBufferBeginInfo { | |
sType = .COMMAND_BUFFER_BEGIN_INFO | |
} | |
vkok(vk.BeginCommandBuffer(rendering.command_buffer, &cbbi)) | |
swapchain_barrier(rendering.command_buffer, swapchain.images[image_index], true) | |
clear_color := vk.ClearValue{ | |
color = {float32 = {0,0,0,1}} | |
} | |
color_attachment_info := vk.RenderingAttachmentInfoKHR{ | |
sType = vk.StructureType.RENDERING_ATTACHMENT_INFO_KHR, | |
imageView = swapchain.image_views[image_index], | |
imageLayout = vk.ImageLayout.ATTACHMENT_OPTIMAL, | |
loadOp = vk.AttachmentLoadOp.CLEAR, | |
storeOp = vk.AttachmentStoreOp.STORE, | |
clearValue = clear_color, | |
} | |
rendering_info := vk.RenderingInfoKHR{ | |
sType = vk.StructureType.RENDERING_INFO_KHR, | |
renderArea = { | |
offset = {0, 0}, | |
extent = swapchain.extent, | |
}, | |
layerCount = 1, | |
colorAttachmentCount = 1, | |
pColorAttachments = &color_attachment_info, | |
} | |
vk.CmdBeginRenderingKHR(rendering.command_buffer, &rendering_info) | |
// Set pipeline state | |
viewport := vk.Viewport{0, 0, f32(swapchain.extent.width), f32(swapchain.extent.height), 0, 1} | |
scissor := vk.Rect2D{{0,0}, swapchain.extent} | |
vk.CmdSetViewportWithCountEXT(rendering.command_buffer, 1, &viewport) | |
vk.CmdSetScissorWithCountEXT(rendering.command_buffer, 1, &scissor) | |
vk.CmdSetCullModeEXT(rendering.command_buffer, {vk.CullModeFlag.BACK}) | |
vk.CmdSetFrontFaceEXT(rendering.command_buffer, vk.FrontFace.CLOCKWISE) | |
vk.CmdSetDepthTestEnableEXT(rendering.command_buffer, true) | |
vk.CmdSetDepthWriteEnableEXT(rendering.command_buffer, true) | |
vk.CmdSetDepthCompareOpEXT(rendering.command_buffer, vk.CompareOp.LESS_OR_EQUAL) | |
vk.CmdSetPrimitiveTopologyEXT(rendering.command_buffer, vk.PrimitiveTopology.TRIANGLE_LIST) | |
vk.CmdSetRasterizerDiscardEnableEXT(rendering.command_buffer, false) | |
vk.CmdSetPolygonModeEXT(rendering.command_buffer, vk.PolygonMode.FILL) | |
vk.CmdSetRasterizationSamplesEXT(rendering.command_buffer, {vk.SampleCountFlag._1}) | |
vk.CmdSetAlphaToCoverageEnableEXT(rendering.command_buffer, false) | |
vk.CmdSetDepthBiasEnableEXT(rendering.command_buffer, false) | |
vk.CmdSetStencilTestEnableEXT(rendering.command_buffer, false) | |
vk.CmdSetPrimitiveRestartEnableEXT(rendering.command_buffer, false) | |
sampleMask := vk.SampleMask(0xFF) | |
vk.CmdSetSampleMaskEXT(rendering.command_buffer, {vk.SampleCountFlag._1}, &sampleMask) | |
colorBlendEnables := b32(false) | |
vk.CmdSetColorBlendEnableEXT(rendering.command_buffer, 0, 1, &colorBlendEnables) | |
colorBlendComponentFlags := vk.ColorComponentFlags{.R, .G, .B, .A} | |
vk.CmdSetColorWriteMaskEXT(rendering.command_buffer, 0, 1, &colorBlendComponentFlags) | |
colorBlendEquations := vk.ColorBlendEquationEXT{ | |
srcColorBlendFactor = vk.BlendFactor.SRC_ALPHA, | |
dstColorBlendFactor = .ONE_MINUS_SRC_ALPHA, | |
colorBlendOp = .ADD, | |
srcAlphaBlendFactor = .ONE, | |
dstAlphaBlendFactor = .ZERO, | |
alphaBlendOp = .ADD, | |
} | |
vk.CmdSetColorBlendEquationEXT(rendering.command_buffer, 0, 1, &colorBlendEquations) | |
// Bind shaders | |
stages := [2]vk.ShaderStageFlags{{.VERTEX}, {.FRAGMENT}} | |
vk.CmdBindShadersEXT(rendering.command_buffer, 2, &stages[0], &rendering.shaders[0]) | |
// Submit draw | |
vk.CmdDraw(rendering.command_buffer, 3, 1, 0, 0) | |
vk.CmdEndRenderingKHR(rendering.command_buffer) | |
swapchain_barrier(rendering.command_buffer, swapchain.images[image_index], false) | |
vkok(vk.EndCommandBuffer(rendering.command_buffer)) | |
} | |
render_frame :: proc(device : Device, surface : Surface, swapchain : ^Swapchain, rendering : ^Rendering) { | |
vkok(vk.WaitForFences(device.device, 1, &rendering.in_flight_fence, true, max(u64))) | |
image_index : u32 | |
acquire_result := vk.AcquireNextImageKHR(device.device, swapchain.swapchain, max(u64), rendering.image_available_semaphore, 0, &image_index) | |
if acquire_result == vk.Result.ERROR_OUT_OF_DATE_KHR || acquire_result == vk.Result.SUBOPTIMAL_KHR { | |
swapchain_recreate(device, surface, swapchain) | |
return | |
} | |
else { | |
vkok(acquire_result) | |
} | |
vkok(vk.ResetFences(device.device, 1, &rendering.in_flight_fence)) | |
vkok(vk.ResetCommandBuffer(rendering.command_buffer, {})) | |
record_command_buffer(swapchain^, rendering, image_index) | |
submit_info := vk.SubmitInfo{ | |
sType = .SUBMIT_INFO, | |
waitSemaphoreCount = 1, | |
pWaitSemaphores = &rendering.image_available_semaphore, | |
pWaitDstStageMask = &vk.PipelineStageFlags{ .COLOR_ATTACHMENT_OUTPUT }, | |
commandBufferCount = 1, | |
pCommandBuffers = &rendering.command_buffer, | |
signalSemaphoreCount = 1, | |
pSignalSemaphores = &rendering.render_finished_semaphore, | |
} | |
vkok(vk.QueueSubmit(device.graphics_queue, 1, &submit_info, rendering.in_flight_fence)) | |
present_info := vk.PresentInfoKHR{ | |
sType = .PRESENT_INFO_KHR, | |
waitSemaphoreCount = 1, | |
pWaitSemaphores = &rendering.render_finished_semaphore, | |
swapchainCount = 1, | |
pSwapchains = &swapchain.swapchain, | |
pImageIndices = &image_index, | |
} | |
present_result := vk.QueuePresentKHR(device.graphics_queue, &present_info) | |
if present_result == vk.Result.ERROR_OUT_OF_DATE_KHR || present_result == vk.Result.SUBOPTIMAL_KHR { | |
swapchain_recreate(device, surface, swapchain) | |
} | |
else { | |
vkok(present_result) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment