Feature/VkExtensions+VkLayers (#2)

This commit is contained in:
Preston McGee
2026-05-16 12:48:39 -07:00
committed by GitHub
parent 836012648b
commit a63c271f92
5 changed files with 213 additions and 23 deletions

View File

@@ -56,6 +56,9 @@ target_link_options(maiden_core PRIVATE --coverage)
target_link_options(maiden PRIVATE --coverage) target_link_options(maiden PRIVATE --coverage)
target_link_options(maiden_test PRIVATE --coverage) target_link_options(maiden_test PRIVATE --coverage)
# vulkan necessity
target_compile_definitions(maiden_core PRIVATE VULKAN_HPP_NO_STRUCT_CONSTRUCTORS)
target_link_libraries(maiden PRIVATE target_link_libraries(maiden PRIVATE
maiden_core maiden_core
SDL3::SDL3 SDL3::SDL3

View File

@@ -19,6 +19,20 @@ Basic outline where I pretty much copy the guide:
- ... Boring optimization stuff, like mipmaps, multithreading, multisampling - ... Boring optimization stuff, like mipmaps, multithreading, multisampling
- it has a section on raytracing :3 - it has a section on raytracing :3
Goals:
- loading 3d models
- applying textures
- simple diffuse lighting
- vertex animations
- 3d camera and scene
- transparency
Future ambitions:
- entity component system
- shadows
- raytracing
- rigidbody simulation
## Supporting App Infrastructure ## Supporting App Infrastructure
Although not crucial to the core rendering functions of the app, features separate from the rendering engine are convenient to have for testing and usability. Components below are less urgent but should be simpler to develop and implement. Although not crucial to the core rendering functions of the app, features separate from the rendering engine are convenient to have for testing and usability. Components below are less urgent but should be simpler to develop and implement.

View File

@@ -10,10 +10,13 @@ The maiden project is a GPU accelerated 3D rendering engine built with C++ based
### Clone Repository ### Clone Repository
```bash ```bash
$ git clone https://git.vxbard.net/homeburger/maiden.git # ssh recommended for contribution
$ git clone git@github.com:Blitblank/maiden.git
# http if you don't like ssh:
$ git clone https://github.com/Blitblank/maiden.git
# If there's any necessary submodules then: # If there's any necessary submodules then:
$ git clone --recurse-submodules https://git.vxbard.net/homeburger/maiden.git $ git clone --recurse-submodules git@github.com:Blitblank/maiden.git
# If you have already cloned the repository and you need its submodules: # If you have already cloned the repository and you need its submodules:
$ git submodule update --init --recursive $ git submodule update --init --recursive
@@ -70,7 +73,20 @@ $ cd build
$ gcovr -r .. --filter "../src" $ gcovr -r .. --filter "../src"
``` ```
### app troubleshooting here ## App Troubleshooting
Basically these are some tricky situations that I encountered when trying to execute this app throughout this development phase. If you are running on WSL Ubuntu 26.04 like me, then you mightr run into these too, hopefully my steps help fix.
note: I am running an x86_64 system with an Nvidia GPU so some things may be slightly different if your system doesn't match.
### [WARN: COPY MODE]
This seems like a WSL specific error and causes real issues with relaying graphics from linux to windows. I fixed this by installing new mesa drivers as reccommended by https://github.com/microsoft/wslg/discussions/312:
```bash
$ sudo add-apt-repository ppa:kisak/kisak-mesa
$ sudo apt-get update && sudo apt upgrade
```
Note: this resulted in the following erre "WARNING: dzn is not a conformant Vulkan implementation, testing use only." Running `$ vkcube` showed that this indeed was just a warning.
(for those curious, dzn is a compaitibility layer between DirectX12 and Vulkan for that WSL conformity)
## Development Roadmap ## Development Roadmap
### lots of todo here ### lots of todo here

View File

@@ -1,7 +1,6 @@
#include "Engine.hpp" #include "Engine.hpp"
#include "vulkan/vulkan.h"
#include <iostream> #include <iostream>
Engine::Engine(Window* window): window_(window) { Engine::Engine(Window* window): window_(window) {
@@ -13,24 +12,7 @@ Engine::Engine(Window* window): window_(window) {
void Engine::init() { void Engine::init() {
VkApplicationInfo appInfo { if(createInstance()) {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "maiden",
.apiVersion = VK_API_VERSION_1_4
};
uint32_t instanceExtensionsCount = 0;
char const* const* instanceExtensions{ SDL_Vulkan_GetInstanceExtensions(&instanceExtensionsCount) };
VkInstanceCreateInfo instanceCI {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledExtensionCount = instanceExtensionsCount,
.ppEnabledExtensionNames = instanceExtensions,
};
VkInstance instance;
if(vkCreateInstance(&instanceCI, nullptr, &instance) == VK_SUCCESS) {
// TODO: need some kind of logger service // TODO: need some kind of logger service
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Vulkan instance successfully created." << std::endl; std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Vulkan instance successfully created." << std::endl;
} else { } else {
@@ -49,3 +31,152 @@ void Engine::init() {
void Engine::draw() { void Engine::draw() {
} }
bool Engine::createInstance() {
uint32_t errorCount = 0;
// create the appInfo filled with information about our app
constexpr vk::ApplicationInfo appInfo { // using the c++ api instead of the c api
.pApplicationName = "maiden",
.applicationVersion = VK_MAKE_VERSION(1, 0, 0),
.pEngineName = "null",
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
.apiVersion = VK_API_VERSION_1_4 // this one is most important
};
std::vector<const char*> requiredInstanceExtensions = getRequiredInstanceExtensions();
// get all available extensions
auto extensionProperties = context_.enumerateInstanceExtensionProperties();
// print if we feel like it
std::cout << "Available Vulkan Extensions: " << std::endl;
for(const auto& extensionProperty : extensionProperties) {
std::cout << "\t" << extensionProperty.extensionName << std::endl;
} // this would be a logger.debug(...)
// check that all required extensions are available
for(uint32_t i = 0; i < requiredInstanceExtensions.size(); i++) { // for each extension that we require
bool found = false;
for(const auto& extensionProperty : extensionProperties) { // see if it matches any extensions that are provided
if(strcmp(extensionProperty.extensionName, requiredInstanceExtensions[i]) == 0) {
found = true;
break;
}
}
if(!found) {
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required SDL3 extension not supported: " << requiredInstanceExtensions[i] << std::endl;
errorCount++;
} else {
// in case you're curious
//std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] SDL3 extension located: " << requiredInstanceExtensions[i] << std::endl;
}
}
// get required validation layers as specified by our app
std::vector<const char*> requiredValidationLayers;
if(enableValidationLayers) requiredValidationLayers.assign(validationLayers.begin(), validationLayers.end());
// get available validation layers
auto validationLayerProperties = context_.enumerateInstanceLayerProperties();
// again print if we feel like it
std::cout << "Available Vulkan Validation Layers: " << std::endl;
for(const auto& validationLayer : validationLayerProperties) {
std::cout << "\t" << validationLayer.layerName << std::endl;
}
// check that all required validation layers are avilable
for(int i = 0; i < requiredValidationLayers.size(); i++) {
bool found = false;
for(const auto& validationLayer : validationLayerProperties) {
if(strcmp(requiredValidationLayers[i], validationLayer.layerName) == 0) {
found = true;
break;
}
}
if(!found) {
errorCount++;
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required validation layer not supported: " << requiredValidationLayers[i] << std::endl;
} else { // in case you're curious
//std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] VkValidation layer located: " << requiredValidationLayers[i] << std::endl;
}
}
// if any we had errors then we must exit
if(errorCount != 0) {
std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Unable to create Vulkan instance. Error count: " << errorCount << std::endl;
return false;
}
vk::InstanceCreateInfo instanceCreateInfo {
.pApplicationInfo = &appInfo,
.enabledLayerCount = static_cast<uint32_t>(requiredValidationLayers.size()),
.ppEnabledLayerNames = requiredValidationLayers.data(),
.enabledExtensionCount = static_cast<uint32_t>(requiredInstanceExtensions.size()),
.ppEnabledExtensionNames = requiredInstanceExtensions.data()
};
instance_ = vk::raii::Instance(context_, instanceCreateInfo);
return (instance_ != nullptr);
}
std::vector<const char*> Engine::getRequiredInstanceExtensions() {
// get extensions that our windowing library requires
uint32_t sdlExtensionsCount = 0;
const char* const* sdlExtensions{ SDL_Vulkan_GetInstanceExtensions(&sdlExtensionsCount) };
// what in the world is this kind of pointer btw
std::vector<const char*> requiredExtensions(sdlExtensions, sdlExtensions + static_cast<size_t>(sdlExtensionsCount));
// manually add an extension for handling validation layers
if(enableValidationLayers) {
requiredExtensions.push_back(vk::EXTDebugUtilsExtensionName);
}
return requiredExtensions;
}
bool Engine::initDebugMessenger() {
if(!enableValidationLayers) return false;
// masks for which debug messages we want to see
vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError);
vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags( vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation);
vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfoEXT{.messageSeverity = severityFlags,
.messageType = messageTypeFlags,
.pfnUserCallback = &debugCallback};
debugMessenger_ = instance_.createDebugUtilsMessengerEXT( debugUtilsMessengerCreateInfoEXT );
// we could get rid of this and just pass all the control to the logger
// like: "treat all messages of severity vk::eVerbose as our own DebugVerbosity::Info"
return (debugMessenger_ != nullptr);
}
VKAPI_ATTR vk::Bool32 VKAPI_CALL Engine::debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity,
vk::DebugUtilsMessageTypeFlagsEXT type,
const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
// this will eventually go through our logger
std::cout << "[ Validation Layer ] [Type: " << to_string(type) << "] " << pCallbackData->pMessage << std::endl;
/*
vk severity types:
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError
vk message types:
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance
*/
// returns whether or not we should abort, we'll always say no
return vk::False;
}

View File

@@ -1,6 +1,9 @@
#pragma once #pragma once
// resource allocation is initializion my beloved
#include <vulkan/vulkan_raii.hpp>
#include "Window.hpp" #include "Window.hpp"
class Engine { class Engine {
@@ -10,6 +13,7 @@ public:
Engine(Window* window); Engine(Window* window);
~Engine() = default; ~Engine() = default;
// initializes and sets up the vulkan instance. outside of constructor to allow control of order the order of initialization
void init(); void init();
// draw is called every render iteration in that while loop // draw is called every render iteration in that while loop
@@ -17,8 +21,30 @@ public:
private: private:
// might get rid of this // returns a list of the required extensions needed by our engine
std::vector<const char*> getRequiredInstanceExtensions();
// helper for organizing the creation of the vulkan instance. returns success or failure
bool createInstance();
// helper for attaching callbacks to the instance
bool initDebugMessenger();
// callback function for the vulkan debug extension: routes debug messages from validation layers to our app
static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity,
vk::DebugUtilsMessageTypeFlagsEXT type,
const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData);
Window* window_; Window* window_;
// Vulkan specific instance members
vk::raii::Context context_;
vk::raii::Instance instance_ = nullptr;
vk::raii::DebugUtilsMessengerEXT debugMessenger_ = nullptr;
// members for control over validation layers
static constexpr bool enableValidationLayers = true; // TODO: only true in debug mode
const std::vector<const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" };
}; };