core sampler and buffer objects

This commit is contained in:
PancakeTAS 2025-06-29 19:04:45 +02:00
parent d3a903524a
commit d0bd00d412
No known key found for this signature in database
4 changed files with 228 additions and 0 deletions

59
include/core/buffer.hpp Normal file
View file

@ -0,0 +1,59 @@
#ifndef BUFFER_HPP
#define BUFFER_HPP
#include "device.hpp"
#include <vulkan/vulkan_core.h>
#include <vector>
#include <memory>
namespace Vulkan::Core {
///
/// C++ wrapper class for a Vulkan buffer.
///
/// This class manages the lifetime of a Vulkan buffer.
///
class Buffer {
public:
///
/// Create the buffer.
///
/// @param device Vulkan device
/// @param size Size of the buffer in bytes.
/// @param data Initial data for the buffer.
/// @param usage Usage flags for the buffer
///
/// @throws std::invalid_argument if the device or buffer size is invalid
/// @throws ls::vulkan_error if object creation fails.
///
Buffer(const Device& device, uint32_t size, std::vector<uint8_t> data,
VkBufferUsageFlags usage);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->buffer; }
/// Get the size of the buffer.
[[nodiscard]] uint32_t getSize() const { return this->size; }
/// Check whether the object is valid.
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->buffer); }
/// if (obj) operator. Checks if the object is valid.
explicit operator bool() const { return this->isValid(); }
/// Trivially copyable, moveable and destructible
Buffer(const Buffer&) noexcept = default;
Buffer& operator=(const Buffer&) noexcept = default;
Buffer(Buffer&&) noexcept = default;
Buffer& operator=(Buffer&&) noexcept = default;
~Buffer() = default;
private:
std::shared_ptr<VkBuffer> buffer;
std::shared_ptr<VkDeviceMemory> memory;
uint32_t size;
};
}
#endif // BUFFER_HPP

50
include/core/sampler.hpp Normal file
View file

@ -0,0 +1,50 @@
#ifndef SAMPLER_HPP
#define SAMPLER_HPP
#include "device.hpp"
#include <vulkan/vulkan_core.h>
#include <memory>
namespace Vulkan::Core {
///
/// C++ wrapper class for a Vulkan sampler.
///
/// This class manages the lifetime of a Vulkan sampler.
///
class Sampler {
public:
///
/// Create the sampler.
///
/// @param device Vulkan device
/// @param mode Address mode for the sampler.
///
/// @throws std::invalid_argument if the device is invalid.
/// @throws ls::vulkan_error if object creation fails.
///
Sampler(const Device& device, VkSamplerAddressMode mode);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->sampler; }
/// Check whether the object is valid.
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->sampler); }
/// if (obj) operator. Checks if the object is valid.
explicit operator bool() const { return this->isValid(); }
/// Trivially copyable, moveable and destructible
Sampler(const Sampler&) noexcept = default;
Sampler& operator=(const Sampler&) noexcept = default;
Sampler(Sampler&&) noexcept = default;
Sampler& operator=(Sampler&&) noexcept = default;
~Sampler() = default;
private:
std::shared_ptr<VkSampler> sampler;
};
}
#endif // SAMPLER_HPP

86
src/core/buffer.cpp Normal file
View file

@ -0,0 +1,86 @@
#include "core/buffer.hpp"
#include "utils/exceptions.hpp"
#include <algorithm>
#include <optional>
using namespace Vulkan::Core;
Buffer::Buffer(const Device& device, uint32_t size, std::vector<uint8_t> data,
VkBufferUsageFlags usage) : size(size) {
if (!device)
throw std::invalid_argument("Invalid Vulkan device");
if (size < data.size())
throw std::invalid_argument("Invalid buffer size");
// create buffer
const VkBufferCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VkBuffer bufferHandle{};
auto res = vkCreateBuffer(device.handle(), &desc, nullptr, &bufferHandle);
if (res != VK_SUCCESS || bufferHandle == VK_NULL_HANDLE)
throw ls::vulkan_error(res, "Failed to create Vulkan buffer");
// find memory type
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
VkMemoryRequirements memReqs;
vkGetBufferMemoryRequirements(device.handle(), bufferHandle, &memReqs);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
std::optional<uint32_t> memType{};
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
(memProps.memoryTypes[i].propertyFlags &
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))) {
memType.emplace(i);
break;
} // NOLINTEND
}
if (!memType.has_value())
throw ls::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
#pragma clang diagnostic pop
// allocate and bind memory
const VkMemoryAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = memType.value()
};
VkDeviceMemory memoryHandle{};
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
throw ls::vulkan_error(res, "Failed to allocate memory for Vulkan buffer");
res = vkBindBufferMemory(device.handle(), bufferHandle, memoryHandle, 0);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "Failed to bind memory to Vulkan buffer");
// store buffer and memory in shared ptr
this->buffer = std::shared_ptr<VkBuffer>(
new VkBuffer(bufferHandle),
[dev = device.handle()](VkBuffer* img) {
vkDestroyBuffer(dev, *img, nullptr);
}
);
this->memory = std::shared_ptr<VkDeviceMemory>(
new VkDeviceMemory(memoryHandle),
[dev = device.handle()](VkDeviceMemory* mem) {
vkFreeMemory(dev, *mem, nullptr);
}
);
// upload data to buffer
uint8_t* buf{};
res = vkMapMemory(device.handle(), memoryHandle, 0, size, 0, reinterpret_cast<void**>(&buf));
if (res != VK_SUCCESS || buf == nullptr)
throw ls::vulkan_error(res, "Failed to map memory for Vulkan buffer");
std::copy_n(data.data(), size, buf);
vkUnmapMemory(device.handle(), memoryHandle);
}

33
src/core/sampler.cpp Normal file
View file

@ -0,0 +1,33 @@
#include "core/sampler.hpp"
#include "utils/exceptions.hpp"
using namespace Vulkan::Core;
Sampler::Sampler(const Device& device, VkSamplerAddressMode mode) {
if (!device)
throw std::invalid_argument("Invalid Vulkan device");
// create sampler
const VkSamplerCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = mode,
.addressModeV = mode,
.addressModeW = mode,
.maxLod = VK_LOD_CLAMP_NONE
};
VkSampler samplerHandle{};
auto res = vkCreateSampler(device.handle(), &desc, nullptr, &samplerHandle);
if (res != VK_SUCCESS || samplerHandle == VK_NULL_HANDLE)
throw ls::vulkan_error(res, "Unable to create sampler");
// store the sampler in a shared pointer
this->sampler = std::shared_ptr<VkSampler>(
new VkSampler(samplerHandle),
[dev = device.handle()](VkSampler* samplerHandle) {
vkDestroySampler(dev, *samplerHandle, nullptr);
}
);
}