triangle is rendered!!!

This commit is contained in:
Nepgfurmixpro 2025-06-02 18:53:09 -04:00
parent 4e8f3292bd
commit 1c01c2a4f1
23 changed files with 816 additions and 268 deletions

View File

@ -16,12 +16,16 @@ namespace dyn::core {
_command_list.push_back(func);
}
void clear() {
_command_list.clear();
}
template<typename ...Args>
void execute(Args&&... args) {
for (auto command : _command_list) {
command(args...);
}
_command_list.clear();
clear();
}
private:

View File

@ -20,6 +20,8 @@
#define _WIN_VER
#endif
#ifdef DYN_ENABLE_BREAKPOINT
#ifdef _WIN_VER
#define DYN_BREAKPOINT __debugbreak()
#elif __has_builtin(__builtin_debugtrap)
@ -34,6 +36,12 @@
#error "Breakpoints where not able to be created"
#endif
#else
#define DYN_BREAKPOINT
#endif
#ifdef DYN_DEBUG
#define DYN_ENABLE_ASSERTIONS

View File

@ -25,10 +25,15 @@ namespace dyn::core {
explicit window(window_spec spec);
~window();
bool is_open() const;
void poll_events() const;
[[nodiscard]] static std::vector<const char*> get_required_extensions();
static ref<window> create(const window_spec& spec);
void destroy();
protected:
static void set_window_hints();
@ -42,6 +47,6 @@ namespace dyn::core {
GLFWwindow* _window;
friend class vulkan_context;
friend class vulkan_swapchain;
friend class vulkan_swap_chain;
};
}

View File

@ -9,15 +9,24 @@
#include <dyn/renderer/render_context.h>
#include "vulkan_devices.h"
#include "vulkan_swapchain.h"
#include "vulkan_swap_chain.h"
#define VK_CMD(name, context, ...) vkCmd ## name (context->get_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;
@ -27,31 +36,32 @@ namespace dyn::core {
void cmd_end() override;
[[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]] VkCommandBuffer get_command_buffer() const { return _command_buffer; }
[[nodiscard]] VkInstance get_instance() const { return _instance; }
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; }
[[nodiscard]] ref<vulkan_device> get_current_device() { return _device; }
void single_cmd(const std::function<void(VkCommandBuffer)>& cmd_func);
void destroy() override;
private:
VkInstance _instance = nullptr;
VkInstance _instance = VK_NULL_HANDLE;
ref<vulkan_physical_device> _physical_device;
ref<vulkan_device> _device;
ref<vulkan_swapchain> _swap_chain;
ref<vulkan_swap_chain> _swap_chain;
VkCommandPool _command_pool = nullptr;
VkCommandPool _command_pool = VK_NULL_HANDLE;
VkCommandBuffer _command_buffer = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT _debug_messenger = nullptr;
VkDebugUtilsMessengerEXT _debug_messenger = VK_NULL_HANDLE;
VkSurfaceKHR _surface = nullptr;
VkSurfaceKHR _surface = VK_NULL_HANDLE;
ref<window> _window;

View File

