diff options
| -rw-r--r-- | src/engine.cpp | 1095 | ||||
| -rw-r--r-- | src/o3d/mesh.cpp | 40 | ||||
| -rw-r--r-- | src/o3d/mesh.hpp | 21 | ||||
| -rw-r--r-- | src/obj_parser.cpp | 8 | ||||
| -rw-r--r-- | src/shaders/shader.slang | 27 | ||||
| -rw-r--r-- | src/vulkan_utils.hpp | 16 |
6 files changed, 684 insertions, 523 deletions
diff --git a/src/engine.cpp b/src/engine.cpp index 0cf3266..c556592 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -93,94 +93,39 @@ static void print_usage(std::ostream& output_stream) { [[noreturn]] static void usage_error_exit() { print_usage(std::cerr); - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } extern Camera* camera; Camera* camera; -template<typename FrameBuffer, typename UpdateFrameFn> -static void scene_main(Renderer<FrameBuffer>& renderer, const Matrix4& final_transform_mat, UpdateFrameFn update_frame) { - bool cont = true; - Scene scene{ - {90.f * PI / 180.f, {{0.f, 1.8f, 7.f}, Quaternion::one(), {1.f, 1.f, 1.f}}}, - { -#if GAME == GAME_PLANE - { - Mesh::plane(2.f, 2.f), - { - Vector3(0.f, 0.f, 0.f), - Quaternion::one(), - Vector3(1.f, 1.f, 1.f), - } - }, -#elif GAME == GAME_SUZANNE - { - engine::parse_object(DATADIR "/assets/suzanne.obj"), - { - Vector3(0.f, 0.f, 0.f), - Quaternion::one(), - Vector3(1.f, 1.f, 1.f), - } - }, -#elif GAME == GAME_PHYSICS - { - Mesh::plane(10.f, 10.f), - { - Vector3(0.f, 0.f, 0.f), - Quaternion::one(), - Vector3(1.f, 1.f, 1.f), - } - }, - { - engine::parse_object(DATADIR "/assets/suzanne.obj"), - { - Vector3(0.f, 1.f, 0.f), - Quaternion::one(), - Vector3(1.f, 1.f, 1.f), - } - }, -#endif - } - }; +template<typename UpdateSurfaceSizeFn, typename PollEventsFn, typename RenderAndPresentFrameFn> +static void scene_main(const Matrix4& final_transform_mat, Scene& scene, + UpdateSurfaceSizeFn update_surface_size, PollEventsFn poll_events, RenderAndPresentFrameFn render_and_present_frame) { + constexpr float movement_speed = 10.f; float rx = 0.f, ry = 0.f; - Keyboard kb{[&](KeyboardKey key) { - (void) key; - }, [&](KeyboardKey key) { - (void) key; - }}; - Mouse mouse{[&](Vector2 rel) { + Keyboard kb { [&](KeyboardKey /* key */) {}, [&](KeyboardKey /* key */) {} }; + Mouse mouse { [&](Vector2 rel) { rx += -rel.y; ry += -rel.x; if (rx < -PI / 2.f) rx = -PI / 2.f; if (rx > PI / 2.f) rx = PI / 2.f; - scene.camera.transform.rot = Quaternion::euler_zxy(rx, ry, 0.f); - }}; + } }; camera = &scene.camera; - while (cont) { - renderer.clear(); - auto pre_final_mat = final_transform_mat - * 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; - const auto& mesh = obj.mesh; - std::vector<Vector4> vertices; - std::vector<VertexData> vertices_data; - for (const auto& vertex : mesh.vertices) { - vertices.push_back(final_mat * vertex); - vertices_data.push_back(VertexData((obj_mat * vertex).xyz())); - } - for (const auto& triangle_indices : mesh.indices) { - [&]<std::size_t... j>(std::integer_sequence<std::size_t, j...>) { - renderer.draw_triangle({{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...}); - }(std::make_integer_sequence<std::size_t, 3>()); - } - } - cont = update_frame(scene, kb, mouse); + auto start_time = std::chrono::high_resolution_clock::now(); + auto last_time = start_time; + + for (;;) { + 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 (!poll_events(kb, mouse)) + break; Vector3 movement(0.f, 0.f, 0.f); if (kb.is_down(KeyboardKey::fw)) movement.z += -1.f; @@ -188,16 +133,67 @@ static void scene_main(Renderer<FrameBuffer>& renderer, const Matrix4& final_tra if (kb.is_down(KeyboardKey::bw)) movement.z += +1.f; if (kb.is_down(KeyboardKey::key_right)) movement.x += +1.f; if (kb.is_down(KeyboardKey::fw) || kb.is_down(KeyboardKey::key_left) - || kb.is_down(KeyboardKey::bw) || kb.is_down(KeyboardKey::key_right)) movement.normalize(); - scene.camera.transform.loc += movement.rot(Quaternion::rot_y(ry)) * .05f; + || kb.is_down(KeyboardKey::bw) || kb.is_down(KeyboardKey::key_right)) + movement.normalize(); + scene.camera.transform.loc += movement.rot(Quaternion::rot_y(ry)) * movement_speed * ellapsed_time; + scene.camera.transform.rot = Quaternion::euler_zxy(rx, ry, 0.f); scene.camera.fov = (kb.is_down(KeyboardKey::zoom) ? 40.f : 80.f) * PI / 180.f; + + auto [surface_width, surface_height] = update_surface_size(); + + // TODO: we no longer use Camera.to_mat4(...), I don't know if we should remove it or not. + // It multiplies projection and view matrices, but right now we do the multiplication in the + // fragment shader. The thing is, having the camera decide which projection mode to chose is + // better, because it allows us to change projection settings just by changing the camera + auto proj_mat = final_transform_mat + * Matrix4::perspective(scene.camera.fov, static_cast<float>(surface_width) / static_cast<float>(surface_height), .5f, 12.f); + + render_and_present_frame(scene.camera.transform.to_inverse_mat4(), proj_mat, time, ellapsed_time); } } +// TODO: find better name +template<typename FrameBuffer, typename PollEventsFn, typename UpdateRendererSizeFn, typename PresentFrameFn> +static void render_software(Renderer<FrameBuffer>& renderer, const Matrix4& final_transform_mat, Scene& scene, + PollEventsFn poll_events, UpdateRendererSizeFn update_renderer_size, PresentFrameFn present_frame) { + scene_main(final_transform_mat, scene, + // update_surface_size + [&]() { + update_renderer_size(); + return std::tuple { renderer.width(), renderer.height() }; + }, + + poll_events, + + // render_and_present_frame + [&](const Matrix4& view_mat, const Matrix4& proj_mat, float /* time */, float /* ellapsed_time */) { + auto proj_view_mat = proj_mat * view_mat; + renderer.clear(); + for (const auto& obj : scene.objs) { + auto model_mat = obj.transform.to_mat4(); + auto final_mat = proj_view_mat * model_mat; + const auto& mesh = obj.mesh; + std::vector<Vector4> vertices; + std::vector<VertexData> vertices_data; + for (const auto& vertex : mesh.vertices) { + vertices.push_back(final_mat * Vector4 { vertex, 1.f }); + vertices_data.push_back(VertexData((model_mat * vertex).xyz())); + } + for (const auto& triangle_indices : mesh.indices) { + [&]<std::size_t... j>(std::integer_sequence<std::size_t, j...>) { + renderer.draw_triangle({{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...}); + }(std::make_integer_sequence<std::size_t, 3>()); + } + } + present_frame(); + } + ); +} + #ifdef HAVE_NCURSES #define MKEY_ESC 27 -static int main_term() { +static int main_term(Scene& scene) { // init std::setlocale(LC_ALL, ""); initscr(); @@ -208,15 +204,15 @@ static int main_term() { set_escdelay(0); curs_set(0); - int w, h; - getmaxyx(stdscr, h, w); - Renderer<CharacterFrameBuffer> renderer{CharacterFrameBuffer{static_cast<unsigned int>(w), static_cast<unsigned int>(h)}}; - - scene_main(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), - [&](Scene& scene, auto& kb, auto& mouse) { - (void) scene; - mvaddnstr(0, 0, renderer.fb.chars(), renderer.width() * renderer.height()); + auto renderer = [&]() { + int w, h; + getmaxyx(stdscr, h, w); + return Renderer<CharacterFrameBuffer> { CharacterFrameBuffer { static_cast<unsigned int>(w), static_cast<unsigned int>(h) } }; + }(); + render_software(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), scene, + // poll_events + [&](auto& kb, auto& mouse) { bool cont = true; std::optional<KeyboardKey> key; std::optional<Vector2> rel; @@ -285,10 +281,19 @@ static int main_term() { if (rel) mouse.mouse_motion_event(*rel); + return cont; + }, + + // update_renderer_size + [&]() { + int w, h; getmaxyx(stdscr, h, w); renderer.resize(static_cast<unsigned int>(w), static_cast<unsigned int>(h)); + }, - return cont; + // present_frame + [&]() { + mvaddnstr(0, 0, renderer.fb.chars(), renderer.width() * renderer.height()); } ); @@ -337,12 +342,12 @@ static bool check_validation_layer_support() { uint32_t avail_layers_count; if (VkResult res = vkEnumerateInstanceLayerProperties(&avail_layers_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to enumerate instance layer properties, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::vector<VkLayerProperties> avail_layers(avail_layers_count); if (VkResult res = vkEnumerateInstanceLayerProperties(&avail_layers_count, avail_layers.data()); res != VK_SUCCESS) { std::cerr << "failed to enumerate instance layer properties, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return avail_layers; }(); @@ -397,7 +402,7 @@ static std::tuple<std::optional<uint32_t>, std::optional<uint32_t>> find_queue_f VkBool32 is_presentation_queue; if (VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &is_presentation_queue); res != VK_SUCCESS) { std::cerr << "failed to check if queue family supports surface, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } if (is_graphics_queue && is_presentation_queue) { graphics_queue_family_index = i; @@ -430,7 +435,7 @@ static std::optional<VkFormat> find_format(VkPhysicalDevice physical_device, con break; default: std::cerr << "tiling not supported: " << string_VkImageTiling(tiling) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } return {}; @@ -458,7 +463,7 @@ static uint32_t find_mem_type(VkPhysicalDevice physical_device, uint32_t type_fi // not found // TODO: improve by being explicit about what are requirements std::cerr << "cannot find memory type matching requirements" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } static std::tuple<VkBuffer, VkDeviceMemory> @@ -477,7 +482,7 @@ create_buf(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size, VkBuffer buf; if (VkResult res = vkCreateBuffer(device, &buf_ci, nullptr, &buf); res != VK_SUCCESS) { std::cerr << "failed to create buffer, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return buf; }(); @@ -495,14 +500,14 @@ create_buf(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size, VkDeviceMemory buf_device_mem; if (VkResult res = vkAllocateMemory(device, &buf_mem_ai, nullptr, &buf_device_mem); res != VK_SUCCESS) { std::cerr << "failed to allocate buffer memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return buf_device_mem; }(); if (VkResult res = vkBindBufferMemory(device, buf, buf_device_mem, 0); res != VK_SUCCESS) { std::cerr << "failed to bind buffer memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return { buf, buf_device_mem }; } @@ -519,7 +524,7 @@ static VkCommandBuffer begin_single_time_cmds(VkDevice device, VkCommandPool cmd VkCommandBuffer cmd_buf; if (VkResult res = vkAllocateCommandBuffers(device, &cmd_buf_ai, &cmd_buf); res != VK_SUCCESS) { std::cerr << "failed to allocate command buffer, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return cmd_buf; }(); @@ -532,7 +537,7 @@ static VkCommandBuffer begin_single_time_cmds(VkDevice device, VkCommandPool cmd }; if (VkResult res = vkBeginCommandBuffer(cmd_buf, &cmd_buf_bi); res != VK_SUCCESS) { std::cerr << "failed to begin command buffer, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } return cmd_buf; @@ -541,7 +546,7 @@ static VkCommandBuffer begin_single_time_cmds(VkDevice device, VkCommandPool cmd static void end_single_time_cmds(VkQueue queue, VkDevice device, VkCommandPool cmd_pool, VkCommandBuffer cmd_buf) { if (VkResult res = vkEndCommandBuffer(cmd_buf); res != VK_SUCCESS) { std::cerr << "failed to end command buffer, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } { VkSubmitInfo submit_info { @@ -559,12 +564,12 @@ static void end_single_time_cmds(VkQueue queue, VkDevice device, VkCommandPool c // submitting and waiting for them to finish separatly if (VkResult res = vkQueueSubmit(queue, 1, &submit_info, nullptr); res != VK_SUCCESS) { std::cerr << "failed to submit command buffer to queue, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } if (VkResult res = vkQueueWaitIdle(queue); res != VK_SUCCESS) { std::cerr << "failed to wait idle for graphics queue, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } vkFreeCommandBuffers(device, cmd_pool, 1, &cmd_buf); } @@ -620,7 +625,7 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui VkImage texture_img; if (VkResult res = vkCreateImage(device, &img_ci, nullptr, &texture_img); res != VK_SUCCESS) { std::cerr << "failed to create image, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return texture_img; }(); @@ -637,14 +642,14 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui VkDeviceMemory texture_img_device_mem; if (VkResult res = vkAllocateMemory(device, &mem_ai, nullptr, &texture_img_device_mem); res != VK_SUCCESS) { std::cerr << "failed to allocate texture image memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return texture_img_device_mem; }(); if (VkResult res = vkBindImageMemory(device, texture_img, texture_img_device_mem, 0); res != VK_SUCCESS) { std::cerr << "failed to bind texture image to memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return std::tuple { texture_img, texture_img_device_mem }; @@ -674,7 +679,7 @@ create_depth_resources(VkPhysicalDevice physical_device, VkDevice device, VkExte VkImageView depth_img_view; if (VkResult res = vkCreateImageView(device, &img_view_ci, nullptr, &depth_img_view); res != VK_SUCCESS) { std::cerr << "failed to create depth image view, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return depth_img_view; }(); @@ -725,7 +730,7 @@ static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img, vkCmdPipelineBarrier2(cmd_buf, &dependency_info); } -static int main_graphical() { +static int main_graphical(Scene& scene) { // init window glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -734,6 +739,8 @@ static int main_graphical() { GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Engine", nullptr, nullptr); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + // TODO: might have to improve, because if in the future we need more values in a glfw callback, // this approch wouldn't work glfwSetWindowUserPointer(window, &fb_resized); @@ -742,24 +749,15 @@ static int main_graphical() { *reinterpret_cast<bool*>(glfwGetWindowUserPointer(window)) = true; }); - // TODO: verify alignment requirements - // TODO: maybe move these definitions? - const std::array vertices { - engine::vk::Vertex { { -.5f, -.5f, 0.f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } }, - engine::vk::Vertex { { .5f, -.5f, 0.f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } }, - engine::vk::Vertex { { .5f, .5f, 0.f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } }, - engine::vk::Vertex { { -.5f, .5f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, - - engine::vk::Vertex { { -.5f, -.5f, -.5f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } }, - engine::vk::Vertex { { .5f, -.5f, -.5f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } }, - engine::vk::Vertex { { .5f, .5f, -.5f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } }, - engine::vk::Vertex { { -.5f, .5f, -.5f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, - }; - - const std::array<uint16_t, 12> indices { - 0, 1, 2, 2, 3, 0, - 4, 5, 6, 6, 7, 4, - }; + // TODO: improve this. This is an ugly workaround for vulkan, see comment in o3d/mesh.hpp in + // linearize_indices() declaration + std::vector<std::vector<engine::vk::Vertex>> meshes_vertices; + std::vector<std::vector<uint16_t>> meshes_indices; + for (const auto& obj : scene.objs) { + const auto [vertices, indices] = obj.mesh.linearize_indices(); + meshes_vertices.push_back(std::move(vertices)); + meshes_indices.push_back(std::move(indices)); + } // init Vulkan std::cout << "Vulkan loader version: " << engine::vk::api { VK_HEADER_VERSION_COMPLETE } << std::endl; @@ -767,7 +765,7 @@ static int main_graphical() { // init Vulkan - create instance if (enable_validation_layers && !check_validation_layer_support()) { std::cerr << "validation layers requested, but not available!" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } auto instance = [&]() { @@ -797,12 +795,12 @@ static int main_graphical() { uint32_t avail_instance_exts_count; if (VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &avail_instance_exts_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to enumerate instance extension properties, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::vector<VkExtensionProperties> avail_instance_exts(avail_instance_exts_count); if (VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &avail_instance_exts_count, avail_instance_exts.data()); res != VK_SUCCESS) { std::cerr << "failed to enumerate instance extension properties, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return avail_instance_exts; }(); @@ -837,7 +835,7 @@ static int main_graphical() { VkInstance instance; if (VkResult res = vkCreateInstance(&instance_ci, nullptr, &instance); res != VK_SUCCESS) { std::cerr << "failed to create instance, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return instance; }(); @@ -850,7 +848,7 @@ static int main_graphical() { auto create_debug_messenger = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (!create_debug_messenger) { std::cerr << "failed to set up debug messenger!" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } create_debug_messenger(instance, &msger_ci, nullptr, &debug_messenger); } @@ -860,7 +858,7 @@ static int main_graphical() { VkSurfaceKHR surface; if (VkResult res = glfwCreateWindowSurface(instance, window, nullptr, &surface); res != VK_SUCCESS) { std::cerr << "failed to create window surface, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return surface; }(); @@ -873,19 +871,19 @@ static int main_graphical() { uint32_t avail_physical_devices_count; if (VkResult res = vkEnumeratePhysicalDevices(instance, &avail_physical_devices_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to enumerate physical devices, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::vector<VkPhysicalDevice> avail_physical_devices(avail_physical_devices_count); if (VkResult res = vkEnumeratePhysicalDevices(instance, &avail_physical_devices_count, avail_physical_devices.data()); res != VK_SUCCESS) { std::cerr << "failed to enumerate physical devices, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return avail_physical_devices; }(); if (avail_physical_devices.empty()) { std::cerr << "failed to find physical devices with Vulkan support" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::cout << "devices:" << std::endl; @@ -915,13 +913,13 @@ static int main_graphical() { if (VkResult res = vkEnumerateDeviceExtensionProperties(avail_physical_device, nullptr, &physical_device_ext_props_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to enumerate physical device extension properties, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::vector<VkExtensionProperties> physical_device_ext_props(physical_device_ext_props_count); if (VkResult res = vkEnumerateDeviceExtensionProperties(avail_physical_device, nullptr, &physical_device_ext_props_count, physical_device_ext_props.data()); res != VK_SUCCESS) { std::cerr << "failed to enumerate physical device extension properties, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return physical_device_ext_props; }(); @@ -1001,7 +999,7 @@ static int main_graphical() { default: { std::cerr << "device type not supported: " << string_VkPhysicalDeviceType(device_type) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } }(physical_device_props.properties.deviceType); @@ -1033,7 +1031,7 @@ static int main_graphical() { if (physical_devices.empty()) { std::cerr << "no suitable physical device found" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } auto best = physical_devices.crbegin(); @@ -1123,7 +1121,7 @@ static int main_graphical() { VkDevice device; if (VkResult res = vkCreateDevice(physical_device, &device_ci, nullptr, &device); res != VK_SUCCESS) { std::cerr << "failed to create device: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return device; }(); @@ -1170,7 +1168,7 @@ static int main_graphical() { if (!is_first_swapchain) { if (VkResult res = vkDeviceWaitIdle(device); res != VK_SUCCESS) { std::cerr << "failed to wait idle for device, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } destroy_depth_resources(device, depth_img, depth_img_device_mem, depth_img_view); destroy_swapchain(); @@ -1182,7 +1180,7 @@ static int main_graphical() { VkSurfaceCapabilitiesKHR surface_capabilities; if (VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_capabilities); res != VK_SUCCESS) { std::cerr << "failed to get physical device surface capabilities, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return surface_capabilities; }(); @@ -1209,12 +1207,12 @@ static int main_graphical() { uint32_t surface_formats_count; if (VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &surface_formats_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to get physical device surface formats, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::vector<VkSurfaceFormatKHR> surface_formats(surface_formats_count); if (VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &surface_formats_count, surface_formats.data()); res != VK_SUCCESS) { std::cerr << "failed to get physical device surface formats, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return surface_formats; }(); @@ -1227,7 +1225,7 @@ static int main_graphical() { // not found std::cerr << "surface format not found (VK_FORMAT_B8G8R8_SRGB and VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); }(); [&]() { @@ -1236,12 +1234,12 @@ static int main_graphical() { uint32_t present_modes_count; if (VkResult res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_modes_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to get physical device present modes, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } std::vector<VkPresentModeKHR> present_modes(present_modes_count); if (VkResult res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_modes_count, present_modes.data()); res != VK_SUCCESS) { std::cerr << "failed to get physical device present modes, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return present_modes; }(); @@ -1295,7 +1293,7 @@ static int main_graphical() { if (VkResult res = vkCreateSwapchainKHR(device, &swapchain_ci, nullptr, &swapchain); res != VK_SUCCESS) { std::cerr << "failed create swapchain, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } }(); @@ -1303,12 +1301,12 @@ static int main_graphical() { uint32_t swapchain_imgs_count; if (VkResult res = vkGetSwapchainImagesKHR(device, swapchain, &swapchain_imgs_count, nullptr); res != VK_SUCCESS) { std::cerr << "failed to get swapchain images, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } swapchain_imgs.resize(swapchain_imgs_count); if (VkResult res = vkGetSwapchainImagesKHR(device, swapchain, &swapchain_imgs_count, swapchain_imgs.data()); res != VK_SUCCESS) { std::cerr << "failed to get swapchain images, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } }(); @@ -1333,7 +1331,7 @@ static int main_graphical() { }; if (VkResult res = vkCreateImageView(device, &img_view_ci, nullptr, &swapchain_img_views[i]); res != VK_SUCCESS) { std::cerr << "failed to create image view, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } }(); @@ -1351,7 +1349,7 @@ static int main_graphical() { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = {}, }, VkDescriptorSetLayoutBinding { @@ -1372,7 +1370,7 @@ static int main_graphical() { 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); + exit(EXIT_FAILURE); } return descriptor_set_layout; }(); @@ -1386,7 +1384,7 @@ static int main_graphical() { std::ifstream shader_file(shader_file_name, std::ios::ate | std::ios::binary); if (!shader_file.is_open()) { std::cerr << "file `" << shader_file_name << "' not found" << std::endl; // TODO: improve - std::exit(EXIT_SUCCESS); + exit(EXIT_SUCCESS); } // shader code has to be 32-bits aligned, which is the case with the default allocator std::vector<char> shader_code(shader_file.tellg()); @@ -1405,7 +1403,7 @@ static int main_graphical() { VkShaderModule shader_module; if (VkResult res = vkCreateShaderModule(device, &shader_module_ci, nullptr, &shader_module); res != VK_SUCCESS) { std::cerr << "failed to create shader module, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return shader_module; }(); @@ -1558,7 +1556,7 @@ static int main_graphical() { VkPipelineLayout pl_layout; if (VkResult res = vkCreatePipelineLayout(device, &pl_layout_ci, nullptr, &pl_layout); res != VK_SUCCESS) { std::cerr << "failed to create pipeline layout, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return pl_layout; }(); @@ -1598,7 +1596,7 @@ static int main_graphical() { VkPipeline graphics_pl; if (VkResult res = vkCreateGraphicsPipelines(device, nullptr, 1, &graphics_pl_ci, nullptr, &graphics_pl); res != VK_SUCCESS) { std::cerr << "failed to pipeline, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return std::tuple { pl_layout, graphics_pl }; }(); @@ -1619,7 +1617,7 @@ static int main_graphical() { VkCommandPool cmd_pool; if (VkResult res = vkCreateCommandPool(device, &cmd_pool_ci, nullptr, &cmd_pool); res != VK_SUCCESS) { std::cerr << "failed to create command pool, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return cmd_pool; }(); @@ -1640,7 +1638,7 @@ static int main_graphical() { if (!pixels) { std::cerr << "failed to load texture image, reason: " << stbi_failure_reason() << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } auto [staging_buf, staging_buf_device_mem] = create_buf(physical_device, device, img_size, @@ -1652,7 +1650,7 @@ static int main_graphical() { if (VkResult res = vkMapMemory(device, staging_buf_device_mem, 0, img_size, 0, &staging_buf_mem); res != VK_SUCCESS) { std::cerr << "failed to map staging buffer memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } memcpy(staging_buf_mem, pixels, img_size); @@ -1709,7 +1707,7 @@ static int main_graphical() { VkImageView texture_img_view; if (VkResult res = vkCreateImageView(device, &img_view_ci, nullptr, &texture_img_view); res != VK_SUCCESS) { std::cerr << "failed to create texture image view, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return texture_img_view; }(); @@ -1740,105 +1738,120 @@ static int main_graphical() { VkSampler sampler; if (VkResult res = vkCreateSampler(device, &sampler_ci, nullptr, &sampler); res != VK_SUCCESS) { std::cerr << "failed to create sampler, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return sampler; }(); - // create vertex buffer - auto [vertex_buf, vertex_buf_device_mem] = [&]() { - VkDeviceSize vertex_buf_size = sizeof(engine::vk::Vertex) * vertices.size(); + // create vertex buffers + auto [vertex_bufs, vertex_buf_device_mems] = [&]() { + std::vector<VkBuffer> vertex_bufs; + std::vector<VkDeviceMemory> vertex_buf_device_mems; + for (const auto& vertices : meshes_vertices) { + VkDeviceSize vertex_buf_size = sizeof(engine::vk::Vertex) * vertices.size(); - auto [staging_buf, staging_buf_device_mem] = create_buf(physical_device, device, vertex_buf_size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + auto [staging_buf, staging_buf_device_mem] = create_buf(physical_device, device, vertex_buf_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - { - void* staging_buf_mem; + { + void* staging_buf_mem; - if (VkResult res = vkMapMemory(device, staging_buf_device_mem, 0, vertex_buf_size, 0, &staging_buf_mem); res != VK_SUCCESS) { - std::cerr << "failed to map staging buffer memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + if (VkResult res = vkMapMemory(device, staging_buf_device_mem, 0, vertex_buf_size, 0, &staging_buf_mem); res != VK_SUCCESS) { + std::cerr << "failed to map staging buffer memory, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } + + memcpy(staging_buf_mem, vertices.data(), static_cast<size_t>(vertex_buf_size)); + + vkUnmapMemory(device, staging_buf_device_mem); } - memcpy(staging_buf_mem, vertices.data(), static_cast<size_t>(vertex_buf_size)); + auto [vertex_buf, vertex_buf_device_mem] = create_buf(physical_device, device, vertex_buf_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - vkUnmapMemory(device, staging_buf_device_mem); - } + // TODO: consider using different command pool for short-lived command buffers, with the flag + // VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, for optimization. For now we will stick to the one + // which does the rendering + // TODO: don't wait individually for every copy operations, use a fence or something similar + // TODO: should use a single command buffer instead of one for each copy + { + auto cmd_buf = begin_single_time_cmds(device, cmd_pool); + copy_buf_to_buf(cmd_buf, staging_buf, vertex_buf, vertex_buf_size); + end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + } - auto [vertex_buf, vertex_buf_device_mem] = create_buf(physical_device, device, vertex_buf_size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vkFreeMemory(device, staging_buf_device_mem, nullptr); + vkDestroyBuffer(device, staging_buf, nullptr); - // TODO: consider using different command pool for short-lived command buffers, with the flag - // VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, for optimization. For now we will stick to the one - // which does the rendering - // TODO: don't wait individually for every copy operations, use a fence or something similar - { - auto cmd_buf = begin_single_time_cmds(device, cmd_pool); - copy_buf_to_buf(cmd_buf, staging_buf, vertex_buf, vertex_buf_size); - end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + vertex_bufs.push_back(vertex_buf); + vertex_buf_device_mems.push_back(vertex_buf_device_mem); } - vkFreeMemory(device, staging_buf_device_mem, nullptr); - vkDestroyBuffer(device, staging_buf, nullptr); - - return std::tuple { vertex_buf, vertex_buf_device_mem }; + return std::tuple { vertex_bufs, vertex_buf_device_mems }; }(); - // create index buffer - // TODO: this code is pretty much a duplicate with minor differences of the vertex_buf one. We + // create index buffers + // TODO: this code is pretty much a duplicate with minor differences of the vertex_bufs one. We // should probably factor it out in a function. Also, every TODOs were remove for this one, but // they still hold - auto [index_buf, index_buf_device_mem] = [&]() { - VkDeviceSize index_buf_size = sizeof(uint16_t) * indices.size(); + auto [index_bufs, index_buf_device_mems] = [&]() { + std::vector<VkBuffer> index_bufs; + std::vector<VkDeviceMemory> index_buf_device_mems; + for (const auto& indices : meshes_indices) { + VkDeviceSize index_buf_size = sizeof(uint16_t) * indices.size(); + + auto [staging_buf, staging_buf_device_mem] = create_buf(physical_device, device, index_buf_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - auto [staging_buf, staging_buf_device_mem] = create_buf(physical_device, device, index_buf_size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + { + void* staging_buf_mem; - { - void* staging_buf_mem; + if (VkResult res = vkMapMemory(device, staging_buf_device_mem, 0, index_buf_size, 0, &staging_buf_mem); res != VK_SUCCESS) { + std::cerr << "failed to map staging buffer memory, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } - if (VkResult res = vkMapMemory(device, staging_buf_device_mem, 0, index_buf_size, 0, &staging_buf_mem); res != VK_SUCCESS) { - std::cerr << "failed to map staging buffer memory, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + memcpy(staging_buf_mem, indices.data(), static_cast<size_t>(index_buf_size)); + + vkUnmapMemory(device, staging_buf_device_mem); } - memcpy(staging_buf_mem, indices.data(), static_cast<size_t>(index_buf_size)); + auto [index_buf, index_buf_device_mem] = create_buf(physical_device, device, index_buf_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - vkUnmapMemory(device, staging_buf_device_mem); - } + { + auto cmd_buf = begin_single_time_cmds(device, cmd_pool); + copy_buf_to_buf(cmd_buf, staging_buf, index_buf, index_buf_size); + end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + } - auto [index_buf, index_buf_device_mem] = create_buf(physical_device, device, index_buf_size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vkFreeMemory(device, staging_buf_device_mem, nullptr); + vkDestroyBuffer(device, staging_buf, nullptr); - { - auto cmd_buf = begin_single_time_cmds(device, cmd_pool); - copy_buf_to_buf(cmd_buf, staging_buf, index_buf, index_buf_size); - end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf); + index_bufs.push_back(index_buf); + index_buf_device_mems.push_back(index_buf_device_mem); } - vkFreeMemory(device, staging_buf_device_mem, nullptr); - vkDestroyBuffer(device, staging_buf, nullptr); - - return std::tuple { index_buf, index_buf_device_mem }; + return std::tuple { index_bufs, index_buf_device_mems }; }(); // 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::vector<VkBuffer> uniform_bufs(max_frames_in_flight * scene.objs.size(), nullptr); + std::vector<VkDeviceMemory> uniform_buf_device_mems(max_frames_in_flight * scene.objs.size(), nullptr); + std::vector<void*> uniform_buf_mems(max_frames_in_flight * scene.objs.size(), nullptr); + for (size_t i = 0; i < max_frames_in_flight * scene.objs.size(); 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); + exit(EXIT_FAILURE); } } return std::tuple { uniform_bufs, uniform_buf_device_mems, uniform_buf_mems }; @@ -1849,11 +1862,11 @@ static int main_graphical() { std::array descriptor_pool_sizes { VkDescriptorPoolSize { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = max_frames_in_flight, + .descriptorCount = static_cast<uint32_t>(max_frames_in_flight * scene.objs.size()), }, VkDescriptorPoolSize { .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = max_frames_in_flight, + .descriptorCount = static_cast<uint32_t>(max_frames_in_flight * scene.objs.size()), }, }; VkDescriptorPoolCreateInfo descriptor_pool_ci { @@ -1862,22 +1875,24 @@ static int main_graphical() { // 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, + .maxSets = static_cast<uint32_t>(max_frames_in_flight * scene.objs.size()), .poolSizeCount = descriptor_pool_sizes.size(), .pPoolSizes = descriptor_pool_sizes.data(), }; 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); + exit(EXIT_FAILURE); } return descriptor_pool; }(); // create descriptor sets + // TODO: right now, we just send every descriptors every frame and for every object, without + // taking into account update frequencies. For example we should have only one set for the + // camera, shared between every objects, etc auto descriptor_sets = [&]() { - std::array<VkDescriptorSetLayout, max_frames_in_flight> layouts; - layouts.fill(descriptor_set_layout); + std::vector<VkDescriptorSetLayout> layouts(max_frames_in_flight * scene.objs.size(), descriptor_set_layout); VkDescriptorSetAllocateInfo descriptor_set_ai { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .pNext = nullptr, @@ -1885,23 +1900,22 @@ static int main_graphical() { .descriptorSetCount = static_cast<uint32_t>(layouts.size()), .pSetLayouts = layouts.data(), }; - std::array<VkDescriptorSet, max_frames_in_flight> descriptor_sets; + std::vector<VkDescriptorSet> descriptor_sets(max_frames_in_flight * scene.objs.size()); 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); + exit(EXIT_FAILURE); } - std::array<VkDescriptorBufferInfo, max_frames_in_flight> descriptor_buffer_infos; - std::array<VkDescriptorImageInfo, max_frames_in_flight> descriptor_image_infos; - // the 2 comes from uniform buffer object + combined image sampler - std::array<VkWriteDescriptorSet, 2 * max_frames_in_flight> write_descriptor_sets; - for (size_t i = 0; i < max_frames_in_flight; i++) { + std::vector<VkDescriptorBufferInfo> descriptor_buffer_infos(max_frames_in_flight * scene.objs.size()); + std::vector<VkDescriptorImageInfo> descriptor_image_infos(max_frames_in_flight * scene.objs.size()); + std::vector<VkWriteDescriptorSet> write_descriptor_sets; + for (size_t i = 0; i < max_frames_in_flight * scene.objs.size(); i++) { // uniform buffer object - descriptor_buffer_infos[i] = VkDescriptorBufferInfo { + descriptor_buffer_infos[i] = { // VkDescriptorBufferInfo .buffer = uniform_bufs[i], .offset = 0, .range = sizeof(engine::vk::UniformBufferObject), }; - write_descriptor_sets[2 * i + 0] = VkWriteDescriptorSet { + write_descriptor_sets.push_back({ // VkWriteDescriptorSet .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = nullptr, .dstSet = descriptor_sets[i], @@ -1912,14 +1926,14 @@ static int main_graphical() { .pImageInfo = {}, .pBufferInfo = &descriptor_buffer_infos[i], .pTexelBufferView = {}, - }; + }); // combined image sampler - descriptor_image_infos[i] = VkDescriptorImageInfo { + descriptor_image_infos[i] = { // VkDescriptorImageInfo .sampler = sampler, .imageView = texture_img_view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; - write_descriptor_sets[2 * i + 1] = VkWriteDescriptorSet { + write_descriptor_sets.push_back({ // VkWriteDescriptorSet .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = nullptr, .dstSet = descriptor_sets[i], @@ -1930,7 +1944,7 @@ static int main_graphical() { .pImageInfo = &descriptor_image_infos[i], .pBufferInfo = {}, .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 @@ -1950,7 +1964,7 @@ static int main_graphical() { std::array<VkCommandBuffer, max_frames_in_flight> cmd_bufs; if (VkResult res = vkAllocateCommandBuffers(device, &cmd_buf_ai, cmd_bufs.data()); res != VK_SUCCESS) { std::cerr << "failed to allocate command buffers, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return cmd_bufs; }(); @@ -1970,7 +1984,7 @@ static int main_graphical() { }; if (VkResult res = vkCreateSemaphore(device, &sem_ci, nullptr, &sem); res != VK_SUCCESS) { std::cerr << "failed to create present complete semaphore #" << (++num) << ", error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } return sems_present_complete; @@ -1987,7 +2001,7 @@ static int main_graphical() { }; if (VkResult res = vkCreateSemaphore(device, &sem_ci, nullptr, &sem); res != VK_SUCCESS) { std::cerr << "failed to create render finished semaphore #" << (++num) << ", error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } return sems_render_finished; @@ -2026,7 +2040,7 @@ static int main_graphical() { }; if (VkResult res = vkCreateFence(device, &fence_in_flight_ci, nullptr, &fence_in_flight); res != VK_SUCCESS) { std::cerr << "failed to create draw fence #" << (++num) << ", error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } return fences_in_flight; @@ -2040,272 +2054,344 @@ static int main_graphical() { } std::cout << " ]" << std::endl; - auto start_time = std::chrono::high_resolution_clock::now(); - auto last_time = start_time; - uint32_t frame_idx = 0; - // main loop - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); - 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.f / ellapsed_time); + scene_main(Matrix4::idty(), scene, + // update_surface_size + [&]() { + return std::tuple { swapchain_extent.width, swapchain_extent.height }; + }, - 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; - std::exit(EXIT_FAILURE); - } + // poll_events + [&](auto& kb, auto& mouse) { + if (glfwWindowShouldClose(window)) + return false; + glfwPollEvents(); - uint32_t img_idx; - { - VkResult res = vkAcquireNextImageKHR(device, swapchain, - std::numeric_limits<uint64_t>::max(), sems_present_complete[frame_idx], nullptr, &img_idx); - switch (res) { - case VK_SUBOPTIMAL_KHR: - case VK_SUCCESS: + // TODO: improve + switch (glfwGetKey(window, GLFW_KEY_W)) { + case GLFW_PRESS: + kb.key_down_event(KeyboardKey::fw); + break; + case GLFW_RELEASE: + kb.key_up_event(KeyboardKey::fw); + break; + } + switch (glfwGetKey(window, GLFW_KEY_A)) { + case GLFW_PRESS: + kb.key_down_event(KeyboardKey::key_left); + break; + case GLFW_RELEASE: + kb.key_up_event(KeyboardKey::key_left); + break; + } + switch (glfwGetKey(window, GLFW_KEY_S)) { + case GLFW_PRESS: + kb.key_down_event(KeyboardKey::bw); + break; + case GLFW_RELEASE: + kb.key_up_event(KeyboardKey::bw); + break; + } + switch (glfwGetKey(window, GLFW_KEY_D)) { + case GLFW_PRESS: + kb.key_down_event(KeyboardKey::key_right); + break; + case GLFW_RELEASE: + kb.key_up_event(KeyboardKey::key_right); + break; + } + switch (glfwGetKey(window, GLFW_KEY_ESCAPE)) { + case GLFW_PRESS: + glfwSetWindowShouldClose(window, GLFW_TRUE); break; - case VK_ERROR_OUT_OF_DATE_KHR: - recreate_swapchain(); - continue; - default: - std::cerr << "failed to acquire next image, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); } - } - // update uniform buffer - { - // TODO: should use push constants - engine::vk::UniformBufferObject ubo { - .model = Matrix4::rot_z(time * PI / 2.f), - .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)), - .proj = Matrix4::perspective(PI / 4.f, static_cast<float>(swapchain_extent.width) / static_cast<float>(swapchain_extent.height), .1f, 10.f), - }; - memcpy(uniform_buf_mems[frame_idx], &ubo, sizeof(engine::vk::UniformBufferObject)); - } + double new_mouse_x, new_mouse_y; + glfwGetCursorPos(window, &new_mouse_x, &new_mouse_y); + + if (new_mouse_x != mouse_x || new_mouse_y != mouse_y) { + // TODO: find a better name + float mouse_fac = 5.f / static_cast<float>(swapchain_extent.height); + mouse.mouse_motion_event({ + mouse_fac * static_cast<float>(new_mouse_x - mouse_x), + mouse_fac * static_cast<float>(new_mouse_y - mouse_y), + }); + mouse_x = new_mouse_x; + mouse_y = new_mouse_y; + } - // implicit command buffer reset - // record command buffer - { - VkCommandBufferBeginInfo cmd_buf_bi { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = nullptr, - // TODO: I don't understand why here we shouldn't also set - // VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, because we always submit this - // command buffer once before resetting it, like when we copy the staging buffer to - // the vertex buffer (where we set this flag). The only difference is that we wait - // for the queue to idle for the latter, where here we only fence for the end of - // vkQueueSubmit - .flags = {}, - .pInheritanceInfo = {}, - }; - if (VkResult res = vkBeginCommandBuffer(cmd_bufs[frame_idx], &cmd_buf_bi); res != VK_SUCCESS) { - std::cerr << "failed to begin command buffer, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + return true; + }, + + // render_and_present_frame + [&](const Matrix4& view_mat, const Matrix4& proj_mat, float /* time */, float ellapsed_time) { + if (show_fps) + 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; + exit(EXIT_FAILURE); } - } - // transition layouts - [&]() { - std::array img_mem_barriers { - VkImageMemoryBarrier2 { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = {}, - .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = swapchain_imgs[img_idx], - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, + uint32_t img_idx; + { + VkResult res = vkAcquireNextImageKHR(device, swapchain, + std::numeric_limits<uint64_t>::max(), sems_present_complete[frame_idx], nullptr, &img_idx); + switch (res) { + case VK_SUBOPTIMAL_KHR: + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_DATE_KHR: + recreate_swapchain(); + return; + default: + std::cerr << "failed to acquire next image, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } + } + + // update uniform buffers + for (size_t i = 0; i < scene.objs.size(); i++) { + // TODO: should use push constants + engine::vk::UniformBufferObject ubo { + .model = scene.objs[i].transform.to_mat4(), + .view = view_mat, + .proj = proj_mat, + .camera_rot = Matrix4::from_quaternion(scene.camera.transform.rot), + .camera_loc = scene.camera.transform.loc, + }; + memcpy(uniform_buf_mems[frame_idx * scene.objs.size() + i], &ubo, sizeof(engine::vk::UniformBufferObject)); + } + + // implicit command buffer reset + // record command buffer + { + VkCommandBufferBeginInfo cmd_buf_bi { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + // TODO: I don't understand why here we shouldn't also set + // VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, because we always submit this + // command buffer once before resetting it, like when we copy the staging buffer to + // the vertex buffer (where we set this flag). The only difference is that we wait + // for the queue to idle for the latter, where here we only fence for the end of + // vkQueueSubmit + .flags = {}, + .pInheritanceInfo = {}, + }; + if (VkResult res = vkBeginCommandBuffer(cmd_bufs[frame_idx], &cmd_buf_bi); res != VK_SUCCESS) { + std::cerr << "failed to begin command buffer, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } + } + + // transition layouts + [&]() { + std::array img_mem_barriers { + VkImageMemoryBarrier2 { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = {}, + .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = swapchain_imgs[img_idx], + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, }, - }, - VkImageMemoryBarrier2 { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, - .srcAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, - .dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = (has_stencil(depth_format) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL), - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = depth_img, - .subresourceRange = { - .aspectMask = static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT | (has_stencil(depth_format) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0)), - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, + VkImageMemoryBarrier2 { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + .srcAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + .dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = (has_stencil(depth_format) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL), + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = depth_img, + .subresourceRange = { + .aspectMask = static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT | (has_stencil(depth_format) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0)), + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, }, - }, - }; - VkDependencyInfo dependency_info { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = {}, - .memoryBarrierCount = 0, - .pMemoryBarriers = {}, - .bufferMemoryBarrierCount = 0, - .pBufferMemoryBarriers = {}, - .imageMemoryBarrierCount = img_mem_barriers.size(), - .pImageMemoryBarriers = img_mem_barriers.data(), - }; - vkCmdPipelineBarrier2(cmd_bufs[frame_idx], &dependency_info); - }(); + }; + VkDependencyInfo dependency_info { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = {}, + .memoryBarrierCount = 0, + .pMemoryBarriers = {}, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = {}, + .imageMemoryBarrierCount = img_mem_barriers.size(), + .pImageMemoryBarriers = img_mem_barriers.data(), + }; + vkCmdPipelineBarrier2(cmd_bufs[frame_idx], &dependency_info); + }(); - { - VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, 1.f } } }; - VkClearValue clear_depth_val { .depthStencil { .depth = 1.f, .stencil = 0 } }; - VkRenderingAttachmentInfo rendering_color_info { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = swapchain_img_views[img_idx], - .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .resolveMode = {}, - .resolveImageView = {}, - .resolveImageLayout = {}, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = clear_color_val, - }; - VkRenderingAttachmentInfo rendering_depth_info { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = depth_img_view, - .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - .resolveMode = {}, - .resolveImageView = {}, - .resolveImageLayout = {}, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .clearValue = clear_depth_val, - }; - VkRenderingInfo render_info { - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, - .pNext = nullptr, - .flags = {}, - .renderArea = { .offset { 0, 0 }, .extent = swapchain_extent }, - .layerCount = 1, - .viewMask = {}, - .colorAttachmentCount = 1, - .pColorAttachments = &rendering_color_info, - .pDepthAttachment = &rendering_depth_info, - .pStencilAttachment = {}, - }; - vkCmdBeginRendering(cmd_bufs[frame_idx], &render_info); - } + { + VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, 1.f } } }; + VkClearValue clear_depth_val { .depthStencil { .depth = 1.f, .stencil = 0 } }; + VkRenderingAttachmentInfo rendering_color_info { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = swapchain_img_views[img_idx], + .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .resolveMode = {}, + .resolveImageView = {}, + .resolveImageLayout = {}, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = clear_color_val, + }; + VkRenderingAttachmentInfo rendering_depth_info { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = depth_img_view, + .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + .resolveMode = {}, + .resolveImageView = {}, + .resolveImageLayout = {}, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .clearValue = clear_depth_val, + }; + VkRenderingInfo render_info { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = {}, + .renderArea = { .offset { 0, 0 }, .extent = swapchain_extent }, + .layerCount = 1, + .viewMask = {}, + .colorAttachmentCount = 1, + .pColorAttachments = &rendering_color_info, + .pDepthAttachment = &rendering_depth_info, + .pStencilAttachment = {}, + }; + vkCmdBeginRendering(cmd_bufs[frame_idx], &render_info); + } - vkCmdBindPipeline(cmd_bufs[frame_idx], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pl); + vkCmdBindPipeline(cmd_bufs[frame_idx], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pl); - { - VkViewport viewport { - .x = 0.f, - .y = 0.f, - .width = static_cast<float>(swapchain_extent.width), - .height = static_cast<float>(swapchain_extent.height), - .minDepth = 0.f, - .maxDepth = 1.f, - }; - vkCmdSetViewport(cmd_bufs[frame_idx], 0, 1, &viewport); - } + { + VkViewport viewport { + .x = 0.f, + .y = 0.f, + .width = static_cast<float>(swapchain_extent.width), + .height = static_cast<float>(swapchain_extent.height), + .minDepth = 0.f, + .maxDepth = 1.f, + }; + vkCmdSetViewport(cmd_bufs[frame_idx], 0, 1, &viewport); + } - { - VkRect2D scissor { - .offset = { 0, 0 }, - .extent = swapchain_extent, - }; - vkCmdSetScissor(cmd_bufs[frame_idx], 0, 1, &scissor); - } + { + VkRect2D scissor { + .offset = { 0, 0 }, + .extent = swapchain_extent, + }; + vkCmdSetScissor(cmd_bufs[frame_idx], 0, 1, &scissor); + } - VkDeviceSize vertex_buf_offset = 0; - vkCmdBindVertexBuffers(cmd_bufs[frame_idx], 0, 1, &vertex_buf, &vertex_buf_offset); + // TODO: not sure if we can just loop through every objects without worrying about it + for (size_t i = 0; i < scene.objs.size(); i++) { + const auto& vertex_buf = vertex_bufs[i]; + const auto& index_buf = index_bufs[i]; + const auto& indices = meshes_indices[i]; - 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); + VkDeviceSize vertex_buf_offset = 0; + vkCmdBindVertexBuffers(cmd_bufs[frame_idx], 0, 1, &vertex_buf, &vertex_buf_offset); - vkCmdEndRendering(cmd_bufs[frame_idx]); + 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 * scene.objs.size() + i], 0, nullptr); + vkCmdDrawIndexed(cmd_bufs[frame_idx], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0); + } - transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx], - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, {}, - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + vkCmdEndRendering(cmd_bufs[frame_idx]); - if (VkResult res = vkEndCommandBuffer(cmd_bufs[frame_idx]); res != VK_SUCCESS) { - std::cerr << "failed to end command buffer, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); - } + transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx], + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, {}, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); - if (VkResult res = vkResetFences(device, 1, &fences_in_flight[frame_idx]); res != VK_SUCCESS) { - std::cerr << "failed to reset draw fence, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); - } + if (VkResult res = vkEndCommandBuffer(cmd_bufs[frame_idx]); res != VK_SUCCESS) { + std::cerr << "failed to end command buffer, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } - { - VkPipelineStageFlags pl_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo submit_info { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = nullptr, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &sems_present_complete[frame_idx], - .pWaitDstStageMask = &pl_stage_flags, - .commandBufferCount = 1, - .pCommandBuffers = &cmd_bufs[frame_idx], - .signalSemaphoreCount = 1, - .pSignalSemaphores = &sems_render_finished[img_idx], - }; - if (VkResult res = vkQueueSubmit(graphics_queue, 1, &submit_info, fences_in_flight[frame_idx]); res != VK_SUCCESS) { - std::cerr << "failed to submit command buffer queue, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + if (VkResult res = vkResetFences(device, 1, &fences_in_flight[frame_idx]); res != VK_SUCCESS) { + std::cerr << "failed to reset draw fence, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); } - } - { - VkPresentInfoKHR present_info { - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .pNext = nullptr, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &sems_render_finished[img_idx], - .swapchainCount = 1, - .pSwapchains = &swapchain, - .pImageIndices = &img_idx, - .pResults = nullptr, - }; - VkResult res = vkQueuePresentKHR(present_queue, &present_info); - if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR || fb_resized) { - fb_resized = false; - recreate_swapchain(); - } else if (res != VK_SUCCESS) { - std::cerr << "failed to present, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + { + VkPipelineStageFlags pl_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submit_info { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &sems_present_complete[frame_idx], + .pWaitDstStageMask = &pl_stage_flags, + .commandBufferCount = 1, + .pCommandBuffers = &cmd_bufs[frame_idx], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &sems_render_finished[img_idx], + }; + if (VkResult res = vkQueueSubmit(graphics_queue, 1, &submit_info, fences_in_flight[frame_idx]); res != VK_SUCCESS) { + std::cerr << "failed to submit command buffer queue, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } } - } - frame_idx = (frame_idx + 1) % max_frames_in_flight; - } + { + VkPresentInfoKHR present_info { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &sems_render_finished[img_idx], + .swapchainCount = 1, + .pSwapchains = &swapchain, + .pImageIndices = &img_idx, + .pResults = nullptr, + }; + VkResult res = vkQueuePresentKHR(present_queue, &present_info); + if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR || fb_resized) { + fb_resized = false; + recreate_swapchain(); + } else if (res != VK_SUCCESS) { + std::cerr << "failed to present, error code: " << string_VkResult(res) << std::endl; + exit(EXIT_FAILURE); + } + } + + frame_idx = (frame_idx + 1) % max_frames_in_flight; + } + ); if (show_fps) std::cout << std::endl; if (VkResult res = vkDeviceWaitIdle(device); res != VK_SUCCESS) { std::cerr << "failed to wait idle for device, error code: " << string_VkResult(res) << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } // cleanup @@ -2316,15 +2402,19 @@ static int main_graphical() { 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--) { + for (size_t i = max_frames_in_flight * scene.objs.size(); 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); + for (size_t i = scene.objs.size(); i > 0; i--) { + vkFreeMemory(device, index_buf_device_mems[i - 1], nullptr); + vkDestroyBuffer(device, index_bufs[i - 1], nullptr); + } + for (size_t i = scene.objs.size(); i > 0; i--) { + vkFreeMemory(device, vertex_buf_device_mems[i - 1], nullptr); + vkDestroyBuffer(device, vertex_bufs[i - 1], nullptr); + } vkDestroySampler(device, sampler, nullptr); vkDestroyImageView(device, texture_img_view, nullptr); vkFreeMemory(device, texture_img_device_mem, nullptr); @@ -2344,7 +2434,7 @@ static int main_graphical() { auto destroy_debug_messenger = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); if (!destroy_debug_messenger) { std::cerr << "failed to destroy debug messenger!" << std::endl; - std::exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } destroy_debug_messenger(instance, debug_messenger, nullptr); } @@ -2413,19 +2503,62 @@ static void parse_args(const std::vector<std::string_view>& args, int& mode) { int main(int argc, char *argv[]) { int mode = MODE_GRAPHICAL; parse_args(convert_args(argc, argv), mode); + + Scene scene { + { 90.f * PI / 180.f, { { 0.f, 1.8f, 7.f }, Quaternion::one(), { 1.f, 1.f, 1.f } } }, + { +#if GAME == GAME_PLANE + { + Mesh::plane(2.f, 2.f), + { + Vector3(0.f, 0.f, 0.f), + Quaternion::one(), + Vector3(1.f, 1.f, 1.f), + } + }, +#elif GAME == GAME_SUZANNE + { + engine::parse_object(DATADIR "/assets/suzanne.obj"), + { + Vector3(0.f, 0.f, 0.f), + Quaternion::one(), + Vector3(1.f, 1.f, 1.f), + } + }, +#elif GAME == GAME_PHYSICS + { + Mesh::plane(10.f, 10.f), + { + Vector3(0.f, 0.f, 0.f), + Quaternion::one(), + Vector3(1.f, 1.f, 1.f), + } + }, + { + engine::parse_object(DATADIR "/assets/suzanne.obj"), + { + Vector3(0.f, 1.f, 0.f), + Quaternion::one(), + Vector3(1.f, 1.f, 1.f), + } + }, +#endif + } + }; + switch (mode) { case MODE_HELP: print_usage(std::cout); return EXIT_SUCCESS; case MODE_TERM: #ifdef HAVE_NCURSES - return main_term(); + return main_term(scene); #else std::cerr << "Error: ncurses was not enabled during compilation." << std::endl; return EXIT_FAILURE; #endif case MODE_GRAPHICAL: - return main_graphical(); + return main_graphical(scene); default: std::unreachable(); } diff --git a/src/o3d/mesh.cpp b/src/o3d/mesh.cpp index c1af5cd..ce6da30 100644 --- a/src/o3d/mesh.cpp +++ b/src/o3d/mesh.cpp @@ -1,8 +1,11 @@ #include "o3d/mesh.hpp" #include <vector> #include <array> +#include <cstdint> #include <cstddef> +#include <tuple> #include "math/vector.hpp" +#include "vulkan_utils.hpp" using namespace engine::o3d; @@ -11,20 +14,37 @@ Mesh Mesh::plane(float width, float height) { h2 = height / 2; return { { - {-w2 / 2, 0.f, -h2 / 2, 1.f}, - {+w2 / 2, 0.f, -h2 / 2, 1.f}, - {+w2 / 2, 0.f, +h2 / 2, 1.f}, - {-w2 / 2, 0.f, +h2 / 2, 1.f}, + { -w2 / 2, 0.f, -h2 / 2 }, + { +w2 / 2, 0.f, -h2 / 2 }, + { +w2 / 2, 0.f, +h2 / 2 }, + { -w2 / 2, 0.f, +h2 / 2 }, }, { - {0.f, -1.f, 0.f}, - {0.f, +1.f, 0.f}, + { 0.f, -1.f, 0.f }, + { 0.f, +1.f, 0.f }, }, { - {{ {{0, 0}}, {{1, 0}}, {{2, 0}} }}, - {{ {{2, 0}}, {{3, 0}}, {{0, 0}} }}, - {{ {{0, 1}}, {{3, 1}}, {{2, 1}} }}, - {{ {{2, 1}}, {{1, 1}}, {{0, 1}} }}, + {{ {{ 0, 0 }}, {{ 1, 0 }}, {{ 2, 0 }} }}, + {{ {{ 2, 0 }}, {{ 3, 0 }}, {{ 0, 0 }} }}, + {{ {{ 0, 1 }}, {{ 3, 1 }}, {{ 2, 1 }} }}, + {{ {{ 2, 1 }}, {{ 1, 1 }}, {{ 0, 1 }} }}, } }; } + +std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> Mesh::linearize_indices() const & { + std::vector<engine::vk::Vertex> linearized_vertices; + std::vector<uint16_t> linearized_indices; + + size_t n = 0; + for (const auto& triangle_indices : this->indices) { + for (const auto& vertex_indices : triangle_indices) { + linearized_vertices.emplace_back(this->vertices[vertex_indices[0]], this->normals[vertex_indices[1]]); + linearized_indices.emplace_back(n++); + } + } + + // TODO: I'm not sure if passing vectors like that makes a copy or not, because they are passed + // to a structured which is instantly returned. I think that copy-ellision catches that + return std::tuple { linearized_vertices, linearized_indices }; +} diff --git a/src/o3d/mesh.hpp b/src/o3d/mesh.hpp index 2c3a065..525d9bd 100644 --- a/src/o3d/mesh.hpp +++ b/src/o3d/mesh.hpp @@ -1,24 +1,29 @@ -#ifndef O3D_MESH_H -#define O3D_MESH_H +#ifndef O3D_MESH_HPP +#define O3D_MESH_HPP #include <vector> #include <array> #include <iterator> +#include <cstdint> #include <cstddef> +#include <tuple> #include "math/vector.hpp" +#include "vulkan_utils.hpp" namespace engine::o3d { -using engine::math::Vector3, engine::math::Vector4; - struct Mesh { static Mesh plane(float width, float height); - std::vector<Vector4> vertices; - std::vector<Vector3> normals; - std::vector<std::array<std::array<std::size_t, 2>, 3>> indices; + std::vector<engine::math::Vector3> vertices; + std::vector<engine::math::Vector3> normals; + std::vector<std::array<std::array<size_t, 2>, 3>> indices; + + // TODO: find a better way to do this. This workaround is due to the fact that vulkan only + // accepts a single index, not an index for each attributes + std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> linearize_indices() const &; }; } -#endif // O3D_MESH_H +#endif // O3D_MESH_HPP diff --git a/src/obj_parser.cpp b/src/obj_parser.cpp index b30eff1..d83d75f 100644 --- a/src/obj_parser.cpp +++ b/src/obj_parser.cpp @@ -104,14 +104,10 @@ o3d::Mesh parse_object(const std::string& obj_path) { // std::cout << "Object: " << line.substr(2) << std::endl; } else if (line.rfind("v ", 0) == 0) { auto s_coords = split(line.substr(2), ' '); - math::Vector4 v{parse_float(s_coords[0]), parse_float(s_coords[1]), parse_float(s_coords[2]), 1.f}; - // std::cout << "Vertex x: " << v.x << " y: " << v.y << " z: " << v.z << std::endl; - mesh.vertices.push_back(v); + mesh.vertices.emplace_back(parse_float(s_coords[0]), parse_float(s_coords[1]), parse_float(s_coords[2])); } else if (line.rfind("vn ", 0) == 0) { auto s_coords = split(line.substr(3), ' '); - math::Vector3 vn{parse_float(s_coords[0]), parse_float(s_coords[1]), parse_float(s_coords[2])}; - // std::cout << "Vertex normal x: " << vn.x << " y: " << vn.y << " z: " << vn.z << std::endl; - mesh.normals.push_back(vn); + mesh.normals.emplace_back(parse_float(s_coords[0]), parse_float(s_coords[1]), parse_float(s_coords[2])); } else if (line.rfind("s ", 0) == 0) { // auto smooth = false; // auto s_smooth = line.substr(2); diff --git a/src/shaders/shader.slang b/src/shaders/shader.slang index 1f055c1..5598e1e 100644 --- a/src/shaders/shader.slang +++ b/src/shaders/shader.slang @@ -1,28 +1,28 @@ struct VertexInput { float3 pos; - float3 col; - float2 uv; + float3 normal; }; struct UniformBuffer { float4x4 model, view, proj; + float4x4 camera_rot; + float3 camera_loc; }; ConstantBuffer<UniformBuffer> ubo; struct VertexOutput { float4 pos : SV_Position; - float3 col; - float2 uv; + float3 world_loc; + float3 normal; }; [shader("vertex")] VertexOutput vert_main(VertexInput vi) { VertexOutput vo; - // vo.pos = float4(vi.pos, 0., 1.); vo.pos = mul(ubo.proj, mul(ubo.view, mul(ubo.model, float4(vi.pos, 1.)))); - vo.col = vi.col; - vo.uv = vi.uv; + vo.world_loc = vi.pos; + vo.normal = vi.normal; return vo; } @@ -30,5 +30,16 @@ Sampler2D texture; [shader("fragment")] float4 frag_main(VertexOutput vo) : SV_Target { - return texture.Sample(vo.uv); + float3 u = vo.world_loc - ubo.camera_loc; + float u_len_sq = dot(u, u); + u = normalize(u); + float attenuation = dot(mul(ubo.camera_rot, float4(0., 0., -1., 0.)).xyz, u); + if (attenuation < .7) attenuation = 0.f; + else if (attenuation > .8) attenuation = 1.; + else attenuation = (attenuation - .7) / .1; + float light = -dot(vo.normal, u) / u_len_sq; + if (light < 0.) light = 0.; + float final_light = .05 + light * attenuation * .8 * .95; + if (final_light > 1.) final_light = 1.; + return float4(final_light, final_light, final_light, 1.); } diff --git a/src/vulkan_utils.hpp b/src/vulkan_utils.hpp index 1647e91..c822caa 100644 --- a/src/vulkan_utils.hpp +++ b/src/vulkan_utils.hpp @@ -39,7 +39,7 @@ struct Vertex { }; } - static constexpr std::array<VkVertexInputAttributeDescription, 3> get_attr_descs() { + static constexpr std::array<VkVertexInputAttributeDescription, 2> get_attr_descs() { return { VkVertexInputAttributeDescription { .location = 0, @@ -51,25 +51,21 @@ struct Vertex { .location = 1, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, - .offset = offsetof(Vertex, col), - }, - VkVertexInputAttributeDescription { - .location = 2, - .binding = 0, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = offsetof(Vertex, uv), + .offset = offsetof(Vertex, normal), }, }; } engine::math::Vector3 pos; - engine::math::Vector3 col; - engine::math::Vector2 uv; + engine::math::Vector3 normal; }; // TODO: move to a better place. Also, see TODOs for struct Vertex struct UniformBufferObject { alignas(16) engine::math::Matrix4 model, view, proj; + // TODO: should me Matrix3, but it isn't implemented yet + alignas(16) engine::math::Matrix4 camera_rot; + alignas(16) engine::math::Vector3 camera_loc; }; } |
