X-Git-Url: https://eleni.mutantstargoat.com/git/?p=vkrt;a=blobdiff_plain;f=src%2Fmain.c;fp=src%2Fmain.c;h=c275372538bed4cdb152726b4ca12b23993bf261;hp=0000000000000000000000000000000000000000;hb=638b86fbbdb0307faf21c04532d8f5ceb3b29884;hpb=c7de7f04077b07757ffef63ce6771a9d561945d5 diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c275372 --- /dev/null +++ b/src/main.c @@ -0,0 +1,546 @@ +#include + +#define GLFW_INCLUDE_VULKAN +#include + +#include +#include +#include + +/* extern "C": vulkan framework */ +#include "vk.h" +#include "util.h" + +/* defines */ +#define FENCE_TIMEOUT UINT64_MAX + +/**************************/ +/* static glfw callbacks */ +/**************************/ + +static void +clb_key(GLFWwindow *win, int key, int scancode, int action, int mods); + +static void +clb_reshape(GLFWwindow *win, int width, int height); + +static void +clb_motion(GLFWwindow *win, double x, double y); + +static void +clb_mouse(GLFWwindow *win, int button, int action, int mods); + +/**************************/ +/* static functions */ +/**************************/ + +/* init, cleanup, display */ + +static bool +init(); + +static void +cleanup(); + +static void +display(); + +/* vulkan, glfw */ + +static bool +vk_init(); + +static bool +vk_create_sync_objects(); + +static void +vk_cleanup(); + +/***************************/ +/* static/global variables */ +/***************************/ + +static GLFWwindow *win; +/* static bool redraw_pending; */ +static bool move_camera; + +/* camera */ +static float cam_phi = 25; +static float cam_theta = 0; +static float cam_dist = 16; + +#if 0 +static Vec3 cam_pos; +static OrbitCamera *camera; +static Mat4 mproj; +#endif + +static float aspect; + +/* win etc */ +static int win_w = 800; +static int win_h = 600; + +/* vulkan */ +static bool vk_enable_layers = true; +static struct vk_ctx vk_core; +static VkSurfaceKHR vk_surf; + +static struct vk_swapchain vk_chain; +static uint32_t vk_current_image; +static int vk_frame_idx; +static VkFramebuffer *vk_framebuffers; + +/* for the moment: one cmd buffer per swapchain image */ +static VkCommandBuffer *vk_cmd_buffers; +static VkFence *vk_fences; +static struct vk_semaphores *vk_semas; + +/* FIXME make them part of each object's functions/params */ +static struct vk_renderer vk_rnd; +static struct vk_attachment vk_depth_att; +static float vk_fb_color[4] = { 0.0, 0.0, 0.5, 1.0 }; + +/* empty for as long as we hardcode the vertices in the vertex shader */ +#if 0 +static struct vk_vertex_info vk_vert_info; +#endif + +int main(int argc, char** argv) +{ + atexit(cleanup); + + /*********************************************************** + * GLFW + ***********************************************************/ + + if (!glfwInit()) { + fprintf(stderr, "Failed to initialize GLFW.\n"); + return 1; + } + + if (glfwVulkanSupported() != GLFW_TRUE) { + fprintf(stderr, "Vulkan is not supported on this device.\n"); + return 1; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + if (!(win = glfwCreateWindow(win_w, win_h, "helloworld rt", 0, 0))) { + fprintf(stderr, "Failed to create GLFW window\n"); + return 1; + } + + glfwSetKeyCallback(win, clb_key); + glfwSetWindowSizeCallback(win, clb_reshape); + glfwSetCursorPosCallback(win, clb_motion); + glfwSetMouseButtonCallback(win, clb_mouse); + + /*********************************************************** + * VULKAN context + ***********************************************************/ + + if (!vk_init_ctx_for_rendering(&vk_core, true, vk_enable_layers)) { + fprintf(stderr, "Failed to initialize Vulkan context.\n"); + return 1; + } + + /*********************************************************** + * XCB/GLFW surface + ***********************************************************/ + + glfwGetFramebufferSize(win, &win_h, &win_h); + if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf) + != VK_SUCCESS) { + fprintf(stderr, "Failed to create XCB surface.\n"); + return 1; + } + + /*********************************************************** + * Initialize Vulkan structs + ***********************************************************/ + + if (!vk_init()) { + return 1; + } + + /*********************************************************** + * Initialize program objects, classes etc + ***********************************************************/ + + if (!init()) { + return 1; + } + + /*********************************************************** + * Rendering loop + ***********************************************************/ + + while(!glfwWindowShouldClose(win)) { + glfwPollEvents(); + display(); + } + + vkDeviceWaitIdle(vk_core.dev); + return 0; +} + +/* static functions */ + +static bool +vk_init() +{ + uint32_t i; + + /* create swapchain */ + if (!vk_create_swapchain(&vk_core, win_w, win_h, false, vk_surf, 0, &vk_chain)) { + fprintf(stderr, "No swapchain was created.\n"); + return false; + } + + if (vk_chain.swapchain == VK_NULL_HANDLE) { + fprintf(stderr, "Invalid swapchain handle.\n"); + return false; + } + + if (!vk_create_sync_objects()) { + fprintf(stderr, "Failed to create sync objects.\n"); + return false; + } + + /* FIXME for the moment one cmd buf. + * But most probably I need to change this later. */ + VkCommandBuffer cmd_buf; + if (!(cmd_buf = vk_create_cmd_buffer(&vk_core))) { + fprintf(stderr, "Failed to create command buffer: %d.\n", 0); + return false; + } + + vk_cmd_buffers = (VkCommandBuffer*)malloc(vk_chain.num_images * sizeof *vk_cmd_buffers); + memset(vk_cmd_buffers, 0, vk_chain.num_images * sizeof *vk_cmd_buffers); + vk_cmd_buffers[0] = cmd_buf; + + /* FIXME: this part is going to be part of each object's + * renderpass and pipeline */ + + /* create depth attachment (for the moment we are going to use this + * for all images */ + if (!vk_fill_image_props(&vk_core, + win_w, win_h, 1, + 1, 1, 1, + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + false, true, false, + &vk_depth_att.props)) { + fprintf(stderr, "Unsupported depth image properties\n"); + return false; + } + if (!vk_create_image(&vk_core, &vk_depth_att.props, &vk_depth_att.obj)) { + fprintf(stderr, "Failed to create depth attachment.\n"); + return false; + } + if(!vk_create_image_view(&vk_core, vk_depth_att.obj.img, VK_IMAGE_VIEW_TYPE_2D, + vk_depth_att.props.format, false, + &vk_depth_att.obj.img_view)) { + fprintf(stderr, "Failed to create image view for depth attachment.\n"); + return false; + } + + /* load shaders */ + int vsz, fsz; + char *vsdr = sdr_load("data/main.vert.spv", &vsz); + char *fsdr = sdr_load("data/main.frag.spv", &fsz); + + /* create renderer */ + if (!vk_create_renderer(&vk_core, + vsdr, vsz, fsdr, fsz, + win_w, win_h, 1, + true, false, + 1, &vk_chain.img_props, &vk_depth_att.props, + 0, &vk_rnd)) { + goto fail; + } + + vk_framebuffers = (VkFramebuffer*)malloc(vk_chain.num_images * sizeof vk_framebuffers[0]); + memset(vk_framebuffers, VK_NULL_HANDLE, vk_chain.num_images * sizeof vk_framebuffers[0]); + + for (i = 0; i < vk_chain.num_images; i++) { + if (!vk_create_framebuffer(&vk_core, + win_w, win_h, 1, + &vk_chain.views[i], + &vk_depth_att.obj.img_view, + vk_rnd.renderpass, + &vk_framebuffers[i])) + goto fail; + } + + /* record cmd buffer FIXME: + * should move this and make it part of each renderer + * also add all other stuff needed like uniforms + * descriptor sets spec constants push constants etc + * renderer + */ + + free(vsdr); + free(fsdr); + + return true; + +fail: + free(vsdr); + free(fsdr); + + return false; +} + +static bool +init() +{ + return true; +} + +static void +cleanup() +{ + vk_cleanup(); +} + +/* FIXME */ +static int count; +static void +display() +{ + vkWaitForFences(vk_core.dev, 1, &vk_fences[vk_frame_idx], VK_TRUE, UINT64_MAX); + vkResetFences(vk_core.dev, 1, &vk_fences[vk_frame_idx]); + + VkResult err; + do { + err = vkAcquireNextImageKHR(vk_core.dev, vk_chain.swapchain, + UINT64_MAX, + vk_semas[vk_frame_idx].frame_ready, + 0, &vk_current_image); + switch(err) { + case VK_ERROR_OUT_OF_DATE_KHR: + fprintf(stderr, "acquire next image error: VK_ERROR_OUT_OF_DATE_KHR.\n"); + abort(); + break; + case VK_SUBOPTIMAL_KHR: + fprintf(stderr, "AcquireNextImageKHR returned VK_SUBOPTIMAL_KHR, ignored.\n"); + abort(); + break; + case VK_ERROR_SURFACE_LOST_KHR: + vkDestroySurfaceKHR(vk_core.inst, vk_surf, 0); + if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf) != + VK_SUCCESS) { + fprintf(stderr, "Failed to recreate GLFW/XCB surface.\n"); + } + abort(); + break; + default: + assert(!err); + break; + } + } while (err != VK_SUCCESS); + + /* FIXME update buffer data */ + if (!vk_record_cmd_buffer(&vk_core, + vk_cmd_buffers[0], + &vk_rnd, + 4, vk_fb_color, + vk_framebuffers[vk_current_image], + 2, 0, + 0, 0, win_w, win_h)) { + fprintf(stderr, "Failed to record command buffer.\n"); + abort(); + } + + /* each object should have a command buffer renderpass etc? */ + VkSubmitInfo sinfo; + VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + memset(&sinfo, 0, sizeof sinfo); + sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + sinfo.waitSemaphoreCount = 1; + sinfo.pWaitSemaphores = &vk_semas[vk_frame_idx].frame_ready; + sinfo.pWaitDstStageMask = &wait_stages; + sinfo.commandBufferCount = 1; + sinfo.pCommandBuffers = &vk_cmd_buffers[0]; + sinfo.signalSemaphoreCount = 1; + sinfo.pSignalSemaphores = &vk_semas[vk_frame_idx].frame_done; + + if (vkQueueSubmit(vk_core.queue, 1, &sinfo, vk_fences[vk_frame_idx]) != 0) { + fprintf(stderr, "Failed to submit draw commands.\n"); + abort(); + } + + if (vkQueueWaitIdle(vk_core.queue) != VK_SUCCESS) { + fprintf(stderr, "Failed to wait idle.\n"); + abort(); + } + + if (!vk_queue_present(&vk_chain, vk_core.queue, vk_current_image, + vk_semas[vk_frame_idx].frame_done)) { + abort(); + } + + vk_frame_idx = (vk_frame_idx + 1) % (vk_chain.num_images - 1); + + printf("display %d\n", count++); + printf("current image %u\n", vk_current_image); + printf("frame idx %d\n", vk_frame_idx); +} + +static void +vk_cleanup() +{ + uint32_t i; + int num_images = vk_chain.num_images - 1; + + vk_destroy_image(&vk_core, &vk_depth_att.obj); + vk_destroy_renderer(&vk_core, &vk_rnd); + + for (i = 0; i < vk_chain.num_images; i++) { + vkDestroyFramebuffer(vk_core.dev, vk_framebuffers[i], 0); + } + + glfwDestroyWindow(win); + + if (vk_chain.swapchain) { + vk_destroy_swapchain(&vk_core, &vk_chain); + vkDestroySurfaceKHR(vk_core.inst, vk_surf, 0); + } + + if (vk_enable_layers) + return; + + vk_destroy_cmd_buffers(&vk_core, vk_chain.num_images, vk_cmd_buffers); + + vk_destroy_fences(&vk_core, vk_chain.num_images, vk_fences); + + for (int i = 0; i < num_images; i++) + vk_destroy_semaphores(&vk_core, &vk_semas[i]); + free(vk_semas); + + glfwTerminate(); + + vk_cleanup_ctx(&vk_core); +} + +static bool +vk_create_sync_objects() +{ + VkFenceCreateInfo finfo; + memset(&finfo, 0, sizeof finfo); + finfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + finfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + VkSemaphoreCreateInfo sinfo; + memset(&sinfo, 0, sizeof sinfo); + sinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + vk_fences = (VkFence*)malloc((vk_chain.num_images - 1) * sizeof *vk_fences); + memset(vk_fences, VK_NULL_HANDLE, (vk_chain.num_images - 1) * sizeof *vk_fences); + + vk_semas = (struct vk_semaphores *)malloc(sizeof(struct vk_semaphores) * (vk_chain.num_images - 1)); + memset(vk_semas, VK_NULL_HANDLE, (vk_chain.num_images - 1) * sizeof *vk_fences); + + for (uint32_t i = 0; i < (vk_chain.num_images - 1); i++) { + if (vkCreateFence(vk_core.dev, &finfo, 0, &vk_fences[i]) != VK_SUCCESS) { + fprintf(stderr, "Failed to create fence: %d\n", i); + return false; + } + + if (vkCreateSemaphore(vk_core.dev, &sinfo, 0, &vk_semas[i].frame_ready) != VK_SUCCESS) { + fprintf(stderr, "Failed to create frame_ready semaphore: %d\n", i); + return false; + } + + if (vkCreateSemaphore(vk_core.dev, &sinfo, 0, &vk_semas[i].frame_done) != VK_SUCCESS) { + fprintf(stderr, "Failed to create frame_done semaphore: %d\n", i); + return false; + } + } + + return true; +} + +/* glfw callbacks */ + +static void +clb_reshape(GLFWwindow *win, + int width, + int height) +{ + aspect = (float)width / (float)height; +#if 0 + mproj = calc_projection_matrix(45, aspect, 0.5, 1000.0f); +#endif + + /* FIXME near and far clipping planes */ + vk_set_viewport(&vk_core, vk_cmd_buffers[0], + 0, 0, win_w, win_h, 0.0f, 1.0f); + win_w = width; + win_h = height; +} + +static void +clb_key(GLFWwindow *win, int key, int scancode, + int action, int mods) +{ + if (action == GLFW_REPEAT) return; + + if (action == GLFW_PRESS) { + switch(key) { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(win, GLFW_TRUE); + exit(0); + case ' ': + move_camera = !move_camera; + break; + default: + break; + } + } +} + +static double prev_x, prev_y; +static bool button[8]; + +static void +clb_motion(GLFWwindow *win, + double x, + double y) +{ + double dx = x - prev_x; + double dy = y - prev_y; + + prev_x = x; + prev_y = y; + + if(button[0]) { + cam_theta += dx * 0.5; + cam_phi += dy * 0.5; + + if(cam_phi < 0) + cam_phi = 0; + if(cam_phi > 90) + cam_phi = 90; + } + + if(button[1]) { + cam_dist += dy * 0.1; + if(cam_dist < 0.0) { + cam_dist = 0.0; + } + } +} + +static void +clb_mouse(GLFWwindow *win, + int button, + int action, + int mods) +{ +}