@ -8,6 +8,7 @@
#include <dyn/core/base.h>
#include <optional>
#include <vector>
namespace dyn::core {
class vulkan_context;
@ -41,7 +42,6 @@ namespace dyn::core {
private:
VkPhysicalDevice _physical_device = nullptr;
queue_families _queue_families;
ref<vulkan_context> _ctx;
VkPhysicalDeviceProperties _device_properties;
};
@ -59,6 +59,8 @@ namespace dyn::core {
static ref<vulkan_device> create(const std::shared_ptr<vulkan_physical_device>& device, const VkPhysicalDeviceFeatures& device_features);
void destroy();
private:
VkDevice _device = nullptr;
ref<vulkan_physical_device> _physical_device;

View File

@ -3,17 +3,21 @@
//
#pragma once
#include "vulkan_shader.h"
#include "dyn/renderer/graphics_pipeline.h"
namespace dyn::core {
class vulkan_context;
inline static std::vector<VkDynamicState> g_vulkan_dynamic_states = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
[[nodiscard]] VkPolygonMode get_vulkan_polygon_mode(PolygonMode mode);
class vulkan_graphics_pipeline : public graphics_pipeline {
public:
vulkan_graphics_pipeline();
vulkan_graphics_pipeline(const ref<vulkan_context>& ctx);
~vulkan_graphics_pipeline();
void set_polygon_mode(PolygonMode mode) override;
@ -25,9 +29,15 @@ namespace dyn::core {
void attach_shader(const ref<shader> &shader) override;
void build(const ref<render_context> &ctx) override;
bool build() override;
void bind() override;
void destroy() override;
private:
std::vector<ref<vulkan_shader>> _shaders;
VkPipelineDynamicStateCreateInfo _dynamic_state_ci{};
VkPipelineVertexInputStateCreateInfo _vertex_input_ci{};
VkPipelineInputAssemblyStateCreateInfo _input_assembly_ci{};
@ -36,8 +46,14 @@ namespace dyn::core {
VkPipelineViewportStateCreateInfo _viewport_state_ci{};
VkPipelineRasterizationStateCreateInfo _rasterization_state_ci{};
VkPipelineMultisampleStateCreateInfo _multisample_state_ci{};
VkPipelineColorBlendAttachmentState _color_blend_state{};
VkPipelineColorBlendStateCreateInfo _color_blend_state_ci{};
VkPipelineLayoutCreateInfo _pipeline_layout_ci{};
VkPipeline _pipleine = VK_NULL_HANDLE;
ref<vulkan_swap_chain> _swap_chain;
ref<vulkan_device> _device;
VkPipelineLayout _pipeline_layout = VK_NULL_HANDLE;
VkPipeline _pipeline = VK_NULL_HANDLE;
};
}

View File

@ -26,9 +26,11 @@ namespace dyn::core {
void build(const ref<render_context> &context) override;
void destroy() override;
private:
std::vector<uint32_t> _binary;
ref<vulkan_context> _ctx = nullptr;
ref<vulkan_device> _device;
bool _usable = false;
std::string _filename;
ShaderType _type;

View File

@ -8,8 +8,9 @@
#include <limits>
#include <algorithm>
#include <dyn/platform/vulkan/vulkan_swapchain.h>
#include <dyn/platform/vulkan/vulkan_swap_chain.h>
#include "vulkan_devices.h"
#include "dyn/renderer/swapchain.h"
namespace dyn::core {
@ -19,45 +20,59 @@ namespace dyn::core {
std::vector<VkPresentModeKHR> present_modes;
};
class vulkan_swapchain : public swapchain {
class vulkan_swap_chain : public swapchain {
public:
explicit vulkan_swapchain(const ref<vulkan_context>& ctx);
~vulkan_swapchain() override;
explicit vulkan_swap_chain(const ref<vulkan_context>& ctx);
~vulkan_swap_chain() override;
void prepare() override {}
void present() override {}
void prepare() override;
void present() override;
void next_frame();
void resize(uint32_t _width, uint32_t _height) override {}
[[nodiscard]] VkCommandBuffer current_cmd_buffer() const { return _command_buffers[_current_frame]; }
static ref<vulkan_swap_chain> create(const ref<vulkan_context>& ctx);
static ref<vulkan_swapchain> create(const ref<vulkan_context>& ctx);
[[nodiscard]] VkExtent2D get_extent() const { return _extent; }
[[nodiscard]] VkRenderPass get_render_pass() const { return _render_pass; }
[[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 build_swap_images();
void rebuild() {}
void destroy();
private:
void build_swap_images();
void create_render_pass();
void create_framebuffers();
struct swap_image {
VkImage image;
VkImageView view;
};
std::vector<VkFramebuffer> _framebuffers;
std::vector<swap_image> _swap_images;
VkFormat _image_format;
VkExtent2D _extent;
std::vector<VkCommandBuffer> _command_buffers;
uint32_t _current_frame = 0;
VkSwapchainKHR _swap_chain;
ref<vulkan_context> _ctx;
VkSemaphore _image_available = VK_NULL_HANDLE;
VkSemaphore _render_finished = VK_NULL_HANDLE;
VkFence _in_flight = VK_NULL_HANDLE;
VkSwapchainKHR _swap_chain = VK_NULL_HANDLE;
VkFramebuffer _frame_buffer = VK_NULL_HANDLE;
VkRenderPass _render_pass = VK_NULL_HANDLE;
ref<vulkan_device> _logical_device = nullptr;
friend class vulkan_context;
};

View File

@ -14,6 +14,18 @@ namespace dyn::core {
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";
}
return "INVALID POLYGON MODE";
}
enum class MSAASamples {
SAMPLES_1,
SAMPLES_2,
@ -45,6 +57,12 @@ namespace dyn::core {
virtual void attach_shader(const ref<shader>& shader) = 0;
virtual void build(const ref<render_context>& ctx) = 0;
virtual bool build() = 0;
virtual void bind() = 0;
virtual void destroy() = 0;
static ref<graphics_pipeline> create(const ref<render_context>& context);
};
}

View File

@ -10,6 +10,7 @@
#include <dyn/core/base.h>
#include <dyn/renderer/mesh.h>
#include "dyn/core/debug.h"
#include "dyn/core/window.h"
namespace dyn::core {
@ -18,6 +19,16 @@ namespace dyn::core {
using render_command = std::function<void()>;
using render_command_queue = command_queue<render_command>;
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;
@ -37,15 +48,20 @@ namespace dyn::core {
// end the frame, render to the screen
virtual void cmd_end() = 0;
virtual void destroy() = 0;
virtual ref<window> get_window() = 0;
static void set_api(RenderPlatform platform);
static RenderPlatform get_api();
void run_at_cleanup(cleanup_command cmd);
static ref<render_context> create(const ref<window> &window);
protected:
render_command_queue _render_command_queue;
cleanup_command_queue _cleanup_command_queue;
private:
static RenderPlatform s_api;

View File

@ -62,6 +62,8 @@ namespace dyn::core {
virtual void build(const ref<render_context>& context) = 0;
virtual void destroy() = 0;
static ref<shader> create_from_string(const std::string& code, ShaderType type, const std::string& filename = "inline-source");
static ref<shader> create_from_file(const std::string& filename, ShaderType type);

View File

@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 0.2);
}

View File

@ -1,9 +1,26 @@
#version 450
#extension GL_GOOGLE_include_directive : require
#include "test.glsl"
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(1.0, 1.0, 0.0, 1.0);
}
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}

View File

@ -14,11 +14,12 @@ namespace dyn::core {
DYN_ASSERT(glfwInit(), "GLFW failed to initialize");
set_window_hints();
const char* title = _spec.title.c_str();
_window = glfwCreateWindow(
static_cast<int>(spec.width),
static_cast<int>(spec.height),
spec.title.c_str(),
static_cast<int>(_spec.width),
static_cast<int>(_spec.height),
title,
nullptr, nullptr);
auto monitor = glfwGetPrimaryMonitor();
@ -32,8 +33,15 @@ namespace dyn::core {
}
window::~window() {
glfwDestroyWindow(_window);
glfwTerminate();
}
bool window::is_open() const {
return !glfwWindowShouldClose(_window);
}
void window::poll_events() const {
glfwPollEvents();
}
std::vector<const char *> window::get_required_extensions() {
@ -61,4 +69,9 @@ namespace dyn::core {
ref<window> window::create(const window_spec &spec) {
return create_ref<window>(spec);
}
void window::destroy() {
glfwDestroyWindow(_window);
glfwTerminate();
}
}

View File

@ -1,5 +1,6 @@
#include "dyn/core/window.h"
#include "dyn/platform/vulkan/vulkan_context.h"
#include "dyn/renderer/graphics_pipeline.h"
#include "dyn/renderer/render_context.h"
#include "dyn/renderer/shader.h"
@ -12,16 +13,49 @@ int main() {
ref<window> win = window::create({
.width = 1280,
.height = 720,
.title = "Dyn Engine",
.title = std::string("Dyn Engine"),
.fullscreen = false
});
ref<render_context> ctx = render_context::create(win);
set_current_context(ctx);
auto vulkan_ctx = ref_as<vulkan_context>(ctx);
ctx->init();
ref<shader> test_shader = shader::create_from_file("resources/shaders/basic.vert.glsl", shader::VERTEX);
if (test_shader != nullptr) {
test_shader->build(ctx);
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);
if (frag_shader != nullptr) {
frag_shader->build(ctx);
}
auto pipeline = graphics_pipeline::create(ctx);
pipeline->attach_shader(vert_shader);
pipeline->attach_shader(frag_shader);
auto success = pipeline->build();
if (!success) {
return -1;
}
while (win->is_open()) {
win->poll_events();
ctx->cmd_begin();
pipeline->bind();
ctx->submit([=] {
VK_CMD(Draw, vulkan_ctx, 6, 1, 0, 0);
});
ctx->cmd_end();
}
ctx->destroy();
win->destroy();
return 0;
}

View File

@ -8,26 +8,26 @@
#include <dyn/core/window.h>
namespace dyn::core {
ref<vulkan_context> g_vulkan_context = nullptr;
vulkan_context::vulkan_context(const ref<window>& window) : _window(window) {
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() {
DYN_DEBUG_LOG("vulkan_context::~vulkan_context()");
if (utils::vk_enable_validation_layers) {
utils::vkDestroyDebugUtilsMessengerEXT(_instance, _debug_messenger, nullptr);
}
vkDestroySurfaceKHR(_instance, _surface, nullptr);
vkDestroyInstance(_instance, nullptr);
}
void vulkan_context::init() {
if (!check_api_version_support(VK_API_VERSION_1_2)) {
DYN_ERROR_LOG("Vulkan api not supported.");
return;
}
g_vulkan_context = ref_as<vulkan_context>(shared_from_this());
// changed to shared_from_this because making a ref using `this` causes the deconstructor to be called twice which causes EXC_BAD_ACCESS.
auto self = ref_as<vulkan_context>(shared_from_this());
@ -99,13 +99,19 @@ namespace dyn::core {
VkPhysicalDeviceFeatures device_features{};
device_features.samplerAnisotropy = true;
device_features.logicOp = true;
device_features.wideLines = true;
device_features.fillModeNonSolid = true;
device_features.independentBlend = true;
device_features.pipelineStatisticsQuery = true;
device_features.shaderStorageImageReadWithoutFormat = true;
_device = vulkan_device::create(_physical_device, device_features);
s_current_device = _device;
}
{
_swap_chain = vulkan_swapchain::create(self);
_swap_chain = vulkan_swap_chain::create(self);
}
// command pool
@ -116,16 +122,42 @@ namespace dyn::core {
create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
dyn_vk_verify(vkCreateCommandPool(_device->get_handle(), &create_info, nullptr, &_command_pool));
VkCommandBufferAllocateInfo allocate_info{};
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;
dyn_vk_verify(vkAllocateCommandBuffers(_device->get_handle(), &allocate_info, &_command_buffer));
}
}
void vulkan_context::cmd_begin() {
_swap_chain->next_frame();
_render_command_queue.clear();
vkResetCommandBuffer(_command_buffer, 0);
VkCommandBufferBeginInfo begin_info{};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
dyn_vk_verify(vkBeginCommandBuffer(_command_buffer, &begin_info));
_swap_chain->prepare();
}
void vulkan_context::render_mesh_source(ref<mesh_source> mesh) {
}
void vulkan_context::cmd_end() {
_render_command_queue.execute();
vkCmdEndRenderPass(_command_buffer);
if (auto result = vkEndCommandBuffer(_command_buffer); result != VK_SUCCESS) {
DYN_ERROR_LOG("Failed to end command buffer. Error:", string_VkResult(result));
}
_swap_chain->present();
}
VkSurfaceKHR vulkan_context::get_surface() const {
@ -173,4 +205,26 @@ namespace dyn::core {
vkFreeCommandBuffers(_device->get_handle(), _command_pool, 1, &cmd_buffer);
}
void vulkan_context::destroy() {
vkDeviceWaitIdle(_device->get_handle());
DYN_DEBUG_LOG("vulkan_context::~vulkan_context()");
_cleanup_command_queue.execute();
vkFreeCommandBuffers(_device->get_handle(), _command_pool, 1, &_command_buffer);
vkDestroyCommandPool(_device->get_handle(), _command_pool, nullptr);
_swap_chain->destroy();
_device->destroy();
if (utils::vk_enable_validation_layers) {
utils::vkDestroyDebugUtilsMessengerEXT(_instance, _debug_messenger, nullptr);
}
vkDestroySurfaceKHR(_instance, _surface, nullptr);
vkDestroyInstance(_instance, nullptr);
}
}

View File

@ -8,12 +8,11 @@
#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"
#include "dyn/platform/vulkan/vulkan_swap_chain.h"
namespace dyn::core {
vulkan_physical_device::vulkan_physical_device(const ref<vulkan_context>& ctx) :
_ctx(ctx) {
auto instance = ref_as<vulkan_context>(_ctx)->get_instance();
vulkan_physical_device::vulkan_physical_device(const ref<vulkan_context>& ctx) {
auto instance = ref_as<vulkan_context>(ctx)->get_instance();
uint32_t device_count = 0;
vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
@ -79,7 +78,7 @@ namespace dyn::core {
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);
swap_chain_support_details details = vulkan_swap_chain::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!");
}
@ -120,6 +119,10 @@ namespace dyn::core {
score += 100;
}
if (features.logicOp == VK_TRUE) {
score += 100;
}
DYN_DEBUG_LOG("Device Name:", std::string_view{properties.deviceName});
DYN_DEBUG_LOG("Device Score:", score);
@ -197,10 +200,14 @@ namespace dyn::core {
}
vulkan_device::~vulkan_device() {
vkDestroyDevice(_device, nullptr);
}
ref<vulkan_device> vulkan_device::create(const std::shared_ptr<vulkan_physical_device> &device, const VkPhysicalDeviceFeatures& device_Features) {
return create_ref<vulkan_device>(device, device_Features);
}
void vulkan_device::destroy() {
vkDestroyDevice(_device, nullptr);
}
}

View File

@ -3,24 +3,98 @@
//
#include <dyn/platform/vulkan/vulkan_graphics_pipeline.h>
#include <dyn/platform/vulkan/vulkan_context.h>
namespace dyn::core {
vulkan_graphics_pipeline::vulkan_graphics_pipeline() {
VkPolygonMode get_vulkan_polygon_mode(PolygonMode mode) {
switch (mode) {
case PolygonMode::FILL:
return VK_POLYGON_MODE_FILL;
case PolygonMode::LINE:
return VK_POLYGON_MODE_LINE;
case PolygonMode::POINT:
return VK_POLYGON_MODE_POINT;
}
return VK_POLYGON_MODE_MAX_ENUM;
}
vulkan_graphics_pipeline::vulkan_graphics_pipeline(const ref<vulkan_context>& ctx) : _swap_chain(ctx->get_swap_chain()), _device(ctx->get_current_device()) {
_dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
_dynamic_state_ci.dynamicStateCount = static_cast<uint32_t>(g_vulkan_dynamic_states.size());
_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;
_input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
_input_assembly_ci.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
_input_assembly_ci.primitiveRestartEnable = VK_FALSE;
_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;
_viewport_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
_viewport_state_ci.viewportCount = 1;
_viewport_state_ci.pViewports = &_viewport;
_viewport_state_ci.scissorCount = 1;
_viewport_state_ci.pScissors = &_scissor;
_rasterization_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
_rasterization_state_ci.depthClampEnable = VK_FALSE;
_rasterization_state_ci.rasterizerDiscardEnable = VK_FALSE;
_rasterization_state_ci.polygonMode = VK_POLYGON_MODE_FILL;
_rasterization_state_ci.lineWidth = 1.0f;
_rasterization_state_ci.cullMode = VK_CULL_MODE_BACK_BIT;
_rasterization_state_ci.frontFace = VK_FRONT_FACE_CLOCKWISE;
_rasterization_state_ci.depthBiasEnable = VK_FALSE;
_multisample_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
_multisample_state_ci.sampleShadingEnable = VK_FALSE;
_multisample_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
_color_blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
_color_blend_state.blendEnable = VK_TRUE;
_color_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
_color_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
_color_blend_state.colorBlendOp = VK_BLEND_OP_ADD;
_color_blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
_color_blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
_color_blend_state.alphaBlendOp = VK_BLEND_OP_ADD;
_color_blend_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
_color_blend_state_ci.logicOpEnable = VK_TRUE;
_color_blend_state_ci.logicOp = VK_LOGIC_OP_COPY;
_color_blend_state_ci.attachmentCount = 1;
_color_blend_state_ci.pAttachments = &_color_blend_state;
_color_blend_state_ci.blendConstants[0] = 0.0f;
_color_blend_state_ci.blendConstants[1] = 0.0f;
_color_blend_state_ci.blendConstants[2] = 0.0f;
_color_blend_state_ci.blendConstants[3] = 0.0f;
_pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
}
vulkan_graphics_pipeline::~vulkan_graphics_pipeline() {
}
void vulkan_graphics_pipeline::set_polygon_mode(PolygonMode mode) {
DYN_DEBUG_LOG("Polygon mode set to:", cvt_string_polygon_mode(mode));
_rasterization_state_ci.polygonMode = get_vulkan_polygon_mode(mode);
}
void vulkan_graphics_pipeline::set_line_width(float line_width) {
_rasterization_state_ci.lineWidth = line_width;
}
void vulkan_graphics_pipeline::set_msaa_samples(MSAASamples samples) {
@ -36,8 +110,66 @@ namespace dyn::core {
}
void vulkan_graphics_pipeline::attach_shader(const ref<shader> &shader) {
_shaders.push_back(ref_as<vulkan_shader>(shader));
}
void vulkan_graphics_pipeline::build(const ref<render_context> &ctx) {
bool vulkan_graphics_pipeline::build() {
auto result = vkCreatePipelineLayout(_device->get_handle(), &_pipeline_layout_ci, nullptr, &_pipeline_layout);
if (result != VK_SUCCESS) {
DYN_ERROR_LOG("vulkan_graphics_pipeline failed to build at pipeline layout.");
return false;
}
VkGraphicsPipelineCreateInfo create_info{};
create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
std::vector<VkPipelineShaderStageCreateInfo> shader_stages;
for (const auto& shader : _shaders) {
shader_stages.push_back(shader->get_stage_info());
}
create_info.stageCount = static_cast<uint32_t>(shader_stages.size());
create_info.pStages = shader_stages.data();
create_info.pVertexInputState = &_vertex_input_ci;
create_info.pInputAssemblyState = &_input_assembly_ci;
create_info.pViewportState = &_viewport_state_ci;
create_info.pRasterizationState = &_rasterization_state_ci;
create_info.pMultisampleState = &_multisample_state_ci;
create_info.pColorBlendState = &_color_blend_state_ci;
create_info.pDepthStencilState = VK_NULL_HANDLE;
create_info.pColorBlendState = &_color_blend_state_ci;
create_info.pDynamicState = &_dynamic_state_ci;
create_info.layout = _pipeline_layout;
create_info.renderPass = _swap_chain->get_render_pass();
create_info.subpass = 0;
result = vkCreateGraphicsPipelines(_device->get_handle(), VK_NULL_HANDLE, 1, &create_info, nullptr, &_pipeline);
if (result != VK_SUCCESS) {
DYN_ERROR_LOG("vulkan_graphics_pipeline failed to create pipeline.");
return false;
}
get_current_context()->run_at_cleanup([=] {
this->destroy();
});
return true;
}
void vulkan_graphics_pipeline::bind() {
get_current_context()->submit([=] {
auto ctx = get_current_vulkan_context();
VK_CMD(BindPipeline, ctx, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipeline);
VK_CMD(SetViewport, ctx, 0, 1, &_viewport);
VK_CMD(SetScissor, ctx, 0, 1, &_scissor);
});
}
void vulkan_graphics_pipeline::destroy() {
auto handle = _device->get_handle();
vkDestroyPipeline(handle, _pipeline, nullptr);
vkDestroyPipelineLayout(handle, _pipeline_layout, nullptr);
}
}

View File

@ -14,9 +14,7 @@ namespace dyn::core {
: _binary(source.begin(), source.end()), _filename(filename), _type(type), _stage(vulkan_stage_from_type(type)) {}
vulkan_shader::~vulkan_shader() {
if (_shader != VK_NULL_HANDLE) {
vkDestroyShaderModule(_ctx->get_current_device()->get_handle(), _shader, nullptr);
}
}
VkShaderStageFlagBits vulkan_shader::vulkan_stage_from_type(ShaderType type) {
@ -34,16 +32,15 @@ namespace dyn::core {
}
void vulkan_shader::build(const ref<render_context> &ctx) {
_ctx = ref_as<vulkan_context>(ctx);
_device = ref_as<vulkan_context>(ctx)->get_current_device();
VkShaderModuleCreateInfo module_create_info = {};
module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
module_create_info.pNext = nullptr;
// times by four as thats the requirement for vulkan shaders
module_create_info.codeSize = _binary.size()*4;
module_create_info.codeSize = _binary.size() * sizeof(uint32_t);
module_create_info.pCode = static_cast<const uint32_t*>(_binary.data());
auto vk_res = vkCreateShaderModule(_ctx->get_current_device()->get_handle(), &module_create_info, nullptr, &_shader);
auto vk_res = vkCreateShaderModule(_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;
@ -55,5 +52,15 @@ namespace dyn::core {
_pipeline_ci.pName = "main";
_usable = true;
ctx->run_at_cleanup([=] {
this->destroy();
});
}
void vulkan_shader::destroy() {
if (_shader != VK_NULL_HANDLE) {
vkDestroyShaderModule(_device->get_handle(), _shader, nullptr);
}
}
}

View File

@ -0,0 +1,344 @@
//
// Created by nepge on 5/14/2025.
//
#include <dyn/platform/vulkan/vulkan_swap_chain.h>
#include "dyn/platform/vulkan/vulkan.h"
#include "dyn/platform/vulkan/vulkan_context.h"
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();
}
vulkan_swap_chain::~vulkan_swap_chain() {
}
void vulkan_swap_chain::prepare() {
get_current_context()->submit([=] {
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.renderArea.offset = {0, 0};
render_pass_info.renderArea.extent = _extent;
VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
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);
});
}
void vulkan_swap_chain::present() {
auto cmd_buffer = get_current_vulkan_context()->get_command_buffer();
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore wait_semaphores[] = {_image_available};
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = wait_semaphores;
submit_info.pWaitDstStageMask = wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &cmd_buffer;
submit_info.signalSemaphoreCount = 1;
VkSemaphore signal_semaphores[] = {_render_finished};
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) {
DYN_ERROR_LOG("Failed to submit queue. Error:", string_VkResult(result));
return;
}
VkPresentInfoKHR present_info{};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = signal_semaphores;
VkSwapchainKHR swap_chains[] = {_swap_chain};
present_info.swapchainCount = 1;
present_info.pSwapchains = swap_chains;
present_info.pImageIndices = &_current_frame;
vkQueuePresentKHR(_logical_device->get_present_queue(), &present_info);
}
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);
}
swap_chain_support_details vulkan_swap_chain::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_swap_chain::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_swap_chain::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_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);
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_swap_chain::build_swap_images() {
auto hogical_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);
temp_img_buf.resize(image_count);
vkGetSwapchainImagesKHR(hogical_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(hogical_device, &create_info, nullptr, &_swap_images[i].view));
}
}
void vulkan_swap_chain::create_render_pass() {
VkAttachmentDescription color_attachment{};
color_attachment.format = _image_format;
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachment_ref{};
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo render_pass_ci{};
render_pass_ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_ci.attachmentCount = 1;
render_pass_ci.pAttachments = &color_attachment;
render_pass_ci.subpassCount = 1;
render_pass_ci.pSubpasses = &subpass;
render_pass_ci.dependencyCount = 1;
render_pass_ci.pDependencies = &dependency;
dyn_vk_verify(vkCreateRenderPass(_logical_device->get_handle(), &render_pass_ci, nullptr, &_render_pass));
}
void vulkan_swap_chain::create_framebuffers() {
_framebuffers.resize(_swap_images.size());
for (uint32_t i = 0; i < _swap_images.size(); i++) {
VkImageView attachments[] = {_swap_images[i].view};
VkFramebufferCreateInfo framebuffer_ci{};
framebuffer_ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_ci.renderPass = _render_pass;
framebuffer_ci.attachmentCount = 1;
framebuffer_ci.pAttachments = attachments;
framebuffer_ci.width = _extent.width;
framebuffer_ci.height = _extent.height;
framebuffer_ci.layers = 1;
dyn_vk_verify(vkCreateFramebuffer(_logical_device->get_handle(), &framebuffer_ci, nullptr, &_framebuffers[i]));
}
}
void vulkan_swap_chain::destroy() {
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);
}
}
ref<vulkan_swap_chain> vulkan_swap_chain::create(const ref<vulkan_context> &ctx) {
return create_ref<vulkan_swap_chain>(ctx);
}
}

View File

@ -1,195 +0,0 @@
//
// 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);
}
}

View File

@ -0,0 +1,17 @@
//
// Created by nepge on 6/2/2025.
//
#include <dyn/renderer/graphics_pipeline.h>
#include <dyn/renderer/render_context.h>
#include "dyn/platform/vulkan/vulkan_graphics_pipeline.h"
namespace dyn::core {
ref<graphics_pipeline> graphics_pipeline::create(const ref<render_context> &context) {
DYN_PLATFORM_CREATE_BEGIN(render_context::get_api())
DYN_PLATFORM_CREATE_VK(vulkan_graphics_pipeline, ref_as<vulkan_context>(context));
DYN_PLATFORM_CREATE_END(graphics_pipeline)
}
}

View File

@ -8,6 +8,13 @@
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) {
_render_command_queue.submit(func);
}
@ -20,6 +27,10 @@ namespace dyn::core {
return s_api;
}
void render_context::run_at_cleanup(cleanup_command cmd) {
_cleanup_command_queue.submit(cmd);
}
ref<render_context> render_context::create(const ref<window>& window) {
DYN_PLATFORM_CREATE_BEGIN(get_api())
DYN_PLATFORM_CREATE_VK(vulkan_context, window);