Compare commits

..

5 Commits

9 changed files with 169 additions and 131 deletions

7
.gitignore vendored
View File

@@ -103,4 +103,9 @@ CMakeUserPresets.json
# Other
.cache/
.vscode/
build/
build*/
.kdev4/
*.kdev4
*_old.*

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "thirdparty/vk-bootstrap"]
path = thirdparty/vk-bootstrap
url = https://github.com/charles-lunarg/vk-bootstrap.git
[submodule "thirdparty/SDL"]
path = thirdparty/SDL
url = https://github.com/libsdl-org/SDL.git

View File

@@ -1,10 +1,11 @@
cmake_minimum_required(VERSION 3.10)
project(vksdlproj VERSION 0.1.0 LANGUAGES C CXX)
project(vksdlproj VERSION 0.2.0 LANGUAGES C CXX)
SET(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
set(EXE_NAME project)
#set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
#set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set(EXE_NAME sdl3vkgame)
set(PROGRAM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/vulkanapp.cc)
set(PROGRAM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/vk_app.cc)
option(SDL3_NONSYSTEM "Use SDL3 from folder in source tree" ON)
@@ -31,8 +32,6 @@ else()
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared)
endif()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/vk-bootstrap)
find_package(Vulkan REQUIRED COMPONENTS glslc)
# Compile each shader
@@ -66,7 +65,14 @@ elseif(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4)
endif()
if(WIN32)
add_custom_command(
TARGET ${EXE_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:SDL3::SDL3-shared> $<TARGET_FILE_DIR:${EXE_NAME}>
VERBATIM
)
endif()
# Linking
target_link_libraries(${EXE_NAME} PRIVATE SDL3::SDL3)
target_link_libraries(${EXE_NAME} PRIVATE Vulkan::Vulkan)
target_link_libraries(${EXE_NAME} PRIVATE vk-bootstrap::vk-bootstrap)
target_link_libraries(${EXE_NAME} PRIVATE Vulkan::Vulkan) # TODO: change this to Vulkna::Headers and use loader

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 macmacmac
Copyright (c) 2025 Alexander Raptis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,13 +1,19 @@
#pragma once
#include <SDL3/SDL_video.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_vulkan.h>
#include <cstdint>
#include <vulkan/vulkan.h>
#include <stdexcept>
#include <set>
#include <limits>
#include <algorithm>
#include <vector>
#include <optional>
#include <vulkan/vulkan.h>
#include "vk_types.hh"
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
@@ -19,53 +25,42 @@ const std::vector<const char*> deviceExtensions = {
const int MAX_FRAMES_IN_FLIGHT = 2;
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
VkResult CreateDebugUtilsMessengerEXT(VkInstance, const VkDebugUtilsMessengerCreateInfoEXT*, const VkAllocationCallbacks*, VkDebugUtilsMessengerEXT*);
void DestroyDebugUtilsMessengerEXT(VkInstance, VkDebugUtilsMessengerEXT, const VkAllocationCallbacks*);
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool complete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
class VulkanApp {
public:
VulkanApp(const uint32_t& _w, const uint32_t& _h) :
mWin(nullptr), mWidth(_w), mHeight(_h) {}
VulkanApp(const char *_t, const uint32_t& _w, const uint32_t& _h) :
mWin(nullptr), mWidth(_w), mHeight(_h), mTitle(_t),
mResized(false), mMinimized(false),
mPhysicalDevice(nullptr) {}
~VulkanApp() { cleanup(); }
void init();
void loop();
void cleanup();
SDL_AppResult mAppState = SDL_APP_CONTINUE;
SDL_AppResult init();
SDL_AppResult loop();
inline bool minimized() { return mMinimized; }
inline void minimized(bool _v) { mMinimized = _v; }
inline void resized(bool _v) { mResized = _v; }
private:
// SDL2
// SDL3
SDL_Window *mWin;
SDL_Event mEvent;
// Not tied to library
uint32_t mWidth;
uint32_t mHeight;
bool mActive = false;
bool mResized = false;
bool mMinimized = false;
const char *mTitle;
bool mResized;
bool mMinimized;
#ifdef NDEBUG
const bool mEnableValidationLayers = false;
#else
const bool mEnableValidationLayers = true;
#endif
// Vulkan
VkInstance mInstance;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice;
VkDevice mLogicalDevice;
VkSurfaceKHR mSurface;
@@ -90,7 +85,6 @@ private:
std::vector<VkSemaphore> mRenderFinishedSemaphores;
std::vector<VkFence> mInFlightFences;
VkDebugUtilsMessengerEXT mDebugMessenger;
void createInstance();
@@ -111,6 +105,7 @@ private:
void recordCommandBuffer(VkCommandBuffer, uint32_t);
void drawFrame();
void cleanup();
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice);
bool isDeviceSuitable(VkPhysicalDevice);
@@ -120,6 +115,7 @@ private:
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>&);
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>&);
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR&);
// Validation layer stuff
void setupDebugMessenger();
bool checkValidationLayerSupport();
@@ -129,11 +125,11 @@ private:
(void)pUserData;
if(messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
fprintf(stderr, "Validation Layer [Error/Warning]: %s \n", pCallbackData->pMessage);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Validation Layer [Error/Warning]: %s \n", pCallbackData->pMessage);
} else if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
fprintf(stderr, "Validation Layer [General]: %s \n", pCallbackData->pMessage);
SDL_Log("Validation Layer [General]: %s \n", pCallbackData->pMessage);
} else {
fprintf(stderr, "Validation Layer: %s \n", pCallbackData->pMessage);
SDL_Log("Validation Layer: %s \n", pCallbackData->pMessage);
}
return VK_FALSE;

