aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/engine.cpp1095
-rw-r--r--src/o3d/mesh.cpp40
-rw-r--r--src/o3d/mesh.hpp21
-rw-r--r--src/obj_parser.cpp8
-rw-r--r--src/shaders/shader.slang27
-rw-r--r--src/vulkan_utils.hpp16
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;
};
}