fixed return values in main
[vkrt] / src / main.cc
1 #include <stdio.h>
2
3 #define GLFW_INCLUDE_VULKAN
4 #include <GLFW/glfw3.h>
5
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <vector>
11
12 #include <gmath/gmath.h>
13
14 /* extern "C": vulkan framework */
15 #include "vk.h"
16 #include "util.h"
17
18 /* C++ */
19 #include "camera.h"
20
21 /* defines */
22 #define FENCE_TIMEOUT UINT64_MAX
23
24 /**************************/
25 /* static glfw callbacks */
26 /**************************/
27
28 static void
29 clb_key(GLFWwindow *win, int key, int scancode, int action, int mods);
30
31 static void
32 clb_reshape(GLFWwindow *win, int width, int height);
33
34 static void
35 clb_motion(GLFWwindow *win, double x, double y);
36
37 static void
38 clb_mouse(GLFWwindow *win, int button, int action, int mods);
39
40 /**************************/
41 /* static functions */
42 /**************************/
43
44 /* init, cleanup, display */
45
46 static bool
47 init();
48
49 static void
50 cleanup();
51
52 static void
53 display();
54
55 /* vulkan, glfw */
56
57 static bool
58 vk_init();
59
60 static bool
61 vk_create_sync_objects();
62
63 static void
64 vk_cleanup();
65
66 /***************************/
67 /* static/global variables */
68 /***************************/
69
70 static GLFWwindow *win;
71 /* static bool redraw_pending; */
72 static bool move_camera;
73
74 /* camera */
75 static float cam_phi = 25;
76 static float cam_theta = 0;
77 static float cam_dist = 16;
78 static Vec3 cam_pos;
79
80 static OrbitCamera *camera;
81
82 static float aspect;
83 static Mat4 mproj;
84
85 /* win etc */
86 static int win_w = 800;
87 static int win_h = 600;
88
89 /* vulkan */
90 static bool vk_enable_layers = true;
91 static struct vk_ctx vk_core;
92 static VkSurfaceKHR vk_surf;
93
94 static struct vk_swapchain vk_chain;
95 static uint32_t vk_current_image;
96 static int vk_frame_idx;
97 static std::vector<VkFramebuffer>vk_framebuffers;
98
99 /* for the moment: one cmd buffer per swapchain image */
100 static std::vector<VkCommandBuffer>vk_cmd_buffers;
101 static std::vector<VkFence>vk_fences;
102 static struct vk_semaphores *vk_semas;
103
104 /* FIXME make them part of each object's functions/params */
105 static struct vk_renderer vk_rnd;
106 static struct vk_attachment vk_depth_att;
107 static float vk_fb_color[4] = { 0.0, 0.0, 0.5, 1.0 };
108
109 /* empty for as long as we hardcode the vertices in the vertex shader */
110 #if 0
111 static struct vk_vertex_info vk_vert_info;
112 #endif
113
114 int main(int argc, char** argv)
115 {
116     atexit(cleanup);
117
118     /***********************************************************
119      * GLFW
120      ***********************************************************/
121
122     if (!glfwInit()) {
123         fprintf(stderr, "Failed to initialize GLFW.\n");
124         return 1;
125     }
126
127     if (glfwVulkanSupported() != GLFW_TRUE) {
128         fprintf(stderr, "Vulkan is not supported on this device.\n");
129         return 1;
130     }
131
132     glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
133     glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
134     if (!(win = glfwCreateWindow(win_w, win_h, "helloworld rt", 0, 0))) {
135         fprintf(stderr, "Failed to create GLFW window\n");
136         return 1;
137     }
138
139     glfwSetKeyCallback(win, clb_key);
140     glfwSetWindowSizeCallback(win, clb_reshape);
141     glfwSetCursorPosCallback(win, clb_motion);
142     glfwSetMouseButtonCallback(win, clb_mouse);
143
144     /***********************************************************
145      * VULKAN context
146      ***********************************************************/
147
148     if (!vk_init_ctx_for_rendering(&vk_core, true, vk_enable_layers)) {
149         fprintf(stderr, "Failed to initialize Vulkan context.\n");
150         return 1;
151     }
152
153     /***********************************************************
154      * XCB/GLFW surface
155      ***********************************************************/
156
157     glfwGetFramebufferSize(win, &win_h, &win_h);
158     if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf)
159             != VK_SUCCESS) {
160         fprintf(stderr, "Failed to create XCB surface.\n");
161         return 1;
162     }
163
164     /***********************************************************
165      * Initialize Vulkan structs
166      ***********************************************************/
167
168     if (!vk_init()) {
169         return 1;
170     }
171
172     /***********************************************************
173      * Initialize program objects, classes etc
174      ***********************************************************/
175
176     if (!init()) {
177         return 1;
178     }
179
180     /***********************************************************
181      * Rendering loop
182      ***********************************************************/
183
184         while(!glfwWindowShouldClose(win)) {
185                 glfwPollEvents();
186                 display();
187         }
188
189     vkDeviceWaitIdle(vk_core.dev);
190     return 0;
191 }
192
193 /* static functions */
194
195 static bool
196 vk_init()
197 {
198     /* create swapchain */
199     if (!vk_create_swapchain(&vk_core, win_w, win_h, false, vk_surf, 0, &vk_chain)) {
200         fprintf(stderr, "No swapchain was created.\n");
201         return false;
202     }
203
204     if (vk_chain.swapchain == VK_NULL_HANDLE) {
205         fprintf(stderr, "Invalid swapchain handle.\n");
206         return false;
207     }
208
209     if (!vk_create_sync_objects()) {
210         fprintf(stderr, "Failed to create sync objects.\n");
211         return false;
212     }
213
214     /* FIXME for the moment one cmd buf.
215      * But most probably I need to change this later. */
216     VkCommandBuffer cmd_buf;
217     if (!(cmd_buf = vk_create_cmd_buffer(&vk_core))) {
218         fprintf(stderr, "Failed to create command buffer: %d.\n", 0);
219         return false;
220     }
221     vk_cmd_buffers.push_back(cmd_buf);
222
223     /* FIXME: this part is going to be part of each object's
224      * renderpass and pipeline */
225
226     /* create depth attachment (for the moment we are going to use this
227      * for all images */
228     if (!vk_fill_image_props(&vk_core,
229                              win_w, win_h, 1,
230                              1, 1, 1,
231                              VK_FORMAT_D32_SFLOAT_S8_UINT,
232                              VK_IMAGE_TILING_OPTIMAL,
233                              VK_IMAGE_LAYOUT_UNDEFINED,
234                              VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
235                              false, true, false,
236                              &vk_depth_att.props)) {
237         fprintf(stderr, "Unsupported depth image properties\n");
238         return false;
239     }
240     if (!vk_create_image(&vk_core, &vk_depth_att.props, &vk_depth_att.obj)) {
241         fprintf(stderr, "Failed to create depth attachment.\n");
242         return false;
243     }
244     if(!vk_create_image_view(&vk_core, vk_depth_att.obj.img, VK_IMAGE_VIEW_TYPE_2D,
245                              vk_depth_att.props.format, false,
246                              &vk_depth_att.obj.img_view)) {
247         fprintf(stderr, "Failed to create image view for depth attachment.\n");
248         return false;
249     }
250
251     /* load shaders */
252     int vsz, fsz;
253     char *vsdr = sdr_load("data/main.vert.spv", &vsz);
254     char *fsdr = sdr_load("data/main.frag.spv", &fsz);
255
256     /* create renderer */
257     if (!vk_create_renderer(&vk_core,
258                             vsdr, vsz, fsdr, fsz,
259                             win_w, win_h, 1,
260                             true, false,
261                             1, &vk_chain.img_props, &vk_depth_att.props,
262                             0, &vk_rnd)) {
263         goto fail;
264     }
265
266     vk_framebuffers.resize(vk_chain.num_images, VK_NULL_HANDLE);
267     for (uint32_t i = 0; i < vk_chain.num_images; i++) {
268         if (!vk_create_framebuffer(&vk_core,
269                                    win_w, win_h, 1,
270                                    &vk_chain.views[i],
271                                    &vk_depth_att.obj.img_view,
272                                    vk_rnd.renderpass,
273                                    &vk_framebuffers[i]))
274         goto fail;
275     }
276
277     /* record cmd buffer FIXME:
278      * should move this and make it part of each renderer
279      * also add all other stuff needed like uniforms
280      * descriptor sets spec constants push constants etc
281      * renderer
282      */
283
284     free(vsdr);
285     free(fsdr);
286
287     return true;
288
289 fail:
290     free(vsdr);
291     free(fsdr);
292
293     return false;
294 }
295
296 static bool
297 init()
298 {
299     camera = new OrbitCamera;
300     return true;
301 }
302
303 static void
304 cleanup()
305 {
306     delete camera;
307
308     vk_cleanup();
309 }
310
311 /* FIXME */
312 static int count;
313 static void
314 display()
315 {
316     vkWaitForFences(vk_core.dev, 1, &vk_fences[vk_frame_idx], VK_TRUE, UINT64_MAX);
317     vkResetFences(vk_core.dev, 1, &vk_fences[vk_frame_idx]);
318
319     VkResult err;
320     do {
321         err = vkAcquireNextImageKHR(vk_core.dev, vk_chain.swapchain,
322                                     UINT64_MAX,
323                                     vk_semas[vk_frame_idx].frame_ready,
324                                     0, &vk_current_image);
325         switch(err) {
326         case VK_ERROR_OUT_OF_DATE_KHR:
327             fprintf(stderr, "acquire next image error: VK_ERROR_OUT_OF_DATE_KHR.\n");
328             abort();
329             break;
330         case VK_SUBOPTIMAL_KHR:
331             fprintf(stderr, "AcquireNextImageKHR returned VK_SUBOPTIMAL_KHR, ignored.\n");
332             abort();
333             break;
334         case VK_ERROR_SURFACE_LOST_KHR:
335             vkDestroySurfaceKHR(vk_core.inst, vk_surf, 0);
336             if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf) !=
337                     VK_SUCCESS) {
338                 fprintf(stderr, "Failed to recreate GLFW/XCB surface.\n");
339             }
340             abort();
341             break;
342         default:
343             assert(!err);
344             break;
345         }
346     } while (err != VK_SUCCESS);
347
348     /* FIXME update buffer data */
349     if (!vk_record_cmd_buffer(&vk_core,
350                 vk_cmd_buffers[0],
351                 &vk_rnd,
352                 4, vk_fb_color,
353                 vk_framebuffers[vk_current_image],
354                 2, 0,
355                 0, 0, win_w, win_h)) {
356         fprintf(stderr, "Failed to record command buffer.\n");
357         abort();
358     }
359
360     /* each object should have a command buffer renderpass etc? */
361     VkSubmitInfo sinfo;
362     VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
363
364     memset(&sinfo, 0, sizeof sinfo);
365     sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
366     sinfo.waitSemaphoreCount = 1;
367     sinfo.pWaitSemaphores = &vk_semas[vk_frame_idx].frame_ready;
368     sinfo.pWaitDstStageMask = &wait_stages;
369     sinfo.commandBufferCount = 1;
370     sinfo.pCommandBuffers = &vk_cmd_buffers[0];
371     sinfo.signalSemaphoreCount = 1;
372         sinfo.pSignalSemaphores = &vk_semas[vk_frame_idx].frame_done;
373
374     if (vkQueueSubmit(vk_core.queue, 1, &sinfo, vk_fences[vk_frame_idx]) != 0) {
375         fprintf(stderr, "Failed to submit draw commands.\n");
376         abort();
377     }
378
379     if (vkQueueWaitIdle(vk_core.queue) != VK_SUCCESS) {
380         fprintf(stderr, "Failed to wait idle.\n");
381         abort();
382     }
383
384     if (!vk_queue_present(&vk_chain, vk_core.queue, vk_current_image,
385                           vk_semas[vk_frame_idx].frame_done)) {
386         abort();
387     }
388
389     vk_frame_idx = (vk_frame_idx + 1) % (vk_chain.num_images - 1);
390
391     printf("display %d\n", count++);
392     printf("current image %u\n", vk_current_image);
393     printf("frame idx %d\n", vk_frame_idx);
394 }
395
396 static void
397 vk_cleanup()
398 {
399     int num_images = vk_chain.num_images - 1;
400
401     vk_destroy_image(&vk_core, &vk_depth_att.obj);
402     vk_destroy_renderer(&vk_core, &vk_rnd);
403
404     for (size_t i = 0; i < vk_framebuffers.size(); i++) {
405         vkDestroyFramebuffer(vk_core.dev, vk_framebuffers[i], 0);
406     }
407     vk_framebuffers.clear();
408
409     glfwDestroyWindow(win);
410
411     if (vk_chain.swapchain) {
412         vk_destroy_swapchain(&vk_core, &vk_chain);
413         vkDestroySurfaceKHR(vk_core.inst, vk_surf, 0);
414     }
415
416     if (vk_enable_layers)
417         return;
418
419     vk_destroy_cmd_buffers(&vk_core, vk_cmd_buffers.size(), vk_cmd_buffers.data());
420     vk_cmd_buffers.clear();
421
422     vk_destroy_fences(&vk_core, vk_fences.size(), vk_fences.data());
423     vk_fences.clear();
424
425     for (int i = 0; i < num_images; i++)
426         vk_destroy_semaphores(&vk_core, &vk_semas[i]);
427     free(vk_semas);
428
429     for (size_t i = 0; i < vk_cmd_buffers.size(); i++)
430         vk_destroy_cmd_buffers(&vk_core, vk_cmd_buffers.size(), vk_cmd_buffers.data());
431     vk_cmd_buffers.clear();
432
433     glfwTerminate();
434
435     vk_cleanup_ctx(&vk_core);
436 }
437
438 static bool
439 vk_create_sync_objects()
440 {
441     VkFenceCreateInfo finfo;
442     memset(&finfo, 0, sizeof finfo);
443     finfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
444     finfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
445
446     VkSemaphoreCreateInfo sinfo;
447     memset(&sinfo, 0, sizeof sinfo);
448     sinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
449
450     vk_fences.resize(vk_chain.num_images - 1, VK_NULL_HANDLE);
451
452     vk_semas = (vk_semaphores *)malloc(sizeof(vk_semaphores) * (vk_chain.num_images - 1));
453     for (uint32_t i = 0; i < (vk_chain.num_images - 1); i++) {
454         if (vkCreateFence(vk_core.dev, &finfo, 0, &vk_fences[i]) != VK_SUCCESS) {
455             fprintf(stderr, "Failed to create fence: %d\n", i);
456             return false;
457         }
458
459         if (vkCreateSemaphore(vk_core.dev, &sinfo, 0, &vk_semas[i].frame_ready) != VK_SUCCESS) {
460             fprintf(stderr, "Failed to create frame_ready semaphore: %d\n", i);
461             return false;
462         }
463
464         if (vkCreateSemaphore(vk_core.dev, &sinfo, 0, &vk_semas[i].frame_done) != VK_SUCCESS) {
465             fprintf(stderr, "Failed to create frame_done semaphore: %d\n", i);
466             return false;
467         }
468     }
469
470     return true;
471 }
472
473 /* glfw callbacks */
474
475 static void
476 clb_reshape(GLFWwindow *win,
477             int width,
478             int height)
479 {
480     aspect = (float)width / (float)height;
481     mproj = calc_projection_matrix(45, aspect, 0.5, 1000.0f);
482
483     /* FIXME near and far clipping planes */
484     vk_set_viewport(&vk_core, vk_cmd_buffers[0],
485                     0, 0, win_w, win_h, 0.0f, 1.0f);
486     win_w = width;
487     win_h = height;
488 }
489
490 static void
491 clb_key(GLFWwindow *win, int key, int scancode,
492         int action, int mods)
493 {
494     if (action == GLFW_REPEAT) return;
495
496     if (action == GLFW_PRESS) {
497         switch(key) {
498         case GLFW_KEY_ESCAPE:
499             glfwSetWindowShouldClose(win, GLFW_TRUE);
500             exit(0);
501         case ' ':
502             move_camera = !move_camera;
503             break;
504         default:
505             break;
506         }
507     }
508 }
509
510 static double prev_x, prev_y;
511 static bool button[8];
512
513 static void
514 clb_motion(GLFWwindow *win,
515            double x,
516            double y)
517 {
518         double dx = x - prev_x;
519         double dy = y - prev_y;
520
521         prev_x = x;
522         prev_y = y;
523
524         if(button[0]) {
525                 cam_theta += dx * 0.5;
526                 cam_phi += dy * 0.5;
527
528                 if(cam_phi < 0)
529                         cam_phi = 0;
530                 if(cam_phi > 90)
531                         cam_phi = 90;
532         }
533
534         if(button[1]) {
535                 cam_dist += dy * 0.1;
536                 if(cam_dist < 0.0) {
537                         cam_dist = 0.0;
538                 }
539         }
540 }
541
542 static void
543 clb_mouse(GLFWwindow *win,
544           int button,
545           int action,
546           int mods)
547 {
548 }