18
include/vk_types.hh Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <vulkan/vulkan.hpp>
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool complete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};

View File

@@ -1,19 +1,61 @@
#include "vulkanapp.hh"
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL_main.h>
#include <exception>
#include "vk_app.hh"
int main() {
VulkanApp app(1024, 1024);
constexpr uint32_t winInitWidth = 1024;
constexpr uint32_t winInitHeight = 1024;
try {
app.init();
app.loop();
app.cleanup();
} catch(const std::exception& except) {
fprintf(stderr, "Error! %s\n", except.what());
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
(void)argc;
(void)argv;
return 1;
*appstate = new VulkanApp("Vulkan+SDL3 App", winInitWidth, winInitHeight);
VulkanApp* app = static_cast<VulkanApp*>(*appstate);
return app->init();
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
VulkanApp* app = static_cast<VulkanApp*>(appstate);
switch(event->type) {
case SDL_EVENT_QUIT:
app->mAppState = SDL_APP_SUCCESS;
break;
case SDL_EVENT_WINDOW_RESIZED:
app->resized(true);
break;
case SDL_EVENT_WINDOW_MINIMIZED:
app->minimized(true);
break;
case SDL_EVENT_WINDOW_RESTORED:
app->minimized(false);
break;
default:
break;
}
return 0;
return app->mAppState;
}
SDL_AppResult SDL_AppIterate(void *appstate) {
VulkanApp* app = static_cast<VulkanApp*>(appstate);
return app->loop();
}
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
VulkanApp* app = static_cast<VulkanApp*>(appstate);
#ifndef NDEBUG
if(result == SDL_APP_FAILURE)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Program failure occured, shutting down");
else
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Program shutting down, no errors.");
#endif
// Cleanup
if(app) delete app;
SDL_Quit();
}

View File

