diff options
| -rw-r--r-- | src/engine.cpp | 158 | ||||
| -rw-r--r-- | src/math/mat4.hpp | 28 | ||||
| -rw-r--r-- | src/o3d/camera.hpp | 2 | ||||
| -rw-r--r-- | src/renderer.cpp | 2 | ||||
| -rw-r--r-- | src/shaders/shader.slang | 9 | ||||
| -rw-r--r-- | src/vulkan_utils.hpp | 6 |
6 files changed, 188 insertions, 17 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); diff --git a/src/math/mat4.hpp b/src/math/mat4.hpp index dfc5a75..1b01e26 100644 --- a/src/math/mat4.hpp +++ b/src/math/mat4.hpp @@ -77,16 +77,29 @@ struct Matrix4 { }; } - static constexpr Matrix4 projection(float fov, float aspect_ratio, float min_z, float max_z) { + static constexpr Matrix4 perspective(float fov, float aspect_ratio, float min_z, float max_z) { float inv_tan = 1.f / std::tan(fov / 2.f); return {{ - aspect_ratio * inv_tan, 0.f, 0.f, 0.f, + inv_tan / aspect_ratio, 0.f, 0.f, 0.f, 0.f, -inv_tan, 0.f, 0.f, - 0.f, 0.f, -2.f / (max_z - min_z), -(max_z + min_z) / (max_z - min_z), + 0.f, 0.f, -1.f / (max_z - min_z), -min_z / (max_z - min_z), 0.f, 0.f, -1.f, 0.f, }}; } + // TODO: improve + static constexpr Matrix4 look_at(const Vector3& eye, const Vector3& center, const Vector3& up) { + Vector3 new_z = -(center - eye).normalize(); + Vector3 new_x = up.cross(new_z).normalize(); + Vector3 new_y = new_z.cross(new_x); + return Matrix4 { + new_x.x, new_x.y, new_x.z, 0.f, + new_y.x, new_y.y, new_y.z, 0.f, + new_z.x, new_z.y, new_z.z, 0.f, + 0.f, 0.f, 0.f, 1.f, + } * Matrix4::translate(-eye); + } + // TODO: should be Quaternion::to_mat4 static constexpr Matrix4 from_quaternion(const Quaternion& q) { return { @@ -154,6 +167,15 @@ struct Matrix4 { { values[ 3], values[ 7], values[11], values[15] }, }}; } + + constexpr Matrix4 transpose() const & { + return { + values[ 0], values[ 4], values[ 8], values[12], + values[ 1], values[ 5], values[ 9], values[13], + values[ 2], values[ 6], values[10], values[14], + values[ 3], values[ 7], values[11], values[15], + }; + } }; constexpr Matrix4 operator*(float fac, const Matrix4& m) { diff --git a/src/o3d/camera.hpp b/src/o3d/camera.hpp index 485c825..b3588fb 100644 --- a/src/o3d/camera.hpp +++ b/src/o3d/camera.hpp @@ -15,7 +15,7 @@ struct Camera { Transform transform; constexpr Matrix4 to_mat4(float aspect_ratio, float min_z, float max_z) const & { - return Matrix4::projection(fov, aspect_ratio, min_z, max_z) * transform.to_inverse_mat4(); + return Matrix4::perspective(fov, aspect_ratio, min_z, max_z) * transform.to_inverse_mat4(); } }; diff --git a/src/renderer.cpp b/src/renderer.cpp index 5321a88..73b31f7 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -41,7 +41,7 @@ enum TriangleSide { top, bottom }; template<typename FrameBuffer> void Renderer<FrameBuffer>::draw_triangle(const Triangle& triangle) { Vector2 fdim_over_2 = Vector2{static_cast<float>(fb.width()), static_cast<float>(fb.height())} / 2.f; - for (const auto& t1 : triangle.to_derived().crop_z_out(-1.f, 1.f)) { + for (const auto& t1 : triangle.to_derived().crop_z_out(0.f, 1.f)) { for (auto t2 : t1.div_by_w().perspective_crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { auto& v1 = t2.derived_vertex1.vertex; auto& v2 = t2.derived_vertex2.vertex; diff --git a/src/shaders/shader.slang b/src/shaders/shader.slang index 37041c6..24d9946 100644 --- a/src/shaders/shader.slang +++ b/src/shaders/shader.slang @@ -3,6 +3,12 @@ struct VertexInput { float3 col; }; +struct UniformBuffer { + float4x4 model, view, proj; +}; + +ConstantBuffer<UniformBuffer> ubo; + struct VertexOutput { float4 pos : SV_Position; float3 col; @@ -11,7 +17,8 @@ struct VertexOutput { [shader("vertex")] VertexOutput vert_main(VertexInput vi) { VertexOutput vo; - vo.pos = float4(vi.pos, 0., 1.); + // vo.pos = float4(vi.pos, 0., 1.); + vo.pos = mul(ubo.proj, mul(ubo.view, mul(ubo.model, float4(vi.pos, 0., 1.)))); vo.col = vi.col; return vo; } diff --git a/src/vulkan_utils.hpp b/src/vulkan_utils.hpp index dd57afc..1efc73e 100644 --- a/src/vulkan_utils.hpp +++ b/src/vulkan_utils.hpp @@ -9,6 +9,7 @@ #include <GLFW/glfw3.h> #include "math/vector.hpp" +#include "math/mat4.hpp" #include "o3d/vertex.hpp" namespace engine::vk { @@ -59,6 +60,11 @@ struct Vertex { engine::math::Vector3 col; }; +// TODO: move to a better place. Also, see TODOs for struct Vertex +struct UniformBufferObject { + alignas(16) engine::math::Matrix4 model, view, proj; +}; + } #endif // VULKAN_UTILS_HPP |
