├── .gitignore ├── README.md ├── test ├── deps.cpp └── main.c ├── example.cmake ├── LICENSE └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vk_sdl 2 | Vulkan Dev Starter Pack with SDL3, CMake, C23 and more! 3 | https://www.youtube.com/watch?v=WLjuV9_fQfw 4 | -------------------------------------------------------------------------------- /test/deps.cpp: -------------------------------------------------------------------------------- 1 | #define VMA_IMPLEMENTATION 2 | #define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2 3 | #define VMA_STATIC_VULKAN_FUNCTIONS 0 4 | #define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 5 | 6 | #include -------------------------------------------------------------------------------- /example.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.26) 2 | project(example C) 3 | enable_language(CXX) 4 | 5 | set(CMAKE_C_STANDARD 23) 6 | 7 | file( 8 | DOWNLOAD 9 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.38.3/CPM.cmake 10 | ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake 11 | EXPECTED_HASH SHA256=cc155ce02e7945e7b8967ddfaff0b050e958a723ef7aad3766d368940cb15494 12 | ) 13 | include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake) 14 | 15 | CPMAddPackage("gh:ilyas-taouaou/vk_sdl#main") 16 | 17 | add_executable(${PROJECT_NAME} main.c) 18 | target_link_libraries(${PROJECT_NAME} PRIVATE vk_sdl) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ilyas taouaou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.26) 2 | project(vk_sdl C CXX) 3 | 4 | set(CMAKE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 5 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 6 | set(CMAKE_PDB_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 7 | 8 | file( 9 | DOWNLOAD 10 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.38.3/CPM.cmake 11 | ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake 12 | EXPECTED_HASH SHA256=cc155ce02e7945e7b8967ddfaff0b050e958a723ef7aad3766d368940cb15494 13 | ) 14 | include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake) 15 | 16 | CPMAddPackage("gh:libsdl-org/SDL#6cf84e2") 17 | CPMAddPackage("gh:libsdl-org/SDL_image#7744158") 18 | CPMAddPackage("gh:libsdl-org/SDL_ttf#f9b636f") 19 | CPMAddPackage("gh:libsdl-org/SDL_mixer#8ab0d03") 20 | 21 | CPMAddPackage("gh:google/googletest#v1.14.x") 22 | 23 | CPMAddPackage("gh:KhronosGroup/Vulkan-Headers#vulkan-sdk-1.3.268.0") 24 | CPMAddPackage("gh:KhronosGroup/SPIRV-Headers#vulkan-sdk-1.3.268.0") 25 | CPMAddPackage("gh:KhronosGroup/SPIRV-Tools#vulkan-sdk-1.3.268.0") 26 | CPMAddPackage("gh:KhronosGroup/glslang#vulkan-sdk-1.3.268.0") 27 | 28 | CPMAddPackage("gh:zeux/volk#41c8f70") 29 | 30 | CPMAddPackage("gh:GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator#dc8a3fb") 31 | 32 | CPMAddPackage("gh:recp/cglm#d93b46b") 33 | 34 | CPMAddPackage("gh:assimp/assimp#a7cfa32") 35 | 36 | CPMAddPackage("gh:cimgui/cimgui#4ca6b29") 37 | 38 | CPMAddPackage( 39 | NAME enet 40 | GITHUB_REPOSITORY lsalzman/enet 41 | GIT_TAG 2a85cd6 42 | DOWNLOAD_ONLY True 43 | ) 44 | 45 | if (enet_ADDED) 46 | add_library(enet) 47 | target_include_directories(enet PUBLIC "${enet_SOURCE_DIR}/include") 48 | target_sources(enet PRIVATE 49 | ${enet_SOURCE_DIR}/callbacks.c 50 | ${enet_SOURCE_DIR}/compress.c 51 | ${enet_SOURCE_DIR}/host.c 52 | ${enet_SOURCE_DIR}/list.c 53 | ${enet_SOURCE_DIR}/packet.c 54 | ${enet_SOURCE_DIR}/peer.c 55 | ${enet_SOURCE_DIR}/protocol.c 56 | ${enet_SOURCE_DIR}/unix.c 57 | ${enet_SOURCE_DIR}/win32.c) 58 | endif () 59 | 60 | add_library(${PROJECT_NAME} INTERFACE) 61 | target_link_libraries(${PROJECT_NAME} INTERFACE 62 | Vulkan::Headers 63 | SPIRV-Headers::SPIRV-Headers 64 | volk::volk_headers 65 | glslang 66 | GPUOpen::VulkanMemoryAllocator 67 | SDL3::SDL3 68 | SDL3_image::SDL3_image 69 | SDL3_mixer::SDL3_mixer 70 | SDL3_ttf::SDL3_ttf 71 | cglm 72 | assimp::assimp 73 | cimgui 74 | enet 75 | ) 76 | 77 | if (PROJECT_IS_TOP_LEVEL) 78 | add_executable(${PROJECT_NAME}_test test/main.c test/deps.cpp) 79 | target_link_libraries(${PROJECT_NAME}_test PRIVATE ${PROJECT_NAME}) 80 | endif () 81 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | #define VK_NO_PROTOTYPES 2 | #define VOLK_IMPLEMENTATION 3 | #define CIMGUI_DEFINE_ENUMS_AND_STRUCTS 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "cimgui.h" 21 | 22 | #include 23 | 24 | typedef struct { 25 | VkInstance instance; 26 | VkPhysicalDevice physicalDevice; 27 | VkDevice device; 28 | VkQueue queue; 29 | VmaAllocator allocator; 30 | VkSurfaceKHR surface; 31 | } VulkanContext; 32 | 33 | typedef struct { 34 | SDL_Window *window; 35 | VulkanContext context; 36 | ImGuiContext *imgui; 37 | bool shouldClose; 38 | } App; 39 | 40 | void createVulkanContext(VulkanContext *ctx, Uint32 apiVersion) { 41 | SDL_Vulkan_LoadLibrary(NULL); 42 | volkInitializeCustom((PFN_vkGetInstanceProcAddr) SDL_Vulkan_GetVkGetInstanceProcAddr()); 43 | 44 | Uint32 extensionsCount; 45 | char const *const *requiredExtensions = SDL_Vulkan_GetInstanceExtensions(&extensionsCount); 46 | #ifdef __MACOS__ 47 | char **extensions = SDL_malloc(sizeof(char *) * (extensionsCount + 1)); 48 | SDL_memcpy(extensions, requiredExtensions, sizeof(char *) * extensionsCount); 49 | #endif 50 | vkCreateInstance(&(VkInstanceCreateInfo) { 51 | .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 52 | #ifdef __MACOS__ 53 | .enabledExtensionCount = extensionsCount + 1, 54 | .ppEnabledExtensionNames = extensions, 55 | #else 56 | .enabledExtensionCount = extensionsCount, 57 | .ppEnabledExtensionNames = requiredExtensions, 58 | #endif 59 | .pApplicationInfo = &(VkApplicationInfo) { 60 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 61 | .apiVersion = apiVersion, 62 | }, 63 | #ifdef __MACOS__ 64 | .flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR, 65 | #endif 66 | }, NULL, &ctx->instance); 67 | volkLoadInstanceOnly(ctx->instance); 68 | 69 | vkEnumeratePhysicalDevices(ctx->instance, &(Uint32) {1}, &ctx->physicalDevice); 70 | 71 | vkCreateDevice(ctx->physicalDevice, &(VkDeviceCreateInfo) { 72 | .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 73 | #ifdef __MACOS__ 74 | .enabledExtensionCount = 2, 75 | #else 76 | .enabledExtensionCount = 1, 77 | #endif 78 | .ppEnabledExtensionNames = (char const *[]) { 79 | VK_KHR_SWAPCHAIN_EXTENSION_NAME, 80 | #ifdef __MACOS__ 81 | VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, 82 | #endif 83 | }, 84 | .queueCreateInfoCount = 1, 85 | .pQueueCreateInfos = &(VkDeviceQueueCreateInfo) { 86 | .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 87 | .queueFamilyIndex = 0, 88 | .queueCount = 1, 89 | .pQueuePriorities = (float[]) {1.0f}, 90 | }, 91 | }, NULL, &ctx->device); 92 | volkLoadDevice(ctx->device); 93 | 94 | VkQueue queue; 95 | vkGetDeviceQueue(ctx->device, 0, 0, &queue); 96 | 97 | vmaCreateAllocator(&(VmaAllocatorCreateInfo) { 98 | .physicalDevice = ctx->physicalDevice, 99 | .device = ctx->device, 100 | .vulkanApiVersion = apiVersion, 101 | .instance = ctx->instance, 102 | .pVulkanFunctions = &(VmaVulkanFunctions) { 103 | .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, 104 | .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, 105 | .vkAllocateMemory = vkAllocateMemory, 106 | .vkFreeMemory = vkFreeMemory, 107 | .vkMapMemory = vkMapMemory, 108 | .vkUnmapMemory = vkUnmapMemory, 109 | .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, 110 | .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, 111 | .vkBindBufferMemory = vkBindBufferMemory, 112 | .vkBindImageMemory = vkBindImageMemory, 113 | .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, 114 | .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, 115 | .vkCreateBuffer = vkCreateBuffer, 116 | .vkDestroyBuffer = vkDestroyBuffer, 117 | .vkCreateImage = vkCreateImage, 118 | .vkDestroyImage = vkDestroyImage, 119 | .vkCmdCopyBuffer = vkCmdCopyBuffer, 120 | .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, 121 | .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, 122 | .vkBindBufferMemory2KHR = vkBindBufferMemory2, 123 | .vkBindImageMemory2KHR = vkBindImageMemory2, 124 | .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, 125 | }, 126 | }, &ctx->allocator); 127 | } 128 | 129 | void bindWindow(VulkanContext *context, SDL_Window *window) { 130 | if (context->surface) return; 131 | SDL_Vulkan_CreateSurface(window, context->instance, &context->surface); 132 | } 133 | 134 | void unbindWindow(VulkanContext *context) { 135 | if (!context->surface) return; 136 | vkDestroySurfaceKHR(context->instance, context->surface, NULL); 137 | context->surface = VK_NULL_HANDLE; 138 | } 139 | 140 | int main(int argc, char **argv) { 141 | SDL_Init(SDL_INIT_EVERYTHING); 142 | IMG_Init(IMG_INIT_PNG); 143 | Mix_Init(MIX_INIT_MP3); 144 | TTF_Init(); 145 | enet_initialize(); 146 | 147 | App app; 148 | app.window = SDL_CreateWindow("Hello World", 800, 600, 149 | SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE); 150 | 151 | createVulkanContext(&app.context, VK_API_VERSION_1_2); 152 | 153 | ImGuiContext *context = igCreateContext(NULL); 154 | 155 | #ifndef __ANDROID__ 156 | bindWindow(&app.context, app.window); 157 | #endif 158 | 159 | SDL_ShowWindow(app.window); 160 | 161 | while (!app.shouldClose) { 162 | for (SDL_Event event; SDL_PollEvent(&event);) { 163 | switch (event.type) { 164 | case SDL_EVENT_QUIT: 165 | app.shouldClose = true; 166 | break; 167 | case SDL_EVENT_DID_ENTER_FOREGROUND: 168 | bindWindow(&app.context, app.window); 169 | break; 170 | case SDL_EVENT_WILL_ENTER_BACKGROUND: 171 | unbindWindow(&app.context); 172 | break; 173 | } 174 | } 175 | } 176 | 177 | vkDeviceWaitIdle(app.context.device); 178 | 179 | return 0; 180 | } 181 | --------------------------------------------------------------------------------