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