From 7a268ae92d44a9f27f4874e1e50413ee33b86dd3 Mon Sep 17 00:00:00 2001 From: Danny Holman Date: Mon, 4 Aug 2025 12:32:39 -0500 Subject: root: restructuring Restructure the root of the project such that the engine is siloed from the rest of the toolchain. Add two new subdirectories that contain an editor and an offline profiling data analyzer. Signed-off-by: Danny Holman --- CMake/SubmoduleDefines.cmake | 60 +++++++ CMakeLists.txt | 97 ++--------- core/abort.c | 93 ----------- core/alloc.c | 157 ------------------ core/callbacks.c | 27 ---- core/init.c | 37 ----- core/logging.c | 114 ------------- core/mod.c | 90 ----------- core/thread.c | 218 ------------------------- editor/CMakeLists.txt | 13 ++ engine/CMakeLists.txt | 48 ++++++ engine/core/abort.c | 93 +++++++++++ engine/core/alloc.c | 178 ++++++++++++++++++++ engine/core/callbacks.c | 27 ++++ engine/core/init.c | 48 ++++++ engine/core/logging.c | 114 +++++++++++++ engine/core/mod.c | 90 +++++++++++ engine/core/thread.c | 218 +++++++++++++++++++++++++ engine/render/directx/renderer.c | 40 +++++ engine/render/vulkan/context.c | 217 +++++++++++++++++++++++++ engine/render/vulkan/context.h | 32 ++++ engine/render/vulkan/device.c | 324 +++++++++++++++++++++++++++++++++++++ engine/render/vulkan/device.h | 34 ++++ engine/render/vulkan/fence.c | 84 ++++++++++ engine/render/vulkan/fence.h | 33 ++++ engine/render/vulkan/framebuffer.c | 55 +++++++ engine/render/vulkan/framebuffer.h | 30 ++++ engine/render/vulkan/image.c | 99 ++++++++++++ engine/render/vulkan/image.h | 30 ++++ engine/render/vulkan/renderer.c | 279 ++++++++++++++++++++++++++++++++ engine/render/vulkan/renderpass.c | 249 ++++++++++++++++++++++++++++ engine/render/vulkan/renderpass.h | 49 ++++++ engine/render/vulkan/swapchain.c | 152 +++++++++++++++++ engine/render/vulkan/swapchain.h | 33 ++++ engine/render/vulkan/vk_types.h | 143 ++++++++++++++++ engine/render/vulkan/vkassert.h | 68 ++++++++ profiler/CMakeLists.txt | 9 ++ render/directx/renderer.c | 40 ----- render/vulkan/context.c | 217 ------------------------- render/vulkan/context.h | 32 ---- render/vulkan/device.c | 248 ---------------------------- render/vulkan/device.h | 34 ---- render/vulkan/fence.c | 84 ---------- render/vulkan/fence.h | 33 ---- render/vulkan/framebuffer.c | 55 ------- render/vulkan/framebuffer.h | 30 ---- render/vulkan/image.c | 99 ------------ render/vulkan/image.h | 30 ---- render/vulkan/renderer.c | 273 ------------------------------- render/vulkan/renderpass.c | 249 ---------------------------- render/vulkan/renderpass.h | 49 ------ render/vulkan/swapchain.c | 150 ----------------- render/vulkan/swapchain.h | 33 ---- render/vulkan/vk_types.h | 130 --------------- render/vulkan/vkassert.h | 68 -------- 55 files changed, 2858 insertions(+), 2678 deletions(-) create mode 100644 CMake/SubmoduleDefines.cmake delete mode 100644 core/abort.c delete mode 100644 core/alloc.c delete mode 100644 core/callbacks.c delete mode 100644 core/init.c delete mode 100644 core/logging.c delete mode 100644 core/mod.c delete mode 100644 core/thread.c create mode 100644 editor/CMakeLists.txt create mode 100644 engine/CMakeLists.txt create mode 100644 engine/core/abort.c create mode 100644 engine/core/alloc.c create mode 100644 engine/core/callbacks.c create mode 100644 engine/core/init.c create mode 100644 engine/core/logging.c create mode 100644 engine/core/mod.c create mode 100644 engine/core/thread.c create mode 100644 engine/render/directx/renderer.c create mode 100644 engine/render/vulkan/context.c create mode 100644 engine/render/vulkan/context.h create mode 100644 engine/render/vulkan/device.c create mode 100644 engine/render/vulkan/device.h create mode 100644 engine/render/vulkan/fence.c create mode 100644 engine/render/vulkan/fence.h create mode 100644 engine/render/vulkan/framebuffer.c create mode 100644 engine/render/vulkan/framebuffer.h create mode 100644 engine/render/vulkan/image.c create mode 100644 engine/render/vulkan/image.h create mode 100644 engine/render/vulkan/renderer.c create mode 100644 engine/render/vulkan/renderpass.c create mode 100644 engine/render/vulkan/renderpass.h create mode 100644 engine/render/vulkan/swapchain.c create mode 100644 engine/render/vulkan/swapchain.h create mode 100644 engine/render/vulkan/vk_types.h create mode 100644 engine/render/vulkan/vkassert.h create mode 100644 profiler/CMakeLists.txt delete mode 100644 render/directx/renderer.c delete mode 100644 render/vulkan/context.c delete mode 100644 render/vulkan/context.h delete mode 100644 render/vulkan/device.c delete mode 100644 render/vulkan/device.h delete mode 100644 render/vulkan/fence.c delete mode 100644 render/vulkan/fence.h delete mode 100644 render/vulkan/framebuffer.c delete mode 100644 render/vulkan/framebuffer.h delete mode 100644 render/vulkan/image.c delete mode 100644 render/vulkan/image.h delete mode 100644 render/vulkan/renderer.c delete mode 100644 render/vulkan/renderpass.c delete mode 100644 render/vulkan/renderpass.h delete mode 100644 render/vulkan/swapchain.c delete mode 100644 render/vulkan/swapchain.h delete mode 100644 render/vulkan/vk_types.h delete mode 100644 render/vulkan/vkassert.h diff --git a/CMake/SubmoduleDefines.cmake b/CMake/SubmoduleDefines.cmake new file mode 100644 index 0000000..edd006b --- /dev/null +++ b/CMake/SubmoduleDefines.cmake @@ -0,0 +1,60 @@ +list(APPEND SUBMODULE_INCLUDE_DIRS ${SUBMODULE_HEADER_DIR}) + +include(EnableCFLAG) +if (MSVC) + enable_c_compiler_flag_if_supported("/GS") +else () + enable_c_compiler_flag_if_supported("-fstack-protector-all") +endif () + +if (WIN32) + find_package(dlfcn-win32 REQUIRED) + find_package(DBGHELP REQUIRED) + list(APPEND SUBMODULE_LINK_LIBS dlfcn-win32::dl ${DBGHELP_LIBRARY}) +endif() + +if (DEFINED SUBMODULE_LIBRARY) + add_definitions(-DRAPI_EXPORT) + set(SUBMODULE_BINARY ${SUBMODULE_LIBRARY}) + add_library(${SUBMODULE_BINARY} SHARED ${SUBMODULE_FILES}) + set_target_properties(${SUBMODULE_BINARY} PROPERTIES VERSION ${PROJECT_VERSION}) + set_target_properties(${SUBMODULE_BINARY} PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) + target_include_directories(${SUBMODULE_BINARY} PUBLIC ${SUBMODULE_INCLUDE_DIRS}) + target_link_libraries(${SUBMODULE_BINARY} PUBLIC ${SUBMODULE_LINK_LIBS}) +elseif (DEFINED SUBMODULE_EXECUTABLE) + set(SUBMODULE_BINARY ${SUBMODULE_EXECUTABLE}) + add_executable(${SUBMODULE_BINARY} ${SUBMODULE_FILES}) + set_target_properties(${SUBMODULE_BINARY} PROPERTIES VERSION ${PROJECT_VERSION}) + target_include_directories(${SUBMODULE_BINARY} PRIVATE ${SUBMODULE_INCLUDE_DIRS}) + target_link_libraries(${SUBMODULE_BINARY} PRIVATE ${SUBMODULE_LINK_LIBS}) +else () + message(FATAL_ERROR "Unknown submodule type defined by submodule ${CMAKE_CURRENT_SOURCE_DIR}") +endif () + +target_compile_definitions(${SUBMODULE_BINARY} PUBLIC RUNE_VER="${CMAKE_PROJECT_VERSION}") +target_compile_definitions(${SUBMODULE_BINARY} PUBLIC RUNE_VER_MAJOR="${CMAKE_PROJECT_VERSION_MAJOR}") +target_compile_definitions(${SUBMODULE_BINARY} PUBLIC RUNE_VER_MINOR="${CMAKE_PROJECT_VERSION_MINOR}") +target_compile_definitions(${SUBMODULE_BINARY} PUBLIC RUNE_VER_PATCH="${CMAKE_PROJECT_VERSION_PATCH}") + +option(ENABLE_PROFILING "Enable profiling") +if (ENABLE_PROFILING) + target_compile_definitions(${SUBMODULE_BINARY} PUBLIC RUNE_PROFILING) +endif () + +include(GNUInstallDirs) +install(TARGETS ${SUBMODULE_BINARY} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT ${SUBMODULE_BINARY}_Runtime + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT ${SUBMODULE_BINARY}_Runtime + NAMELINK_COMPONENT ${SUBMODULE_BINARY}_Development + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT ${SUBMODULE_BINARY}_Development +) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${SUBMODULE_BINARY}Version.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90770d1..0fa8785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,110 +1,31 @@ cmake_minimum_required(VERSION 3.20) project(rune-engine VERSION 0.63.0 DESCRIPTION "High performance game engine designed for Quake-style shooters") -set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) -list(APPEND SOURCE_FILES - core/abort.c - core/alloc.c - core/callbacks.c - core/init.c - core/logging.c - core/mod.c - core/network.c - core/thread.c -) - -list(APPEND SOURCE_FILES - render/directx/renderer.c - render/vulkan/context.c - render/vulkan/device.c - render/vulkan/fence.c - render/vulkan/framebuffer.c - render/vulkan/image.c - render/vulkan/renderer.c - render/vulkan/renderpass.c - render/vulkan/swapchain.c - ui/window.c - ui/input.c -) - find_package(Vulkan REQUIRED) find_package(glfw3 REQUIRED) find_package(OpenAL REQUIRED) find_package(cglm REQUIRED) +find_package(json-c REQUIRED) find_package(Threads REQUIRED) -set(HEADER_DIR include) - -list(APPEND INCLUDE_DIRS - ${HEADER_DIR} +set(ENGINE_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) +list(APPEND SUBMODULE_INCLUDE_DIRS + ${ENGINE_HEADER_DIR} ${Vulkan_INCLUDE_DIRS} ${GLFW_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR} ) -list(APPEND LINK_LIBS - ${Vulkan_LIBRARIES} - ${GLFW_LIBRARIES} - ${OPENAL_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} -) - -if (WIN32) - find_package(dlfcn-win32 REQUIRED) - find_package(DBGHELP REQUIRED) - set(GLFW_LIBRARIES glfw3dll) - set(DX12_LIBRARIES d3d12.lib dxgi.lib dxguid.lib) - set(DL_LIBRARIES dlfcn-win32::dl) - list(APPEND LINK_LIBS ${DX12_LIBRARIES} ${DL_LIBRARIES} ${DBGHELP_LIBRARY}) -else () - set(GLFW_LIBRARIES glfw) -endif () - -include(EnableCFLAG) -if (MSVC) - enable_c_compiler_flag_if_supported("/GS") -else () - enable_c_compiler_flag_if_supported("-fstack-protector-all") -endif () - -list(APPEND LINK_LIBS ${GLFW_LIBRARIES}) - -add_compile_definitions(RUNE_VER="${PROJECT_VERSION}") -add_compile_definitions(RUNE_VER_MAJOR="${PROJECT_VERSION_MAJOR}") -add_compile_definitions(RUNE_VER_MINOR="${PROJECT_VERSION_MINOR}") -add_compile_definitions(RUNE_VER_PATCH="${PROJECT_VERSION_PATCH}") -add_compile_definitions(RAPI_EXPORT) -add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON) -target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS}) -target_link_libraries(${PROJECT_NAME} ${LINK_LIBS}) -set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) -set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) - +add_subdirectory("engine") +add_subdirectory("editor") +add_subdirectory("profiler") add_subdirectory("doc") -include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - COMPONENT ${PROJECT_NAME}_Runtime - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - COMPONENT ${PROJECT_NAME}_Runtime - NAMELINK_COMPONENT ${PROJECT_NAME}_Development - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - COMPONENT ${PROJECT_NAME}_Development -) -install(DIRECTORY ${HEADER_DIR}/rune DESTINATION include) - -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - ${PROJECT_NAME}Version.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion -) +install(DIRECTORY ${ENGINE_HEADER_DIR}/rune DESTINATION include) diff --git a/core/abort.c b/core/abort.c deleted file mode 100644 index 5ee42f6..0000000 --- a/core/abort.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include -#include -#include - -#define MAX_TRACE_ITEMS 30 - -#ifdef _WIN32 - -#include -#include - -void _stack_trace(void) { - void* buffer[MAX_TRACE_ITEMS]; - HANDLE process = GetCurrentProcess(); - - SymInitialize(process, NULL, TRUE); - int num_links = CaptureStackBackTrace(0, MAX_TRACE_ITEMS, buffer, NULL); - SYMBOL_INFO *symbol = (SYMBOL_INFO*)rune_alloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char)); - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = 255; - - for (int i = 0; i < num_links; i++) { - SymFromAddr(process, (DWORD64)(buffer[i]), 0, symbol); - log_output(LOG_INFO, "#%d: %s", i, symbol->Name, symbol->Address); - } - - rune_free(symbol); - SymCleanup(process); -} - -#else - -#include - -void _stack_trace(void) { - void* buffer[MAX_TRACE_ITEMS]; - int num_links = backtrace(buffer, MAX_TRACE_ITEMS); - char** symbuf = backtrace_symbols(buffer, num_links); - - for (int i = 0; i < num_links; i++) - log_output(LOG_INFO, "#%d: %s", i, symbuf[i]); - - free(symbuf); -} - -#endif - -NORET void rune_abort(void) { - log_output(LOG_INFO, "Abort called, printing stack trace"); - _stack_trace(); - rune_exit(); - exit(REXIT_FAIL); -} - -#ifdef MSVC - -NORET void __security_error_handler(void) { - log_output(LOG_FATAL, "Stack smashing detected in engine code"); - rune_abort(); -} - -#else - -NORET void __stack_chk_fail(void) { - log_output(LOG_FATAL, "Stack smashing detected in engine code"); - rune_abort(); -} - -#endif diff --git a/core/alloc.c b/core/alloc.c deleted file mode 100644 index 0ed705c..0000000 --- a/core/alloc.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include - -// TODO: implement block coalescing so we can reuse freed blocks - -#define DEADBLOCK ((void*)0xDEADBEEF) - -static mem_block_t first_block; - -static mem_block_t* _find_free_block(size_t sz) { - list_head_t *temp = &first_block.list; - mem_block_t *block; - while (temp != NULL) { - block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); - if (block->sz == sz && block->free == 1) - return block; - temp = temp->next; - } - return NULL; -} - -static mem_block_t* _find_block(void *ptr) { - list_head_t *temp = &first_block.list; - mem_block_t *block; - while (temp != NULL) { - block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); - if (block->ptr == ptr) - return block; - temp = temp->next; - } - return NULL; -} - -static mem_block_t* _alloc_block(size_t sz) { - if (first_block.ptr == NULL) { - first_block.ptr == DEADBLOCK; - first_block.sz = 0; - } - - mem_block_t *ret = _find_free_block(sz); - if (ret != NULL) { - ret->free = 0; - return ret->ptr; - } - - ret = malloc(sizeof(mem_block_t)); - if (ret == NULL) { - log_output(LOG_ERROR, "Cannot allocate block of size %d", sz); - return NULL; - } - - ret->ptr = malloc(sz); - ret->sz = sz; - ret->free = 0; - list_add(&ret->list, &first_block.list); - log_output(LOG_DEBUG, "Alloc'd block of size %d", sz); - return ret; -} - -static void _free_block(mem_block_t *block, int hard) { - if (hard == 1) { - list_del(&block->list); - log_output(LOG_DEBUG, "Freed block of size %d", block->sz); - free(block); - return; - } - block->free = 1; -} - -void* rune_alloc(size_t sz) { - if (sz == 0) - return NULL; - - mem_block_t *block = _find_free_block(sz); - if (block != NULL) { - block->free = 0; - return block->ptr; - } - - block = _alloc_block(sz); - return block->ptr; -} - -void* rune_calloc(size_t nmemb, size_t sz) { - if (sz == 0) - return NULL; - - mem_block_t *block = _find_free_block(sz); - if (block != NULL) { - block->free = 0; - return block->ptr; - } - - block = _alloc_block(sz); - memset(block->ptr, 0, sz); - return block->ptr; -} - -void* rune_realloc(void *ptr, size_t sz) { - if (ptr == NULL || sz == 0) - return rune_alloc(sz); - - mem_block_t *old = _find_block(ptr); - mem_block_t *new = _alloc_block(sz); - memcpy(new->ptr, old->ptr, old->sz); - old->free = 1; - return new->ptr; -} - -void rune_free(void *ptr) { - if (ptr == NULL) - return; - - mem_block_t *block = _find_block(ptr); - if (block->free == 1) - return; - block->free = 1; -} - -void rune_free_all(void) { - list_head_t *temp = &first_block.list; - mem_block_t *block; - while (temp != NULL) { - block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); - if (block->ptr == DEADBLOCK) { - temp = temp->next; - continue; - } - - temp = temp->next; - if (block->ptr != NULL) - _free_block(block, 1); - } -} diff --git a/core/callbacks.c b/core/callbacks.c deleted file mode 100644 index ea7f50e..0000000 --- a/core/callbacks.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include - -void error_callback(int error, const char *desc) { - log_output(LOG_ERROR, "%d: %s\n", error, desc); -} diff --git a/core/init.c b/core/init.c deleted file mode 100644 index 5df4d04..0000000 --- a/core/init.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include - -int rune_init(int argc, char* argv[]) { - log_output(LOG_INFO, "Started Rune Engine version %s", RUNE_VER); - _parse_args(argc, argv); - rune_init_thread_api(); - return 0; -} - -void rune_exit(void) { - log_output(LOG_INFO, "Engine shutdown requested"); - rune_free_all(); -} diff --git a/core/logging.c b/core/logging.c deleted file mode 100644 index b7f77bd..0000000 --- a/core/logging.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include -#include - -#define COLOR_NONE "" -#define COLOR_DEFAULT "\033[0m" -#define COLOR_RED "\033[31m" -#define COLOR_GREEN "\033[32m" -#define COLOR_YELLOW "\033[33m" -#define COLOR_BLUE "\033[34m" - -#define LSTR_FATAL "[FATAL]" -#define LSTR_ERROR "[ERROR]" -#define LSTR_WARN "[WARNING]" -#define LSTR_INFO "[INFO]" -#define LSTR_DEBUG "[DEBUG]" - -static int debug_enabled = 0; -static int color_enabled = 0; - -void log_output(int level, const char *fmt, ...) { - char out[4096]; - memset(out, 0, sizeof(out)); - - debug_enabled = rune_get_log_debug(); - color_enabled = rune_get_log_color(); - - va_list arg_ptr; - va_start(arg_ptr, fmt); - vsnprintf(out, 4096, fmt, arg_ptr); - va_end(arg_ptr); - - char *lvl_str; - char *color = COLOR_NONE; - switch (level) { - case LOG_FATAL: - if (color_enabled == 1) - color = COLOR_RED; - lvl_str = LSTR_FATAL; - break; - case LOG_ERROR: - if (color_enabled == 1) - color = COLOR_RED; - lvl_str = LSTR_ERROR; - break; - case LOG_WARN: - if (color_enabled == 1) - color = COLOR_YELLOW; - lvl_str = LSTR_WARN; - break; - case LOG_INFO: - lvl_str = LSTR_INFO; - break; - case LOG_DEBUG: - if (color_enabled == 1) - color = COLOR_GREEN; - if (debug_enabled == 0) - return; - lvl_str = LSTR_DEBUG; - break; - } - - if (color_enabled == 0) { - printf("%s %s\n", lvl_str, out); - } else { - printf("%s%s %s\n", color, lvl_str, out); - printf(COLOR_DEFAULT); - } -} - -//#ifdef _WIN32 -// -//#include -// -//void enable_log_color(void) { -// HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); -// SetConsoleMode(handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING); -// color_enabled = 1; -//} -// -//#else -// -//void enable_log_color(void) { -// color_enabled = 1; -//} -// -//#endif -// -//void disable_log_color(void) { -// color_enabled = 0; -//} diff --git a/core/mod.c b/core/mod.c deleted file mode 100644 index 3d9e710..0000000 --- a/core/mod.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include -#include -#include -#include - -list_head_t *mods = NULL; - -void _load_mod(const char *filename) { - char mod_path[4096]; - sprintf(mod_path, "mods/%s", filename); - void *handle = dlopen(mod_path, RTLD_LAZY); - if (handle == NULL) - log_output(LOG_ERROR, "Error loading mod %s: %s", filename, dlerror()); -} - -void rune_load_mods(void) { - DIR *dir = opendir("mods"); - if (dir == NULL) { - log_output(LOG_INFO, "No mods folder found, skipping mod loading"); - return; - } - - struct dirent *mods_de; - while ((mods_de = readdir(dir)) != NULL) { - if (strstr(mods_de->d_name, ".so")) - _load_mod(mods_de->d_name); - } -} - -void rune_init_mods(void) { - list_head_t *temp = mods; - struct mod *mod; - while (temp != NULL) { - mod = (struct mod*)((void*)temp - offsetof(struct mod, list)); - (*mod->init_func)(); - temp = temp->next; - } -} - -void rune_close_mods(void) { - if (mods == NULL) - return; - - list_head_t *temp = mods; - struct mod *mod; - while (temp != NULL) { - mod = (struct mod*)((void*)temp - offsetof(struct mod, list)); - (*mod->exit_func)(); - temp = temp->next; - rune_free(mod); - } -} - -void rune_register_mod(const char *name, mod_func init_func, mod_func exit_func, mod_func update_func) { - struct mod *new = rune_alloc(sizeof(struct mod)); - new->name = name; - new->init_func = init_func; - new->exit_func = exit_func; - new->update_func = update_func; - new->list.next = NULL; - new->list.prev = NULL; - if (mods == NULL) - mods = &new->list; - else - list_add(&new->list, mods); -} diff --git a/core/thread.c b/core/thread.c deleted file mode 100644 index 3c5db8c..0000000 --- a/core/thread.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include -#include -#include - -static list_head_t *threads = NULL; -static list_head_t *mutexes = NULL; -static int next_tid = 0; -static int next_mid = 0; - -struct start_args { - struct thread *thread; - void* (*thread_fn)(void*); - void *thread_args; -}; - -static struct thread* _find_thread_by_handle(void *handle) { - if (threads == NULL) - return NULL; - - list_head_t *temp = threads; - struct thread *ret; - while (temp != NULL) { - ret = (struct thread*)((void*)temp - offsetof(struct thread, list)); - if (pthread_equal(*(pthread_t*)ret->thread_handle, *(pthread_t*)handle) == 1) - return ret; - temp = temp->next; - } - return NULL; -} - -static struct thread* _find_thread_by_id(int ID) { - if (threads == NULL) - return NULL; - - list_head_t *temp = threads; - struct thread *ret; - while (temp != NULL) { - ret = (struct thread*)((void*)temp - offsetof(struct thread, list)); - if (ret->ID == ID) - return ret; - temp = temp->next; - } - return NULL; -} - -static struct mutex* _find_mutex_by_id(int ID) { - if (mutexes == NULL) - return NULL; - - list_head_t *temp = mutexes; - struct mutex *ret; - while (temp != NULL) { - ret = (struct mutex*)((void*)temp - offsetof(struct mutex, list)); - if (ret->ID == ID) - return ret; - temp = temp->next; - } - return NULL; -} - - -static void _cleanup_pthread(void *arg) { - struct thread *thread = (struct thread*)arg; - if (&thread->list != threads) - list_del(&thread->list); - rune_free(thread->thread_handle); - rune_free(thread); -} - -static void* _startup_pthread(void *arg) { - struct start_args *start_args = (struct start_args*)arg; - - void* (*thread_fn)(void *data) = start_args->thread_fn; - pthread_cleanup_push(_cleanup_pthread, start_args->thread); - if (thread_fn != NULL) - (*thread_fn)(start_args->thread_args); - pthread_cleanup_pop(1); - return NULL; -} - -void rune_init_thread_api(void) { - struct thread *start_thread = rune_alloc(sizeof(struct thread)); - start_thread->ID = next_tid++; - start_thread->detached = 0; - start_thread->thread_handle = rune_alloc(sizeof(pthread_t)); - *(pthread_t*)start_thread->thread_handle = pthread_self(); - pthread_cleanup_push(_cleanup_pthread, threads); - pthread_cleanup_pop(0); -} - -int rune_thread_init(void* (*thread_fn)(void *data), void *data, int detached) { - struct thread *thread = rune_alloc(sizeof(struct thread)); - thread->ID = next_tid++; - thread->detached = detached; - thread->thread_handle = rune_alloc(sizeof(pthread_t)); - if (threads == NULL) - threads = &thread->list; - else - list_add(&thread->list, threads); - - struct start_args *args = rune_alloc(sizeof(struct start_args)); - args->thread = thread; - args->thread_fn = thread_fn; - args->thread_args = data; - int retval = pthread_create(thread->thread_handle, NULL, _startup_pthread, args); - rune_free(args); - if (retval != 0) { - log_output(LOG_ERROR, "Thread creation failed: %s", strerror(retval)); - return -1; - } - log_output(LOG_DEBUG, "Initialized new thread with ID=%d", thread->ID); - if (detached == 1) { - log_output(LOG_DEBUG, "Thread %d has been detached, join no longer possible"); - pthread_detach(*(pthread_t*)thread->thread_handle); - } - return thread->ID; -} - -int rune_thread_cancel(int ID) { - log_output(LOG_DEBUG, "Received cancel request for thread %d", ID); - struct thread *thread = _find_thread_by_id(ID); - if (thread == NULL) { - log_output(LOG_ERROR, "Thread %d does not exist", ID); - return -1; - } - pthread_cancel(*((pthread_t*)thread->thread_handle)); - return 0; -} - -int rune_thread_join(int ID, void **retval) { - struct thread *thread = _find_thread_by_id(ID); - if (thread == NULL) - return 0; - - if (thread->detached == 1) { - log_output(LOG_ERROR, "Cannot join thread %d, detached", thread->ID); - return -1; - } - pthread_join(*((pthread_t*)thread->thread_handle), retval); - return 0; -} - -int rune_thread_self(void) { - pthread_t cur = pthread_self(); - struct thread *ret = _find_thread_by_handle((void*)&cur); - if (ret != NULL) - return ret->ID; - return -1; -} - -void rune_thread_exit(void *retval) { - pthread_t cur = pthread_self(); - struct thread *self = _find_thread_by_handle((void*)&cur); - log_output(LOG_DEBUG, "Thread %d called thread_exit", self->ID); - pthread_exit(retval); -} - -int rune_mutex_init(void) { - struct mutex *mutex = rune_alloc(sizeof(struct mutex)); - mutex->ID = next_mid++; - mutex->mutex_handle = rune_alloc(sizeof(pthread_mutex_t)); - pthread_mutex_init((pthread_mutex_t*)mutex->mutex_handle, NULL); - if (mutexes == NULL) - mutexes = &mutex->list; - else - list_add(&mutex->list, mutexes); - return mutex->ID; -} - -int rune_mutex_destroy(int ID) { - struct mutex *mutex = _find_mutex_by_id(ID); - rune_free(mutex->mutex_handle); - if (&mutex->list != mutexes) - list_del(&mutex->list); - rune_free(mutex); -} - -int rune_mutex_lock(int ID) { - struct mutex *mutex = _find_mutex_by_id(ID); - int retval = pthread_mutex_lock((pthread_mutex_t*)mutex->mutex_handle); - if (retval != 0) { - char *str = strerror(retval); - log_output(LOG_ERROR, "Cannot lock mutex %d: %s", mutex->ID, str); - } -} - -int rune_mutex_unlock(int ID) { - struct mutex *mutex = _find_mutex_by_id(ID); - int retval = pthread_mutex_unlock((pthread_mutex_t*)mutex->mutex_handle); - if (retval != 0) { - char *str = strerror(retval); - log_output(LOG_ERROR, "Cannot unlock mutex %d: %s", mutex->ID, str); - } -} diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt new file mode 100644 index 0000000..d633eb4 --- /dev/null +++ b/editor/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SUBMODULE_EXECUTABLE rune-editor) + +list(APPEND SUBMODULE_FILES + src/editor.c +) + +list(APPEND SUBMODULE_LINK_LIBS + rune-engine +) + +set(SUBMODULE_HEADER_DIR ${CMAKE_SOURCE_DIR}/editor/include) + +include(${CMAKE_SOURCE_DIR}/CMake/SubmoduleDefines.cmake) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000..0d1465f --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,48 @@ +set(SUBMODULE_LIBRARY rune-engine) + +list(APPEND SUBMODULE_FILES + core/abort.c + core/alloc.c + core/callbacks.c + core/config.c + core/console.c + core/init.c + core/logging.c + core/mesh.c + core/mod.c + core/object.c + core/profiling.c + core/thread.c +) + +list(APPEND SUBMODULE_FILES + render/directx/renderer.c + render/vulkan/context.c + render/vulkan/device.c + render/vulkan/fence.c + render/vulkan/framebuffer.c + render/vulkan/image.c + render/vulkan/renderer.c + render/vulkan/renderpass.c + render/vulkan/swapchain.c + ui/input.c + ui/panel.c + ui/window.c + sound/sound.c +) + +list(APPEND SUBMODULE_LINK_LIBS + ${Vulkan_LIBRARIES} + ${GLFW_LIBRARIES} + ${OPENAL_LIBRARY} + json-c::json-c + ${CMAKE_THREAD_LIBS_INIT} +) + +if (WIN32) + list(APPEND SUBMODULE_LINK_LIBS glfw3dll d3d12.lib dxgi.lib dxguid.lib) +else () + list(APPEND SUBMODULE_LINK_LIBS glfw) +endif () + +include(${CMAKE_SOURCE_DIR}/CMake/SubmoduleDefines.cmake) diff --git a/engine/core/abort.c b/engine/core/abort.c new file mode 100644 index 0000000..5ee42f6 --- /dev/null +++ b/engine/core/abort.c @@ -0,0 +1,93 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_TRACE_ITEMS 30 + +#ifdef _WIN32 + +#include +#include + +void _stack_trace(void) { + void* buffer[MAX_TRACE_ITEMS]; + HANDLE process = GetCurrentProcess(); + + SymInitialize(process, NULL, TRUE); + int num_links = CaptureStackBackTrace(0, MAX_TRACE_ITEMS, buffer, NULL); + SYMBOL_INFO *symbol = (SYMBOL_INFO*)rune_alloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char)); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = 255; + + for (int i = 0; i < num_links; i++) { + SymFromAddr(process, (DWORD64)(buffer[i]), 0, symbol); + log_output(LOG_INFO, "#%d: %s", i, symbol->Name, symbol->Address); + } + + rune_free(symbol); + SymCleanup(process); +} + +#else + +#include + +void _stack_trace(void) { + void* buffer[MAX_TRACE_ITEMS]; + int num_links = backtrace(buffer, MAX_TRACE_ITEMS); + char** symbuf = backtrace_symbols(buffer, num_links); + + for (int i = 0; i < num_links; i++) + log_output(LOG_INFO, "#%d: %s", i, symbuf[i]); + + free(symbuf); +} + +#endif + +NORET void rune_abort(void) { + log_output(LOG_INFO, "Abort called, printing stack trace"); + _stack_trace(); + rune_exit(); + exit(REXIT_FAIL); +} + +#ifdef MSVC + +NORET void __security_error_handler(void) { + log_output(LOG_FATAL, "Stack smashing detected in engine code"); + rune_abort(); +} + +#else + +NORET void __stack_chk_fail(void) { + log_output(LOG_FATAL, "Stack smashing detected in engine code"); + rune_abort(); +} + +#endif diff --git a/engine/core/alloc.c b/engine/core/alloc.c new file mode 100644 index 0000000..b75f1d3 --- /dev/null +++ b/engine/core/alloc.c @@ -0,0 +1,178 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include + +// TODO: implement block coalescing so we can reuse freed blocks + +#define DEADBLOCK ((void*)0xDEADBEEF) + +static mem_block_t first_block; + +static mem_block_t* _find_free_block(size_t sz) { + list_head_t *temp = &first_block.list; + mem_block_t *block; + while (temp != NULL) { + block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); + if (block->sz == sz && block->free == 1) + return block; + temp = temp->next; + } + return NULL; +} + +static mem_block_t* _find_block(void *ptr) { + list_head_t *temp = &first_block.list; + mem_block_t *block; + while (temp != NULL) { + block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); + if (block->ptr == ptr) + return block; + temp = temp->next; + } + return NULL; +} + +static mem_block_t* _alloc_block(size_t sz) { + RUNE_PROFILE_SCOPE("Block allocation"); + if (first_block.ptr == NULL) { + first_block.ptr = DEADBLOCK; + first_block.sz = 0; + } + + mem_block_t *ret = _find_free_block(sz); + if (ret != NULL) { + ret->free = 0; + RUNE_PROFILE_END(); + return ret->ptr; + } + + ret = malloc(sizeof(mem_block_t)); + if (ret == NULL) { + RUNE_PROFILE_END(); + log_output(LOG_ERROR, "Cannot allocate block of size %d", sz); + return NULL; + } + + ret->ptr = malloc(sz); + ret->sz = sz; + ret->free = 0; + list_add(&ret->list, &first_block.list); + RUNE_PROFILE_END(); + log_output(LOG_DEBUG, "Alloc'd block of size %d", sz); + return ret; +} + +static void _free_block(mem_block_t *block, int hard) { + RUNE_PROFILE_SCOPE("Block free"); + if (hard == 1) { + list_del(&block->list); + RUNE_PROFILE_END(); + free(block); + log_output(LOG_DEBUG, "Freed block of size %d", block->sz); + return; + } + block->free = 1; +} + +void* rune_alloc(size_t sz) { + if (sz == 0) + return NULL; + + RUNE_PROFILE_SCOPE("Pool allocation"); + mem_block_t *block = _find_free_block(sz); + if (block != NULL) { + block->free = 0; + RUNE_PROFILE_END(); + return block->ptr; + } + + block = _alloc_block(sz); + RUNE_PROFILE_END(); + return block->ptr; +} + +void* rune_calloc(size_t nmemb, size_t sz) { + if (sz == 0) + return NULL; + + RUNE_PROFILE_SCOPE("Zero array pool allocation"); + mem_block_t *block = _find_free_block(sz); + if (block != NULL) { + block->free = 0; + RUNE_PROFILE_END(); + return block->ptr; + } + + block = _alloc_block(sz); + memset(block->ptr, 0, sz); + RUNE_PROFILE_END(); + return block->ptr; +} + +void* rune_realloc(void *ptr, size_t sz) { + if (ptr == NULL || sz == 0) + return rune_alloc(sz); + + RUNE_PROFILE_SCOPE("Pool reallocation"); + mem_block_t *old = _find_block(ptr); + mem_block_t *new = _alloc_block(sz); + memcpy(new->ptr, old->ptr, old->sz); + old->free = 1; + RUNE_PROFILE_END(); + return new->ptr; +} + +void rune_free(void *ptr) { + if (ptr == NULL) + return; + + RUNE_PROFILE_SCOPE("Pool free"); + mem_block_t *block = _find_block(ptr); + if (block->free == 1) { + RUNE_PROFILE_END(); + return; + } + block->free = 1; + RUNE_PROFILE_END(); +} + +void rune_free_all(void) { + RUNE_PROFILE_SCOPE("Pool free all"); + list_head_t *temp = &first_block.list; + mem_block_t *block; + while (temp != NULL) { + block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); + if (block->ptr == DEADBLOCK) { + temp = temp->next; + continue; + } + + temp = temp->next; + if (block->ptr != NULL) + _free_block(block, 1); + } + RUNE_PROFILE_END(); +} diff --git a/engine/core/callbacks.c b/engine/core/callbacks.c new file mode 100644 index 0000000..ea7f50e --- /dev/null +++ b/engine/core/callbacks.c @@ -0,0 +1,27 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include + +void error_callback(int error, const char *desc) { + log_output(LOG_ERROR, "%d: %s\n", error, desc); +} diff --git a/engine/core/init.c b/engine/core/init.c new file mode 100644 index 0000000..6fcd086 --- /dev/null +++ b/engine/core/init.c @@ -0,0 +1,48 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int rune_init(int argc, char* argv[]) { + log_output(LOG_INFO, "Started Rune Engine version %s", RUNE_VER); + + rune_init_default_settings(); + rune_init_thread_api(); + + rune_load_mods(); + rune_init_mods(); + + return 0; +} + +void rune_exit(void) { + log_output(LOG_INFO, "Engine shutdown requested"); + rune_clear_objs(); + rune_close_mods(); + rune_free_all(); +} diff --git a/engine/core/logging.c b/engine/core/logging.c new file mode 100644 index 0000000..b7f77bd --- /dev/null +++ b/engine/core/logging.c @@ -0,0 +1,114 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include + +#define COLOR_NONE "" +#define COLOR_DEFAULT "\033[0m" +#define COLOR_RED "\033[31m" +#define COLOR_GREEN "\033[32m" +#define COLOR_YELLOW "\033[33m" +#define COLOR_BLUE "\033[34m" + +#define LSTR_FATAL "[FATAL]" +#define LSTR_ERROR "[ERROR]" +#define LSTR_WARN "[WARNING]" +#define LSTR_INFO "[INFO]" +#define LSTR_DEBUG "[DEBUG]" + +static int debug_enabled = 0; +static int color_enabled = 0; + +void log_output(int level, const char *fmt, ...) { + char out[4096]; + memset(out, 0, sizeof(out)); + + debug_enabled = rune_get_log_debug(); + color_enabled = rune_get_log_color(); + + va_list arg_ptr; + va_start(arg_ptr, fmt); + vsnprintf(out, 4096, fmt, arg_ptr); + va_end(arg_ptr); + + char *lvl_str; + char *color = COLOR_NONE; + switch (level) { + case LOG_FATAL: + if (color_enabled == 1) + color = COLOR_RED; + lvl_str = LSTR_FATAL; + break; + case LOG_ERROR: + if (color_enabled == 1) + color = COLOR_RED; + lvl_str = LSTR_ERROR; + break; + case LOG_WARN: + if (color_enabled == 1) + color = COLOR_YELLOW; + lvl_str = LSTR_WARN; + break; + case LOG_INFO: + lvl_str = LSTR_INFO; + break; + case LOG_DEBUG: + if (color_enabled == 1) + color = COLOR_GREEN; + if (debug_enabled == 0) + return; + lvl_str = LSTR_DEBUG; + break; + } + + if (color_enabled == 0) { + printf("%s %s\n", lvl_str, out); + } else { + printf("%s%s %s\n", color, lvl_str, out); + printf(COLOR_DEFAULT); + } +} + +//#ifdef _WIN32 +// +//#include +// +//void enable_log_color(void) { +// HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); +// SetConsoleMode(handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING); +// color_enabled = 1; +//} +// +//#else +// +//void enable_log_color(void) { +// color_enabled = 1; +//} +// +//#endif +// +//void disable_log_color(void) { +// color_enabled = 0; +//} diff --git a/engine/core/mod.c b/engine/core/mod.c new file mode 100644 index 0000000..3d9e710 --- /dev/null +++ b/engine/core/mod.c @@ -0,0 +1,90 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include +#include +#include + +list_head_t *mods = NULL; + +void _load_mod(const char *filename) { + char mod_path[4096]; + sprintf(mod_path, "mods/%s", filename); + void *handle = dlopen(mod_path, RTLD_LAZY); + if (handle == NULL) + log_output(LOG_ERROR, "Error loading mod %s: %s", filename, dlerror()); +} + +void rune_load_mods(void) { + DIR *dir = opendir("mods"); + if (dir == NULL) { + log_output(LOG_INFO, "No mods folder found, skipping mod loading"); + return; + } + + struct dirent *mods_de; + while ((mods_de = readdir(dir)) != NULL) { + if (strstr(mods_de->d_name, ".so")) + _load_mod(mods_de->d_name); + } +} + +void rune_init_mods(void) { + list_head_t *temp = mods; + struct mod *mod; + while (temp != NULL) { + mod = (struct mod*)((void*)temp - offsetof(struct mod, list)); + (*mod->init_func)(); + temp = temp->next; + } +} + +void rune_close_mods(void) { + if (mods == NULL) + return; + + list_head_t *temp = mods; + struct mod *mod; + while (temp != NULL) { + mod = (struct mod*)((void*)temp - offsetof(struct mod, list)); + (*mod->exit_func)(); + temp = temp->next; + rune_free(mod); + } +} + +void rune_register_mod(const char *name, mod_func init_func, mod_func exit_func, mod_func update_func) { + struct mod *new = rune_alloc(sizeof(struct mod)); + new->name = name; + new->init_func = init_func; + new->exit_func = exit_func; + new->update_func = update_func; + new->list.next = NULL; + new->list.prev = NULL; + if (mods == NULL) + mods = &new->list; + else + list_add(&new->list, mods); +} diff --git a/engine/core/thread.c b/engine/core/thread.c new file mode 100644 index 0000000..3c5db8c --- /dev/null +++ b/engine/core/thread.c @@ -0,0 +1,218 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include +#include + +static list_head_t *threads = NULL; +static list_head_t *mutexes = NULL; +static int next_tid = 0; +static int next_mid = 0; + +struct start_args { + struct thread *thread; + void* (*thread_fn)(void*); + void *thread_args; +}; + +static struct thread* _find_thread_by_handle(void *handle) { + if (threads == NULL) + return NULL; + + list_head_t *temp = threads; + struct thread *ret; + while (temp != NULL) { + ret = (struct thread*)((void*)temp - offsetof(struct thread, list)); + if (pthread_equal(*(pthread_t*)ret->thread_handle, *(pthread_t*)handle) == 1) + return ret; + temp = temp->next; + } + return NULL; +} + +static struct thread* _find_thread_by_id(int ID) { + if (threads == NULL) + return NULL; + + list_head_t *temp = threads; + struct thread *ret; + while (temp != NULL) { + ret = (struct thread*)((void*)temp - offsetof(struct thread, list)); + if (ret->ID == ID) + return ret; + temp = temp->next; + } + return NULL; +} + +static struct mutex* _find_mutex_by_id(int ID) { + if (mutexes == NULL) + return NULL; + + list_head_t *temp = mutexes; + struct mutex *ret; + while (temp != NULL) { + ret = (struct mutex*)((void*)temp - offsetof(struct mutex, list)); + if (ret->ID == ID) + return ret; + temp = temp->next; + } + return NULL; +} + + +static void _cleanup_pthread(void *arg) { + struct thread *thread = (struct thread*)arg; + if (&thread->list != threads) + list_del(&thread->list); + rune_free(thread->thread_handle); + rune_free(thread); +} + +static void* _startup_pthread(void *arg) { + struct start_args *start_args = (struct start_args*)arg; + + void* (*thread_fn)(void *data) = start_args->thread_fn; + pthread_cleanup_push(_cleanup_pthread, start_args->thread); + if (thread_fn != NULL) + (*thread_fn)(start_args->thread_args); + pthread_cleanup_pop(1); + return NULL; +} + +void rune_init_thread_api(void) { + struct thread *start_thread = rune_alloc(sizeof(struct thread)); + start_thread->ID = next_tid++; + start_thread->detached = 0; + start_thread->thread_handle = rune_alloc(sizeof(pthread_t)); + *(pthread_t*)start_thread->thread_handle = pthread_self(); + pthread_cleanup_push(_cleanup_pthread, threads); + pthread_cleanup_pop(0); +} + +int rune_thread_init(void* (*thread_fn)(void *data), void *data, int detached) { + struct thread *thread = rune_alloc(sizeof(struct thread)); + thread->ID = next_tid++; + thread->detached = detached; + thread->thread_handle = rune_alloc(sizeof(pthread_t)); + if (threads == NULL) + threads = &thread->list; + else + list_add(&thread->list, threads); + + struct start_args *args = rune_alloc(sizeof(struct start_args)); + args->thread = thread; + args->thread_fn = thread_fn; + args->thread_args = data; + int retval = pthread_create(thread->thread_handle, NULL, _startup_pthread, args); + rune_free(args); + if (retval != 0) { + log_output(LOG_ERROR, "Thread creation failed: %s", strerror(retval)); + return -1; + } + log_output(LOG_DEBUG, "Initialized new thread with ID=%d", thread->ID); + if (detached == 1) { + log_output(LOG_DEBUG, "Thread %d has been detached, join no longer possible"); + pthread_detach(*(pthread_t*)thread->thread_handle); + } + return thread->ID; +} + +int rune_thread_cancel(int ID) { + log_output(LOG_DEBUG, "Received cancel request for thread %d", ID); + struct thread *thread = _find_thread_by_id(ID); + if (thread == NULL) { + log_output(LOG_ERROR, "Thread %d does not exist", ID); + return -1; + } + pthread_cancel(*((pthread_t*)thread->thread_handle)); + return 0; +} + +int rune_thread_join(int ID, void **retval) { + struct thread *thread = _find_thread_by_id(ID); + if (thread == NULL) + return 0; + + if (thread->detached == 1) { + log_output(LOG_ERROR, "Cannot join thread %d, detached", thread->ID); + return -1; + } + pthread_join(*((pthread_t*)thread->thread_handle), retval); + return 0; +} + +int rune_thread_self(void) { + pthread_t cur = pthread_self(); + struct thread *ret = _find_thread_by_handle((void*)&cur); + if (ret != NULL) + return ret->ID; + return -1; +} + +void rune_thread_exit(void *retval) { + pthread_t cur = pthread_self(); + struct thread *self = _find_thread_by_handle((void*)&cur); + log_output(LOG_DEBUG, "Thread %d called thread_exit", self->ID); + pthread_exit(retval); +} + +int rune_mutex_init(void) { + struct mutex *mutex = rune_alloc(sizeof(struct mutex)); + mutex->ID = next_mid++; + mutex->mutex_handle = rune_alloc(sizeof(pthread_mutex_t)); + pthread_mutex_init((pthread_mutex_t*)mutex->mutex_handle, NULL); + if (mutexes == NULL) + mutexes = &mutex->list; + else + list_add(&mutex->list, mutexes); + return mutex->ID; +} + +int rune_mutex_destroy(int ID) { + struct mutex *mutex = _find_mutex_by_id(ID); + rune_free(mutex->mutex_handle); + if (&mutex->list != mutexes) + list_del(&mutex->list); + rune_free(mutex); +} + +int rune_mutex_lock(int ID) { + struct mutex *mutex = _find_mutex_by_id(ID); + int retval = pthread_mutex_lock((pthread_mutex_t*)mutex->mutex_handle); + if (retval != 0) { + char *str = strerror(retval); + log_output(LOG_ERROR, "Cannot lock mutex %d: %s", mutex->ID, str); + } +} + +int rune_mutex_unlock(int ID) { + struct mutex *mutex = _find_mutex_by_id(ID); + int retval = pthread_mutex_unlock((pthread_mutex_t*)mutex->mutex_handle); + if (retval != 0) { + char *str = strerror(retval); + log_output(LOG_ERROR, "Cannot unlock mutex %d: %s", mutex->ID, str); + } +} diff --git a/engine/render/directx/renderer.c b/engine/render/directx/renderer.c new file mode 100644 index 0000000..8820441 --- /dev/null +++ b/engine/render/directx/renderer.c @@ -0,0 +1,40 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include + +#ifdef _WIN32 + +struct rune_renderer* select_render_directx(window_t *window) { + log_output(LOG_FATAL, "DirectX support has not yet been implemented"); + rune_abort(); +} + +#else + +struct rune_renderer* select_render_directx(window_t *window) { + log_output(LOG_FATAL, "DirectX is not supported on this platform"); + rune_abort(); +} + +#endif diff --git a/engine/render/vulkan/context.c b/engine/render/vulkan/context.c new file mode 100644 index 0000000..1282706 --- /dev/null +++ b/engine/render/vulkan/context.c @@ -0,0 +1,217 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "context.h" +#include "vkassert.h" +#include +#include +#include +#include +#include + +VKAPI_ATTR VkBool32 VKAPI_CALL _vulkan_db_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_types, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *user_data) { + switch (message_severity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + log_output(LOG_ERROR, callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + log_output(LOG_WARN, callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + log_output(LOG_INFO, callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + log_output(LOG_DEBUG, callback_data->pMessage); + break; + default: + break; + } + return VK_FALSE; +} + +int _init_vkdebugger(vkcontext_t *context) { + log_output(LOG_INFO, "Initializing Vulkan debugger"); + + VkDebugUtilsMessengerCreateInfoEXT dbinfo; + dbinfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + dbinfo.pNext = NULL; + dbinfo.flags = 0; + dbinfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + dbinfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + dbinfo.pfnUserCallback = _vulkan_db_callback; + dbinfo.pUserData = NULL; + + PFN_vkCreateDebugUtilsMessengerEXT func = + (PFN_vkCreateDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(context->instance, + "vkCreateDebugUtilsMessengerEXT"); + + if (func == NULL) { + log_output(LOG_ERROR, "Failed to create Vulkan debugger"); + return -1; + } + + VkResult res; + res = func(context->instance, &dbinfo, NULL, &context->db_messenger); + if (res != VK_SUCCESS) { + char *err_str = get_vkerr_str(res); + log_output(LOG_ERROR, "Debug session error: %s", err_str); + free(err_str); + return -1; + } + return 0; +} + +uint32_t _vertoi(const char *version) { + uint32_t major = 0; + uint32_t minor = 0; + uint32_t patch = 0; + char *version_copy = strdup(version); + char *token = strtok(version_copy, "."); + if (token != NULL) + major = (uint32_t)atoi(token); + + token = strtok(NULL, "."); + if (token != NULL) + minor = (uint32_t)atoi(token); + + token = strtok(NULL, "."); + if (token != NULL) + patch = (uint32_t)atoi(token); + + free(version_copy); + return (major << 22) | (minor << 12) | patch; +} + +vkcontext_t* create_vkcontext(vklayer_container_t *vklayers, ext_container_t *ext) { + vkcontext_t *ret = rune_calloc(0, sizeof(vkcontext_t)); + ret->surface = rune_alloc(sizeof(vksurface_t)); + + VkApplicationInfo app_info; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pNext = NULL; + app_info.apiVersion = VK_API_VERSION_1_2; + app_info.pApplicationName = rune_get_app_name(); + const char *app_ver = rune_get_app_ver(); + app_info.applicationVersion = _vertoi(app_ver); + app_info.pEngineName = "RuneEngine"; + app_info.engineVersion = _vertoi(RUNE_VER); + + VkInstanceCreateInfo cinfo; + cinfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + cinfo.pNext = NULL; + cinfo.flags = 0; + cinfo.pApplicationInfo = &app_info; + cinfo.enabledExtensionCount = ext->ext_count; + cinfo.ppEnabledExtensionNames = ext->extensions; + cinfo.pNext = NULL; + + if (vklayers != NULL) { + log_output(LOG_INFO, "Validation layers enabled"); + cinfo.enabledLayerCount = vklayers->vklayer_count; + cinfo.ppEnabledLayerNames = vklayers->vklayer_names; + } else { + log_output(LOG_INFO, "Validation layers disabled"); + cinfo.enabledLayerCount = 0; + cinfo.ppEnabledLayerNames = NULL; + } + + VkResult res; + res = vkCreateInstance(&cinfo, NULL, &ret->instance); + if (res != VK_SUCCESS) { + char *err_str = get_vkerr_str(res); + log_output(LOG_FATAL, "Cannot create a Vulkan instance: %s", err_str); + rune_abort(); + } + + if (vklayers != NULL) + _init_vkdebugger(ret); + + return ret; +} + +void destroy_vkcontext(vkcontext_t *context) { + log_output(LOG_DEBUG, "Closing Vulkan instance"); + if (rune_get_vk_debug() == 1) { + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(context->instance, + "vkDestroyDebugUtilsMessengerEXT"); + func(context->instance, context->db_messenger, NULL); + } + vkDestroySurfaceKHR(context->instance, context->surface->handle, NULL); + vkDestroyInstance(context->instance, NULL); + rune_free(context->surface); + rune_free(context); +} + +vklayer_container_t* init_vklayers(ext_container_t *ext) { + ext->ext_count++; + const char** new_extensions = rune_alloc(sizeof(char*) * ext->ext_count); + if (new_extensions == NULL) { + log_output(LOG_FATAL, "Cannot allocate memory for debug extensions"); + rune_abort(); + } + + for (uint32_t i = 0; i < ext->ext_count-1; i++) + new_extensions[i] = ext->extensions[i]; + new_extensions[ext->ext_count-1] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + ext->extensions = new_extensions; + + uint32_t layer_count; + vkEnumerateInstanceLayerProperties(&layer_count, NULL); + VkLayerProperties layer_props[layer_count]; + vkEnumerateInstanceLayerProperties(&layer_count, layer_props); + + vklayer_container_t *ret = rune_alloc(sizeof(vklayer_container_t)); + ret->vklayer_count = 1; + ret->vklayer_names = rune_alloc(sizeof(char*) * ret->vklayer_count); + ret->vklayer_names[0] = "VK_LAYER_KHRONOS_validation"; + + for (uint32_t i = 0; i < ret->vklayer_count; i++) { + log_output(LOG_DEBUG, "Searching for layer: %s", ret->vklayer_names[i]); + int found = 0; + for (uint32_t j = 0; j < layer_count; j++) { + if (strcmp(ret->vklayer_names[i], layer_props[j].layerName) == 0) { + found = 1; + break; + } + } + if (found == 0) { + log_output(LOG_WARN, "Required validation layer is missing: %s", ret->vklayer_names[i]); + rune_free(ret); + rune_free(ext->extensions); + return NULL; + } + } + log_output(LOG_INFO, "All prerequisite validation layers found"); + rune_free(ext->extensions); + return ret; +} diff --git a/engine/render/vulkan/context.h b/engine/render/vulkan/context.h new file mode 100644 index 0000000..15817e8 --- /dev/null +++ b/engine/render/vulkan/context.h @@ -0,0 +1,32 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKCONTEXT_H +#define VKCONTEXT_H + +#include "vk_types.h" + +vkcontext_t* create_vkcontext(vklayer_container_t *vklayers, ext_container_t *ext); +void destroy_vkcontext(vkcontext_t *context); + +vklayer_container_t* init_vklayers(ext_container_t *ext); + +#endif diff --git a/engine/render/vulkan/device.c b/engine/render/vulkan/device.c new file mode 100644 index 0000000..11d9433 --- /dev/null +++ b/engine/render/vulkan/device.c @@ -0,0 +1,324 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "device.h" +#include "vkassert.h" +#include +#include + +static int gfx_qfam = -1; +static int tsfr_qfam = -1; +static int comp_qfam = -1; +static int pres_qfam = -1; + +struct vkdev_data { + VkPhysicalDeviceProperties pdev_props; + VkPhysicalDeviceFeatures pdev_feats; + VkPhysicalDeviceMemoryProperties pdev_mprops; + uint32_t pdev_ext_count; + const char** pdev_extensions; +}; + +void _query_pdev_data(VkPhysicalDevice pdev, struct vkdev_data *pdata) { + vkGetPhysicalDeviceProperties(pdev, &pdata->pdev_props); + vkGetPhysicalDeviceFeatures(pdev, &pdata->pdev_feats); + vkGetPhysicalDeviceMemoryProperties(pdev, &pdata->pdev_mprops); +} + +uint32_t _query_qfam_data(VkSurfaceKHR surface, VkPhysicalDevice pdev, VkQueueFamilyProperties** qfam_props) { + uint32_t count; + vkGetPhysicalDeviceQueueFamilyProperties(pdev, &count, NULL); + *qfam_props = rune_alloc(sizeof(VkQueueFamilyProperties) * count); + vkGetPhysicalDeviceQueueFamilyProperties(pdev, &count, *qfam_props); + return count; +} + +int _query_gfx_index(int num_props, VkQueueFamilyProperties *qfam_props) { + for (int i = 0; i < num_props; i++) { + if (qfam_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + gfx_qfam = i; + } + return gfx_qfam; +} + +int _query_tsfr_index(int num_props, VkQueueFamilyProperties *qfam_props) { + for (int i = 0; i < num_props; i++) { + if (qfam_props[i].queueFlags & VK_QUEUE_TRANSFER_BIT) + tsfr_qfam = i; + } + return tsfr_qfam; +} + +int _query_comp_index(int num_props, VkQueueFamilyProperties *qfam_props) { + for (int i = 0; i < num_props; i++) { + if (qfam_props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) + comp_qfam = i; + } + return comp_qfam; +} + +int _query_pres_index(int num_props, VkQueueFamilyProperties *qfam_props, VkPhysicalDevice pdev, VkSurfaceKHR surface) { + VkBool32 present_bit; + for (int i = 0; i < num_props; i++) { + vkGetPhysicalDeviceSurfaceSupportKHR(pdev, i, surface, &present_bit); + if (present_bit != VK_FALSE) + pres_qfam = i; + } + return pres_qfam; +} + +int _check_pdev(VkSurfaceKHR surface, VkPhysicalDevice pdev) { + int score = 0; + + struct vkdev_data pdata; + _query_pdev_data(pdev, &pdata); + if (pdata.pdev_props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return score; + + VkQueueFamilyProperties *qfam_props; + uint32_t num_qfams = _query_qfam_data(surface, pdev, &qfam_props); + if (_query_gfx_index(num_qfams, qfam_props) != -1) + score += 20; + if (_query_comp_index(num_qfams, qfam_props) != -1) + score += 20; + if (_query_tsfr_index(num_qfams, qfam_props) != -1) + score += 20; + if (_query_pres_index(num_qfams, qfam_props, pdev, surface) != -1) + score += 20; + + rune_free(qfam_props); + return score; +} + +VkPhysicalDevice _select_pdev(VkInstance instance, VkSurfaceKHR surface) { + uint32_t count; + vkEnumeratePhysicalDevices(instance, &count, NULL); + if (count == 0) + return NULL; + + VkPhysicalDevice pdevs[count]; + vkEnumeratePhysicalDevices(instance, &count, pdevs); + + for (uint32_t i = 0; i < count; i++) { + if (_check_pdev(surface, pdevs[i]) >= 80) + return pdevs[i]; + } + return NULL; +} + +void _create_queue(vkdev_t *dev, int qfam_type, int qfam_index, int queue_index) { + VkQueue *queue_arr; + int num_queues; + switch (qfam_type) { + case QFAM_TYPE_GRAPHICS: + dev->num_gfx_queues++; + num_queues = dev->num_gfx_queues; + dev->gfx_queues = rune_realloc(dev->gfx_queues, sizeof(VkQueue)*num_queues); + queue_arr = dev->gfx_queues; + break; + case QFAM_TYPE_TRANSFER: + dev->num_tsfr_queues++; + num_queues = dev->num_tsfr_queues; + dev->gfx_queues = rune_realloc(dev->tsfr_queues, sizeof(VkQueue)*num_queues); + queue_arr = dev->tsfr_queues; + break; + case QFAM_TYPE_COMPUTE: + dev->num_comp_queues++; + num_queues = dev->num_comp_queues; + dev->gfx_queues = rune_realloc(dev->comp_queues, sizeof(VkQueue)*num_queues); + queue_arr = dev->comp_queues; + break; + case QFAM_TYPE_PRESENT: + if (dev->pres_queue != NULL) + rune_free(dev->pres_queue); + dev->pres_queue = rune_alloc(sizeof(VkQueue)); + queue_arr = dev->pres_queue; + break; + default: + log_output(LOG_FATAL, "Requested unknown queue type"); + rune_abort(); + } + + vkGetDeviceQueue(dev->ldev, qfam_index, queue_index, &queue_arr[num_queues-1]); + if (queue_arr[num_queues-1] == NULL) { + log_output(LOG_FATAL, "Error creating required Vulkan queue"); + rune_abort(); + } +} + +vkdev_t* create_vkdev(VkInstance instance, VkSurfaceKHR surface, int num_gfx, int num_tsfr, int num_comp, int presentable) { + VkPhysicalDevice pdev = _select_pdev(instance, surface); + if (pdev == NULL) { + log_output(LOG_FATAL, "No device meets minimum requirements for rendering"); + rune_abort(); + } + + vkdev_t *dev = rune_calloc(0, sizeof(vkdev_t)); + dev->pdev = pdev; + dev->gfx_qfam = gfx_qfam; + dev->tsfr_qfam = tsfr_qfam; + dev->comp_qfam = comp_qfam; + dev->pres_qfam = pres_qfam; + + if (num_gfx > 0 && gfx_qfam == -1) { + log_output(LOG_FATAL, "Requested graphics queues but none found on device"); + rune_abort(); + } + if (num_tsfr > 0 && tsfr_qfam == -1) { + log_output(LOG_FATAL, "Requested transfer queues but none found on device"); + rune_abort(); + } + if (num_comp > 0 && comp_qfam == -1) { + log_output(LOG_FATAL, "Requested compute queues but none found on device"); + rune_abort(); + } + if (presentable == 1 && pres_qfam == -1) { + log_output(LOG_FATAL, "Requested presentation queue but none found on device"); + rune_abort(); + } + + int num_total = num_gfx + num_tsfr + num_comp + presentable; + int index = 0; + static const float queue_priority = 1.0f; + VkDeviceQueueCreateInfo *qcinfos = rune_alloc(sizeof(VkDeviceQueueCreateInfo)*num_total); + for (int i = 0; i < index+num_gfx; i++) { + qcinfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + qcinfos[i].pNext = NULL; + qcinfos[i].flags = 0; + qcinfos[i].queueFamilyIndex = gfx_qfam; + qcinfos[i].queueCount = 1; + qcinfos[i].pQueuePriorities = &queue_priority; + } + //index += num_gfx; + //for (int i = index; i < index+num_tsfr; i++) { + // qcinfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + // qcinfos[i].pNext = NULL; + // qcinfos[i].flags = 0; + // qcinfos[i].queueFamilyIndex = tsfr_qfam; + // qcinfos[i].queueCount = 1; + // qcinfos[i].pQueuePriorities = &queue_priority; + //} + //index += num_tsfr; + //for (int i = index; i < index+num_comp; i++) { + // qcinfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + // qcinfos[i].pNext = NULL; + // qcinfos[i].flags = 0; + // qcinfos[i].queueFamilyIndex = comp_qfam; + // qcinfos[i].queueCount = 1; + // qcinfos[i].pQueuePriorities = &queue_priority; + //} + + struct vkdev_data pdata; + _query_pdev_data(pdev, &pdata); + + VkDeviceCreateInfo dcinfo; + dcinfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + dcinfo.pNext = NULL; + dcinfo.flags = 0; + dcinfo.queueCreateInfoCount = num_total; + dcinfo.pQueueCreateInfos = qcinfos; + dcinfo.enabledLayerCount = 0; + dcinfo.ppEnabledLayerNames = NULL; + dcinfo.enabledExtensionCount = 1; + const char *ext_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + dcinfo.ppEnabledExtensionNames = &ext_names; + dcinfo.pEnabledFeatures = &pdata.pdev_feats; + vkassert(vkCreateDevice(dev->pdev, &dcinfo, NULL, &dev->ldev)); + rune_free(qcinfos); + + for (int i = 0; i < num_gfx; i++) + _create_queue(dev, QFAM_TYPE_GRAPHICS, gfx_qfam, i); + for (int i = 0; i < num_tsfr; i++) + _create_queue(dev, QFAM_TYPE_TRANSFER, tsfr_qfam, i); + for (int i = 0; i < num_comp; i++) + _create_queue(dev, QFAM_TYPE_COMPUTE, comp_qfam, i); + + VkCommandPoolCreateInfo pcinfo; + pcinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pcinfo.pNext = NULL; + pcinfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pcinfo.queueFamilyIndex = gfx_qfam; + vkassert(vkCreateCommandPool(dev->ldev, &pcinfo, NULL, &dev->gfx_cmd_pool)); + pcinfo.queueFamilyIndex = tsfr_qfam; + vkassert(vkCreateCommandPool(dev->ldev, &pcinfo, NULL, &dev->tsfr_cmd_pool)); + pcinfo.queueFamilyIndex = comp_qfam; + vkassert(vkCreateCommandPool(dev->ldev, &pcinfo, NULL, &dev->comp_cmd_pool)); + + log_output(LOG_DEBUG, "Initialized new logical device"); + return dev; +} + +void destroy_vkdev(vkdev_t *dev) { + vkDestroyCommandPool(dev->ldev, dev->gfx_cmd_pool, NULL); + vkDestroyCommandPool(dev->ldev, dev->tsfr_cmd_pool, NULL); + vkDestroyCommandPool(dev->ldev, dev->comp_cmd_pool, NULL); + vkDestroyDevice(dev->ldev, NULL); + rune_free(dev); +} + +void get_swapchain_data(vkdev_t *dev, VkSurfaceKHR *surface) { + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev->pdev, *surface, &dev->scdata.capabilities); + + vkGetPhysicalDeviceSurfaceFormatsKHR(dev->pdev, *surface, &dev->scdata.format_count, NULL); + dev->scdata.formats = rune_alloc(sizeof(VkSurfaceFormatKHR) * dev->scdata.format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(dev->pdev, *surface, &dev->scdata.format_count, dev->scdata.formats); + + vkGetPhysicalDeviceSurfacePresentModesKHR(dev->pdev, *surface, &dev->scdata.present_count, NULL); + dev->scdata.present_modes = rune_alloc(sizeof(VkPresentModeKHR) * dev->scdata.present_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(dev->pdev, *surface, &dev->scdata.present_count, dev->scdata.present_modes); +} + +int get_depth_format(vkdev_t *dev) { + const uint64_t count = 3; + VkFormat formats[3] = { + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT}; + + uint32_t flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + VkFormatProperties props; + for (uint64_t i = 0; i < count; i++) { + vkGetPhysicalDeviceFormatProperties(dev->pdev, formats[i], &props); + if ((props.linearTilingFeatures & flags) == flags) { + dev->depth_format = formats[i]; + return 1; + } else if ((props.optimalTilingFeatures & flags) == flags) { + dev->depth_format = formats[i]; + return 1; + } + } + return 0; +} + +uint32_t get_memory_index(vkdev_t *dev, uint32_t type, uint32_t flags) { + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(dev->pdev, &mem_props); + + for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { + uint32_t tmp = type & (1 << i); + uint32_t prop_flags = mem_props.memoryTypes[i].propertyFlags; + if (tmp & prop_flags & flags) + return i; + } + + log_output(LOG_WARN, "Unable to find suitable memory type"); + return -1; +} diff --git a/engine/render/vulkan/device.h b/engine/render/vulkan/device.h new file mode 100644 index 0000000..3dab471 --- /dev/null +++ b/engine/render/vulkan/device.h @@ -0,0 +1,34 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKDEVICE_H +#define VKDEVICE_H + +#include "vk_types.h" + +vkdev_t* create_vkdev(VkInstance instance, VkSurfaceKHR surface, int num_gfx, int num_tsfr, int num_comp, int presentable); +void destroy_vkdev(vkdev_t *dev); + +void get_swapchain_data(vkdev_t *dev, VkSurfaceKHR *surface); +int get_depth_format(vkdev_t *dev); +uint32_t get_memory_index(vkdev_t *dev, uint32_t type, uint32_t flags); + +#endif diff --git a/engine/render/vulkan/fence.c b/engine/render/vulkan/fence.c new file mode 100644 index 0000000..cee022e --- /dev/null +++ b/engine/render/vulkan/fence.c @@ -0,0 +1,84 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "fence.h" +#include "vkassert.h" +#include +#include + +vkfence_t* create_vkfence(vkdev_t *dev, uint8_t signal) { + vkfence_t *ret = rune_alloc(sizeof(vkfence_t)); + + VkFenceCreateInfo fcinfo; + fcinfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fcinfo.pNext = NULL; + if (signal == 1) + fcinfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + else + fcinfo.flags = 0; + vkassert(vkCreateFence(dev->ldev, &fcinfo, NULL, &ret->handle)); + return ret; +} + +void destroy_vkfence(vkfence_t *fence, vkdev_t *dev) { + if (fence->handle != NULL) { + vkDestroyFence(dev->ldev, fence->handle, NULL); + fence->handle = NULL; + } + rune_free(fence); +} + +int fence_lock(vkfence_t *fence, vkdev_t *dev, uint64_t timeout) { + if (fence->signal == 1) + return 0; + + VkResult res = vkWaitForFences(dev->ldev, 1, &fence->handle, VK_TRUE, timeout); + switch (res) { + case VK_SUCCESS: + fence->signal = 1; + return 0; + case VK_TIMEOUT: + log_output(LOG_WARN, "Vulkan fence timed out"); + break; + case VK_ERROR_DEVICE_LOST: + log_output(LOG_ERROR, "Lost access to host device"); + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + log_output(LOG_ERROR, "Out of host memory"); + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + log_output(LOG_ERROR, "Out of device memory"); + break; + default: + log_output(LOG_ERROR, "Unknown error occurred on Vulkan fence"); + break; + } + + return -1; +} + +void fence_unlock(vkfence_t *fence, vkdev_t *dev) { + if (fence->signal == 0) + return; + + vkassert(vkResetFences(dev->ldev, 1, &fence->handle)); + fence->signal = 0; +} diff --git a/engine/render/vulkan/fence.h b/engine/render/vulkan/fence.h new file mode 100644 index 0000000..49a04fe --- /dev/null +++ b/engine/render/vulkan/fence.h @@ -0,0 +1,33 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKFENCE_H +#define VKFENCE_H + +#include "vk_types.h" + +vkfence_t* create_vkfence(vkdev_t *dev, uint8_t signal); +void destroy_vkfence(vkfence_t *fence, vkdev_t *dev); + +int fence_lock(vkfence_t *fence, vkdev_t *dev, uint64_t timeout); +void fence_unlock(vkfence_t *fence, vkdev_t *dev); + +#endif diff --git a/engine/render/vulkan/framebuffer.c b/engine/render/vulkan/framebuffer.c new file mode 100644 index 0000000..697bbb9 --- /dev/null +++ b/engine/render/vulkan/framebuffer.c @@ -0,0 +1,55 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "framebuffer.h" +#include "vkassert.h" +#include +#include + +vkframebuffer_t* create_vkframebuffer(vkdev_t *dev, vkrendpass_t *rendpass, uint32_t width, uint32_t height, uint32_t at_count, VkImageView *at) { + vkframebuffer_t *ret = rune_alloc(sizeof(vkframebuffer_t)); + ret->at_count = at_count; + ret->attachments = rune_alloc(sizeof(VkImageView) * at_count); + for (uint32_t i = 0; i < at_count; i++) + ret->attachments[i] = at[i]; + ret->rendpass = rendpass; + + VkFramebufferCreateInfo fbinfo; + fbinfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbinfo.pNext = NULL; + fbinfo.flags = 0; + fbinfo.renderPass = rendpass->handle; + fbinfo.attachmentCount = ret->at_count; + fbinfo.pAttachments = ret->attachments; + fbinfo.width = width; + fbinfo.height = height; + fbinfo.layers = 1; + vkassert(vkCreateFramebuffer(dev->ldev, &fbinfo, NULL, &ret->handle)); + + return ret; +} + +void destroy_vkframebuffer(vkframebuffer_t *framebuffer, vkdev_t *dev) { + vkDestroyFramebuffer(dev->ldev, framebuffer->handle, NULL); + if (framebuffer->attachments) + rune_free(framebuffer->attachments); + rune_free(framebuffer); +} diff --git a/engine/render/vulkan/framebuffer.h b/engine/render/vulkan/framebuffer.h new file mode 100644 index 0000000..dbf914d --- /dev/null +++ b/engine/render/vulkan/framebuffer.h @@ -0,0 +1,30 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKFRAMEBUFFER_H +#define VKFRAMEBUFFER_H + +#include "vk_types.h" + +vkframebuffer_t* create_vkframebuffer(vkdev_t *dev, vkrendpass_t *rendpass, uint32_t width, uint32_t height, uint32_t at_count, VkImageView *at); +void destroy_vkframebuffer(vkframebuffer_t *framebuffer, vkdev_t *dev); + +#endif diff --git a/engine/render/vulkan/image.c b/engine/render/vulkan/image.c new file mode 100644 index 0000000..0334e94 --- /dev/null +++ b/engine/render/vulkan/image.c @@ -0,0 +1,99 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "image.h" +#include "device.h" +#include "vkassert.h" +#include + +int _create_image_view(vkimage_t *image, vkdev_t *dev, VkFormat format, VkImageAspectFlags aflags) { + VkImageViewCreateInfo vcinfo; + vcinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + vcinfo.pNext = NULL; + vcinfo.flags = 0; + vcinfo.image = image->handle; + vcinfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + vcinfo.format = format; + vcinfo.components.r = VK_COMPONENT_SWIZZLE_R; + vcinfo.components.g = VK_COMPONENT_SWIZZLE_G; + vcinfo.components.b = VK_COMPONENT_SWIZZLE_B; + vcinfo.components.a = VK_COMPONENT_SWIZZLE_A; + vcinfo.subresourceRange.aspectMask = aflags; + vcinfo.subresourceRange.baseMipLevel = 0; + vcinfo.subresourceRange.levelCount = 1; + vcinfo.subresourceRange.baseArrayLayer = 0; + vcinfo.subresourceRange.layerCount = 1; + vkassert(vkCreateImageView(dev->ldev, &vcinfo, NULL, &image->view)); +} + +vkimage_t* create_vkimage(vkdev_t *dev, VkFormat format, uint32_t width, uint32_t height, uint32_t usage, uint32_t mem_flags, uint32_t aflags, int create_view) { + vkimage_t *ret = rune_alloc(sizeof(vkimage_t)); + ret->width = width; + ret->height = height; + + VkImageCreateInfo icinfo; + icinfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + icinfo.pNext = NULL; + icinfo.flags = 0; + icinfo.imageType = VK_IMAGE_TYPE_2D; + icinfo.extent.width = width; + icinfo.extent.height = height; + icinfo.extent.depth = 1; + icinfo.mipLevels = 1; + icinfo.arrayLayers = 1; + icinfo.format = format; + icinfo.tiling = VK_IMAGE_TILING_OPTIMAL; + icinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + icinfo.usage = usage; + icinfo.samples = VK_SAMPLE_COUNT_1_BIT; + icinfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vkassert(vkCreateImage(dev->ldev, &icinfo, NULL, &ret->handle)); + + VkMemoryRequirements mem_req; + vkGetImageMemoryRequirements(dev->ldev, ret->handle, &mem_req); + + int32_t mem_type = get_memory_index(dev, mem_req.memoryTypeBits, mem_flags); + if (mem_type == -1) + log_output(LOG_ERROR, "Image does not have a valid memory type"); + + VkMemoryAllocateInfo mainfo; + mainfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mainfo.pNext = NULL; + mainfo.allocationSize = mem_req.size; + mainfo.memoryTypeIndex = mem_type; + vkassert(vkAllocateMemory(dev->ldev, &mainfo, NULL, &ret->memory)); + vkassert(vkBindImageMemory(dev->ldev, ret->handle, ret->memory, 0)); + + if (create_view == 1) + _create_image_view(ret, dev, format, aflags); + + return ret; +} + +void destroy_vkimage(vkimage_t *image, vkdev_t *dev) { + if (image->view) + vkDestroyImageView(dev->ldev, image->view, NULL); + if (image->memory) + vkFreeMemory(dev->ldev, image->memory, NULL); + if (image->handle) + vkDestroyImage(dev->ldev, image->handle, NULL); + rune_free(image); +} diff --git a/engine/render/vulkan/image.h b/engine/render/vulkan/image.h new file mode 100644 index 0000000..067025d --- /dev/null +++ b/engine/render/vulkan/image.h @@ -0,0 +1,30 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKIMAGE_H +#define VKIMAGE_H + +#include "vk_types.h" + +vkimage_t* create_vkimage(vkdev_t *dev, VkFormat format, uint32_t width, uint32_t height, uint32_t usage, uint32_t mem_flags, uint32_t aflags, int create_view); +void destroy_vkimage(vkimage_t *image, vkdev_t *dev); + +#endif diff --git a/engine/render/vulkan/renderer.c b/engine/render/vulkan/renderer.c new file mode 100644 index 0000000..f02d84f --- /dev/null +++ b/engine/render/vulkan/renderer.c @@ -0,0 +1,279 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "vk_types.h" +#include "renderpass.h" +#include "framebuffer.h" +#include "swapchain.h" +#include "device.h" +#include "context.h" +#include "image.h" +#include "fence.h" +#include "vkassert.h" +#include +#include +#include +#include +#include +#include + +static vkcontext_t *context = NULL; + +void _init_cmdbuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->cmdbuffers == NULL) + context->cmdbuffers = rune_calloc(0, sizeof(vkcmdbuffer_t*) * num_buffers); + + for (uint32_t i = 0; i < num_buffers; i++) { + if (context->cmdbuffers[i] != NULL) + destroy_vkcmdbuffer(context->cmdbuffers[i], context->dev); + context->cmdbuffers[i] = create_vkcmdbuffer(context->dev, 1); + } + log_output(LOG_DEBUG, "Created %d command buffers", num_buffers); +} + +void _destroy_cmdbuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->cmdbuffers == NULL) + return; + for (uint32_t i = 0; i < num_buffers; i++) { + if (context->cmdbuffers[i] != NULL) + destroy_vkcmdbuffer(context->cmdbuffers[i], context->dev); + } + log_output(LOG_DEBUG, "Destroyed %d command buffers", num_buffers); +} + +void _init_framebuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->framebuffers == NULL) + context->framebuffers = rune_calloc(0, sizeof(vkframebuffer_t*) * num_buffers); + + uint32_t at_count = 2; + for (uint32_t i = 0; i < num_buffers; i++) { + VkImageView attachments[at_count]; + attachments[0] = context->swapchain->views[i]; + attachments[1] = context->swapchain->depth_attachment->view; + + if (context->framebuffers[i] != NULL) + destroy_vkframebuffer(context->framebuffers[i], context->dev); + context->framebuffers[i] = create_vkframebuffer(context->dev, + context->rendpass, + context->surface->width, + context->surface->height, + at_count, + attachments); + } + log_output(LOG_DEBUG, "Created %d frame buffers", num_buffers); +} + +void _destroy_framebuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->framebuffers == NULL) + return; + + for (uint32_t i = 0; i < num_buffers; i++) + destroy_vkframebuffer(context->framebuffers[i], context->dev); + log_output(LOG_DEBUG, "Destroyed %d frame buffers", num_buffers); +} + +int _init_vulkan(window_t *window) { + log_output(LOG_DEBUG, "Initializing Vulkan"); + struct timeval start; + struct timeval stop; + gettimeofday(&start, NULL); + + ext_container_t ext; + ext.extensions = glfwGetRequiredInstanceExtensions(&ext.ext_count); + + if (rune_get_vk_debug() == 1) { + vklayer_container_t *vklayers = init_vklayers(&ext); + context = create_vkcontext(vklayers, &ext); + } else { + context = create_vkcontext(NULL, &ext); + } + + if (context == NULL) + return -1; + + VkResult res = glfwCreateWindowSurface(context->instance, + window->window, + NULL, + &context->surface->handle); + if (res != VK_SUCCESS) { + log_output(LOG_FATAL, "Cannot create rendering surface"); + return -1; + } + + context->dev = create_vkdev(context->instance, + context->surface->handle, + 1, + 1, + 1, + 1); + if (context->dev == NULL) + return -1; + + context->surface->width = window->winw; + context->surface->height = window->winh; + context->swapchain = create_swapchain(context->surface, context->dev); + if (context->swapchain == NULL) + return -1; + + vec4 area = {0, 0, context->surface->width, context->surface->height}; + vec4 color = {0, 0, 0, 1.0f}; + context->rendpass = create_vkrendpass(context->dev, context->swapchain, area, color, 1.0, 0); + if (context->rendpass == NULL) + return -1; + + _init_framebuffers(); + _init_cmdbuffers(); + + context->image_semaphores = rune_alloc(sizeof(VkSemaphore) * context->swapchain->max_frames); + context->queue_semaphores = rune_alloc(sizeof(VkSemaphore) * context->swapchain->max_frames); + context->fences_in_flight = rune_calloc(0, sizeof(vkfence_t*) * context->swapchain->max_frames); + + VkSemaphoreCreateInfo scinfo; + for (uint8_t i = 0; i < context->swapchain->max_frames; i++) { + scinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + scinfo.pNext = NULL; + scinfo.flags = 0; + vkCreateSemaphore(context->dev->ldev, &scinfo, NULL, &context->image_semaphores[i]); + vkCreateSemaphore(context->dev->ldev, &scinfo, NULL, &context->queue_semaphores[i]); + context->fences_in_flight[i] = create_vkfence(context->dev, 1); + } + context->images_in_flight = rune_calloc(0, sizeof(vkfence_t*) * context->swapchain->img_count); + + gettimeofday(&stop, NULL); + log_output(LOG_INFO, "Finished initializing Vulkan in %lums", (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec); + return 0; +} + +void _close_vulkan(void) { + vkDeviceWaitIdle(context->dev->ldev); + for (uint8_t i = 0; i < context->swapchain->max_frames; i++) { + if (context->image_semaphores[i] != NULL) + vkDestroySemaphore(context->dev->ldev, context->image_semaphores[i], NULL); + if (context->queue_semaphores[i] != NULL) + vkDestroySemaphore(context->dev->ldev, context->queue_semaphores[i], NULL); + destroy_vkfence(context->fences_in_flight[i], context->dev); + } + + _destroy_cmdbuffers(); + _destroy_framebuffers(); + destroy_vkrendpass(context->rendpass, context->dev); + destroy_swapchain(context->swapchain, context->dev); + destroy_vkdev(context->dev); + destroy_vkcontext(context); +} + +int _begin_frame(float time) { + vkfence_t *frame_fence = context->fences_in_flight[context->swapchain->frame]; + if (fence_lock(frame_fence, context->dev, UINT64_MAX) == -1) { + log_output(LOG_WARN, "Error locking in-flight fence"); + return -1; + } + + uint32_t next_img = vkswapchain_get_next_img(context->swapchain, + context->dev, + UINT64_MAX, + NULL, + context->image_semaphores[context->swapchain->frame]); + + if (next_img == -1) + return -1; + + context->img_index = next_img; + vkcmdbuffer_t *cmdbuf = context->cmdbuffers[context->img_index]; + cmdbuf_begin(cmdbuf, 0, 0, 0); + + VkViewport vport; + vport.x = 0; + vport.y = (float)context->surface->height; + vport.width = (float)context->surface->width; + vport.height = (float)context->surface->height; + vport.minDepth = 0; + vport.maxDepth = 1; + + VkRect2D scissor; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = context->surface->width; + scissor.extent.height = context->surface->height; + + vkCmdSetViewport(cmdbuf->handle, 0, 1, &vport); + vkCmdSetScissor(cmdbuf->handle, 0, 1, &scissor); + + context->rendpass->area[2] = context->surface->width; + context->rendpass->area[3] = context->surface->height; + + VkFramebuffer framebuf = context->framebuffers[context->img_index]->handle; + renderpass_begin(cmdbuf, context->rendpass, framebuf); + return 0; +} + +int _end_frame(float time) { + vkcmdbuffer_t *cmdbuf = context->cmdbuffers[context->img_index]; + renderpass_end(cmdbuf, context->rendpass); + cmdbuf_end(cmdbuf); + + vkfence_t** img_in_flight = &context->images_in_flight[context->img_index]; + if (*img_in_flight != NULL) + fence_lock(*img_in_flight, context->dev, UINT64_MAX); + + context->images_in_flight[context->img_index] = context->fences_in_flight[context->swapchain->frame]; + *img_in_flight = context->fences_in_flight[context->swapchain->frame]; + fence_unlock(*img_in_flight, context->dev); + + for (int i = 0; i < context->dev->num_gfx_queues; i++) { + cmdbuf_submit(cmdbuf, + &context->queue_semaphores[context->swapchain->frame], + &context->image_semaphores[context->swapchain->frame], + context->dev->gfx_queues[i], + (*img_in_flight)->handle); + } + + vkswapchain_present(context->swapchain, + context->dev, + &context->queue_semaphores[context->swapchain->frame], + &context->img_index); + + cmdbuf_reset(cmdbuf); + + return 0; +} + +void _draw_vulkan(void) { + _begin_frame(0); + _end_frame(0); +} + +void _clear_vulkan(void) { +} + +renderer_t* select_render_vulkan(window_t *window) { + renderer_t *ret = rune_alloc(sizeof(renderer_t)); + ret->close = _close_vulkan; + ret->draw = _draw_vulkan; + ret->clear = _clear_vulkan; + if (_init_vulkan(window) != 0) + rune_abort(); + return ret; +} diff --git a/engine/render/vulkan/renderpass.c b/engine/render/vulkan/renderpass.c new file mode 100644 index 0000000..e1e8743 --- /dev/null +++ b/engine/render/vulkan/renderpass.c @@ -0,0 +1,249 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "renderpass.h" +#include "vkassert.h" +#include + +vkcmdbuffer_t* create_vkcmdbuffer(vkdev_t *dev, int primary) { + vkcmdbuffer_t *ret = rune_calloc(0, sizeof(vkcmdbuffer_t)); + + VkCommandBufferAllocateInfo ainfo; + ainfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + ainfo.pNext = NULL; + ainfo.commandPool = dev->gfx_cmd_pool; + if (primary == 1) + ainfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + else + ainfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + ainfo.commandBufferCount = 1; + vkassert(vkAllocateCommandBuffers(dev->ldev, &ainfo, &ret->handle)); + + ret->state = CMDBUF_INITIAL; + return ret; +} + +void destroy_vkcmdbuffer(vkcmdbuffer_t *cmdbuffer, vkdev_t *dev) { + vkFreeCommandBuffers(dev->ldev, dev->gfx_cmd_pool, 1, &cmdbuffer->handle); + rune_free(cmdbuffer); +} + +void cmdbuf_begin(vkcmdbuffer_t *cmdbuffer, int single, int rpass_cont, int sim_use) { + if (cmdbuffer->state != CMDBUF_INITIAL) { + log_output(LOG_FATAL, "Attempted to record to a command buffer not in initial state"); + rune_abort(); + } + + VkCommandBufferBeginInfo binfo; + binfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + binfo.flags = 0; + binfo.pNext = NULL; + if (single) + binfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + if (rpass_cont) + binfo.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + if (sim_use) + binfo.flags |= VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + vkassert(vkBeginCommandBuffer(cmdbuffer->handle, &binfo)); + cmdbuffer->state = CMDBUF_RECORDING; +} + +void cmdbuf_end(vkcmdbuffer_t *cmdbuffer) { + if (cmdbuffer->state != CMDBUF_RECORDING) { + log_output(LOG_FATAL, "Attempted to end command buffer not in recording state"); + rune_abort(); + } + + vkassert(vkEndCommandBuffer(cmdbuffer->handle)); + cmdbuffer->state = CMDBUF_READY; +} + +void cmdbuf_submit(vkcmdbuffer_t *cmdbuffer, VkSemaphore *signal, VkSemaphore *wait, VkQueue queue_handle, VkFence fence_handle) { + if (cmdbuffer->state != CMDBUF_READY) { + log_output(LOG_FATAL, "Attempted to submit command buffer not in ready state"); + return; + } + + VkSubmitInfo sinfo; + sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + sinfo.pNext = NULL; + sinfo.commandBufferCount = 1; + sinfo.pCommandBuffers = &cmdbuffer->handle; + sinfo.signalSemaphoreCount = 1; + sinfo.pSignalSemaphores = signal; + sinfo.waitSemaphoreCount = 1; + sinfo.pWaitSemaphores = wait; + VkPipelineStageFlags flags[1] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + sinfo.pWaitDstStageMask = flags; + vkassert(vkQueueSubmit(queue_handle, 1, &sinfo, fence_handle)); + cmdbuffer->state = CMDBUF_SUBMITTED; +} + +void cmdbuf_reset(vkcmdbuffer_t *cmdbuffer) { + cmdbuffer->state = CMDBUF_INITIAL; +} + +vkcmdbuffer_t* cmdbuf_begin_single_use(vkdev_t *dev) { + vkcmdbuffer_t *ret = create_vkcmdbuffer(dev, 1); + cmdbuf_begin(ret, 1, 0, 0); + return ret; +} + +void cmdbuf_end_single_use(vkcmdbuffer_t *cmdbuffer, vkdev_t *dev, VkQueue queue) { + cmdbuf_end(cmdbuffer); + VkSubmitInfo sinfo; + sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + sinfo.pNext = NULL; + sinfo.waitSemaphoreCount = 0; + sinfo.pWaitSemaphores = NULL; + sinfo.pWaitDstStageMask = NULL; + sinfo.commandBufferCount = 1; + sinfo.pCommandBuffers = &cmdbuffer->handle; + sinfo.signalSemaphoreCount = 0; + sinfo.pSignalSemaphores = NULL; + vkassert(vkQueueSubmit(queue, 1, &sinfo, 0)); + + vkassert(vkQueueWaitIdle(queue)); + destroy_vkcmdbuffer(cmdbuffer, dev); +} + +vkrendpass_t* create_vkrendpass(vkdev_t *dev, vkswapchain_t *swapchain, vec4 area, vec4 color, float depth, uint32_t stencil) { + VkAttachmentDescription atdesc[2]; + atdesc[0].flags = 0; + atdesc[0].format = swapchain->format_khr.format; + atdesc[0].samples = VK_SAMPLE_COUNT_1_BIT; + atdesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + atdesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + atdesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + atdesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + atdesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + atdesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + atdesc[1].flags = 0; + atdesc[1].format = dev->depth_format; + atdesc[1].samples = VK_SAMPLE_COUNT_1_BIT; + atdesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + atdesc[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + atdesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + atdesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + atdesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + atdesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference cref; + cref.attachment = 0; + cref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference dref; + dref.attachment = 1; + dref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &cref; + subpass.pDepthStencilAttachment = &dref; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + + VkSubpassDependency dep; + dep.srcSubpass = VK_SUBPASS_EXTERNAL; + dep.dstSubpass = 0; + dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.srcAccessMask = 0; + dep.dstAccessMask = 0; + dep.dependencyFlags = 0; + + vkrendpass_t *ret = rune_alloc(sizeof(vkrendpass_t)); + ret->color[0] = color[0]; + ret->color[1] = color[1]; + ret->color[2] = color[2]; + ret->color[3] = color[3]; + ret->area[0] = area[0]; + ret->area[1] = area[1]; + ret->area[2] = area[2]; + ret->area[3] = area[3]; + ret->depth = depth; + ret->stencil = stencil; + VkRenderPassCreateInfo rcinfo; + rcinfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rcinfo.pNext = NULL; + rcinfo.flags = 0; + rcinfo.attachmentCount = 2; + rcinfo.pAttachments = atdesc; + rcinfo.subpassCount = 1; + rcinfo.pSubpasses = &subpass; + rcinfo.dependencyCount = 1; + rcinfo.pDependencies = &dep; + vkassert(vkCreateRenderPass(dev->ldev, &rcinfo, NULL, &ret->handle)); + + log_output(LOG_DEBUG, "Initialized renderpass"); + return ret; +} + +void destroy_vkrendpass(vkrendpass_t *rendpass, vkdev_t *dev) { + if (rendpass->handle) + vkDestroyRenderPass(dev->ldev, rendpass->handle, NULL); + rune_free(rendpass); +} + +void renderpass_begin(vkcmdbuffer_t *buf, vkrendpass_t *rendpass, VkFramebuffer framebuf) { + if (buf->state != CMDBUF_RECORDING) { + log_output(LOG_FATAL, "Attempted to place command buffer not in recording state in a render pass"); + rune_abort(); + } + + VkClearValue cvals[2]; + cvals[0].color.float32[0] = rendpass->color[0]; + cvals[0].color.float32[1] = rendpass->color[1]; + cvals[0].color.float32[2] = rendpass->color[2]; + cvals[0].color.float32[3] = rendpass->color[3]; + cvals[1].depthStencil.depth = rendpass->depth; + cvals[1].depthStencil.stencil = rendpass->stencil; + + VkRenderPassBeginInfo binfo; + binfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + binfo.pNext = NULL; + binfo.renderPass = rendpass->handle; + binfo.framebuffer = framebuf; + binfo.renderArea.offset.x = rendpass->area[0]; + binfo.renderArea.offset.y = rendpass->area[1]; + binfo.renderArea.extent.width = rendpass->area[2]; + binfo.renderArea.extent.height = rendpass->area[3]; + binfo.clearValueCount = 2; + binfo.pClearValues = cvals; + + vkCmdBeginRenderPass(buf->handle, &binfo, VK_SUBPASS_CONTENTS_INLINE); + buf->state = CMDBUF_IN_RENDERPASS; +} + +void renderpass_end(vkcmdbuffer_t *buf, vkrendpass_t *rendpass) { + if (buf->state != CMDBUF_IN_RENDERPASS) { + log_output(LOG_FATAL, "Attempted to purge command buffer not in render pass"); + rune_abort(); + } + + vkCmdEndRenderPass(buf->handle); + buf->state = CMDBUF_RECORDING; +} diff --git a/engine/render/vulkan/renderpass.h b/engine/render/vulkan/renderpass.h new file mode 100644 index 0000000..1524438 --- /dev/null +++ b/engine/render/vulkan/renderpass.h @@ -0,0 +1,49 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKRENDERPASS_H +#define VKRENDERPASS_H + +#include "vk_types.h" + +enum cmdbuf_state { + CMDBUF_INITIAL, + CMDBUF_RECORDING, + CMDBUF_IN_RENDERPASS, + CMDBUF_READY, + CMDBUF_SUBMITTED, +}; + +vkcmdbuffer_t* create_vkcmdbuffer(vkdev_t *dev, int primary); +void destroy_vkcmdbuffer(vkcmdbuffer_t *cmdbuffer, vkdev_t *dev); + +void cmdbuf_begin(vkcmdbuffer_t *cmdbuffer, int single, int rpass_cont, int sim_use); +void cmdbuf_end(vkcmdbuffer_t *cmdbuffer); +void cmdbuf_submit(vkcmdbuffer_t *cmdbuffer, VkSemaphore *signal, VkSemaphore *wait, VkQueue queue_handle, VkFence fence_handle); +void cmdbuf_reset(vkcmdbuffer_t *cmdbuffer); + +vkrendpass_t* create_vkrendpass(vkdev_t *dev, vkswapchain_t *swapchain, vec4 area, vec4 color, float depth, uint32_t stencil); +void destroy_vkrendpass(vkrendpass_t *rendpass, vkdev_t *dev); + +void renderpass_begin(vkcmdbuffer_t *buf, vkrendpass_t *rendpass, VkFramebuffer framebuf); +void renderpass_end(vkcmdbuffer_t *buf, vkrendpass_t *rendpass); + +#endif diff --git a/engine/render/vulkan/swapchain.c b/engine/render/vulkan/swapchain.c new file mode 100644 index 0000000..36899de --- /dev/null +++ b/engine/render/vulkan/swapchain.c @@ -0,0 +1,152 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "swapchain.h" +#include "image.h" +#include "device.h" +#include "vkassert.h" +#include +#include +#include + +vkswapchain_t* create_swapchain(vksurface_t *surface, vkdev_t *dev) { + vkswapchain_t *swapchain = rune_alloc(sizeof(vkswapchain_t)); + VkExtent2D sc_extent = {surface->width, surface->height}; + swapchain->max_frames = 2; + get_swapchain_data(dev, &surface->handle); + swapchain->format_khr = dev->scdata.formats[0]; + if (dev->scdata.capabilities.currentExtent.width != UINT32_MAX) + sc_extent = dev->scdata.capabilities.currentExtent; + + VkExtent2D min = dev->scdata.capabilities.minImageExtent; + VkExtent2D max = dev->scdata.capabilities.maxImageExtent; + sc_extent.width = clamp(sc_extent.width, min.width, max.width); + sc_extent.height = clamp(sc_extent.height, min.height, max.height); + + uint32_t img_count = dev->scdata.capabilities.minImageCount + 1; + if (dev->scdata.capabilities.maxImageCount > 0 && img_count > dev->scdata.capabilities.maxImageCount) + img_count = dev->scdata.capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR cinfo; + cinfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + cinfo.pNext = NULL; + cinfo.flags = 0; + cinfo.surface = surface->handle; + cinfo.minImageCount = img_count; + cinfo.imageFormat = swapchain->format_khr.format; + cinfo.imageColorSpace = swapchain->format_khr.colorSpace; + cinfo.imageExtent = sc_extent; + cinfo.imageArrayLayers = 1; + cinfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + cinfo.preTransform = dev->scdata.capabilities.currentTransform; + cinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + cinfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + cinfo.clipped = VK_TRUE; + cinfo.oldSwapchain = NULL; + if (dev->gfx_qfam != dev->pres_qfam) { + uint32_t qfams[] = {dev->gfx_qfam, dev->pres_qfam}; + cinfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + cinfo.queueFamilyIndexCount = 2; + cinfo.pQueueFamilyIndices = qfams; + } else { + cinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + cinfo.queueFamilyIndexCount = 0; + cinfo.pQueueFamilyIndices = 0; + } + vkassert(vkCreateSwapchainKHR(dev->ldev, &cinfo, NULL, &swapchain->handle)); + vkassert(vkGetSwapchainImagesKHR(dev->ldev, swapchain->handle, &swapchain->img_count, NULL)); + + swapchain->images = rune_alloc(sizeof(VkImage) * swapchain->img_count); + swapchain->views = rune_alloc(sizeof(VkImageView) * swapchain->img_count); + vkassert(vkGetSwapchainImagesKHR(dev->ldev, swapchain->handle, &swapchain->img_count, swapchain->images)); + + VkImageViewCreateInfo vcinfo; + for (uint32_t i = 0; i < swapchain->img_count; i++) { + vcinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + vcinfo.pNext = NULL; + vcinfo.image = swapchain->images[i]; + vcinfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + vcinfo.format = swapchain->format_khr.format; + vcinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vcinfo.subresourceRange.baseMipLevel = 0; + vcinfo.subresourceRange.levelCount = 1; + vcinfo.subresourceRange.baseArrayLayer = 0; + vcinfo.subresourceRange.layerCount = 1; + vkassert(vkCreateImageView(dev->ldev, &vcinfo, NULL, &swapchain->views[i])); + } + + if (get_depth_format(dev) == 0) { + log_output(LOG_FATAL, "Failed to find a supported image format"); + rune_abort(); + } + + swapchain->depth_attachment = create_vkimage(dev, + dev->depth_format, + surface->width, + surface->height, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_IMAGE_ASPECT_DEPTH_BIT, + 1); + swapchain->frame = 0; + log_output(LOG_DEBUG, "Initialized swapchain"); + return swapchain; +} + +void destroy_swapchain(vkswapchain_t *swapchain, vkdev_t *dev) { + for (uint32_t i = 0; i < swapchain->img_count; i++) + vkDestroyImageView(dev->ldev, swapchain->views[i], NULL); + destroy_vkimage(swapchain->depth_attachment, dev); + vkDestroySwapchainKHR(dev->ldev, swapchain->handle, NULL); + rune_free(swapchain->images); + rune_free(swapchain->views); +} + +int32_t vkswapchain_get_next_img(vkswapchain_t *swapchain, vkdev_t *dev, uint64_t tmout, VkFence fence, VkSemaphore img_available) { + uint32_t ret = 0; + VkResult res = vkAcquireNextImageKHR(dev->ldev, swapchain->handle, tmout, img_available, fence, &ret); + if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) { + log_output(LOG_ERROR, "Error on getting next image index"); + return -1; + } + + return (int32_t)ret; +} + +void vkswapchain_present(vkswapchain_t *swapchain, vkdev_t *dev, VkSemaphore *render_complete, uint32_t *img_index) { + VkPresentInfoKHR pinfo; + pinfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + pinfo.pNext = NULL; + pinfo.waitSemaphoreCount = 1; + pinfo.pWaitSemaphores = render_complete; + pinfo.swapchainCount = 1; + pinfo.pSwapchains = &swapchain->handle; + pinfo.pImageIndices = img_index; + pinfo.pResults = NULL; + + VkResult res = vkQueuePresentKHR(dev->pres_queue, &pinfo); + if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) + STUBBED("Recreate swapchain"); + else if (res != VK_SUCCESS) + log_output(LOG_ERROR, "Vulkan error: %s", get_vkerr_str(res)); + + swapchain->frame = (swapchain->frame + 1) % swapchain->max_frames; +} diff --git a/engine/render/vulkan/swapchain.h b/engine/render/vulkan/swapchain.h new file mode 100644 index 0000000..7c2e2a5 --- /dev/null +++ b/engine/render/vulkan/swapchain.h @@ -0,0 +1,33 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKSWAPCHAIN_H +#define VKSWAPCHAIN_H + +#include "vk_types.h" + +vkswapchain_t* create_swapchain(vksurface_t *surface, vkdev_t *dev); +void destroy_swapchain(vkswapchain_t *swapchain, vkdev_t *dev); + +int32_t vkswapchain_get_next_img(vkswapchain_t *swapchain, vkdev_t *dev, uint64_t tmout, VkFence fence, VkSemaphore img_available); +void vkswapchain_present(vkswapchain_t *swapchain, vkdev_t *dev, VkSemaphore *render_complete, uint32_t *img_index); + +#endif diff --git a/engine/render/vulkan/vk_types.h b/engine/render/vulkan/vk_types.h new file mode 100644 index 0000000..2011771 --- /dev/null +++ b/engine/render/vulkan/vk_types.h @@ -0,0 +1,143 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VK_TYPES_H +#define VK_TYPES_H + +#include +#include +#include +#include + +#define QFAM_TYPE_GRAPHICS 1 +#define QFAM_TYPE_TRANSFER 2 +#define QFAM_TYPE_COMPUTE 3 +#define QFAM_TYPE_PRESENT 4 + +typedef struct vksurface { + VkSurfaceKHR handle; + uint32_t width; + uint32_t height; +} vksurface_t; + +typedef struct vkcmdbuffer { + VkCommandBuffer handle; + int state; +} vkcmdbuffer_t; + +typedef struct vkfence { + VkFence handle; + int signal; +} vkfence_t; + +typedef struct vkimage { + VkImage handle; + VkDeviceMemory memory; + VkImageView view; + uint32_t width; + uint32_t height; +} vkimage_t; + +typedef struct ext_container { + const char** extensions; + uint32_t ext_count; +} ext_container_t; + +typedef struct vklayer_container { + const char** vklayer_names; + uint32_t vklayer_count; +} vklayer_container_t; + +typedef struct vkswapchain_data { + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR *formats; + VkPresentModeKHR *present_modes; + uint32_t format_count; + uint32_t present_count; +} vkswapchain_data_t; + +typedef struct vkrendpass { + VkRenderPass handle; + vec4 area; + vec4 color; + float depth; + uint32_t stencil; +} vkrendpass_t; + +typedef struct vkframebuffer { + VkFramebuffer handle; + uint32_t at_count; + VkImageView *attachments; + vkrendpass_t *rendpass; +} vkframebuffer_t; + +typedef struct vkdev { + VkPhysicalDevice pdev; + VkDevice ldev; + vkswapchain_data_t scdata; + VkQueue *gfx_queues; + int num_gfx_queues; + int gfx_qfam; + VkQueue *tsfr_queues; + int num_tsfr_queues; + int tsfr_qfam; + VkQueue *comp_queues; + int num_comp_queues; + int comp_qfam; + VkQueue *pres_queue; + int pres_qfam; + VkCommandPool gfx_cmd_pool; + VkCommandPool tsfr_cmd_pool; + VkCommandPool comp_cmd_pool; + VkCommandPool pres_cmd_pool; + VkFormat depth_format; +} vkdev_t; + +typedef struct vkswapchain { + VkSwapchainKHR handle; + VkSurfaceFormatKHR format_khr; + VkFormat format; + VkImage *images; + VkImageView *views; + vkimage_t *depth_attachment; + uint8_t max_frames; + uint32_t frame; + uint32_t img_count; +} vkswapchain_t; + +typedef struct vkcontext { + VkInstance instance; + VkDebugUtilsMessengerEXT db_messenger; + VkSemaphore *queue_semaphores; + VkSemaphore *image_semaphores; + vksurface_t *surface; + vkswapchain_t *swapchain; + vkrendpass_t *rendpass; + vkdev_t *dev; + vkcmdbuffer_t** cmdbuffers; + vkframebuffer_t** framebuffers; + vkfence_t** fences_in_flight; + vkfence_t** images_in_flight; + uint32_t num_fences_in_flight; + uint32_t img_index; +} vkcontext_t; + +#endif diff --git a/engine/render/vulkan/vkassert.h b/engine/render/vulkan/vkassert.h new file mode 100644 index 0000000..ca0a89d --- /dev/null +++ b/engine/render/vulkan/vkassert.h @@ -0,0 +1,68 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef VKASSERT_H +#define VKASSERT_H + +#include "vk_types.h" +#include +#include +#include + +static char* get_vkerr_str(VkResult res) { + char *ret; + switch (res) { + case VK_SUCCESS: + ret = "SUCCESS"; + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ret = "OUT OF HOST MEMORY"; + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + ret = "OUT OF DEVICE MEMORY"; + break; + case VK_ERROR_INITIALIZATION_FAILED: + ret = "INITIALIZATION FAILED"; + break; + case VK_ERROR_LAYER_NOT_PRESENT: + ret = "VALIDATION LAYER NOT PRESENT"; + break; + case VK_ERROR_EXTENSION_NOT_PRESENT: + ret = "EXTENSION NOT PRESENT"; + break; + case VK_ERROR_INCOMPATIBLE_DRIVER: + ret = "INCOMPATIBLE DRIVER"; + break; + default: + ret = "UNKNOWN RESULT"; + break; + } + return strdup(ret); +} + +static inline void vkassert(VkResult value) { + if (value != VK_SUCCESS) { + log_output(LOG_FATAL, "Vulkan error: %s", get_vkerr_str(value)); + rune_abort(); + } +} + +#endif diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt new file mode 100644 index 0000000..a80b982 --- /dev/null +++ b/profiler/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SUBMODULE_EXECUTABLE rune-profiler) + +list(APPEND SUBMODULE_FILES + src/profiler.c +) + +set(SUBMODULE_HEADER_DIR ${CMAKE_SOURCE_DIR}/profiler/include) + +include(${CMAKE_SOURCE_DIR}/CMake/SubmoduleDefines.cmake) diff --git a/render/directx/renderer.c b/render/directx/renderer.c deleted file mode 100644 index 8820441..0000000 --- a/render/directx/renderer.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include - -#ifdef _WIN32 - -struct rune_renderer* select_render_directx(window_t *window) { - log_output(LOG_FATAL, "DirectX support has not yet been implemented"); - rune_abort(); -} - -#else - -struct rune_renderer* select_render_directx(window_t *window) { - log_output(LOG_FATAL, "DirectX is not supported on this platform"); - rune_abort(); -} - -#endif diff --git a/render/vulkan/context.c b/render/vulkan/context.c deleted file mode 100644 index 1282706..0000000 --- a/render/vulkan/context.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "context.h" -#include "vkassert.h" -#include -#include -#include -#include -#include - -VKAPI_ATTR VkBool32 VKAPI_CALL _vulkan_db_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, - VkDebugUtilsMessageTypeFlagsEXT message_types, - const VkDebugUtilsMessengerCallbackDataEXT *callback_data, - void *user_data) { - switch (message_severity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - log_output(LOG_ERROR, callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - log_output(LOG_WARN, callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - log_output(LOG_INFO, callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - log_output(LOG_DEBUG, callback_data->pMessage); - break; - default: - break; - } - return VK_FALSE; -} - -int _init_vkdebugger(vkcontext_t *context) { - log_output(LOG_INFO, "Initializing Vulkan debugger"); - - VkDebugUtilsMessengerCreateInfoEXT dbinfo; - dbinfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - dbinfo.pNext = NULL; - dbinfo.flags = 0; - dbinfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - dbinfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; - dbinfo.pfnUserCallback = _vulkan_db_callback; - dbinfo.pUserData = NULL; - - PFN_vkCreateDebugUtilsMessengerEXT func = - (PFN_vkCreateDebugUtilsMessengerEXT) - vkGetInstanceProcAddr(context->instance, - "vkCreateDebugUtilsMessengerEXT"); - - if (func == NULL) { - log_output(LOG_ERROR, "Failed to create Vulkan debugger"); - return -1; - } - - VkResult res; - res = func(context->instance, &dbinfo, NULL, &context->db_messenger); - if (res != VK_SUCCESS) { - char *err_str = get_vkerr_str(res); - log_output(LOG_ERROR, "Debug session error: %s", err_str); - free(err_str); - return -1; - } - return 0; -} - -uint32_t _vertoi(const char *version) { - uint32_t major = 0; - uint32_t minor = 0; - uint32_t patch = 0; - char *version_copy = strdup(version); - char *token = strtok(version_copy, "."); - if (token != NULL) - major = (uint32_t)atoi(token); - - token = strtok(NULL, "."); - if (token != NULL) - minor = (uint32_t)atoi(token); - - token = strtok(NULL, "."); - if (token != NULL) - patch = (uint32_t)atoi(token); - - free(version_copy); - return (major << 22) | (minor << 12) | patch; -} - -vkcontext_t* create_vkcontext(vklayer_container_t *vklayers, ext_container_t *ext) { - vkcontext_t *ret = rune_calloc(0, sizeof(vkcontext_t)); - ret->surface = rune_alloc(sizeof(vksurface_t)); - - VkApplicationInfo app_info; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.pNext = NULL; - app_info.apiVersion = VK_API_VERSION_1_2; - app_info.pApplicationName = rune_get_app_name(); - const char *app_ver = rune_get_app_ver(); - app_info.applicationVersion = _vertoi(app_ver); - app_info.pEngineName = "RuneEngine"; - app_info.engineVersion = _vertoi(RUNE_VER); - - VkInstanceCreateInfo cinfo; - cinfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - cinfo.pNext = NULL; - cinfo.flags = 0; - cinfo.pApplicationInfo = &app_info; - cinfo.enabledExtensionCount = ext->ext_count; - cinfo.ppEnabledExtensionNames = ext->extensions; - cinfo.pNext = NULL; - - if (vklayers != NULL) { - log_output(LOG_INFO, "Validation layers enabled"); - cinfo.enabledLayerCount = vklayers->vklayer_count; - cinfo.ppEnabledLayerNames = vklayers->vklayer_names; - } else { - log_output(LOG_INFO, "Validation layers disabled"); - cinfo.enabledLayerCount = 0; - cinfo.ppEnabledLayerNames = NULL; - } - - VkResult res; - res = vkCreateInstance(&cinfo, NULL, &ret->instance); - if (res != VK_SUCCESS) { - char *err_str = get_vkerr_str(res); - log_output(LOG_FATAL, "Cannot create a Vulkan instance: %s", err_str); - rune_abort(); - } - - if (vklayers != NULL) - _init_vkdebugger(ret); - - return ret; -} - -void destroy_vkcontext(vkcontext_t *context) { - log_output(LOG_DEBUG, "Closing Vulkan instance"); - if (rune_get_vk_debug() == 1) { - PFN_vkDestroyDebugUtilsMessengerEXT func = - (PFN_vkDestroyDebugUtilsMessengerEXT) - vkGetInstanceProcAddr(context->instance, - "vkDestroyDebugUtilsMessengerEXT"); - func(context->instance, context->db_messenger, NULL); - } - vkDestroySurfaceKHR(context->instance, context->surface->handle, NULL); - vkDestroyInstance(context->instance, NULL); - rune_free(context->surface); - rune_free(context); -} - -vklayer_container_t* init_vklayers(ext_container_t *ext) { - ext->ext_count++; - const char** new_extensions = rune_alloc(sizeof(char*) * ext->ext_count); - if (new_extensions == NULL) { - log_output(LOG_FATAL, "Cannot allocate memory for debug extensions"); - rune_abort(); - } - - for (uint32_t i = 0; i < ext->ext_count-1; i++) - new_extensions[i] = ext->extensions[i]; - new_extensions[ext->ext_count-1] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - ext->extensions = new_extensions; - - uint32_t layer_count; - vkEnumerateInstanceLayerProperties(&layer_count, NULL); - VkLayerProperties layer_props[layer_count]; - vkEnumerateInstanceLayerProperties(&layer_count, layer_props); - - vklayer_container_t *ret = rune_alloc(sizeof(vklayer_container_t)); - ret->vklayer_count = 1; - ret->vklayer_names = rune_alloc(sizeof(char*) * ret->vklayer_count); - ret->vklayer_names[0] = "VK_LAYER_KHRONOS_validation"; - - for (uint32_t i = 0; i < ret->vklayer_count; i++) { - log_output(LOG_DEBUG, "Searching for layer: %s", ret->vklayer_names[i]); - int found = 0; - for (uint32_t j = 0; j < layer_count; j++) { - if (strcmp(ret->vklayer_names[i], layer_props[j].layerName) == 0) { - found = 1; - break; - } - } - if (found == 0) { - log_output(LOG_WARN, "Required validation layer is missing: %s", ret->vklayer_names[i]); - rune_free(ret); - rune_free(ext->extensions); - return NULL; - } - } - log_output(LOG_INFO, "All prerequisite validation layers found"); - rune_free(ext->extensions); - return ret; -} diff --git a/render/vulkan/context.h b/render/vulkan/context.h deleted file mode 100644 index 15817e8..0000000 --- a/render/vulkan/context.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKCONTEXT_H -#define VKCONTEXT_H - -#include "vk_types.h" - -vkcontext_t* create_vkcontext(vklayer_container_t *vklayers, ext_container_t *ext); -void destroy_vkcontext(vkcontext_t *context); - -vklayer_container_t* init_vklayers(ext_container_t *ext); - -#endif diff --git a/render/vulkan/device.c b/render/vulkan/device.c deleted file mode 100644 index 583fb54..0000000 --- a/render/vulkan/device.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "device.h" -#include "vkassert.h" -#include -#include - -static int gfx_qfam = -1; -static int tsfr_qfam = -1; -static int comp_qfam = -1; -static int pres_qfam = -1; - -struct vkdev_data { - VkPhysicalDeviceProperties pdev_props; - VkPhysicalDeviceFeatures pdev_feats; - VkPhysicalDeviceMemoryProperties pdev_mprops; - uint32_t pdev_ext_count; - const char** pdev_extensions; -}; - -void _query_pdev_data(VkPhysicalDevice pdev, struct vkdev_data *pdata) { - vkGetPhysicalDeviceProperties(pdev, &pdata->pdev_props); - vkGetPhysicalDeviceFeatures(pdev, &pdata->pdev_feats); - vkGetPhysicalDeviceMemoryProperties(pdev, &pdata->pdev_mprops); -} - -uint32_t _query_qfam_data(VkSurfaceKHR surface, VkPhysicalDevice pdev, VkQueueFamilyProperties** qfam_props) { - uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(pdev, &count, NULL); - *qfam_props = rune_alloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceQueueFamilyProperties(pdev, &count, *qfam_props); - return count; -} - -int _query_gfx_index(int num_props, VkQueueFamilyProperties *qfam_props) { - for (int i = 0; i < num_props; i++) { - if (qfam_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - gfx_qfam = i; - } - return gfx_qfam; -} - -int _query_tsfr_index(int num_props, VkQueueFamilyProperties *qfam_props) { - for (int i = 0; i < num_props; i++) { - if (qfam_props[i].queueFlags & VK_QUEUE_TRANSFER_BIT) - tsfr_qfam = i; - } - return tsfr_qfam; -} - -int _query_comp_index(int num_props, VkQueueFamilyProperties *qfam_props) { - for (int i = 0; i < num_props; i++) { - if (qfam_props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) - comp_qfam = i; - } - return comp_qfam; -} - -int _query_pres_index(int num_props, VkQueueFamilyProperties *qfam_props, VkPhysicalDevice pdev, VkSurfaceKHR surface) { - VkBool32 present_bit; - for (int i = 0; i < num_props; i++) { - vkGetPhysicalDeviceSurfaceSupportKHR(pdev, 0, surface, &present_bit); - if (present_bit != VK_FALSE) - pres_qfam = i; - } - return pres_qfam; -} - -int _check_pdev(VkSurfaceKHR surface, VkPhysicalDevice pdev) { - int score = 0; - - struct vkdev_data pdata; - _query_pdev_data(pdev, &pdata); - if (pdata.pdev_props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) - return score; - - VkQueueFamilyProperties *qfam_props; - uint32_t num_qfams = _query_qfam_data(surface, pdev, &qfam_props); - if (_query_gfx_index(num_qfams, qfam_props) != -1) - score += 20; - if (_query_comp_index(num_qfams, qfam_props) != -1) - score += 20; - if (_query_tsfr_index(num_qfams, qfam_props) != -1) - score += 20; - if (_query_pres_index(num_qfams, qfam_props, pdev, surface) != -1) - score += 20; - - rune_free(qfam_props); - return score; -} - -VkPhysicalDevice _select_pdev(VkInstance instance, VkSurfaceKHR surface) { - uint32_t count; - vkEnumeratePhysicalDevices(instance, &count, NULL); - if (count == 0) - return NULL; - - VkPhysicalDevice pdevs[count]; - vkEnumeratePhysicalDevices(instance, &count, pdevs); - - for (uint32_t i = 0; i < count; i++) { - if (_check_pdev(surface, pdevs[i]) >= 80) - return pdevs[i]; - } - return NULL; -} - -void _create_queue(vkdev_t *dev, int qfam_index, int queue_index) { - vkGetDeviceQueue(dev->ldev, qfam_index, queue_index, &dev->queues[qfam_index].handle); - if (dev->queues[qfam_index].handle == NULL) { - log_output(LOG_FATAL, "Error creating required Vulkan queue"); - rune_abort(); - } -} - -vkdev_t* create_vkdev(VkInstance instance, VkSurfaceKHR surface) { - VkPhysicalDevice pdev = _select_pdev(instance, surface); - if (pdev == NULL) { - log_output(LOG_FATAL, "No device meets minimum requirements for rendering"); - rune_abort(); - } - - vkdev_t *dev = rune_calloc(0, sizeof(vkdev_t)); - dev->pdev = pdev; - - dev->queues[0].qfam = gfx_qfam; - dev->queues[1].qfam = tsfr_qfam; - dev->queues[2].qfam = comp_qfam; - dev->queues[3].qfam = 0; - - float queue_priority = 1.0f; - VkDeviceQueueCreateInfo qcinfos[3]; - for (int i = 0; i < 3; i++) { - qcinfos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - qcinfos[i].pNext = NULL; - qcinfos[i].flags = 0; - qcinfos[i].queueFamilyIndex = dev->queues[i].qfam; - qcinfos[i].queueCount = 1; - qcinfos[i].pQueuePriorities = &queue_priority; - } - - struct vkdev_data pdata; - _query_pdev_data(pdev, &pdata); - - VkDeviceCreateInfo dcinfo; - dcinfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - dcinfo.pNext = NULL; - dcinfo.flags = 0; - dcinfo.queueCreateInfoCount = 3; - dcinfo.pQueueCreateInfos = qcinfos; - dcinfo.enabledLayerCount = 0; - dcinfo.ppEnabledLayerNames = NULL; - dcinfo.enabledExtensionCount = 1; - const char *ext_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; - dcinfo.ppEnabledExtensionNames = &ext_names; - dcinfo.pEnabledFeatures = &pdata.pdev_feats; - vkassert(vkCreateDevice(dev->pdev, &dcinfo, NULL, &dev->ldev)); - - for (uint32_t i = 0; i < 3; i++) - _create_queue(dev, i, 0); - - // FIXME: This is a dirty hack and should be fixed - dev->queues[3].handle = dev->queues[0].handle; - - VkCommandPoolCreateInfo pcinfo; - pcinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - pcinfo.pNext = NULL; - pcinfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - pcinfo.queueFamilyIndex = dev->queues[0].qfam; - vkassert(vkCreateCommandPool(dev->ldev, &pcinfo, NULL, &dev->cmd_pool)); - - log_output(LOG_DEBUG, "Initialized new logical device"); - return dev; -} - -void destroy_vkdev(vkdev_t *dev) { - vkDestroyCommandPool(dev->ldev, dev->cmd_pool, NULL); - vkDestroyDevice(dev->ldev, NULL); - rune_free(dev); -} - -void get_swapchain_data(vkdev_t *dev, VkSurfaceKHR *surface) { - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev->pdev, *surface, &dev->scdata.capabilities); - - vkGetPhysicalDeviceSurfaceFormatsKHR(dev->pdev, *surface, &dev->scdata.format_count, NULL); - dev->scdata.formats = rune_alloc(sizeof(VkSurfaceFormatKHR) * dev->scdata.format_count); - vkGetPhysicalDeviceSurfaceFormatsKHR(dev->pdev, *surface, &dev->scdata.format_count, dev->scdata.formats); - - vkGetPhysicalDeviceSurfacePresentModesKHR(dev->pdev, *surface, &dev->scdata.present_count, NULL); - dev->scdata.present_modes = rune_alloc(sizeof(VkPresentModeKHR) * dev->scdata.present_count); - vkGetPhysicalDeviceSurfacePresentModesKHR(dev->pdev, *surface, &dev->scdata.present_count, dev->scdata.present_modes); -} - -int get_depth_format(vkdev_t *dev) { - const uint64_t count = 3; - VkFormat formats[3] = { - VK_FORMAT_D32_SFLOAT_S8_UINT, - VK_FORMAT_D24_UNORM_S8_UINT, - VK_FORMAT_D32_SFLOAT}; - - uint32_t flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - VkFormatProperties props; - for (uint64_t i = 0; i < count; i++) { - vkGetPhysicalDeviceFormatProperties(dev->pdev, formats[i], &props); - if ((props.linearTilingFeatures & flags) == flags) { - dev->depth_format = formats[i]; - return 1; - } else if ((props.optimalTilingFeatures & flags) == flags) { - dev->depth_format = formats[i]; - return 1; - } - } - return 0; -} - -uint32_t get_memory_index(vkdev_t *dev, uint32_t type, uint32_t flags) { - VkPhysicalDeviceMemoryProperties mem_props; - vkGetPhysicalDeviceMemoryProperties(dev->pdev, &mem_props); - - for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { - uint32_t tmp = type & (1 << i); - uint32_t prop_flags = mem_props.memoryTypes[i].propertyFlags; - if (tmp & prop_flags & flags) - return i; - } - - log_output(LOG_WARN, "Unable to find suitable memory type"); - return -1; -} diff --git a/render/vulkan/device.h b/render/vulkan/device.h deleted file mode 100644 index b375c5b..0000000 --- a/render/vulkan/device.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKDEVICE_H -#define VKDEVICE_H - -#include "vk_types.h" - -vkdev_t* create_vkdev(VkInstance instance, VkSurfaceKHR surface); -void destroy_vkdev(vkdev_t *dev); - -void get_swapchain_data(vkdev_t *dev, VkSurfaceKHR *surface); -int get_depth_format(vkdev_t *dev); -uint32_t get_memory_index(vkdev_t *dev, uint32_t type, uint32_t flags); - -#endif diff --git a/render/vulkan/fence.c b/render/vulkan/fence.c deleted file mode 100644 index cee022e..0000000 --- a/render/vulkan/fence.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "fence.h" -#include "vkassert.h" -#include -#include - -vkfence_t* create_vkfence(vkdev_t *dev, uint8_t signal) { - vkfence_t *ret = rune_alloc(sizeof(vkfence_t)); - - VkFenceCreateInfo fcinfo; - fcinfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fcinfo.pNext = NULL; - if (signal == 1) - fcinfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - else - fcinfo.flags = 0; - vkassert(vkCreateFence(dev->ldev, &fcinfo, NULL, &ret->handle)); - return ret; -} - -void destroy_vkfence(vkfence_t *fence, vkdev_t *dev) { - if (fence->handle != NULL) { - vkDestroyFence(dev->ldev, fence->handle, NULL); - fence->handle = NULL; - } - rune_free(fence); -} - -int fence_lock(vkfence_t *fence, vkdev_t *dev, uint64_t timeout) { - if (fence->signal == 1) - return 0; - - VkResult res = vkWaitForFences(dev->ldev, 1, &fence->handle, VK_TRUE, timeout); - switch (res) { - case VK_SUCCESS: - fence->signal = 1; - return 0; - case VK_TIMEOUT: - log_output(LOG_WARN, "Vulkan fence timed out"); - break; - case VK_ERROR_DEVICE_LOST: - log_output(LOG_ERROR, "Lost access to host device"); - break; - case VK_ERROR_OUT_OF_HOST_MEMORY: - log_output(LOG_ERROR, "Out of host memory"); - break; - case VK_ERROR_OUT_OF_DEVICE_MEMORY: - log_output(LOG_ERROR, "Out of device memory"); - break; - default: - log_output(LOG_ERROR, "Unknown error occurred on Vulkan fence"); - break; - } - - return -1; -} - -void fence_unlock(vkfence_t *fence, vkdev_t *dev) { - if (fence->signal == 0) - return; - - vkassert(vkResetFences(dev->ldev, 1, &fence->handle)); - fence->signal = 0; -} diff --git a/render/vulkan/fence.h b/render/vulkan/fence.h deleted file mode 100644 index 49a04fe..0000000 --- a/render/vulkan/fence.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKFENCE_H -#define VKFENCE_H - -#include "vk_types.h" - -vkfence_t* create_vkfence(vkdev_t *dev, uint8_t signal); -void destroy_vkfence(vkfence_t *fence, vkdev_t *dev); - -int fence_lock(vkfence_t *fence, vkdev_t *dev, uint64_t timeout); -void fence_unlock(vkfence_t *fence, vkdev_t *dev); - -#endif diff --git a/render/vulkan/framebuffer.c b/render/vulkan/framebuffer.c deleted file mode 100644 index 697bbb9..0000000 --- a/render/vulkan/framebuffer.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "framebuffer.h" -#include "vkassert.h" -#include -#include - -vkframebuffer_t* create_vkframebuffer(vkdev_t *dev, vkrendpass_t *rendpass, uint32_t width, uint32_t height, uint32_t at_count, VkImageView *at) { - vkframebuffer_t *ret = rune_alloc(sizeof(vkframebuffer_t)); - ret->at_count = at_count; - ret->attachments = rune_alloc(sizeof(VkImageView) * at_count); - for (uint32_t i = 0; i < at_count; i++) - ret->attachments[i] = at[i]; - ret->rendpass = rendpass; - - VkFramebufferCreateInfo fbinfo; - fbinfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - fbinfo.pNext = NULL; - fbinfo.flags = 0; - fbinfo.renderPass = rendpass->handle; - fbinfo.attachmentCount = ret->at_count; - fbinfo.pAttachments = ret->attachments; - fbinfo.width = width; - fbinfo.height = height; - fbinfo.layers = 1; - vkassert(vkCreateFramebuffer(dev->ldev, &fbinfo, NULL, &ret->handle)); - - return ret; -} - -void destroy_vkframebuffer(vkframebuffer_t *framebuffer, vkdev_t *dev) { - vkDestroyFramebuffer(dev->ldev, framebuffer->handle, NULL); - if (framebuffer->attachments) - rune_free(framebuffer->attachments); - rune_free(framebuffer); -} diff --git a/render/vulkan/framebuffer.h b/render/vulkan/framebuffer.h deleted file mode 100644 index dbf914d..0000000 --- a/render/vulkan/framebuffer.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKFRAMEBUFFER_H -#define VKFRAMEBUFFER_H - -#include "vk_types.h" - -vkframebuffer_t* create_vkframebuffer(vkdev_t *dev, vkrendpass_t *rendpass, uint32_t width, uint32_t height, uint32_t at_count, VkImageView *at); -void destroy_vkframebuffer(vkframebuffer_t *framebuffer, vkdev_t *dev); - -#endif diff --git a/render/vulkan/image.c b/render/vulkan/image.c deleted file mode 100644 index 0334e94..0000000 --- a/render/vulkan/image.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "image.h" -#include "device.h" -#include "vkassert.h" -#include - -int _create_image_view(vkimage_t *image, vkdev_t *dev, VkFormat format, VkImageAspectFlags aflags) { - VkImageViewCreateInfo vcinfo; - vcinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - vcinfo.pNext = NULL; - vcinfo.flags = 0; - vcinfo.image = image->handle; - vcinfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - vcinfo.format = format; - vcinfo.components.r = VK_COMPONENT_SWIZZLE_R; - vcinfo.components.g = VK_COMPONENT_SWIZZLE_G; - vcinfo.components.b = VK_COMPONENT_SWIZZLE_B; - vcinfo.components.a = VK_COMPONENT_SWIZZLE_A; - vcinfo.subresourceRange.aspectMask = aflags; - vcinfo.subresourceRange.baseMipLevel = 0; - vcinfo.subresourceRange.levelCount = 1; - vcinfo.subresourceRange.baseArrayLayer = 0; - vcinfo.subresourceRange.layerCount = 1; - vkassert(vkCreateImageView(dev->ldev, &vcinfo, NULL, &image->view)); -} - -vkimage_t* create_vkimage(vkdev_t *dev, VkFormat format, uint32_t width, uint32_t height, uint32_t usage, uint32_t mem_flags, uint32_t aflags, int create_view) { - vkimage_t *ret = rune_alloc(sizeof(vkimage_t)); - ret->width = width; - ret->height = height; - - VkImageCreateInfo icinfo; - icinfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - icinfo.pNext = NULL; - icinfo.flags = 0; - icinfo.imageType = VK_IMAGE_TYPE_2D; - icinfo.extent.width = width; - icinfo.extent.height = height; - icinfo.extent.depth = 1; - icinfo.mipLevels = 1; - icinfo.arrayLayers = 1; - icinfo.format = format; - icinfo.tiling = VK_IMAGE_TILING_OPTIMAL; - icinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - icinfo.usage = usage; - icinfo.samples = VK_SAMPLE_COUNT_1_BIT; - icinfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - vkassert(vkCreateImage(dev->ldev, &icinfo, NULL, &ret->handle)); - - VkMemoryRequirements mem_req; - vkGetImageMemoryRequirements(dev->ldev, ret->handle, &mem_req); - - int32_t mem_type = get_memory_index(dev, mem_req.memoryTypeBits, mem_flags); - if (mem_type == -1) - log_output(LOG_ERROR, "Image does not have a valid memory type"); - - VkMemoryAllocateInfo mainfo; - mainfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mainfo.pNext = NULL; - mainfo.allocationSize = mem_req.size; - mainfo.memoryTypeIndex = mem_type; - vkassert(vkAllocateMemory(dev->ldev, &mainfo, NULL, &ret->memory)); - vkassert(vkBindImageMemory(dev->ldev, ret->handle, ret->memory, 0)); - - if (create_view == 1) - _create_image_view(ret, dev, format, aflags); - - return ret; -} - -void destroy_vkimage(vkimage_t *image, vkdev_t *dev) { - if (image->view) - vkDestroyImageView(dev->ldev, image->view, NULL); - if (image->memory) - vkFreeMemory(dev->ldev, image->memory, NULL); - if (image->handle) - vkDestroyImage(dev->ldev, image->handle, NULL); - rune_free(image); -} diff --git a/render/vulkan/image.h b/render/vulkan/image.h deleted file mode 100644 index 067025d..0000000 --- a/render/vulkan/image.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKIMAGE_H -#define VKIMAGE_H - -#include "vk_types.h" - -vkimage_t* create_vkimage(vkdev_t *dev, VkFormat format, uint32_t width, uint32_t height, uint32_t usage, uint32_t mem_flags, uint32_t aflags, int create_view); -void destroy_vkimage(vkimage_t *image, vkdev_t *dev); - -#endif diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c deleted file mode 100644 index 679abef..0000000 --- a/render/vulkan/renderer.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "vk_types.h" -#include "renderpass.h" -#include "framebuffer.h" -#include "swapchain.h" -#include "device.h" -#include "context.h" -#include "image.h" -#include "fence.h" -#include "vkassert.h" -#include -#include -#include -#include -#include -#include - -static vkcontext_t *context = NULL; - -void _init_cmdbuffers(void) { - uint32_t num_buffers = context->swapchain->img_count; - if (context->cmdbuffers == NULL) - context->cmdbuffers = rune_calloc(0, sizeof(vkcmdbuffer_t*) * num_buffers); - - for (uint32_t i = 0; i < num_buffers; i++) { - if (context->cmdbuffers[i] != NULL) - destroy_vkcmdbuffer(context->cmdbuffers[i], context->dev); - context->cmdbuffers[i] = create_vkcmdbuffer(context->dev, 1); - } - log_output(LOG_DEBUG, "Created %d command buffers", num_buffers); -} - -void _destroy_cmdbuffers(void) { - uint32_t num_buffers = context->swapchain->img_count; - if (context->cmdbuffers == NULL) - return; - for (uint32_t i = 0; i < num_buffers; i++) { - if (context->cmdbuffers[i] != NULL) - destroy_vkcmdbuffer(context->cmdbuffers[i], context->dev); - } - log_output(LOG_DEBUG, "Destroyed %d command buffers", num_buffers); -} - -void _init_framebuffers(void) { - uint32_t num_buffers = context->swapchain->img_count; - if (context->framebuffers == NULL) - context->framebuffers = rune_calloc(0, sizeof(vkframebuffer_t*) * num_buffers); - - uint32_t at_count = 2; - for (uint32_t i = 0; i < num_buffers; i++) { - VkImageView attachments[at_count]; - attachments[0] = context->swapchain->views[i]; - attachments[1] = context->swapchain->depth_attachment->view; - - if (context->framebuffers[i] != NULL) - destroy_vkframebuffer(context->framebuffers[i], context->dev); - context->framebuffers[i] = create_vkframebuffer(context->dev, - context->rendpass, - context->surface->width, - context->surface->height, - at_count, - attachments); - } - log_output(LOG_DEBUG, "Created %d frame buffers", num_buffers); -} - -void _destroy_framebuffers(void) { - uint32_t num_buffers = context->swapchain->img_count; - if (context->framebuffers == NULL) - return; - - for (uint32_t i = 0; i < num_buffers; i++) - destroy_vkframebuffer(context->framebuffers[i], context->dev); - log_output(LOG_DEBUG, "Destroyed %d frame buffers", num_buffers); -} - -int _init_vulkan(window_t *window) { - log_output(LOG_DEBUG, "Initializing Vulkan"); - struct timeval start; - struct timeval stop; - gettimeofday(&start, NULL); - - ext_container_t ext; - ext.extensions = glfwGetRequiredInstanceExtensions(&ext.ext_count); - - if (rune_get_vk_debug() == 1) { - vklayer_container_t *vklayers = init_vklayers(&ext); - context = create_vkcontext(vklayers, &ext); - } else { - context = create_vkcontext(NULL, &ext); - } - - if (context == NULL) - return -1; - - VkResult res = glfwCreateWindowSurface(context->instance, - window->window, - NULL, - &context->surface->handle); - if (res != VK_SUCCESS) { - log_output(LOG_FATAL, "Cannot create rendering surface"); - return -1; - } - - context->dev = create_vkdev(context->instance, - context->surface->handle); - if (context->dev == NULL) - return -1; - - context->surface->width = window->winw; - context->surface->height = window->winh; - context->swapchain = create_swapchain(context->surface, context->dev); - if (context->swapchain == NULL) - return -1; - - vec4 area = {0, 0, context->surface->width, context->surface->height}; - vec4 color = {0, 0, 0, 1.0f}; - context->rendpass = create_vkrendpass(context->dev, context->swapchain, area, color, 1.0, 0); - if (context->rendpass == NULL) - return -1; - - _init_framebuffers(); - _init_cmdbuffers(); - - context->image_semaphores = rune_alloc(sizeof(VkSemaphore) * context->swapchain->max_frames); - context->queue_semaphores = rune_alloc(sizeof(VkSemaphore) * context->swapchain->max_frames); - context->fences_in_flight = rune_calloc(0, sizeof(vkfence_t*) * context->swapchain->max_frames); - - VkSemaphoreCreateInfo scinfo; - for (uint8_t i = 0; i < context->swapchain->max_frames; i++) { - scinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - scinfo.pNext = NULL; - scinfo.flags = 0; - vkCreateSemaphore(context->dev->ldev, &scinfo, NULL, &context->image_semaphores[i]); - vkCreateSemaphore(context->dev->ldev, &scinfo, NULL, &context->queue_semaphores[i]); - context->fences_in_flight[i] = create_vkfence(context->dev, 1); - } - context->images_in_flight = rune_calloc(0, sizeof(vkfence_t*) * context->swapchain->img_count); - - gettimeofday(&stop, NULL); - log_output(LOG_INFO, "Finished initializing Vulkan in %lums", (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec); - return 0; -} - -void _close_vulkan(void) { - vkDeviceWaitIdle(context->dev->ldev); - for (uint8_t i = 0; i < context->swapchain->max_frames; i++) { - if (context->image_semaphores[i] != NULL) - vkDestroySemaphore(context->dev->ldev, context->image_semaphores[i], NULL); - if (context->queue_semaphores[i] != NULL) - vkDestroySemaphore(context->dev->ldev, context->queue_semaphores[i], NULL); - destroy_vkfence(context->fences_in_flight[i], context->dev); - } - - _destroy_cmdbuffers(); - _destroy_framebuffers(); - destroy_vkrendpass(context->rendpass, context->dev); - destroy_swapchain(context->swapchain, context->dev); - destroy_vkdev(context->dev); - destroy_vkcontext(context); -} - -int _begin_frame(float time) { - vkfence_t *frame_fence = context->fences_in_flight[context->swapchain->frame]; - if (fence_lock(frame_fence, context->dev, UINT64_MAX) == -1) { - log_output(LOG_WARN, "Error locking in-flight fence"); - return -1; - } - - uint32_t next_img = vkswapchain_get_next_img(context->swapchain, - context->dev, - UINT64_MAX, - NULL, - context->image_semaphores[context->swapchain->frame]); - - if (next_img == -1) - return -1; - - context->img_index = next_img; - vkcmdbuffer_t *cmdbuf = context->cmdbuffers[context->img_index]; - cmdbuf_begin(cmdbuf, 0, 0, 0); - - VkViewport vport; - vport.x = 0; - vport.y = (float)context->surface->height; - vport.width = (float)context->surface->width; - vport.height = (float)context->surface->height; - vport.minDepth = 0; - vport.maxDepth = 1; - - VkRect2D scissor; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = context->surface->width; - scissor.extent.height = context->surface->height; - - vkCmdSetViewport(cmdbuf->handle, 0, 1, &vport); - vkCmdSetScissor(cmdbuf->handle, 0, 1, &scissor); - - context->rendpass->area[2] = context->surface->width; - context->rendpass->area[3] = context->surface->height; - - VkFramebuffer framebuf = context->framebuffers[context->img_index]->handle; - renderpass_begin(cmdbuf, context->rendpass, framebuf); - return 0; -} - -int _end_frame(float time) { - vkcmdbuffer_t *cmdbuf = context->cmdbuffers[context->img_index]; - renderpass_end(cmdbuf, context->rendpass); - cmdbuf_end(cmdbuf); - - vkfence_t** img_in_flight = &context->images_in_flight[context->img_index]; - if (*img_in_flight != NULL) - fence_lock(*img_in_flight, context->dev, UINT64_MAX); - - context->images_in_flight[context->img_index] = context->fences_in_flight[context->swapchain->frame]; - *img_in_flight = context->fences_in_flight[context->swapchain->frame]; - fence_unlock(*img_in_flight, context->dev); - - cmdbuf_submit(cmdbuf, - &context->queue_semaphores[context->swapchain->frame], - &context->image_semaphores[context->swapchain->frame], - context->dev->queues[0].handle, - (*img_in_flight)->handle); - - vkswapchain_present(context->swapchain, - context->dev, - &context->queue_semaphores[context->swapchain->frame], - &context->img_index); - - cmdbuf_reset(cmdbuf); - - return 0; -} - -void _draw_vulkan(void) { - _begin_frame(0); - _end_frame(0); -} - -void _clear_vulkan(void) { -} - -renderer_t* select_render_vulkan(window_t *window) { - renderer_t *ret = rune_alloc(sizeof(renderer_t)); - ret->close = _close_vulkan; - ret->draw = _draw_vulkan; - ret->clear = _clear_vulkan; - if (_init_vulkan(window) != 0) - rune_abort(); - return ret; -} diff --git a/render/vulkan/renderpass.c b/render/vulkan/renderpass.c deleted file mode 100644 index 981039a..0000000 --- a/render/vulkan/renderpass.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "renderpass.h" -#include "vkassert.h" -#include - -vkcmdbuffer_t* create_vkcmdbuffer(vkdev_t *dev, int primary) { - vkcmdbuffer_t *ret = rune_calloc(0, sizeof(vkcmdbuffer_t)); - - VkCommandBufferAllocateInfo ainfo; - ainfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - ainfo.pNext = NULL; - ainfo.commandPool = dev->cmd_pool; - if (primary == 1) - ainfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - else - ainfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - ainfo.commandBufferCount = 1; - vkassert(vkAllocateCommandBuffers(dev->ldev, &ainfo, &ret->handle)); - - ret->state = CMDBUF_INITIAL; - return ret; -} - -void destroy_vkcmdbuffer(vkcmdbuffer_t *cmdbuffer, vkdev_t *dev) { - vkFreeCommandBuffers(dev->ldev, dev->cmd_pool, 1, &cmdbuffer->handle); - rune_free(cmdbuffer); -} - -void cmdbuf_begin(vkcmdbuffer_t *cmdbuffer, int single, int rpass_cont, int sim_use) { - if (cmdbuffer->state != CMDBUF_INITIAL) { - log_output(LOG_FATAL, "Attempted to record to a command buffer not in initial state"); - rune_abort(); - } - - VkCommandBufferBeginInfo binfo; - binfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - binfo.flags = 0; - binfo.pNext = NULL; - if (single) - binfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - if (rpass_cont) - binfo.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - if (sim_use) - binfo.flags |= VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; - vkassert(vkBeginCommandBuffer(cmdbuffer->handle, &binfo)); - cmdbuffer->state = CMDBUF_RECORDING; -} - -void cmdbuf_end(vkcmdbuffer_t *cmdbuffer) { - if (cmdbuffer->state != CMDBUF_RECORDING) { - log_output(LOG_FATAL, "Attempted to end command buffer not in recording state"); - rune_abort(); - } - - vkassert(vkEndCommandBuffer(cmdbuffer->handle)); - cmdbuffer->state = CMDBUF_READY; -} - -void cmdbuf_submit(vkcmdbuffer_t *cmdbuffer, VkSemaphore *signal, VkSemaphore *wait, VkQueue queue_handle, VkFence fence_handle) { - if (cmdbuffer->state != CMDBUF_READY) { - log_output(LOG_FATAL, "Attempted to submit command buffer not in ready state"); - return; - } - - VkSubmitInfo sinfo; - sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - sinfo.pNext = NULL; - sinfo.commandBufferCount = 1; - sinfo.pCommandBuffers = &cmdbuffer->handle; - sinfo.signalSemaphoreCount = 1; - sinfo.pSignalSemaphores = signal; - sinfo.waitSemaphoreCount = 1; - sinfo.pWaitSemaphores = wait; - VkPipelineStageFlags flags[1] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - sinfo.pWaitDstStageMask = flags; - vkassert(vkQueueSubmit(queue_handle, 1, &sinfo, fence_handle)); - cmdbuffer->state = CMDBUF_SUBMITTED; -} - -void cmdbuf_reset(vkcmdbuffer_t *cmdbuffer) { - cmdbuffer->state = CMDBUF_INITIAL; -} - -vkcmdbuffer_t* cmdbuf_begin_single_use(vkdev_t *dev) { - vkcmdbuffer_t *ret = create_vkcmdbuffer(dev, 1); - cmdbuf_begin(ret, 1, 0, 0); - return ret; -} - -void cmdbuf_end_single_use(vkcmdbuffer_t *cmdbuffer, vkdev_t *dev, VkQueue queue) { - cmdbuf_end(cmdbuffer); - VkSubmitInfo sinfo; - sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - sinfo.pNext = NULL; - sinfo.waitSemaphoreCount = 0; - sinfo.pWaitSemaphores = NULL; - sinfo.pWaitDstStageMask = NULL; - sinfo.commandBufferCount = 1; - sinfo.pCommandBuffers = &cmdbuffer->handle; - sinfo.signalSemaphoreCount = 0; - sinfo.pSignalSemaphores = NULL; - vkassert(vkQueueSubmit(queue, 1, &sinfo, 0)); - - vkassert(vkQueueWaitIdle(queue)); - destroy_vkcmdbuffer(cmdbuffer, dev); -} - -vkrendpass_t* create_vkrendpass(vkdev_t *dev, vkswapchain_t *swapchain, vec4 area, vec4 color, float depth, uint32_t stencil) { - VkAttachmentDescription atdesc[2]; - atdesc[0].flags = 0; - atdesc[0].format = swapchain->format_khr.format; - atdesc[0].samples = VK_SAMPLE_COUNT_1_BIT; - atdesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - atdesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - atdesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - atdesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - atdesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - atdesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - atdesc[1].flags = 0; - atdesc[1].format = dev->depth_format; - atdesc[1].samples = VK_SAMPLE_COUNT_1_BIT; - atdesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - atdesc[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - atdesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - atdesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - atdesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - atdesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkAttachmentReference cref; - cref.attachment = 0; - cref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference dref; - dref.attachment = 1; - dref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass; - subpass.flags = 0; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &cref; - subpass.pDepthStencilAttachment = &dref; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = NULL; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = NULL; - - VkSubpassDependency dep; - dep.srcSubpass = VK_SUBPASS_EXTERNAL; - dep.dstSubpass = 0; - dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dep.srcAccessMask = 0; - dep.dstAccessMask = 0; - dep.dependencyFlags = 0; - - vkrendpass_t *ret = rune_alloc(sizeof(vkrendpass_t)); - ret->color[0] = color[0]; - ret->color[1] = color[1]; - ret->color[2] = color[2]; - ret->color[3] = color[3]; - ret->area[0] = area[0]; - ret->area[1] = area[1]; - ret->area[2] = area[2]; - ret->area[3] = area[3]; - ret->depth = depth; - ret->stencil = stencil; - VkRenderPassCreateInfo rcinfo; - rcinfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - rcinfo.pNext = NULL; - rcinfo.flags = 0; - rcinfo.attachmentCount = 2; - rcinfo.pAttachments = atdesc; - rcinfo.subpassCount = 1; - rcinfo.pSubpasses = &subpass; - rcinfo.dependencyCount = 1; - rcinfo.pDependencies = &dep; - vkassert(vkCreateRenderPass(dev->ldev, &rcinfo, NULL, &ret->handle)); - - log_output(LOG_DEBUG, "Initialized renderpass"); - return ret; -} - -void destroy_vkrendpass(vkrendpass_t *rendpass, vkdev_t *dev) { - if (rendpass->handle) - vkDestroyRenderPass(dev->ldev, rendpass->handle, NULL); - rune_free(rendpass); -} - -void renderpass_begin(vkcmdbuffer_t *buf, vkrendpass_t *rendpass, VkFramebuffer framebuf) { - if (buf->state != CMDBUF_RECORDING) { - log_output(LOG_FATAL, "Attempted to place command buffer not in recording state in a render pass"); - rune_abort(); - } - - VkClearValue cvals[2]; - cvals[0].color.float32[0] = rendpass->color[0]; - cvals[0].color.float32[1] = rendpass->color[1]; - cvals[0].color.float32[2] = rendpass->color[2]; - cvals[0].color.float32[3] = rendpass->color[3]; - cvals[1].depthStencil.depth = rendpass->depth; - cvals[1].depthStencil.stencil = rendpass->stencil; - - VkRenderPassBeginInfo binfo; - binfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - binfo.pNext = NULL; - binfo.renderPass = rendpass->handle; - binfo.framebuffer = framebuf; - binfo.renderArea.offset.x = rendpass->area[0]; - binfo.renderArea.offset.y = rendpass->area[1]; - binfo.renderArea.extent.width = rendpass->area[2]; - binfo.renderArea.extent.height = rendpass->area[3]; - binfo.clearValueCount = 2; - binfo.pClearValues = cvals; - - vkCmdBeginRenderPass(buf->handle, &binfo, VK_SUBPASS_CONTENTS_INLINE); - buf->state = CMDBUF_IN_RENDERPASS; -} - -void renderpass_end(vkcmdbuffer_t *buf, vkrendpass_t *rendpass) { - if (buf->state != CMDBUF_IN_RENDERPASS) { - log_output(LOG_FATAL, "Attempted to purge command buffer not in render pass"); - rune_abort(); - } - - vkCmdEndRenderPass(buf->handle); - buf->state = CMDBUF_RECORDING; -} diff --git a/render/vulkan/renderpass.h b/render/vulkan/renderpass.h deleted file mode 100644 index 1524438..0000000 --- a/render/vulkan/renderpass.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKRENDERPASS_H -#define VKRENDERPASS_H - -#include "vk_types.h" - -enum cmdbuf_state { - CMDBUF_INITIAL, - CMDBUF_RECORDING, - CMDBUF_IN_RENDERPASS, - CMDBUF_READY, - CMDBUF_SUBMITTED, -}; - -vkcmdbuffer_t* create_vkcmdbuffer(vkdev_t *dev, int primary); -void destroy_vkcmdbuffer(vkcmdbuffer_t *cmdbuffer, vkdev_t *dev); - -void cmdbuf_begin(vkcmdbuffer_t *cmdbuffer, int single, int rpass_cont, int sim_use); -void cmdbuf_end(vkcmdbuffer_t *cmdbuffer); -void cmdbuf_submit(vkcmdbuffer_t *cmdbuffer, VkSemaphore *signal, VkSemaphore *wait, VkQueue queue_handle, VkFence fence_handle); -void cmdbuf_reset(vkcmdbuffer_t *cmdbuffer); - -vkrendpass_t* create_vkrendpass(vkdev_t *dev, vkswapchain_t *swapchain, vec4 area, vec4 color, float depth, uint32_t stencil); -void destroy_vkrendpass(vkrendpass_t *rendpass, vkdev_t *dev); - -void renderpass_begin(vkcmdbuffer_t *buf, vkrendpass_t *rendpass, VkFramebuffer framebuf); -void renderpass_end(vkcmdbuffer_t *buf, vkrendpass_t *rendpass); - -#endif diff --git a/render/vulkan/swapchain.c b/render/vulkan/swapchain.c deleted file mode 100644 index 055d36f..0000000 --- a/render/vulkan/swapchain.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "swapchain.h" -#include "image.h" -#include "device.h" -#include "vkassert.h" -#include -#include -#include - -vkswapchain_t* create_swapchain(vksurface_t *surface, vkdev_t *dev) { - vkswapchain_t *swapchain = rune_alloc(sizeof(vkswapchain_t)); - VkExtent2D sc_extent = {surface->width, surface->height}; - swapchain->max_frames = 2; - get_swapchain_data(dev, &surface->handle); - swapchain->format_khr = dev->scdata.formats[0]; - if (dev->scdata.capabilities.currentExtent.width != UINT32_MAX) - sc_extent = dev->scdata.capabilities.currentExtent; - - VkExtent2D min = dev->scdata.capabilities.minImageExtent; - VkExtent2D max = dev->scdata.capabilities.maxImageExtent; - sc_extent.width = clamp(sc_extent.width, min.width, max.width); - sc_extent.height = clamp(sc_extent.height, min.height, max.height); - - uint32_t img_count = dev->scdata.capabilities.minImageCount + 1; - if (dev->scdata.capabilities.maxImageCount > 0 && img_count > dev->scdata.capabilities.maxImageCount) - img_count = dev->scdata.capabilities.maxImageCount; - - VkSwapchainCreateInfoKHR cinfo; - cinfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - cinfo.surface = surface->handle; - cinfo.minImageCount = img_count; - cinfo.imageFormat = swapchain->format_khr.format; - cinfo.imageColorSpace = swapchain->format_khr.colorSpace; - cinfo.imageExtent = sc_extent; - cinfo.imageArrayLayers = 1; - cinfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - cinfo.preTransform = dev->scdata.capabilities.currentTransform; - cinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - cinfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; - cinfo.clipped = VK_TRUE; - cinfo.oldSwapchain = NULL; - if (dev->queues[0].qfam != dev->queues[3].qfam) { - uint32_t qfams[] = {dev->queues[0].qfam, dev->queues[3].qfam}; - cinfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - cinfo.queueFamilyIndexCount = 2; - cinfo.pQueueFamilyIndices = qfams; - } else { - cinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - cinfo.queueFamilyIndexCount = 0; - cinfo.pQueueFamilyIndices = 0; - } - vkassert(vkCreateSwapchainKHR(dev->ldev, &cinfo, NULL, &swapchain->handle)); - vkassert(vkGetSwapchainImagesKHR(dev->ldev, swapchain->handle, &swapchain->img_count, NULL)); - - swapchain->images = rune_alloc(sizeof(VkImage) * swapchain->img_count); - swapchain->views = rune_alloc(sizeof(VkImageView) * swapchain->img_count); - vkassert(vkGetSwapchainImagesKHR(dev->ldev, swapchain->handle, &swapchain->img_count, swapchain->images)); - - VkImageViewCreateInfo vcinfo; - for (uint32_t i = 0; i < swapchain->img_count; i++) { - vcinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - vcinfo.pNext = NULL; - vcinfo.image = swapchain->images[i]; - vcinfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - vcinfo.format = swapchain->format_khr.format; - vcinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vcinfo.subresourceRange.baseMipLevel = 0; - vcinfo.subresourceRange.levelCount = 1; - vcinfo.subresourceRange.baseArrayLayer = 0; - vcinfo.subresourceRange.layerCount = 1; - vkassert(vkCreateImageView(dev->ldev, &vcinfo, NULL, &swapchain->views[i])); - } - - if (get_depth_format(dev) == 0) { - log_output(LOG_FATAL, "Failed to find a supported image format"); - rune_abort(); - } - - swapchain->depth_attachment = create_vkimage(dev, - dev->depth_format, - surface->width, - surface->height, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - VK_IMAGE_ASPECT_DEPTH_BIT, - 1); - swapchain->frame = 0; - log_output(LOG_DEBUG, "Initialized swapchain"); - return swapchain; -} - -void destroy_swapchain(vkswapchain_t *swapchain, vkdev_t *dev) { - for (uint32_t i = 0; i < swapchain->img_count; i++) - vkDestroyImageView(dev->ldev, swapchain->views[i], NULL); - destroy_vkimage(swapchain->depth_attachment, dev); - vkDestroySwapchainKHR(dev->ldev, swapchain->handle, NULL); - rune_free(swapchain->images); - rune_free(swapchain->views); -} - -int32_t vkswapchain_get_next_img(vkswapchain_t *swapchain, vkdev_t *dev, uint64_t tmout, VkFence fence, VkSemaphore img_available) { - uint32_t ret = 0; - VkResult res = vkAcquireNextImageKHR(dev->ldev, swapchain->handle, tmout, img_available, fence, &ret); - if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) { - log_output(LOG_ERROR, "Error on getting next image index"); - return -1; - } - - return (int32_t)ret; -} - -void vkswapchain_present(vkswapchain_t *swapchain, vkdev_t *dev, VkSemaphore *render_complete, uint32_t *img_index) { - VkPresentInfoKHR pinfo; - pinfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - pinfo.pNext = NULL; - pinfo.waitSemaphoreCount = 1; - pinfo.pWaitSemaphores = render_complete; - pinfo.swapchainCount = 1; - pinfo.pSwapchains = &swapchain->handle; - pinfo.pImageIndices = img_index; - pinfo.pResults = NULL; - - VkResult res = vkQueuePresentKHR(dev->queues[3].handle, &pinfo); - if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) - STUBBED("Recreate swapchain"); - else if (res != VK_SUCCESS) - log_output(LOG_ERROR, "Vulkan error: %s", get_vkerr_str(res)); - - swapchain->frame = (swapchain->frame + 1) % swapchain->max_frames; -} diff --git a/render/vulkan/swapchain.h b/render/vulkan/swapchain.h deleted file mode 100644 index 7c2e2a5..0000000 --- a/render/vulkan/swapchain.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKSWAPCHAIN_H -#define VKSWAPCHAIN_H - -#include "vk_types.h" - -vkswapchain_t* create_swapchain(vksurface_t *surface, vkdev_t *dev); -void destroy_swapchain(vkswapchain_t *swapchain, vkdev_t *dev); - -int32_t vkswapchain_get_next_img(vkswapchain_t *swapchain, vkdev_t *dev, uint64_t tmout, VkFence fence, VkSemaphore img_available); -void vkswapchain_present(vkswapchain_t *swapchain, vkdev_t *dev, VkSemaphore *render_complete, uint32_t *img_index); - -#endif diff --git a/render/vulkan/vk_types.h b/render/vulkan/vk_types.h deleted file mode 100644 index c7610c9..0000000 --- a/render/vulkan/vk_types.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VK_TYPES_H -#define VK_TYPES_H - -#include -#include -#include -#include - -typedef struct vkqueue { - uint32_t qfam; - VkQueue handle; -} vkqueue_t; - -typedef struct vksurface { - VkSurfaceKHR handle; - uint32_t width; - uint32_t height; -} vksurface_t; - -typedef struct vkcmdbuffer { - VkCommandBuffer handle; - int state; -} vkcmdbuffer_t; - -typedef struct vkfence { - VkFence handle; - int signal; -} vkfence_t; - -typedef struct vkimage { - VkImage handle; - VkDeviceMemory memory; - VkImageView view; - uint32_t width; - uint32_t height; -} vkimage_t; - -typedef struct ext_container { - const char** extensions; - uint32_t ext_count; -} ext_container_t; - -typedef struct vklayer_container { - const char** vklayer_names; - uint32_t vklayer_count; -} vklayer_container_t; - -typedef struct vkswapchain_data { - VkSurfaceCapabilitiesKHR capabilities; - VkSurfaceFormatKHR *formats; - VkPresentModeKHR *present_modes; - uint32_t format_count; - uint32_t present_count; -} vkswapchain_data_t; - -typedef struct vkrendpass { - VkRenderPass handle; - vec4 area; - vec4 color; - float depth; - uint32_t stencil; -} vkrendpass_t; - -typedef struct vkframebuffer { - VkFramebuffer handle; - uint32_t at_count; - VkImageView *attachments; - vkrendpass_t *rendpass; -} vkframebuffer_t; - -typedef struct vkdev { - VkPhysicalDevice pdev; - VkDevice ldev; - vkswapchain_data_t scdata; - vkqueue_t queues[4]; - VkCommandPool cmd_pool; - VkFormat depth_format; -} vkdev_t; - -typedef struct vkswapchain { - VkSwapchainKHR handle; - VkSurfaceFormatKHR format_khr; - VkFormat format; - VkImage *images; - VkImageView *views; - vkimage_t *depth_attachment; - uint8_t max_frames; - uint32_t frame; - uint32_t img_count; -} vkswapchain_t; - -typedef struct vkcontext { - VkInstance instance; - VkDebugUtilsMessengerEXT db_messenger; - VkSemaphore *queue_semaphores; - VkSemaphore *image_semaphores; - vksurface_t *surface; - vkswapchain_t *swapchain; - vkrendpass_t *rendpass; - vkdev_t *dev; - vkcmdbuffer_t** cmdbuffers; - vkframebuffer_t** framebuffers; - vkfence_t** fences_in_flight; - vkfence_t** images_in_flight; - uint32_t num_fences_in_flight; - uint32_t img_index; -} vkcontext_t; - -#endif diff --git a/render/vulkan/vkassert.h b/render/vulkan/vkassert.h deleted file mode 100644 index ca0a89d..0000000 --- a/render/vulkan/vkassert.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Rune Game Engine - * Copyright 2024 Danny Holman - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef VKASSERT_H -#define VKASSERT_H - -#include "vk_types.h" -#include -#include -#include - -static char* get_vkerr_str(VkResult res) { - char *ret; - switch (res) { - case VK_SUCCESS: - ret = "SUCCESS"; - break; - case VK_ERROR_OUT_OF_HOST_MEMORY: - ret = "OUT OF HOST MEMORY"; - break; - case VK_ERROR_OUT_OF_DEVICE_MEMORY: - ret = "OUT OF DEVICE MEMORY"; - break; - case VK_ERROR_INITIALIZATION_FAILED: - ret = "INITIALIZATION FAILED"; - break; - case VK_ERROR_LAYER_NOT_PRESENT: - ret = "VALIDATION LAYER NOT PRESENT"; - break; - case VK_ERROR_EXTENSION_NOT_PRESENT: - ret = "EXTENSION NOT PRESENT"; - break; - case VK_ERROR_INCOMPATIBLE_DRIVER: - ret = "INCOMPATIBLE DRIVER"; - break; - default: - ret = "UNKNOWN RESULT"; - break; - } - return strdup(ret); -} - -static inline void vkassert(VkResult value) { - if (value != VK_SUCCESS) { - log_output(LOG_FATAL, "Vulkan error: %s", get_vkerr_str(value)); - rune_abort(); - } -} - -#endif -- cgit v1.2.3