vertex buffers done

This commit is contained in:
Nepgfurmixpro 2025-06-04 12:20:20 -04:00
parent 1c01c2a4f1
commit 1387ac4f27
31 changed files with 708 additions and 273 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
cmake-build-debug/
cmake-build-release/
.idea/

View File

@ -15,6 +15,8 @@ find_package(spirv_cross_msl CONFIG REQUIRED)
find_package(spirv_cross_reflect CONFIG REQUIRED)
find_package(spirv_cross_util CONFIG REQUIRED)
find_package(unofficial-shaderc CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
foreach (file ${sourceFiles})
message("Source File Found ${file}")
@ -24,8 +26,11 @@ add_executable(Dyn ${sourceFiles})
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)
target_link_libraries(Dyn PRIVATE unofficial::shaderc::shaderc)
target_link_libraries(Dyn PRIVATE
Vulkan::Vulkan
glfw
Vulkan::SafeStruct Vulkan::LayerSettings Vulkan::UtilityHeaders Vulkan::CompilerConfiguration
spirv-cross-core spirv-cross-glsl spirv-cross-hlsl spirv-cross-msl spirv-cross-reflect spirv-cross-util
unofficial::shaderc::shaderc
glm::glm
GPUOpen::VulkanMemoryAllocator)

View File

