base vulkan classes

This commit is contained in:
PancakeTAS 2025-06-29 05:05:29 +02:00
parent bf2b683264
commit fe5a8520e5
No known key found for this signature in database
5 changed files with 247 additions and 0 deletions

60
include/device.hpp Normal file
View file

@ -0,0 +1,60 @@
#ifndef DEVICE_HPP
#define DEVICE_HPP
#include "instance.hpp"
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <memory>
namespace Vulkan {
///
/// C++ wrapper class for a Vulkan device.
///
/// This class manages the lifetime of a Vulkan device.
///
class Device {
public:
///
/// Create the device.
///
/// @param instance Vulkan instance
///
/// @throws std::invalid_argument if the instance is invalid.
/// @throws ls::vulkan_error if object creation fails.
///
Device(const Vulkan::Instance& instance);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->device; }
/// Get the physical device associated with this logical device.
[[nodiscard]] VkPhysicalDevice getPhysicalDevice() const { return this->physicalDevice; }
/// Get the compute queue family index.
[[nodiscard]] uint32_t getComputeFamilyIdx() const { return this->computeFamilyIdx; }
/// Get the compute queue.
[[nodiscard]] VkQueue getComputeQueue() const { return this->computeQueue; }
/// Check whether the object is valid.
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->device); }
/// if (obj) operator. Checks if the object is valid.
explicit operator bool() const { return this->isValid(); }
// Trivially copyable, moveable and destructible
Device(const Device&) noexcept = default;
Device& operator=(const Device&) noexcept = default;
Device(Device&&) noexcept = default;
Device& operator=(Device&&) noexcept = default;
~Device() = default;
private:
std::shared_ptr<VkDevice> device;
VkPhysicalDevice physicalDevice{};
uint32_t computeFamilyIdx{0};
VkQueue computeQueue{};
};
}
#endif // DEVICE_HPP

44
include/instance.hpp Normal file
View file

@ -0,0 +1,44 @@
#ifndef INSTANCE_HPP
#define INSTANCE_HPP
#include <vulkan/vulkan_core.h>
#include <memory>
namespace Vulkan {
///
/// C++ wrapper class for a Vulkan instance.
///
/// This class manages the lifetime of a Vulkan instance.
///
class Instance {
public:
///
/// Create the instance.
///
/// @throws ls::vulkan_error if object creation fails.
///
Instance();
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return this->instance ? *this->instance : VK_NULL_HANDLE; }
/// Check whether the object is valid.
[[nodiscard]] bool isValid() const { return this->handle() != VK_NULL_HANDLE; }
/// if (obj) operator. Checks if the object is valid.
explicit operator bool() const { return this->isValid(); }
/// Trivially copyable, moveable and destructible
Instance(const Instance&) noexcept = default;
Instance& operator=(const Instance&) noexcept = default;
Instance(Instance&&) noexcept = default;
Instance& operator=(Instance&&) noexcept = default;
~Instance() = default;
private:
std::shared_ptr<VkInstance> instance;
};
}
#endif // INSTANCE_HPP

85
src/device.cpp Normal file
View file

@ -0,0 +1,85 @@
#include "device.hpp"
#include "utils/exceptions.hpp"
#include <optional>
#include <vector>
using namespace Vulkan;
Device::Device(const Instance& instance) {
if (!instance)
throw std::invalid_argument("Invalid Vulkan instance");
// get all physical devices
uint32_t deviceCount{};
auto res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, nullptr);
if (res != VK_SUCCESS || deviceCount == 0)
throw ls::vulkan_error(res, "Failed to enumerate physical devices");
std::vector<VkPhysicalDevice> devices(deviceCount);
res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, devices.data());
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "Failed to get physical devices");
// find first discrete GPU
std::optional<VkPhysicalDevice> physicalDevice;
for (const auto& device : devices) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(device, &properties);
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
physicalDevice = device;
break;
}
}
if (!physicalDevice)
throw ls::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No discrete GPU found");
// find queue family indices
uint32_t familyCount{};
vkGetPhysicalDeviceQueueFamilyProperties(*physicalDevice, &familyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(familyCount);
vkGetPhysicalDeviceQueueFamilyProperties(*physicalDevice, &familyCount, queueFamilies.data());
std::optional<uint32_t> computeFamilyIdx;
for (uint32_t i = 0; i < familyCount; ++i) {
if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
computeFamilyIdx = i;
}
if (!computeFamilyIdx)
throw ls::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No compute queue family found");
// create logical device
const float queuePriority{1.0F}; // highest priority
const VkDeviceQueueCreateInfo computeQueueDesc{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = *computeFamilyIdx,
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
const VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &computeQueueDesc
};
VkDevice deviceHandle{};
res = vkCreateDevice(*physicalDevice, &deviceCreateInfo, nullptr, &deviceHandle);
if (res != VK_SUCCESS | deviceHandle == VK_NULL_HANDLE)
throw ls::vulkan_error(res, "Failed to create logical device");
// get compute queue
VkQueue queueHandle{};
vkGetDeviceQueue(deviceHandle, *computeFamilyIdx, 0, &queueHandle);
// store in shared ptr
this->computeQueue = queueHandle;
this->computeFamilyIdx = *computeFamilyIdx;
this->physicalDevice = *physicalDevice;
this->device = std::shared_ptr<VkDevice>(
new VkDevice(deviceHandle),
[](VkDevice* device) {
vkDestroyDevice(*device, nullptr);
}
);
}

46
src/instance.cpp Normal file
View file

@ -0,0 +1,46 @@
#include "instance.hpp"
#include "utils/exceptions.hpp"
#include <vector>
using namespace Vulkan;
const std::vector<const char*> requiredExtensions = {
};
const std::vector<const char*> requiredLayers = {
"VK_LAYER_KHRONOS_validation"
};
Instance::Instance() {
// create Vulkan instance
const VkApplicationInfo appInfo{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "lsfg-vk-base",
.applicationVersion = VK_MAKE_VERSION(0, 0, 1),
.pEngineName = "lsfg-vk-base",
.engineVersion = VK_MAKE_VERSION(0, 0, 1),
.apiVersion = VK_API_VERSION_1_4
};
const VkInstanceCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledLayerCount = static_cast<uint32_t>(requiredLayers.size()),
.ppEnabledLayerNames = requiredLayers.data(),
.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size()),
.ppEnabledExtensionNames = requiredExtensions.data()
};
VkInstance instanceHandle{};
auto res = vkCreateInstance(&createInfo, nullptr, &instanceHandle);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "Failed to create Vulkan instance");
// store in shared ptr
this->instance = std::shared_ptr<VkInstance>(
new VkInstance(instanceHandle),
[](VkInstance* instance) {
vkDestroyInstance(*instance, nullptr);
}
);
}

12
src/main.cpp Normal file
View file

@ -0,0 +1,12 @@
#include "device.hpp"
#include "instance.hpp"
#include <iostream>
int main() {
const Vulkan::Instance instance;
const Vulkan::Device device(instance);
std::cerr << "Application finished" << '\n';
return 0;
}