@@ -1,13 +1,4 @@
#include "vulkanapp.hh"
#include <SDL3/SDL.h>
#include <SDL3/SDL_vulkan.h>
#include <cstdio>
#include <stdexcept>
#include <set>
#include <limits>
#include <algorithm>
#include "vk_app.hh"
// Validator stuff
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
@@ -160,7 +151,7 @@ VkSurfaceFormatKHR VulkanApp::chooseSwapSurfaceFormat(const std::vector<VkSurfac
VkPresentModeKHR VulkanApp::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
for (const VkPresentModeKHR& availablePresentMode : availablePresentModes) {
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
if (availablePresentMode == VK_PRESENT_MODE_FIFO_KHR) {
return availablePresentMode;
}
}
@@ -304,13 +295,18 @@ void VulkanApp::drawFrame() {
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
void VulkanApp::init() {
// Initialize SDL2
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
//SDL_Vulkan_LoadLibrary(nullptr);
SDL_AppResult VulkanApp::init() {
// Initialize SDL3
SDL_Init(SDL_INIT_VIDEO);
// if(!SDL_Vulkan_LoadLibrary(nullptr)) {
// std::string err = "Could not load vulkan library! " + std::string(SDL_GetError()) + "\n";
//
// throw std::runtime_error(err);
// }
// Create the window
mWin = SDL_CreateWindow("Vulkan+SDL2 Application",
mWin = SDL_CreateWindow(mTitle,
mWidth,
mHeight,
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE
@@ -321,6 +317,9 @@ void VulkanApp::init() {
throw std::runtime_error(err);
}
SDL_SetWindowMaximumSize(mWin, mWidth, mHeight);
SDL_SetWindowMinimumSize(mWin, mWidth / 2, mHeight / 2);
// Vulkan stuff from here
createInstance();
setupDebugMessenger();
@@ -330,6 +329,7 @@ void VulkanApp::init() {
throw std::runtime_error("Could not create Vulkan surface!");
}
// TODO: Replace some of this crap with vulkan hpp code
selectPhysicalDevice();
createLogicalDevice();
createSwapChain();
@@ -340,29 +340,32 @@ void VulkanApp::init() {
createCommandPool();
createCommandBuffer();
createSyncObjects();
return SDL_APP_CONTINUE;
}
void VulkanApp::createInstance() {
if (enableValidationLayers && !checkValidationLayerSupport()) {
if (mEnableValidationLayers && !checkValidationLayerSupport()) {
throw std::runtime_error("Validation layers requested, but not available!");
}
// Get # of extensions then get extensions
uint32_t extensionCount = 0;
const char * const * extensionArr = SDL_Vulkan_GetInstanceExtensions(&extensionCount);
const char * const *extensionArr = SDL_Vulkan_GetInstanceExtensions(&extensionCount);
// Put extensions into vector
std::vector<const char*> extensionNames(extensionArr, extensionArr + extensionCount);
if(enableValidationLayers)
if(mEnableValidationLayers)
extensionNames.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
// App info
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Simple Triangle";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pApplicationName = "Triangle";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 1, 0);
appInfo.pEngineName = "RAPT2";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
appInfo.engineVersion = VK_MAKE_VERSION(1, 1, 0);
appInfo.apiVersion = VK_API_VERSION_1_1;
// Extensions
VkInstanceCreateInfo createInfo{};
@@ -374,7 +377,7 @@ void VulkanApp::createInstance() {
// Debug handling
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
if(enableValidationLayers) {
if(mEnableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
@@ -404,9 +407,8 @@ void VulkanApp::selectPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
if(deviceCount == 0) {
if(deviceCount == 0)
throw std::runtime_error("Could not find a Vulkan compatible GPU!");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
@@ -419,9 +421,8 @@ void VulkanApp::selectPhysicalDevice() {
}
}
if(mPhysicalDevice == VK_NULL_HANDLE) {
if(mPhysicalDevice == VK_NULL_HANDLE)
throw std::runtime_error("Could not find any suitable GPU!");
}
}
void VulkanApp::createLogicalDevice() {
@@ -458,7 +459,7 @@ void VulkanApp::createLogicalDevice() {
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
if (enableValidationLayers) {
if (mEnableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
@@ -838,7 +839,7 @@ bool VulkanApp::checkValidationLayerSupport() {
}
void VulkanApp::setupDebugMessenger() {
if (!enableValidationLayers) return;
if (!mEnableValidationLayers) return;
VkDebugUtilsMessengerCreateInfoEXT createInfo;
createInfo = {};
@@ -856,39 +857,17 @@ void VulkanApp::setupDebugMessenger() {
}
}
void VulkanApp::loop() {
SDL_AppResult VulkanApp::loop() {
if(mWin == nullptr) {
std::string err = "Could not find window " + std::string(SDL_GetError()) + "\n";
throw std::runtime_error(err);
}
mActive = true;
// Main loop
while(mActive) {
while(SDL_PollEvent(&mEvent)) {
switch (mEvent.type) {
case SDL_EVENT_QUIT:
mActive = false;
break;
case SDL_EVENT_WINDOW_RESIZED:
mResized = true;
break;
case SDL_EVENT_WINDOW_MINIMIZED:
mMinimized = true;
break;
case SDL_EVENT_WINDOW_RESTORED:
mMinimized = false;
break;
default:
break;
}
}
if(!mMinimized) drawFrame();
}
if(!mMinimized) drawFrame();
vkDeviceWaitIdle(mLogicalDevice);
return SDL_APP_CONTINUE;
}
void VulkanApp::cleanup() {
@@ -909,9 +888,8 @@ void VulkanApp::cleanup() {
vkDestroyDevice(mLogicalDevice, nullptr);
if (enableValidationLayers) {
if (mEnableValidationLayers)
DestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
}
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
vkDestroyInstance(mInstance, nullptr);
@@ -921,7 +899,4 @@ void VulkanApp::cleanup() {
SDL_DestroyWindow(mWin);
mWin = nullptr;
}
//SDL_Vulkan_UnloadLibrary();
SDL_Quit();
}

Submodule thirdparty/vk-bootstrap deleted from 0437431fd0