@ -12,7 +12,7 @@ namespace dyn::core {
public:
command_queue() = default;
void submit(FuncT func) {
void submit(const FuncT &func) {
_command_list.push_back(func);
}

View File

@ -12,14 +12,16 @@
#define DYN_WARNING_LOG(...) ::dyn::core::warnln(__VA_ARGS__)
#define DYN_FATAL_LOG(...) ::dyn::core::fatalln(__VA_ARGS__)
#ifdef DYN_DEBUG
#define DYN_DEBUG_LOG(...) ::dyn::core::debugln(__VA_ARGS__)
#else
#define DYN_DEBUG_LOG(...)
#endif
#define DYN_ERROR_LOG(...) ::dyn::core::errorln(__VA_ARGS__)
#define DYN_INFO_LOG(...) ::dyn::core::logln(__VA_ARGS__)
#ifdef WIN32
#define _WIN_VER
#endif
#ifdef DYN_ENABLE_BREAKPOINT
#ifdef _WIN_VER
@ -44,11 +46,6 @@
#ifdef DYN_DEBUG
#define DYN_ENABLE_ASSERTIONS
#else
#define DYN_DEBUG_LOG(...)
#endif
#ifdef DYN_ENABLE_ASSERTIONS
@ -57,6 +54,6 @@
#else
#define DYN_ASSERT(...)
#define DYN_ASSERT(cond, ...) if (!(cond)) { DYN_ERROR_LOG("Assert failed", __VA_ARGS__); }
#endif

41
inc/dyn/core/event.h Normal file
View File

@ -0,0 +1,41 @@
//
// Created by nepge on 6/2/2025.
//
#pragma once
#include <functional>
namespace dyn::core {
enum class event_type {
WindowEventResize
};
#define DYN_EVENT_TYPE(type) static event_type get_static_event_type() { return event_type::type; } \
event_type get_event_type() const override { return get_static_event_type(); }
class event {
public:
virtual ~event() = default;
[[nodiscard]] virtual event_type get_event_type() const = 0;
};
class event_dispatcher {
public:
explicit event_dispatcher(event& event);
~event_dispatcher() = default;
template <typename T>
using event_fn = std::function<void(T&)>;
template<typename Event, typename = std::is_base_of<event, Event>>
void dispatch(event_fn<Event> func) {
if (_event.get_event_type() == Event::get_static_event_type()) {
func(*static_cast<Event*>(&_event));
}
}
private:
event& _event;
};
}

View File

@ -12,12 +12,12 @@
namespace dyn::core {
class file {
public:
file(const std::string& path);
file(const std::filesystem::path& path);
explicit file(const std::string& path);
explicit file(const std::filesystem::path& 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]] std::vector<uint8_t> bytes() const { return {_contents.begin(), _contents.end()}; }
[[nodiscard]] size_t size() const { return _contents.size(); }
[[nodiscard]] bool empty() const { return _contents.empty(); }
[[nodiscard]] std::string path() const { return _file_path; }

View File

@ -11,8 +11,8 @@
namespace dyn::core {
class logger {
public:
explicit logger(const std::string& name) : m_name(name) {}
virtual ~logger() = default;
explicit logger(std::string name) : m_name(std::move(name)) {}
~logger() = default;
template <typename... Args>
void log(const std::string& msg, Args&&... args) {

View File

@ -5,10 +5,15 @@
#pragma once
#include <string>
#include <vector>
#include <functional>
#define GLFW_INCLUDE_VULKAN
#include <glfw/glfw3.h>
#include "base.h"
#include "event.h"
#define glfw_verify(f) f; { const char* desc; uint32_t code = glfwGetError(&desc); \
if (desc != nullptr) { DYN_FATAL_LOG("GLFW error occured", desc, "at line", __LINE__, "in file", __FILE__, "at function call", #f); }}
namespace dyn::core {
class vulkan_context;
@ -20,6 +25,18 @@ namespace dyn::core {
bool fullscreen;
};
class window_resize_event : public event {
public:
window_resize_event(uint32_t width, uint32_t height);
uint32_t width;
uint32_t height;
DYN_EVENT_TYPE(WindowEventResize);
};
using event_callback_fn = std::function<void(event&)>;
class window {
public:
explicit window(window_spec spec);
@ -30,6 +47,10 @@ namespace dyn::core {
[[nodiscard]] static std::vector<const char*> get_required_extensions();
void set_event_callback(const event_callback_fn& callback);
void get_size(int* width, int* height) const;
static ref<window> create(const window_spec& spec);
void destroy();
@ -41,11 +62,16 @@ namespace dyn::core {
[[nodiscard]] GLFWwindow* get_window_handle() { return _window; }
private:
VkSurfaceKHR _surface = VK_NULL_HANDLE;
window_spec _spec;
GLFWwindow* _window;
struct window_mem {
event_callback_fn event_callback;
};
window_mem _window_mem;
friend class vulkan_context;
friend class vulkan_swap_chain;
};

View File

@ -5,6 +5,7 @@
#pragma once
#include <vulkan/vulkan.h>
#include <dyn/core/debug.h>
#include <vulkan/vk_enum_string_helper.h>
#include <vector>

View File

@ -5,31 +5,28 @@
#pragma once
#include <vulkan/vulkan.h>
#include <vk_mem_alloc.h>
#include <dyn/core/base.h>
#include <dyn/renderer/render_context.h>
#include "vulkan_devices.h"
#include "vulkan_swap_chain.h"
#define VK_CMD(name, context, ...) vkCmd ## name (context->get_command_buffer(), __VA_ARGS__);
#define VK_CMD(name, context, ...) vkCmd ## name (context->get_current_command_buffer(), __VA_ARGS__);
namespace dyn::core {
extern ref<vulkan_context> g_vulkan_context;
ref<vulkan_context> get_current_vulkan_context();
class vulkan_context : public render_context {
public:
explicit vulkan_context(const ref<window>& window);
~vulkan_context() override;
[[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 init() override;
void cmd_begin() override;
bool cmd_begin() override;
void render_mesh_source(ref<mesh_source> mesh) override;
@ -38,8 +35,9 @@ namespace dyn::core {
[[nodiscard]] ref<window> get_window() override { return _window; }
[[nodiscard]] ref<vulkan_device> get_current_device() const { return _device; }
[[nodiscard]] ref<vulkan_swap_chain> get_swap_chain() const { return _swap_chain; }
[[nodiscard]] VmaAllocator get_allocator() const { return _allocator; }
[[nodiscard]] VkCommandBuffer get_command_buffer() const { return _command_buffer; }
[[nodiscard]] VkCommandBuffer get_current_command_buffer() const { return _command_buffers[_swap_chain->current_frame()]; }
[[nodiscard]] VkInstance get_instance() const { return _instance; }
VkSurfaceKHR get_surface() const;
@ -49,6 +47,10 @@ namespace dyn::core {
void destroy() override;
void resize(uint32_t width, uint32_t height) override;
void set_vsync(bool vsync) override;
private:
VkInstance _instance = VK_NULL_HANDLE;
ref<vulkan_physical_device> _physical_device;
@ -57,12 +59,14 @@ namespace dyn::core {
ref<vulkan_swap_chain> _swap_chain;
VkCommandPool _command_pool = VK_NULL_HANDLE;
VkCommandBuffer _command_buffer = VK_NULL_HANDLE;
std::array<VkCommandBuffer, VULKAN_FRAMES_IN_FLIGHT> _command_buffers{};
VkDebugUtilsMessengerEXT _debug_messenger = VK_NULL_HANDLE;
VkSurfaceKHR _surface = VK_NULL_HANDLE;
VmaAllocator _allocator = VK_NULL_HANDLE;
ref<window> _window;
inline static ref<vulkan_device> s_current_device = nullptr;

View File

@ -55,5 +55,7 @@ namespace dyn::core {
VkPipelineLayout _pipeline_layout = VK_NULL_HANDLE;
VkPipeline _pipeline = VK_NULL_HANDLE;
VkVertexInputBindingDescription _vertex_binding_desc;
std::array<VkVertexInputAttributeDescription, 2> _vertex_attribute_desc;
};
}

View File

@ -11,7 +11,7 @@
#include <dyn/platform/vulkan/vulkan_swap_chain.h>
#include "vulkan_devices.h"
#include "dyn/renderer/swapchain.h"
#include "dyn/renderer/swap_chain.h"
namespace dyn::core {
struct swap_chain_support_details {
@ -20,7 +20,9 @@ namespace dyn::core {
std::vector<VkPresentModeKHR> present_modes;
};
class vulkan_swap_chain : public swapchain {
inline const int VULKAN_FRAMES_IN_FLIGHT = 2;
class vulkan_swap_chain : public swap_chain {
public:
explicit vulkan_swap_chain(const ref<vulkan_context>& ctx);
~vulkan_swap_chain() override;
@ -28,9 +30,11 @@ namespace dyn::core {
void prepare() override;
void present() override;
void next_frame();
bool next_frame();
void resize(uint32_t _width, uint32_t _height) override {}
uint32_t current_frame() const { return _current_frame; }
void resize(uint32_t width, uint32_t height) override;
static ref<vulkan_swap_chain> create(const ref<vulkan_context>& ctx);
@ -39,12 +43,15 @@ namespace dyn::core {
[[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 VkPresentModeKHR get_present_mode(const std::vector<VkPresentModeKHR> &modes, bool vsync);
[[nodiscard]] static VkExtent2D get_extent(ref<vulkan_context> ctx, const VkSurfaceCapabilitiesKHR& capabilities);
void rebuild() {}
void rebuild(bool fresh = false);
void invalidate() { _invalidated = true; }
void destroy();
void set_vsync(bool vsync) override;
private:
@ -52,6 +59,9 @@ namespace dyn::core {
void create_render_pass();
void create_framebuffers();
void destroy_framebuffers();
private:
struct swap_image {
VkImage image;
VkImageView view;
@ -63,17 +73,23 @@ namespace dyn::core {
VkFormat _image_format;
VkExtent2D _extent;
uint32_t _current_frame = 0;
uint32_t _new_width = 0;
uint32_t _new_height = 0;
VkSemaphore _image_available = VK_NULL_HANDLE;
VkSemaphore _render_finished = VK_NULL_HANDLE;
VkFence _in_flight = VK_NULL_HANDLE;
uint32_t _current_frame = 0;
std::array<uint32_t, VULKAN_FRAMES_IN_FLIGHT> _image_indices = {0, 0};
std::array<VkSemaphore, VULKAN_FRAMES_IN_FLIGHT> _image_available_semaphores{};
std::array<VkSemaphore, VULKAN_FRAMES_IN_FLIGHT> _render_finished_semaphores{};
std::array<VkFence, VULKAN_FRAMES_IN_FLIGHT> _in_flight_fences{};
VkSwapchainKHR _swap_chain = VK_NULL_HANDLE;
VkFramebuffer _frame_buffer = VK_NULL_HANDLE;
VkRenderPass _render_pass = VK_NULL_HANDLE;
ref<vulkan_device> _logical_device = nullptr;
bool _invalidated = false;
bool _vsync = false;
friend class vulkan_context;
};
}

View File

@ -0,0 +1,30 @@
//
// Created by nepge on 6/3/2025.
//
#pragma once
#include <vector>
#include <vk_mem_alloc.h>
#include <dyn/renderer/vertex_buffer.h>
namespace dyn::core {
class vulkan_vertex_buffer : public vertex_buffer {
public:
vulkan_vertex_buffer(const std::vector<vertex_pack>& vertices);
~vulkan_vertex_buffer() = default;
[[nodiscard]] VkBuffer get_handle() { return _buffer; }
void destroy() override;
private:
VkBuffer _buffer;
VmaAllocation _buffer_allocation;
std::vector<vertex_pack> _vertices;
};
}

View File

@ -9,39 +9,39 @@
namespace dyn::core {
enum class PolygonMode {
FILL,
LINE,
POINT
Fill,
Line,
Point
};
inline std::string cvt_string_polygon_mode(PolygonMode mode) {
switch (mode) {
case PolygonMode::FILL:
return "PolygonMode::FILL";
case PolygonMode::LINE:
return "PolygonMode::LINE";
case PolygonMode::POINT:
return "PolygonMode::POINT";
case PolygonMode::Fill:
return "PolygonMode::Fill";
case PolygonMode::Line:
return "PolygonMode::Line";
case PolygonMode::Point:
return "PolygonMode::Point";
}
return "INVALID POLYGON MODE";
}
enum class MSAASamples {
SAMPLES_1,
SAMPLES_2,
SAMPLES_4,
SAMPLES_8,
Samples1,
Samples2,
Samples4,
Samples8,
};
enum class CullMode {
FRONT,
BACK,
FRONT_AND_BACK,
Front,
Back,
FrontAndBack,
};
enum class FrontFace {
CLOCKWISE,
COUNTERCLOCKWISE,
Clockwise,
CounterClockwise,
};
class graphics_pipeline {
@ -49,7 +49,7 @@ namespace dyn::core {
virtual ~graphics_pipeline() = default;
virtual void set_polygon_mode(PolygonMode mode) = 0;
virtual void set_line_width(float line_width = 1.0f) = 0;
virtual void set_line_width(float line_width) = 0;
virtual void set_msaa_samples(MSAASamples samples) = 0;
virtual void set_cull_mode(CullMode mode, FrontFace face) = 0;
virtual void set_depth_bias_enabled(bool enable) = 0;

View File

@ -22,13 +22,6 @@ namespace dyn::core {
using cleanup_command = std::function<void()>;
using cleanup_command_queue = command_queue<cleanup_command>;
extern ref<render_context> g_context;
void set_current_context(const ref<render_context>& context);
inline ref<render_context> get_current_context() {
return g_context;
}
class render_context : public std::enable_shared_from_this<render_context> {
public:
render_context() = default;
@ -36,11 +29,13 @@ namespace dyn::core {
virtual void init() = 0;
virtual void set_vsync(bool vsync) = 0;
// begin a new render command
virtual void cmd_begin() = 0;
virtual bool cmd_begin() = 0;
// submit a render cmd to the queue
void submit(render_command func);
void submit(const render_command &func);
// submit a mesh source to be renderered
virtual void render_mesh_source(ref<mesh_source> mesh) = 0;
@ -50,19 +45,33 @@ namespace dyn::core {
virtual void destroy() = 0;
virtual void resize(uint32_t width, uint32_t height) = 0;
virtual ref<window> get_window() = 0;
static void set_api(RenderPlatform platform);
static RenderPlatform get_api();
void run_at_cleanup(cleanup_command cmd);
void run_at_cleanup(const cleanup_command &cmd);
static ref<render_context> create(const ref<window> &window);
static ref<render_context> current();
template <typename T, typename = std::is_base_of<render_context, T>>
static ref<T> current_as() {
return ref_as<T>(g_current_context);
}
static void submit_current(render_command func);
static void set_current(const ref<render_context> &ctx);
protected:
render_command_queue _render_command_queue;
cleanup_command_queue _cleanup_command_queue;
inline static ref<render_context> g_current_context;
private:
static RenderPlatform s_api;
};

View File

@ -38,21 +38,21 @@ namespace dyn::core {
virtual ~shader() = default;
enum ShaderType {
VERTEX,
FRAGMENT,
GEOMETRY,
COMPUTE
Vertex,
Fragment,
Geometry,
Compute
};
[[nodiscard]] static shaderc_shader_kind convert_shader_type(ShaderType type) noexcept {
switch (type) {
case VERTEX:
case Vertex:
return shaderc_vertex_shader;
case FRAGMENT:
case Fragment:
return shaderc_fragment_shader;
case GEOMETRY:
case Geometry:
return shaderc_geometry_shader;
case COMPUTE:
case Compute:
return shaderc_compute_shader;
}
return shaderc_vertex_shader;

View File

@ -9,15 +9,17 @@
#include <dyn/renderer/render_context.h>
namespace dyn::core {
class swapchain {
class swap_chain {
public:
swapchain() = default;
virtual ~swapchain() = default;
swap_chain() = default;
virtual ~swap_chain() = default;
virtual void resize(uint32_t width, uint32_t height) = 0;
virtual void prepare() = 0;
virtual void present() = 0;
ref<swapchain> create(const ref<render_context>& ctx);
virtual void set_vsync(bool vsync) = 0;
ref<swap_chain> create(const ref<render_context>& ctx);
};
}

View File

@ -0,0 +1,37 @@
//
// Created by nepge on 6/3/2025.
//
#pragma once
#include <glm/glm.hpp>
#include <vulkan/vulkan_core.h>
#include <array>
#include <vector>
#include "dyn/core/base.h"
namespace dyn::core {
class vulkan_graphics_pipeline;
struct vertex_pack {
glm::vec3 position;
glm::vec3 color;
protected:
static VkVertexInputBindingDescription get_binding_description();
static std::array<VkVertexInputAttributeDescription, 2> get_vertex_attribute_descriptions();
friend vulkan_graphics_pipeline;
};
class vertex_buffer {
public:
virtual ~vertex_buffer() = default;
virtual void destroy() = 0;
static ref<vertex_buffer> create(const std::vector<vertex_pack>& vertices);
};
}

View File

@ -5,5 +5,5 @@ layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 0.2);
outColor = vec4(fragColor, 0.5);
}

View File

@ -1,26 +1,11 @@
#version 450
layout(location = 0) in vec3 in_pos;
layout(location = 1) in vec3 in_color;
layout(location = 0) out vec3 fragColor;
vec2 positions[6] = vec2[](
vec2(-0.5, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5),
vec2(-0.5, -0.5),
vec2(0.5, -0.5),
vec2(0.5, 0.5)
);
vec3 colors[6] = vec3[](
vec3(1.0, 0.0, 1.0),
vec3(1.0, 1.0, 0.0),
vec3(0.0, 1.0, 1.0),
vec3(1.0, 0.0, 1.0),
vec3(0.0, 1.0, 1.0),
vec3(1.0, 1.0, 0.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
gl_Position = vec4(in_pos.xyz, 1.0);
fragColor = in_color;
}

10
src/core/event.cpp Normal file
View File

@ -0,0 +1,10 @@
//
// Created by nepge on 6/2/2025.
//
#include <dyn/core/event.h>
namespace dyn::core {
event_dispatcher::event_dispatcher(event &event) : _event(event) {
}
}

View File

@ -10,26 +10,40 @@
#include "dyn/renderer/render_context.h"
namespace dyn::core {
window_resize_event::window_resize_event(uint32_t width, uint32_t height) : width(width), height(height) {
}
window::window(window_spec spec): _spec(std::move(spec)) {
DYN_ASSERT(glfwInit(), "GLFW failed to initialize");
auto glfw_init_result = glfw_verify(glfwInit());
DYN_ASSERT(glfw_init_result, "GLFW failed to initialize");
set_window_hints();
const char* title = _spec.title.c_str();
_window = glfwCreateWindow(
_window = glfw_verify(glfwCreateWindow(
static_cast<int>(_spec.width),
static_cast<int>(_spec.height),
title,
nullptr, nullptr);
nullptr, nullptr));
auto monitor = glfwGetPrimaryMonitor();
auto vidMode = glfwGetVideoMode(monitor);
DYN_ASSERT(_window != nullptr, "Failed to create GLFW window");
auto monitor = glfw_verify(glfwGetPrimaryMonitor());
DYN_ASSERT(monitor != nullptr, "Failed to get primary monitor");
auto vidMode = glfw_verify(glfwGetVideoMode(monitor));
auto x = (vidMode->width/2) - spec.width/2;
auto y = (vidMode->height/2) - spec.height/2;
glfwSetWindowPos(_window, x, y);
DYN_ASSERT(_window != nullptr, "Failed to create GLFW window");
glfwSetWindowUserPointer(_window, &_window_mem);
glfwSetWindowSizeCallback(_window, [](GLFWwindow* window, int width, int height) {
auto win_mem = (window_mem*)glfwGetWindowUserPointer(window);
auto resize_event = window_resize_event(width, height);
win_mem->event_callback(resize_event);
});
}
window::~window() {
@ -52,11 +66,21 @@ namespace dyn::core {
std::vector<const char *> result;
result.reserve(extensions_count);
result.insert(result.begin(), extensions, extensions + extensions_count);
#ifdef __APPLE__
// support MoltenVK (https://stackoverflow.com/questions/58732459/vk-error-incompatible-driver-with-mac-os-and-vulkan-moltenvk)
result.push_back("VK_KHR_portability_enumeration");
#endif
return result;
}
void window::set_event_callback(const event_callback_fn &callback) {
_window_mem.event_callback = callback;
}
void window::get_size(int *width, int *height) const {
glfwGetFramebufferSize(_window, width, height);
}
void window::set_window_hints() {
auto render_api = render_context::get_api();

View File

@ -1,8 +1,10 @@
#include "dyn/core/window.h"
#include "dyn/platform/vulkan/vulkan_context.h"
#include "dyn/platform/vulkan/vulkan_vertex_buffer.h"
#include "dyn/renderer/graphics_pipeline.h"
#include "dyn/renderer/render_context.h"
#include "dyn/renderer/shader.h"
#include "dyn/renderer/vertex_buffer.h"
using namespace dyn;
using namespace dyn::core;
@ -18,16 +20,25 @@ int main() {
});
ref<render_context> ctx = render_context::create(win);
set_current_context(ctx);
render_context::set_current(ctx);
auto vulkan_ctx = ref_as<vulkan_context>(ctx);
ctx->init();
ctx->set_vsync(true);
ref<shader> vert_shader = shader::create_from_file("resources/shaders/basic.vert.glsl", shader::VERTEX);
win->set_event_callback([=](event& ev) {
event_dispatcher dispatcher(ev);
dispatcher.dispatch<window_resize_event>([=](window_resize_event& e) {
ctx->resize(e.width, e.height);
});
});
ref<shader> vert_shader = shader::create_from_file("resources/shaders/basic.vert.glsl", shader::Vertex);
if (vert_shader != nullptr) {
vert_shader->build(ctx);
}
auto frag_shader = shader::create_from_file("resources/shaders/basic.frag.glsl", shader::FRAGMENT);
auto frag_shader = shader::create_from_file("resources/shaders/basic.frag.glsl", shader::Fragment);
if (frag_shader != nullptr) {
frag_shader->build(ctx);
}
@ -41,17 +52,40 @@ int main() {
return -1;
}
std::vector<vertex_pack> vertices = {
{{0.0f, -0.2f, 1.0f}, {91.f/255.f, 206.f/250.f, 250.f/255.f}},
{{0.2f, 0.2f, 0.0f}, {245.f/255.f, 169.f/255.f, 184.f/255.f}},
{{-0.2f, 0.2f, 0.0f}, {245.f/255.f, 169.f/255.f, 184.f/255.f}},
{{-0.4f, -0.4f, 0.f}, {245.f/255.f, 169.f/255.f, 184.f/255.f}},
{{0.4f, -0.4f, 0.0f}, {245.f/255.f, 169.f/255.f, 184.f/255.f}},
{{0.0f, -0.2f, 0.0f}, {91.f/255.f, 206.f/250.f, 250.f/255.f}},
};
auto buffer = ref_as<vulkan_vertex_buffer>(vertex_buffer::create(vertices));
VkBuffer vertex_buffers[] = {buffer->get_handle()};
VkDeviceSize offsets[] = {0};
while (win->is_open()) {
win->poll_events();
ctx->cmd_begin();
auto start = std::chrono::high_resolution_clock::now();
if (ctx->cmd_begin()) {
pipeline->bind();
ctx->submit([=] {
VK_CMD(Draw, vulkan_ctx, 6, 1, 0, 0);
render_context::submit_current([=] {
VK_CMD(BindVertexBuffers, vulkan_ctx, 0, 1, vertex_buffers, offsets);
VK_CMD(Draw, vulkan_ctx, static_cast<uint32_t>(vertices.size()), 1, 0, 0);
});
ctx->cmd_end();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
//DYN_INFO_LOG("Frame time:", static_cast<double>(duration.count()) / 1000.0, "ms");
}
ctx->destroy();

View File

@ -2,6 +2,9 @@
// Created by Neo on 2/24/25.
//
#define VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>
#include <dyn/platform/vulkan/vulkan.h>
namespace dyn::utils {

View File

@ -10,10 +10,6 @@
namespace dyn::core {
ref<vulkan_context> g_vulkan_context = nullptr;
ref<vulkan_context> get_current_vulkan_context() {
return g_vulkan_context;
}
vulkan_context::vulkan_context(const ref<window>& window) : _window(window) {}
vulkan_context::~vulkan_context() {
@ -45,8 +41,10 @@ namespace dyn::core {
VkInstanceCreateInfo create_info{};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
#ifdef __APPLE__
// support MoltenVK (https://stackoverflow.com/questions/58732459/vk-error-incompatible-driver-with-mac-os-and-vulkan-moltenvk)
create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif
// extensions
auto extensions = window::get_required_extensions();
@ -110,6 +108,17 @@ namespace dyn::core {
s_current_device = _device;
}
{
VmaAllocatorCreateInfo allocator_ci{};
allocator_ci.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
allocator_ci.vulkanApiVersion = VK_API_VERSION_1_2;
allocator_ci.physicalDevice = _device->get_physical_device()->get_handle();
allocator_ci.device = _device->get_handle();
allocator_ci.instance = _instance;
vmaCreateAllocator(&allocator_ci, &_allocator);
}
{
_swap_chain = vulkan_swap_chain::create(self);
}
@ -127,24 +136,28 @@ namespace dyn::core {
allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocate_info.commandPool = _command_pool;
allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate_info.commandBufferCount = 1;
allocate_info.commandBufferCount = VULKAN_FRAMES_IN_FLIGHT;
dyn_vk_verify(vkAllocateCommandBuffers(_device->get_handle(), &allocate_info, &_command_buffer));
dyn_vk_verify(vkAllocateCommandBuffers(_device->get_handle(), &allocate_info, _command_buffers.data()));
}
}
void vulkan_context::cmd_begin() {
_swap_chain->next_frame();
bool vulkan_context::cmd_begin() {
if (!_swap_chain->next_frame()) {
return false;
}
_render_command_queue.clear();
vkResetCommandBuffer(_command_buffer, 0);
vkResetCommandBuffer(_command_buffers[_swap_chain->current_frame()], 0);
VkCommandBufferBeginInfo begin_info{};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
dyn_vk_verify(vkBeginCommandBuffer(_command_buffer, &begin_info));
dyn_vk_verify(vkBeginCommandBuffer(_command_buffers[_swap_chain->current_frame()], &begin_info));
_swap_chain->prepare();
return true;
}
void vulkan_context::render_mesh_source(ref<mesh_source> mesh) {
@ -152,8 +165,8 @@ namespace dyn::core {
void vulkan_context::cmd_end() {
_render_command_queue.execute();
vkCmdEndRenderPass(_command_buffer);
if (auto result = vkEndCommandBuffer(_command_buffer); result != VK_SUCCESS) {
vkCmdEndRenderPass(_command_buffers[_swap_chain->current_frame()]);
if (auto result = vkEndCommandBuffer(_command_buffers[_swap_chain->current_frame()]); result != VK_SUCCESS) {
DYN_ERROR_LOG("Failed to end command buffer. Error:", string_VkResult(result));
}
@ -209,14 +222,16 @@ namespace dyn::core {
void vulkan_context::destroy() {
vkDeviceWaitIdle(_device->get_handle());
DYN_DEBUG_LOG("vulkan_context::~vulkan_context()");
DYN_DEBUG_LOG("vulkan_context::destroy()");
_swap_chain->destroy();
_cleanup_command_queue.execute();
vkFreeCommandBuffers(_device->get_handle(), _command_pool, 1, &_command_buffer);
vkFreeCommandBuffers(_device->get_handle(), _command_pool, VULKAN_FRAMES_IN_FLIGHT, _command_buffers.data());
vkDestroyCommandPool(_device->get_handle(), _command_pool, nullptr);
_swap_chain->destroy();
vmaDestroyAllocator(_allocator);
_device->destroy();
@ -227,4 +242,12 @@ namespace dyn::core {
vkDestroySurfaceKHR(_instance, _surface, nullptr);
vkDestroyInstance(_instance, nullptr);
}
void vulkan_context::resize(uint32_t width, uint32_t height) {
_swap_chain->resize(width, height);
}
void vulkan_context::set_vsync(bool vsync) {
_swap_chain->set_vsync(vsync);
}
}

View File

@ -5,14 +5,16 @@
#include <dyn/platform/vulkan/vulkan_graphics_pipeline.h>
#include <dyn/platform/vulkan/vulkan_context.h>
#include "dyn/renderer/vertex_buffer.h"
namespace dyn::core {
VkPolygonMode get_vulkan_polygon_mode(PolygonMode mode) {
switch (mode) {
case PolygonMode::FILL:
case PolygonMode::Fill:
return VK_POLYGON_MODE_FILL;
case PolygonMode::LINE:
case PolygonMode::Line:
return VK_POLYGON_MODE_LINE;
case PolygonMode::POINT:
case PolygonMode::Point:
return VK_POLYGON_MODE_POINT;
}
return VK_POLYGON_MODE_MAX_ENUM;
@ -24,7 +26,15 @@ namespace dyn::core {
_dynamic_state_ci.pDynamicStates = g_vulkan_dynamic_states.data();
_vertex_input_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
_vertex_input_ci.vertexBindingDescriptionCount = 0;
_vertex_binding_desc = vertex_pack::get_binding_description();
_vertex_attribute_desc = vertex_pack::get_vertex_attribute_descriptions();
_vertex_input_ci.vertexBindingDescriptionCount = 1;
_vertex_input_ci.vertexAttributeDescriptionCount = static_cast<uint32_t>(_vertex_attribute_desc.size());
_vertex_input_ci.pVertexBindingDescriptions = &_vertex_binding_desc;
_vertex_input_ci.pVertexAttributeDescriptions = _vertex_attribute_desc.data();
_input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
_input_assembly_ci.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
@ -150,7 +160,7 @@ namespace dyn::core {
return false;
}
get_current_context()->run_at_cleanup([=] {
render_context::current()->run_at_cleanup([=] {
this->destroy();
});
@ -158,10 +168,21 @@ namespace dyn::core {
}
void vulkan_graphics_pipeline::bind() {
get_current_context()->submit([=] {
auto ctx = get_current_vulkan_context();
render_context::submit_current([=] {
auto ctx = render_context::current_as<vulkan_context>();
VK_CMD(BindPipeline, ctx, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipeline);
_viewport.x = 0.0f;
_viewport.y = 0.0f;
auto swap_extent = ctx->get_swap_chain()->get_extent();
_viewport.width = static_cast<float>(swap_extent.width);
_viewport.height = static_cast<float>(swap_extent.height);
_viewport.minDepth = 0.0f;
_viewport.maxDepth = 1.0f;
_scissor.offset = {0, 0};
_scissor.extent = swap_extent;
VK_CMD(SetViewport, ctx, 0, 1, &_viewport);
VK_CMD(SetScissor, ctx, 0, 1, &_scissor);
});

View File

@ -11,7 +11,9 @@ namespace dyn::core {
vulkan_shader::vulkan_shader(
const std::vector<uint32_t>&source,
const std::string& filename, ShaderType type)
: _binary(source.begin(), source.end()), _filename(filename), _type(type), _stage(vulkan_stage_from_type(type)) {}
: _binary(source.begin(), source.end()), _filename(filename), _type(type), _stage(vulkan_stage_from_type(type)) {
}
vulkan_shader::~vulkan_shader() {
@ -19,13 +21,13 @@ namespace dyn::core {
VkShaderStageFlagBits vulkan_shader::vulkan_stage_from_type(ShaderType type) {
switch (type) {
case VERTEX:
case Vertex:
return VK_SHADER_STAGE_VERTEX_BIT;
case FRAGMENT:
case Fragment:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case GEOMETRY:
case Geometry:
return VK_SHADER_STAGE_GEOMETRY_BIT;
case COMPUTE:
case Compute:
return VK_SHADER_STAGE_COMPUTE_BIT;
}
return static_cast<VkShaderStageFlagBits>(0);

View File

@ -9,76 +9,7 @@
namespace dyn::core {
vulkan_swap_chain::vulkan_swap_chain(const ref<vulkan_context> &ctx) : _logical_device(ctx->get_current_device()) {
auto phy_device = _logical_device->get_physical_device();
auto dev_handle = _logical_device->get_handle();
auto support_details = vulkan_swap_chain::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(dev_handle, &create_info, nullptr, &_swap_chain));
VkSemaphoreCreateInfo semaphore_info{};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fence_info{};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
dyn_vk_verify(vkCreateSemaphore(dev_handle, &semaphore_info, nullptr, &_image_available));
dyn_vk_verify(vkCreateSemaphore(dev_handle, &semaphore_info, nullptr, &_render_finished));
dyn_vk_verify(vkCreateFence(dev_handle, &fence_info, nullptr, &_in_flight));
// create swap chain images
build_swap_images();
create_render_pass();
create_framebuffers();
this->rebuild(true);
}
vulkan_swap_chain::~vulkan_swap_chain() {
@ -86,11 +17,11 @@ namespace dyn::core {
}
void vulkan_swap_chain::prepare() {
get_current_context()->submit([=] {
render_context::submit_current([=] {
VkRenderPassBeginInfo render_pass_info{};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = _render_pass;
render_pass_info.framebuffer = _framebuffers[_current_frame];
render_pass_info.framebuffer = _framebuffers[_image_indices[_current_frame]];
render_pass_info.renderArea.offset = {0, 0};
render_pass_info.renderArea.extent = _extent;
@ -98,16 +29,16 @@ namespace dyn::core {
render_pass_info.clearValueCount = 1;
render_pass_info.pClearValues = &clear_value;
VK_CMD(BeginRenderPass, get_current_vulkan_context(), &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
VK_CMD(BeginRenderPass, render_context::current_as<vulkan_context>(), &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
});
}
void vulkan_swap_chain::present() {
auto cmd_buffer = get_current_vulkan_context()->get_command_buffer();
auto cmd_buffer = render_context::current_as<vulkan_context>()->get_current_command_buffer();
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore wait_semaphores[] = {_image_available};
VkSemaphore wait_semaphores[] = {_image_available_semaphores[_current_frame]};
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = wait_semaphores;
@ -116,11 +47,11 @@ namespace dyn::core {
submit_info.pCommandBuffers = &cmd_buffer;
submit_info.signalSemaphoreCount = 1;
VkSemaphore signal_semaphores[] = {_render_finished};
VkSemaphore signal_semaphores[] = {_render_finished_semaphores[_current_frame]};
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
if (auto result = vkQueueSubmit(_logical_device->get_graphics_queue(), 1, &submit_info, _in_flight); result != VK_SUCCESS) {
if (auto result = vkQueueSubmit(_logical_device->get_graphics_queue(), 1, &submit_info, _in_flight_fences[_current_frame]); result != VK_SUCCESS) {
DYN_ERROR_LOG("Failed to submit queue. Error:", string_VkResult(result));
return;
}
@ -133,16 +64,49 @@ namespace dyn::core {
VkSwapchainKHR swap_chains[] = {_swap_chain};
present_info.swapchainCount = 1;
present_info.pSwapchains = swap_chains;
present_info.pImageIndices = &_current_frame;
present_info.pImageIndices = &_image_indices[_current_frame];
vkQueuePresentKHR(_logical_device->get_present_queue(), &present_info);
auto result = vkQueuePresentKHR(_logical_device->get_present_queue(), &present_info);
if (
result == VK_ERROR_OUT_OF_DATE_KHR ||
result == VK_SUBOPTIMAL_KHR ||
_invalidated) {
_invalidated = false;
rebuild();
} else if (result != VK_SUCCESS) {
DYN_DEBUG_LOG("Failed to present swap chain image. Error:", string_VkResult(result));
}
_current_frame = (_current_frame + 1) % VULKAN_FRAMES_IN_FLIGHT;
}
bool vulkan_swap_chain::next_frame() {
if (_new_width == 0 || _new_height == 0) {
return false;
}
void vulkan_swap_chain::next_frame() {
auto dev_handle = _logical_device->get_handle();
vkWaitForFences(dev_handle, 1, &_in_flight, VK_TRUE, UINT64_MAX);
vkResetFences(dev_handle, 1, &_in_flight);
vkAcquireNextImageKHR(dev_handle, _swap_chain, UINT64_MAX, _image_available, VK_NULL_HANDLE, &_current_frame);
vkWaitForFences(dev_handle, 1, &_in_flight_fences[_current_frame], VK_TRUE, UINT64_MAX);
auto result = vkAcquireNextImageKHR(dev_handle, _swap_chain, UINT64_MAX, _image_available_semaphores[_current_frame], VK_NULL_HANDLE, &_image_indices[_current_frame]);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
rebuild();
return false;
}
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
DYN_ERROR_LOG("Acquisition of swap chain image failed with error:", string_VkResult(result));
return false;
}
vkResetFences(dev_handle, 1, &_in_flight_fences[_current_frame]);
return true;
}
void vulkan_swap_chain::resize(uint32_t width, uint32_t height) {
_new_width = width;
_new_height = height;
invalidate();
}
swap_chain_support_details vulkan_swap_chain::get_support_details(VkSurfaceKHR surface, VkPhysicalDevice physical_device) {
@ -183,10 +147,14 @@ namespace dyn::core {
return formats[0];
}
VkPresentModeKHR vulkan_swap_chain::get_present_mode(const std::vector<VkPresentModeKHR> &modes) {
VkPresentModeKHR vulkan_swap_chain::get_present_mode(const std::vector<VkPresentModeKHR> &modes, bool vsync) {
for (const auto &mode : modes) {
// mailbox khr is preferred.
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
if (vsync && mode == VK_PRESENT_MODE_MAILBOX_KHR) {
return mode;
}
if (!vsync && mode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
return mode;
}
}
@ -198,11 +166,10 @@ namespace dyn::core {
VkExtent2D vulkan_swap_chain::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);
ctx->get_window()->get_size(&width, &height);
VkExtent2D actual_extent = {
static_cast<uint32_t>(width),
@ -219,19 +186,104 @@ namespace dyn::core {
capabilities.minImageExtent.height,
capabilities.maxImageExtent.height);
return actual_extent;
};
return actual_extent;;
}
void vulkan_swap_chain::rebuild(bool fresh) {
auto ctx = render_context::current_as<vulkan_context>();
if (!fresh) {
vkDeviceWaitIdle(_logical_device->get_handle());
this->destroy_framebuffers();
}
auto phy_device = _logical_device->get_physical_device();
auto dev_handle = _logical_device->get_handle();
auto support_details = vulkan_swap_chain::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, _vsync);
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;
}
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;
_new_width = extent.width;
_new_height = extent.height;
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_CONCURRENT;
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(dev_handle, &create_info, nullptr, &_swap_chain));
VkSemaphoreCreateInfo semaphore_info{};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fence_info{};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
// create swap chain images
build_swap_images();
if (fresh) {
for (auto i = 0; i < VULKAN_FRAMES_IN_FLIGHT; i++) {
dyn_vk_verify(vkCreateSemaphore(dev_handle, &semaphore_info, nullptr, &_image_available_semaphores[i]));
dyn_vk_verify(vkCreateSemaphore(dev_handle, &semaphore_info, nullptr, &_render_finished_semaphores[i]));
dyn_vk_verify(vkCreateFence(dev_handle, &fence_info, nullptr, &_in_flight_fences[i]));
}
create_render_pass();
}
create_framebuffers();
}
void vulkan_swap_chain::build_swap_images() {
auto hogical_device = _logical_device->get_handle();
auto logical_device = _logical_device->get_handle();
uint32_t image_count = static_cast<uint32_t>(_swap_images.size());
std::vector<VkImage> temp_img_buf;
vkGetSwapchainImagesKHR(hogical_device, _swap_chain, &image_count, nullptr);
vkGetSwapchainImagesKHR(logical_device, _swap_chain, &image_count, nullptr);
temp_img_buf.resize(image_count);
vkGetSwapchainImagesKHR(hogical_device, _swap_chain, &image_count, temp_img_buf.data());
vkGetSwapchainImagesKHR(logical_device, _swap_chain, &image_count, temp_img_buf.data());
_swap_images.resize(image_count);
for (uint32_t i = 0; i < image_count; i++) {
@ -254,7 +306,7 @@ namespace dyn::core {
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.layerCount = 1;
dyn_vk_verify(vkCreateImageView(hogical_device, &create_info, nullptr, &_swap_images[i].view));
dyn_vk_verify(vkCreateImageView(logical_device, &create_info, nullptr, &_swap_images[i].view));
}
}
@ -318,24 +370,37 @@ namespace dyn::core {
}
}
void vulkan_swap_chain::destroy() {
void vulkan_swap_chain::destroy_framebuffers() {
auto dev_handle = _logical_device->get_handle();
vkDestroySemaphore(_logical_device->get_handle(), _image_available, nullptr);
vkDestroySemaphore(_logical_device->get_handle(), _render_finished, nullptr);
vkDestroyFence(_logical_device->get_handle(), _in_flight, nullptr);
for (auto framebuffer : _framebuffers) {
vkDestroyFramebuffer(dev_handle, framebuffer, nullptr);
}
vkDestroyRenderPass(dev_handle, _render_pass, nullptr);
vkDestroySwapchainKHR(dev_handle, _swap_chain, nullptr);
for (auto img : _swap_images) {
vkDestroyImageView(dev_handle, img.view, nullptr);
}
vkDestroySwapchainKHR(dev_handle, _swap_chain, nullptr);
}
void vulkan_swap_chain::destroy() {
auto dev_handle = _logical_device->get_handle();
this->destroy_framebuffers();
for (auto i = 0; i < VULKAN_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(_logical_device->get_handle(), _image_available_semaphores[i], nullptr);
vkDestroySemaphore(_logical_device->get_handle(), _render_finished_semaphores[i], nullptr);
vkDestroyFence(_logical_device->get_handle(), _in_flight_fences[i], nullptr);
}
vkDestroyRenderPass(dev_handle, _render_pass, nullptr);
}
void vulkan_swap_chain::set_vsync(bool vsync) {
_vsync = vsync;
this->invalidate();
}
ref<vulkan_swap_chain> vulkan_swap_chain::create(const ref<vulkan_context> &ctx) {

View File

@ -0,0 +1,44 @@
//
// Created by nepge on 6/3/2025.
//
#include <dyn/platform/vulkan/vulkan_vertex_buffer.h>
#include <vulkan/vk_enum_string_helper.h>
#include "dyn/platform/vulkan/vulkan_context.h"
#include "dyn/renderer/render_context.h"
namespace dyn::core {
vulkan_vertex_buffer::vulkan_vertex_buffer(const std::vector<vertex_pack>& vertices): _buffer(nullptr) {
auto allocator = render_context::current_as<vulkan_context>()->get_allocator();
_vertices.resize(vertices.size());
_vertices.assign(vertices.begin(), vertices.end());
VkBufferCreateInfo buffer_ci{};
buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_ci.size = _vertices.size() * sizeof(_vertices[0]);
buffer_ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
buffer_ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocation_info{};
allocation_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
allocation_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
if (auto result = vmaCreateBuffer(allocator, &buffer_ci, &allocation_info, &_buffer, &_buffer_allocation, nullptr); result != VK_SUCCESS) {
DYN_ERROR_LOG("Failed to creat vertex buffer with error result:", string_VkResult(result));
}
vmaCopyMemoryToAllocation(allocator, vertices.data(), _buffer_allocation, 0, buffer_ci.size);
render_context::current()->run_at_cleanup([=] {
this->destroy();
});
}
void vulkan_vertex_buffer::destroy() {
if (_buffer != nullptr) {
auto allocator = render_context::current_as<vulkan_context>()->get_allocator();
vmaDestroyBuffer(allocator, _buffer, _buffer_allocation);
}
}
}

View File

@ -8,14 +8,7 @@
namespace dyn::core {
RenderPlatform render_context::s_api = RenderPlatform::None;
ref<render_context> g_context = nullptr;
void set_current_context(const ref<render_context> &context) {
g_context = std::move(context);
DYN_BREAKPOINT;
}
void render_context::submit(render_command func) {
void render_context::submit(const render_command &func) {
_render_command_queue.submit(func);
}
@ -27,7 +20,7 @@ namespace dyn::core {
return s_api;
}
void render_context::run_at_cleanup(cleanup_command cmd) {
void render_context::run_at_cleanup(const cleanup_command &cmd) {
_cleanup_command_queue.submit(cmd);
}
@ -36,4 +29,19 @@ namespace dyn::core {
DYN_PLATFORM_CREATE_VK(vulkan_context, window);
DYN_PLATFORM_CREATE_END(render_context)
}
ref<render_context> render_context::current() {
if (g_current_context == nullptr) {
DYN_ERROR_LOG("Attempt to access null render context. Make sure render context is set properly.");
}
return g_current_context;
}
void render_context::submit_current(render_command func) {
current()->submit(func);
}
void render_context::set_current(const ref<render_context> &ctx) {
g_current_context = ctx;
}
}

View File

@ -0,0 +1,45 @@
//
// Created by nepge on 6/3/2025.
//
#pragma once
#include <dyn/renderer/vertex_buffer.h>
#include "dyn/platform/vulkan/vulkan_vertex_buffer.h"
#include "dyn/renderer/render_context.h"
#include "dyn/renderer/shader.h"
namespace dyn::core {
VkVertexInputBindingDescription vertex_pack::get_binding_description() {
VkVertexInputBindingDescription binding_description{};
binding_description.binding = 0;
binding_description.stride = sizeof(vertex_pack);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return binding_description;
}
std::array<VkVertexInputAttributeDescription, 2> vertex_pack::get_vertex_attribute_descriptions() {
std::array<VkVertexInputAttributeDescription, 2> vertex_attribute_descriptions{};
vertex_attribute_descriptions[0].binding = 0;
vertex_attribute_descriptions[0].location = 0;
vertex_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attribute_descriptions[0].offset = offsetof(vertex_pack, position);
vertex_attribute_descriptions[1].binding = 0;
vertex_attribute_descriptions[1].location = 1;
vertex_attribute_descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attribute_descriptions[1].offset = offsetof(vertex_pack, color);
return vertex_attribute_descriptions;
}
ref<vertex_buffer> vertex_buffer::create(const std::vector<vertex_pack> &vertices) {
DYN_PLATFORM_CREATE_BEGIN(render_context::get_api())
DYN_PLATFORM_CREATE_VK(vulkan_vertex_buffer, vertices);
DYN_PLATFORM_CREATE_END(vertex_buffer)
}
}