-
-
Save terickson001/bdaa52ce621a6c7f4120abba8959ffe6 to your computer and use it in GitHub Desktop.
| import "shared:shaderc" | |
| import "vendor:glfw" | |
| import vk "vendor:vulkan" | |
| MAX_FRAMES_IN_FLIGHT :: 2 | |
| Context :: struct | |
| { | |
| instance: vk.Instance, | |
| device: vk.Device, | |
| physical_device: vk.PhysicalDevice, | |
| swap_chain: Swapchain, | |
| pipeline: Pipeline, | |
| queue_indices: [QueueFamily]int, | |
| queues: [QueueFamily]vk.Queue, | |
| surface: vk.SurfaceKHR, | |
| window: glfw.WindowHandle, | |
| command_pool: vk.CommandPool, | |
| command_buffers: [MAX_FRAMES_IN_FLIGHT]vk.CommandBuffer, | |
| vertex_buffer: Buffer, | |
| index_buffer: Buffer, | |
| image_available: [MAX_FRAMES_IN_FLIGHT]vk.Semaphore, | |
| render_finished: [MAX_FRAMES_IN_FLIGHT]vk.Semaphore, | |
| in_flight: [MAX_FRAMES_IN_FLIGHT]vk.Fence, | |
| curr_frame: u32, | |
| framebuffer_resized: bool, | |
| } | |
| Buffer :: struct | |
| { | |
| buffer: vk.Buffer, | |
| memory: vk.DeviceMemory, | |
| length: int, | |
| size: vk.DeviceSize, | |
| } | |
| Pipeline :: struct | |
| { | |
| handle: vk.Pipeline, | |
| render_pass: vk.RenderPass, | |
| layout: vk.PipelineLayout, | |
| } | |
| QueueFamily :: enum | |
| { | |
| Graphics, | |
| Present, | |
| } | |
| Swapchain :: struct | |
| { | |
| handle: vk.SwapchainKHR, | |
| images: []vk.Image, | |
| image_views: []vk.ImageView, | |
| format: vk.SurfaceFormatKHR, | |
| extent: vk.Extent2D, | |
| present_mode: vk.PresentModeKHR, | |
| image_count: u32, | |
| support: SwapChainDetails, | |
| framebuffers: []vk.Framebuffer, | |
| } | |
| SwapChainDetails :: struct | |
| { | |
| capabilities: vk.SurfaceCapabilitiesKHR, | |
| formats: []vk.SurfaceFormatKHR, | |
| present_modes: []vk.PresentModeKHR, | |
| } | |
| Vertex :: struct | |
| { | |
| pos: [2]f32, | |
| color: [3]f32, | |
| } | |
| DEVICE_EXTENSIONS := [?]cstring{ | |
| "VK_KHR_swapchain", | |
| }; | |
| VALIDATION_LAYERS := [?]cstring{"VK_LAYER_KHRONOS_validation"}; | |
| main :: proc() | |
| { | |
| using ctx: Context; | |
| init_window(&ctx); | |
| for q in &queue_indices do q = -1; | |
| vertices := [?]Vertex{ | |
| {{-0.5, -0.5}, {0.0, 0.0, 1.0}}, | |
| {{ 0.5, -0.5}, {1.0, 0.0, 0.0}}, | |
| {{ 0.5, 0.5}, {0.0, 1.0, 0.0}}, | |
| {{-0.5, 0.5}, {1.0, 0.0, 0.0}}, | |
| }; | |
| indices := [?]u16{ | |
| 0, 1, 2, | |
| 2, 3, 0, | |
| }; | |
| init_vulkan(&ctx, vertices[:], indices[:]); | |
| for !glfw.WindowShouldClose(window) | |
| { | |
| glfw.PollEvents(); | |
| draw_frame(&ctx, vertices[:], indices[:]); | |
| } | |
| vk.DeviceWaitIdle(device); | |
| deinit_vulkan(&ctx); | |
| glfw.DestroyWindow(window); | |
| glfw.Terminate(); | |
| } | |
| VERTEX_BINDING := vk.VertexInputBindingDescription{ | |
| binding = 0, | |
| stride = size_of(Vertex), | |
| inputRate = .VERTEX, | |
| }; | |
| VERTEX_ATTRIBUTES := [?]vk.VertexInputAttributeDescription{ | |
| { | |
| binding = 0, | |
| location = 0, | |
| format = .R32G32_SFLOAT, | |
| offset = cast(u32)offset_of(Vertex, pos), | |
| }, | |
| { | |
| binding = 0, | |
| location = 1, | |
| format = .R32G32B32_SFLOAT, | |
| offset = cast(u32)offset_of(Vertex, color), | |
| }, | |
| }; | |
| draw_frame :: proc(using ctx: ^Context, vertices: []Vertex, indices: []u16) | |
| { | |
| vk.WaitForFences(device, 1, &in_flight[curr_frame], true, max(u64)); | |
| image_index: u32; | |
| res := vk.AcquireNextImageKHR(device, swap_chain.handle, max(u64), image_available[curr_frame], {}, &image_index); | |
| if res == .ERROR_OUT_OF_DATE_KHR || res == .SUBOPTIMAL_KHR || framebuffer_resized | |
| { | |
| framebuffer_resized = false; | |
| recreate_swap_chain(ctx); | |
| return; | |
| } | |
| else if res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed tp acquire swap chain image!\n"); | |
| os.exit(1); | |
| } | |
| vk.ResetFences(device, 1, &in_flight[curr_frame]); | |
| vk.ResetCommandBuffer(command_buffers[curr_frame], {}); | |
| record_command_buffer(ctx, command_buffers[curr_frame], image_index); | |
| submit_info: vk.SubmitInfo; | |
| submit_info.sType = .SUBMIT_INFO; | |
| wait_semaphores := [?]vk.Semaphore{image_available[curr_frame]}; | |
| wait_stages := [?]vk.PipelineStageFlags{{.COLOR_ATTACHMENT_OUTPUT}}; | |
| submit_info.waitSemaphoreCount = 1; | |
| submit_info.pWaitSemaphores = &wait_semaphores[0]; | |
| submit_info.pWaitDstStageMask = &wait_stages[0]; | |
| submit_info.commandBufferCount = 1; | |
| submit_info.pCommandBuffers = &command_buffers[curr_frame]; | |
| signal_semaphores := [?]vk.Semaphore{render_finished[curr_frame]}; | |
| submit_info.signalSemaphoreCount = 1; | |
| submit_info.pSignalSemaphores = &signal_semaphores[0]; | |
| if res := vk.QueueSubmit(queues[.Graphics], 1, &submit_info, in_flight[curr_frame]); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to submit draw command buffer!\n"); | |
| os.exit(1); | |
| } | |
| present_info: vk.PresentInfoKHR; | |
| present_info.sType = .PRESENT_INFO_KHR; | |
| present_info.waitSemaphoreCount = 1; | |
| present_info.pWaitSemaphores = &signal_semaphores[0]; | |
| swap_chains := [?]vk.SwapchainKHR{swap_chain.handle}; | |
| present_info.swapchainCount = 1; | |
| present_info.pSwapchains = &swap_chains[0]; | |
| present_info.pImageIndices = &image_index; | |
| present_info.pResults = nil; | |
| vk.QueuePresentKHR(queues[.Present], &present_info); | |
| curr_frame = (curr_frame + 1) % MAX_FRAMES_IN_FLIGHT; | |
| } | |
| init_window :: proc(using ctx: ^Context) | |
| { | |
| glfw.Init(); | |
| glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API); | |
| glfw.WindowHint(glfw.RESIZABLE, 0); | |
| window = glfw.CreateWindow(800, 600, "Vulkan", nil, nil); | |
| glfw.SetWindowUserPointer(window, ctx); | |
| glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback); | |
| } | |
| framebuffer_size_callback :: proc "c" (window: glfw.WindowHandle, width, height: i32) | |
| { | |
| using ctx := cast(^Context)glfw.GetWindowUserPointer(window); | |
| framebuffer_resized = true; | |
| } | |
| init_vulkan :: proc(using ctx: ^Context, vertices: []Vertex, indices: []u16) | |
| { | |
| context.user_ptr = &instance; | |
| get_proc_address :: proc(p: rawptr, name: cstring) | |
| { | |
| (cast(^rawptr)p)^ = glfw.GetInstanceProcAddress((^vk.Instance)(context.user_ptr)^, name); | |
| } | |
| vk.load_proc_addresses(get_proc_address); | |
| create_instance(ctx); | |
| vk.load_proc_addresses(get_proc_address); | |
| extensions := get_extensions(); | |
| for ext in &extensions do fmt.println(cstring(&ext.extensionName[0])); | |
| create_surface(ctx); | |
| get_suitable_device(ctx); | |
| find_queue_families(ctx); | |
| fmt.println("Queue Indices:"); | |
| for q, f in queue_indices do fmt.printf(" %v: %d\n", f, q); | |
| create_device(ctx); | |
| for q, f in &queues | |
| { | |
| vk.GetDeviceQueue(device, u32(queue_indices[f]), 0, &q); | |
| } | |
| create_swap_chain(ctx); | |
| create_image_views(ctx); | |
| create_graphics_pipeline(ctx, "shader.vert", "shader.frag"); | |
| create_framebuffers(ctx); | |
| create_command_pool(ctx); | |
| create_vertex_buffer(ctx, vertices); | |
| create_index_buffer(ctx, indices); | |
| create_command_buffers(ctx); | |
| create_sync_objects(ctx); | |
| return; | |
| } | |
| deinit_vulkan :: proc(using ctx: ^Context) | |
| { | |
| cleanup_swap_chain(ctx); | |
| vk.FreeMemory(device, index_buffer.memory, nil); | |
| vk.DestroyBuffer(device, index_buffer.buffer, nil); | |
| vk.FreeMemory(device, vertex_buffer.memory, nil); | |
| vk.DestroyBuffer(device, vertex_buffer.buffer, nil); | |
| vk.DestroyPipeline(device, pipeline.handle, nil); | |
| vk.DestroyPipelineLayout(device, pipeline.layout, nil); | |
| vk.DestroyRenderPass(device, pipeline.render_pass, nil); | |
| for i in 0..<MAX_FRAMES_IN_FLIGHT | |
| { | |
| vk.DestroySemaphore(device, image_available[i], nil); | |
| vk.DestroySemaphore(device, render_finished[i], nil); | |
| vk.DestroyFence(device, in_flight[i], nil); | |
| } | |
| vk.DestroyCommandPool(device, command_pool, nil); | |
| vk.DestroyDevice(device, nil); | |
| vk.DestroySurfaceKHR(instance, surface, nil); | |
| vk.DestroyInstance(instance, nil); | |
| } | |
| compile_shader :: proc(name: string, kind: shaderc.shader_kind) -> []u8 | |
| { | |
| src_path := fmt.tprintf("./shaders/%s", name); | |
| cmp_path := fmt.tprintf("./shaders/compiled/%s.spv", name); | |
| src_time, src_err := os.last_write_time_by_name(src_path); | |
| if (src_err != os.ERROR_NONE) | |
| { | |
| fmt.eprintf("Failed to open shader %q\n", src_path); | |
| return nil; | |
| } | |
| cmp_time, cmp_err := os.last_write_time_by_name(cmp_path); | |
| if cmp_err == os.ERROR_NONE && cmp_time >= src_time | |
| { | |
| code, _ := os.read_entire_file(cmp_path); | |
| return code; | |
| } | |
| comp := shaderc.compiler_initialize(); | |
| options := shaderc.compile_options_initialize(); | |
| defer | |
| { | |
| shaderc.compiler_release(comp); | |
| shaderc.compile_options_release(options); | |
| } | |
| shaderc.compile_options_set_optimization_level(options, .performance); | |
| code, _ := os.read_entire_file(src_path); | |
| c_path := strings.clone_to_cstring(src_path, context.temp_allocator); | |
| res := shaderc.compile_into_spv(comp, cstring(raw_data(code)), len(code), kind, c_path, cstring("main"), options); | |
| defer shaderc.result_release(res); | |
| status := shaderc.result_get_compilation_status(res); | |
| if status != .success | |
| { | |
| fmt.printf("%s: Error: %s\n", name, shaderc.result_get_error_message(res)); | |
| return nil; | |
| } | |
| length := shaderc.result_get_length(res); | |
| out := make([]u8, length); | |
| c_out := shaderc.result_get_bytes(res); | |
| mem.copy(raw_data(out), c_out, int(length)); | |
| os.write_entire_file(cmp_path, out); | |
| return out; | |
| } | |
| /* | |
| compile_shader :: proc(path: string) -> []u8 | |
| { | |
| code, ok := os.read_entire_file(path); | |
| if !ok | |
| { | |
| fmt.eprintf("Failed to open shader %q\n", path); | |
| return nil; | |
| } | |
| defer delete(code); | |
| input := glslang.input_t{ | |
| language = glslang.SOURCE_GLSL, | |
| stage = glslang.STAGE_VERTEX, | |
| client = glslang.CLIENT_VULKAN, | |
| client_version = glslang.TARGET_VULKAN_1_0, | |
| target_language = glslang.TARGET_SPV, | |
| target_language_version = glslang.TARGET_SPV_1_5, | |
| code = cstring(&code[0]), | |
| default_version = 100, | |
| default_profile = glslang.CORE_PROFILE, | |
| force_default_version_and_profile = 0, | |
| forward_compatible = 0, | |
| messages = glslang.MSG_DEFAULT_BIT, | |
| }; | |
| shader := glslang.shader_create(&input); | |
| defer glslang.shader_delete(shader); | |
| if res := glslang.shader_preprocess(shader, &input); res == 0 | |
| { | |
| fmt.eprintf("GLSL preprocessing failed: %s\n", path); | |
| fmt.eprintf("%s\n", glslang.shader_get_info_log(shader)); | |
| fmt.eprintf("%s\n", glslang.shader_get_info_debug_log(shader)); | |
| fmt.eprintf("%s\n", code); | |
| } | |
| return nil; | |
| } | |
| */ | |
| create_instance :: proc(using ctx: ^Context) | |
| { | |
| app_info: vk.ApplicationInfo; | |
| app_info.sType = .APPLICATION_INFO; | |
| app_info.pApplicationName = "Hello Triangle"; | |
| app_info.applicationVersion = vk.MAKE_VERSION(0, 0, 1); | |
| app_info.pEngineName = "No Engine"; | |
| app_info.engineVersion = vk.MAKE_VERSION(1, 0, 0); | |
| app_info.apiVersion = vk.API_VERSION_1_0; | |
| create_info: vk.InstanceCreateInfo; | |
| create_info.sType = .INSTANCE_CREATE_INFO; | |
| create_info.pApplicationInfo = &app_info; | |
| glfw_ext := glfw.GetRequiredInstanceExtensions(); | |
| create_info.ppEnabledExtensionNames = raw_data(glfw_ext); | |
| create_info.enabledExtensionCount = cast(u32)len(glfw_ext); | |
| when ODIN_DEBUG | |
| { | |
| layer_count: u32; | |
| vk.EnumerateInstanceLayerProperties(&layer_count, nil); | |
| layers := make([]vk.LayerProperties, layer_count); | |
| vk.EnumerateInstanceLayerProperties(&layer_count, raw_data(layers)); | |
| outer: for name in VALIDATION_LAYERS | |
| { | |
| for layer in &layers | |
| { | |
| if name == cstring(&layer.layerName[0]) do continue outer; | |
| } | |
| fmt.eprintf("ERROR: validation layer %q not available\n", name); | |
| os.exit(1); | |
| } | |
| create_info.ppEnabledLayerNames = &VALIDATION_LAYERS[0]; | |
| create_info.enabledLayerCount = len(VALIDATION_LAYERS); | |
| fmt.println("Validation Layers Loaded"); | |
| } | |
| else | |
| { | |
| create_info.enabledLayerCount = 0; | |
| } | |
| if (vk.CreateInstance(&create_info, nil, &instance) != .SUCCESS) | |
| { | |
| fmt.eprintf("ERROR: Failed to create instance\n"); | |
| return; | |
| } | |
| fmt.println("Instance Created"); | |
| } | |
| get_extensions :: proc() -> []vk.ExtensionProperties | |
| { | |
| n_ext: u32; | |
| vk.EnumerateInstanceExtensionProperties(nil, &n_ext, nil); | |
| extensions := make([]vk.ExtensionProperties, n_ext); | |
| vk.EnumerateInstanceExtensionProperties(nil, &n_ext, raw_data(extensions)); | |
| return extensions; | |
| } | |
| create_surface :: proc(using ctx: ^Context) | |
| { | |
| surface_create_info := vk.Win32SurfaceCreateInfoKHR{}; | |
| surface_create_info.sType= .WIN32_SURFACE_CREATE_INFO_KHR; | |
| surface_create_info.hwnd = glfw.GetWin32Window(window); | |
| surface_create_info.hinstance = cast(vk.HANDLE)windows.GetModuleHandleA(nil); | |
| if res := glfw.CreateWindowSurface(instance, window, nil, &surface); res != .SUCCESS | |
| { | |
| fmt.eprintf("ERROR: Failed to create window surface\n"); | |
| os.exit(1); | |
| } | |
| } | |
| check_device_extension_support :: proc(physical_device: vk.PhysicalDevice) -> bool | |
| { | |
| ext_count: u32; | |
| vk.EnumerateDeviceExtensionProperties(physical_device, nil, &ext_count, nil); | |
| available_extensions := make([]vk.ExtensionProperties, ext_count); | |
| vk.EnumerateDeviceExtensionProperties(physical_device, nil, &ext_count, raw_data(available_extensions)); | |
| for ext in DEVICE_EXTENSIONS | |
| { | |
| found: b32; | |
| for available in &available_extensions | |
| { | |
| if cstring(&available.extensionName[0]) == ext | |
| { | |
| found = true; | |
| break; | |
| } | |
| } | |
| if !found do return false; | |
| } | |
| return true; | |
| } | |
| get_suitable_device :: proc(using ctx: ^Context) | |
| { | |
| device_count: u32; | |
| vk.EnumeratePhysicalDevices(instance, &device_count, nil); | |
| if device_count == 0 | |
| { | |
| fmt.eprintf("ERROR: Failed to find GPUs with Vulkan support\n"); | |
| os.exit(1); | |
| } | |
| devices := make([]vk.PhysicalDevice, device_count); | |
| vk.EnumeratePhysicalDevices(instance, &device_count, raw_data(devices)); | |
| suitability :: proc(using ctx: ^Context, dev: vk.PhysicalDevice) -> int | |
| { | |
| props: vk.PhysicalDeviceProperties; | |
| features: vk.PhysicalDeviceFeatures; | |
| vk.GetPhysicalDeviceProperties(dev, &props); | |
| vk.GetPhysicalDeviceFeatures(dev, &features); | |
| score := 0; | |
| if props.deviceType == .DISCRETE_GPU do score += 1000; | |
| score += cast(int)props.limits.maxImageDimension2D; | |
| if !features.geometryShader do return 0; | |
| if !check_device_extension_support(dev) do return 0; | |
| query_swap_chain_details(ctx, dev); | |
| if len(swap_chain.support.formats) == 0 || len(swap_chain.support.present_modes) == 0 do return 0; | |
| return score; | |
| } | |
| hiscore := 0; | |
| for dev in devices | |
| { | |
| score := suitability(ctx, dev); | |
| if score > hiscore | |
| { | |
| physical_device = dev; | |
| hiscore = score; | |
| } | |
| } | |
| if (hiscore == 0) | |
| { | |
| fmt.eprintf("ERROR: Failed to find a suitable GPU\n"); | |
| os.exit(1); | |
| } | |
| } | |
| find_queue_families :: proc(using ctx: ^Context) | |
| { | |
| queue_count: u32; | |
| vk.GetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_count, nil); | |
| available_queues := make([]vk.QueueFamilyProperties, queue_count); | |
| vk.GetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_count, raw_data(available_queues)); | |
| for v, i in available_queues | |
| { | |
| if .GRAPHICS in v.queueFlags && queue_indices[.Graphics] == -1 do queue_indices[.Graphics] = i; | |
| present_support: b32; | |
| vk.GetPhysicalDeviceSurfaceSupportKHR(physical_device, u32(i), surface, &present_support); | |
| if present_support && queue_indices[.Present] == -1 do queue_indices[.Present] = i; | |
| for q in queue_indices do if q == -1 do continue; | |
| break; | |
| } | |
| } | |
| create_device :: proc(using ctx: ^Context) | |
| { | |
| unique_indices: map[int]b8; | |
| defer delete(unique_indices); | |
| for i in queue_indices do unique_indices[i] = true; | |
| queue_priority := f32(1.0); | |
| queue_create_infos: [dynamic]vk.DeviceQueueCreateInfo; | |
| defer delete(queue_create_infos); | |
| for k, _ in unique_indices | |
| { | |
| queue_create_info: vk.DeviceQueueCreateInfo; | |
| queue_create_info.sType = .DEVICE_QUEUE_CREATE_INFO; | |
| queue_create_info.queueFamilyIndex = u32(queue_indices[.Graphics]); | |
| queue_create_info.queueCount = 1; | |
| queue_create_info.pQueuePriorities = &queue_priority; | |
| append(&queue_create_infos, queue_create_info); | |
| } | |
| device_features: vk.PhysicalDeviceFeatures; | |
| device_create_info: vk.DeviceCreateInfo; | |
| device_create_info.sType = .DEVICE_CREATE_INFO; | |
| device_create_info.enabledExtensionCount = u32(len(DEVICE_EXTENSIONS)); | |
| device_create_info.ppEnabledExtensionNames = &DEVICE_EXTENSIONS[0]; | |
| device_create_info.pQueueCreateInfos = raw_data(queue_create_infos); | |
| device_create_info.queueCreateInfoCount = u32(len(queue_create_infos)); | |
| device_create_info.pEnabledFeatures = &device_features; | |
| device_create_info.enabledLayerCount = 0; | |
| if vk.CreateDevice(physical_device, &device_create_info, nil, &device) != .SUCCESS | |
| { | |
| fmt.eprintf("ERROR: Failed to create logical device\n"); | |
| os.exit(1); | |
| } | |
| } | |
| query_swap_chain_details :: proc(using ctx: ^Context, dev: vk.PhysicalDevice) | |
| { | |
| vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface, &swap_chain.support.capabilities); | |
| format_count: u32; | |
| vk.GetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, nil); | |
| if format_count > 0 | |
| { | |
| swap_chain.support.formats = make([]vk.SurfaceFormatKHR, format_count); | |
| vk.GetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, raw_data(swap_chain.support.formats)); | |
| } | |
| present_mode_count: u32; | |
| vk.GetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &present_mode_count, nil); | |
| if present_mode_count > 0 | |
| { | |
| swap_chain.support.present_modes = make([]vk.PresentModeKHR, present_mode_count); | |
| vk.GetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &present_mode_count, raw_data(swap_chain.support.present_modes)); | |
| } | |
| } | |
| choose_surface_format :: proc(using ctx: ^Context) -> vk.SurfaceFormatKHR | |
| { | |
| for v in swap_chain.support.formats | |
| { | |
| if v.format == .B8G8R8A8_SRGB && v.colorSpace == .SRGB_NONLINEAR do return v; | |
| } | |
| return swap_chain.support.formats[0]; | |
| } | |
| choose_present_mode :: proc(using ctx: ^Context) -> vk.PresentModeKHR | |
| { | |
| for v in swap_chain.support.present_modes | |
| { | |
| if v == .MAILBOX do return v; | |
| } | |
| return .FIFO; | |
| } | |
| choose_swap_extent :: proc(using ctx: ^Context) -> vk.Extent2D | |
| { | |
| if (swap_chain.support.capabilities.currentExtent.width != max(u32)) | |
| { | |
| return swap_chain.support.capabilities.currentExtent; | |
| } | |
| else | |
| { | |
| width, height := glfw.GetFramebufferSize(window); | |
| extent := vk.Extent2D{u32(width), u32(height)}; | |
| extent.width = clamp(extent.width, swap_chain.support.capabilities.minImageExtent.width, swap_chain.support.capabilities.maxImageExtent.width); | |
| extent.height = clamp(extent.height, swap_chain.support.capabilities.minImageExtent.height, swap_chain.support.capabilities.maxImageExtent.height); | |
| return extent; | |
| } | |
| } | |
| create_swap_chain :: proc(using ctx: ^Context) | |
| { | |
| using ctx.swap_chain.support; | |
| swap_chain.format = choose_surface_format(ctx); | |
| swap_chain.present_mode = choose_present_mode(ctx); | |
| swap_chain.extent = choose_swap_extent(ctx); | |
| swap_chain.image_count = capabilities.minImageCount + 1; | |
| if capabilities.maxImageCount > 0 && swap_chain.image_count > capabilities.maxImageCount | |
| { | |
| swap_chain.image_count = capabilities.maxImageCount; | |
| } | |
| create_info: vk.SwapchainCreateInfoKHR; | |
| create_info.sType = .SWAPCHAIN_CREATE_INFO_KHR; | |
| create_info.surface = surface; | |
| create_info.minImageCount = swap_chain.image_count; | |
| create_info.imageFormat = swap_chain.format.format; | |
| create_info.imageColorSpace = swap_chain.format.colorSpace; | |
| create_info.imageExtent = swap_chain.extent; | |
| create_info.imageArrayLayers = 1; | |
| create_info.imageUsage = {.COLOR_ATTACHMENT}; | |
| queue_family_indices := [len(QueueFamily)]u32{u32(queue_indices[.Graphics]), u32(queue_indices[.Present])}; | |
| if queue_indices[.Graphics] != queue_indices[.Present] | |
| { | |
| create_info.imageSharingMode = .CONCURRENT; | |
| create_info.queueFamilyIndexCount = 2; | |
| create_info.pQueueFamilyIndices = &queue_family_indices[0]; | |
| } | |
| else | |
| { | |
| create_info.imageSharingMode = .EXCLUSIVE; | |
| create_info.queueFamilyIndexCount = 0; | |
| create_info.pQueueFamilyIndices = nil; | |
| } | |
| create_info.preTransform = capabilities.currentTransform; | |
| create_info.compositeAlpha = {.OPAQUE}; | |
| create_info.presentMode = swap_chain.present_mode; | |
| create_info.clipped = true; | |
| create_info.oldSwapchain = vk.SwapchainKHR{}; | |
| if res := vk.CreateSwapchainKHR(device, &create_info, nil, &swap_chain.handle); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: failed to create swap chain!\n"); | |
| os.exit(1); | |
| } | |
| vk.GetSwapchainImagesKHR(device, swap_chain.handle, &swap_chain.image_count, nil); | |
| swap_chain.images = make([]vk.Image, swap_chain.image_count); | |
| vk.GetSwapchainImagesKHR(device, swap_chain.handle, &swap_chain.image_count, raw_data(swap_chain.images)); | |
| } | |
| create_image_views :: proc(using ctx: ^Context) | |
| { | |
| using ctx.swap_chain; | |
| image_views = make([]vk.ImageView, len(images)); | |
| for _, i in images | |
| { | |
| create_info: vk.ImageViewCreateInfo; | |
| create_info.sType = .IMAGE_VIEW_CREATE_INFO; | |
| create_info.image = images[i]; | |
| create_info.viewType = .D2; | |
| create_info.format = format.format; | |
| create_info.components.r = .IDENTITY; | |
| create_info.components.g = .IDENTITY; | |
| create_info.components.b = .IDENTITY; | |
| create_info.components.a = .IDENTITY; | |
| create_info.subresourceRange.aspectMask = {.COLOR}; | |
| create_info.subresourceRange.baseMipLevel = 0; | |
| create_info.subresourceRange.levelCount = 1; | |
| create_info.subresourceRange.baseArrayLayer = 0; | |
| create_info.subresourceRange.layerCount = 1; | |
| if res := vk.CreateImageView(device, &create_info, nil, &image_views[i]); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: failed to create image view!"); | |
| os.exit(1); | |
| } | |
| } | |
| } | |
| create_graphics_pipeline :: proc(using ctx: ^Context, vs_name: string, fs_name: string) | |
| { | |
| vs_code := compile_shader(vs_name, .vertex_shader); | |
| fs_code := compile_shader(fs_name, .fragment_shader); | |
| /* | |
| vs_code, vs_ok := os.read_entire_file(vs_path); | |
| fs_code, fs_ok := os.read_entire_file(fs_path); | |
| if !vs_ok | |
| { | |
| fmt.eprintf("Error: could not load vertex shader %q\n", vs_path); | |
| os.exit(1); | |
| } | |
| if !fs_ok | |
| { | |
| fmt.eprintf("Error: could not load fragment shader %q\n", fs_path); | |
| os.exit(1); | |
| } | |
| */ | |
| defer | |
| { | |
| delete(vs_code); | |
| delete(fs_code); | |
| } | |
| vs_shader := create_shader_module(ctx, vs_code); | |
| fs_shader := create_shader_module(ctx, fs_code); | |
| defer | |
| { | |
| vk.DestroyShaderModule(device, vs_shader, nil); | |
| vk.DestroyShaderModule(device, fs_shader, nil); | |
| } | |
| vs_info: vk.PipelineShaderStageCreateInfo; | |
| vs_info.sType = .PIPELINE_SHADER_STAGE_CREATE_INFO; | |
| vs_info.stage = {.VERTEX}; | |
| vs_info.module = vs_shader; | |
| vs_info.pName = "main"; | |
| fs_info: vk.PipelineShaderStageCreateInfo; | |
| fs_info.sType = .PIPELINE_SHADER_STAGE_CREATE_INFO; | |
| fs_info.stage = {.FRAGMENT}; | |
| fs_info.module = fs_shader; | |
| fs_info.pName = "main"; | |
| shader_stages := [?]vk.PipelineShaderStageCreateInfo{vs_info, fs_info}; | |
| dynamic_states := [?]vk.DynamicState{.VIEWPORT, .SCISSOR}; | |
| dynamic_state: vk.PipelineDynamicStateCreateInfo; | |
| dynamic_state.sType = .PIPELINE_DYNAMIC_STATE_CREATE_INFO; | |
| dynamic_state.dynamicStateCount = len(dynamic_states); | |
| dynamic_state.pDynamicStates = &dynamic_states[0]; | |
| vertex_input: vk.PipelineVertexInputStateCreateInfo; | |
| vertex_input.sType = .PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
| vertex_input.vertexBindingDescriptionCount = 1; | |
| vertex_input.pVertexBindingDescriptions = &VERTEX_BINDING; | |
| vertex_input.vertexAttributeDescriptionCount = len(VERTEX_ATTRIBUTES); | |
| vertex_input.pVertexAttributeDescriptions = &VERTEX_ATTRIBUTES[0]; | |
| input_assembly: vk.PipelineInputAssemblyStateCreateInfo; | |
| input_assembly.sType = .PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | |
| input_assembly.topology = .TRIANGLE_LIST; | |
| input_assembly.primitiveRestartEnable = false; | |
| viewport: vk.Viewport; | |
| viewport.x = 0.0; | |
| viewport.y = 0.0; | |
| viewport.width = cast(f32)swap_chain.extent.width; | |
| viewport.height = cast(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; | |
| viewport_state: vk.PipelineViewportStateCreateInfo; | |
| viewport_state.sType = .PIPELINE_VIEWPORT_STATE_CREATE_INFO; | |
| viewport_state.viewportCount = 1; | |
| viewport_state.scissorCount = 1; | |
| rasterizer: vk.PipelineRasterizationStateCreateInfo; | |
| rasterizer.sType = .PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
| rasterizer.depthClampEnable = false; | |
| rasterizer.rasterizerDiscardEnable = false; | |
| rasterizer.polygonMode = .FILL; | |
| rasterizer.lineWidth = 1.0; | |
| rasterizer.cullMode = {.BACK}; | |
| rasterizer.frontFace = .CLOCKWISE; | |
| rasterizer.depthBiasEnable = false; | |
| rasterizer.depthBiasConstantFactor = 0.0; | |
| rasterizer.depthBiasClamp = 0.0; | |
| rasterizer.depthBiasSlopeFactor = 0.0; | |
| multisampling: vk.PipelineMultisampleStateCreateInfo; | |
| multisampling.sType = .PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
| multisampling.sampleShadingEnable = false; | |
| multisampling.rasterizationSamples = {._1}; | |
| multisampling.minSampleShading = 1.0; | |
| multisampling.pSampleMask = nil; | |
| multisampling.alphaToCoverageEnable = false; | |
| multisampling.alphaToOneEnable = false; | |
| color_blend_attachment: vk.PipelineColorBlendAttachmentState; | |
| color_blend_attachment.colorWriteMask = {.R, .G, .B, .A}; | |
| color_blend_attachment.blendEnable = true; | |
| color_blend_attachment.srcColorBlendFactor = .SRC_ALPHA; | |
| color_blend_attachment.dstColorBlendFactor = .ONE_MINUS_SRC_ALPHA; | |
| color_blend_attachment.colorBlendOp = .ADD; | |
| color_blend_attachment.srcAlphaBlendFactor = .ONE; | |
| color_blend_attachment.dstAlphaBlendFactor = .ZERO; | |
| color_blend_attachment.alphaBlendOp = .ADD; | |
| color_blending: vk.PipelineColorBlendStateCreateInfo; | |
| color_blending.sType = .PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | |
| color_blending.logicOpEnable = false; | |
| color_blending.logicOp = .COPY; | |
| color_blending.attachmentCount = 1; | |
| color_blending.pAttachments = &color_blend_attachment; | |
| color_blending.blendConstants[0] = 0.0; | |
| color_blending.blendConstants[1] = 0.0; | |
| color_blending.blendConstants[2] = 0.0; | |
| color_blending.blendConstants[3] = 0.0; | |
| pipeline_layout_info: vk.PipelineLayoutCreateInfo; | |
| pipeline_layout_info.sType = .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 res := vk.CreatePipelineLayout(device, &pipeline_layout_info, nil, &pipeline.layout); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create pipeline layout!\n"); | |
| os.exit(1); | |
| } | |
| create_render_pass(ctx); | |
| pipeline_info: vk.GraphicsPipelineCreateInfo; | |
| pipeline_info.sType = .GRAPHICS_PIPELINE_CREATE_INFO; | |
| pipeline_info.stageCount = 2; | |
| pipeline_info.pStages = &shader_stages[0]; | |
| pipeline_info.pVertexInputState = &vertex_input; | |
| pipeline_info.pInputAssemblyState = &input_assembly; | |
| pipeline_info.pViewportState = &viewport_state; | |
| pipeline_info.pRasterizationState = &rasterizer; | |
| pipeline_info.pMultisampleState = &multisampling; | |
| pipeline_info.pDepthStencilState = nil; | |
| pipeline_info.pColorBlendState = &color_blending; | |
| pipeline_info.pDynamicState = &dynamic_state; | |
| pipeline_info.layout = pipeline.layout; | |
| pipeline_info.renderPass = pipeline.render_pass; | |
| pipeline_info.subpass = 0; | |
| pipeline_info.basePipelineHandle = vk.Pipeline{}; | |
| pipeline_info.basePipelineIndex = -1; | |
| if res := vk.CreateGraphicsPipelines(device, 0, 1, &pipeline_info, nil, &pipeline.handle); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create graphics pipeline!\n"); | |
| os.exit(1); | |
| } | |
| } | |
| create_shader_module :: proc(using ctx: ^Context, code: []u8) -> vk.ShaderModule | |
| { | |
| create_info: vk.ShaderModuleCreateInfo; | |
| create_info.sType = .SHADER_MODULE_CREATE_INFO; | |
| create_info.codeSize = len(code); | |
| create_info.pCode = cast(^u32)raw_data(code); | |
| shader: vk.ShaderModule; | |
| if res := vk.CreateShaderModule(device, &create_info, nil, &shader); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Could not create shader module!\n"); | |
| os.exit(1); | |
| } | |
| return shader; | |
| } | |
| create_render_pass :: proc(using ctx: ^Context) | |
| { | |
| color_attachment: vk.AttachmentDescription; | |
| color_attachment.format = swap_chain.format.format; | |
| color_attachment.samples = {._1}; | |
| color_attachment.loadOp = .CLEAR; | |
| color_attachment.storeOp = .STORE; | |
| color_attachment.stencilLoadOp = .DONT_CARE; | |
| color_attachment.stencilStoreOp = .DONT_CARE; | |
| color_attachment.initialLayout = .UNDEFINED; | |
| color_attachment.finalLayout = .PRESENT_SRC_KHR; | |
| color_attachment_ref: vk.AttachmentReference; | |
| color_attachment_ref.attachment = 0; | |
| color_attachment_ref.layout = .COLOR_ATTACHMENT_OPTIMAL; | |
| subpass: vk.SubpassDescription; | |
| subpass.pipelineBindPoint = .GRAPHICS; | |
| subpass.colorAttachmentCount = 1; | |
| subpass.pColorAttachments = &color_attachment_ref; | |
| dependency: vk.SubpassDependency; | |
| dependency.srcSubpass = vk.SUBPASS_EXTERNAL; | |
| dependency.dstSubpass = 0; | |
| dependency.srcStageMask = {.COLOR_ATTACHMENT_OUTPUT}; | |
| dependency.srcAccessMask = {}; | |
| dependency.dstStageMask = {.COLOR_ATTACHMENT_OUTPUT}; | |
| dependency.dstAccessMask = {.COLOR_ATTACHMENT_WRITE}; | |
| render_pass_info: vk.RenderPassCreateInfo; | |
| render_pass_info.sType = .RENDER_PASS_CREATE_INFO; | |
| render_pass_info.attachmentCount = 1; | |
| render_pass_info.pAttachments = &color_attachment; | |
| render_pass_info.subpassCount = 1; | |
| render_pass_info.pSubpasses = &subpass; | |
| render_pass_info.dependencyCount = 1; | |
| render_pass_info.pDependencies = &dependency; | |
| if res := vk.CreateRenderPass(device, &render_pass_info, nil, &pipeline.render_pass); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create render pass!\n"); | |
| os.exit(1); | |
| } | |
| } | |
| create_framebuffers :: proc(using ctx: ^Context) | |
| { | |
| swap_chain.framebuffers = make([]vk.Framebuffer, len(swap_chain.image_views)); | |
| for v, i in swap_chain.image_views | |
| { | |
| attachments := [?]vk.ImageView{v}; | |
| framebuffer_info: vk.FramebufferCreateInfo; | |
| framebuffer_info.sType = .FRAMEBUFFER_CREATE_INFO; | |
| framebuffer_info.renderPass = pipeline.render_pass; | |
| framebuffer_info.attachmentCount = 1; | |
| framebuffer_info.pAttachments = &attachments[0]; | |
| framebuffer_info.width = swap_chain.extent.width; | |
| framebuffer_info.height = swap_chain.extent.height; | |
| framebuffer_info.layers = 1; | |
| if res := vk.CreateFramebuffer(device, &framebuffer_info, nil, &swap_chain.framebuffers[i]); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create framebuffer #%d!\n", i); | |
| os.exit(1); | |
| } | |
| } | |
| } | |
| create_command_pool :: proc(using ctx: ^Context) | |
| { | |
| pool_info: vk.CommandPoolCreateInfo; | |
| pool_info.sType = .COMMAND_POOL_CREATE_INFO; | |
| pool_info.flags = {.RESET_COMMAND_BUFFER}; | |
| pool_info.queueFamilyIndex = u32(queue_indices[.Graphics]); | |
| if res := vk.CreateCommandPool(device, &pool_info, nil, &command_pool); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create command pool!\n"); | |
| os.exit(1); | |
| } | |
| } | |
| create_command_buffers :: proc(using ctx: ^Context) | |
| { | |
| alloc_info: vk.CommandBufferAllocateInfo; | |
| alloc_info.sType = .COMMAND_BUFFER_ALLOCATE_INFO; | |
| alloc_info.commandPool = command_pool; | |
| alloc_info.level = .PRIMARY; | |
| alloc_info.commandBufferCount = len(command_buffers); | |
| if res := vk.AllocateCommandBuffers(device, &alloc_info, &command_buffers[0]); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to allocate command buffers!\n"); | |
| os.exit(1); | |
| } | |
| } | |
| record_command_buffer :: proc(using ctx: ^Context, buffer: vk.CommandBuffer, image_index: u32) | |
| { | |
| begin_info: vk.CommandBufferBeginInfo; | |
| begin_info.sType = .COMMAND_BUFFER_BEGIN_INFO; | |
| begin_info.flags = {}; | |
| begin_info.pInheritanceInfo = nil; | |
| if res := vk.BeginCommandBuffer(buffer, &begin_info); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to begin recording command buffer!\n"); | |
| os.exit(1); | |
| } | |
| render_pass_info: vk.RenderPassBeginInfo; | |
| render_pass_info.sType = .RENDER_PASS_BEGIN_INFO; | |
| render_pass_info.renderPass = pipeline.render_pass; | |
| render_pass_info.framebuffer = swap_chain.framebuffers[image_index]; | |
| render_pass_info.renderArea.offset = {0, 0}; | |
| render_pass_info.renderArea.extent = swap_chain.extent; | |
| clear_color: vk.ClearValue; | |
| clear_color.color.float32 = [4]f32{0.0, 0.0, 0.0, 1.0}; | |
| render_pass_info.clearValueCount = 1; | |
| render_pass_info.pClearValues = &clear_color; | |
| vk.CmdBeginRenderPass(buffer, &render_pass_info, .INLINE); | |
| vk.CmdBindPipeline(buffer, .GRAPHICS, pipeline.handle); | |
| vertex_buffers := [?]vk.Buffer{vertex_buffer.buffer}; | |
| offsets := [?]vk.DeviceSize{0}; | |
| vk.CmdBindVertexBuffers(buffer, 0, 1, &vertex_buffers[0], &offsets[0]); | |
| vk.CmdBindIndexBuffer(buffer, index_buffer.buffer, 0, .UINT16); | |
| 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(buffer, 0, 1, &viewport); | |
| scissor: vk.Rect2D; | |
| scissor.offset = {0, 0}; | |
| scissor.extent = swap_chain.extent; | |
| vk.CmdSetScissor(buffer, 0, 1, &scissor); | |
| vk.CmdDrawIndexed(buffer, cast(u32)index_buffer.length, 1, 0, 0, 0); | |
| vk.CmdEndRenderPass(buffer); | |
| if res := vk.EndCommandBuffer(buffer); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to record command buffer!\n"); | |
| os.exit(1); | |
| } | |
| } | |
| create_sync_objects :: proc(using ctx: ^Context) | |
| { | |
| semaphore_info: vk.SemaphoreCreateInfo; | |
| semaphore_info.sType = .SEMAPHORE_CREATE_INFO; | |
| fence_info: vk.FenceCreateInfo; | |
| fence_info.sType = .FENCE_CREATE_INFO; | |
| fence_info.flags = {.SIGNALED} | |
| for i in 0..<MAX_FRAMES_IN_FLIGHT | |
| { | |
| res := vk.CreateSemaphore(device, &semaphore_info, nil, &image_available[i]); | |
| if res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create \"image_available\" semaphore\n"); | |
| os.exit(1); | |
| } | |
| res = vk.CreateSemaphore(device, &semaphore_info, nil, &render_finished[i]); | |
| if res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create \"render_finished\" semaphore\n"); | |
| os.exit(1); | |
| } | |
| res = vk.CreateFence(device, &fence_info, nil, &in_flight[i]); | |
| if res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to create \"in_flight\" fence\n"); | |
| os.exit(1); | |
| } | |
| } | |
| } | |
| recreate_swap_chain :: proc(using ctx: ^Context) | |
| { | |
| width, height := glfw.GetFramebufferSize(window); | |
| for width == 0 && height == 0 | |
| { | |
| width, height = glfw.GetFramebufferSize(window); | |
| glfw.WaitEvents(); | |
| } | |
| vk.DeviceWaitIdle(device); | |
| cleanup_swap_chain(ctx); | |
| create_swap_chain(ctx); | |
| create_image_views(ctx); | |
| create_framebuffers(ctx); | |
| } | |
| cleanup_swap_chain :: proc(using ctx: ^Context) | |
| { | |
| for f in swap_chain.framebuffers | |
| { | |
| vk.DestroyFramebuffer(device, f, nil); | |
| } | |
| for view in swap_chain.image_views | |
| { | |
| vk.DestroyImageView(device, view, nil); | |
| } | |
| vk.DestroySwapchainKHR(device, swap_chain.handle, nil); | |
| } | |
| create_vertex_buffer :: proc(using ctx: ^Context, vertices: []Vertex) | |
| { | |
| vertex_buffer.length = len(vertices); | |
| vertex_buffer.size = cast(vk.DeviceSize)(len(vertices) * size_of(Vertex)); | |
| staging: Buffer; | |
| create_buffer(ctx, size_of(Vertex), len(vertices), {.TRANSFER_SRC}, {.HOST_VISIBLE, .HOST_COHERENT}, &staging); | |
| data: rawptr; | |
| vk.MapMemory(device, staging.memory, 0, vertex_buffer.size, {}, &data); | |
| mem.copy(data, raw_data(vertices), cast(int)vertex_buffer.size); | |
| vk.UnmapMemory(device, staging.memory); | |
| create_buffer(ctx, size_of(Vertex), len(vertices), {.VERTEX_BUFFER, .TRANSFER_DST}, {.DEVICE_LOCAL}, &vertex_buffer); | |
| copy_buffer(ctx, staging, vertex_buffer, vertex_buffer.size); | |
| vk.FreeMemory(device, staging.memory, nil); | |
| vk.DestroyBuffer(device, staging.buffer, nil); | |
| } | |
| create_index_buffer :: proc(using ctx: ^Context, indices: []u16) | |
| { | |
| index_buffer.length = len(indices); | |
| index_buffer.size = cast(vk.DeviceSize)(len(indices) * size_of(indices[0])); | |
| staging: Buffer; | |
| create_buffer(ctx, size_of(indices[0]), len(indices), {.TRANSFER_SRC}, {.HOST_VISIBLE, .HOST_COHERENT}, &staging); | |
| data: rawptr; | |
| vk.MapMemory(device, staging.memory, 0, index_buffer.size, {}, &data); | |
| mem.copy(data, raw_data(indices), cast(int)index_buffer.size); | |
| vk.UnmapMemory(device, staging.memory); | |
| create_buffer(ctx, size_of(Vertex), len(indices), {.INDEX_BUFFER, .TRANSFER_DST}, {.DEVICE_LOCAL}, &index_buffer); | |
| copy_buffer(ctx, staging, index_buffer, index_buffer.size); | |
| vk.FreeMemory(device, staging.memory, nil); | |
| vk.DestroyBuffer(device, staging.buffer, nil); | |
| } | |
| copy_buffer :: proc(using ctx: ^Context, src, dst: Buffer, size: vk.DeviceSize) | |
| { | |
| alloc_info := vk.CommandBufferAllocateInfo{ | |
| sType = .COMMAND_BUFFER_ALLOCATE_INFO, | |
| level = .PRIMARY, | |
| commandPool = command_pool, | |
| commandBufferCount = 1, | |
| }; | |
| cmd_buffer: vk.CommandBuffer; | |
| vk.AllocateCommandBuffers(device, &alloc_info, &cmd_buffer); | |
| begin_info := vk.CommandBufferBeginInfo{ | |
| sType = .COMMAND_BUFFER_BEGIN_INFO, | |
| flags = {.ONE_TIME_SUBMIT}, | |
| } | |
| vk.BeginCommandBuffer(cmd_buffer, &begin_info); | |
| copy_region := vk.BufferCopy{ | |
| srcOffset = 0, | |
| dstOffset = 0, | |
| size = size, | |
| } | |
| vk.CmdCopyBuffer(cmd_buffer, src.buffer, dst.buffer, 1, ©_region); | |
| vk.EndCommandBuffer(cmd_buffer); | |
| submit_info := vk.SubmitInfo{ | |
| sType = .SUBMIT_INFO, | |
| commandBufferCount = 1, | |
| pCommandBuffers = &cmd_buffer, | |
| }; | |
| vk.QueueSubmit(queues[.Graphics], 1, &submit_info, {}); | |
| vk.QueueWaitIdle(queues[.Graphics]); | |
| vk.FreeCommandBuffers(device, command_pool, 1, &cmd_buffer); | |
| } | |
| find_memory_type :: proc(using ctx: ^Context, type_filter: u32, properties: vk.MemoryPropertyFlags) -> u32 | |
| { | |
| mem_properties: vk.PhysicalDeviceMemoryProperties; | |
| vk.GetPhysicalDeviceMemoryProperties(physical_device, &mem_properties); | |
| for i in 0..<mem_properties.memoryTypeCount | |
| { | |
| if (type_filter & (1 << i) != 0) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties | |
| { | |
| return i; | |
| } | |
| } | |
| fmt.eprintf("Error: Failed to find suitable memory type!\n"); | |
| os.exit(1); | |
| } | |
| create_buffer :: proc(using ctx: ^Context, member_size: int, count: int, usage: vk.BufferUsageFlags, properties: vk.MemoryPropertyFlags, buffer: ^Buffer) | |
| { | |
| buffer_info := vk.BufferCreateInfo{ | |
| sType = .BUFFER_CREATE_INFO, | |
| size = cast(vk.DeviceSize)(member_size * count), | |
| usage = usage, | |
| sharingMode = .EXCLUSIVE, | |
| }; | |
| if res := vk.CreateBuffer(device, &buffer_info, nil, &buffer.buffer); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: failed to create buffer\n"); | |
| os.exit(1); | |
| } | |
| mem_requirements: vk.MemoryRequirements; | |
| vk.GetBufferMemoryRequirements(device, buffer.buffer, &mem_requirements); | |
| alloc_info := vk.MemoryAllocateInfo{ | |
| sType = .MEMORY_ALLOCATE_INFO, | |
| allocationSize = mem_requirements.size, | |
| memoryTypeIndex = find_memory_type(ctx, mem_requirements.memoryTypeBits, {.HOST_VISIBLE, .HOST_COHERENT}) | |
| }; | |
| if res := vk.AllocateMemory(device, &alloc_info, nil, &buffer.memory); res != .SUCCESS | |
| { | |
| fmt.eprintf("Error: Failed to allocate buffer memory!\n"); | |
| os.exit(1); | |
| } | |
| vk.BindBufferMemory(device, buffer.buffer, buffer.memory, 0); | |
| } |
| #version 450 | |
| layout(location = 0) in vec3 fragColor; | |
| layout(location = 0) out vec4 outColor; | |
| void main() | |
| { | |
| outColor = vec4(fragColor, 1.0); | |
| } |
| #version 450 | |
| layout(location = 0) in vec2 in_position; | |
| layout(location = 1) in vec3 in_color; | |
| layout(location = 0) out vec3 fragColor; | |
| void main() | |
| { | |
| gl_Position = vec4(in_position, 0.0, 1.0); | |
| fragColor = in_color; | |
| } |
Hey, did you bind shaderc for use with Odin ? if yes could you share the lib ?
Thank you!
The bindings are right there in my "example repo", in that same file. They might be a bit old and not work anymore, might have to check. There are not too many functions so they're easy to write manually.
The bindings are right there in my "example repo", in that same file. They might be a bit old and not work anymore, might have to check. There are not too many functions so they're easy to write manually.
can't find it to save my life can you link it please :)
Just realized you weren't talking about shadercross, but shaderc which is a different thing lol. Now it makes sense why you'd ask that here
Just realized you weren't talking about shadercross, but shaderc which is a different thing lol. Now it makes sense why you'd ask that here
Yeah I am trying to make a binding for it but it's not looking good.
@A1-Forge I have some old bindings for shaderc here along with some other libraries: https://github.com/terickson001/odin-bindings
These bindings are auto-generated with https://github.com/terickson001/bind-odin-port, a forever WIP bindings generator of mine. Just a heads up as the quality may not be ideal, but they're completely functional.
As for the first comment from @LeonardoTemperanza , this is some very beginner code of mine from following one of the various VK guides out there. Someone on the Odin discord asked for some example code and I happened to have just finished this. This code is by no means a great example, and I have much more correct code for this nowadays.
@terickson001 Thank you for the reply and links. I will look into the bind repo as I want to bind Tracy, as the current bindings are outdated, even in the creator's repo about the other bindings. I am going to update them next week as I am a bit busy at the moment and those will be of great help for what I am working on.
Thanks for the example. Just a heads up, you're not actually using "properties" in your "create_buffer" procedure, which means that all created buffers will be .HOST_VISIBLE and .HOST_COHERENT, even vertex and index buffers. This will significantly slow down your renderer.