Skip to content

Instantly share code, notes, and snippets.

@jockus
Last active July 29, 2024 21:03
Show Gist options
  • Save jockus/61a90bef44048a26e056f2d538e67886 to your computer and use it in GitHub Desktop.
Save jockus/61a90bef44048a26e056f2d538e67886 to your computer and use it in GitHub Desktop.
minimal_modern_vulkan
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