start work on shaders and compiling. move compilation to general function, support include directories.

This commit is contained in:
Nepgfurmixpro 2025-05-31 00:27:30 -04:00
parent 8aa57b4187
commit 6fe98cdb5b
14 changed files with 488 additions and 14 deletions

View File

@ -8,6 +8,12 @@ file(GLOB_RECURSE sourceFiles "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_C
find_package(Vulkan REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(VulkanUtilityLibraries CONFIG REQUIRED)
find_package(spirv_cross_core CONFIG REQUIRED)
find_package(spirv_cross_glsl CONFIG REQUIRED)
find_package(spirv_cross_hlsl CONFIG REQUIRED)
find_package(spirv_cross_msl CONFIG REQUIRED)
find_package(spirv_cross_reflect CONFIG REQUIRED)
find_package(spirv_cross_util CONFIG REQUIRED)
foreach (file ${sourceFiles})
message("Source File Found ${file}")
@ -20,3 +26,4 @@ target_include_directories(Dyn PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/inc")
target_link_libraries(Dyn PRIVATE Vulkan::Vulkan)
target_link_libraries(Dyn PRIVATE glfw)
target_link_libraries(Dyn PRIVATE Vulkan::SafeStruct Vulkan::LayerSettings Vulkan::UtilityHeaders Vulkan::CompilerConfiguration)
target_link_libraries(Dyn PRIVATE spirv-cross-core spirv-cross-glsl spirv-cross-hlsl spirv-cross-msl spirv-cross-reflect spirv-cross-util)

37
inc/dyn/core/file.h Normal file
View File

@ -0,0 +1,37 @@
//
// Created by nepge on 5/22/2025.
//
#pragma once
#include <string>
#include <vector>
#include <fstream>
#include <filesystem>
namespace dyn::core {
class file {
public:
file(const std::string& path);
~file() = default;
[[nodiscard]] std::string text() const { return _contents; }
[[nodiscard]] std::vector<uint8_t> bytes() const { return std::vector<uint8_t>(_contents.begin(), _contents.end()); }
[[nodiscard]] static bool exists(const std::string& path);
void write(const std::string& text);
void write(const std::vector<uint8_t>& bytes);
void append(const std::string& text);
void append(const std::vector<uint8_t>& bytes);
void close();
private:
std::fstream _file;
std::string _file_path;
std::string _contents;
};
}

View File

@ -42,5 +42,6 @@ namespace dyn::core {
GLFWwindow* _window;
friend class vulkan_context;
friend class vulkan_swapchain;
};
}

View File

@ -9,6 +9,7 @@
#include <dyn/renderer/render_context.h>
#include "vulkan_devices.h"
#include "vulkan_swapchain.h"
namespace dyn::core {
@ -29,19 +30,23 @@ namespace dyn::core {
[[nodiscard]] VkInstance get_instance() const { return _instance; }
VkSurfaceKHR& get_surface();
VkSurfaceKHR get_surface() const;
[[nodiscard]] static bool check_api_version_support(uint32_t api_version);
[[nodiscard]] static ref<vulkan_device> get_last_selected_logical_device() { return s_current_device; }
void single_cmd(std::function<void(VkCommandBuffer)> cmdFunc);
[[nodiscard]] ref<vulkan_device> get_current_device() { return _device; }
void single_cmd(const std::function<void(VkCommandBuffer)>& cmd_func);
private:
VkInstance _instance = nullptr;
ref<vulkan_physical_device> _physical_device;
ref<vulkan_device> _device;
ref<vulkan_swapchain> _swap_chain;
VkCommandPool _command_pool = nullptr;
VkDebugUtilsMessengerEXT _debug_messenger = nullptr;

View File

@ -55,10 +55,13 @@ namespace dyn::core {
[[nodiscard]] VkQueue get_graphics_queue() const { return _graphics_queue; }
[[nodiscard]] VkQueue get_present_queue() const { return _present_queue; }
[[nodiscard]] ref<vulkan_physical_device> get_physical_device() const { return _physical_device; };
static ref<vulkan_device> create(const std::shared_ptr<vulkan_physical_device>& device, const VkPhysicalDeviceFeatures& device_features);
private:
VkDevice _device = nullptr;
ref<vulkan_physical_device> _physical_device;
VkQueue _graphics_queue = VK_NULL_HANDLE;
VkQueue _present_queue = VK_NULL_HANDLE;

View File

@ -0,0 +1,32 @@
//
// Created by nepge on 5/22/2025.
//
#pragma once
#include <vulkan/vulkan.h>
#include "dyn/renderer/shader.h"
#include <dyn/core/file.h>
#include "vulkan_context.h"
namespace dyn::core {
class vulkan_shader : public shader {
public:
vulkan_shader(const std::string& source, const std::string& filename, shader_type type, const ref<vulkan_context>& ctx);
~vulkan_shader();
[[nodiscard]] VkShaderModule get_handle() const { return _shader; }
[[nodiscard]] VkPipelineShaderStageCreateInfo get_stage_info() const { return _pipeline_ci; }
[[nodiscard]] bool is_usable() const override { return _usable; }
private:
std::vector<uint32_t> _binary;
ref<vulkan_context> _ctx;
bool _usable = false;
VkShaderModule _shader = VK_NULL_HANDLE;
VkPipelineShaderStageCreateInfo _pipeline_ci = {};
};
}

View File

@ -4,25 +4,45 @@
#pragma once
#include <cstdint>
#include <limits>
#include <algorithm>
#include <dyn/platform/vulkan/vulkan_swapchain.h>
#include "dyn/renderer/swapchain.h"
namespace dyn::core {
struct swap_chain_support_details {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> present_modes;
};
class vulkan_swapchain : public swapchain {
public:
explicit vulkan_swapchain(const ref<vulkan_context>& ctx);
~vulkan_swapchain() override;
void prepare() override;
void present() override;
void prepare() override {}
void present() override {}
void resize(uint32_t width, uint32_t height) override;
void resize(uint32_t width, uint32_t height) override {}
[[nodiscard]] VkCommandBuffer current_cmd_buffer() const { return _command_buffers[_current_frame]; }
static ref<vulkan_swapchain> create(const ref<vulkan_context>& ctx);
[[nodiscard]] static swap_chain_support_details get_support_details(VkSurfaceKHR surface, VkPhysicalDevice physical_device);
[[nodiscard]] static VkSurfaceFormatKHR get_format(const std::vector<VkSurfaceFormatKHR>& formats);
[[nodiscard]] static VkPresentModeKHR get_present_mode(const std::vector<VkPresentModeKHR>& modes);
[[nodiscard]] static VkExtent2D get_extent(ref<vulkan_context> ctx, const VkSurfaceCapabilitiesKHR& capabilities);
protected:
void rebuild();
void build_swap_images();
void rebuild() {}
private:
struct swap_image {
@ -30,11 +50,15 @@ namespace dyn::core {
VkImageView view;
};
std::vector<swap_image> _swap_images;
VkFormat _image_format;
VkExtent2D _extent;
std::vector<VkCommandBuffer> _command_buffers;
uint32_t _current_frame = 0;
VkSwapchainKHR _swapchain;
VkSwapchainKHR _swap_chain;
ref<vulkan_context> _ctx;
friend class vulkan_context;
};
}

43
inc/dyn/renderer/shader.h Normal file
View File

@ -0,0 +1,43 @@
//
// Created by nepge on 5/21/2025.
//
#pragma once
#include <string>
#include "dyn/core/base.h"
#include <shaderc/shaderc.hpp>
namespace dyn::core {
class render_context;
class shader {
public:
enum shader_type {
SHADER_VERTEX,
SHADER_FRAGMENT,
SHADER_GEOMETRY,
SHADER_COMPUTE
};
[[nodiscard]] shaderc_shader_kind convert_shader_type(shader_type type) const noexcept {
switch (type) {
case SHADER_VERTEX:
return shaderc_vertex_shader;
case SHADER_FRAGMENT:
return shaderc_fragment_shader;
case SHADER_GEOMETRY:
return shaderc_geometry_shader;
case SHADER_COMPUTE:
return shaderc_compute_shader;
}
return shaderc_vertex_shader;
}
[[nodiscard]] virtual bool is_usable() const = 0;
static ref<shader> create_from_string(const std::string& code, shader_type type, const ref<render_context>& context);
static ref<shader> create_from_file(const std::string& filename, shader_type type, const ref<render_context>& context);
};
}

52
src/core/file.cpp Normal file
View File

@ -0,0 +1,52 @@
//
// Created by nepge on 5/22/2025.
//
#include <ios>
#include <dyn/core/file.h>
namespace dyn::core {
file::file(const std::string &path) : _file(path, std::ios::in | std::ios::binary | std::ios::out), _file_path(path) {
_file >> _contents;
}
void file::write(const std::string &text) {
_file << text;
_contents.resize(text.size());
_contents.assign(text.begin(), text.end());
}
void file::write(const std::vector<uint8_t> &bytes) {
_file << std::string(bytes.begin(), bytes.end());
_contents.resize(bytes.size());
_contents.assign(bytes.begin(), bytes.end());
}
void file::append(const std::string &text) {
std::string out = _contents;
out += text;
_file << out;
_contents.resize(out.size());
_contents.assign(out.begin(), out.end());
}
void file::append(const std::vector<uint8_t> &bytes) {
std::string out = _contents;
out += std::string(bytes.begin(), bytes.end());
_file << out;
_contents.resize(out.size());
_contents.assign(out.begin(), out.end());
}
void file::close() {
_file.close();
}
bool file::exists(const std::string &path) {
return std::filesystem::exists(path);
}
}

View File

@ -104,6 +104,10 @@ namespace dyn::core {
s_current_device = _device;
}
{
_swap_chain = vulkan_swapchain::create(self);
}
// command pool
{
VkCommandPoolCreateInfo create_info{};
@ -124,7 +128,7 @@ namespace dyn::core {
void vulkan_context::cmd_end() {
}
VkSurfaceKHR& vulkan_context::get_surface() {
VkSurfaceKHR vulkan_context::get_surface() const {
return _surface;
}
@ -139,7 +143,7 @@ namespace dyn::core {
return true;
}
void vulkan_context::single_cmd(std::function<void(VkCommandBuffer)> cmdFunc) {
void vulkan_context::single_cmd(const std::function<void(VkCommandBuffer)>& cmd_func) {
VkCommandBufferAllocateInfo cmd_allocate_info{};
cmd_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd_allocate_info.commandPool = _command_pool;
@ -155,7 +159,7 @@ namespace dyn::core {
vkBeginCommandBuffer(cmd_buffer, &begin_info);
cmdFunc(cmd_buffer);
cmd_func(cmd_buffer);
vkEndCommandBuffer(cmd_buffer);

View File

@ -8,6 +8,7 @@
#include "dyn/platform/vulkan/vulkan.h"
#include "dyn/platform/vulkan/vulkan_context.h"
#include "dyn/platform/vulkan/vulkan_devices.h"
#include "dyn/platform/vulkan/vulkan_swapchain.h"
namespace dyn::core {
vulkan_physical_device::vulkan_physical_device(const ref<vulkan_context>& ctx) :
@ -73,8 +74,18 @@ namespace dyn::core {
const VkSurfaceKHR &surface) {
uint32_t prev_score = 0;
VkPhysicalDevice current_device = VK_NULL_HANDLE;
for (const auto& device : available_devices) {
if (queue_families::find(device, surface).is_complete() && check_extension_support(device)) {
auto is_suitable = [device, surface] {
bool good_swap_chain = false;
if (check_extension_support(device)) {
swap_chain_support_details details = vulkan_swapchain::get_support_details(surface, device);
good_swap_chain = !details.formats.empty() && !details.present_modes.empty();
DYN_DEBUG_LOG("is swap chain good?", good_swap_chain ? "yes!" : "no!");
}
return queue_families::find(device, surface).is_complete() && good_swap_chain;
};
if (is_suitable()) {
auto score = score_device(device);
if (prev_score < score) {
prev_score = score;
@ -123,7 +134,7 @@ namespace dyn::core {
auto device_extensions = utils::vk_device_extensions;
std::set<std::string> exts{device_extensions.begin(), device_extensions.end()};
for (const auto &required_extension : exts) {
for (const auto &required_extension : device_extensions) {
exts.erase(required_extension);
}
@ -134,7 +145,7 @@ namespace dyn::core {
return create_ref<vulkan_physical_device>(ctx);
}
vulkan_device::vulkan_device(const ref<vulkan_physical_device> &physical_device, VkPhysicalDeviceFeatures device_Features) {
vulkan_device::vulkan_device(const ref<vulkan_physical_device> &physical_device, VkPhysicalDeviceFeatures device_Features) : _physical_device(physical_device) {
vulkan_physical_device::queue_families families = physical_device->get_queue_families();
std::set queue_family_indices = {families.graphics_family.value(), families.present_family.value()};

View File

@ -0,0 +1,51 @@
//
// Created by nepge on 5/22/2025.
//
#include <dyn/platform/vulkan/vulkan_shader.h>
#include "dyn/core/debug.h"
#include "dyn/platform/vulkan/vulkan.h"
namespace dyn::core {
vulkan_shader::vulkan_shader(const std::string &source, const std::string& filename, shader_type type, const ref<vulkan_context>& ctx) : _ctx(ctx) {
shaderc::Compiler compiler;
auto compile_result = compiler.CompileGlslToSpv(
source.data(), source.length(),
shader::convert_shader_type(type),
filename.data());
auto result = compile_result.GetCompilationStatus();
if (result != shaderc_compilation_status_success) {
DYN_ERROR_LOG("Failed to compile shader: {}", filename);
DYN_ERROR_LOG("Shader Errors: {}", compile_result.GetNumErrors());
DYN_ERROR_LOG("Shader Warnings: {}", compile_result.GetNumWarnings());
DYN_ERROR_LOG("Shader Compilation Error Message:");
DYN_ERROR_LOG(compile_result.GetErrorMessage());
return;
}
_binary = std::vector(compile_result.begin(), compile_result.end());
VkShaderModuleCreateInfo module_create_info = {};
module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
module_create_info.pNext = nullptr;
module_create_info.codeSize = _binary.size();
module_create_info.pCode = _binary.data();
auto vk_res = vkCreateShaderModule(ctx->get_current_device()->get_handle(), &module_create_info, nullptr, &_shader);
if (vk_res != VK_SUCCESS) {
DYN_ERROR_LOG("Failed to create shader module: {}", string_VkResult(vk_res));
return;
}
_usable = true;
}
vulkan_shader::~vulkan_shader() {
if (_shader != VK_NULL_HANDLE) {
vkDestroyShaderModule(_ctx->get_current_device()->get_handle(), _shader, nullptr);
}
}
}

View File

@ -0,0 +1,195 @@
//
// Created by nepge on 5/14/2025.
//
#include <dyn/platform/vulkan/vulkan_swapchain.h>
#include "dyn/platform/vulkan/vulkan.h"
#include "dyn/platform/vulkan/vulkan_context.h"
namespace dyn::core {
vulkan_swapchain::vulkan_swapchain(const ref<vulkan_context> &ctx) : _ctx(ctx) {
auto phy_device = _ctx->get_current_device()->get_physical_device();
auto logical_device = _ctx->get_current_device();
auto support_details = vulkan_swapchain::get_support_details(
_ctx->get_surface(),
phy_device->get_handle());
auto surface_format = get_format(support_details.formats);
auto present_mode = get_present_mode(support_details.present_modes);
auto extent = get_extent(_ctx, support_details.capabilities);
uint32_t image_count = support_details.capabilities.minImageCount + 2;
if (support_details.capabilities.maxImageCount > 0 && image_count > support_details.capabilities.maxImageCount) {
image_count = support_details.capabilities.maxImageCount;
}
_swap_images.resize(image_count);
VkSwapchainCreateInfoKHR create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info.surface = ctx->get_surface();
create_info.minImageCount = image_count;
create_info.imageFormat = surface_format.format;
create_info.imageColorSpace = surface_format.colorSpace;
create_info.imageExtent = extent;
create_info.presentMode = present_mode;
create_info.imageArrayLayers = 1;
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
auto indices = phy_device->get_queue_families();
uint32_t family_indices[] = {indices.graphics_family.value(), indices.present_family.value()};
if (indices.graphics_family.value() != indices.present_family.value()) {
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info.queueFamilyIndexCount = 2;
create_info.pQueueFamilyIndices = family_indices;
} else {
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info.queueFamilyIndexCount = 0;
create_info.pQueueFamilyIndices = nullptr;
}
create_info.preTransform = support_details.capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = present_mode;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE;
_image_format = surface_format.format;
_extent = extent;
dyn_vk_verify(vkCreateSwapchainKHR(logical_device->get_handle(), &create_info, nullptr, &_swap_chain));
// create swap chain images
build_swap_images();
}
vulkan_swapchain::~vulkan_swapchain() {
auto dev_handle = _ctx->get_current_device()->get_handle();
vkDestroySwapchainKHR(dev_handle, _swap_chain, nullptr);
for (auto img : _swap_images) {
vkDestroyImageView(dev_handle, img.view, nullptr);
}
}
swap_chain_support_details vulkan_swapchain::get_support_details(VkSurfaceKHR surface, VkPhysicalDevice physical_device) {
swap_chain_support_details details{};
dyn_vk_verify(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
physical_device,
surface,
&details.capabilities
));
uint32_t format_count;
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, nullptr);
if (format_count != 0) {
details.formats.resize(format_count);
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, details.formats.data());
}
uint32_t present_mode_count;
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, nullptr);
if (present_mode_count != 0) {
details.present_modes.resize(present_mode_count);
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, details.present_modes.data());
}
return details;
}
VkSurfaceFormatKHR vulkan_swapchain::get_format(const std::vector<VkSurfaceFormatKHR> &formats) {
for (const auto &format : formats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return format;
}
}
return formats[0];
}
VkPresentModeKHR vulkan_swapchain::get_present_mode(const std::vector<VkPresentModeKHR> &modes) {
for (const auto &mode : modes) {
// mailbox khr is preferred.
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
return mode;
}
}
// fifo khr default
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D vulkan_swapchain::get_extent(ref<vulkan_context> ctx, const VkSurfaceCapabilitiesKHR &capabilities) {
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
return capabilities.currentExtent;
} else {
int32_t width, height;
glfwGetFramebufferSize(
ctx->get_window()->get_window_handle(),
&width, &height);
VkExtent2D actual_extent = {
static_cast<uint32_t>(width),
static_cast<uint32_t>(height),
};
actual_extent.width = std::clamp(
actual_extent.width,
capabilities.minImageExtent.width,
capabilities.maxImageExtent.width);
actual_extent.height = std::clamp(
actual_extent.height,
capabilities.minImageExtent.height,
capabilities.maxImageExtent.height);
return actual_extent;
};
}
void vulkan_swapchain::build_swap_images() {
auto hlogical_device = _ctx->get_current_device()->get_handle();
uint32_t image_count = static_cast<uint32_t>(_swap_images.size());
std::vector<VkImage> temp_img_buf;
vkGetSwapchainImagesKHR(hlogical_device, _swap_chain, &image_count, nullptr);
temp_img_buf.resize(image_count);
vkGetSwapchainImagesKHR(hlogical_device, _swap_chain, &image_count, temp_img_buf.data());
_swap_images.resize(image_count);
for (uint32_t i = 0; i < image_count; i++) {
_swap_images[i].image = temp_img_buf[i];
VkImageViewCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.image = _swap_images[i].image;
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = _image_format;
create_info.components.r = VK_COMPONENT_SWIZZLE_R;
create_info.components.g = VK_COMPONENT_SWIZZLE_G;
create_info.components.b = VK_COMPONENT_SWIZZLE_B;
create_info.components.a = VK_COMPONENT_SWIZZLE_A;
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
create_info.subresourceRange.baseMipLevel = 0;
create_info.subresourceRange.levelCount = 1;
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.layerCount = 1;
dyn_vk_verify(vkCreateImageView(hlogical_device, &create_info, nullptr, &_swap_images[i].view));
}
}
ref<vulkan_swapchain> vulkan_swapchain::create(const ref<vulkan_context> &ctx) {
return create_ref<vulkan_swapchain>(ctx);
}
}

9
src/renderer/shader.cpp Normal file
View File

@ -0,0 +1,9 @@
//
// Created by nepge on 5/31/2025.
//
#include <dyn/renderer/shader.h>
namespace dyn::core {
}