├── LICENSE ├── README.md ├── example ├── first.jai ├── run_tree │ ├── vulkan_example.exe │ └── vulkan_example.pdb └── src │ ├── main.jai │ └── shaders │ ├── built │ ├── fullscreen_example.frag.spv │ ├── fullscreen_example.vert.spv │ ├── model_example.frag.spv │ └── model_example.vert.spv │ ├── common_descriptor_sets.glsl │ ├── fullscreen_example.frag │ ├── fullscreen_example.vert │ ├── model_example.frag │ └── model_example.vert ├── generate_code ├── generate_vulkan_code.exe ├── generate_vulkan_code.jai ├── generate_vulkan_code.pdb └── vk.xml ├── module.jai ├── vulkan_header.jai └── vulkan_loader.jai /LICENSE: -------------------------------------------------------------------------------- 1 | This software is available under 2 licenses -- choose whichever you prefer. 2 | ------------------------------------------------------------------------------ 3 | ALTERNATIVE A - MIT License 4 | Copyright (c) 2020 Rubén Osorio López 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | ------------------------------------------------------------------------------ 21 | ALTERNATIVE B - Public Domain (www.unlicense.org) 22 | This is free and unencumbered software released into the public domain. 23 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 24 | software, either in source code form or as a compiled binary, for any purpose, 25 | commercial or non-commercial, and by any means. 26 | In jurisdictions that recognize copyright laws, the author or authors of this 27 | software dedicate any and all copyright interest in the software to the public 28 | domain. We make this dedication for the benefit of the public at large and to 29 | the detriment of our heirs and successors. We intend this dedication to be an 30 | overt act of relinquishment in perpetuity of all present and future rights to 31 | this software under copyright law. 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 36 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :volcano: A Vulkan Module for Jai 2 | 3 | Just a module to use Vulkan in Jai. Both the header and the loader are generated from vk.xml by the code in this repository. 4 | 5 | ## [Header](vulkan_header.jai) 6 | 7 | Not much to say about [the header](vulkan_header.jai). It should just pick up the types from `vk.xml` and try to format them in Jai with some hopefully useful comments. 8 | 9 | ## [Loader](vulkan_loader.jai) 10 | 11 | There are instructions for usage of the loader in [the file itself](vulkan_loader.jai). It defines globals that will hold the procedure pointers to the API procedures. Then by using the three `load_vulkan_*` procedures you'll be able to fill all these global procedure pointers and start using them. 12 | 13 | ## Notes 14 | 15 | **This module is not super active** at the moment because I'm not personally using Vulkan too much these days. It's somewhat likely to fall behind in which Vulkan version it currently has on the provided header and loader. In a similar fashion, most of this module was written in the very early days of Jai's closed beta so it's possible that some things in it don't compile with the latest versions of the language/compiler. 16 | 17 | The way `vk.xml` is modified doesn't make it particularly easy to keep track of all of the stuff they are adding either, and since I'm not using Vulkan too much I can't justify the bandwidth of reacting to everything they add that doesn't follow previous conventions. The code that parses `vk.xml` was the first thing I wrote in the language, and can be done much better, while also accounting for all the changes that the xml file has had since then. 18 | 19 | ## License 20 | 21 | I want people to be able to just use this without thinking about licensing, although give me a shout if you do use it and attribution is appreciated 😊. Replicating STB's (https://github.com/nothings/stb) licensing stuff where you can choose to use public domain or MIT. 22 | -------------------------------------------------------------------------------- /example/first.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "String"; 3 | #import "File"; 4 | #import "Compiler"; 5 | #import "Process"; 6 | 7 | #run 8 | { 9 | print("\n"); 10 | 11 | if shader_compiler_available() 12 | { 13 | build_shader("src/shaders/fullscreen_example.vert", "src/shaders/built/fullscreen_example.vert.spv"); 14 | build_shader("src/shaders/fullscreen_example.frag", "src/shaders/built/fullscreen_example.frag.spv"); 15 | build_shader("src/shaders/model_example.vert", "src/shaders/built/model_example.vert.spv"); 16 | build_shader("src/shaders/model_example.frag", "src/shaders/built/model_example.frag.spv"); 17 | } 18 | else 19 | { 20 | print("Not building shaders because we couldn't find a compiler to do it\n"); 21 | } 22 | 23 | set_build_options_dc(.{do_output=false}); 24 | 25 | main_workspace := compiler_create_workspace(); 26 | if !main_workspace 27 | { 28 | print("Couldn't create workspace to build the application!!!\n"); 29 | exit(0); 30 | } 31 | 32 | build_options := get_build_options(); 33 | build_options.output_type = .EXECUTABLE; 34 | build_options.output_executable_name = "vulkan_example"; 35 | build_options.output_path = join(#filepath, "run_tree/"); 36 | make_directory_if_it_does_not_exist(build_options.output_path); 37 | 38 | new_import_path := string.[ 39 | "../../", // @@NOTE: Relative path to wherever this module is 40 | "", 41 | ]; 42 | new_import_path[1] = build_options.import_path[0]; 43 | build_options.import_path = new_import_path; 44 | 45 | print("\n"); 46 | 47 | set_build_options(build_options, main_workspace); 48 | 49 | compiler_begin_intercept(main_workspace); 50 | { 51 | add_build_file("./src/main.jai", main_workspace); 52 | 53 | success_building := false; 54 | while true { 55 | message := compiler_wait_for_message(); 56 | if !message break; 57 | 58 | if message.kind == .COMPLETE 59 | { 60 | success_building = true; 61 | break; 62 | } 63 | } 64 | 65 | if !success_building 66 | { 67 | print("We didn't succeed at building our program in workspace\n\n%\n\n", main_workspace); 68 | exit(0); 69 | } 70 | } 71 | compiler_end_intercept(main_workspace); 72 | 73 | } 74 | 75 | 76 | 77 | shader_compiler_available :: () -> bool 78 | { 79 | success, 80 | exit_code, 81 | output_string, 82 | error_string := os_run_command("glslc", "--version"); 83 | if success print("\n"); 84 | return success; 85 | } 86 | 87 | build_shader :: (filename : string, output : string) #expand 88 | { 89 | success, 90 | exit_code, 91 | output_string, 92 | error_string := os_run_command("glslc", filename, "-o", output); 93 | if !success 94 | print("Couldn't build shader %!! Error: %\n", filename, error_string); 95 | else 96 | print("Built % to %\n", filename, output); 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /example/run_tree/vulkan_example.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/example/run_tree/vulkan_example.exe -------------------------------------------------------------------------------- /example/run_tree/vulkan_example.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/example/run_tree/vulkan_example.pdb -------------------------------------------------------------------------------- /example/src/main.jai: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file tries to be a simple example of how to do some stuff in Vulkan. It's not trying 4 | to be a super robust solution for a Vulkan renderer but a starting point to see how things are 5 | kinda done with the API and with the current version of this Vulkan module for Jai. 6 | 7 | There's some things that could be improved upon that should be annotated throughout the code, some 8 | others are in a decent place already to the point you could reuse some of that for your own code. 9 | 10 | Feel free to request changes here :) 11 | 12 | */ 13 | 14 | #import "Basic"; 15 | #import "Math"; 16 | #import "File"; 17 | #import "String"; 18 | #import "Windows"; 19 | #import "Input"; 20 | #import "Window_Creation"; 21 | #import "Debug"; 22 | #import,dir "../../../osor_vulkan"; 23 | 24 | 25 | 26 | main :: () 27 | { 28 | window_handle := create_window(window_name="Vulkan Example", width=1920, height=1080); 29 | 30 | // # Creates the instance, surface and device 31 | vulkan_context : Vulkan_Context; 32 | vulkan_init(*vulkan_context, window_handle); 33 | defer vulkan_deinit(*vulkan_context); 34 | 35 | // # Create the swapchain, its images and its views 36 | success := vulkan_create_swapchain(*vulkan_context); 37 | assert(success); 38 | 39 | // # Creating the command pools we can use for each in-flight frame 40 | vulkan_create_command_pools(*vulkan_context); 41 | 42 | // # Load the vertex and fragment shaders for this example 43 | fullscreen_vertex_shader_module := vulkan_create_shader_module(*vulkan_context, Assets.Example_Fullscreen_Vertex_Shader_Code); 44 | fullscreen_fragment_shader_module := vulkan_create_shader_module(*vulkan_context, Assets.Example_Fullscreen_Fragment_Shader_Code); 45 | defer vulkan_destroy_shader_module(*vulkan_context, *fullscreen_vertex_shader_module); 46 | defer vulkan_destroy_shader_module(*vulkan_context, *fullscreen_fragment_shader_module); 47 | model_vertex_shader_module := vulkan_create_shader_module(*vulkan_context, Assets.Example_Model_Vertex_Shader_Code); 48 | model_fragment_shader_module := vulkan_create_shader_module(*vulkan_context, Assets.Example_Model_Fragment_Shader_Code); 49 | defer vulkan_destroy_shader_module(*vulkan_context, *model_vertex_shader_module); 50 | defer vulkan_destroy_shader_module(*vulkan_context, *model_fragment_shader_module); 51 | 52 | // # Create a Graphics Pipeline (which needs a layout and a compatible render pass to the one that's going to use it) 53 | fullscreen_graphics_pipeline, 54 | fullscreen_compatible_render_pass, 55 | fullscreen_pipeline_layout, 56 | fullscreen_descriptor_set_layouts := vulkan_example_create_graphics_pipeline(*vulkan_context, 57 | fullscreen_vertex_shader_module, 58 | fullscreen_fragment_shader_module, 59 | has_vertex_stuff = false); 60 | defer vulkan_destroy_graphics_pipeline(*vulkan_context, 61 | *fullscreen_graphics_pipeline, 62 | *fullscreen_compatible_render_pass, 63 | *fullscreen_pipeline_layout, 64 | fullscreen_descriptor_set_layouts); 65 | 66 | model_graphics_pipeline, 67 | model_compatible_render_pass, 68 | model_pipeline_layout, 69 | model_descriptor_set_layouts := vulkan_example_create_graphics_pipeline(*vulkan_context, 70 | model_vertex_shader_module, 71 | model_fragment_shader_module, 72 | has_vertex_stuff = true); 73 | defer vulkan_destroy_graphics_pipeline(*vulkan_context, 74 | *model_graphics_pipeline, 75 | *model_compatible_render_pass, 76 | *model_pipeline_layout, 77 | model_descriptor_set_layouts); 78 | 79 | common_pipeline_layout := model_pipeline_layout; 80 | per_frame_descriptor_set_layout := model_descriptor_set_layouts[0]; 81 | per_view_descriptor_set_layout := model_descriptor_set_layouts[1]; 82 | per_object_descriptor_set_layout := model_descriptor_set_layouts[2]; 83 | 84 | // # Creating the render pass tha we actually want to use to render 85 | render_pass := vulkan_example_create_render_pass(*vulkan_context); 86 | defer vulkan_destroy_render_pass(*vulkan_context, *render_pass); 87 | 88 | // # Creating the framebuffers for the images on the swapchains 89 | framebuffers := vulkan_example_make_framebuffers_for_renderpass_to_render_to_current_swapchain(*vulkan_context, render_pass); 90 | defer vulkan_destroy_framebuffers(*vulkan_context, *framebuffers); 91 | 92 | // # Creating buffer for our per-frame per-buffer and per-object data for the shader 93 | per_frame_uniform_buffers, per_frame_uniform_buffer_memories := vulkan_create_uniform_buffers(*vulkan_context, Per_Frame_Data); 94 | defer vulkan_destroy_uniform_buffers(*vulkan_context, *per_frame_uniform_buffers, *per_frame_uniform_buffer_memories); 95 | per_view_uniform_buffers, per_view_uniform_buffer_memories := vulkan_create_uniform_buffers(*vulkan_context, Per_View_Data); 96 | defer vulkan_destroy_uniform_buffers(*vulkan_context, *per_view_uniform_buffers, *per_view_uniform_buffer_memories); 97 | per_object_uniform_buffers, per_object_uniform_buffer_memories := vulkan_create_uniform_buffers(*vulkan_context, Per_Object_Data); 98 | defer vulkan_destroy_uniform_buffers(*vulkan_context, *per_object_uniform_buffers, *per_object_uniform_buffer_memories); 99 | 100 | // # Making a vertex and index buffer with some contents to draw! 101 | vertices, indices := make_vertex_and_index_buffer_for_cube(); 102 | vertex_buffer, vertex_buffer_memory, 103 | index_buffer, index_buffer_memory, 104 | semaphore_vertex_data_is_ready, 105 | fence_vertex_data_is_ready := vulkan_make_vertex_and_index_buffer(*vulkan_context, vertices, indices); 106 | defer 107 | { 108 | vulkan_destroy_buffer(*vulkan_context, *vertex_buffer, *vertex_buffer_memory); 109 | vulkan_destroy_buffer(*vulkan_context, *index_buffer, *index_buffer_memory); 110 | vulkan_destroy_semaphore(*vulkan_context, *semaphore_vertex_data_is_ready); 111 | vulkan_destroy_fence(*vulkan_context, *fence_vertex_data_is_ready); 112 | } 113 | waited_for_vertex_buffer := false; 114 | 115 | // # Main Loop 116 | last_time := get_time() - 1.0/60.0; 117 | need_to_recreate_framebuffers := false; 118 | run_main_loop := true; 119 | while run_main_loop 120 | { 121 | reset_temporary_storage(); 122 | 123 | mouse_position_x : s32; 124 | mouse_position_y : s32; 125 | { 126 | mouse_x, mouse_y, success := get_mouse_pointer_position(); 127 | if success { 128 | mouse_position_x = xx mouse_x; 129 | mouse_position_y = xx mouse_y; 130 | } 131 | } 132 | 133 | now := get_time(); 134 | delta_time : float64 = now - last_time; 135 | last_time = now; 136 | 137 | update_window_events(); 138 | for event : events_this_frame 139 | { 140 | if (event.type == Event_Type.QUIT) || (event.key_code == Key_Code.ESCAPE && event.key_pressed) 141 | { 142 | run_main_loop = false; 143 | break; 144 | } 145 | } 146 | if !run_main_loop break; 147 | 148 | window_resizes := get_window_resizes(); 149 | resize_record : *Window_Resize_Record; 150 | for * window_resizes 151 | { 152 | if it.window == window_handle 153 | { 154 | resize_record == it; 155 | break; 156 | } 157 | } 158 | if resize_record 159 | { 160 | success := vulkan_create_swapchain(*vulkan_context, width = resize_record.width, height = resize_record.height); 161 | need_to_recreate_framebuffers = success; 162 | } 163 | 164 | // ## Get image from the swapchain we can render to 165 | can_render, 166 | swapchain_image, 167 | swapchain_image_view, 168 | swapchain_image_index, 169 | image_acquired_semaphore, 170 | rendering_finished_semaphore, 171 | rendering_finished_fence, 172 | recreated_swapchain := vulkan_begin_frame_and_acquire_swapchain_image_to_render_to(*vulkan_context); 173 | 174 | // ## Ignore rendering if we can't render (duh...) 175 | if !can_render 176 | continue; 177 | 178 | // ## Recreate Framebuffers for render pass if swapchain has changed and has new images 179 | need_to_recreate_framebuffers = need_to_recreate_framebuffers || recreated_swapchain; 180 | if need_to_recreate_framebuffers 181 | { 182 | need_to_recreate_framebuffers = false; 183 | framebuffers = vulkan_example_make_framebuffers_for_renderpass_to_render_to_current_swapchain(*vulkan_context, render_pass, previous=*framebuffers); 184 | } 185 | 186 | // ## Get the framebuffer for the correct swapchain image 187 | framebuffer := framebuffers[swapchain_image_index]; 188 | 189 | // ## Get and Begin Command Buffer 190 | command_buffer := vulkan_get_new_command_buffer_for_graphics_queue(*vulkan_context); 191 | { 192 | begin_info : VkCommandBufferBeginInfo; 193 | begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 194 | result := vkBeginCommandBuffer(command_buffer, *begin_info); 195 | assert(result == VK_SUCCESS); 196 | } 197 | 198 | // ## Record beginning of Render Pass 199 | { 200 | begin_info : VkRenderPassBeginInfo; 201 | begin_info.renderPass = render_pass; 202 | begin_info.framebuffer = framebuffer; 203 | begin_info.renderArea.extent = vulkan_context.current_swapchain_image_extents; 204 | begin_info.clearValueCount = 1; 205 | clear_value : VkClearValue; 206 | clear_value.color.float32_[0] = 0.9; 207 | clear_value.color.float32_[1] = 0.0; 208 | clear_value.color.float32_[2] = 0.0; 209 | clear_value.color.float32_[3] = 1.0; 210 | begin_info.pClearValues = *clear_value; 211 | vkCmdBeginRenderPass(command_buffer, *begin_info, VK_SUBPASS_CONTENTS_INLINE); 212 | } 213 | 214 | // ## Updating our buffer with per-frame data 215 | per_frame_data : Per_Frame_Data; 216 | per_frame_data.resolution_x = xx vulkan_context.current_swapchain_image_extents.width; 217 | per_frame_data.resolution_y = xx vulkan_context.current_swapchain_image_extents.height; 218 | per_frame_data.time = cast(float32) now; 219 | per_frame_data.delta_time = cast(float32) delta_time; 220 | per_frame_data.mouse_position_x = mouse_position_x; 221 | per_frame_data.mouse_position_y = mouse_position_y; 222 | 223 | per_view_data : Per_View_Data; 224 | camera_position := make_vector3(0,0,5); 225 | per_view_data.view_from_world_matrix = prepare_matrix_for_shader(make_translation_matrix4(-camera_position)); 226 | per_view_data.projection_from_view_matrix = prepare_matrix_for_shader( 227 | make_projection_matrix_for_vulkan(fov_vertical_in_degrees = 90.0, 228 | aspect_ratio_horizontal_over_vertical = cast(float)per_frame_data.resolution_x / cast(float) per_frame_data.resolution_y, 229 | near = 0.1, 230 | far = 100.0)); 231 | 232 | // ## Allocating a descriptor set for per-frame data, make it point to our buffer and record binding it 233 | vulkan_example_update_and_bind_uniform_buffer(*vulkan_context, 234 | command_buffer, 235 | common_pipeline_layout, 236 | per_frame_descriptor_set_layout, 237 | per_frame_uniform_buffers[swapchain_image_index], 238 | per_frame_uniform_buffer_memories[swapchain_image_index], 239 | per_frame_data, 240 | set = 0, 241 | binding = 0); 242 | 243 | vulkan_example_update_and_bind_uniform_buffer(*vulkan_context, 244 | command_buffer, 245 | common_pipeline_layout, 246 | per_view_descriptor_set_layout, 247 | per_view_uniform_buffers[swapchain_image_index], 248 | per_view_uniform_buffer_memories[swapchain_image_index], 249 | per_view_data, 250 | set = 1, 251 | binding = 0); 252 | 253 | 254 | // ## Record the scissor and viewport we're going to use 255 | { 256 | scissor_rect : VkRect2D; 257 | scissor_rect.extent = vulkan_context.current_swapchain_image_extents; 258 | vkCmdSetScissor(command_buffer, 0, 1, *scissor_rect); 259 | viewport : VkViewport; 260 | viewport.x = 0.0; 261 | viewport.y = 0.0; 262 | viewport.width = cast(float32) vulkan_context.current_swapchain_image_extents.width; 263 | viewport.height = cast(float32) vulkan_context.current_swapchain_image_extents.height; 264 | viewport.minDepth = 0.0; 265 | viewport.maxDepth = 1.0; 266 | vkCmdSetViewport(command_buffer, 0, 1, *viewport); 267 | } 268 | 269 | // ## Record the binding of our pipeline and drawing some fullscreen stuff 270 | { 271 | vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, fullscreen_graphics_pipeline); 272 | vkCmdDraw(command_buffer, 3, 1, 0, 0); 273 | } 274 | 275 | // ## We record now drawing something with vertices 276 | { 277 | per_object_data : Per_Object_Data; 278 | per_object_data.world_from_model_matrix = prepare_matrix_for_shader(make_scale_matrix4(make_vector3(0.3,0.3,0.3)) * make_translation_matrix4(make_vector3(0,0,-3))); 279 | vulkan_example_update_and_bind_uniform_buffer(*vulkan_context, 280 | command_buffer, 281 | common_pipeline_layout, 282 | per_object_descriptor_set_layout, 283 | per_object_uniform_buffers[swapchain_image_index], 284 | per_object_uniform_buffer_memories[swapchain_image_index], 285 | per_object_data, 286 | set = 2, 287 | binding = 0); 288 | 289 | vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, model_graphics_pipeline); 290 | zero_device_size : VkDeviceSize = 0; 291 | vkCmdBindVertexBuffers(command_buffer, 292 | firstBinding = 0, 293 | bindingCount = 1, 294 | pBuffers = *vertex_buffer, 295 | pOffsets = *zero_device_size); 296 | vkCmdBindIndexBuffer(command_buffer, 297 | buffer = index_buffer, 298 | offset = zero_device_size, 299 | indexType = VK_INDEX_TYPE_UINT32); 300 | vkCmdDrawIndexed(command_buffer, 301 | indexCount = cast(u32) indices.count, 302 | instanceCount = cast(u32) 1, 303 | firstIndex = cast(u32) 0, 304 | vertexOffset = 0, 305 | firstInstance = cast(u32) 0); 306 | } 307 | 308 | // ## Record ending of Render Pass 309 | vkCmdEndRenderPass(command_buffer); 310 | 311 | // ## End Command Buffer 312 | { 313 | end_result := vkEndCommandBuffer(command_buffer); 314 | assert(end_result == VK_SUCCESS); 315 | } 316 | 317 | // ## Submit the Command Buffer 318 | { 319 | semaphores_to_wait_for := new_temporary_array(VkSemaphore); 320 | wait_stages := new_temporary_array(VkPipelineStageFlags); 321 | array_add(*semaphores_to_wait_for, image_acquired_semaphore); 322 | array_add(*wait_stages, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); 323 | if !waited_for_vertex_buffer 324 | { 325 | array_add(*semaphores_to_wait_for, semaphore_vertex_data_is_ready); 326 | array_add(*wait_stages, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT); 327 | waited_for_vertex_buffer = true; 328 | } 329 | assert(semaphores_to_wait_for.count == wait_stages.count); 330 | submit_info : VkSubmitInfo; 331 | submit_info.waitSemaphoreCount = cast(u32) semaphores_to_wait_for.count; 332 | submit_info.pWaitSemaphores = semaphores_to_wait_for.data; 333 | submit_info.pWaitDstStageMask = wait_stages.data; 334 | submit_info.commandBufferCount = 1; 335 | submit_info.pCommandBuffers = *command_buffer; 336 | submit_info.signalSemaphoreCount = 1; 337 | submit_info.pSignalSemaphores = *rendering_finished_semaphore; 338 | 339 | result := vkQueueSubmit(vulkan_context.graphics_queue, 340 | submitCount = 1, 341 | *submit_info, 342 | fence = rendering_finished_fence); 343 | assert(result == VK_SUCCESS); 344 | } 345 | 346 | // ## And presenting the image to the screen 347 | recreated_swapchain_when_presenting := vulkan_end_frame_and_present_image(*vulkan_context, swapchain_image_index, rendering_finished_semaphore); 348 | need_to_recreate_framebuffers = need_to_recreate_framebuffers || recreated_swapchain_when_presenting; 349 | } 350 | 351 | // # Wait for idle after exiting main loop to make sure we destroy things after we stopped using them 352 | vkDeviceWaitIdle(vulkan_context.device); 353 | } 354 | 355 | 356 | 357 | Per_Frame_Data :: struct 358 | { 359 | resolution_x : s32; 360 | resolution_y : s32; 361 | time : float32; 362 | delta_time : float32; 363 | 364 | mouse_position_x : s32; 365 | mouse_position_y : s32; 366 | something : s32; 367 | something_else : s32; 368 | } 369 | 370 | 371 | Per_View_Data :: struct 372 | { 373 | view_from_world_matrix : Matrix4; 374 | projection_from_view_matrix : Matrix4; 375 | } 376 | 377 | 378 | Per_Object_Data :: struct 379 | { 380 | world_from_model_matrix : Matrix4; 381 | } 382 | 383 | 384 | Vertex :: struct 385 | { 386 | position : Vector3; 387 | color : Vector3; 388 | } 389 | 390 | 391 | Assets :: struct 392 | { 393 | #insert #run insert_array_of_bytes("Example_Fullscreen_Vertex_Shader_Code", "src/shaders/built/fullscreen_example.vert.spv"); 394 | #insert #run insert_array_of_bytes("Example_Fullscreen_Fragment_Shader_Code", "src/shaders/built/fullscreen_example.frag.spv"); 395 | 396 | #insert #run insert_array_of_bytes("Example_Model_Vertex_Shader_Code", "src/shaders/built/model_example.vert.spv"); 397 | #insert #run insert_array_of_bytes("Example_Model_Fragment_Shader_Code", "src/shaders/built/model_example.frag.spv"); 398 | } 399 | 400 | 401 | make_vertex_and_index_buffer_for_cube :: () -> [8] Vertex, [6*2*3] u32 402 | { 403 | vertices : [8] Vertex; 404 | vertices[0].position = make_vector3(-1.0, +1.0, -1.0); 405 | vertices[1].position = make_vector3(+1.0, +1.0, -1.0); 406 | vertices[2].position = make_vector3(-1.0, +1.0, +1.0); 407 | vertices[3].position = make_vector3(+1.0, +1.0, +1.0); 408 | vertices[4].position = make_vector3(-1.0, -1.0, -1.0); 409 | vertices[5].position = make_vector3(+1.0, -1.0, -1.0); 410 | vertices[6].position = make_vector3(-1.0, -1.0, +1.0); 411 | vertices[7].position = make_vector3(+1.0, -1.0, +1.0); 412 | vertices[0].color = make_vector3(0.0, 1.0, 0.0); 413 | vertices[1].color = make_vector3(1.0, 1.0, 0.0); 414 | vertices[2].color = make_vector3(0.0, 1.0, 1.0); 415 | vertices[3].color = make_vector3(1.0, 1.0, 1.0); 416 | vertices[4].color = make_vector3(0.0, 0.0, 0.0); 417 | vertices[5].color = make_vector3(1.0, 0.0, 0.0); 418 | vertices[6].color = make_vector3(0.0, 0.0, 1.0); 419 | vertices[7].color = make_vector3(1.0, 0.0, 1.0); 420 | indices : [6*2*3] u32; 421 | counter := 0; 422 | tri(0,2,1); tri(2,3,1); 423 | tri(2,6,3); tri(3,6,7); 424 | tri(1,5,0); tri(0,5,4); 425 | tri(0,4,2); tri(2,4,6); 426 | tri(3,5,1); tri(3,7,5); 427 | tri(6,4,5); tri(6,5,7); 428 | return vertices, indices; 429 | tri :: (a : u32, b : u32, c : u32) #expand 430 | { 431 | `indices[`counter] = a; 432 | `indices[`counter+1] = b; 433 | `indices[`counter+2] = c; 434 | `counter += 3; 435 | } 436 | } 437 | 438 | 439 | 440 | 441 | 442 | // 443 | // @@NOTE: There's compile time code that will insert these extensions 444 | // in the Extension_Data members of the Vulkan_Context. This means that 445 | // selecting an extension we might want to use should be as easy as adding 446 | // it to this array and then check, for example, for is_enabled.ray_tracing 447 | // 448 | Extensions_We_Might_Want :: string.[ 449 | "VK_KHR_swapchain", 450 | "VK_KHR_ray_tracing", 451 | "VK_KHR_deferred_host_operations", 452 | "VK_KHR_pipeline_library", 453 | "VK_NV_mesh_shader" 454 | ]; 455 | 456 | using Vulkan_Context :: struct 457 | { 458 | instance : VkInstance; 459 | allocator_callbacks : VkAllocationCallbacks; 460 | vulkan_allocator : *VkAllocationCallbacks = null; 461 | debug_utils_messenger : VkDebugUtilsMessengerEXT; 462 | current_frame : s64 = 0; 463 | 464 | using physical_device_data : Physical_Device_Data; 465 | Physical_Device_Data :: struct 466 | { 467 | physical_device : VkPhysicalDevice; 468 | physical_device_properties : VkPhysicalDeviceProperties; 469 | physical_device_properties_vulkan_1_2 : VkPhysicalDeviceVulkan12Properties; 470 | physical_device_features : VkPhysicalDeviceFeatures; 471 | physical_device_memory_properties : VkPhysicalDeviceMemoryProperties; 472 | 473 | queue_family_indices : Queue_Family_Indices; 474 | Queue_Family_Indices :: struct 475 | { 476 | graphics_family_index := -1; 477 | graphics_queue_index := -1; 478 | async_family_index := -1; 479 | async_queue_index := -1; 480 | transfer_family_index := -1; 481 | transfer_queue_index := -1; 482 | } 483 | } 484 | 485 | using surface_data : Surface_Data; 486 | Surface_Data :: struct 487 | { 488 | surface : VkSurfaceKHR; 489 | surface_capabilities : VkSurfaceCapabilitiesKHR; 490 | surface_formats : [] VkSurfaceFormatKHR; 491 | surface_present_modes : [] VkPresentModeKHR; 492 | } 493 | 494 | using extensions : Extension_Data; 495 | Extension_Data :: struct 496 | { 497 | is_enabled : Is_Set; 498 | is_supported : Is_Set; 499 | Is_Set :: struct 500 | { 501 | #insert #run () -> string 502 | { 503 | builder: String_Builder; 504 | defer free_buffers(*builder); 505 | for Extensions_We_Might_Want 506 | { 507 | print_to_builder(*builder, "% := false;\n", cleanup_extension_name(it)); 508 | } 509 | return builder_to_string(*builder); 510 | }(); 511 | } 512 | } 513 | 514 | using logical_device_data : Logical_Device_Data; 515 | Logical_Device_Data :: struct 516 | { 517 | device : VkDevice; 518 | graphics_queue : VkQueue; 519 | async_queue : VkQueue; 520 | transfer_queue : VkQueue; 521 | } 522 | 523 | using swapchain_data : Swapchain_Data; 524 | Swapchain_Data :: struct 525 | { 526 | swapchain : VkSwapchainKHR; 527 | present_mode : VkPresentModeKHR; 528 | swapchain_format : VkFormat; 529 | swapchain_color_space : VkColorSpaceKHR; 530 | swapchain_images : [] VkImage; 531 | swapchain_image_views : [] VkImageView; 532 | current_swapchain_image_extents : VkExtent2D; 533 | 534 | acquired_swapchain_image_semaphores : [] VkSemaphore; 535 | rendering_finished_to_swapchain_image_semaphores : [] VkSemaphore; 536 | rendering_finished_to_swapchain_image_fences : [] VkFence; 537 | frame_numbers_per_image : [] s64; 538 | current_swapchain_semaphore_index := 0; 539 | 540 | should_recreate_swapchain_when_we_get_a_chance := false; 541 | } 542 | 543 | Queue_Type :: enum 544 | { 545 | Graphics :: 0; 546 | Async_Compute :: 1; 547 | Transfer :: 2; 548 | } 549 | 550 | using command_pool_data : Command_Pool_Data; 551 | Command_Pool_Data :: struct 552 | { 553 | current_command_pool_index := 0; 554 | 555 | command_pools_per_type : [3] Per_Type; 556 | Per_Type :: struct 557 | { 558 | command_pools : [] VkCommandPool; 559 | used_command_buffers : [] [..] VkCommandBuffer; 560 | free_command_buffers : [] [..] VkCommandBuffer; 561 | } 562 | } 563 | 564 | using descriptor_data : Descriptor_Data; 565 | Descriptor_Data :: struct 566 | { 567 | MAX_SETS_PER_POOL :: 1024; 568 | Pool_And_Frame :: struct 569 | { 570 | pool : VkDescriptorPool; 571 | last_frame_that_used_it : s64; 572 | } 573 | current_descriptor_pool : Pool_And_Frame; 574 | full_descriptor_pools : [..] Pool_And_Frame; 575 | empty_descriptor_pools : [..] Pool_And_Frame; 576 | } 577 | } 578 | 579 | 580 | vulkan_init :: (using vulkan_context : *Vulkan_Context, 581 | window_handle : Window_Handle, 582 | $want_validation_layers := true, 583 | $want_debug_extensions := true) 584 | { 585 | // # Loader Procedures 586 | #if OS == .WINDOWS 587 | { 588 | vulkan_library := LoadLibraryA("vulkan-1.dll"); 589 | load_vulkan_loader_procedures(vulkan_library, GetProcAddress); 590 | } 591 | else #assert(false); 592 | 593 | 594 | // # Instance Layers 595 | enabled_layers := new_temporary_array(string); 596 | validation_enabled := false; 597 | { 598 | desired_instance_layers := new_temporary_array(string); 599 | #if want_validation_layers array_add(*desired_instance_layers, "VK_LAYER_KHRONOS_validation"); 600 | 601 | properties := vulkan_fill_array(VkLayerProperties, vkEnumerateInstanceLayerProperties); 602 | for desired : desired_instance_layers 603 | for property : properties 604 | if desired == to_string(property.layerName.data) 605 | array_add(*enabled_layers, desired); 606 | 607 | validation_enabled = array_contains_string(enabled_layers, "VK_LAYER_KHRONOS_validation"); 608 | } 609 | 610 | 611 | // # Instance Extensions 612 | enabled_instance_extensions := new_temporary_array(string); 613 | debug_extension_enabled := false; 614 | { 615 | desired_instance_extensions := new_temporary_array(string); 616 | array_add(*desired_instance_extensions, "VK_KHR_surface"); 617 | #if OS == .WINDOWS 618 | array_add(*desired_instance_extensions, "VK_KHR_win32_surface"); 619 | else #assert(false); 620 | #if want_debug_extensions array_add(*desired_instance_extensions, "VK_EXT_debug_utils"); 621 | 622 | instance_properties := vulkan_fill_array(VkExtensionProperties, vkEnumerateInstanceExtensionProperties, null); 623 | for desired : desired_instance_extensions 624 | for property : instance_properties 625 | if desired == to_string(property.extensionName.data) 626 | array_add(*enabled_instance_extensions, desired); 627 | 628 | debug_extension_enabled = array_contains_string(enabled_instance_extensions, "VK_EXT_debug_utils"); 629 | assert(array_contains_string(enabled_instance_extensions, "VK_KHR_surface")); 630 | #if OS == .WINDOWS 631 | assert(array_contains_string(enabled_instance_extensions, "VK_KHR_win32_surface")); 632 | else #assert(false); 633 | } 634 | 635 | 636 | // # Create Instance 637 | { 638 | app_info : VkApplicationInfo; 639 | app_info.pApplicationName = "Vulkan Example"; 640 | app_info.pEngineName = "Unireal-Maker"; 641 | app_info.apiVersion = VK_MAKE_API_VERSION(0,1,2,135); 642 | 643 | create_info : VkInstanceCreateInfo; 644 | create_info.pApplicationInfo = *app_info; 645 | create_info.enabledLayerCount = cast(u32) enabled_layers.count; 646 | create_info.ppEnabledLayerNames = strings_to_cstrings(enabled_layers).data; 647 | create_info.enabledExtensionCount = cast(u32) enabled_instance_extensions.count; 648 | create_info.ppEnabledExtensionNames = strings_to_cstrings(enabled_instance_extensions).data; 649 | 650 | result := vkCreateInstance(*create_info, vulkan_allocator, *instance); 651 | assert(result == VK_SUCCESS); 652 | } 653 | 654 | 655 | // # Instance Procedures 656 | load_vulkan_instance_procedures(instance); 657 | 658 | 659 | // # Set-up debug extensions 660 | if debug_extension_enabled 661 | { 662 | debug_utils_user_data := null; 663 | debug_utils_callback :: (message_severity : VkDebugUtilsMessageSeverityFlagBitsEXT, 664 | message_types : VkDebugUtilsMessageTypeFlagsEXT, 665 | callback_data : *VkDebugUtilsMessengerCallbackDataEXT, 666 | user_data : *void) -> VkBool32 #c_call 667 | { 668 | is_error : bool = xx message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 669 | is_warning : bool = xx message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; 670 | is_info : bool = xx message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; 671 | is_verbose : bool = xx message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; 672 | is_general : bool = xx message_types & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; 673 | is_validation : bool = xx message_types & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; 674 | is_performance : bool = xx message_types & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 675 | message := callback_data.pMessage; 676 | should_log := (!is_info && !is_verbose); 677 | if should_log 678 | { 679 | severity_string := ifx is_error "[ERROR]" 680 | else ifx is_warning "[WARNING]" 681 | else ifx is_info "[INFO]" 682 | else ifx is_verbose "[VERBOSE]" 683 | else ""; 684 | type_string := ifx is_validation "[VALIDATION]" 685 | else ifx is_performance "[PERFORMANCE]" 686 | else ifx is_general "[GENERAL]" 687 | else ""; 688 | 689 | temp_context : Context; 690 | push_context temp_context 691 | { 692 | print("\n\nVulkan Debug Message % %:\n\n%\n\n", 693 | severity_string, type_string, to_string(message)); 694 | if is_error breakpoint(); 695 | } 696 | } 697 | return VK_FALSE; 698 | } 699 | debug_utils_create_info : VkDebugUtilsMessengerCreateInfoEXT; 700 | debug_utils_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | 701 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | 702 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | 703 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 704 | debug_utils_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | 705 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | 706 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 707 | debug_utils_create_info.pfnUserCallback = debug_utils_callback; 708 | debug_utils_create_info.pUserData = debug_utils_user_data; 709 | 710 | debug_utils_result := vkCreateDebugUtilsMessengerEXT(instance, *debug_utils_create_info, vulkan_allocator, *debug_utils_messenger); 711 | assert(debug_utils_result == VK_SUCCESS); 712 | } 713 | 714 | 715 | // # Surface 716 | #if OS == .WINDOWS 717 | { 718 | surface_create_info : VkWin32SurfaceCreateInfoKHR; 719 | surface_create_info.hinstance = GetModuleHandleW(null); 720 | surface_create_info.hwnd = window_handle; 721 | surface_result := vkCreateWin32SurfaceKHR(instance, *surface_create_info, vulkan_allocator, *surface); 722 | assert(surface_result == VK_SUCCESS); 723 | } 724 | else #assert(false); 725 | 726 | 727 | // # Physical Device 728 | { 729 | // ## First we'll request a bunch of data about each device to see what they offer 730 | physical_devices := vulkan_fill_array(VkPhysicalDevice, vkEnumeratePhysicalDevices, instance); 731 | physical_devices_properties := new_temporary_array(VkPhysicalDeviceProperties2); 732 | for physical_devices 733 | { 734 | properties : VkPhysicalDeviceProperties2; 735 | vkGetPhysicalDeviceProperties2(it, *properties); 736 | array_add(*physical_devices_properties, properties); 737 | } 738 | physical_devices_properties_vulkan_1_2 := new_temporary_array(VkPhysicalDeviceVulkan12Properties); 739 | for physical_devices_properties 740 | { 741 | properties_1_2 : VkPhysicalDeviceVulkan12Properties; 742 | properties := it; 743 | properties.pNext = *properties_1_2; 744 | vkGetPhysicalDeviceProperties2(physical_devices[it_index], *properties); 745 | array_add(*physical_devices_properties_vulkan_1_2, properties_1_2); 746 | } 747 | physical_devices_memory_properties := new_temporary_array(VkPhysicalDeviceMemoryProperties); 748 | for physical_devices 749 | { 750 | properties : VkPhysicalDeviceMemoryProperties; 751 | vkGetPhysicalDeviceMemoryProperties(it, *properties); 752 | array_add(*physical_devices_memory_properties, properties); 753 | } 754 | physical_devices_device_local_memory_sizes := new_temporary_array(s64); 755 | for physical_device : physical_devices 756 | { 757 | size_for_device := 0; 758 | properties := physical_devices_properties[it_index]; 759 | memory_properties := physical_devices_memory_properties[it_index]; 760 | for 0..memory_properties.memoryHeapCount-1 761 | { 762 | heap := memory_properties.memoryHeaps[it]; 763 | if heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT 764 | { 765 | heap_size : s64 = xx heap.size; 766 | size_for_device += heap_size; 767 | } 768 | } 769 | array_add(*physical_devices_device_local_memory_sizes, size_for_device); 770 | } 771 | physical_devices_features := new_temporary_array(VkPhysicalDeviceFeatures2); 772 | for physical_devices 773 | { 774 | features : VkPhysicalDeviceFeatures2; 775 | vkGetPhysicalDeviceFeatures2(it, *features); 776 | array_add(*physical_devices_features, features); 777 | } 778 | physical_devices_queue_properties := new_temporary_array([] VkQueueFamilyProperties2); 779 | for physical_devices 780 | { 781 | queues := vulkan_fill_array_no_result(VkQueueFamilyProperties2, vkGetPhysicalDeviceQueueFamilyProperties2, it); 782 | array_add(*physical_devices_queue_properties, queues); 783 | } 784 | physical_devices_available_extensions := new_temporary_array([] VkExtensionProperties); 785 | for physical_devices 786 | { 787 | extensions := vulkan_fill_array(VkExtensionProperties, vkEnumerateDeviceExtensionProperties, it, null); 788 | array_add(*physical_devices_available_extensions, extensions); 789 | } 790 | physical_devices_queue_family_indices := new_temporary_array(Queue_Family_Indices); 791 | for queue_properties_array : physical_devices_queue_properties 792 | { 793 | physical_device := physical_devices[it_index]; 794 | queue_family_indices : Queue_Family_Indices; 795 | for queue_properties, queue_family_index : queue_properties_array 796 | { 797 | queue_count := queue_properties.queueFamilyProperties.queueCount; 798 | supports_graphics := cast(bool) queue_properties.queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT; 799 | supports_compute := cast(bool) queue_properties.queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT; 800 | supports_transfer := (queue_properties.queueFamilyProperties.queueFlags & VK_QUEUE_TRANSFER_BIT) || (supports_graphics || supports_compute); 801 | supports_present := false; 802 | if surface 803 | { 804 | supports : VkBool32; 805 | vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, cast(u32) queue_family_index, surface, *supports); 806 | supports_present = cast(bool) supports; 807 | } 808 | if supports_graphics && supports_compute && supports_transfer && supports_present && queue_family_indices.graphics_family_index == -1 809 | { 810 | queue_family_indices.graphics_family_index = queue_family_index; 811 | queue_family_indices.graphics_queue_index = 0; 812 | } 813 | else if supports_compute && queue_family_indices.async_family_index == -1 814 | { 815 | queue_family_indices.async_family_index = queue_family_index; 816 | queue_family_indices.async_queue_index = 0; 817 | } 818 | else if supports_transfer && queue_family_indices.transfer_family_index == -1 819 | { 820 | queue_family_indices.transfer_family_index = queue_family_index; 821 | queue_family_indices.transfer_queue_index = 0; 822 | } 823 | } 824 | array_add(*physical_devices_queue_family_indices, queue_family_indices); 825 | } 826 | physical_devices_supported_extensions := new_temporary_array(Is_Set); 827 | for extensions : physical_devices_available_extensions 828 | { 829 | array_add(*physical_devices_supported_extensions, fill_extension_set(extensions)); 830 | } 831 | physical_devices_surface_capabilities := new_temporary_array(VkSurfaceCapabilitiesKHR); 832 | physical_devices_surface_formats := new_temporary_array([] VkSurfaceFormatKHR); 833 | physical_devices_surface_present_modes := new_temporary_array([] VkPresentModeKHR); 834 | if surface 835 | { 836 | for physical_devices 837 | { 838 | capabilities : VkSurfaceCapabilitiesKHR; 839 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(it, surface, *capabilities); 840 | array_add(*physical_devices_surface_capabilities, capabilities); 841 | } 842 | for physical_devices 843 | { 844 | surface_formats := vulkan_fill_array(VkSurfaceFormatKHR, vkGetPhysicalDeviceSurfaceFormatsKHR, it, surface); 845 | array_add(*physical_devices_surface_formats, surface_formats); 846 | } 847 | for physical_devices 848 | { 849 | present_modes := vulkan_fill_array(VkPresentModeKHR, vkGetPhysicalDeviceSurfacePresentModesKHR, it, surface); 850 | array_add(*physical_devices_surface_present_modes, present_modes); 851 | } 852 | } 853 | 854 | // ## Then we assign a score to each available device 855 | scores := new_temporary_array(s64); 856 | array_resize(*scores, physical_devices.count); 857 | for physical_device, index : physical_devices 858 | { 859 | scores[index] = -1; 860 | queue_family_indices := physical_devices_queue_family_indices[index]; 861 | extensions := physical_devices_supported_extensions[index]; 862 | device_local_memory := physical_devices_device_local_memory_sizes[index]; 863 | properties := physical_devices_properties[index].properties; 864 | 865 | if queue_family_indices.graphics_family_index == -1 || !extensions.swapchain 866 | continue; 867 | 868 | if properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU 869 | scores[index] += 1000; 870 | 871 | if queue_family_indices.async_family_index != -1 872 | scores[index] += 1000; 873 | 874 | if queue_family_indices.transfer_family_index != -1 875 | scores[index] += 500; 876 | 877 | if extensions.mesh_shader 878 | scores[index] += 1000; 879 | 880 | if extensions.ray_tracing 881 | scores[index] += 1000; 882 | 883 | scores[index] += cast(s64) ((cast(float)device_local_memory) / (1024.0 * 1024.0 * 1024.0) * 100.0); 884 | } 885 | 886 | // ## Finally we choose a device and store the data about it in the context 887 | chosen_index := -1; 888 | max_score := -1; 889 | for score, index : scores 890 | { 891 | if score > max_score 892 | { 893 | max_score = score; 894 | chosen_index = index; 895 | } 896 | } 897 | if chosen_index == -1 898 | { 899 | print("We couldn't find a suitable device!!!!\n"); 900 | assert(false); 901 | } 902 | else 903 | { 904 | for properties : physical_devices_properties 905 | { 906 | print("% - % - %MB - score % %\n", 907 | to_string(properties.properties.deviceName.data), 908 | properties.properties.deviceType, 909 | physical_devices_device_local_memory_sizes[it_index] / (1024 * 1024), 910 | scores[it_index], 911 | ifx it_index == chosen_index "(Chosen)" else ""); 912 | } 913 | } 914 | physical_device = physical_devices[chosen_index]; 915 | queue_family_indices = physical_devices_queue_family_indices[chosen_index]; 916 | is_supported = physical_devices_supported_extensions[chosen_index]; 917 | physical_device_properties = physical_devices_properties[chosen_index].properties; 918 | physical_device_properties_vulkan_1_2 = physical_devices_properties_vulkan_1_2[chosen_index]; 919 | physical_device_features = physical_devices_features[chosen_index].features; 920 | surface_capabilities = physical_devices_surface_capabilities[chosen_index]; 921 | surface_formats = alloc_and_copy_array(physical_devices_surface_formats[chosen_index]); 922 | surface_present_modes = alloc_and_copy_array(physical_devices_surface_present_modes[chosen_index]); 923 | vkGetPhysicalDeviceMemoryProperties(physical_device, *physical_device_memory_properties); 924 | } 925 | 926 | 927 | // # Logical Device 928 | { 929 | device_queue_create_infos := new_temporary_array(VkDeviceQueueCreateInfo); 930 | { 931 | graphics_priorities := new_temporary_array(float32); 932 | async_priorities := new_temporary_array(float32); 933 | transfer_priorities := new_temporary_array(float32); 934 | 935 | array_add(*graphics_priorities, 1.0); 936 | 937 | async_uses_graphics_queue := queue_family_indices.async_family_index == queue_family_indices.graphics_family_index; 938 | transfer_uses_graphics_queue := queue_family_indices.transfer_family_index == queue_family_indices.graphics_family_index; 939 | if async_uses_graphics_queue 940 | array_add(*graphics_priorities, 0.75); 941 | else 942 | array_add(*async_priorities, 0.75); 943 | if transfer_uses_graphics_queue 944 | array_add(*graphics_priorities, 0.5); 945 | else 946 | array_add(*transfer_priorities, 0.5); 947 | 948 | assert(queue_family_indices.graphics_family_index != -1); 949 | graphics_queue_create_info : VkDeviceQueueCreateInfo; 950 | graphics_queue_create_info.queueFamilyIndex = xx queue_family_indices.graphics_family_index; 951 | graphics_queue_create_info.queueCount = xx graphics_priorities.count; 952 | graphics_queue_create_info.pQueuePriorities = graphics_priorities.data; 953 | array_add(*device_queue_create_infos, graphics_queue_create_info); 954 | 955 | if !async_uses_graphics_queue && queue_family_indices.async_family_index != -1 956 | { 957 | async_queue_create_info : VkDeviceQueueCreateInfo; 958 | async_queue_create_info.queueFamilyIndex = xx queue_family_indices.async_family_index; 959 | async_queue_create_info.queueCount = xx async_priorities.count; 960 | async_queue_create_info.pQueuePriorities = async_priorities.data; 961 | array_add(*device_queue_create_infos, async_queue_create_info); 962 | } 963 | 964 | if !transfer_uses_graphics_queue && queue_family_indices.transfer_family_index != -1 965 | { 966 | transfer_queue_create_info : VkDeviceQueueCreateInfo; 967 | transfer_queue_create_info.queueFamilyIndex = xx queue_family_indices.transfer_family_index; 968 | transfer_queue_create_info.queueCount = xx transfer_priorities.count; 969 | transfer_queue_create_info.pQueuePriorities = transfer_priorities.data; 970 | array_add(*device_queue_create_infos, transfer_queue_create_info); 971 | } 972 | } 973 | 974 | is_enabled = is_supported; 975 | is_enabled.ray_tracing = is_enabled.ray_tracing && is_enabled.deferred_host_operations && is_enabled.pipeline_library; 976 | // @@NOTE: Ray Tracing extension cannot be enabled by itself. Needs to have deferred host operations and pipelie libraries. 977 | enabled_extension_names := get_set_extension_cstrings(is_enabled); 978 | 979 | create_info : VkDeviceCreateInfo; 980 | create_info.queueCreateInfoCount = xx device_queue_create_infos.count; 981 | create_info.pQueueCreateInfos = device_queue_create_infos.data; 982 | create_info.enabledExtensionCount = xx enabled_extension_names.count; 983 | create_info.ppEnabledExtensionNames = enabled_extension_names.data; 984 | 985 | result := vkCreateDevice(physical_device, *create_info, vulkan_allocator, *device); 986 | assert(result == VK_SUCCESS); 987 | } 988 | 989 | 990 | // # Device Procedures 991 | load_vulkan_device_procedures(device); 992 | 993 | 994 | // # Load Queues 995 | { 996 | vkGetDeviceQueue(device, cast(u32) queue_family_indices.graphics_family_index, cast(u32) queue_family_indices.graphics_queue_index, *graphics_queue); 997 | vkGetDeviceQueue(device, cast(u32) queue_family_indices.async_family_index, cast(u32) queue_family_indices.async_queue_index, *async_queue); 998 | vkGetDeviceQueue(device, cast(u32) queue_family_indices.transfer_family_index, cast(u32) queue_family_indices.transfer_queue_index, *transfer_queue); 999 | } 1000 | 1001 | // # Descriptor Pools 1002 | { 1003 | current_descriptor_pool.pool = vulkan_create_descriptor_pool(vulkan_context); 1004 | current_descriptor_pool.last_frame_that_used_it = 0; 1005 | } 1006 | } 1007 | 1008 | 1009 | vulkan_create_descriptor_pool :: (using vulkan_context : *Vulkan_Context) -> VkDescriptorPool 1010 | { 1011 | create_info : VkDescriptorPoolCreateInfo; 1012 | create_info.flags = 0; 1013 | create_info.maxSets = MAX_SETS_PER_POOL; 1014 | pool_sizes : [11] VkDescriptorPoolSize; 1015 | pool_sizes[0].type, pool_sizes[0].descriptorCount = VK_DESCRIPTOR_TYPE_SAMPLER , 4; 1016 | pool_sizes[1].type, pool_sizes[1].descriptorCount = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16; 1017 | pool_sizes[2].type, pool_sizes[2].descriptorCount = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE , 16; 1018 | pool_sizes[3].type, pool_sizes[3].descriptorCount = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE , 4; 1019 | pool_sizes[4].type, pool_sizes[4].descriptorCount = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 4; 1020 | pool_sizes[5].type, pool_sizes[5].descriptorCount = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER , 4; 1021 | pool_sizes[6].type, pool_sizes[6].descriptorCount = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 8; 1022 | pool_sizes[7].type, pool_sizes[7].descriptorCount = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 8; 1023 | pool_sizes[8].type, pool_sizes[8].descriptorCount = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 3; 1024 | pool_sizes[9].type, pool_sizes[9].descriptorCount = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 3; 1025 | pool_sizes[10].type, pool_sizes[10].descriptorCount = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT , 2; 1026 | create_info.poolSizeCount = cast(u32) pool_sizes.count; 1027 | create_info.pPoolSizes = pool_sizes.data; 1028 | pool : VkDescriptorPool; 1029 | result := vkCreateDescriptorPool(device, *create_info, vulkan_allocator, *pool); 1030 | assert(result == VK_SUCCESS); 1031 | return pool; 1032 | } 1033 | 1034 | 1035 | vulkan_deinit :: (using vulkan_context : *Vulkan_Context) 1036 | { 1037 | vkDeviceWaitIdle(device); 1038 | vkDestroyDescriptorPool(device, current_descriptor_pool.pool, vulkan_allocator); 1039 | for * full_descriptor_pools 1040 | vkDestroyDescriptorPool(device, it.pool, vulkan_allocator); 1041 | for * empty_descriptor_pools 1042 | vkDestroyDescriptorPool(device, it.pool, vulkan_allocator); 1043 | for * acquired_swapchain_image_semaphores 1044 | vulkan_destroy_semaphore(vulkan_context, it); 1045 | for * rendering_finished_to_swapchain_image_semaphores 1046 | vulkan_destroy_semaphore(vulkan_context, it); 1047 | for * rendering_finished_to_swapchain_image_fences 1048 | vulkan_destroy_fence(vulkan_context, it); 1049 | if swapchain 1050 | { 1051 | vkDestroySwapchainKHR(device, swapchain, vulkan_allocator); 1052 | for * swapchain_image_views 1053 | { 1054 | vkDestroyImageView(device, < success : bool #must 1127 | { 1128 | using Presentation_Mode; 1129 | using vulkan_context; 1130 | assert(is_enabled.swapchain); 1131 | assert(surface != null); 1132 | 1133 | vkDeviceWaitIdle(device); 1134 | 1135 | old_swapchain_data := swapchain_data; 1136 | 1137 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, *surface_capabilities); 1138 | if surface_capabilities.maxImageExtent.width == 0 || surface_capabilities.maxImageExtent.height == 0 1139 | { 1140 | // If width or height of the swapchain would have been 0 then we can't create one, so we don't 1141 | // and we leave the old stuff around until we get a chance to recreate a new one with the proper size. 1142 | return false; 1143 | } 1144 | 1145 | // # Creating the Swapchain 1146 | { 1147 | format : VkSurfaceFormatKHR; 1148 | { 1149 | desired_format : VkSurfaceFormatKHR; 1150 | desired_format.format = VK_FORMAT_B8G8R8A8_SRGB; 1151 | desired_format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; 1152 | found := false; 1153 | assert(surface_formats.count > 0); 1154 | for surface_formats 1155 | { 1156 | if it.format == desired_format.format && it.colorSpace == desired_format.colorSpace 1157 | { 1158 | format = it; 1159 | found = true; 1160 | break; 1161 | } 1162 | } 1163 | if !found format = surface_formats[0]; 1164 | } 1165 | 1166 | extent : VkExtent2D; 1167 | { 1168 | user_gave_data := (width != -999 || height != -999); 1169 | if surface_capabilities.currentExtent.width == 0xFF_FF_FF_FF && surface_capabilities.currentExtent.height == 0xFF_FF_FF_FF 1170 | { 1171 | assert(user_gave_data); 1172 | // @@NOTE Expecial case in the API where the size of the surface will be 1173 | // determined by the size we set the swapchain here. 1174 | extent.width = xx width; 1175 | extent.height = xx height; 1176 | } 1177 | else 1178 | { 1179 | if user_gave_data 1180 | { 1181 | assert(width == surface_capabilities.currentExtent.width); 1182 | assert(height == surface_capabilities.currentExtent.height); 1183 | } 1184 | extent.width = xx surface_capabilities.currentExtent.width; 1185 | extent.height = xx surface_capabilities.currentExtent.height; 1186 | } 1187 | 1188 | extent.width = clamp(extent.width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width); 1189 | extent.height = clamp(extent.height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height); 1190 | } 1191 | assert(extent.width != 0); 1192 | assert(extent.height != 0); 1193 | 1194 | selected_present_mode : VkPresentModeKHR; 1195 | { 1196 | modes_by_priority : [4] VkPresentModeKHR; 1197 | if #complete presentation_mode == 1198 | { 1199 | case Mailbox; 1200 | modes_by_priority[0] = VK_PRESENT_MODE_MAILBOX_KHR; 1201 | modes_by_priority[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; 1202 | modes_by_priority[2] = VK_PRESENT_MODE_FIFO_RELAXED_KHR; 1203 | modes_by_priority[3] = VK_PRESENT_MODE_FIFO_KHR; 1204 | 1205 | case Strict_Fifo; 1206 | modes_by_priority[0] = VK_PRESENT_MODE_MAILBOX_KHR; 1207 | modes_by_priority[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; 1208 | modes_by_priority[2] = VK_PRESENT_MODE_FIFO_RELAXED_KHR; 1209 | modes_by_priority[3] = VK_PRESENT_MODE_FIFO_KHR; 1210 | 1211 | case Relaxed_Fifo; 1212 | modes_by_priority[0] = VK_PRESENT_MODE_FIFO_RELAXED_KHR; 1213 | modes_by_priority[1] = VK_PRESENT_MODE_FIFO_KHR; 1214 | modes_by_priority[2] = VK_PRESENT_MODE_MAILBOX_KHR; 1215 | modes_by_priority[3] = VK_PRESENT_MODE_IMMEDIATE_KHR; 1216 | 1217 | case Immediate; 1218 | modes_by_priority[0] = VK_PRESENT_MODE_FIFO_RELAXED_KHR; 1219 | modes_by_priority[1] = VK_PRESENT_MODE_FIFO_KHR; 1220 | modes_by_priority[2] = VK_PRESENT_MODE_MAILBOX_KHR; 1221 | modes_by_priority[3] = VK_PRESENT_MODE_IMMEDIATE_KHR; 1222 | } 1223 | 1224 | found := false; 1225 | for available_mode : surface_present_modes 1226 | { 1227 | for mode_we_want : modes_by_priority 1228 | { 1229 | if available_mode == mode_we_want 1230 | { 1231 | found = true; 1232 | selected_present_mode = available_mode; 1233 | break available_mode; 1234 | } 1235 | } 1236 | } 1237 | assert(found); 1238 | } 1239 | 1240 | create_info : VkSwapchainCreateInfoKHR; 1241 | create_info.surface = surface; 1242 | create_info.minImageCount = max(cast(u32) 3, surface_capabilities.minImageCount); 1243 | if surface_capabilities.maxImageCount != 0 create_info.minImageCount = min(create_info.minImageCount, surface_capabilities.maxImageCount); 1244 | create_info.imageFormat = format.format; 1245 | create_info.imageColorSpace = format.colorSpace; 1246 | create_info.imageExtent = extent; 1247 | create_info.imageArrayLayers = 1; 1248 | create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; 1249 | create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 1250 | create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; 1251 | assert((create_info.preTransform & surface_capabilities.supportedTransforms) != 0); 1252 | create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 1253 | create_info.presentMode = selected_present_mode; 1254 | create_info.clipped = VK_TRUE; 1255 | create_info.oldSwapchain = old_swapchain_data.swapchain; 1256 | 1257 | result := vkCreateSwapchainKHR(device, *create_info, vulkan_allocator, *swapchain); 1258 | assert(result == VK_SUCCESS); 1259 | 1260 | present_mode = create_info.presentMode; 1261 | swapchain_format = create_info.imageFormat; 1262 | swapchain_color_space = create_info.imageColorSpace; 1263 | current_swapchain_image_extents = extent; 1264 | 1265 | if old_swapchain_data.swapchain 1266 | { 1267 | vkDestroySwapchainKHR(device, old_swapchain_data.swapchain, vulkan_allocator); 1268 | old_swapchain_data.swapchain = null; 1269 | } 1270 | } 1271 | 1272 | 1273 | // # Getting the images 1274 | { 1275 | images := vulkan_fill_array(VkImage, vkGetSwapchainImagesKHR, device, swapchain); 1276 | if swapchain_images.count == 0 1277 | swapchain_images = alloc_and_copy_array(images); 1278 | else if swapchain_images.count == images.count 1279 | copy_array_dst_src(swapchain_images, images); 1280 | else 1281 | assert(false); 1282 | } 1283 | 1284 | 1285 | // # Getting the image views from the images 1286 | { 1287 | for old_swapchain_data.swapchain_image_views 1288 | vkDestroyImageView(device, it, vulkan_allocator); 1289 | 1290 | new_image_views := new_temporary_array(VkImageView); 1291 | for swapchain_images 1292 | { 1293 | create_info : VkImageViewCreateInfo; 1294 | create_info.image = it; 1295 | create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; 1296 | create_info.format = swapchain_format; 1297 | create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; 1298 | create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; 1299 | create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; 1300 | create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 1301 | create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1302 | create_info.subresourceRange.baseMipLevel = 0; 1303 | create_info.subresourceRange.levelCount = 1; 1304 | create_info.subresourceRange.baseArrayLayer = 0; 1305 | create_info.subresourceRange.layerCount = 1; 1306 | image_view : VkImageView; 1307 | result := vkCreateImageView(device, *create_info, vulkan_allocator, *image_view); 1308 | assert(result == VK_SUCCESS); 1309 | array_add(*new_image_views, image_view); 1310 | } 1311 | 1312 | if swapchain_image_views.count == 0 1313 | swapchain_image_views = alloc_and_copy_array(new_image_views); 1314 | else if swapchain_image_views.count == new_image_views.count 1315 | copy_array_dst_src(swapchain_image_views, new_image_views); 1316 | else 1317 | assert(false); 1318 | } 1319 | 1320 | // # Creating the semaphores to control access to the images 1321 | { 1322 | // ## Semaphores to acquire the images from the swapchain 1323 | { 1324 | for * acquired_swapchain_image_semaphores 1325 | vulkan_destroy_semaphore(vulkan_context, it); 1326 | semaphores := new_temporary_array(VkSemaphore); 1327 | for 0..swapchain_images.count-1 1328 | array_add(*semaphores, vulkan_create_semaphore(vulkan_context)); 1329 | if acquired_swapchain_image_semaphores.count == 0 1330 | acquired_swapchain_image_semaphores = alloc_and_copy_array(semaphores); 1331 | else if acquired_swapchain_image_semaphores.count == swapchain_images.count 1332 | copy_array_dst_src(acquired_swapchain_image_semaphores, semaphores); 1333 | else 1334 | assert(false); 1335 | } 1336 | 1337 | // ## Semaphores to know if rendering has finished for an image 1338 | { 1339 | for * rendering_finished_to_swapchain_image_semaphores 1340 | vulkan_destroy_semaphore(vulkan_context, it); 1341 | semaphores := new_temporary_array(VkSemaphore); 1342 | for 0..swapchain_images.count-1 1343 | array_add(*semaphores, vulkan_create_semaphore(vulkan_context)); 1344 | if rendering_finished_to_swapchain_image_semaphores.count == 0 1345 | rendering_finished_to_swapchain_image_semaphores = alloc_and_copy_array(semaphores); 1346 | else if rendering_finished_to_swapchain_image_semaphores.count == swapchain_images.count 1347 | copy_array_dst_src(rendering_finished_to_swapchain_image_semaphores, semaphores); 1348 | else 1349 | assert(false); 1350 | } 1351 | 1352 | // ## Fences to know if rendering has finished for an image 1353 | { 1354 | for * rendering_finished_to_swapchain_image_fences 1355 | vulkan_destroy_fence(vulkan_context, it); 1356 | fences := new_temporary_array(VkFence); 1357 | for 0..swapchain_images.count-1 1358 | array_add(*fences, vulkan_create_fence(vulkan_context, signaled = true)); 1359 | if rendering_finished_to_swapchain_image_fences.count == 0 1360 | rendering_finished_to_swapchain_image_fences = alloc_and_copy_array(fences); 1361 | else if rendering_finished_to_swapchain_image_fences.count == swapchain_images.count 1362 | copy_array_dst_src(rendering_finished_to_swapchain_image_fences, fences); 1363 | else 1364 | assert(false); 1365 | } 1366 | 1367 | // ## Setting up the array of frame numbers per image 1368 | { 1369 | if frame_numbers_per_image.count == 0 1370 | { 1371 | temp := new_temporary_array(s64); 1372 | for 0..swapchain_images.count-1 1373 | array_add(*temp, -1); 1374 | frame_numbers_per_image = alloc_and_copy_array(temp); 1375 | } 1376 | else if frame_numbers_per_image.count != swapchain_images.count 1377 | { 1378 | assert(false); 1379 | } 1380 | } 1381 | } 1382 | 1383 | should_recreate_swapchain_when_we_get_a_chance = false; 1384 | 1385 | return true; 1386 | } 1387 | 1388 | 1389 | #if OS == .WINDOWS 1390 | { 1391 | Window_Handle :: HWND; 1392 | } 1393 | else #assert(false); 1394 | 1395 | 1396 | vulkan_create_command_pools :: (using vulkan_context : *Vulkan_Context) 1397 | { 1398 | do_for_type(Queue_Type.Graphics, cast(u32) queue_family_indices.graphics_family_index); 1399 | do_for_type(Queue_Type.Async_Compute, cast(u32) queue_family_indices.async_family_index); 1400 | do_for_type(Queue_Type.Transfer, cast(u32) queue_family_indices.transfer_family_index); 1401 | do_for_type :: (type : Queue_Type, queue_family_index : u32) #expand 1402 | { 1403 | temp_command_pools := new_temporary_array(VkCommandPool); 1404 | temp_used_command_buffers := new_temporary_array([..] VkCommandBuffer); 1405 | temp_free_command_buffers := new_temporary_array([..] VkCommandBuffer); 1406 | create_info : VkCommandPoolCreateInfo; 1407 | create_info.queueFamilyIndex = queue_family_index; 1408 | for 0..swapchain_images.count-1 1409 | { 1410 | command_pool : VkCommandPool; 1411 | result := vkCreateCommandPool(device, *create_info, vulkan_allocator, *command_pool); 1412 | assert(result == VK_SUCCESS); 1413 | array_add(*temp_command_pools, command_pool); 1414 | dynamic_array : [..] VkCommandBuffer; 1415 | array_add(*temp_used_command_buffers, dynamic_array); 1416 | another_dynamic_array : [..] VkCommandBuffer; 1417 | array_add(*temp_free_command_buffers, another_dynamic_array); 1418 | } 1419 | command_pools_per_type[type].command_pools = alloc_and_copy_array(temp_command_pools); 1420 | command_pools_per_type[type].used_command_buffers = alloc_and_copy_array(temp_used_command_buffers); 1421 | command_pools_per_type[type].free_command_buffers = alloc_and_copy_array(temp_free_command_buffers); 1422 | } 1423 | } 1424 | 1425 | 1426 | vulkan_get_new_command_buffer_for_graphics_queue :: (using vulkan_context : *Vulkan_Context) -> VkCommandBuffer #must 1427 | { 1428 | return vulkan_get_new_command_buffer_for_queue_type(vulkan_context, Queue_Type.Graphics); 1429 | } 1430 | 1431 | 1432 | vulkan_get_new_command_buffer_for_async_compute_queue :: (using vulkan_context : *Vulkan_Context) -> VkCommandBuffer #must 1433 | { 1434 | return vulkan_get_new_command_buffer_for_queue_type(vulkan_context, Queue_Type.Async_Compute); 1435 | } 1436 | 1437 | 1438 | vulkan_get_new_command_buffer_for_transfer_queue :: (using vulkan_context : *Vulkan_Context) -> VkCommandBuffer #must 1439 | { 1440 | return vulkan_get_new_command_buffer_for_queue_type(vulkan_context, Queue_Type.Transfer); 1441 | } 1442 | 1443 | 1444 | vulkan_get_new_command_buffer_for_queue_type :: (using vulkan_context : *Vulkan_Context, 1445 | queue_type : Queue_Type) -> VkCommandBuffer #must 1446 | { 1447 | per_type := *command_pools_per_type[queue_type]; 1448 | free_array := *per_type.free_command_buffers[current_command_pool_index]; 1449 | used_array := *per_type.used_command_buffers[current_command_pool_index]; 1450 | command_buffer : VkCommandBuffer; 1451 | if free_array.count > 0 1452 | { 1453 | command_buffer = pop(free_array); 1454 | } 1455 | else 1456 | { 1457 | allocate_info : VkCommandBufferAllocateInfo; 1458 | allocate_info.commandPool = per_type.command_pools[current_command_pool_index]; 1459 | allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 1460 | allocate_info.commandBufferCount = 1; 1461 | result := vkAllocateCommandBuffers(device, *allocate_info, *command_buffer); 1462 | assert(result == VK_SUCCESS); 1463 | } 1464 | array_add(used_array, command_buffer); 1465 | return command_buffer; 1466 | } 1467 | 1468 | 1469 | vulkan_one_time_submit_to_graphics_queue :: (using vulkan_context : *Vulkan_Context, semaphores_to_wait_on : [] VkSemaphore, code : Code, semaphore_to_signal : *VkSemaphore = null, fence_to_signal : *VkFence = null) #expand 1470 | { 1471 | `command_buffer : VkCommandBuffer; 1472 | vulkan_one_time_submit_to_queue(vulkan_context, Queue_Type.Graphics, semaphores_to_wait_on, code, semaphore_to_signal, fence_to_signal); 1473 | } 1474 | 1475 | 1476 | vulkan_one_time_submit_to_async_queue :: (using vulkan_context : *Vulkan_Context, semaphores_to_wait_on : [] VkSemaphore, code : Code, semaphore_to_signal : *VkSemaphore = null, fence_to_signal : *VkFence = null) #expand 1477 | { 1478 | `command_buffer : VkCommandBuffer; 1479 | vulkan_one_time_submit_to_queue(vulkan_context, Queue_Type.Async_Compute, semaphores_to_wait_on, code, semaphore_to_signal, fence_to_signal); 1480 | } 1481 | 1482 | 1483 | vulkan_one_time_submit_to_transfer_queue :: (using vulkan_context : *Vulkan_Context, semaphores_to_wait_on : [] VkSemaphore, code : Code, semaphore_to_signal : *VkSemaphore = null, fence_to_signal : *VkFence = null) #expand 1484 | { 1485 | `command_buffer : VkCommandBuffer; 1486 | vulkan_one_time_submit_to_queue(vulkan_context, Queue_Type.Transfer, semaphores_to_wait_on, code, semaphore_to_signal, fence_to_signal); 1487 | } 1488 | 1489 | 1490 | // 1491 | // @@TODO: There is some weirdness here when handling how to pass things into the code we want 1492 | // to insert to record the command buffer. I'd like for the command_buffer, variable to make it 1493 | // cleanly to user code, so it's somehow explicit that you're getting this to use on your own code. 1494 | // 1495 | // There is also problems I'm having where I want the command_buffer to be declared in the inner-most 1496 | // macro but it doesn't seem to make it out since the backtick sends it only to the outer macro not to 1497 | // user code. Righ now I'm declaring the macro on all the outer macros but I'd like to not have to do that. 1498 | // 1499 | 1500 | 1501 | vulkan_one_time_submit_to_queue :: (using vulkan_context : *Vulkan_Context, queue_type : Queue_Type, semaphores_to_wait_on : [] VkSemaphore, code : Code, semaphore_to_signal : *VkSemaphore = null, fence_to_signal : *VkFence = null) #expand 1502 | { 1503 | queue := vulkan_get_queue_for_type(vulkan_context, queue_type); 1504 | `command_buffer = vulkan_get_new_command_buffer_for_queue_type(vulkan_context, queue_type); 1505 | { 1506 | begin_info : VkCommandBufferBeginInfo; 1507 | begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 1508 | result := vkBeginCommandBuffer(`command_buffer, *begin_info); 1509 | assert(result == VK_SUCCESS); 1510 | } 1511 | { 1512 | #insert code; 1513 | } 1514 | { 1515 | vkEndCommandBuffer(`command_buffer); 1516 | 1517 | submit_info : VkSubmitInfo; 1518 | submit_info.commandBufferCount = 1; 1519 | submit_info.pCommandBuffers = *`command_buffer; 1520 | submit_info.waitSemaphoreCount = cast(u32) semaphores_to_wait_on.count; 1521 | submit_info.pWaitSemaphores = semaphores_to_wait_on.data; 1522 | wait_stage_masks := new_temporary_array(VkPipelineStageFlags); 1523 | for semaphores_to_wait_on 1524 | { 1525 | array_add(*wait_stage_masks, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); 1526 | } 1527 | submit_info.pWaitDstStageMask = wait_stage_masks.data; 1528 | assert(wait_stage_masks.count == semaphores_to_wait_on.count); 1529 | // 1530 | // @@IMPROVEMENT: We could improve performance here by saying how we should wait for the semaphore. 1531 | // We have to be conservative here and wait at the beginning of the pipeline but if, for example, 1532 | // we were waiting on a semaphore that transfers a texture to a GPU we only sample on the fragment shader 1533 | // we could put VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT and vertex work could start earlier and get some 1534 | // sweet overlap! :) 1535 | // 1536 | // This would be maybe annoying for the user to have to set though, cause they would need to give us 1537 | // a stage per semaphore. So maybe we can overload this procedure and default to TOP_OF_PIPE if we're not 1538 | // given anything. 1539 | // 1540 | if semaphore_to_signal 1541 | { 1542 | submit_info.signalSemaphoreCount = 1; 1543 | submit_info.pSignalSemaphores = semaphore_to_signal; 1544 | } 1545 | fence : VkFence = null; 1546 | if fence_to_signal 1547 | { 1548 | fence = < VkQueue 1560 | { 1561 | if #complete queue_type == 1562 | { 1563 | case Queue_Type.Graphics; return graphics_queue; 1564 | case Queue_Type.Async_Compute; return async_queue; 1565 | case Queue_Type.Transfer; return transfer_queue; 1566 | } 1567 | assert(false); 1568 | return null; 1569 | } 1570 | 1571 | 1572 | vulkan_wait_for_all_graphics_operations_to_finish :: (using vulkan_context : *Vulkan_Context) 1573 | { 1574 | vkQueueWaitIdle(graphics_queue); 1575 | } 1576 | 1577 | vulkan_wait_for_all_async_compute_operations_to_finish :: (using vulkan_context : *Vulkan_Context) 1578 | { 1579 | vkQueueWaitIdle(async_queue); 1580 | } 1581 | 1582 | vulkan_wait_for_all_transfer_operations_to_finish :: (using vulkan_context : *Vulkan_Context) 1583 | { 1584 | vkQueueWaitIdle(transfer_queue); 1585 | } 1586 | 1587 | 1588 | vulkan_create_shader_module :: (using vulkan_context : *Vulkan_Context, shader_spirv : [] u8) -> VkShaderModule 1589 | { 1590 | create_info : VkShaderModuleCreateInfo; 1591 | create_info.codeSize = cast(u64) shader_spirv.count; 1592 | create_info.pCode = cast(*u32) shader_spirv.data; 1593 | shader_module : VkShaderModule; 1594 | result := vkCreateShaderModule(device, *create_info, vulkan_allocator, *shader_module); 1595 | assert(result == VK_SUCCESS); 1596 | return shader_module; 1597 | } 1598 | 1599 | 1600 | vulkan_destroy_shader_module :: (using vulkan_context : *Vulkan_Context, module : *VkShaderModule) 1601 | { 1602 | vkDestroyShaderModule(device, < VkPipeline #must, 1612 | VkRenderPass #must, 1613 | VkPipelineLayout #must, 1614 | [3] VkDescriptorSetLayout #must 1615 | { 1616 | /* 1617 | 1618 | @@NOTE: If you wanted to use this to do some real stuff, you'd need to provide 1619 | more inputs to configure the state of the fixed function stuff of the pipeline. 1620 | 1621 | Most of this fixed function stuff will depend on the inputs and outputs for the 1622 | shaders you're using. We're hardcoding here most of it cause we'd end up with a full 1623 | renderer if we tried to support all the configurations. 1624 | 1625 | Maybe eventually I'll try to provide a module that does this better, but that would be 1626 | sort of a graphics library on top of Vulkan that would do stuff like creating a graphics 1627 | pipeline purely from assets or something (since most of this stuff you can figure out from 1628 | the SPIR-V refleced + some extra stuff). But this would be waaay outside the scope of adding 1629 | vulkan bindings and a simple vulkan example, which is what this module is for :) 1630 | 1631 | 1632 | 1633 | @@IMPROVEMENT: Eventually I'd like to move away from giving the descriptor set layouts and pipeline layouts here 1634 | and have those cached internally. The reasoning is that for shaders that will have the exact same pipeline 1635 | layout and descriptor set layouts we're creating duplicated objects, which we then have to destroy. The other 1636 | reason is that from looking at the usage it LOOKS like pipeline layouts and descriptor set layouts for the same shader 1637 | but with just a code change are important when they are really not!!! 1638 | 1639 | If two shaders have slightly different code but they access the same kind of descriptor sets with the same stuff and 1640 | the same push constant ranges, they effectively have the same pipeline layout and descriptor set layouts. 1641 | 1642 | 1643 | 1644 | @@IMPROVEMENT: The Render-Pass objects we create here all the time should also be cached really, especially so the user 1645 | doesn't have to worry about destroying them. But mostly cause similar shaders are very likely to reuse the same ones 1646 | constantly. 1647 | 1648 | All that matters for renderpasses (I think) to create the pipeline is which targets this actually writes to, how many 1649 | MSAA samples they have and which format they have (rgba16, rgb32, whatever). All we need here is just a "compatible" render 1650 | pass to be able to create the pipeline, not the actual renderpass that will be used during rendering which will be created 1651 | in the middle of the frame when needed. For compatibility see: 1652 | - https://renderdoc.org/vkspec_chunked/chap7.html#renderpass-compatibility 1653 | 1654 | */ 1655 | 1656 | vertex_shader_stage_create_info : VkPipelineShaderStageCreateInfo; 1657 | { 1658 | using vertex_shader_stage_create_info; 1659 | stage = VK_SHADER_STAGE_VERTEX_BIT; 1660 | module = vertex_shader_module; 1661 | pName = "main"; 1662 | } 1663 | fragment_shader_stage_create_info : VkPipelineShaderStageCreateInfo; 1664 | { 1665 | using fragment_shader_stage_create_info; 1666 | stage = VK_SHADER_STAGE_FRAGMENT_BIT; 1667 | module = fragment_shader_module; 1668 | pName = "main"; 1669 | } 1670 | 1671 | // # Programmable Stages 1672 | stage_create_infos : [2] VkPipelineShaderStageCreateInfo; 1673 | stage_create_infos[0] = vertex_shader_stage_create_info; 1674 | stage_create_infos[1] = fragment_shader_stage_create_info; 1675 | 1676 | // # Vertex Input 1677 | vertex_input_state : VkPipelineVertexInputStateCreateInfo; 1678 | vertex_binding_description : VkVertexInputBindingDescription; 1679 | vertex_attribute_descriptions : [2] VkVertexInputAttributeDescription; 1680 | #if has_vertex_stuff 1681 | { 1682 | vertex_binding_description.binding = 0; 1683 | vertex_binding_description.stride = size_of(Vertex); 1684 | vertex_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; 1685 | 1686 | // ## Position 1687 | vertex_attribute_descriptions[0].location = 0; 1688 | vertex_attribute_descriptions[0].binding = 0; 1689 | vertex_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; 1690 | vertex_attribute_descriptions[0].offset = #run offset_of(Vertex, "position"); 1691 | 1692 | // ## Color 1693 | vertex_attribute_descriptions[1].location = 1; 1694 | vertex_attribute_descriptions[1].binding = 0; 1695 | vertex_attribute_descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; 1696 | vertex_attribute_descriptions[1].offset = #run offset_of(Vertex, "color"); 1697 | 1698 | vertex_input_state.vertexBindingDescriptionCount = 1; 1699 | vertex_input_state.pVertexBindingDescriptions = *vertex_binding_description; 1700 | vertex_input_state.vertexAttributeDescriptionCount = vertex_attribute_descriptions.count; 1701 | vertex_input_state.pVertexAttributeDescriptions = vertex_attribute_descriptions.data; 1702 | } 1703 | else 1704 | { 1705 | vertex_input_state.vertexBindingDescriptionCount = 0; 1706 | vertex_input_state.pVertexBindingDescriptions = null; 1707 | vertex_input_state.vertexAttributeDescriptionCount = 0; 1708 | vertex_input_state.pVertexAttributeDescriptions = null; 1709 | } 1710 | 1711 | // # Input Assembly 1712 | input_assembly_state : VkPipelineInputAssemblyStateCreateInfo; 1713 | input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 1714 | input_assembly_state.primitiveRestartEnable = VK_FALSE; 1715 | 1716 | // # Viewport (also set as dynamic state so we'll set it when drawing) 1717 | viewport_state : VkPipelineViewportStateCreateInfo; 1718 | viewport_state.viewportCount = 1; 1719 | viewport : VkViewport; 1720 | viewport.x = 0.0; 1721 | viewport.y = 0.0; 1722 | viewport.width = 1920.0; 1723 | viewport.height = 1080.0; 1724 | viewport.minDepth = 0.0; 1725 | viewport.maxDepth = 1.0; 1726 | viewport_state.pViewports = *viewport; 1727 | viewport_state.scissorCount = 1; 1728 | scissor_rect : VkRect2D; 1729 | scissor_rect.offset.x = 0; 1730 | scissor_rect.offset.y = 0; 1731 | scissor_rect.extent.width = 1920; 1732 | scissor_rect.extent.height = 1080; 1733 | viewport_state.pScissors = *scissor_rect; 1734 | 1735 | // # Rasterization 1736 | rasterization_state : VkPipelineRasterizationStateCreateInfo; 1737 | rasterization_state.depthClampEnable = VK_FALSE; 1738 | rasterization_state.rasterizerDiscardEnable = VK_FALSE; 1739 | rasterization_state.polygonMode = VK_POLYGON_MODE_FILL; 1740 | rasterization_state.cullMode = VK_CULL_MODE_BACK_BIT; 1741 | rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 1742 | rasterization_state.depthBiasEnable = VK_FALSE; 1743 | rasterization_state.depthBiasConstantFactor = 0.0; 1744 | rasterization_state.depthBiasClamp = 0.0; 1745 | rasterization_state.depthBiasSlopeFactor = 0.0; 1746 | rasterization_state.lineWidth = 1.0; 1747 | 1748 | // # Multisample 1749 | multisample_state : VkPipelineMultisampleStateCreateInfo; 1750 | multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; 1751 | 1752 | // # Depth/Stencil 1753 | depth_stencil_state : VkPipelineDepthStencilStateCreateInfo; 1754 | depth_stencil_state.depthTestEnable = VK_FALSE; 1755 | depth_stencil_state.depthWriteEnable = VK_FALSE; 1756 | depth_stencil_state.depthCompareOp = VK_COMPARE_OP_ALWAYS; 1757 | depth_stencil_state.depthBoundsTestEnable = VK_FALSE; 1758 | depth_stencil_state.stencilTestEnable = VK_FALSE; 1759 | depth_stencil_state.minDepthBounds = 0.0; 1760 | depth_stencil_state.maxDepthBounds = 1.0; 1761 | depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; 1762 | depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP; 1763 | depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; 1764 | depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; 1765 | depth_stencil_state.front.compareMask = 0x0; 1766 | depth_stencil_state.front.writeMask = 0x0; 1767 | depth_stencil_state.front.reference = 0x0; 1768 | depth_stencil_state.back.failOp = VK_STENCIL_OP_KEEP; 1769 | depth_stencil_state.back.passOp = VK_STENCIL_OP_KEEP; 1770 | depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_KEEP; 1771 | depth_stencil_state.back.compareOp = VK_COMPARE_OP_ALWAYS; 1772 | depth_stencil_state.back.compareMask = 0x0; 1773 | depth_stencil_state.back.writeMask = 0x0; 1774 | depth_stencil_state.back.reference = 0x0; 1775 | 1776 | // # Color Blend 1777 | color_blend_state : VkPipelineColorBlendStateCreateInfo; 1778 | color_blend_state.logicOpEnable = VK_FALSE; 1779 | color_blend_state.logicOp = VK_LOGIC_OP_NO_OP; 1780 | color_blend_state.attachmentCount = 1; 1781 | attachment_data : VkPipelineColorBlendAttachmentState; 1782 | attachment_data.blendEnable = VK_FALSE; 1783 | attachment_data.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; 1784 | attachment_data.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; 1785 | attachment_data.colorBlendOp = VK_BLEND_OP_ADD; 1786 | attachment_data.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; 1787 | attachment_data.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; 1788 | attachment_data.alphaBlendOp = VK_BLEND_OP_ADD; 1789 | attachment_data.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; 1790 | color_blend_state.pAttachments = *attachment_data; 1791 | color_blend_state.blendConstants[0] = 1.0; 1792 | color_blend_state.blendConstants[1] = 1.0; 1793 | color_blend_state.blendConstants[2] = 1.0; 1794 | color_blend_state.blendConstants[3] = 1.0; 1795 | 1796 | // # Dynamic state 1797 | dynamic_state : VkPipelineDynamicStateCreateInfo; 1798 | dynamic_states : [2] VkDynamicState; 1799 | dynamic_states[0] = VK_DYNAMIC_STATE_VIEWPORT; 1800 | dynamic_states[1] = VK_DYNAMIC_STATE_SCISSOR; 1801 | dynamic_state.dynamicStateCount = cast(u32) dynamic_states.count; 1802 | dynamic_state.pDynamicStates = dynamic_states.data; 1803 | 1804 | // # Pipeline Layout 1805 | pipeline_layout : VkPipelineLayout; 1806 | pipeline_layout_create_info : VkPipelineLayoutCreateInfo; 1807 | // ## Descriptor Set Layout 1808 | per_frame_descriptor_set_layout : VkDescriptorSetLayout; 1809 | { 1810 | create_info : VkDescriptorSetLayoutCreateInfo; 1811 | create_info.flags = 0; 1812 | create_info.bindingCount = 1; 1813 | binding : VkDescriptorSetLayoutBinding; 1814 | binding.binding = 0; // this is the "binding" in layout(set = 0, binding = 0) uniform ... 1815 | binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 1816 | binding.descriptorCount = 1; // only 1 element on this buffer 1817 | binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; // could make this the actual stage, could improve performance. 1818 | binding.pImmutableSamplers = null; 1819 | create_info.pBindings = *binding; 1820 | result := vkCreateDescriptorSetLayout(device, *create_info, vulkan_allocator, *per_frame_descriptor_set_layout); 1821 | assert(result == VK_SUCCESS); 1822 | } 1823 | per_view_descriptor_set_layout : VkDescriptorSetLayout; 1824 | { 1825 | create_info : VkDescriptorSetLayoutCreateInfo; 1826 | create_info.flags = 0; 1827 | create_info.bindingCount = 1; 1828 | binding : VkDescriptorSetLayoutBinding; 1829 | binding.binding = 0; 1830 | binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 1831 | binding.descriptorCount = 1; 1832 | binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; 1833 | binding.pImmutableSamplers = null; 1834 | create_info.pBindings = *binding; 1835 | result := vkCreateDescriptorSetLayout(device, *create_info, vulkan_allocator, *per_view_descriptor_set_layout); 1836 | assert(result == VK_SUCCESS); 1837 | } 1838 | per_object_descriptor_set_layout : VkDescriptorSetLayout; 1839 | { 1840 | create_info : VkDescriptorSetLayoutCreateInfo; 1841 | create_info.flags = 0; 1842 | create_info.bindingCount = 1; 1843 | binding : VkDescriptorSetLayoutBinding; 1844 | binding.binding = 0; 1845 | binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 1846 | binding.descriptorCount = 1; 1847 | binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; 1848 | binding.pImmutableSamplers = null; 1849 | create_info.pBindings = *binding; 1850 | result := vkCreateDescriptorSetLayout(device, *create_info, vulkan_allocator, *per_object_descriptor_set_layout); 1851 | assert(result == VK_SUCCESS); 1852 | } 1853 | all_descriptor_set_layouts : [3] VkDescriptorSetLayout; 1854 | all_descriptor_set_layouts[0] = per_frame_descriptor_set_layout; 1855 | all_descriptor_set_layouts[1] = per_view_descriptor_set_layout; 1856 | all_descriptor_set_layouts[2] = per_object_descriptor_set_layout; 1857 | 1858 | pipeline_layout_create_info.setLayoutCount = xx all_descriptor_set_layouts.count; 1859 | pipeline_layout_create_info.pSetLayouts = all_descriptor_set_layouts.data; 1860 | pipeline_layout_create_info.pushConstantRangeCount = 0; 1861 | pipeline_layout_create_info.pPushConstantRanges = null; 1862 | layout_result := vkCreatePipelineLayout(device, *pipeline_layout_create_info, vulkan_allocator, *pipeline_layout); 1863 | assert(layout_result == VK_SUCCESS); 1864 | 1865 | // # Compatible Render Pass 1866 | render_pass : VkRenderPass; 1867 | render_pass_create_info : VkRenderPassCreateInfo; 1868 | render_pass_create_info.flags = 0; 1869 | render_pass_create_info.attachmentCount = 1; 1870 | attachment_description : VkAttachmentDescription; 1871 | attachment_description.flags = 0; 1872 | attachment_description.format = swapchain_format; 1873 | attachment_description.samples = VK_SAMPLE_COUNT_1_BIT; 1874 | attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 1875 | attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 1876 | attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 1877 | attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 1878 | attachment_description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 1879 | attachment_description.finalLayout = VK_IMAGE_LAYOUT_GENERAL; 1880 | render_pass_create_info.pAttachments = *attachment_description; 1881 | render_pass_create_info.subpassCount = 1; 1882 | subpass : VkSubpassDescription; 1883 | subpass.flags = 0; 1884 | subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 1885 | subpass.inputAttachmentCount = 0; 1886 | subpass.pInputAttachments = null; 1887 | subpass.colorAttachmentCount = 1; 1888 | color_attachment : VkAttachmentReference; 1889 | color_attachment.attachment = 0; 1890 | color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 1891 | subpass.pColorAttachments = *color_attachment; 1892 | subpass.pResolveAttachments = null; 1893 | subpass.pDepthStencilAttachment = null; 1894 | subpass.preserveAttachmentCount = 0; 1895 | subpass.pPreserveAttachments = null; 1896 | render_pass_create_info.pSubpasses = *subpass; 1897 | render_pass_create_info.dependencyCount = 0; 1898 | render_pass_create_info.pDependencies = null; 1899 | render_pass_result := vkCreateRenderPass(device, *render_pass_create_info, vulkan_allocator, *render_pass); 1900 | assert(render_pass_result == VK_SUCCESS); 1901 | 1902 | create_info : VkGraphicsPipelineCreateInfo; 1903 | create_info.stageCount = xx stage_create_infos.count; 1904 | create_info.pStages = stage_create_infos.data; 1905 | create_info.pVertexInputState = *vertex_input_state; 1906 | create_info.pInputAssemblyState = *input_assembly_state; 1907 | create_info.pViewportState = *viewport_state; 1908 | create_info.pRasterizationState = *rasterization_state; 1909 | create_info.pMultisampleState = *multisample_state; 1910 | create_info.pDepthStencilState = *depth_stencil_state; 1911 | create_info.pColorBlendState = *color_blend_state; 1912 | create_info.pDynamicState = *dynamic_state; 1913 | create_info.layout = pipeline_layout; 1914 | create_info.renderPass = render_pass; 1915 | create_info.subpass = 0; 1916 | create_info.pTessellationState = null; 1917 | pipeline_cache : VkPipelineCache = VK_NULL_HANDLE; 1918 | pipeline : VkPipeline; 1919 | result := vkCreateGraphicsPipelines(device, pipeline_cache, 1, *create_info, vulkan_allocator, *pipeline); 1920 | assert(result == VK_SUCCESS); 1921 | return pipeline, render_pass, pipeline_layout, all_descriptor_set_layouts; 1922 | } 1923 | 1924 | 1925 | vulkan_destroy_graphics_pipeline :: (using vulkan_context : *Vulkan_Context, 1926 | graphics_pipeline : *VkPipeline, 1927 | render_pass : *VkRenderPass, 1928 | pipeline_layout : *VkPipelineLayout, 1929 | descriptor_set_layout : [] VkDescriptorSetLayout) 1930 | { 1931 | for descriptor_set_layout vkDestroyDescriptorSetLayout(device, it, vulkan_allocator); 1932 | if < VkRenderPass #must 1945 | { 1946 | /* 1947 | 1948 | @@NOTE: Same as the graphics pipeline procedure, a lot of this config you'd 1949 | need to decide yourself when making the pass, but we're setting it up for this example 1950 | only while showing the kind of parameters you'll need to setup. 1951 | 1952 | */ 1953 | 1954 | attachment_description : VkAttachmentDescription; 1955 | attachment_description.flags = 0; 1956 | attachment_description.format = swapchain_format; 1957 | attachment_description.samples = VK_SAMPLE_COUNT_1_BIT; 1958 | attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 1959 | attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 1960 | attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 1961 | attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 1962 | attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 1963 | attachment_description.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; 1964 | 1965 | color_attachment : VkAttachmentReference; 1966 | color_attachment.attachment = 0; 1967 | color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 1968 | 1969 | subpass : VkSubpassDescription; 1970 | subpass.flags = 0; 1971 | subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 1972 | subpass.inputAttachmentCount = 0; 1973 | subpass.pInputAttachments = null; 1974 | subpass.colorAttachmentCount = 1; 1975 | subpass.pColorAttachments = *color_attachment; 1976 | subpass.pResolveAttachments = null; 1977 | subpass.pDepthStencilAttachment = null; 1978 | subpass.preserveAttachmentCount = 0; 1979 | subpass.pPreserveAttachments = null; 1980 | 1981 | render_pass_create_info : VkRenderPassCreateInfo; 1982 | render_pass_create_info.flags = 0; 1983 | render_pass_create_info.attachmentCount = 1; 1984 | render_pass_create_info.pAttachments = *attachment_description; 1985 | render_pass_create_info.subpassCount = 1; 1986 | render_pass_create_info.pSubpasses = *subpass; 1987 | render_pass_create_info.dependencyCount = 0; 1988 | render_pass_create_info.pDependencies = null; 1989 | 1990 | render_pass : VkRenderPass; 1991 | render_pass_result := vkCreateRenderPass(device, *render_pass_create_info, vulkan_allocator, *render_pass); 1992 | assert(render_pass_result == VK_SUCCESS); 1993 | return render_pass; 1994 | } 1995 | 1996 | 1997 | vulkan_destroy_render_pass :: (using vulkan_context : *Vulkan_Context, render_pass : *VkRenderPass) 1998 | { 1999 | vkDestroyRenderPass(device, < [] VkFramebuffer #must 2006 | { 2007 | framebuffers := new_temporary_array(VkFramebuffer); 2008 | create_info : VkFramebufferCreateInfo; 2009 | create_info.renderPass = render_pass; 2010 | create_info.width = current_swapchain_image_extents.width; 2011 | create_info.height = current_swapchain_image_extents.height; 2012 | create_info.layers = 1; 2013 | for swapchain_image_views 2014 | { 2015 | framebuffer : VkFramebuffer; 2016 | create_info.attachmentCount = 1; 2017 | create_info.pAttachments = *it; 2018 | result := vkCreateFramebuffer(device, *create_info, vulkan_allocator, *framebuffer); 2019 | assert(result == VK_SUCCESS); 2020 | array_add(*framebuffers, framebuffer); 2021 | } 2022 | 2023 | if previous 2024 | { 2025 | for < VkSemaphore #must 2055 | { 2056 | create_info : VkSemaphoreCreateInfo; 2057 | semaphore : VkSemaphore; 2058 | result := vkCreateSemaphore(device, *create_info, vulkan_allocator, *semaphore); 2059 | return semaphore; 2060 | } 2061 | 2062 | 2063 | vulkan_destroy_semaphore :: (using vulkan_context : *Vulkan_Context, semaphore : *VkSemaphore) 2064 | { 2065 | vkDestroySemaphore(device, < VkFence #must 2071 | { 2072 | create_info : VkFenceCreateInfo; 2073 | if signaled create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; 2074 | fence : VkFence; 2075 | result := vkCreateFence(device, *create_info, vulkan_allocator, *fence); 2076 | return fence; 2077 | } 2078 | 2079 | 2080 | vulkan_destroy_fence :: (using vulkan_context : *Vulkan_Context, fence : *VkFence) 2081 | { 2082 | vkDestroyFence(device, < can_render : bool #must, 2088 | image : VkImage #must, 2089 | image_view : VkImageView #must, 2090 | image_index : u32 #must, 2091 | image_acquired_semaphore : VkSemaphore #must, 2092 | finished_rendering_semaphore : VkSemaphore #must, 2093 | finished_rendering_fence : VkFence #must, 2094 | recreated_swapchain : bool #must 2095 | { 2096 | // # Deal with needing to recreate the swapchain 2097 | recreated_swapchain := false; 2098 | if (should_recreate_swapchain_when_we_get_a_chance) || 2099 | (surface_capabilities.maxImageExtent.width == 0 || surface_capabilities.maxImageExtent.height == 0) 2100 | { 2101 | // @@NOTE: If the surface capabilities had a max extent for the image of 0 for width or height then 2102 | // we can't render to those! In this case we try to create swapchain again to see if these image extents 2103 | // have changed, which will create a new swapchain if they have, and just return if they haven't. 2104 | success := vulkan_create_swapchain(vulkan_context); 2105 | if !success 2106 | { 2107 | return can_render = false, 2108 | image = null, 2109 | image_view = null, 2110 | image_index = 0, 2111 | image_acquired_semaphore = null, 2112 | finished_rendering_semaphore = null, 2113 | finished_rendering_fence = null, 2114 | recreated_swapchain = false; 2115 | } 2116 | else 2117 | { 2118 | recreated_swapchain = true; 2119 | } 2120 | } 2121 | 2122 | // # Get the next swapchain image and the associated resources 2123 | current_swapchain_semaphore_index = current_frame % acquired_swapchain_image_semaphores.count; 2124 | image_acquired_semaphore := acquired_swapchain_image_semaphores[current_swapchain_semaphore_index]; 2125 | finished_rendering_semaphore := rendering_finished_to_swapchain_image_semaphores[current_swapchain_semaphore_index]; 2126 | frame_that_finished := frame_numbers_per_image[current_swapchain_semaphore_index]; 2127 | frame_numbers_per_image[current_swapchain_semaphore_index] = current_frame; 2128 | { 2129 | fence_that_should_have_finished := rendering_finished_to_swapchain_image_fences[current_swapchain_semaphore_index]; 2130 | fence_status_result := vkGetFenceStatus(device, fence_that_should_have_finished); 2131 | if fence_status_result == VK_NOT_READY 2132 | { 2133 | wait_result := vkWaitForFences(device, fenceCount = 1, *fence_that_should_have_finished, waitAll = VK_TRUE, 0xFF_FF_FF_FF_FF_FF_FF_FF); 2134 | assert(wait_result == VK_SUCCESS); 2135 | } 2136 | else 2137 | { 2138 | assert(fence_status_result == VK_SUCCESS); 2139 | } 2140 | reset_result := vkResetFences(device, fenceCount = 1, *fence_that_should_have_finished); 2141 | assert(reset_result == VK_SUCCESS); 2142 | } 2143 | finished_rendering_fence := rendering_finished_to_swapchain_image_fences[current_swapchain_semaphore_index]; 2144 | image_index : u32 = 0xFF_FF_FF_FF; 2145 | result := vkAcquireNextImageKHR(device, swapchain, 2146 | timeout = 0xFF_FF_FF_FF_FF_FF_FF_FF, 2147 | image_acquired_semaphore, 2148 | fence = null, 2149 | *image_index); 2150 | if result == VK_ERROR_OUT_OF_DATE_KHR 2151 | { 2152 | success := vulkan_create_swapchain(vulkan_context); 2153 | if !success 2154 | { 2155 | return can_render = false, 2156 | image = null, 2157 | image_view = null, 2158 | image_index = 0, 2159 | image_acquired_semaphore = null, 2160 | finished_rendering_semaphore = null, 2161 | finished_rendering_fence = null, 2162 | recreated_swapchain = false; 2163 | } 2164 | } 2165 | else if result == VK_SUBOPTIMAL_KHR 2166 | { 2167 | should_recreate_swapchain_when_we_get_a_chance = true; 2168 | } 2169 | else if result != VK_SUCCESS 2170 | { 2171 | assert(false, "%", result); 2172 | } 2173 | 2174 | // # Reseting the command pools for this image 2175 | { 2176 | current_command_pool_index = image_index; 2177 | for * command_pools_per_type 2178 | { 2179 | current_pool := it.command_pools[current_command_pool_index]; 2180 | reset_result := vkResetCommandPool(device, current_pool, flags = 0); 2181 | assert(reset_result == VK_SUCCESS); 2182 | used_command_buffers := *it.used_command_buffers[current_command_pool_index]; 2183 | free_command_buffers := *it.free_command_buffers[current_command_pool_index]; 2184 | while used_command_buffers.count > 0 2185 | { 2186 | array_add(free_command_buffers, pop(used_command_buffers)); 2187 | } 2188 | } 2189 | } 2190 | 2191 | // # Do work we can do when we know a frame number has finished 2192 | if frame_that_finished > 0 2193 | { 2194 | // ## Reset the full descriptor pools whose frames have ended. 2195 | for * full_descriptor_pools 2196 | { 2197 | if it.last_frame_that_used_it <= frame_that_finished 2198 | { 2199 | it.last_frame_that_used_it = -1; 2200 | result := vkResetDescriptorPool(device, it.pool, flags=0); 2201 | assert(result == VK_SUCCESS); 2202 | array_add(*empty_descriptor_pools, < recreated_swapchain : bool 2221 | { 2222 | semaphores_to_wait_on : [1] VkSemaphore; 2223 | semaphores_to_wait_on[0] = semaphore_to_wait_on; 2224 | recreated := vulkan_end_frame_and_present_image(vulkan_context, image_index, semaphores_to_wait_on); 2225 | return recreated; 2226 | } 2227 | 2228 | 2229 | vulkan_end_frame_and_present_image :: (using vulkan_context : *Vulkan_Context, image_index : u32, semaphores_to_wait_on : [] VkSemaphore) -> recreated_swapchain : bool 2230 | { 2231 | recreated_swapchain := false; 2232 | 2233 | // # Presenting the image 2234 | present_info : VkPresentInfoKHR; 2235 | present_info.waitSemaphoreCount = cast(u32) semaphores_to_wait_on.count; 2236 | present_info.pWaitSemaphores = semaphores_to_wait_on.data; 2237 | present_info.swapchainCount = 1; 2238 | present_info.pSwapchains = *swapchain; 2239 | present_info.pImageIndices = *image_index; 2240 | result := vkQueuePresentKHR(graphics_queue, *present_info); 2241 | if result == VK_ERROR_OUT_OF_DATE_KHR 2242 | { 2243 | success := vulkan_create_swapchain(vulkan_context); 2244 | recreated_swapchain = success; 2245 | } 2246 | else if result == VK_SUBOPTIMAL_KHR 2247 | { 2248 | should_recreate_swapchain_when_we_get_a_chance = true; 2249 | } 2250 | else if result != VK_SUCCESS 2251 | { 2252 | assert(false, "%", result); 2253 | } 2254 | 2255 | // # Incrementing Frame Number 2256 | current_frame += 1; 2257 | 2258 | return recreated_swapchain; 2259 | } 2260 | 2261 | 2262 | vulkan_allocate_descriptor_set :: (using vulkan_context : *Vulkan_Context, layout : VkDescriptorSetLayout) -> VkDescriptorSet 2263 | { 2264 | allocate_info : VkDescriptorSetAllocateInfo; 2265 | allocate_info.descriptorPool = current_descriptor_pool.pool; 2266 | allocate_info.descriptorSetCount = 1; 2267 | allocate_info.pSetLayouts = *layout; 2268 | set : VkDescriptorSet; 2269 | allocate_result := vkAllocateDescriptorSets(device, *allocate_info, *set); 2270 | if allocate_result == VK_ERROR_OUT_OF_POOL_MEMORY 2271 | { 2272 | array_add(*full_descriptor_pools, current_descriptor_pool); 2273 | if empty_descriptor_pools.count > 0 2274 | { 2275 | current_descriptor_pool = pop(*empty_descriptor_pools); 2276 | current_descriptor_pool.last_frame_that_used_it = current_frame; 2277 | } 2278 | else 2279 | { 2280 | current_descriptor_pool.pool = vulkan_create_descriptor_pool(vulkan_context); 2281 | current_descriptor_pool.last_frame_that_used_it = current_frame; 2282 | } 2283 | return vulkan_allocate_descriptor_set(vulkan_context, layout); 2284 | } 2285 | else 2286 | { 2287 | current_descriptor_pool.last_frame_that_used_it = current_frame; 2288 | } 2289 | return set; 2290 | } 2291 | 2292 | 2293 | vulkan_create_uniform_buffers :: (using vulkan_context : *Vulkan_Context, $Buffer_Type : Type) -> [] VkBuffer, [] VkDeviceMemory 2294 | { 2295 | count := swapchain_images.count; 2296 | buffers : [] VkBuffer; 2297 | buffers.data = xx alloc(count * size_of(VkBuffer)); 2298 | buffers.count = count; 2299 | memories : [] VkDeviceMemory; 2300 | memories.data = xx alloc(count * size_of(VkDeviceMemory)); 2301 | memories.count = count; 2302 | for 0..buffers.count-1 2303 | { 2304 | buffer, memory := vulkan_create_uniform_buffer(vulkan_context, Buffer_Type); 2305 | buffers[it] = buffer; 2306 | memories[it] = memory; 2307 | } 2308 | return buffers, memories; 2309 | } 2310 | 2311 | 2312 | vulkan_destroy_uniform_buffers :: (using vulkan_context : *Vulkan_Context, buffers : * [] VkBuffer, memories : * [] VkDeviceMemory) 2313 | { 2314 | assert(buffers.count == memories.count); 2315 | for 0..buffers.count-1 2316 | { 2317 | buffer := *(< VkBuffer, VkDeviceMemory 2329 | { 2330 | size_of_buffer := size_of(Buffer_Type); 2331 | assert(size_of_buffer & 15 == 0); 2332 | buffer, memory := vulkan_create_buffer(vulkan_context, 2333 | size_of_buffer, 2334 | desired_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 2335 | usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); 2336 | return buffer, memory; 2337 | } 2338 | 2339 | 2340 | vulkan_create_buffer :: (using vulkan_context : *Vulkan_Context, 2341 | size_of_buffer : int, 2342 | desired_properties : VkMemoryPropertyFlagBits, 2343 | usage : VkBufferUsageFlagBits) -> VkBuffer, VkDeviceMemory 2344 | { 2345 | // # First we create the buffer handle (but is no backed with memory yet!) 2346 | create_info : VkBufferCreateInfo; 2347 | create_info.flags = 0; 2348 | create_info.size = cast(u32) size_of_buffer; 2349 | create_info.usage = usage; 2350 | create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 2351 | create_info.queueFamilyIndexCount = 0; 2352 | create_info.pQueueFamilyIndices = null; 2353 | buffer : VkBuffer; 2354 | result := vkCreateBuffer(device, *create_info, vulkan_allocator, *buffer); 2355 | assert(result == VK_SUCCESS); 2356 | 2357 | // # Now we need to calculate the memory requirements of it and allocate it 2358 | memory_requirements : VkMemoryRequirements; 2359 | vkGetBufferMemoryRequirements(device, buffer, *memory_requirements); 2360 | allocate_info : VkMemoryAllocateInfo; 2361 | allocate_info.allocationSize = memory_requirements.size; 2362 | found_memory_type, memory_type_index := find_memory_type_index(vulkan_context, 2363 | memory_requirements.memoryTypeBits, 2364 | desired_properties); 2365 | // This is the index on the physical device memoryTypes in physical_device_memory_properties that covers all the requirements 2366 | // this buffer has, that is, it has to be one of the indices indicated by the memory_requirements.memoryTypeBits bit-field but also 2367 | // needs to have available all the properties that we want from desired properties. 2368 | assert(found_memory_type); 2369 | allocate_info.memoryTypeIndex = memory_type_index; 2370 | device_memory : VkDeviceMemory; 2371 | allocate_result := vkAllocateMemory(device, *allocate_info, vulkan_allocator, *device_memory); 2372 | assert(allocate_result == VK_SUCCESS); 2373 | 2374 | bind_result := vkBindBufferMemory(device, buffer, device_memory, memoryOffset=0); 2375 | assert(bind_result == VK_SUCCESS); 2376 | 2377 | return buffer, device_memory; 2378 | 2379 | find_memory_type_index :: (using vulkan_context : *Vulkan_Context, 2380 | memory_type_bits : u32, 2381 | desired_properties : VkMemoryPropertyFlags) -> found : bool, u32 2382 | { 2383 | for 0..vulkan_context.physical_device_memory_properties.memoryTypeCount-1 2384 | { 2385 | if (memory_type_bits & (1 << it)) && 2386 | ((desired_properties & vulkan_context.physical_device_memory_properties.memoryTypes[it].propertyFlags) == desired_properties) 2387 | { 2388 | return true, it; 2389 | } 2390 | } 2391 | return false, 0; 2392 | } 2393 | } 2394 | 2395 | 2396 | vulkan_make_vertex_and_index_buffer :: (using vulkan_context : *Vulkan_Context, 2397 | vertices : [] $Vertex_Type, 2398 | indices : [] $Index_Type) -> vertex_buffer : VkBuffer #must, 2399 | vertex_buffer_memory : VkDeviceMemory #must, 2400 | index_buffer : VkBuffer #must, 2401 | index_buffer_memory : VkDeviceMemory #must, 2402 | semaphore_signalled_when_ending : VkSemaphore #must, 2403 | fence_signalled_when_ending : VkFence #must 2404 | { 2405 | size_of_vertex_buffer := size_of(Vertex_Type) * vertices.count; 2406 | size_of_index_buffer := size_of(Index_Type) * indices.count; 2407 | 2408 | vertex_staging_buffer, vertex_staging_buffer_memory := vulkan_create_buffer(vulkan_context, 2409 | size_of_vertex_buffer, 2410 | desired_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 2411 | usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT); 2412 | defer vulkan_destroy_buffer(vulkan_context, *vertex_staging_buffer, *vertex_staging_buffer_memory); 2413 | 2414 | index_staging_buffer, index_staging_buffer_memory := vulkan_create_buffer(vulkan_context, 2415 | size_of_index_buffer, 2416 | desired_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 2417 | usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT); 2418 | defer vulkan_destroy_buffer(vulkan_context, *index_staging_buffer, *index_staging_buffer_memory); 2419 | 2420 | 2421 | vertex_buffer, vertex_buffer_memory := vulkan_create_buffer(vulkan_context, 2422 | size_of_vertex_buffer, 2423 | desired_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 2424 | usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); 2425 | 2426 | 2427 | index_buffer, index_buffer_memory := vulkan_create_buffer(vulkan_context, 2428 | size_of_index_buffer, 2429 | desired_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 2430 | usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); 2431 | 2432 | mapped_vertex_data : *Vertex_Type; 2433 | vkMapMemory(device, 2434 | vertex_staging_buffer_memory, 2435 | offset = 0, 2436 | size = cast(u64) size_of_vertex_buffer, 2437 | flags = 0, 2438 | ppData = xx *mapped_vertex_data); 2439 | memcpy(mapped_vertex_data, vertices.data, size_of_vertex_buffer); 2440 | vkUnmapMemory(device, vertex_staging_buffer_memory); 2441 | 2442 | mapped_index_data : *Index_Type; 2443 | vkMapMemory(device, 2444 | index_staging_buffer_memory, 2445 | offset = 0, 2446 | size = cast(u64) size_of_index_buffer, 2447 | flags = 0, 2448 | ppData = xx *mapped_index_data); 2449 | memcpy(mapped_index_data, indices.data, size_of_index_buffer); 2450 | vkUnmapMemory(device, index_staging_buffer_memory); 2451 | 2452 | 2453 | semaphore := vulkan_create_semaphore(vulkan_context); 2454 | all_finished_fence := vulkan_create_fence(vulkan_context, signaled = false); 2455 | 2456 | // 2457 | // @@TODO: Have to do an extra scope on this one time submits since both macros want to export 2458 | // the variable "command_buffer" and I'd get an error saying it's already defined. I would like to 2459 | // have a way to export "command_buffer" ONLY for the #code block we pass in. 2460 | // 2461 | 2462 | { 2463 | semaphores_to_wait_on : [0] VkSemaphore; 2464 | vulkan_one_time_submit_to_transfer_queue(vulkan_context, semaphores_to_wait_on = semaphores_to_wait_on, #code 2465 | { 2466 | vertex_copy_region : VkBufferCopy; 2467 | vertex_copy_region.srcOffset = 0; 2468 | vertex_copy_region.dstOffset = 0; 2469 | vertex_copy_region.size = cast(u64) size_of_vertex_buffer; 2470 | vkCmdCopyBuffer(command_buffer, srcBuffer = vertex_staging_buffer, dstBuffer = vertex_buffer, regionCount = 1, pRegions = *vertex_copy_region); 2471 | 2472 | index_copy_region : VkBufferCopy; 2473 | index_copy_region.srcOffset = 0; 2474 | index_copy_region.dstOffset = 0; 2475 | index_copy_region.size = cast(u64) size_of_index_buffer; 2476 | vkCmdCopyBuffer(command_buffer, srcBuffer = index_staging_buffer, dstBuffer = index_buffer, regionCount = 1, pRegions = *index_copy_region); 2477 | 2478 | vertex_buffer_memory_barrier : VkBufferMemoryBarrier; 2479 | vertex_buffer_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 2480 | vertex_buffer_memory_barrier.dstAccessMask = 0; 2481 | vertex_buffer_memory_barrier.srcQueueFamilyIndex = cast(u32) queue_family_indices.transfer_family_index; 2482 | vertex_buffer_memory_barrier.dstQueueFamilyIndex = cast(u32) queue_family_indices.graphics_family_index; 2483 | vertex_buffer_memory_barrier.buffer = vertex_buffer; 2484 | vertex_buffer_memory_barrier.offset = 0; 2485 | vertex_buffer_memory_barrier.size = VK_WHOLE_SIZE; 2486 | 2487 | index_buffer_memory_barrier : VkBufferMemoryBarrier; 2488 | index_buffer_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 2489 | index_buffer_memory_barrier.dstAccessMask = 0; 2490 | index_buffer_memory_barrier.srcQueueFamilyIndex = cast(u32) queue_family_indices.transfer_family_index; 2491 | index_buffer_memory_barrier.dstQueueFamilyIndex = cast(u32) queue_family_indices.graphics_family_index; 2492 | index_buffer_memory_barrier.buffer = index_buffer; 2493 | index_buffer_memory_barrier.offset = 0; 2494 | index_buffer_memory_barrier.size = VK_WHOLE_SIZE; 2495 | 2496 | buffer_memory_barriers : [2] VkBufferMemoryBarrier; 2497 | buffer_memory_barriers[0] = vertex_buffer_memory_barrier; 2498 | buffer_memory_barriers[1] = index_buffer_memory_barrier; 2499 | 2500 | vkCmdPipelineBarrier(command_buffer, 2501 | srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT, 2502 | dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 2503 | dependencyFlags = 0, 2504 | memoryBarrierCount = 0, 2505 | pMemoryBarriers = null, 2506 | bufferMemoryBarrierCount = buffer_memory_barriers.count, 2507 | pBufferMemoryBarriers = buffer_memory_barriers.data, 2508 | imageMemoryBarrierCount = 0, 2509 | pImageMemoryBarriers = null); 2510 | 2511 | }, *semaphore); 2512 | } 2513 | 2514 | { 2515 | semaphores_to_wait_on : [1] VkSemaphore; 2516 | semaphores_to_wait_on[0] = semaphore; 2517 | vulkan_one_time_submit_to_graphics_queue(vulkan_context, semaphores_to_wait_on = semaphores_to_wait_on, #code 2518 | { 2519 | vertex_buffer_memory_barrier : VkBufferMemoryBarrier; 2520 | vertex_buffer_memory_barrier.srcAccessMask = 0; 2521 | vertex_buffer_memory_barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; 2522 | vertex_buffer_memory_barrier.srcQueueFamilyIndex = cast(u32) queue_family_indices.transfer_family_index; 2523 | vertex_buffer_memory_barrier.dstQueueFamilyIndex = cast(u32) queue_family_indices.graphics_family_index; 2524 | vertex_buffer_memory_barrier.buffer = vertex_buffer; 2525 | vertex_buffer_memory_barrier.offset = 0; 2526 | vertex_buffer_memory_barrier.size = VK_WHOLE_SIZE; 2527 | 2528 | index_buffer_memory_barrier : VkBufferMemoryBarrier; 2529 | index_buffer_memory_barrier.srcAccessMask = 0; 2530 | index_buffer_memory_barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; 2531 | index_buffer_memory_barrier.srcQueueFamilyIndex = cast(u32) queue_family_indices.transfer_family_index; 2532 | index_buffer_memory_barrier.dstQueueFamilyIndex = cast(u32) queue_family_indices.graphics_family_index; 2533 | index_buffer_memory_barrier.buffer = index_buffer; 2534 | index_buffer_memory_barrier.offset = 0; 2535 | index_buffer_memory_barrier.size = VK_WHOLE_SIZE; 2536 | 2537 | buffer_memory_barriers : [2] VkBufferMemoryBarrier; 2538 | buffer_memory_barriers[0] = vertex_buffer_memory_barrier; 2539 | buffer_memory_barriers[1] = index_buffer_memory_barrier; 2540 | 2541 | vkCmdPipelineBarrier(command_buffer, 2542 | srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 2543 | dstStageMask = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 2544 | dependencyFlags = 0, 2545 | memoryBarrierCount = 0, 2546 | pMemoryBarriers = null, 2547 | bufferMemoryBarrierCount = buffer_memory_barriers.count, 2548 | pBufferMemoryBarriers = buffer_memory_barriers.data, 2549 | imageMemoryBarrierCount = 0, 2550 | pImageMemoryBarriers = null); 2551 | 2552 | }, semaphore_to_signal = *semaphore, fence_to_signal = *all_finished_fence); 2553 | } 2554 | 2555 | // @@IMPROVEMENT: Have to wait here since we're going to destroy the staging buffers at the 2556 | // end of this procedure (see defer uses above). If we wanted to not have to wait 2557 | // we could add these to a list with the associated fence and destroy them when the fence 2558 | // is signalled or something. 2559 | wait_result := vkWaitForFences(device, 2560 | fenceCount = 1, 2561 | pFences = *all_finished_fence, 2562 | waitAll = VK_TRUE, 2563 | timeout = 0xFF_FF_FF_FF_FF_FF_FF_FF); 2564 | assert(wait_result == VK_SUCCESS); 2565 | 2566 | return vertex_buffer, vertex_buffer_memory, 2567 | index_buffer, index_buffer_memory, 2568 | semaphore, all_finished_fence; 2569 | } 2570 | 2571 | 2572 | vulkan_destroy_buffer :: (using vulkan_context : *Vulkan_Context, buffer : *VkBuffer, device_memory : *VkDeviceMemory) 2573 | { 2574 | vkFreeMemory(device, < [..] Element_Type #must 2676 | { 2677 | array : [..] Element_Type; 2678 | array.allocator = __temporary_allocator; 2679 | array_reserve(*array, initial_reserved_count); 2680 | return array; 2681 | } 2682 | 2683 | 2684 | strings_to_cstrings :: (array : [] string) -> [..] *u8 #must 2685 | { 2686 | new_array := new_temporary_array(*u8); 2687 | for array 2688 | array_add(*new_array, it.data); 2689 | return new_array; 2690 | } 2691 | 2692 | 2693 | fill_extension_set :: (array_of_extension_properties : [] $Extension_Properties_Type) -> Vulkan_Context.Extension_Data.Is_Set 2694 | { 2695 | is_set : Vulkan_Context.Extension_Data.Is_Set; 2696 | for properties : array_of_extension_properties 2697 | { 2698 | name := to_string(properties.extensionName.data); 2699 | #insert #run () -> string 2700 | { 2701 | builder: String_Builder; 2702 | defer free_buffers(*builder); 2703 | for Extensions_We_Might_Want 2704 | { 2705 | og_name := it; 2706 | clean_name := cleanup_extension_name(og_name); 2707 | print_to_builder(*builder, "if name == \"%\" is_set.% = true;\n", og_name, clean_name); 2708 | } 2709 | return builder_to_string(*builder); 2710 | }(); 2711 | } 2712 | return is_set; 2713 | } 2714 | 2715 | 2716 | get_set_extension_cstrings :: (is_set : Vulkan_Context.Extension_Data.Is_Set) -> [] *u8 #must 2717 | { 2718 | names := new_temporary_array(*u8); 2719 | #insert #run () -> string 2720 | { 2721 | builder: String_Builder; 2722 | defer free_buffers(*builder); 2723 | for Extensions_We_Might_Want 2724 | { 2725 | og_name := it; 2726 | clean_name := cleanup_extension_name(og_name); 2727 | print_to_builder(*builder, "if is_set.% array_add(*names, \"%\");\n", clean_name, og_name); 2728 | } 2729 | return builder_to_string(*builder); 2730 | }(); 2731 | return names; 2732 | } 2733 | 2734 | 2735 | cleanup_extension_name :: (name : string) -> string 2736 | { 2737 | beg := "VK_"; 2738 | first_3 := name; 2739 | first_3.count = 3; 2740 | assert(beg == first_3); 2741 | new := name; 2742 | new.data += 3; 2743 | new.count -= 3; 2744 | while new[0] != #char "_" 2745 | { 2746 | new.data += 1; 2747 | new.count -= 1; 2748 | } 2749 | new.data += 1; 2750 | new.count -= 1; 2751 | return new; 2752 | } 2753 | 2754 | 2755 | 2756 | // 2757 | // Generates 2758 | // 2759 | // - vulkan_fill_array :: ($type : Type, procedure : $Procedure, WITH_ARGUMENTS_FOR_PROCEDURE_HERE) -> [] type {...} 2760 | // // Use this one when the procedure passed in returns a VkResult so we assert on it. 2761 | // 2762 | // - vulkan_fill_array_no_result :: ($type : Type, procedure : $Procedure, WITH_ARGUMENTS_FOR_PROCEDURE_HERE) -> [] type {...} 2763 | // // Use this one when the procedure doesn't return a VkResult or you don't want to check anything. 2764 | // 2765 | // @@TODO: I would really like to use varargs here, but I found no way to "flatten" it to the be able to call "procedure" 2766 | // so for now I'm just generating versions of this procedure with different number of arguments. 2767 | // 2768 | #insert #run () -> string 2769 | { 2770 | FILL_ARRAY_STRING :: #string DONE 2771 | 2772 | vulkan_fill_array :: ($type : Type, procedure : $Procedure%) -> [] type 2773 | { 2774 | array := new_temporary_array(type, 0); 2775 | count : u32; 2776 | result := procedure(%*count, null); 2777 | assert(result == VK_SUCCESS); 2778 | array_resize(*array, count); 2779 | result = procedure(%*count, array.data); 2780 | assert(result == VK_SUCCESS); 2781 | array.count = xx count; 2782 | return array; 2783 | } 2784 | 2785 | vulkan_fill_array_no_result :: ($type : Type, procedure : $Procedure%) -> [] type 2786 | { 2787 | array := new_temporary_array(type, 0); 2788 | count : u32; 2789 | procedure(%*count, null); 2790 | array_resize(*array, count); 2791 | procedure(%*count, array.data); 2792 | array.count = xx count; 2793 | return array; 2794 | } 2795 | DONE 2796 | 2797 | builder : String_Builder; 2798 | free_buffers(*builder); 2799 | for 0..17 2800 | { 2801 | arguments_builder : String_Builder; 2802 | free_buffers(*arguments_builder); 2803 | forwarding_args_builder : String_Builder; 2804 | free_buffers(*forwarding_args_builder); 2805 | 2806 | for 0..it-1 2807 | { 2808 | print_to_builder(*arguments_builder, ", argument_% : $Type_%", it, it); 2809 | print_to_builder(*forwarding_args_builder, "argument_%, ", it); 2810 | } 2811 | 2812 | arguments := builder_to_string(*arguments_builder); 2813 | forwarding_args := builder_to_string(*forwarding_args_builder); 2814 | print_to_builder(*builder, FILL_ARRAY_STRING, 2815 | arguments, 2816 | forwarding_args, forwarding_args, 2817 | arguments, 2818 | forwarding_args, forwarding_args); 2819 | } 2820 | 2821 | /* 2822 | 2823 | @@NOTE: I would like to be able to derive the return type from the procedure they pass in. This 2824 | would mean that we can get the type of the last argument and use it in the declaration of the procedure 2825 | which I can't find a way to do right now for it to work with polymorphism. Essentially I would like to do something 2826 | like the following: 2827 | 2828 | test :: ($procedure : $Procedure) -> #insert #run get_array_type(procedure) 2829 | { 2830 | // . . . 2831 | } 2832 | 2833 | get_array_type :: ($procedure : Procedure) -> string 2834 | { 2835 | info := type_info(procedure); 2836 | argument := info.return_types[info.return_types-1]; 2837 | assert(argument.type == Type_Info_Tag.POINTER); 2838 | argument_pointer := cast(*Type_Info_Pointer) argument; 2839 | type_pointed_to := argument_pointer.pointer_to; 2840 | assert(type_pointed_to.type == Type_Info_Tag.STRUCT); 2841 | type_pointed_to_struct := cast(*Type_Info_Struct) type_pointed_to; 2842 | return type_pointer_to_struct.name; 2843 | } 2844 | 2845 | */ 2846 | return builder_to_string(*builder); 2847 | }(); 2848 | 2849 | alloc_and_copy_array :: (array : [] $T, allocator := context.allocator) -> [] T #must 2850 | { 2851 | new_array := array; 2852 | new_array.data = xx alloc(array.count * size_of(T), allocator); 2853 | memcpy(new_array.data, array.data, array.count * size_of(T)); 2854 | return new_array; 2855 | } 2856 | 2857 | 2858 | copy_array_dst_src :: (dst : [] $T, src : [] T) 2859 | { 2860 | assert(dst.count == src.count); 2861 | memcpy(dst.data, src.data, size_of(T) * dst.count); 2862 | } 2863 | 2864 | 2865 | array_contains_string :: (array : [] string, the_string : string) -> bool 2866 | { 2867 | for array if it == the_string return true; 2868 | return false; 2869 | } 2870 | 2871 | 2872 | insert_array_of_bytes :: (name_of_constant : string, filename : string) -> string 2873 | { 2874 | builder : String_Builder; 2875 | init_string_builder(*builder); 2876 | defer free_buffers(*builder); 2877 | print_to_builder(*builder, "% :: u8.[ ", name_of_constant); 2878 | data, success := read_entire_file(filename); 2879 | if !success 2880 | { 2881 | print("Couldn't open file % to extract bytes from!!\n", filename); 2882 | exit(0); 2883 | } 2884 | array : [] u8; 2885 | array.data = data.data; 2886 | array.count = data.count; 2887 | for array 2888 | { 2889 | if it_index == array.count - 1 2890 | print_to_builder(*builder, "%", it); 2891 | else 2892 | print_to_builder(*builder, "%, ", it); 2893 | } 2894 | print_to_builder(*builder, "];\n", name_of_constant); 2895 | return builder_to_string(*builder); 2896 | } 2897 | 2898 | 2899 | offset_of :: ($struct_type : Type, name : string) -> offset : int, found : bool 2900 | { 2901 | info := type_info(struct_type); 2902 | for info.members 2903 | if it.name == name 2904 | return it.offset_in_bytes, true; 2905 | return 0, false; 2906 | } 2907 | 2908 | 2909 | make_projection_matrix_for_vulkan :: (fov_vertical_in_degrees : float, 2910 | aspect_ratio_horizontal_over_vertical : float, 2911 | near : float, 2912 | far : float) -> Matrix4 2913 | { 2914 | /* 2915 | @@NOTE: Vulkan uses +X right, +Y DOWN, and +Z forward for clip space. 2916 | They also have the clip space range being (0,1) instead of (-1,1) like 2917 | other APIs (X and Y are still -1 to 1). 2918 | 2919 | We're using +X right, +Y up (so we need to negate) and +Z backwards 2920 | (so we need to negate too). 2921 | */ 2922 | matrix : Matrix4; 2923 | fov_vertical := fov_vertical_in_degrees * (PI/180.0); 2924 | half_fov_vertical := fov_vertical / 2.0; 2925 | tan_half_fov := tan(half_fov_vertical); 2926 | near_minus_far := near - far; 2927 | matrix._11 = 1.0 / (tan_half_fov * aspect_ratio_horizontal_over_vertical); 2928 | matrix._22 = -1.0 / tan_half_fov; // Negating Y 2929 | matrix._33 = far / near_minus_far; // Negating Z 2930 | matrix._43 = -1.0; // Negating Z 2931 | matrix._34 = (near * far) / near_minus_far; 2932 | return matrix; 2933 | } 2934 | 2935 | 2936 | prepare_matrix_for_shader :: (matrix : Matrix4) -> Matrix4 2937 | { 2938 | // @@NOTE: I have to do this since Vulkan is expecting to receive 2939 | // the base vectors of the matrix contiguous in memory. In this case 2940 | // we're working with column vectors so they expect to get the columns 2941 | // all together. Matrix4 at the time of writing seems to be using 2942 | // column vectors that are layed out in memory in rows, so need to 2943 | // transpose here. 2944 | return transpose(matrix); 2945 | } 2946 | -------------------------------------------------------------------------------- /example/src/shaders/built/fullscreen_example.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/example/src/shaders/built/fullscreen_example.frag.spv -------------------------------------------------------------------------------- /example/src/shaders/built/fullscreen_example.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/example/src/shaders/built/fullscreen_example.vert.spv -------------------------------------------------------------------------------- /example/src/shaders/built/model_example.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/example/src/shaders/built/model_example.frag.spv -------------------------------------------------------------------------------- /example/src/shaders/built/model_example.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/example/src/shaders/built/model_example.vert.spv -------------------------------------------------------------------------------- /example/src/shaders/common_descriptor_sets.glsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | layout(set = 0, binding = 0) uniform per_frame_data 5 | { 6 | ivec2 resolution; 7 | float time; 8 | float delta_time; 9 | 10 | ivec2 mouse; 11 | int something; 12 | int something_else; 13 | }; 14 | 15 | 16 | layout(set = 1, binding = 0) uniform per_view_data 17 | { 18 | mat4 view_from_world; 19 | mat4 projection_from_view; 20 | }; 21 | 22 | 23 | layout(set = 2, binding = 0) uniform per_object_data 24 | { 25 | mat4 world_from_model; 26 | }; 27 | -------------------------------------------------------------------------------- /example/src/shaders/fullscreen_example.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #include "common_descriptor_sets.glsl" 4 | #define PI (3.141592653589) 5 | #define NOISE_OCTAVES (10) 6 | 7 | 8 | layout(location = 0) out vec4 output_color; 9 | 10 | 11 | float hash(vec2 p) 12 | { 13 | vec3 p3 = fract(vec3(p.xyx) * 0.13); 14 | p3 += dot(p3, p3.yzx + 3.333); 15 | return fract((p3.x + p3.y) * p3.z); 16 | } 17 | 18 | float noise(vec2 x) 19 | { 20 | vec2 i = floor(x); 21 | vec2 f = fract(x); 22 | float a = hash(i); 23 | float b = hash(i + vec2(1.0, 0.0)); 24 | float c = hash(i + vec2(0.0, 1.0)); 25 | float d = hash(i + vec2(1.0, 1.0)); 26 | vec2 u = f * f * (3.0 - 2.0 * f); 27 | return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; 28 | } 29 | 30 | float fbm(vec2 x) 31 | { 32 | float v = 0.0; 33 | float a = 0.5; 34 | vec2 shift = vec2(100); 35 | mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50)); 36 | for (int i = 0; i < NOISE_OCTAVES; ++i) { 37 | v += a * noise(x); 38 | x = rot * x * 2.0 + shift; 39 | a *= 0.5; 40 | } 41 | return v; 42 | } 43 | 44 | vec2 rotate(vec2 dir, float angle) 45 | { 46 | dir.x = cos(angle) * dir.x + -sin(angle) * dir.y; 47 | dir.y = sin(angle) * dir.x + cos(angle) * dir.y; 48 | return dir; 49 | } 50 | 51 | 52 | float ease_in_3(float x) 53 | { 54 | return (x*x*x); 55 | } 56 | 57 | float remap (float value, float min_in, float max_in, float min_out, float max_out) 58 | { 59 | return min_out + ((value - min_in) / (max_in - min_in)) * (max_out - min_out); 60 | } 61 | 62 | void main() 63 | { 64 | vec2 uv = gl_FragCoord.xy/resolution.xy; 65 | vec2 ndcuv = uv * 2.0 - 1.0; 66 | ndcuv.x *= float(resolution.x)/resolution.y; 67 | vec2 mouse_uv = vec2(mouse.xy)/resolution.xy; 68 | vec2 mouse_ndcuv = mouse_uv * 2.0 - 1.0; 69 | mouse_ndcuv.x *= float(resolution.x)/resolution.y; 70 | float distance_to_mouse = length(mouse_ndcuv - ndcuv); 71 | 72 | float angle_a = PI/2.0; 73 | float angle_b = PI/2.5; 74 | float angle_c = PI/0.5; 75 | vec2 dir_a = normalize(vec2(cos(angle_a), sin(angle_a))); 76 | vec2 dir_b = normalize(vec2(cos(angle_b), sin(angle_b))); 77 | vec2 dir_c = normalize(vec2(cos(angle_c), sin(angle_c))); 78 | 79 | ndcuv = rotate(ndcuv, length(ndcuv.xy)); 80 | float dist_to_seam = abs(ndcuv.y); 81 | 82 | vec2 dir = normalize(ndcuv.xy); 83 | float dist = length(ndcuv.xy); 84 | vec2 sample_coordinates; 85 | sample_coordinates.x = (atan(dir.y, dir.x)); 86 | sample_coordinates.y = dist; 87 | 88 | vec2 fbm_sample_a = sample_coordinates + (time*0.5)*dir_a; 89 | vec2 fbm_sample_b = sample_coordinates + (time*0.7)*dir_b; 90 | vec2 fbm_sample_c = sample_coordinates + (time*0.2)*dir_c; 91 | 92 | float angle = dist*0.2; 93 | fbm_sample_a = rotate(fbm_sample_a, angle); 94 | fbm_sample_b = rotate(fbm_sample_b, angle); 95 | fbm_sample_c = rotate(fbm_sample_c, angle); 96 | 97 | vec3 col = vec3(1,1,1); 98 | col.x = fbm(fbm_sample_a); 99 | col.y = fbm(fbm_sample_b); 100 | col.z = fbm(fbm_sample_c); 101 | 102 | float mouse_factor = 0.2 * ease_in_3(1.0 - clamp(remap(distance_to_mouse, 0, 2, 0, 1), 0, 1)); 103 | float baw = clamp(col.x * col.y * col.z * 1.0, 0.0, 1.0); 104 | baw = mix(0.0, baw, smoothstep(0.0, 0.05, dist_to_seam)); 105 | output_color = vec4(baw, baw-mouse_factor, baw-mouse_factor, 1.0); 106 | } -------------------------------------------------------------------------------- /example/src/shaders/fullscreen_example.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #include "common_descriptor_sets.glsl" 4 | 5 | vec2 fullscreen_positions[3] = vec2[] 6 | ( 7 | vec2(-1.0, -1.0), 8 | vec2(-1.0, 3.0), 9 | vec2( 3.0, -1.0) 10 | ); 11 | 12 | void main() 13 | { 14 | gl_Position = vec4(fullscreen_positions[gl_VertexIndex], 0.0, 1.0); 15 | } -------------------------------------------------------------------------------- /example/src/shaders/model_example.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #include "common_descriptor_sets.glsl" 4 | 5 | 6 | 7 | layout(location = 0) in vec3 vertex_color; 8 | 9 | layout(location = 0) out vec4 output_color; 10 | 11 | 12 | void main() 13 | { 14 | output_color = vec4(vertex_color, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /example/src/shaders/model_example.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #include "common_descriptor_sets.glsl" 4 | 5 | 6 | layout(location = 0) in vec3 vertex_position; 7 | layout(location = 1) in vec3 in_vertex_color; 8 | 9 | 10 | 11 | layout(location = 0) out vec3 out_vertex_color; 12 | 13 | 14 | mat4 rotation_matrix(vec3 axis,float angle) { 15 | vec3 n = normalize(axis); 16 | float s = sin(angle); 17 | float c = cos(angle); 18 | return mat4(c+(1.-c)*n.x*n.x , (1.-c)*n.x*n.y-s*n.z , (1.-c)*n.x*n.z+s*n.y , 0.0, 19 | (1.-c)*n.x*n.y+s*n.z, c+(1.-c)*n.y*n.y , (1.-c)*n.y*n.z-s*n.x , 0.0, 20 | (1.-c)*n.x*n.z-s*n.y, (1.-c)*n.y*n.z+s*n.x , c+(1.-c)*n.z*n.z , 0.0, 21 | 0.0 , 0.0 , 0.0 , 1.0); 22 | } 23 | 24 | void main() 25 | { 26 | float rotation_value = 1.0 - clamp(length((vec2(mouse.xy)/resolution.xy) * 2.0 - 1.0), 0, 1); 27 | float rotation_scale = 1.0 + rotation_value; 28 | 29 | mat4 extra_rotation = rotation_matrix(vec3(0,-3.0,0.7), 3.3 * sin(time) * rotation_scale); 30 | extra_rotation *= rotation_matrix(vec3(2,0.3,0.1), 0.3 * sin(0.5 + time*2) * rotation_scale); 31 | extra_rotation *= rotation_matrix(vec3(0.1,0.3,2), 4.6 * sin(0.7 + time*3) * rotation_scale); 32 | 33 | vec4 model_position = vec4(vertex_position, 1.0); 34 | mat4 world_from_model_rotated = world_from_model * extra_rotation; 35 | vec4 world_position = world_from_model_rotated * model_position; 36 | vec4 view_position = view_from_world * world_position; 37 | gl_Position = projection_from_view * view_position; 38 | out_vertex_color = mix(in_vertex_color, vec3(1.0,1.0,0.0), rotation_value); 39 | } -------------------------------------------------------------------------------- /generate_code/generate_vulkan_code.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/generate_code/generate_vulkan_code.exe -------------------------------------------------------------------------------- /generate_code/generate_vulkan_code.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osor-io/osor_vulkan/5f7df7fa03f99778ead8445ee14f69d151f75370/generate_code/generate_vulkan_code.pdb -------------------------------------------------------------------------------- /module.jai: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #load "vulkan_header.jai"; 5 | // Header with all the Vulkan types, constants, commands, etc. 6 | // with hopefully some useful documentation and links to help 7 | // understand the API. 8 | 9 | 10 | #load "vulkan_loader.jai"; 11 | // File that handles loading the pointers to Vulkan's procedures. -------------------------------------------------------------------------------- /vulkan_loader.jai: -------------------------------------------------------------------------------- 1 | /* 2 | This file handles loading the entry points for the Vulkan API. Vulkan works 3 | by having a dynamic library deployed on the system that you have to load and get the 4 | entry-points to. You can also link against their own loader and that will give you 5 | the procedures directly, but this would have *some* overhead and we don't want that :) 6 | 7 | In this file there is global variables that will hold all the procedure pointers for 8 | each entry-point, and then there is the procedures you can call to fill them. If you want 9 | to load the procedures into your own structures and not the global variables here, 10 | you can pass in the data structure to fill in each of the load_vulkan_* procedures. 11 | 12 | Usage should be fairly straight-forward, there's 3 types of commands (for our purposes): 13 | 14 | - Those which should be loaded directly from the library 15 | - Those which depend on the vulkan instance (VkInstance) and should be loaded from it 16 | - Those which depend on the vulkan device (VkDevice) and should be loaded from it 17 | 18 | To load each of the types you have: 19 | 20 | - load_vulkan_loader_procedures 21 | - load_vulkan_instance_procedures 22 | - load_vulkan_device_procedures 23 | 24 | An Example in Windows would look something like this: 25 | 26 | vulkan_library := LoadLibraryA("vulkan-1.dll"); 27 | load_vulkan_loader_procedures(vulkan_library , GetProcAddress); 28 | // . . . you do your code to create a VkInstance . . . 29 | load_vulkan_instance_procedures(your_vk_instance); 30 | // . . . you do your code to create a VkDevice . . . 31 | load_vulkan_device_procedures(your_vk_device); 32 | */ 33 | 34 | 35 | load_vulkan_loader_procedures :: (library_handle : $Library_Handle_Type, $load : $Loader_Procedure_Type, loader_commands : *Vulkan_Loader_Commands = null) 36 | { 37 | loader_commands = ifx loader_commands then loader_commands else *_vulkan_loader_commands; 38 | info := type_info(type_of(<