aboutsummaryrefslogtreecommitdiff
path: root/src/engine.cpp
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2025-12-24 01:38:45 +0100
committervimene <vincent.menegaux@gmail.com>2025-12-24 01:38:45 +0100
commit00370c21f107ff2a320fbdef170e24a8b5cda92a (patch)
tree04ff3e32e88e0acdfe0783947a0f2510d0520a1f /src/engine.cpp
parent236b6a3160cd3d8c217a966aaa2795c779c13a91 (diff)
downloadengine-00370c21f107ff2a320fbdef170e24a8b5cda92a.tar.gz
added uniform buffers, small tweaks
- fixed aspect ratio being the inverse of what it's supposed to be - use std::chrono::high_resolution_clock instead of system_clock - convert ellapsed time to float representing seconds - renamed engine::math::Matrix4::projection to perspective - use [0,1] instead of [-1,1] for the range of z values, to be aligned with vulkan - added engine::math::Matrix4::{look_at,transpose}
Diffstat (limited to 'src/engine.cpp')
-rw-r--r--src/engine.cpp158
1 files changed, 147 insertions, 11 deletions
diff --git a/src/engine.cpp b/src/engine.cpp
index 84e62a5..142a8e1 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -161,7 +161,7 @@ static void scene_main(Renderer<FrameBuffer>& renderer, const Matrix4& final_tra
while (cont) {
renderer.clear();
auto pre_final_mat = final_transform_mat
- * scene.camera.to_mat4(static_cast<float>(renderer.height()) / static_cast<float>(renderer.width()), .5f, 12.f);
+ * scene.camera.to_mat4(static_cast<float>(renderer.width()) / static_cast<float>(renderer.height()), .5f, 12.f);
for (const auto& obj : scene.objs) {
auto obj_mat = obj.transform.to_mat4();
auto final_mat = pre_final_mat * obj_mat;
@@ -297,8 +297,8 @@ static int main_term() {
}
#endif
-#define SCREEN_WIDTH 640
-#define SCREEN_HEIGHT 480
+#define SCREEN_WIDTH 800
+#define SCREEN_HEIGHT 600
static const std::vector<const char*> validation_layers = {
"VK_LAYER_KHRONOS_validation",
@@ -1143,6 +1143,31 @@ static int main_graphical() {
recreate_swapchain();
+ // create descriptor set layout
+ auto descriptor_set_layout = [&]() {
+ VkDescriptorSetLayoutBinding descriptor_set_layout_binding {
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
+ .pImmutableSamplers = {},
+ };
+ VkDescriptorSetLayoutCreateInfo descriptor_set_layout_ci {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .bindingCount = 1,
+ .pBindings = &descriptor_set_layout_binding,
+ };
+ VkDescriptorSetLayout descriptor_set_layout;
+ if (VkResult res = vkCreateDescriptorSetLayout(device, &descriptor_set_layout_ci, nullptr, &descriptor_set_layout); res != VK_SUCCESS) {
+ std::cerr << "failed to create descriptor set layout, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return descriptor_set_layout;
+ }();
+
+ // create pipeline
auto [pl_layout, graphics_pl] = [&]() {
// reading shader file
auto shader_module = [&]() {
@@ -1248,7 +1273,7 @@ static int main_graphical() {
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_BACK_BIT,
- .frontFace = VK_FRONT_FACE_CLOCKWISE,
+ .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = {},
.depthBiasClamp = {},
@@ -1299,8 +1324,8 @@ static int main_graphical() {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = {},
- .setLayoutCount = 0,
- .pSetLayouts = {},
+ .setLayoutCount = 1,
+ .pSetLayouts = &descriptor_set_layout,
.pushConstantRangeCount = 0,
.pPushConstantRanges = {},
};
@@ -1446,6 +1471,91 @@ static int main_graphical() {
return std::tuple { index_buf, index_buf_device_mem };
}();
+ // create uniform buffers
+ auto [uniform_bufs, uniform_buf_device_mems, uniform_buf_mems] = [&]() {
+ constexpr VkDeviceSize uniform_buf_size = sizeof(engine::vk::UniformBufferObject);
+ std::array<VkBuffer, max_frames_in_flight> uniform_bufs;
+ std::array<VkDeviceMemory, max_frames_in_flight> uniform_buf_device_mems;
+ std::array<void*, max_frames_in_flight> uniform_buf_mems;
+ for (size_t i = 0; i < max_frames_in_flight; i++) {
+ std::tie(uniform_bufs[i], uniform_buf_device_mems[i]) = create_buf(physical_device, device, uniform_buf_size,
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+ if (VkResult res = vkMapMemory(device, uniform_buf_device_mems[i], 0, uniform_buf_size, 0, &uniform_buf_mems[i]); res != VK_SUCCESS) {
+ std::cerr << "failed to map uniform buffer #" << i << " memory, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ return std::tuple { uniform_bufs, uniform_buf_device_mems, uniform_buf_mems };
+ }();
+
+ // create descriptor pool
+ auto descriptor_pool = [&]() {
+ VkDescriptorPoolSize descriptor_pool_size {
+ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .descriptorCount = max_frames_in_flight,
+ };
+ VkDescriptorPoolCreateInfo descriptor_pool_ci {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .pNext = nullptr,
+ // TODO: right now we do not touch descriptor sets after they've been created, so
+ // setting VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT is useless
+ .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
+ .maxSets = max_frames_in_flight,
+ .poolSizeCount = 1,
+ .pPoolSizes = &descriptor_pool_size,
+ };
+ VkDescriptorPool descriptor_pool;
+ if (VkResult res = vkCreateDescriptorPool(device, &descriptor_pool_ci, nullptr, &descriptor_pool); res != VK_SUCCESS) {
+ std::cerr << "failed to create descriptor pool, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return descriptor_pool;
+ }();
+
+ // create descriptor sets
+ auto descriptor_sets = [&]() {
+ std::array<VkDescriptorSetLayout, max_frames_in_flight> layouts;
+ layouts.fill(descriptor_set_layout);
+ VkDescriptorSetAllocateInfo descriptor_set_ai {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .pNext = nullptr,
+ .descriptorPool = descriptor_pool,
+ .descriptorSetCount = static_cast<uint32_t>(layouts.size()),
+ .pSetLayouts = layouts.data(),
+ };
+ std::array<VkDescriptorSet, max_frames_in_flight> descriptor_sets;
+ if (VkResult res = vkAllocateDescriptorSets(device, &descriptor_set_ai, descriptor_sets.data()); res != VK_SUCCESS) {
+ std::cerr << "failed to allocate descriptor sets, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ std::array<VkDescriptorBufferInfo, max_frames_in_flight> descriptor_buffer_infos;
+ std::array<VkWriteDescriptorSet, max_frames_in_flight> write_descriptor_sets;
+ for (size_t i = 0; i < max_frames_in_flight; i++) {
+ descriptor_buffer_infos[i] = VkDescriptorBufferInfo {
+ .buffer = uniform_bufs[i],
+ .offset = 0,
+ .range = sizeof(engine::vk::UniformBufferObject),
+ };
+ write_descriptor_sets[i] = VkWriteDescriptorSet {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .pNext = nullptr,
+ .dstSet = descriptor_sets[i],
+ .dstBinding = 0,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .pImageInfo = {},
+ .pBufferInfo = &descriptor_buffer_infos[i],
+ .pTexelBufferView = {},
+ };
+ }
+ // TODO: maybe we should call this function for each descriptor sets, which I would find
+ // weird by the fact that it takes an array as an argument, but IDK
+ vkUpdateDescriptorSets(device, static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
+ return descriptor_sets;
+ }();
+
// create command buffers
auto cmd_bufs = [&]() {
VkCommandBufferAllocateInfo cmd_buf_ai {
@@ -1548,7 +1658,8 @@ static int main_graphical() {
}
std::cout << " ]" << std::endl;
- auto last_time = std::chrono::system_clock::now();
+ auto start_time = std::chrono::high_resolution_clock::now();
+ auto last_time = start_time;
uint32_t frame_idx = 0;
@@ -1556,12 +1667,13 @@ static int main_graphical() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
- auto cur_time = std::chrono::system_clock::now();
- auto ellapsed_time = cur_time - last_time;
+ auto cur_time = std::chrono::high_resolution_clock::now();
+ float ellapsed_time = std::chrono::duration<float, std::chrono::seconds::period>(cur_time - last_time).count();
last_time = cur_time;
+ float time = std::chrono::duration<float, std::chrono::seconds::period>(cur_time - start_time).count();
if (show_fps)
- std::cout << "\rfps: " << (1'000'000'000. / static_cast<double>(ellapsed_time.count()));
+ std::cout << "\rfps: " << (1.f / ellapsed_time);
if (VkResult res = vkWaitForFences(device, 1, &fences_in_flight[frame_idx], VK_TRUE, std::numeric_limits<uint64_t>::max()); res != VK_SUCCESS) {
std::cerr << "failed to wait for draw fence, error code: " << string_VkResult(res) << std::endl;
@@ -1585,6 +1697,21 @@ static int main_graphical() {
}
}
+ // update uniform buffer
+ {
+ // TODO: should use push constants
+ // TODO: we shouldn't have to transpose, we could change the Matrix4 implementation to
+ // make all operation on transpose, which wouldn't change anything for the software
+ // renderer
+ engine::vk::UniformBufferObject ubo {
+ .model = Matrix4::rot_z(time * PI / 2.f).transpose(),
+ .view = Matrix4::look_at(Vector3(2.f, 2.f, 2.f), Vector3(0.f, 0.f, 0.f), Vector3(0.f, 0.f, 1.f)).transpose(),
+ .proj = Matrix4::perspective(PI / 4.f, static_cast<float>(swapchain_extent.width) / static_cast<float>(swapchain_extent.height), .1f, 10.f).transpose(),
+ };
+ memcpy(uniform_buf_mems[frame_idx], &ubo, sizeof(engine::vk::UniformBufferObject));
+ }
+
+ // implicit command buffer reset
// record command buffer
{
VkCommandBufferBeginInfo cmd_buf_bi {
@@ -1665,6 +1792,7 @@ static int main_graphical() {
vkCmdBindVertexBuffers(cmd_bufs[frame_idx], 0, 1, &vertex_buf, &vertex_buf_offset);
vkCmdBindIndexBuffer(cmd_bufs[frame_idx], index_buf, 0, VK_INDEX_TYPE_UINT16);
+ vkCmdBindDescriptorSets(cmd_bufs[frame_idx], VK_PIPELINE_BIND_POINT_GRAPHICS, pl_layout, 0, 1, &descriptor_sets[frame_idx], 0, nullptr);
vkCmdDrawIndexed(cmd_bufs[frame_idx], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
vkCmdEndRendering(cmd_bufs[frame_idx]);
@@ -1742,14 +1870,22 @@ static int main_graphical() {
vkDestroySemaphore(device, *it_sem, nullptr);
for (auto it_sem = sems_present_complete.rbegin(); it_sem != sems_present_complete.rend(); ++it_sem)
vkDestroySemaphore(device, *it_sem, nullptr);
+ vkDestroyDescriptorPool(device, descriptor_pool, nullptr);
+ for (size_t i = max_frames_in_flight; i > 0; i--) {
+ vkUnmapMemory(device, uniform_buf_device_mems[i - 1]);
+ vkFreeMemory(device, uniform_buf_device_mems[i - 1], nullptr);
+ vkDestroyBuffer(device, uniform_bufs[i - 1], nullptr);
+ }
vkFreeMemory(device, index_buf_device_mem, nullptr);
vkDestroyBuffer(device, index_buf, nullptr);
vkFreeMemory(device, vertex_buf_device_mem, nullptr);
vkDestroyBuffer(device, vertex_buf, nullptr);
// no need to free cmd_buf because destroying the command pool will
vkDestroyCommandPool(device, cmd_pool, nullptr);
- vkDestroyPipeline(device, graphics_pl, nullptr);
+ // no need to free descriptor_sets because destroying the descriptor pool will
+ vkDestroyDescriptorSetLayout(device, descriptor_set_layout, nullptr);
vkDestroyPipelineLayout(device, pl_layout, nullptr);
+ vkDestroyPipeline(device, graphics_pl, nullptr);
destroy_swapchain();
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyDevice(device, nullptr);