This commit is contained in:
Martin Slachta
2026-07-18 14:31:15 +02:00
commit a04f0dc262
3343 changed files with 1140208 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
#pragma once
@@ -0,0 +1,33 @@
#pragma once
#include <volk.h>
#include <vector>
#include "Gpu.hpp"
struct Framebuffer {
VkFramebuffer framebuffer;
Framebuffer(VkFramebuffer fb);
Framebuffer(const Gpu* gpu, const VkRenderPass renderpass, const VkExtent2D extent,
const std::vector<VkImageView>& attachments);
};
class FramebufferBuilder {
private:
VkRenderPass m_renderpass;
VkExtent2D m_extent;
std::vector<ImageView> m_attachments;
public:
FramebufferBuilder(VkRenderPass renderpass, VkExtent2D extent);
FramebufferBuilder(VkRenderPass renderpass, VkExtent2D extent, uint32_t numAttachments);
FramebufferBuilder(VkRenderPass renderpass, VkExtent2D extent, std::vector<ImageView> attachments);
FramebufferBuilder& set_attachment(uint32_t index, VkImageView view);
Framebuffer build(const Gpu* gpu);
};
+120
View File
@@ -0,0 +1,120 @@
#pragma once
#include "result.hpp"
#include "props.hpp"
#include "Instance.hpp"
#include "Surface.hpp"
#include "resources/GpuAllocator.h"
#include <vector>
#include <memory>
#include <optional>
#include <stdexcept>
/**
* Abstract interface for a GPU
*/
class Gpu {
private:
const Instance* m_instance;
VkPhysicalDevice m_gpu = VK_NULL_HANDLE;
VkDevice m_dev = VK_NULL_HANDLE;
VkDescriptorPool m_descriptorPool{};
// Queue for a general commands
VkQueue m_graphicsQueue{};
uint32_t m_graphicsQueueIdx{};
VkCommandPool m_graphicsCommandPool{};
// Queue for a transfer commands
VkQueue m_transferQueue{};
uint32_t m_transferQueueIdx{};
VkCommandPool m_transferCommandPool{};
// Queue for present commands
VkQueue m_presentQueue{};
uint32_t m_presentQueueIdx{};
VkCommandPool m_presentCommandPool{};
VkCommandBuffer m_tracyCommandBuffer;
std::vector<int32_t> get_queues(std::optional<Surface*> surface);
ResultCode create_logical_device(std::optional<Surface*> surface);
ResultCode choose_gpu(VkPhysicalDevice *pOut);
ResultCode create_descriptor_pool();
std::unique_ptr<GpuAllocator> m_pAllocator;
public:
GET(m_gpu, gpu);
GET(m_dev, dev);
[[nodiscard]] const Instance* instance() const {
return m_instance;
}
GET(m_descriptorPool, descriptor_pool);
GET(m_graphicsCommandPool, graphics_command_pool);
GET(m_graphicsQueue, graphics_queue);
GET(m_transferCommandPool, transfer_command_pool);
GET(m_transferQueue, transfer_queue);
GET(m_tracyCommandBuffer, tracy_cmd_buf);
GET(m_pAllocator.get(), memory);
explicit
Gpu(const Instance* instance, std::optional<Surface*> surface);
~Gpu();
// Forbid copy
Gpu(const Gpu&) = delete;
static result<Gpu, ResultCode> create(std::shared_ptr<const Instance> instance, VkSurfaceKHR surface);
inline std::vector<uint32_t> present_queue_ids() const {
return {m_presentQueueIdx};
}
inline uint32_t transfer_queue_idx() const {
return m_transferQueueIdx;
}
void enqueue_present(VkPresentInfoKHR *pPresentInfo) const;
void enqueue_graphics(VkSubmitInfo2 *pSubmitInfo, VkFence fence) const;
void enqueue_transfer(VkSubmitInfo *pSubmitInfo, VkFence fence) const;
inline VkSemaphore create_semaphore() const {
VkSemaphoreCreateInfo semaphore_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
VkSemaphore semaphore = VK_NULL_HANDLE;
if(vkCreateSemaphore(this->dev(), &semaphore_info, nullptr, &semaphore)) {
throw std::runtime_error("Failed to create semaphore");
}
return semaphore;
}
inline VkFence create_fence(bool is_signaled) const {
VkFenceCreateInfo fence_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = is_signaled ? VK_FENCE_CREATE_SIGNALED_BIT : (VkFenceCreateFlagBits)0
};
VkFence fence = VK_NULL_HANDLE;
if(vkCreateFence(this->dev(), &fence_info, nullptr, &fence)) {
throw std::runtime_error("Failed to create fence");
}
return fence;
}
};
+17
View File
@@ -0,0 +1,17 @@
//
// Created by martin on 11/12/23.
//
#ifndef LOFT_GPUSCHEDULER_H
#define LOFT_GPUSCHEDULER_H
#include <volk.h>
class GpuScheduler {
public:
virtual void enqueue_transfer(VkSubmitInfo submitInfo) = 0;
virtual void enqueue_graphics(VkSubmitInfo submitInfo) = 0;
virtual void enqueue_present(VkPresentInfoKHR presentInfo) = 0;
};
#endif //LOFT_GPUSCHEDULER_H
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include <iterator>
#include <volk.h>
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
#include <vulkan/vulkan_core.h>
#include "props.hpp"
#include "debug/Debug.hpp"
/**
* Maintains a connection to a GPU driver
*/
class Instance {
private:
VkInstance m_instance;
public:
GET(m_instance, instance);
// disable copy
Instance(const Instance&) = delete;
Instance() = delete;
Instance(const Instance&& other) {
this->m_instance = other.m_instance;
}
/**
* Creates new instance and initializes a connection to a GPU driver
* @param applicationName Name of the application
* @param engineName Name of the rendering engine
* @param extensions Additional Vulkan extensions you might want to enable
* @param callback Debug log callback
*/
Instance(const std::string applicationName,
const std::string engineName,
std::vector<std::string> extensions,
std::vector<std::string> layers,
lft::dbg::lft_log_callback callback);
static std::vector<std::string> find_unsupported_layers(
std::vector<std::string>& required_layers
) {
uint32_t available_layers_count = 0;
vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr);
if(available_layers_count == 0) {
return required_layers;
}
std::vector<VkLayerProperties> available_layers(available_layers_count);
vkEnumerateInstanceLayerProperties(&available_layers_count, available_layers.data());
std::vector<std::string> available_layer_names(available_layers_count);
std::transform(available_layers.begin(), available_layers.end(),
available_layer_names.begin(),
[](const VkLayerProperties& props) {
return std::string(props.layerName);
});
std::sort(available_layer_names.begin(), available_layer_names.end());
std::sort(required_layers.begin(), required_layers.end());
std::vector<std::string> unsupported_layers;
std::set_difference(required_layers.begin(), required_layers.end(),
available_layer_names.begin(), available_layer_names.end(),
std::back_inserter(unsupported_layers));
return unsupported_layers;
}
/**
* Checks if the required extensions are available
* @return A vector of indices of not available extensionse
*/
static std::vector<std::string> check_extensions(const std::vector<std::string>& requiredExtensions) {
auto supportedExtensions = list_extensions();
std::vector<std::string> unsupported_extensions;
std::set_difference(requiredExtensions.begin(), requiredExtensions.end(),
supportedExtensions.begin(), supportedExtensions.end(),
std::back_inserter(unsupported_extensions));
return unsupported_extensions;
}
/**
* Gets a list of available physical devices
* @return A vector of VkPhysicalDevice objects representing available physical devices
*/
[[nodiscard]] inline std::vector<VkPhysicalDevice> list_physical_devices() const {
uint32_t numDevices = 0;
vkEnumeratePhysicalDevices(m_instance, &numDevices, nullptr);
std::vector<VkPhysicalDevice> devices(numDevices);
vkEnumeratePhysicalDevices(m_instance, &numDevices, devices.data());
return devices;
}
/**
* Gets a list of available instance extensions
* @return A vector of strings representing available instance extensions
*/
[[nodiscard]] static inline std::vector<std::string> list_extensions() {
uint32_t numExtensions = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &numExtensions, nullptr);
std::vector<VkExtensionProperties> extensions(numExtensions);
vkEnumerateInstanceExtensionProperties(nullptr, &numExtensions, extensions.data());
std::vector<std::string> extension_names(numExtensions);
std::transform(extensions.begin(), extensions.end(),
extension_names.begin(),
[](const VkExtensionProperties& props) {
return std::string(props.extensionName);
});
return extension_names;
}
};
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <exception>
#include <string>
namespace lft {
class GpuException : public std::exception {
std::string m_reason;
public:
GpuException(const std::string& reason) : m_reason(reason) {
}
virtual const char* what() const noexcept override {
return m_reason.c_str();
}
};
}
+2
View File
@@ -0,0 +1,2 @@
#pragma once
+120
View File
@@ -0,0 +1,120 @@
#pragma once
#include <optional>
#include <algorithm>
#include <stdexcept>
#include "shaders/Pipeline.hpp"
namespace lft {
class Recording;
class RecordingBindPoint {
private:
const Recording* m_recording;
Pipeline m_pipeline;
VkPipelineBindPoint m_bind_point;
public:
RecordingBindPoint(
const Recording* recording,
const Pipeline pipeline,
VkPipelineBindPoint bind_point
);
const RecordingBindPoint& bind_descriptor_set(
uint32_t set,
VkDescriptorSet descriptor_set
) const;
const RecordingBindPoint& bind_descriptor_sets(
uint32_t first_set,
const std::vector<VkDescriptorSet>& descriptor_sets
) const;
const RecordingBindPoint& push_constants(
VkShaderStageFlags shader_stages,
uint32_t offset, uint32_t size, const void* data
) const;
};
class Recording {
private:
VkCommandBuffer m_cmdbuf;
std::optional<RecordingBindPoint> m_latest_graphics_pipeline;
public:
GET(m_cmdbuf, cmdbuf);
Recording(VkCommandBuffer cmdbuf) : m_cmdbuf(cmdbuf) {}
/**
* Binds pipeline. Will bind all the descriptor sets, etc. to it after.
*/
inline const RecordingBindPoint bind_graphics_pipeline(const Pipeline& pipeline) const {
return RecordingBindPoint(this, pipeline, VK_PIPELINE_BIND_POINT_GRAPHICS);
}
/**
* Binds pipeline. Will bind all the descriptor sets, etc. to it after.
*/
inline const RecordingBindPoint bind_compute_pipeline(const Pipeline& pipeline) const {
return RecordingBindPoint(this, pipeline, VK_PIPELINE_BIND_POINT_COMPUTE);
}
inline const Recording& draw(
uint32_t vertex_count,
uint32_t instance_count,
uint32_t first_vertex,
uint32_t first_instance
) const {
vkCmdDraw(m_cmdbuf, vertex_count, instance_count, first_vertex, first_instance);
return *this;
}
inline const Recording& draw_indexed(
uint32_t index_count,
uint32_t instance_count,
uint32_t first_index,
uint32_t vertex_offset,
uint32_t first_instance
) const {
vkCmdDrawIndexed(m_cmdbuf, index_count, instance_count, first_index, vertex_offset, first_instance);
return *this;
}
inline const Recording& bind_vertex_buffers(
const std::vector<Buffer> buffers,
std::vector<std::size_t> offsets
) const {
std::vector<VkBuffer> vertex_buffers(buffers.size());
std::transform(buffers.begin(), buffers.end(), vertex_buffers.begin(),
[](const Buffer& buffer) { return buffer.buf; });
vkCmdBindVertexBuffers(m_cmdbuf, 0, vertex_buffers.size(), vertex_buffers.data(), offsets.data());
return *this;
}
inline const Recording& bind_index_buffer(
const Buffer index_buffer,
std::size_t offset,
VkIndexType index_type
) const {
vkCmdBindIndexBuffer(m_cmdbuf, index_buffer.buf, offset, index_type);
return *this;
}
inline const Recording& dispatch(
uint32_t group_count_x,
uint32_t group_count_y,
uint32_t group_count_z
) const {
vkCmdDispatch(m_cmdbuf, group_count_x, group_count_y, group_count_z);
return *this;
}
};
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <volk.h>
#include "Gpu.hpp"
#include "RenderPassLayout.hpp"
class RenderContext {
private:
VkCommandBuffer m_commandBuffer;
Gpu *m_pGpu;
RenderPassLayout *m_pLayout;
VkFramebuffer m_framebuffer;
public:
GET(m_commandBuffer, command_buffer);
GET(m_framebuffer, framebuffer);
GET(m_pGpu, gpu);
GET(m_pLayout, layout);
RenderContext(Gpu *pGpu, VkCommandBuffer commandBuffer,
RenderPassLayout *pLayout, VkFramebuffer framebuffer) :
m_pGpu(pGpu), m_commandBuffer(commandBuffer), m_pLayout(pLayout),
m_framebuffer(framebuffer) {
}
};
@@ -0,0 +1,9 @@
#pragma once
#include <volk.h>
struct RenderPassLayout {
VkRenderPass renderpass;
VkDescriptorSetLayout setLayout[4];
VkPipelineLayout layout;
};
+83
View File
@@ -0,0 +1,83 @@
#pragma once
#include <memory>
#include <stdexcept>
#include <volk.h>
#include "Gpu.hpp"
namespace lft {
class SamplerBuilder {
private:
VkSamplerCreateInfo m_create_info;
public:
SamplerBuilder() : m_create_info({}) {
m_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
}
inline SamplerBuilder& address_mode_u(VkSamplerAddressMode address_mode) {
m_create_info.addressModeU = address_mode;
return *this;
}
inline SamplerBuilder& address_mode_v(VkSamplerAddressMode address_mode) {
m_create_info.addressModeV = address_mode;
return *this;
}
inline SamplerBuilder& address_mode_w(VkSamplerAddressMode address_mode) {
m_create_info.addressModeW = address_mode;
return *this;
}
inline SamplerBuilder& address_mode(VkSamplerAddressMode address_mode) {
address_mode_u(address_mode);
address_mode_v(address_mode);
address_mode_w(address_mode);
return *this;
}
inline SamplerBuilder& min_filter(VkFilter filter) {
m_create_info.minFilter = filter;
return *this;
}
inline SamplerBuilder& mag_filter(VkFilter filter) {
m_create_info.magFilter = filter;
return *this;
}
inline SamplerBuilder& filter(VkFilter filter) {
min_filter(filter);
mag_filter(filter);
return *this;
}
inline SamplerBuilder& mipmap_mode(VkSamplerMipmapMode mipmap_mode) {
m_create_info.mipmapMode = mipmap_mode;
return *this;
}
inline SamplerBuilder& border_color(VkBorderColor color) {
m_create_info.borderColor = color;
return *this;
}
inline SamplerBuilder& lod(float min_lod, float max_lod) {
m_create_info.minLod = min_lod;
m_create_info.maxLod = max_lod;
return *this;
}
inline VkSampler build(const Gpu* gpu) {
VkSampler sampler = VK_NULL_HANDLE;
if(vkCreateSampler(gpu->dev(), &m_create_info, nullptr, &sampler)) {
throw std::runtime_error("Failed to create sampler");
}
return sampler;
}
};
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <utility>
#include <volk.h>
#include "Instance.hpp"
/**
* Surface is a target for the Gpu instance to render to.
*/
struct Surface {
private:
VkInstance m_instance;
VkSurfaceKHR m_vk_surface;
public:
GET(m_vk_surface, surface);
Surface(const Instance* instance, VkSurfaceKHR vk_surface) :
m_instance(instance->instance()), m_vk_surface(vk_surface) {}
Surface(const Surface&) = delete;
Surface(Surface&& other) noexcept :
m_instance(std::exchange(other.m_instance, nullptr)),
m_vk_surface(std::exchange(other.m_vk_surface, nullptr)) {
}
Surface& operator=(const Surface& other) = delete;
Surface& operator=(const Surface&& other) noexcept {
this->m_vk_surface = other.m_vk_surface;
this->m_instance = other.m_instance;
return *this;
}
~Surface() {
vkDestroySurfaceKHR(m_instance, m_vk_surface, nullptr);
}
};
@@ -0,0 +1,41 @@
#pragma once
#include "Recording.hpp"
#include <volk.h>
namespace lft {
class ImageSubresource {
};
class ImageSubresourceRange {
VkImageSubresourceRange m_range;
public:
ImageSubresourceRange(const Image& image,
uint32_t base_mip_level, uint32_t level_count,
uint32_t base_array_layer, uint32_t layer_count) {
m_range.aspectMask = image.m_aspect_mask;
m_range.baseMipLevel = base_mip_level;
m_range.levelCount = level_count;
m_range.baseArrayLayer = base_array_layer;
m_range.layerCount = layer_count;
}
static ImageSubresourceRange full(const Image& image) {
return ImageSubresourceRange(image, 0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS);
}
static ImageSubresourceRange mipmap(const Image& image, uint32_t base_mip_level, uint32_t level_count) {
return ImageSubresourceRange(image, 0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS);
}
};
}
+58
View File
@@ -0,0 +1,58 @@
//
// Created by martin on 8/1/24.
//
#pragma once
#ifndef LOFT_DEBUG_HPP
#define LOFT_DEBUG_HPP
#include <string>
#include <stdarg.h>
namespace lft::dbg {
enum LogMessageSeverity {
info = 0,
warning,
error
};
enum LogMessageType {
general = 0,
validation,
performance
};
typedef void(*lft_log_callback)(LogMessageSeverity, LogMessageType, const char *__restrict __format, va_list args);
}
extern lft::dbg::lft_log_callback g_logCallback;
#define LOG_INFO(fmt, ...) g_logCallback(lft::dbg::LogMessageSeverity::info, lft::dbg::LogMessageType::general, fmt, __VA_ARGS__)
#define LOG_WARN(fmt, ...) g_logCallback(lft::dbg::LogMessageSeverity::warning, lft::dbg::LogMessageType::general, fmt, __VA_ARGS__)
#define LOG_FAIL(fmt, ...) g_logCallback(lft::dbg::LogMessageSeverity::error, lft::dbg::LogMessageType::general, fmt, __VA_ARGS__)
namespace lft::log {
static void info(const char* __format, ...) {
va_list list;
va_start(list, __format);
// g_logCallback(lft::dbg::LogMessageSeverity::info, lft::dbg::LogMessageType::general, __format, list);
va_end(list);
}
static void warn(const char* __format, ...) {
va_list list;
va_start(list, __format);
// g_logCallback(lft::dbg::LogMessageSeverity::warning, lft::dbg::LogMessageType::general, __format, list);
va_end(list);
}
static void fail(const char* __format, ...) {
va_list list;
va_start(list, __format);
// g_logCallback(lft::dbg::LogMessageSeverity::error, lft::dbg::LogMessageType::general, __format, list);
va_end(list);
}
}
#endif //LOFT_DEBUG_HPP
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "Gpu.hpp"
class MockGpu : Gpu {
public:
MockGpu();
};
+32
View File
@@ -0,0 +1,32 @@
//
// Created by martin on 7/1/24.
//
#ifndef LOFT_SHADERBINARY_H
#define LOFT_SHADERBINARY_H
#include <utility>
#include <vector>
#include <cstdint>
struct ShaderBinary {
private:
std::vector<uint32_t> m_data;
public:
inline const std::vector<uint32_t>& data() const {
return m_data;
}
inline const uint32_t code_size() const {
// last integer is '\0' => should not be included in code size
return m_data.size() - 1;
}
explicit ShaderBinary(std::vector<uint32_t> data) :
m_data(std::move(data)) {
}
};
#endif //LOFT_SHADERBINARY_H
+14
View File
@@ -0,0 +1,14 @@
#include <cstdint>
#include <string>
#include "ShaderBinary.h"
namespace io::file {
/**
* Reads file on path in as binary.
*
* @param path Path of the file
* @param pOutSize Pointer to the size_t variable to which the number of
* bytes of the file will be set.
*/
ShaderBinary read_binary(const std::string& path);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#if _WIN32
#define GET(slot, name) const inline decltype(slot) name() const { return slot; }
#define REF(slot, name) const inline decltype(slot)& name() const { return slot; }
#else
#define GET(slot, name) [[nodiscard("Do not discard result of a getter! Use the result!")]] \
const inline __typeof(slot) name() const { return slot; }
#define REF(slot, name) [[nodiscard("Do not discard result of a getter! Use the result!")]] \
const inline __typeof(slot)& name() const { return slot; }
#endif
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <string>
#include <volk.h>
#include <memory>
#include "GpuAllocation.h"
class Gpu;
/**
* Buffer
* Device memory suballocation with generic data inside.
*/
struct Buffer {
VkBuffer buf;
GpuAllocation allocation;
Buffer() : Buffer(VK_NULL_HANDLE, {}) {
}
Buffer(VkBuffer buffer, GpuAllocation allocation) :
buf(buffer), allocation(allocation) {
}
Buffer(const Buffer&& a) noexcept :
buf(a.buf), allocation(a.allocation) {
}
Buffer(const Buffer& a) noexcept :
buf(a.buf), allocation(a.allocation) {
}
void set_debug_name(const Gpu* gpu, const std::string& name) const;
/* disable copy */
};
@@ -0,0 +1,37 @@
#pragma once
#include "Gpu.hpp"
#include <vector>
/**
* Writes data to a buffer by using a staging buffer
*/
class BufferBusWriter {
private:
const Gpu* m_gpu;
Buffer m_stagingBuffer;
size_t m_busSize;
VkCommandBuffer m_stagingCommandBuffer;
void *m_pData;
size_t m_unflushedSize;
uint32_t m_numWrites;
std::vector<std::pair<Buffer*, VkBufferCopy>> m_writes;
VkFence m_fence;
int create_staging_command_buffer();
int create_staging_buffer(size_t size);
public:
BufferBusWriter(const Gpu* gpu, size_t size);
~BufferBusWriter();
void write(Buffer* pTarget, void *pData, size_t offset, size_t size);
void flush();
void wait();
};
@@ -0,0 +1,41 @@
#pragma once
#include "resources/GpuAllocator.h"
#include "vk_mem_alloc.h"
class DefaultAllocator : public GpuAllocator {
private:
VmaAllocator m_allocator;
public:
explicit DefaultAllocator(Gpu *pGpu);
int create_image(ImageCreateInfo *pImageInfo,
MemoryAllocationInfo *pAllocInfo,
Image *pOut) override;
int create_buffer(BufferCreateInfo *pBufferInfo,
MemoryAllocationInfo *pAllocInfo,
Buffer *pOut) override;
void destroy_buffer(Buffer *pBuffer) override {
}
void destroy_image(Image *pImage) override {
}
inline void map(GpuAllocation& allocation, void **pData) override {
vmaMapMemory(m_allocator, allocation.allocation, pData);
}
inline void unmap(GpuAllocation& allocation) override {
vmaUnmapMemory(m_allocator, allocation.allocation);
}
inline VkResult flush(GpuAllocation& allocation, size_t offset, size_t size) override {
vmaFlushAllocation(m_allocator, allocation.allocation, offset, size);
return vmaInvalidateAllocation(m_allocator, allocation.allocation, offset, size);
}
};
@@ -0,0 +1,10 @@
#pragma once
#include "vk_mem_alloc.h"
/*
* Gpu memory node.
*/
struct GpuAllocation {
VmaAllocation allocation;
};
@@ -0,0 +1,57 @@
#pragma once
#include "Image.hpp"
#include "Buffer.hpp"
#include "GpuAllocation.h"
struct BufferCreateInfo {
size_t size;
VkBufferUsageFlags usage;
bool isExclusive;
};
struct ImageCreateInfo {
VkExtent2D extent;
VkFormat format;
VkImageUsageFlags usage;
VkImageAspectFlags aspectMask;
uint32_t arrayLayers;
uint32_t mipLevels;
};
enum MemoryUsage {
MEMORY_USAGE_AUTO = 0,
MEMORY_USAGE_AUTO_PREFER_DEVICE = 1,
MEMORY_USAGE_AUTO_PREFER_HOST = 2
};
struct MemoryAllocationInfo {
MemoryUsage usage;
VkMemoryPropertyFlags requiredFlags;
};
class Gpu;
/**
* Allocating of memory
*/
class GpuAllocator {
protected:
public:
virtual int create_buffer(BufferCreateInfo *pBufferInfo,
MemoryAllocationInfo *pAllocInfo, Buffer *pOut) = 0;
virtual int create_image(ImageCreateInfo *pImageInfo,
MemoryAllocationInfo *pAllocInfo, Image *pOut) = 0;
virtual void map(GpuAllocation& allocation, void **pData) = 0;
virtual void unmap(GpuAllocation& allocation) = 0;
virtual void destroy_buffer(Buffer *pBuffer) = 0;
virtual void destroy_image(Image *pImage) = 0;
virtual VkResult flush(GpuAllocation& allocation,
size_t offset, size_t size) = 0;
};
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <volk.h>
#include <memory>
#include "resources/GpuAllocation.h"
#include "ImageView.hpp"
class Gpu;
struct Image {
VkImage img;
VkImageAspectFlagBits m_aspect_mask;
uint32_t m_layer_count;
uint32_t m_level_count;
GpuAllocation allocation;
public:
Image() : Image(VK_NULL_HANDLE, {}) {
}
Image(VkImage img, GpuAllocation allocation) :
img(img), allocation(allocation) {
}
ImageView create_view(const Gpu* gpu, VkFormat format,
VkImageSubresourceRange subresource);
void set_debug_name(const Gpu* gpu, const std::string& name) const;
};
@@ -0,0 +1,54 @@
#pragma once
#include "Gpu.hpp"
class ImageBusWriter {
private:
const Gpu* m_gpu;
/**
* Image to which we are writing
*/
Image *m_pTarget;
uint32_t m_formatSize;
uint32_t m_imageSize;
/**
* Intermediate staging buffer to pass data from heap 3 to heap 0
*/
Buffer m_stagingBuffer;
size_t m_stagingBufferSize;
VkCommandBuffer m_stagingCommandBuffer;
/**
* Pointing to where the staging buffer is mapped
*/
void *m_pMappedData;
std::vector<VkBufferImageCopy> m_writes;
uint32_t m_numWrites;
VkFence m_fence;
int create_staging_buffer(size_t size);
int create_staging_command_buffer();
int create_fence();
public:
ImageBusWriter(const Gpu* gpu, Image *pImage,
VkExtent2D extent, uint32_t formatSize,
size_t maxWrites);
void write(VkBufferImageCopy region, void *pData, size_t size);
void set_target(Image *pTarget) {
flush();
m_pTarget = pTarget;
}
void flush();
};
@@ -0,0 +1,22 @@
#pragma once
#include <volk.h>
#include <string>
#include <memory>
#include <vulkan/vulkan_core.h>
class Gpu;
struct ImageView {
VkImageView view;
ImageView() :
view(VK_NULL_HANDLE) {
}
ImageView(VkImageView view) :
view(view) {
}
void set_debug_name(const std::shared_ptr<const Gpu>& gpu, const std::string& name) const;
};
@@ -0,0 +1,24 @@
#pragma once
#include <cstdint>
#include <volk.h>
#include "Gpu.hpp"
class MipmapGenerator {
private:
const Gpu* m_gpu;
VkCommandBuffer m_commandBuffer;
VkFence m_fence;
VkFence create_fence(const Gpu* gpu);
VkCommandBuffer create_command_buffer(const Gpu* gpu);
public:
explicit MipmapGenerator(const Gpu* gpu);
uint32_t generate(Image image, VkImageLayout oldLayout, VkExtent2D extent, VkImageSubresourceRange range);
};
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <signal.h>
#include <utility>
#define EXPECT(expression,msg) if(!(expression)) { throw std::runtime_error(msg); }
enum ResultCode {
RESULT_OK,
RESULT_INVALID_ARGUMENT,
RESULT_DRW_FAILURE,
RESULT_GPU_ALLOCATION_FAIL,
RESULT_GPU_RESOURCE_FAIL,
RESULT_GPU_IMAGE_MEMORY_FAIL,
RESULT_DRW_FAILED_CREATE_SHADER,
RESULT_GPU_DEVICE_CREATION_FAILED,
RESULT_GPU_COMMAND_POOL_CREATION_FAILED,
RESULT_GPU_DESCRIPTOR_POOL_CREATION_FAILED,
RESULT_NO_AVAILABLE_GPU,
};
/**
* Result. Returns either TResult or TError
*/
template<typename TResult, typename TError>
union result {
private:
bool m_isOk;
struct {
bool m_isOk;
TResult m_result;
} m_result;
struct {
bool m_isOk;
TError m_error;
} m_error;
public:
result(TResult result) :
m_result({
.m_isOk = true,
.m_result = result
}) {
}
~result() {
}
static result ok(TResult value) { return std::move(result(value)); }
static result err(TError err) { return { .m_error = err }; }
void expect(const char* msg) { raise(SIGINT); }
bool is_ok() const { return m_isOk; }
};
@@ -0,0 +1,25 @@
#pragma once
#include <vulkan/vulkan.h>
/**
* Defines blending operations for a graphics pipeline.
*/
struct BlendingInfo {
VkBlendOp colorBlendOp;
VkBlendFactor srcColorBlendFactor;
VkBlendFactor dstColorBlendFactor;
VkBlendOp alphaBlendOp;
VkBlendFactor srcAlphaBlendFactor;
VkBlendFactor dstAlphaBlendFactor;
BlendingInfo() :
colorBlendOp(VK_BLEND_OP_ADD),
srcColorBlendFactor(VK_BLEND_FACTOR_SRC_ALPHA),
dstColorBlendFactor(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA),
alphaBlendOp(VK_BLEND_OP_ADD),
srcAlphaBlendFactor(VK_BLEND_FACTOR_SRC_ALPHA),
dstAlphaBlendFactor(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA) {
}
};
@@ -0,0 +1,21 @@
#pragma once
#include "Shader.hpp"
#include "Pipeline.hpp"
#include <volk.h>
namespace lft {
class ComputePipelineBuilder {
private:
const Shader* m_shader;
VkPipelineLayout m_layout;
public:
ComputePipelineBuilder(const Shader* shader, VkPipelineLayout layout);
Pipeline build(const Gpu* gpu);
};
}
@@ -0,0 +1,8 @@
#pragma once
#include "ShaderBuilder.hpp"
class GlslShaderBuilder : public ShaderBuilder {
public:
Shader from_file(std::string path) override;
};
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include <volk.h>
#include <vector>
#include <optional>
#include "RenderContext.hpp"
#include "Shader.hpp"
#include "Gpu.hpp"
/**
* Abstracts a Vulkan pipeline
*/
class Pipeline {
VkPipeline m_pipeline;
VkPipelineLayout m_layout;
public:
/**
* Wraps new pipeline with layout
* @param layout pipeline layout
* @param pipeline pipeline
*/
Pipeline(VkPipelineLayout layout, VkPipeline pipeline) :
m_layout(layout), m_pipeline(pipeline) {
}
VkPipeline pipeline() const {
return m_pipeline;
}
VkPipelineLayout pipeline_layout() const {
return m_layout;
}
Pipeline& use(RenderContext *pRenderContext) {
vkCmdBindPipeline(pRenderContext->command_buffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS,
m_pipeline);
return *this;
}
Pipeline& bind_input_set(RenderContext *pRenderContext, uint32_t idx, uint32_t num, VkDescriptorSet *pSets) {
vkCmdBindDescriptorSets(pRenderContext->command_buffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS,
m_layout,
idx, num, pSets,
0, nullptr);
return *this;
}
inline void set_debug_name(const Gpu* gpu, const std::string name) const {
#if LOFT_DEBUG
VkDebugUtilsObjectNameInfoEXT nameInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_PIPELINE,
.objectHandle = (uint64_t)pipeline(),
.pObjectName = name.c_str(),
};
vkSetDebugUtilsObjectNameEXT(gpu->dev(), &nameInfo);
#endif
}
};
@@ -0,0 +1,147 @@
#pragma once
#include "Gpu.hpp"
#include "Shader.hpp"
#include "VertexBinding.h"
#include "VertexAttribute.h"
#include "BlendingInfo.h"
#include "Pipeline.hpp"
#include <volk.h>
#include <stdexcept>
#include <algorithm>
#include <utility>
class PipelineLayoutBuilder {
private:
std::vector<VkDescriptorSetLayout> m_layouts;
std::vector<VkPushConstantRange> m_pushConstantRanges;
uint32_t m_numLayouts;
public:
PipelineLayoutBuilder() :
m_layouts(4), m_pushConstantRanges(), m_numLayouts(0) {
}
PipelineLayoutBuilder& input_set(uint32_t idx, VkDescriptorSetLayout setLayout) {
if(idx >= m_layouts.size()) {
throw std::runtime_error("As of now, only 4 descriptor set layouts are supported");
}
m_layouts[idx] = setLayout;
m_numLayouts = std::max(m_numLayouts, idx + 1);
return *this;
}
PipelineLayoutBuilder& push_constant_range(uint32_t offset, uint32_t size, VkShaderStageFlags stages) {
m_pushConstantRanges.push_back({
.stageFlags = stages,
.offset = offset,
.size = size,
});
return *this;
}
VkPipelineLayout build(const Gpu* gpu) {
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = m_numLayouts,
.pSetLayouts = m_layouts.data(),
.pushConstantRangeCount = (uint32_t)m_pushConstantRanges.size(),
.pPushConstantRanges = m_pushConstantRanges.data(),
};
VkPipelineLayout inputLayout = VK_NULL_HANDLE;
if (vkCreatePipelineLayout(gpu->dev(), &pipelineLayoutInfo, nullptr, &inputLayout)) {
throw std::runtime_error("Failed to build pipeline layout");
}
return inputLayout;
}
};
class PipelineBuilder {
private:
const Gpu* m_gpu;
VkPipelineLayout m_layout;
VkRenderPass m_renderpass;
VkViewport m_viewport;
VkRect2D m_scissor;
VkPipelineRasterizationStateCreateInfo m_rasterInfo;
VkPipelineInputAssemblyStateCreateInfo m_inputAssemblyInfo;
VkPipelineDepthStencilStateCreateInfo m_depthStencilInfo;
VkPipelineVertexInputStateCreateInfo m_vertexInputInfo;
std::vector<VkPipelineColorBlendAttachmentState> m_blendingInfo;
std::vector<VkPipelineShaderStageCreateInfo> stages;
std::vector<VertexBinding> m_vertexBindings;
std::vector<VertexAttribute> m_vertexAttributes;
public:
inline size_t num_attachments() { return m_blendingInfo.size(); }
PipelineBuilder(const Gpu* gpu, const VkViewport& viewport,
VkPipelineLayout layout, VkRenderPass outputLayout,
uint32_t numAttachments,
const Shader* vertexShader, const Shader* fragmentShader);
inline PipelineBuilder& set_vertex_input_info(std::vector<VertexBinding> bindings,
std::vector<VertexAttribute> attributes) {
m_vertexBindings = std::move(bindings);
m_vertexAttributes = std::move(attributes);
return *this;
}
/* Rasterization */
inline PipelineBuilder& polygon_mode(VkPolygonMode mode) {
this->m_rasterInfo.polygonMode = mode;
return *this;
}
inline PipelineBuilder& cull_mode(VkCullModeFlags flags) {
this->m_rasterInfo.cullMode = flags;
return *this;
}
inline PipelineBuilder& topology(VkPrimitiveTopology topology) {
this->m_inputAssemblyInfo.topology = topology;
return *this;
}
inline PipelineBuilder& set_depth_bias(float depthBiasConstantFactor, float depthBiasClamp,
float depthBiasSlopeFactor) {
m_rasterInfo.depthBiasEnable = true,
m_rasterInfo.depthBiasConstantFactor = depthBiasConstantFactor;
m_rasterInfo.depthBiasClamp = depthBiasClamp;
m_rasterInfo.depthBiasSlopeFactor = depthBiasSlopeFactor;
return *this;
}
inline PipelineBuilder& unset_blending(uint32_t attachmentIdx) {
m_blendingInfo[attachmentIdx].blendEnable = false;
return *this;
}
inline PipelineBuilder& set_blending(uint32_t attachmentIdx, BlendingInfo blendingInfo) {
m_blendingInfo[attachmentIdx] = {
.blendEnable = true,
.srcColorBlendFactor = blendingInfo.srcColorBlendFactor,
.dstColorBlendFactor = blendingInfo.dstColorBlendFactor,
.colorBlendOp = blendingInfo.colorBlendOp,
.srcAlphaBlendFactor = blendingInfo.srcAlphaBlendFactor,
.dstAlphaBlendFactor = blendingInfo.dstAlphaBlendFactor,
.alphaBlendOp = blendingInfo.alphaBlendOp,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT
};
return *this;
}
Pipeline build();
};
@@ -0,0 +1,59 @@
#pragma once
#include <vector>
#include <stdexcept>
#include <volk.h>
#include "Gpu.hpp"
namespace lft {
class PipelineLayoutBuilder {
private:
std::vector<VkDescriptorSetLayout> m_layouts;
std::vector<VkPushConstantRange> m_pushConstantRanges;
uint32_t m_numLayouts;
public:
PipelineLayoutBuilder() :
m_layouts(4), m_pushConstantRanges(), m_numLayouts(0) {
}
PipelineLayoutBuilder& input_set(uint32_t idx, VkDescriptorSetLayout setLayout) {
if(idx >= m_layouts.size()) {
throw std::runtime_error("As of now, only 4 descriptor set layouts are supported");
}
m_layouts[idx] = setLayout;
m_numLayouts = std::max(m_numLayouts, idx + 1);
return *this;
}
PipelineLayoutBuilder& push_constant_range(uint32_t offset, uint32_t size, VkShaderStageFlags stages) {
m_pushConstantRanges.push_back({
.stageFlags = stages,
.offset = offset,
.size = size,
});
return *this;
}
VkPipelineLayout build(const Gpu* gpu) {
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = m_numLayouts,
.pSetLayouts = m_layouts.data(),
.pushConstantRangeCount = (uint32_t)m_pushConstantRanges.size(),
.pPushConstantRanges = m_pushConstantRanges.data(),
};
VkPipelineLayout inputLayout = VK_NULL_HANDLE;
if (vkCreatePipelineLayout(gpu->dev(), &pipelineLayoutInfo, nullptr, &inputLayout)) {
throw std::runtime_error("Failed to build pipeline layout");
}
return inputLayout;
}
};
}
@@ -0,0 +1,72 @@
#pragma once
#include <volk.h>
#include <memory>
#include <vulkan/vulkan_core.h>
#include "Gpu.hpp"
namespace lft {
class SamplerBuilder {
VkSamplerCreateInfo m_sampler_info = {};
public:
SamplerBuilder() {
m_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
}
inline SamplerBuilder& mag_filter(VkFilter filter) {
m_sampler_info.magFilter = filter;
return *this;
}
inline SamplerBuilder& min_filter(VkFilter filter) {
m_sampler_info.minFilter = filter;
return *this;
}
inline SamplerBuilder& filter(VkFilter filter) {
mag_filter(filter);
min_filter(filter);
return *this;
}
inline SamplerBuilder& address_mode_u(VkSamplerAddressMode address_mode) {
m_sampler_info.addressModeU = address_mode;
return *this;
}
inline SamplerBuilder& address_mode_v(VkSamplerAddressMode address_mode) {
m_sampler_info.addressModeV = address_mode;
return *this;
}
inline SamplerBuilder& address_mode_w(VkSamplerAddressMode address_mode) {
m_sampler_info.addressModeW = address_mode;
return *this;
}
inline SamplerBuilder& address_mode(VkSamplerAddressMode address_mode) {
address_mode_u(address_mode);
address_mode_v(address_mode);
address_mode_w(address_mode);
return *this;
}
inline SamplerBuilder& border_color(VkBorderColor border_color) {
m_sampler_info.borderColor = border_color;
return *this;
}
VkSampler build(std::shared_ptr<Gpu> gpu) {
VkSampler sampler = VK_NULL_HANDLE;
if(vkCreateSampler(gpu->dev(), &m_sampler_info, nullptr, &sampler)) {
throw std::runtime_error("failed to create sampler");
}
return sampler;
}
};
}
+49
View File
@@ -0,0 +1,49 @@
//
// Created by martin on 10/24/23.
//
#ifndef LOFT_SHADER_HPP
#define LOFT_SHADER_HPP
#include "Gpu.hpp"
#include "io/ShaderBinary.h"
#include <volk.h>
#include <utility>
struct Shader {
private:
VkShaderModule m_module;
public:
Shader(Shader&& s) :
m_module(s.m_module) {
}
Shader(VkShaderModule m) :
m_module(m) {
}
const inline Shader& set_name(const Gpu* gpu, const std::string &name) {
#if LOFT_DEBUG
VkDebugUtilsObjectNameInfoEXT nameInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_SHADER_MODULE,
.objectHandle = (uint64_t) m_module,
.pObjectName = name.c_str(),
};
vkSetDebugUtilsObjectNameEXT(gpu->dev(), &nameInfo);
#endif
return *this;
}
[[nodiscard]] inline VkShaderModule module() const {
return m_module;
}
};
#endif //LOFT_SHADER_HPP
@@ -0,0 +1,17 @@
//
// Created by martin on 10/24/23.
//
#ifndef LOFT_SHADERBUILDER_HPP
#define LOFT_SHADERBUILDER_HPP
#include <string>
#include "Shader.hpp"
class ShaderBuilder {
public:
virtual Shader from_file(std::string path) = 0;
};
#endif //LOFT_SHADERBUILDER_HPP
@@ -0,0 +1,279 @@
#pragma once
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include <volk.h>
#include <memory>
#include <exception>
#include <assert.h>
#include <stdexcept>
#include <print>
#include "resources/Buffer.hpp"
#include "resources/Image.hpp"
#include "Gpu.hpp"
/**
* ShaderInputSetBufferWrite
* For having uniform buffer
*/
struct ShaderInputSetBufferWrite {
private:
VkDescriptorType m_type;
uint32_t m_binding;
VkDescriptorBufferInfo m_bufferInfo;
public:
/**
* Creates new shader input set write for Buffer
*/
ShaderInputSetBufferWrite(VkDescriptorType type, uint32_t binding, const Buffer& buffer,
uint32_t offset, uint32_t size) :
m_bufferInfo(
{
.buffer = buffer.buf,
.offset = offset,
.range = size
}
),
m_binding(binding),
m_type(type) {
}
const VkDescriptorBufferInfo* info() {
return &m_bufferInfo;
}
};
struct ShaderInputSetImageWrite {
private:
VkDescriptorType m_type;
uint32_t m_binding;
VkDescriptorImageInfo m_imageInfo;
public:
ShaderInputSetImageWrite(uint32_t binding, const ImageView& view, VkSampler sampler) :
m_imageInfo(
{
.sampler = sampler,
.imageView = view.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
}
),
m_binding(binding),
m_type(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
}
const VkDescriptorImageInfo* info() {
return &m_imageInfo;
}
};
union ShaderInputSetWrite {
private:
struct {
VkDescriptorType type;
uint32_t binding;
} common;
ShaderInputSetBufferWrite buffer;
ShaderInputSetImageWrite image;
public:
ShaderInputSetWrite() :
common() {
}
explicit ShaderInputSetWrite(ShaderInputSetBufferWrite bufferWrite) :
buffer(bufferWrite) {
}
explicit ShaderInputSetWrite(ShaderInputSetImageWrite imageWrite) :
image(imageWrite) {
}
uint32_t is_buffer_write(VkDescriptorType type) {
return type > VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
}
VkWriteDescriptorSet to_write(VkDescriptorSet set) {
return {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = set,
.dstBinding = common.binding,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = common.type,
.pImageInfo = is_buffer_write(common.type) ? nullptr : image.info(),
.pBufferInfo = is_buffer_write(common.type) ? buffer.info() : nullptr,
};
}
};
/**
* ShaderInputSetBuilder
*
* Builds descriptor sets
*/
struct ShaderInputSetBuilder {
private:
std::vector<ShaderInputSetWrite> m_writes;
public:
explicit ShaderInputSetBuilder() :
m_writes() {
}
ShaderInputSetBuilder& image(uint32_t binding, const ImageView& imageView, VkSampler sampler) {
assert(imageView.view != VK_NULL_HANDLE);
assert(sampler != VK_NULL_HANDLE);
m_writes.push_back(ShaderInputSetWrite(ShaderInputSetImageWrite(binding, imageView, sampler)));
return *this;
}
ShaderInputSetBuilder& buffer(const VkDescriptorType type, const uint32_t binding, const Buffer& buffer, const uint32_t offset, const uint32_t size) {
assert(buffer.buf != VK_NULL_HANDLE);
m_writes.push_back(ShaderInputSetWrite(ShaderInputSetBufferWrite(type, binding, buffer, offset, size)));
return *this;
}
/**
* Allocates new descriptor sets from layout.
* All the writes must be set already.
* @param pGpu Gpu on which to perform
* @param layout Descriptor set layout
* @return Allocated descriptor set with all the writes.
*/
VkDescriptorSet build(const Gpu* gpu, VkDescriptorSetLayout layout) {
VkDescriptorSetAllocateInfo descriptorInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = gpu->descriptor_pool(),
.descriptorSetCount = 1,
.pSetLayouts = &layout
};
VkDescriptorSet set = VK_NULL_HANDLE;
if(vkAllocateDescriptorSets(gpu->dev(), &descriptorInfo, &set)) {
throw std::runtime_error("Failed to allocate descriptor set");
}
std::vector<VkWriteDescriptorSet> writes(m_writes.size());
for(uint32_t i = 0; i < m_writes.size(); i++) {
writes[i] = m_writes[i].to_write(set);
}
vkUpdateDescriptorSets(gpu->dev(), writes.size(), writes.data(),
0, nullptr);
return set;
}
};
struct ShaderInputSet {
private:
VkDescriptorSet m_descriptorSet;
public:
inline const VkDescriptorSet descriptor_set() const {
return m_descriptorSet;
}
ShaderInputSet(VkDescriptorSet descriptorSet) :
m_descriptorSet(descriptorSet) {
}
ShaderInputSet(const Gpu* gpu, VkDescriptorSetLayout layout) :
m_descriptorSet(VK_NULL_HANDLE) {
VkDescriptorSetAllocateInfo descriptorInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = gpu->descriptor_pool(),
.descriptorSetCount = 1,
.pSetLayouts = &layout
};
if(vkAllocateDescriptorSets(gpu->dev(), &descriptorInfo, &m_descriptorSet)) {
throw std::runtime_error("Failed to allocate descriptor set");
}
}
};
enum ShaderInputWriteType {
};
class ShaderInputSetWriter {
private:
const Gpu* m_gpu;
std::vector<VkWriteDescriptorSet> m_writes;
std::vector<std::vector<VkDescriptorImageInfo>> m_imageWrites;
std::vector<std::vector<VkDescriptorBufferInfo>> m_bufferWrites;
public:
explicit ShaderInputSetWriter(const Gpu* gpu) :
m_gpu(gpu) {
}
ShaderInputSetWriter& write_images(const ShaderInputSet& dstInputSet,
const uint32_t dstBinding,
const uint32_t dstArrayElement,
const VkDescriptorType type,
const std::vector<VkDescriptorImageInfo>& writes) {
/* copy write data, to not lose it */
m_imageWrites.push_back(writes);
m_writes.push_back({
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = dstInputSet.descriptor_set(),
.dstBinding = dstBinding,
.dstArrayElement = dstArrayElement,
.descriptorCount = (uint32_t)writes.size(),
.descriptorType = type,
.pImageInfo = m_imageWrites[m_imageWrites.size() - 1].data()
});
return *this;
}
ShaderInputSetWriter& write_buffer(const ShaderInputSet& dstInputSet,
const uint32_t dstBinding,
const uint32_t dstArrayElement,
const VkDescriptorType type,
const std::vector<VkDescriptorBufferInfo>& writes) {
/* copy write data, to not lose it */
m_bufferWrites.push_back(writes);
m_writes.push_back({
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = dstInputSet.descriptor_set(),
.dstBinding = dstBinding,
.dstArrayElement = dstArrayElement,
.descriptorCount = (uint32_t)writes.size(),
.descriptorType = type,
.pBufferInfo = m_bufferWrites[m_bufferWrites.size() - 1].data()
});
return *this;
}
ShaderInputSetWriter& write() {
vkUpdateDescriptorSets(m_gpu->dev(), m_writes.size(), m_writes.data(), 0, nullptr);
return *this;
}
};
@@ -0,0 +1,98 @@
#pragma once
#include "Gpu.hpp"
#include <vector>
#include <volk.h>
#include <assert.h>
#include <stdexcept>
class ShaderInputSetLayoutBuilder {
private:
std::vector<VkDescriptorSetLayoutBinding> m_bindings;
public:
ShaderInputSetLayoutBuilder() :
m_bindings() {
}
ShaderInputSetLayoutBuilder(std::vector<VkDescriptorSetLayoutBinding> bindings) :
m_bindings(bindings) {
}
ShaderInputSetLayoutBuilder& uniform_buffer(
uint32_t binding,
VkShaderStageFlags shader_stages = VK_SHADER_STAGE_ALL
) {
m_bindings.push_back({
.binding = binding,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = shader_stages,
});
return *this;
}
ShaderInputSetLayoutBuilder& image(
uint32_t binding,
VkShaderStageFlags shader_stages = VK_SHADER_STAGE_ALL
) {
m_bindings.push_back({
.binding = binding,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = shader_stages,
});
return *this;
}
ShaderInputSetLayoutBuilder& n_images(
uint32_t binding,
uint32_t count,
VkShaderStageFlags shader_stages = VK_SHADER_STAGE_ALL
) {
m_bindings.push_back({
.binding = binding,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = count,
.stageFlags = shader_stages,
});
return *this;
}
ShaderInputSetLayoutBuilder& binding(
uint32_t binding,
VkDescriptorType type,
uint32_t count,
VkShaderStageFlags stages
) {
m_bindings.push_back({
.binding = binding,
.descriptorType = type,
.descriptorCount = count,
.stageFlags = stages,
});
return *this;
}
VkDescriptorSetLayout build(const Gpu* gpu) const {
VkDescriptorSetLayoutCreateInfo layoutInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = (uint32_t)m_bindings.size(),
.pBindings = m_bindings.data(),
};
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
if(vkCreateDescriptorSetLayout(gpu->dev(), &layoutInfo, nullptr, &layout)) {
throw std::runtime_error("Failed to create descriptor set layout");
}
return layout;
}
};
@@ -0,0 +1,23 @@
//
// Created by martin on 10/24/23.
//
#ifndef LOFT_SPIRVSHADERBUILDER_HPP
#define LOFT_SPIRVSHADERBUILDER_HPP
#include "ShaderBuilder.hpp"
#include "Gpu.hpp"
class SpirvShaderBuilder : public ShaderBuilder {
private:
const Gpu* m_gpu;
public:
SpirvShaderBuilder(const Gpu* gpu);
Shader from_binary(const std::vector<uint32_t>& code) const;
Shader from_file(std::string path) override;
};
#endif //LOFT_SPIRVSHADERBUILDER_HPP
@@ -0,0 +1,15 @@
#pragma once
#include <cstdint>
#include <volk.h>
struct VertexAttribute {
uint32_t location;
uint32_t binding;
VkFormat format;
uint32_t offset;
VkVertexInputAttributeDescription get_vk() {
return *(VkVertexInputAttributeDescription*)this;
}
};
@@ -0,0 +1,13 @@
#pragma once
#include <volk.h>
struct VertexBinding {
uint32_t binding;
uint32_t stride;
VkVertexInputRate inputRate;
VkVertexInputBindingDescription get_vk() {
return *(VkVertexInputBindingDescription*)this;
}
};