1 /*
2 * Copyright (c) 2015-2016 The Khronos Group Inc.
3 * Copyright (c) 2015-2016 Valve Corporation
4 * Copyright (c) 2015-2016 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and/or associated documentation files (the "Materials"), to
8 * deal in the Materials without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Materials, and to permit persons to whom the Materials are
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice(s) and this permission notice shall be included in
14 * all copies or substantial portions of the Materials.
15 *
16 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 *
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23 * USE OR OTHER DEALINGS IN THE MATERIALS.
24 *
25 * Author: Chia-I Wu <olv@lunarg.com>
26 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
27 * Author: Ian Elliott <ian@LunarG.com>
28 * Author: Jon Ashburn <jon@lunarg.com>
29 */
30
31 #define _GNU_SOURCE
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdbool.h>
36 #include <assert.h>
37 #include <signal.h>
38
39 #ifdef _WIN32
40 #pragma comment(linker, "/subsystem:windows")
41 #define APP_NAME_STR_LEN 80
42 #endif // _WIN32
43
44 #include <vulkan/vulkan.h>
45
46 #include <vulkan/vk_sdk_platform.h>
47 #include "linmath.h"
48
49 #define DEMO_TEXTURE_COUNT 1
50 #define APP_SHORT_NAME "cube"
51 #define APP_LONG_NAME "The Vulkan Cube Demo Program"
52
53 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
54
55 #if defined(NDEBUG) && defined(__GNUC__)
56 #define U_ASSERT_ONLY __attribute__((unused))
57 #else
58 #define U_ASSERT_ONLY
59 #endif
60
61 #ifdef _WIN32
62 #define ERR_EXIT(err_msg, err_class) \
63 do { \
64 MessageBox(NULL, err_msg, err_class, MB_OK); \
65 exit(1); \
66 } while (0)
67
68 #else // _WIN32
69
70 #define ERR_EXIT(err_msg, err_class) \
71 do { \
72 printf(err_msg); \
73 fflush(stdout); \
74 exit(1); \
75 } while (0)
76 #endif // _WIN32
77
78 #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
79 { \
80 demo->fp##entrypoint = \
81 (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \
82 if (demo->fp##entrypoint == NULL) { \
83 ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \
84 "vkGetInstanceProcAddr Failure"); \
85 } \
86 }
87
88 static PFN_vkGetDeviceProcAddr g_gdpa = NULL;
89
90 #define GET_DEVICE_PROC_ADDR(dev, entrypoint) \
91 { \
92 if (!g_gdpa) \
93 g_gdpa = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr( \
94 demo->inst, "vkGetDeviceProcAddr"); \
95 demo->fp##entrypoint = \
96 (PFN_vk##entrypoint)g_gdpa(dev, "vk" #entrypoint); \
97 if (demo->fp##entrypoint == NULL) { \
98 ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \
99 "vkGetDeviceProcAddr Failure"); \
100 } \
101 }
102
103 /*
104 * structure to track all objects related to a texture.
105 */
106 struct texture_object {
107 VkSampler sampler;
108
109 VkImage image;
110 VkImageLayout imageLayout;
111
112 VkMemoryAllocateInfo mem_alloc;
113 VkDeviceMemory mem;
114 VkImageView view;
115 int32_t tex_width, tex_height;
116 };
117
118 static char *tex_files[] = {"lunarg.ppm"};
119
120 static int validation_error = 0;
121
122 struct vkcube_vs_uniform {
123 // Must start with MVP
124 float mvp[4][4];
125 float position[12 * 3][4];
126 float color[12 * 3][4];
127 };
128
129 struct vktexcube_vs_uniform {
130 // Must start with MVP
131 float mvp[4][4];
132 float position[12 * 3][4];
133 float attr[12 * 3][4];
134 };
135
136 //--------------------------------------------------------------------------------------
137 // Mesh and VertexFormat Data
138 //--------------------------------------------------------------------------------------
139 // clang-format off
140 struct Vertex
141 {
142 float posX, posY, posZ, posW; // Position data
143 float r, g, b, a; // Color
144 };
145
146 struct VertexPosTex
147 {
148 float posX, posY, posZ, posW; // Position data
149 float u, v, s, t; // Texcoord
150 };
151
152 #define XYZ1(_x_, _y_, _z_) (_x_), (_y_), (_z_), 1.f
153 #define UV(_u_, _v_) (_u_), (_v_), 0.f, 1.f
154
155 static const float g_vertex_buffer_data[] = {
156 -1.0f,-1.0f,-1.0f, // -X side
157 -1.0f,-1.0f, 1.0f,
158 -1.0f, 1.0f, 1.0f,
159 -1.0f, 1.0f, 1.0f,
160 -1.0f, 1.0f,-1.0f,
161 -1.0f,-1.0f,-1.0f,
162
163 -1.0f,-1.0f,-1.0f, // -Z side
164 1.0f, 1.0f,-1.0f,
165 1.0f,-1.0f,-1.0f,
166 -1.0f,-1.0f,-1.0f,
167 -1.0f, 1.0f,-1.0f,
168 1.0f, 1.0f,-1.0f,
169
170 -1.0f,-1.0f,-1.0f, // -Y side
171 1.0f,-1.0f,-1.0f,
172 1.0f,-1.0f, 1.0f,
173 -1.0f,-1.0f,-1.0f,
174 1.0f,-1.0f, 1.0f,
175 -1.0f,-1.0f, 1.0f,
176
177 -1.0f, 1.0f,-1.0f, // +Y side
178 -1.0f, 1.0f, 1.0f,
179 1.0f, 1.0f, 1.0f,
180 -1.0f, 1.0f,-1.0f,
181 1.0f, 1.0f, 1.0f,
182 1.0f, 1.0f,-1.0f,
183
184 1.0f, 1.0f,-1.0f, // +X side
185 1.0f, 1.0f, 1.0f,
186 1.0f,-1.0f, 1.0f,
187 1.0f,-1.0f, 1.0f,
188 1.0f,-1.0f,-1.0f,
189 1.0f, 1.0f,-1.0f,
190
191 -1.0f, 1.0f, 1.0f, // +Z side
192 -1.0f,-1.0f, 1.0f,
193 1.0f, 1.0f, 1.0f,
194 -1.0f,-1.0f, 1.0f,
195 1.0f,-1.0f, 1.0f,
196 1.0f, 1.0f, 1.0f,
197 };
198
199 static const float g_uv_buffer_data[] = {
200 0.0f, 0.0f, // -X side
201 1.0f, 0.0f,
202 1.0f, 1.0f,
203 1.0f, 1.0f,
204 0.0f, 1.0f,
205 0.0f, 0.0f,
206
207 1.0f, 0.0f, // -Z side
208 0.0f, 1.0f,
209 0.0f, 0.0f,
210 1.0f, 0.0f,
211 1.0f, 1.0f,
212 0.0f, 1.0f,
213
214 1.0f, 1.0f, // -Y side
215 1.0f, 0.0f,
216 0.0f, 0.0f,
217 1.0f, 1.0f,
218 0.0f, 0.0f,
219 0.0f, 1.0f,
220
221 1.0f, 1.0f, // +Y side
222 0.0f, 1.0f,
223 0.0f, 0.0f,
224 1.0f, 1.0f,
225 0.0f, 0.0f,
226 1.0f, 0.0f,
227
228 1.0f, 1.0f, // +X side
229 0.0f, 1.0f,
230 0.0f, 0.0f,
231 0.0f, 0.0f,
232 1.0f, 0.0f,
233 1.0f, 1.0f,
234
235 0.0f, 1.0f, // +Z side
236 0.0f, 0.0f,
237 1.0f, 1.0f,
238 0.0f, 0.0f,
239 1.0f, 0.0f,
240 1.0f, 1.0f,
241 };
242 // clang-format on
243
dumpMatrix(const char * note,mat4x4 MVP)244 void dumpMatrix(const char *note, mat4x4 MVP) {
245 int i;
246
247 printf("%s: \n", note);
248 for (i = 0; i < 4; i++) {
249 printf("%f, %f, %f, %f\n", MVP[i][0], MVP[i][1], MVP[i][2], MVP[i][3]);
250 }
251 printf("\n");
252 fflush(stdout);
253 }
254
dumpVec4(const char * note,vec4 vector)255 void dumpVec4(const char *note, vec4 vector) {
256 printf("%s: \n", note);
257 printf("%f, %f, %f, %f\n", vector[0], vector[1], vector[2], vector[3]);
258 printf("\n");
259 fflush(stdout);
260 }
261
262 VKAPI_ATTR VkBool32 VKAPI_CALL
dbgFunc(VkFlags msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)263 dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
264 uint64_t srcObject, size_t location, int32_t msgCode,
265 const char *pLayerPrefix, const char *pMsg, void *pUserData) {
266 char *message = (char *)malloc(strlen(pMsg) + 100);
267
268 assert(message);
269
270 if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
271 sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode,
272 pMsg);
273 validation_error = 1;
274 } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
275 // We know that we're submitting queues without fences, ignore this
276 // warning
277 if (strstr(pMsg,
278 "vkQueueSubmit parameter, VkFence fence, is null pointer")) {
279 return false;
280 }
281 sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode,
282 pMsg);
283 validation_error = 1;
284 } else {
285 validation_error = 1;
286 return false;
287 }
288
289 #ifdef _WIN32
290 MessageBox(NULL, message, "Alert", MB_OK);
291 #else
292 printf("%s\n", message);
293 fflush(stdout);
294 #endif
295 free(message);
296
297 /*
298 * false indicates that layer should not bail-out of an
299 * API call that had validation failures. This may mean that the
300 * app dies inside the driver due to invalid parameter(s).
301 * That's what would happen without validation layers, so we'll
302 * keep that behavior here.
303 */
304 return false;
305 }
306
BreakCallback(VkFlags msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)307 VkBool32 BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
308 uint64_t srcObject, size_t location, int32_t msgCode,
309 const char *pLayerPrefix, const char *pMsg,
310 void *pUserData) {
311 #ifndef WIN32
312 raise(SIGTRAP);
313 #else
314 DebugBreak();
315 #endif
316
317 return false;
318 }
319
320 typedef struct _SwapchainBuffers {
321 VkImage image;
322 VkCommandBuffer cmd;
323 VkImageView view;
324 } SwapchainBuffers;
325
326 struct demo {
327 #ifdef _WIN32
328 #define APP_NAME_STR_LEN 80
329 HINSTANCE connection; // hInstance - Windows Instance
330 char name[APP_NAME_STR_LEN]; // Name to put on the window/icon
331 HWND window; // hWnd - window handle
332 #else // _WIN32
333 xcb_connection_t *connection;
334 xcb_screen_t *screen;
335 xcb_window_t window;
336 xcb_intern_atom_reply_t *atom_wm_delete_window;
337 #endif // _WIN32
338 VkSurfaceKHR surface;
339 bool prepared;
340 bool use_staging_buffer;
341
342 VkInstance inst;
343 VkPhysicalDevice gpu;
344 VkDevice device;
345 VkQueue queue;
346 uint32_t graphics_queue_node_index;
347 VkPhysicalDeviceProperties gpu_props;
348 VkQueueFamilyProperties *queue_props;
349 VkPhysicalDeviceMemoryProperties memory_properties;
350
351 uint32_t enabled_extension_count;
352 uint32_t enabled_layer_count;
353 char *extension_names[64];
354 char *device_validation_layers[64];
355
356 int width, height;
357 VkFormat format;
358 VkColorSpaceKHR color_space;
359
360 PFN_vkGetPhysicalDeviceSurfaceSupportKHR
361 fpGetPhysicalDeviceSurfaceSupportKHR;
362 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
363 fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
364 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR
365 fpGetPhysicalDeviceSurfaceFormatsKHR;
366 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
367 fpGetPhysicalDeviceSurfacePresentModesKHR;
368 PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
369 PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
370 PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
371 PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
372 PFN_vkQueuePresentKHR fpQueuePresentKHR;
373 uint32_t swapchainImageCount;
374 VkSwapchainKHR swapchain;
375 SwapchainBuffers *buffers;
376
377 VkCommandPool cmd_pool;
378
379 struct {
380 VkFormat format;
381
382 VkImage image;
383 VkMemoryAllocateInfo mem_alloc;
384 VkDeviceMemory mem;
385 VkImageView view;
386 } depth;
387
388 struct texture_object textures[DEMO_TEXTURE_COUNT];
389
390 struct {
391 VkBuffer buf;
392 VkMemoryAllocateInfo mem_alloc;
393 VkDeviceMemory mem;
394 VkDescriptorBufferInfo buffer_info;
395 } uniform_data;
396
397 VkCommandBuffer cmd; // Buffer for initialization commands
398 VkPipelineLayout pipeline_layout;
399 VkDescriptorSetLayout desc_layout;
400 VkPipelineCache pipelineCache;
401 VkRenderPass render_pass;
402 VkPipeline pipeline;
403
404 mat4x4 projection_matrix;
405 mat4x4 view_matrix;
406 mat4x4 model_matrix;
407
408 float spin_angle;
409 float spin_increment;
410 bool pause;
411
412 VkShaderModule vert_shader_module;
413 VkShaderModule frag_shader_module;
414
415 VkDescriptorPool desc_pool;
416 VkDescriptorSet desc_set;
417
418 VkFramebuffer *framebuffers;
419
420 bool quit;
421 int32_t curFrame;
422 int32_t frameCount;
423 bool validate;
424 bool use_break;
425 PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback;
426 PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback;
427 VkDebugReportCallbackEXT msg_callback;
428 PFN_vkDebugReportMessageEXT DebugReportMessage;
429
430 uint32_t current_buffer;
431 uint32_t queue_count;
432 };
433
434 // Forward declaration:
435 static void demo_resize(struct demo *demo);
436
memory_type_from_properties(struct demo * demo,uint32_t typeBits,VkFlags requirements_mask,uint32_t * typeIndex)437 static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits,
438 VkFlags requirements_mask,
439 uint32_t *typeIndex) {
440 // Search memtypes to find first index with those properties
441 for (uint32_t i = 0; i < 32; i++) {
442 if ((typeBits & 1) == 1) {
443 // Type is available, does it match user properties?
444 if ((demo->memory_properties.memoryTypes[i].propertyFlags &
445 requirements_mask) == requirements_mask) {
446 *typeIndex = i;
447 return true;
448 }
449 }
450 typeBits >>= 1;
451 }
452 // No memory types matched, return failure
453 return false;
454 }
455
demo_flush_init_cmd(struct demo * demo)456 static void demo_flush_init_cmd(struct demo *demo) {
457 VkResult U_ASSERT_ONLY err;
458
459 if (demo->cmd == VK_NULL_HANDLE)
460 return;
461
462 err = vkEndCommandBuffer(demo->cmd);
463 assert(!err);
464
465 const VkCommandBuffer cmd_bufs[] = {demo->cmd};
466 VkFence nullFence = VK_NULL_HANDLE;
467 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
468 .pNext = NULL,
469 .waitSemaphoreCount = 0,
470 .pWaitSemaphores = NULL,
471 .pWaitDstStageMask = NULL,
472 .commandBufferCount = 1,
473 .pCommandBuffers = cmd_bufs,
474 .signalSemaphoreCount = 0,
475 .pSignalSemaphores = NULL};
476
477 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
478 assert(!err);
479
480 err = vkQueueWaitIdle(demo->queue);
481 assert(!err);
482
483 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs);
484 demo->cmd = VK_NULL_HANDLE;
485 }
486
demo_set_image_layout(struct demo * demo,VkImage image,VkImageAspectFlags aspectMask,VkImageLayout old_image_layout,VkImageLayout new_image_layout,VkAccessFlagBits srcAccessMask)487 static void demo_set_image_layout(struct demo *demo, VkImage image,
488 VkImageAspectFlags aspectMask,
489 VkImageLayout old_image_layout,
490 VkImageLayout new_image_layout,
491 VkAccessFlagBits srcAccessMask) {
492 VkResult U_ASSERT_ONLY err;
493
494 if (demo->cmd == VK_NULL_HANDLE) {
495 const VkCommandBufferAllocateInfo cmd = {
496 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
497 .pNext = NULL,
498 .commandPool = demo->cmd_pool,
499 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
500 .commandBufferCount = 1,
501 };
502
503 err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->cmd);
504 assert(!err);
505
506 VkCommandBufferInheritanceInfo cmd_buf_hinfo = {
507 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
508 .pNext = NULL,
509 .renderPass = VK_NULL_HANDLE,
510 .subpass = 0,
511 .framebuffer = VK_NULL_HANDLE,
512 .occlusionQueryEnable = VK_FALSE,
513 .queryFlags = 0,
514 .pipelineStatistics = 0,
515 };
516 VkCommandBufferBeginInfo cmd_buf_info = {
517 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
518 .pNext = NULL,
519 .flags = 0,
520 .pInheritanceInfo = &cmd_buf_hinfo,
521 };
522 err = vkBeginCommandBuffer(demo->cmd, &cmd_buf_info);
523 assert(!err);
524 }
525
526 VkImageMemoryBarrier image_memory_barrier = {
527 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
528 .pNext = NULL,
529 .srcAccessMask = srcAccessMask,
530 .dstAccessMask = 0,
531 .oldLayout = old_image_layout,
532 .newLayout = new_image_layout,
533 .image = image,
534 .subresourceRange = {aspectMask, 0, 1, 0, 1}};
535
536 if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
537 /* Make sure anything that was copying from this image has completed */
538 image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
539 }
540
541 if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
542 image_memory_barrier.dstAccessMask =
543 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
544 }
545
546 if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
547 image_memory_barrier.dstAccessMask =
548 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
549 }
550
551 if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
552 /* Make sure any Copy or CPU writes to image are flushed */
553 image_memory_barrier.dstAccessMask =
554 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
555 }
556
557 VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
558
559 VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
560 VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
561
562 vkCmdPipelineBarrier(demo->cmd, src_stages, dest_stages, 0, 0, NULL, 0,
563 NULL, 1, pmemory_barrier);
564 }
565
demo_draw_build_cmd(struct demo * demo,VkCommandBuffer cmd_buf)566 static void demo_draw_build_cmd(struct demo *demo, VkCommandBuffer cmd_buf) {
567 VkCommandBufferInheritanceInfo cmd_buf_hinfo = {
568 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
569 .pNext = NULL,
570 .renderPass = VK_NULL_HANDLE,
571 .subpass = 0,
572 .framebuffer = VK_NULL_HANDLE,
573 .occlusionQueryEnable = VK_FALSE,
574 .queryFlags = 0,
575 .pipelineStatistics = 0,
576 };
577 const VkCommandBufferBeginInfo cmd_buf_info = {
578 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
579 .pNext = NULL,
580 .flags = 0,
581 .pInheritanceInfo = &cmd_buf_hinfo,
582 };
583 const VkClearValue clear_values[2] = {
584 [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}},
585 [1] = {.depthStencil = {1.0f, 0}},
586 };
587 const VkRenderPassBeginInfo rp_begin = {
588 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
589 .pNext = NULL,
590 .renderPass = demo->render_pass,
591 .framebuffer = demo->framebuffers[demo->current_buffer],
592 .renderArea.offset.x = 0,
593 .renderArea.offset.y = 0,
594 .renderArea.extent.width = demo->width,
595 .renderArea.extent.height = demo->height,
596 .clearValueCount = 2,
597 .pClearValues = clear_values,
598 };
599 VkResult U_ASSERT_ONLY err;
600
601 err = vkBeginCommandBuffer(cmd_buf, &cmd_buf_info);
602 assert(!err);
603
604 vkCmdBeginRenderPass(cmd_buf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
605
606 vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, demo->pipeline);
607 vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS,
608 demo->pipeline_layout, 0, 1, &demo->desc_set, 0,
609 NULL);
610
611 VkViewport viewport;
612 memset(&viewport, 0, sizeof(viewport));
613 viewport.height = (float)demo->height;
614 viewport.width = (float)demo->width;
615 viewport.minDepth = (float)0.0f;
616 viewport.maxDepth = (float)1.0f;
617 vkCmdSetViewport(cmd_buf, 0, 1, &viewport);
618
619 VkRect2D scissor;
620 memset(&scissor, 0, sizeof(scissor));
621 scissor.extent.width = demo->width;
622 scissor.extent.height = demo->height;
623 scissor.offset.x = 0;
624 scissor.offset.y = 0;
625 vkCmdSetScissor(cmd_buf, 0, 1, &scissor);
626
627 vkCmdDraw(cmd_buf, 12 * 3, 1, 0, 0);
628 vkCmdEndRenderPass(cmd_buf);
629
630 VkImageMemoryBarrier prePresentBarrier = {
631 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
632 .pNext = NULL,
633 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
634 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
635 .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
636 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
637 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
638 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
639 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
640
641 prePresentBarrier.image = demo->buffers[demo->current_buffer].image;
642 VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier;
643 vkCmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
644 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0,
645 NULL, 1, pmemory_barrier);
646
647 err = vkEndCommandBuffer(cmd_buf);
648 assert(!err);
649 }
650
demo_update_data_buffer(struct demo * demo)651 void demo_update_data_buffer(struct demo *demo) {
652 mat4x4 MVP, Model, VP;
653 int matrixSize = sizeof(MVP);
654 uint8_t *pData;
655 VkResult U_ASSERT_ONLY err;
656
657 mat4x4_mul(VP, demo->projection_matrix, demo->view_matrix);
658
659 // Rotate 22.5 degrees around the Y axis
660 mat4x4_dup(Model, demo->model_matrix);
661 mat4x4_rotate(demo->model_matrix, Model, 0.0f, 1.0f, 0.0f,
662 (float)degreesToRadians(demo->spin_angle));
663 mat4x4_mul(MVP, VP, demo->model_matrix);
664
665 err = vkMapMemory(demo->device, demo->uniform_data.mem, 0,
666 demo->uniform_data.mem_alloc.allocationSize, 0,
667 (void **)&pData);
668 assert(!err);
669
670 memcpy(pData, (const void *)&MVP[0][0], matrixSize);
671
672 vkUnmapMemory(demo->device, demo->uniform_data.mem);
673 }
674
demo_draw(struct demo * demo)675 static void demo_draw(struct demo *demo) {
676 VkResult U_ASSERT_ONLY err;
677 VkSemaphore presentCompleteSemaphore;
678 VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = {
679 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
680 .pNext = NULL,
681 .flags = 0,
682 };
683 VkFence nullFence = VK_NULL_HANDLE;
684
685 err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo,
686 NULL, &presentCompleteSemaphore);
687 assert(!err);
688
689 // Get the index of the next available swapchain image:
690 err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX,
691 presentCompleteSemaphore,
692 (VkFence)0, // TODO: Show use of fence
693 &demo->current_buffer);
694 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
695 // demo->swapchain is out of date (e.g. the window was resized) and
696 // must be recreated:
697 demo_resize(demo);
698 demo_draw(demo);
699 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL);
700 return;
701 } else if (err == VK_SUBOPTIMAL_KHR) {
702 // demo->swapchain is not as optimal as it could be, but the platform's
703 // presentation engine will still present the image correctly.
704 } else {
705 assert(!err);
706 }
707
708 // Assume the command buffer has been run on current_buffer before so
709 // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL
710 demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image,
711 VK_IMAGE_ASPECT_COLOR_BIT,
712 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
713 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
714 0);
715 demo_flush_init_cmd(demo);
716
717 // Wait for the present complete semaphore to be signaled to ensure
718 // that the image won't be rendered to until the presentation
719 // engine has fully released ownership to the application, and it is
720 // okay to render to the image.
721
722 // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
723 VkPipelineStageFlags pipe_stage_flags =
724 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
725 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
726 .pNext = NULL,
727 .waitSemaphoreCount = 1,
728 .pWaitSemaphores = &presentCompleteSemaphore,
729 .pWaitDstStageMask = &pipe_stage_flags,
730 .commandBufferCount = 1,
731 .pCommandBuffers =
732 &demo->buffers[demo->current_buffer].cmd,
733 .signalSemaphoreCount = 0,
734 .pSignalSemaphores = NULL};
735
736 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
737 assert(!err);
738
739 VkPresentInfoKHR present = {
740 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
741 .pNext = NULL,
742 .swapchainCount = 1,
743 .pSwapchains = &demo->swapchain,
744 .pImageIndices = &demo->current_buffer,
745 };
746
747 // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER?
748 err = demo->fpQueuePresentKHR(demo->queue, &present);
749 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
750 // demo->swapchain is out of date (e.g. the window was resized) and
751 // must be recreated:
752 demo_resize(demo);
753 } else if (err == VK_SUBOPTIMAL_KHR) {
754 // demo->swapchain is not as optimal as it could be, but the platform's
755 // presentation engine will still present the image correctly.
756 } else {
757 assert(!err);
758 }
759
760 err = vkQueueWaitIdle(demo->queue);
761 assert(err == VK_SUCCESS);
762
763 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL);
764 }
765
demo_prepare_buffers(struct demo * demo)766 static void demo_prepare_buffers(struct demo *demo) {
767 VkResult U_ASSERT_ONLY err;
768 VkSwapchainKHR oldSwapchain = demo->swapchain;
769
770 // Check the surface capabilities and formats
771 VkSurfaceCapabilitiesKHR surfCapabilities;
772 err = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR(
773 demo->gpu, demo->surface, &surfCapabilities);
774 assert(!err);
775
776 uint32_t presentModeCount;
777 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(
778 demo->gpu, demo->surface, &presentModeCount, NULL);
779 assert(!err);
780 VkPresentModeKHR *presentModes =
781 (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
782 assert(presentModes);
783 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(
784 demo->gpu, demo->surface, &presentModeCount, presentModes);
785 assert(!err);
786
787 VkExtent2D swapchainExtent;
788 // width and height are either both -1, or both not -1.
789 if (surfCapabilities.currentExtent.width == (uint32_t)-1) {
790 // If the surface size is undefined, the size is set to
791 // the size of the images requested.
792 swapchainExtent.width = demo->width;
793 swapchainExtent.height = demo->height;
794 } else {
795 // If the surface size is defined, the swap chain size must match
796 swapchainExtent = surfCapabilities.currentExtent;
797 demo->width = surfCapabilities.currentExtent.width;
798 demo->height = surfCapabilities.currentExtent.height;
799 }
800
801 // If mailbox mode is available, use it, as is the lowest-latency non-
802 // tearing mode. If not, try IMMEDIATE which will usually be available,
803 // and is fastest (though it tears). If not, fall back to FIFO which is
804 // always available.
805 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
806 for (size_t i = 0; i < presentModeCount; i++) {
807 if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
808 swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
809 break;
810 }
811 if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) &&
812 (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
813 swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
814 }
815 }
816
817 // Determine the number of VkImage's to use in the swap chain (we desire to
818 // own only 1 image at a time, besides the images being displayed and
819 // queued for display):
820 uint32_t desiredNumberOfSwapchainImages =
821 surfCapabilities.minImageCount + 1;
822 if ((surfCapabilities.maxImageCount > 0) &&
823 (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) {
824 // Application must settle for fewer images than desired:
825 desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount;
826 }
827
828 VkSurfaceTransformFlagsKHR preTransform;
829 if (surfCapabilities.supportedTransforms &
830 VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
831 preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
832 } else {
833 preTransform = surfCapabilities.currentTransform;
834 }
835
836 const VkSwapchainCreateInfoKHR swapchain = {
837 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
838 .pNext = NULL,
839 .surface = demo->surface,
840 .minImageCount = desiredNumberOfSwapchainImages,
841 .imageFormat = demo->format,
842 .imageColorSpace = demo->color_space,
843 .imageExtent =
844 {
845 .width = swapchainExtent.width, .height = swapchainExtent.height,
846 },
847 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
848 .preTransform = preTransform,
849 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
850 .imageArrayLayers = 1,
851 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
852 .queueFamilyIndexCount = 0,
853 .pQueueFamilyIndices = NULL,
854 .presentMode = swapchainPresentMode,
855 .oldSwapchain = oldSwapchain,
856 .clipped = true,
857 };
858 uint32_t i;
859
860 err = demo->fpCreateSwapchainKHR(demo->device, &swapchain, NULL,
861 &demo->swapchain);
862 assert(!err);
863
864 // If we just re-created an existing swapchain, we should destroy the old
865 // swapchain at this point.
866 // Note: destroying the swapchain also cleans up all its associated
867 // presentable images once the platform is done with them.
868 if (oldSwapchain != VK_NULL_HANDLE) {
869 demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL);
870 }
871
872 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain,
873 &demo->swapchainImageCount, NULL);
874 assert(!err);
875
876 VkImage *swapchainImages =
877 (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage));
878 assert(swapchainImages);
879 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain,
880 &demo->swapchainImageCount,
881 swapchainImages);
882 assert(!err);
883
884 demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) *
885 demo->swapchainImageCount);
886 assert(demo->buffers);
887
888 for (i = 0; i < demo->swapchainImageCount; i++) {
889 VkImageViewCreateInfo color_image_view = {
890 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
891 .pNext = NULL,
892 .format = demo->format,
893 .components =
894 {
895 .r = VK_COMPONENT_SWIZZLE_R,
896 .g = VK_COMPONENT_SWIZZLE_G,
897 .b = VK_COMPONENT_SWIZZLE_B,
898 .a = VK_COMPONENT_SWIZZLE_A,
899 },
900 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
901 .baseMipLevel = 0,
902 .levelCount = 1,
903 .baseArrayLayer = 0,
904 .layerCount = 1},
905 .viewType = VK_IMAGE_VIEW_TYPE_2D,
906 .flags = 0,
907 };
908
909 demo->buffers[i].image = swapchainImages[i];
910
911 // Render loop will expect image to have been used before and in
912 // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
913 // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image
914 // to that state
915 demo_set_image_layout(
916 demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT,
917 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
918 0);
919
920 color_image_view.image = demo->buffers[i].image;
921
922 err = vkCreateImageView(demo->device, &color_image_view, NULL,
923 &demo->buffers[i].view);
924 assert(!err);
925 }
926
927 if (NULL != presentModes) {
928 free(presentModes);
929 }
930 }
931
demo_prepare_depth(struct demo * demo)932 static void demo_prepare_depth(struct demo *demo) {
933 const VkFormat depth_format = VK_FORMAT_D16_UNORM;
934 const VkImageCreateInfo image = {
935 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
936 .pNext = NULL,
937 .imageType = VK_IMAGE_TYPE_2D,
938 .format = depth_format,
939 .extent = {demo->width, demo->height, 1},
940 .mipLevels = 1,
941 .arrayLayers = 1,
942 .samples = VK_SAMPLE_COUNT_1_BIT,
943 .tiling = VK_IMAGE_TILING_OPTIMAL,
944 .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
945 .flags = 0,
946 };
947
948 VkImageViewCreateInfo view = {
949 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
950 .pNext = NULL,
951 .image = VK_NULL_HANDLE,
952 .format = depth_format,
953 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
954 .baseMipLevel = 0,
955 .levelCount = 1,
956 .baseArrayLayer = 0,
957 .layerCount = 1},
958 .flags = 0,
959 .viewType = VK_IMAGE_VIEW_TYPE_2D,
960 };
961
962 VkMemoryRequirements mem_reqs;
963 VkResult U_ASSERT_ONLY err;
964 bool U_ASSERT_ONLY pass;
965
966 demo->depth.format = depth_format;
967
968 /* create image */
969 err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image);
970 assert(!err);
971
972 vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs);
973 assert(!err);
974
975 demo->depth.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
976 demo->depth.mem_alloc.pNext = NULL;
977 demo->depth.mem_alloc.allocationSize = mem_reqs.size;
978 demo->depth.mem_alloc.memoryTypeIndex = 0;
979
980 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
981 0, /* No requirements */
982 &demo->depth.mem_alloc.memoryTypeIndex);
983 assert(pass);
984
985 /* allocate memory */
986 err = vkAllocateMemory(demo->device, &demo->depth.mem_alloc, NULL,
987 &demo->depth.mem);
988 assert(!err);
989
990 /* bind memory */
991 err =
992 vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0);
993 assert(!err);
994
995 demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT,
996 VK_IMAGE_LAYOUT_UNDEFINED,
997 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
998 0);
999
1000 /* create image view */
1001 view.image = demo->depth.image;
1002 err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view);
1003 assert(!err);
1004 }
1005
1006 /* Load a ppm file into memory */
loadTexture(const char * filename,uint8_t * rgba_data,VkSubresourceLayout * layout,int32_t * width,int32_t * height)1007 bool loadTexture(const char *filename, uint8_t *rgba_data,
1008 VkSubresourceLayout *layout, int32_t *width, int32_t *height) {
1009 FILE *fPtr = fopen(filename, "rb");
1010 char header[256], *cPtr, *tmp;
1011
1012 if (!fPtr)
1013 return false;
1014
1015 cPtr = fgets(header, 256, fPtr); // P6
1016 if (cPtr == NULL || strncmp(header, "P6\n", 3)) {
1017 fclose(fPtr);
1018 return false;
1019 }
1020
1021 do {
1022 cPtr = fgets(header, 256, fPtr);
1023 if (cPtr == NULL) {
1024 fclose(fPtr);
1025 return false;
1026 }
1027 } while (!strncmp(header, "#", 1));
1028
1029 sscanf(header, "%u %u", height, width);
1030 if (rgba_data == NULL) {
1031 fclose(fPtr);
1032 return true;
1033 }
1034 tmp = fgets(header, 256, fPtr); // Format
1035 (void)tmp;
1036 if (cPtr == NULL || strncmp(header, "255\n", 3)) {
1037 fclose(fPtr);
1038 return false;
1039 }
1040
1041 for (int y = 0; y < *height; y++) {
1042 uint8_t *rowPtr = rgba_data;
1043 for (int x = 0; x < *width; x++) {
1044 size_t s = fread(rowPtr, 3, 1, fPtr);
1045 (void)s;
1046 rowPtr[3] = 255; /* Alpha of 1 */
1047 rowPtr += 4;
1048 }
1049 rgba_data += layout->rowPitch;
1050 }
1051 fclose(fPtr);
1052 return true;
1053 }
1054
demo_prepare_texture_image(struct demo * demo,const char * filename,struct texture_object * tex_obj,VkImageTiling tiling,VkImageUsageFlags usage,VkFlags required_props)1055 static void demo_prepare_texture_image(struct demo *demo, const char *filename,
1056 struct texture_object *tex_obj,
1057 VkImageTiling tiling,
1058 VkImageUsageFlags usage,
1059 VkFlags required_props) {
1060 const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM;
1061 int32_t tex_width;
1062 int32_t tex_height;
1063 VkResult U_ASSERT_ONLY err;
1064 bool U_ASSERT_ONLY pass;
1065
1066 if (!loadTexture(filename, NULL, NULL, &tex_width, &tex_height)) {
1067 printf("Failed to load textures\n");
1068 fflush(stdout);
1069 exit(1);
1070 }
1071
1072 tex_obj->tex_width = tex_width;
1073 tex_obj->tex_height = tex_height;
1074
1075 const VkImageCreateInfo image_create_info = {
1076 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1077 .pNext = NULL,
1078 .imageType = VK_IMAGE_TYPE_2D,
1079 .format = tex_format,
1080 .extent = {tex_width, tex_height, 1},
1081 .mipLevels = 1,
1082 .arrayLayers = 1,
1083 .samples = VK_SAMPLE_COUNT_1_BIT,
1084 .tiling = tiling,
1085 .usage = usage,
1086 .flags = 0,
1087 .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
1088 };
1089
1090 VkMemoryRequirements mem_reqs;
1091
1092 err =
1093 vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image);
1094 assert(!err);
1095
1096 vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs);
1097
1098 tex_obj->mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1099 tex_obj->mem_alloc.pNext = NULL;
1100 tex_obj->mem_alloc.allocationSize = mem_reqs.size;
1101 tex_obj->mem_alloc.memoryTypeIndex = 0;
1102
1103 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
1104 required_props,
1105 &tex_obj->mem_alloc.memoryTypeIndex);
1106 assert(pass);
1107
1108 /* allocate memory */
1109 err = vkAllocateMemory(demo->device, &tex_obj->mem_alloc, NULL,
1110 &(tex_obj->mem));
1111 assert(!err);
1112
1113 /* bind memory */
1114 err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0);
1115 assert(!err);
1116
1117 if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
1118 const VkImageSubresource subres = {
1119 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1120 .mipLevel = 0,
1121 .arrayLayer = 0,
1122 };
1123 VkSubresourceLayout layout;
1124 void *data;
1125
1126 vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres,
1127 &layout);
1128
1129 err = vkMapMemory(demo->device, tex_obj->mem, 0,
1130 tex_obj->mem_alloc.allocationSize, 0, &data);
1131 assert(!err);
1132
1133 if (!loadTexture(filename, data, &layout, &tex_width, &tex_height)) {
1134 fprintf(stderr, "Error loading texture: %s\n", filename);
1135 }
1136
1137 vkUnmapMemory(demo->device, tex_obj->mem);
1138 }
1139
1140 tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1141 demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT,
1142 VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout,
1143 VK_ACCESS_HOST_WRITE_BIT);
1144 /* setting the image layout does not reference the actual memory so no need
1145 * to add a mem ref */
1146 }
1147
demo_destroy_texture_image(struct demo * demo,struct texture_object * tex_objs)1148 static void demo_destroy_texture_image(struct demo *demo,
1149 struct texture_object *tex_objs) {
1150 /* clean up staging resources */
1151 vkFreeMemory(demo->device, tex_objs->mem, NULL);
1152 vkDestroyImage(demo->device, tex_objs->image, NULL);
1153 }
1154
demo_prepare_textures(struct demo * demo)1155 static void demo_prepare_textures(struct demo *demo) {
1156 const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM;
1157 VkFormatProperties props;
1158 uint32_t i;
1159
1160 vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props);
1161
1162 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1163 VkResult U_ASSERT_ONLY err;
1164
1165 if ((props.linearTilingFeatures &
1166 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) &&
1167 !demo->use_staging_buffer) {
1168 /* Device can texture using linear textures */
1169 demo_prepare_texture_image(demo, tex_files[i], &demo->textures[i],
1170 VK_IMAGE_TILING_LINEAR,
1171 VK_IMAGE_USAGE_SAMPLED_BIT,
1172 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1173 } else if (props.optimalTilingFeatures &
1174 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) {
1175 /* Must use staging buffer to copy linear texture to optimized */
1176 struct texture_object staging_texture;
1177
1178 memset(&staging_texture, 0, sizeof(staging_texture));
1179 demo_prepare_texture_image(demo, tex_files[i], &staging_texture,
1180 VK_IMAGE_TILING_LINEAR,
1181 VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
1182 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1183
1184 demo_prepare_texture_image(
1185 demo, tex_files[i], &demo->textures[i], VK_IMAGE_TILING_OPTIMAL,
1186 (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
1187 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1188
1189 demo_set_image_layout(demo, staging_texture.image,
1190 VK_IMAGE_ASPECT_COLOR_BIT,
1191 staging_texture.imageLayout,
1192 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1193 0);
1194
1195 demo_set_image_layout(demo, demo->textures[i].image,
1196 VK_IMAGE_ASPECT_COLOR_BIT,
1197 demo->textures[i].imageLayout,
1198 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1199 0);
1200
1201 VkImageCopy copy_region = {
1202 .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
1203 .srcOffset = {0, 0, 0},
1204 .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
1205 .dstOffset = {0, 0, 0},
1206 .extent = {staging_texture.tex_width,
1207 staging_texture.tex_height, 1},
1208 };
1209 vkCmdCopyImage(
1210 demo->cmd, staging_texture.image,
1211 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image,
1212 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
1213
1214 demo_set_image_layout(demo, demo->textures[i].image,
1215 VK_IMAGE_ASPECT_COLOR_BIT,
1216 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1217 demo->textures[i].imageLayout,
1218 0);
1219
1220 demo_flush_init_cmd(demo);
1221
1222 demo_destroy_texture_image(demo, &staging_texture);
1223 } else {
1224 /* Can't support VK_FORMAT_R8G8B8A8_UNORM !? */
1225 assert(!"No support for R8G8B8A8_UNORM as texture image format");
1226 }
1227
1228 const VkSamplerCreateInfo sampler = {
1229 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1230 .pNext = NULL,
1231 .magFilter = VK_FILTER_NEAREST,
1232 .minFilter = VK_FILTER_NEAREST,
1233 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1234 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1235 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1236 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1237 .mipLodBias = 0.0f,
1238 .anisotropyEnable = VK_FALSE,
1239 .maxAnisotropy = 1,
1240 .compareOp = VK_COMPARE_OP_NEVER,
1241 .minLod = 0.0f,
1242 .maxLod = 0.0f,
1243 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
1244 .unnormalizedCoordinates = VK_FALSE,
1245 };
1246
1247 VkImageViewCreateInfo view = {
1248 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1249 .pNext = NULL,
1250 .image = VK_NULL_HANDLE,
1251 .viewType = VK_IMAGE_VIEW_TYPE_2D,
1252 .format = tex_format,
1253 .components =
1254 {
1255 VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
1256 VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
1257 },
1258 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
1259 .flags = 0,
1260 };
1261
1262 /* create sampler */
1263 err = vkCreateSampler(demo->device, &sampler, NULL,
1264 &demo->textures[i].sampler);
1265 assert(!err);
1266
1267 /* create image view */
1268 view.image = demo->textures[i].image;
1269 err = vkCreateImageView(demo->device, &view, NULL,
1270 &demo->textures[i].view);
1271 assert(!err);
1272 }
1273 }
1274
demo_prepare_cube_data_buffer(struct demo * demo)1275 void demo_prepare_cube_data_buffer(struct demo *demo) {
1276 VkBufferCreateInfo buf_info;
1277 VkMemoryRequirements mem_reqs;
1278 uint8_t *pData;
1279 int i;
1280 mat4x4 MVP, VP;
1281 VkResult U_ASSERT_ONLY err;
1282 bool U_ASSERT_ONLY pass;
1283 struct vktexcube_vs_uniform data;
1284
1285 mat4x4_mul(VP, demo->projection_matrix, demo->view_matrix);
1286 mat4x4_mul(MVP, VP, demo->model_matrix);
1287 memcpy(data.mvp, MVP, sizeof(MVP));
1288 // dumpMatrix("MVP", MVP);
1289
1290 for (i = 0; i < 12 * 3; i++) {
1291 data.position[i][0] = g_vertex_buffer_data[i * 3];
1292 data.position[i][1] = g_vertex_buffer_data[i * 3 + 1];
1293 data.position[i][2] = g_vertex_buffer_data[i * 3 + 2];
1294 data.position[i][3] = 1.0f;
1295 data.attr[i][0] = g_uv_buffer_data[2 * i];
1296 data.attr[i][1] = g_uv_buffer_data[2 * i + 1];
1297 data.attr[i][2] = 0;
1298 data.attr[i][3] = 0;
1299 }
1300
1301 memset(&buf_info, 0, sizeof(buf_info));
1302 buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1303 buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
1304 buf_info.size = sizeof(data);
1305 err =
1306 vkCreateBuffer(demo->device, &buf_info, NULL, &demo->uniform_data.buf);
1307 assert(!err);
1308
1309 vkGetBufferMemoryRequirements(demo->device, demo->uniform_data.buf,
1310 &mem_reqs);
1311
1312 demo->uniform_data.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1313 demo->uniform_data.mem_alloc.pNext = NULL;
1314 demo->uniform_data.mem_alloc.allocationSize = mem_reqs.size;
1315 demo->uniform_data.mem_alloc.memoryTypeIndex = 0;
1316
1317 pass = memory_type_from_properties(
1318 demo, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
1319 &demo->uniform_data.mem_alloc.memoryTypeIndex);
1320 assert(pass);
1321
1322 err = vkAllocateMemory(demo->device, &demo->uniform_data.mem_alloc, NULL,
1323 &(demo->uniform_data.mem));
1324 assert(!err);
1325
1326 err = vkMapMemory(demo->device, demo->uniform_data.mem, 0,
1327 demo->uniform_data.mem_alloc.allocationSize, 0,
1328 (void **)&pData);
1329 assert(!err);
1330
1331 memcpy(pData, &data, sizeof data);
1332
1333 vkUnmapMemory(demo->device, demo->uniform_data.mem);
1334
1335 err = vkBindBufferMemory(demo->device, demo->uniform_data.buf,
1336 demo->uniform_data.mem, 0);
1337 assert(!err);
1338
1339 demo->uniform_data.buffer_info.buffer = demo->uniform_data.buf;
1340 demo->uniform_data.buffer_info.offset = 0;
1341 demo->uniform_data.buffer_info.range = sizeof(data);
1342 }
1343
demo_prepare_descriptor_layout(struct demo * demo)1344 static void demo_prepare_descriptor_layout(struct demo *demo) {
1345 const VkDescriptorSetLayoutBinding layout_bindings[2] = {
1346 [0] =
1347 {
1348 .binding = 0,
1349 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1350 .descriptorCount = 1,
1351 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
1352 .pImmutableSamplers = NULL,
1353 },
1354 [1] =
1355 {
1356 .binding = 1,
1357 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1358 .descriptorCount = DEMO_TEXTURE_COUNT,
1359 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
1360 .pImmutableSamplers = NULL,
1361 },
1362 };
1363 const VkDescriptorSetLayoutCreateInfo descriptor_layout = {
1364 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1365 .pNext = NULL,
1366 .bindingCount = 2,
1367 .pBindings = layout_bindings,
1368 };
1369 VkResult U_ASSERT_ONLY err;
1370
1371 err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL,
1372 &demo->desc_layout);
1373 assert(!err);
1374
1375 const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
1376 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
1377 .pNext = NULL,
1378 .setLayoutCount = 1,
1379 .pSetLayouts = &demo->desc_layout,
1380 };
1381
1382 err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL,
1383 &demo->pipeline_layout);
1384 assert(!err);
1385 }
1386
demo_prepare_render_pass(struct demo * demo)1387 static void demo_prepare_render_pass(struct demo *demo) {
1388 const VkAttachmentDescription attachments[2] = {
1389 [0] =
1390 {
1391 .format = demo->format,
1392 .samples = VK_SAMPLE_COUNT_1_BIT,
1393 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1394 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
1395 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1396 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1397 .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1398 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1399 },
1400 [1] =
1401 {
1402 .format = demo->depth.format,
1403 .samples = VK_SAMPLE_COUNT_1_BIT,
1404 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1405 .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1406 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1407 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1408 .initialLayout =
1409 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1410 .finalLayout =
1411 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1412 },
1413 };
1414 const VkAttachmentReference color_reference = {
1415 .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1416 };
1417 const VkAttachmentReference depth_reference = {
1418 .attachment = 1,
1419 .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1420 };
1421 const VkSubpassDescription subpass = {
1422 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
1423 .flags = 0,
1424 .inputAttachmentCount = 0,
1425 .pInputAttachments = NULL,
1426 .colorAttachmentCount = 1,
1427 .pColorAttachments = &color_reference,
1428 .pResolveAttachments = NULL,
1429 .pDepthStencilAttachment = &depth_reference,
1430 .preserveAttachmentCount = 0,
1431 .pPreserveAttachments = NULL,
1432 };
1433 const VkRenderPassCreateInfo rp_info = {
1434 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
1435 .pNext = NULL,
1436 .attachmentCount = 2,
1437 .pAttachments = attachments,
1438 .subpassCount = 1,
1439 .pSubpasses = &subpass,
1440 .dependencyCount = 0,
1441 .pDependencies = NULL,
1442 };
1443 VkResult U_ASSERT_ONLY err;
1444
1445 err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass);
1446 assert(!err);
1447 }
1448
1449 static VkShaderModule
demo_prepare_shader_module(struct demo * demo,const void * code,size_t size)1450 demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) {
1451 VkShaderModule module;
1452 VkShaderModuleCreateInfo moduleCreateInfo;
1453 VkResult U_ASSERT_ONLY err;
1454
1455 moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1456 moduleCreateInfo.pNext = NULL;
1457
1458 moduleCreateInfo.codeSize = size;
1459 moduleCreateInfo.pCode = code;
1460 moduleCreateInfo.flags = 0;
1461 err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module);
1462 assert(!err);
1463
1464 return module;
1465 }
1466
demo_read_spv(const char * filename,size_t * psize)1467 char *demo_read_spv(const char *filename, size_t *psize) {
1468 long int size;
1469 size_t U_ASSERT_ONLY retval;
1470 void *shader_code;
1471
1472 FILE *fp = fopen(filename, "rb");
1473 if (!fp)
1474 return NULL;
1475
1476 fseek(fp, 0L, SEEK_END);
1477 size = ftell(fp);
1478
1479 fseek(fp, 0L, SEEK_SET);
1480
1481 shader_code = malloc(size);
1482 retval = fread(shader_code, size, 1, fp);
1483 assert(retval == 1);
1484
1485 *psize = size;
1486
1487 fclose(fp);
1488 return shader_code;
1489 }
1490
demo_prepare_vs(struct demo * demo)1491 static VkShaderModule demo_prepare_vs(struct demo *demo) {
1492 void *vertShaderCode;
1493 size_t size;
1494
1495 vertShaderCode = demo_read_spv("cube-vert.spv", &size);
1496
1497 demo->vert_shader_module =
1498 demo_prepare_shader_module(demo, vertShaderCode, size);
1499
1500 free(vertShaderCode);
1501
1502 return demo->vert_shader_module;
1503 }
1504
demo_prepare_fs(struct demo * demo)1505 static VkShaderModule demo_prepare_fs(struct demo *demo) {
1506 void *fragShaderCode;
1507 size_t size;
1508
1509 fragShaderCode = demo_read_spv("cube-frag.spv", &size);
1510
1511 demo->frag_shader_module =
1512 demo_prepare_shader_module(demo, fragShaderCode, size);
1513
1514 free(fragShaderCode);
1515
1516 return demo->frag_shader_module;
1517 }
1518
demo_prepare_pipeline(struct demo * demo)1519 static void demo_prepare_pipeline(struct demo *demo) {
1520 VkGraphicsPipelineCreateInfo pipeline;
1521 VkPipelineCacheCreateInfo pipelineCache;
1522 VkPipelineVertexInputStateCreateInfo vi;
1523 VkPipelineInputAssemblyStateCreateInfo ia;
1524 VkPipelineRasterizationStateCreateInfo rs;
1525 VkPipelineColorBlendStateCreateInfo cb;
1526 VkPipelineDepthStencilStateCreateInfo ds;
1527 VkPipelineViewportStateCreateInfo vp;
1528 VkPipelineMultisampleStateCreateInfo ms;
1529 VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE];
1530 VkPipelineDynamicStateCreateInfo dynamicState;
1531 VkResult U_ASSERT_ONLY err;
1532
1533 memset(dynamicStateEnables, 0, sizeof dynamicStateEnables);
1534 memset(&dynamicState, 0, sizeof dynamicState);
1535 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1536 dynamicState.pDynamicStates = dynamicStateEnables;
1537
1538 memset(&pipeline, 0, sizeof(pipeline));
1539 pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1540 pipeline.layout = demo->pipeline_layout;
1541
1542 memset(&vi, 0, sizeof(vi));
1543 vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1544
1545 memset(&ia, 0, sizeof(ia));
1546 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1547 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1548
1549 memset(&rs, 0, sizeof(rs));
1550 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1551 rs.polygonMode = VK_POLYGON_MODE_FILL;
1552 rs.cullMode = VK_CULL_MODE_BACK_BIT;
1553 rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1554 rs.depthClampEnable = VK_FALSE;
1555 rs.rasterizerDiscardEnable = VK_FALSE;
1556 rs.depthBiasEnable = VK_FALSE;
1557
1558 memset(&cb, 0, sizeof(cb));
1559 cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1560 VkPipelineColorBlendAttachmentState att_state[1];
1561 memset(att_state, 0, sizeof(att_state));
1562 att_state[0].colorWriteMask = 0xf;
1563 att_state[0].blendEnable = VK_FALSE;
1564 cb.attachmentCount = 1;
1565 cb.pAttachments = att_state;
1566
1567 memset(&vp, 0, sizeof(vp));
1568 vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1569 vp.viewportCount = 1;
1570 dynamicStateEnables[dynamicState.dynamicStateCount++] =
1571 VK_DYNAMIC_STATE_VIEWPORT;
1572 vp.scissorCount = 1;
1573 dynamicStateEnables[dynamicState.dynamicStateCount++] =
1574 VK_DYNAMIC_STATE_SCISSOR;
1575
1576 memset(&ds, 0, sizeof(ds));
1577 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1578 ds.depthTestEnable = VK_TRUE;
1579 ds.depthWriteEnable = VK_TRUE;
1580 ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
1581 ds.depthBoundsTestEnable = VK_FALSE;
1582 ds.back.failOp = VK_STENCIL_OP_KEEP;
1583 ds.back.passOp = VK_STENCIL_OP_KEEP;
1584 ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
1585 ds.stencilTestEnable = VK_FALSE;
1586 ds.front = ds.back;
1587
1588 memset(&ms, 0, sizeof(ms));
1589 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1590 ms.pSampleMask = NULL;
1591 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1592
1593 // Two stages: vs and fs
1594 pipeline.stageCount = 2;
1595 VkPipelineShaderStageCreateInfo shaderStages[2];
1596 memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo));
1597
1598 shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1599 shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
1600 shaderStages[0].module = demo_prepare_vs(demo);
1601 shaderStages[0].pName = "main";
1602
1603 shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1604 shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1605 shaderStages[1].module = demo_prepare_fs(demo);
1606 shaderStages[1].pName = "main";
1607
1608 memset(&pipelineCache, 0, sizeof(pipelineCache));
1609 pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
1610
1611 err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL,
1612 &demo->pipelineCache);
1613 assert(!err);
1614
1615 pipeline.pVertexInputState = &vi;
1616 pipeline.pInputAssemblyState = &ia;
1617 pipeline.pRasterizationState = &rs;
1618 pipeline.pColorBlendState = &cb;
1619 pipeline.pMultisampleState = &ms;
1620 pipeline.pViewportState = &vp;
1621 pipeline.pDepthStencilState = &ds;
1622 pipeline.pStages = shaderStages;
1623 pipeline.renderPass = demo->render_pass;
1624 pipeline.pDynamicState = &dynamicState;
1625
1626 pipeline.renderPass = demo->render_pass;
1627
1628 err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1,
1629 &pipeline, NULL, &demo->pipeline);
1630 assert(!err);
1631
1632 vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL);
1633 vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL);
1634 }
1635
demo_prepare_descriptor_pool(struct demo * demo)1636 static void demo_prepare_descriptor_pool(struct demo *demo) {
1637 const VkDescriptorPoolSize type_counts[2] = {
1638 [0] =
1639 {
1640 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1641 .descriptorCount = 1,
1642 },
1643 [1] =
1644 {
1645 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1646 .descriptorCount = DEMO_TEXTURE_COUNT,
1647 },
1648 };
1649 const VkDescriptorPoolCreateInfo descriptor_pool = {
1650 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
1651 .pNext = NULL,
1652 .maxSets = 1,
1653 .poolSizeCount = 2,
1654 .pPoolSizes = type_counts,
1655 };
1656 VkResult U_ASSERT_ONLY err;
1657
1658 err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL,
1659 &demo->desc_pool);
1660 assert(!err);
1661 }
1662
demo_prepare_descriptor_set(struct demo * demo)1663 static void demo_prepare_descriptor_set(struct demo *demo) {
1664 VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT];
1665 VkWriteDescriptorSet writes[2];
1666 VkResult U_ASSERT_ONLY err;
1667 uint32_t i;
1668
1669 VkDescriptorSetAllocateInfo alloc_info = {
1670 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
1671 .pNext = NULL,
1672 .descriptorPool = demo->desc_pool,
1673 .descriptorSetCount = 1,
1674 .pSetLayouts = &demo->desc_layout};
1675 err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set);
1676 assert(!err);
1677
1678 memset(&tex_descs, 0, sizeof(tex_descs));
1679 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1680 tex_descs[i].sampler = demo->textures[i].sampler;
1681 tex_descs[i].imageView = demo->textures[i].view;
1682 tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
1683 }
1684
1685 memset(&writes, 0, sizeof(writes));
1686
1687 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1688 writes[0].dstSet = demo->desc_set;
1689 writes[0].descriptorCount = 1;
1690 writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1691 writes[0].pBufferInfo = &demo->uniform_data.buffer_info;
1692
1693 writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1694 writes[1].dstSet = demo->desc_set;
1695 writes[1].dstBinding = 1;
1696 writes[1].descriptorCount = DEMO_TEXTURE_COUNT;
1697 writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1698 writes[1].pImageInfo = tex_descs;
1699
1700 vkUpdateDescriptorSets(demo->device, 2, writes, 0, NULL);
1701 }
1702
demo_prepare_framebuffers(struct demo * demo)1703 static void demo_prepare_framebuffers(struct demo *demo) {
1704 VkImageView attachments[2];
1705 attachments[1] = demo->depth.view;
1706
1707 const VkFramebufferCreateInfo fb_info = {
1708 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
1709 .pNext = NULL,
1710 .renderPass = demo->render_pass,
1711 .attachmentCount = 2,
1712 .pAttachments = attachments,
1713 .width = demo->width,
1714 .height = demo->height,
1715 .layers = 1,
1716 };
1717 VkResult U_ASSERT_ONLY err;
1718 uint32_t i;
1719
1720 demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount *
1721 sizeof(VkFramebuffer));
1722 assert(demo->framebuffers);
1723
1724 for (i = 0; i < demo->swapchainImageCount; i++) {
1725 attachments[0] = demo->buffers[i].view;
1726 err = vkCreateFramebuffer(demo->device, &fb_info, NULL,
1727 &demo->framebuffers[i]);
1728 assert(!err);
1729 }
1730 }
1731
demo_prepare(struct demo * demo)1732 static void demo_prepare(struct demo *demo) {
1733 VkResult U_ASSERT_ONLY err;
1734
1735 const VkCommandPoolCreateInfo cmd_pool_info = {
1736 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
1737 .pNext = NULL,
1738 .queueFamilyIndex = demo->graphics_queue_node_index,
1739 .flags = 0,
1740 };
1741 err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL,
1742 &demo->cmd_pool);
1743 assert(!err);
1744
1745 const VkCommandBufferAllocateInfo cmd = {
1746 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
1747 .pNext = NULL,
1748 .commandPool = demo->cmd_pool,
1749 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1750 .commandBufferCount = 1,
1751 };
1752
1753 demo_prepare_buffers(demo);
1754 demo_prepare_depth(demo);
1755 demo_prepare_textures(demo);
1756 demo_prepare_cube_data_buffer(demo);
1757
1758 demo_prepare_descriptor_layout(demo);
1759 demo_prepare_render_pass(demo);
1760 demo_prepare_pipeline(demo);
1761
1762 for (uint32_t i = 0; i < demo->swapchainImageCount; i++) {
1763 err =
1764 vkAllocateCommandBuffers(demo->device, &cmd, &demo->buffers[i].cmd);
1765 assert(!err);
1766 }
1767
1768 demo_prepare_descriptor_pool(demo);
1769 demo_prepare_descriptor_set(demo);
1770
1771 demo_prepare_framebuffers(demo);
1772
1773 for (uint32_t i = 0; i < demo->swapchainImageCount; i++) {
1774 demo->current_buffer = i;
1775 demo_draw_build_cmd(demo, demo->buffers[i].cmd);
1776 }
1777
1778 /*
1779 * Prepare functions above may generate pipeline commands
1780 * that need to be flushed before beginning the render loop.
1781 */
1782 demo_flush_init_cmd(demo);
1783
1784 demo->current_buffer = 0;
1785 demo->prepared = true;
1786 }
1787
demo_cleanup(struct demo * demo)1788 static void demo_cleanup(struct demo *demo) {
1789 uint32_t i;
1790
1791 demo->prepared = false;
1792
1793 for (i = 0; i < demo->swapchainImageCount; i++) {
1794 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
1795 }
1796 free(demo->framebuffers);
1797 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
1798
1799 vkDestroyPipeline(demo->device, demo->pipeline, NULL);
1800 vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
1801 vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
1802 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
1803 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
1804
1805 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1806 vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
1807 vkDestroyImage(demo->device, demo->textures[i].image, NULL);
1808 vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
1809 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
1810 }
1811 demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL);
1812
1813 vkDestroyImageView(demo->device, demo->depth.view, NULL);
1814 vkDestroyImage(demo->device, demo->depth.image, NULL);
1815 vkFreeMemory(demo->device, demo->depth.mem, NULL);
1816
1817 vkDestroyBuffer(demo->device, demo->uniform_data.buf, NULL);
1818 vkFreeMemory(demo->device, demo->uniform_data.mem, NULL);
1819
1820 for (i = 0; i < demo->swapchainImageCount; i++) {
1821 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
1822 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1,
1823 &demo->buffers[i].cmd);
1824 }
1825 free(demo->buffers);
1826
1827 free(demo->queue_props);
1828
1829 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
1830 vkDestroyDevice(demo->device, NULL);
1831 if (demo->validate) {
1832 demo->DestroyDebugReportCallback(demo->inst, demo->msg_callback, NULL);
1833 }
1834 vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
1835 vkDestroyInstance(demo->inst, NULL);
1836
1837 #ifndef _WIN32
1838 xcb_destroy_window(demo->connection, demo->window);
1839 xcb_disconnect(demo->connection);
1840 free(demo->atom_wm_delete_window);
1841 #endif // _WIN32
1842 }
1843
demo_resize(struct demo * demo)1844 static void demo_resize(struct demo *demo) {
1845 uint32_t i;
1846
1847 // Don't react to resize until after first initialization.
1848 if (!demo->prepared) {
1849 return;
1850 }
1851 // In order to properly resize the window, we must re-create the swapchain
1852 // AND redo the command buffers, etc.
1853 //
1854 // First, perform part of the demo_cleanup() function:
1855 demo->prepared = false;
1856
1857 for (i = 0; i < demo->swapchainImageCount; i++) {
1858 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
1859 }
1860 free(demo->framebuffers);
1861 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
1862
1863 vkDestroyPipeline(demo->device, demo->pipeline, NULL);
1864 vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
1865 vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
1866 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
1867 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
1868
1869 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1870 vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
1871 vkDestroyImage(demo->device, demo->textures[i].image, NULL);
1872 vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
1873 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
1874 }
1875
1876 vkDestroyImageView(demo->device, demo->depth.view, NULL);
1877 vkDestroyImage(demo->device, demo->depth.image, NULL);
1878 vkFreeMemory(demo->device, demo->depth.mem, NULL);
1879
1880 vkDestroyBuffer(demo->device, demo->uniform_data.buf, NULL);
1881 vkFreeMemory(demo->device, demo->uniform_data.mem, NULL);
1882
1883 for (i = 0; i < demo->swapchainImageCount; i++) {
1884 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
1885 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1,
1886 &demo->buffers[i].cmd);
1887 }
1888 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
1889 free(demo->buffers);
1890
1891 // Second, re-perform the demo_prepare() function, which will re-create the
1892 // swapchain:
1893 demo_prepare(demo);
1894 }
1895
1896 // On MS-Windows, make this a global, so it's available to WndProc()
1897 struct demo demo;
1898
1899 #ifdef _WIN32
demo_run(struct demo * demo)1900 static void demo_run(struct demo *demo) {
1901 if (!demo->prepared)
1902 return;
1903 // Wait for work to finish before updating MVP.
1904 vkDeviceWaitIdle(demo->device);
1905 demo_update_data_buffer(demo);
1906
1907 demo_draw(demo);
1908
1909 // Wait for work to finish before updating MVP.
1910 vkDeviceWaitIdle(demo->device);
1911
1912 demo->curFrame++;
1913
1914 if (demo->frameCount != INT_MAX && demo->curFrame == demo->frameCount) {
1915 demo->quit = true;
1916 demo_cleanup(demo);
1917 ExitProcess(0);
1918 }
1919 }
1920
1921 // MS-Windows event handling function:
WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1922 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1923 switch (uMsg) {
1924 case WM_CLOSE:
1925 PostQuitMessage(validation_error);
1926 break;
1927 case WM_PAINT:
1928 demo_run(&demo);
1929 break;
1930 case WM_SIZE:
1931 // Resize the application to the new window size, except when
1932 // it was minimized. Vulkan doesn't support images or swapchains
1933 // with width=0 and height=0.
1934 if (wParam != SIZE_MINIMIZED) {
1935 demo.width = lParam & 0xffff;
1936 demo.height = lParam & 0xffff0000 >> 16;
1937 demo_resize(&demo);
1938 }
1939 break;
1940 default:
1941 break;
1942 }
1943 return (DefWindowProc(hWnd, uMsg, wParam, lParam));
1944 }
1945
demo_create_window(struct demo * demo)1946 static void demo_create_window(struct demo *demo) {
1947 WNDCLASSEX win_class;
1948
1949 // Initialize the window class structure:
1950 win_class.cbSize = sizeof(WNDCLASSEX);
1951 win_class.style = CS_HREDRAW | CS_VREDRAW;
1952 win_class.lpfnWndProc = WndProc;
1953 win_class.cbClsExtra = 0;
1954 win_class.cbWndExtra = 0;
1955 win_class.hInstance = demo->connection; // hInstance
1956 win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
1957 win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
1958 win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
1959 win_class.lpszMenuName = NULL;
1960 win_class.lpszClassName = demo->name;
1961 win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
1962 // Register window class:
1963 if (!RegisterClassEx(&win_class)) {
1964 // It didn't work, so try to give a useful error:
1965 printf("Unexpected error trying to start the application!\n");
1966 fflush(stdout);
1967 exit(1);
1968 }
1969 // Create window with the registered class:
1970 RECT wr = {0, 0, demo->width, demo->height};
1971 AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
1972 demo->window = CreateWindowEx(0,
1973 demo->name, // class name
1974 demo->name, // app name
1975 WS_OVERLAPPEDWINDOW | // window style
1976 WS_VISIBLE | WS_SYSMENU,
1977 100, 100, // x/y coords
1978 wr.right - wr.left, // width
1979 wr.bottom - wr.top, // height
1980 NULL, // handle to parent
1981 NULL, // handle to menu
1982 demo->connection, // hInstance
1983 NULL); // no extra parameters
1984 if (!demo->window) {
1985 // It didn't work, so try to give a useful error:
1986 printf("Cannot create a window in which to draw!\n");
1987 fflush(stdout);
1988 exit(1);
1989 }
1990 }
1991 #else // _WIN32
demo_handle_event(struct demo * demo,const xcb_generic_event_t * event)1992 static void demo_handle_event(struct demo *demo,
1993 const xcb_generic_event_t *event) {
1994 uint8_t event_code = event->response_type & 0x7f;
1995 switch (event_code) {
1996 case XCB_EXPOSE:
1997 // TODO: Resize window
1998 break;
1999 case XCB_CLIENT_MESSAGE:
2000 if ((*(xcb_client_message_event_t *)event).data.data32[0] ==
2001 (*demo->atom_wm_delete_window).atom) {
2002 demo->quit = true;
2003 }
2004 break;
2005 case XCB_KEY_RELEASE: {
2006 const xcb_key_release_event_t *key =
2007 (const xcb_key_release_event_t *)event;
2008
2009 switch (key->detail) {
2010 case 0x9: // Escape
2011 demo->quit = true;
2012 break;
2013 case 0x71: // left arrow key
2014 demo->spin_angle += demo->spin_increment;
2015 break;
2016 case 0x72: // right arrow key
2017 demo->spin_angle -= demo->spin_increment;
2018 break;
2019 case 0x41:
2020 demo->pause = !demo->pause;
2021 break;
2022 }
2023 } break;
2024 case XCB_CONFIGURE_NOTIFY: {
2025 const xcb_configure_notify_event_t *cfg =
2026 (const xcb_configure_notify_event_t *)event;
2027 if ((demo->width != cfg->width) || (demo->height != cfg->height)) {
2028 demo->width = cfg->width;
2029 demo->height = cfg->height;
2030 demo_resize(demo);
2031 }
2032 } break;
2033 default:
2034 break;
2035 }
2036 }
2037
demo_run(struct demo * demo)2038 static void demo_run(struct demo *demo) {
2039 xcb_flush(demo->connection);
2040
2041 while (!demo->quit) {
2042 xcb_generic_event_t *event;
2043
2044 if (demo->pause) {
2045 event = xcb_wait_for_event(demo->connection);
2046 } else {
2047 event = xcb_poll_for_event(demo->connection);
2048 }
2049 if (event) {
2050 demo_handle_event(demo, event);
2051 free(event);
2052 }
2053
2054 // Wait for work to finish before updating MVP.
2055 vkDeviceWaitIdle(demo->device);
2056 demo_update_data_buffer(demo);
2057
2058 demo_draw(demo);
2059
2060 // Wait for work to finish before updating MVP.
2061 vkDeviceWaitIdle(demo->device);
2062 demo->curFrame++;
2063 if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount)
2064 demo->quit = true;
2065 }
2066 }
2067
demo_create_window(struct demo * demo)2068 static void demo_create_window(struct demo *demo) {
2069 uint32_t value_mask, value_list[32];
2070
2071 demo->window = xcb_generate_id(demo->connection);
2072
2073 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
2074 value_list[0] = demo->screen->black_pixel;
2075 value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE |
2076 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
2077
2078 xcb_create_window(demo->connection, XCB_COPY_FROM_PARENT, demo->window,
2079 demo->screen->root, 0, 0, demo->width, demo->height, 0,
2080 XCB_WINDOW_CLASS_INPUT_OUTPUT, demo->screen->root_visual,
2081 value_mask, value_list);
2082
2083 /* Magic code that will send notification when window is destroyed */
2084 xcb_intern_atom_cookie_t cookie =
2085 xcb_intern_atom(demo->connection, 1, 12, "WM_PROTOCOLS");
2086 xcb_intern_atom_reply_t *reply =
2087 xcb_intern_atom_reply(demo->connection, cookie, 0);
2088
2089 xcb_intern_atom_cookie_t cookie2 =
2090 xcb_intern_atom(demo->connection, 0, 16, "WM_DELETE_WINDOW");
2091 demo->atom_wm_delete_window =
2092 xcb_intern_atom_reply(demo->connection, cookie2, 0);
2093
2094 xcb_change_property(demo->connection, XCB_PROP_MODE_REPLACE, demo->window,
2095 (*reply).atom, 4, 32, 1,
2096 &(*demo->atom_wm_delete_window).atom);
2097 free(reply);
2098
2099 xcb_map_window(demo->connection, demo->window);
2100
2101 // Force the x/y coordinates to 100,100 results are identical in consecutive
2102 // runs
2103 const uint32_t coords[] = {100, 100};
2104 xcb_configure_window(demo->connection, demo->window,
2105 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords);
2106 }
2107 #endif // _WIN32
2108
2109 /*
2110 * Return 1 (true) if all layer names specified in check_names
2111 * can be found in given layer properties.
2112 */
demo_check_layers(uint32_t check_count,char ** check_names,uint32_t layer_count,VkLayerProperties * layers)2113 static VkBool32 demo_check_layers(uint32_t check_count, char **check_names,
2114 uint32_t layer_count,
2115 VkLayerProperties *layers) {
2116 for (uint32_t i = 0; i < check_count; i++) {
2117 VkBool32 found = 0;
2118 for (uint32_t j = 0; j < layer_count; j++) {
2119 if (!strcmp(check_names[i], layers[j].layerName)) {
2120 found = 1;
2121 break;
2122 }
2123 }
2124 if (!found) {
2125 fprintf(stderr, "Cannot find layer: %s\n", check_names[i]);
2126 return 0;
2127 }
2128 }
2129 return 1;
2130 }
2131
demo_init_vk(struct demo * demo)2132 static void demo_init_vk(struct demo *demo) {
2133 VkResult err;
2134 char *extension_names[64];
2135 uint32_t instance_extension_count = 0;
2136 uint32_t instance_layer_count = 0;
2137 uint32_t device_validation_layer_count = 0;
2138 uint32_t enabled_extension_count = 0;
2139 uint32_t enabled_layer_count = 0;
2140
2141 char *instance_validation_layers[] = {
2142 "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation",
2143 "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_object_tracker",
2144 "VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation",
2145 "VK_LAYER_LUNARG_swapchain",
2146 "VK_LAYER_GOOGLE_unique_objects"
2147 };
2148
2149 demo->device_validation_layers[0] = "VK_LAYER_GOOGLE_threading";
2150 demo->device_validation_layers[1] = "VK_LAYER_LUNARG_parameter_validation";
2151 demo->device_validation_layers[2] = "VK_LAYER_LUNARG_device_limits";
2152 demo->device_validation_layers[3] = "VK_LAYER_LUNARG_object_tracker";
2153 demo->device_validation_layers[4] = "VK_LAYER_LUNARG_image";
2154 demo->device_validation_layers[5] = "VK_LAYER_LUNARG_core_validation";
2155 demo->device_validation_layers[6] = "VK_LAYER_LUNARG_swapchain";
2156 demo->device_validation_layers[7] = "VK_LAYER_GOOGLE_unique_objects";
2157 device_validation_layer_count = 8;
2158
2159 /* Look for validation layers */
2160 VkBool32 validation_found = 0;
2161 err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
2162 assert(!err);
2163
2164 if (instance_layer_count > 0) {
2165 VkLayerProperties *instance_layers =
2166 malloc(sizeof(VkLayerProperties) * instance_layer_count);
2167 err = vkEnumerateInstanceLayerProperties(&instance_layer_count,
2168 instance_layers);
2169 assert(!err);
2170
2171 if (demo->validate) {
2172 validation_found = demo_check_layers(
2173 ARRAY_SIZE(instance_validation_layers),
2174 instance_validation_layers, instance_layer_count,
2175 instance_layers);
2176
2177 enabled_layer_count = ARRAY_SIZE(instance_validation_layers);
2178 }
2179
2180 free(instance_layers);
2181 }
2182
2183 if (demo->validate && !validation_found) {
2184 ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find"
2185 "required validation layer.\n\n"
2186 "Please look at the Getting Started guide for additional "
2187 "information.\n",
2188 "vkCreateInstance Failure");
2189 }
2190
2191 /* Look for instance extensions */
2192 VkBool32 surfaceExtFound = 0;
2193 VkBool32 platformSurfaceExtFound = 0;
2194 memset(extension_names, 0, sizeof(extension_names));
2195
2196 err = vkEnumerateInstanceExtensionProperties(
2197 NULL, &instance_extension_count, NULL);
2198 assert(!err);
2199
2200 if (instance_extension_count > 0) {
2201 VkExtensionProperties *instance_extensions =
2202 malloc(sizeof(VkExtensionProperties) * instance_extension_count);
2203 err = vkEnumerateInstanceExtensionProperties(
2204 NULL, &instance_extension_count, instance_extensions);
2205 assert(!err);
2206 for (uint32_t i = 0; i < instance_extension_count; i++) {
2207 if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME,
2208 instance_extensions[i].extensionName)) {
2209 surfaceExtFound = 1;
2210 extension_names[enabled_extension_count++] =
2211 VK_KHR_SURFACE_EXTENSION_NAME;
2212 }
2213 #ifdef _WIN32
2214 if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
2215 instance_extensions[i].extensionName)) {
2216 platformSurfaceExtFound = 1;
2217 extension_names[enabled_extension_count++] =
2218 VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
2219 }
2220 #else // _WIN32
2221 if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME,
2222 instance_extensions[i].extensionName)) {
2223 platformSurfaceExtFound = 1;
2224 extension_names[enabled_extension_count++] =
2225 VK_KHR_XCB_SURFACE_EXTENSION_NAME;
2226 }
2227 #endif // _WIN32
2228 if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
2229 instance_extensions[i].extensionName)) {
2230 if (demo->validate) {
2231 extension_names[enabled_extension_count++] =
2232 VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
2233 }
2234 }
2235 assert(enabled_extension_count < 64);
2236 }
2237
2238 free(instance_extensions);
2239 }
2240
2241 if (!surfaceExtFound) {
2242 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
2243 "the " VK_KHR_SURFACE_EXTENSION_NAME
2244 " extension.\n\nDo you have a compatible "
2245 "Vulkan installable client driver (ICD) installed?\nPlease "
2246 "look at the Getting Started guide for additional "
2247 "information.\n",
2248 "vkCreateInstance Failure");
2249 }
2250 if (!platformSurfaceExtFound) {
2251 #ifdef _WIN32
2252 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
2253 "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME
2254 " extension.\n\nDo you have a compatible "
2255 "Vulkan installable client driver (ICD) installed?\nPlease "
2256 "look at the Getting Started guide for additional "
2257 "information.\n",
2258 "vkCreateInstance Failure");
2259 #else // _WIN32
2260 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
2261 "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME
2262 " extension.\n\nDo you have a compatible "
2263 "Vulkan installable client driver (ICD) installed?\nPlease "
2264 "look at the Getting Started guide for additional "
2265 "information.\n",
2266 "vkCreateInstance Failure");
2267 #endif // _WIN32
2268 }
2269 const VkApplicationInfo app = {
2270 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
2271 .pNext = NULL,
2272 .pApplicationName = APP_SHORT_NAME,
2273 .applicationVersion = 0,
2274 .pEngineName = APP_SHORT_NAME,
2275 .engineVersion = 0,
2276 .apiVersion = VK_API_VERSION_1_0,
2277 };
2278 VkInstanceCreateInfo inst_info = {
2279 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
2280 .pNext = NULL,
2281 .pApplicationInfo = &app,
2282 .enabledLayerCount = enabled_layer_count,
2283 .ppEnabledLayerNames =
2284 (const char *const *)((demo->validate) ? instance_validation_layers
2285 : NULL),
2286 .enabledExtensionCount = enabled_extension_count,
2287 .ppEnabledExtensionNames = (const char *const *)extension_names,
2288 };
2289
2290 uint32_t gpu_count;
2291
2292 err = vkCreateInstance(&inst_info, NULL, &demo->inst);
2293 if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
2294 ERR_EXIT("Cannot find a compatible Vulkan installable client driver "
2295 "(ICD).\n\nPlease look at the Getting Started guide for "
2296 "additional information.\n",
2297 "vkCreateInstance Failure");
2298 } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) {
2299 ERR_EXIT("Cannot find a specified extension library"
2300 ".\nMake sure your layers path is set appropriately.\n",
2301 "vkCreateInstance Failure");
2302 } else if (err) {
2303 ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan "
2304 "installable client driver (ICD) installed?\nPlease look at "
2305 "the Getting Started guide for additional information.\n",
2306 "vkCreateInstance Failure");
2307 }
2308
2309 /* Make initial call to query gpu_count, then second call for gpu info*/
2310 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
2311 assert(!err && gpu_count > 0);
2312
2313 if (gpu_count > 0) {
2314 VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
2315 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, physical_devices);
2316 assert(!err);
2317 /* For cube demo we just grab the first physical device */
2318 demo->gpu = physical_devices[0];
2319 free(physical_devices);
2320 } else {
2321 ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
2322 "Do you have a compatible Vulkan installable client driver (ICD) "
2323 "installed?\nPlease look at the Getting Started guide for "
2324 "additional information.\n",
2325 "vkEnumeratePhysicalDevices Failure");
2326 }
2327
2328 /* Look for validation layers */
2329 validation_found = 0;
2330 demo->enabled_layer_count = 0;
2331 uint32_t device_layer_count = 0;
2332 err =
2333 vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL);
2334 assert(!err);
2335
2336 if (device_layer_count > 0) {
2337 VkLayerProperties *device_layers =
2338 malloc(sizeof(VkLayerProperties) * device_layer_count);
2339 err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count,
2340 device_layers);
2341 assert(!err);
2342
2343 if (demo->validate) {
2344 validation_found = demo_check_layers(device_validation_layer_count,
2345 demo->device_validation_layers,
2346 device_layer_count,
2347 device_layers);
2348 demo->enabled_layer_count = device_validation_layer_count;
2349 }
2350
2351 free(device_layers);
2352 }
2353
2354 if (demo->validate && !validation_found) {
2355 ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find"
2356 "a required validation layer.\n\n"
2357 "Please look at the Getting Started guide for additional "
2358 "information.\n",
2359 "vkCreateDevice Failure");
2360 }
2361
2362 /* Look for device extensions */
2363 uint32_t device_extension_count = 0;
2364 VkBool32 swapchainExtFound = 0;
2365 demo->enabled_extension_count = 0;
2366 memset(extension_names, 0, sizeof(extension_names));
2367
2368 err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL,
2369 &device_extension_count, NULL);
2370 assert(!err);
2371
2372 if (device_extension_count > 0) {
2373 VkExtensionProperties *device_extensions =
2374 malloc(sizeof(VkExtensionProperties) * device_extension_count);
2375 err = vkEnumerateDeviceExtensionProperties(
2376 demo->gpu, NULL, &device_extension_count, device_extensions);
2377 assert(!err);
2378
2379 for (uint32_t i = 0; i < device_extension_count; i++) {
2380 if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
2381 device_extensions[i].extensionName)) {
2382 swapchainExtFound = 1;
2383 demo->extension_names[demo->enabled_extension_count++] =
2384 VK_KHR_SWAPCHAIN_EXTENSION_NAME;
2385 }
2386 assert(demo->enabled_extension_count < 64);
2387 }
2388
2389 free(device_extensions);
2390 }
2391
2392 if (!swapchainExtFound) {
2393 ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find "
2394 "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME
2395 " extension.\n\nDo you have a compatible "
2396 "Vulkan installable client driver (ICD) installed?\nPlease "
2397 "look at the Getting Started guide for additional "
2398 "information.\n",
2399 "vkCreateInstance Failure");
2400 }
2401
2402 if (demo->validate) {
2403 demo->CreateDebugReportCallback =
2404 (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(
2405 demo->inst, "vkCreateDebugReportCallbackEXT");
2406 demo->DestroyDebugReportCallback =
2407 (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(
2408 demo->inst, "vkDestroyDebugReportCallbackEXT");
2409 if (!demo->CreateDebugReportCallback) {
2410 ERR_EXIT(
2411 "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n",
2412 "vkGetProcAddr Failure");
2413 }
2414 if (!demo->DestroyDebugReportCallback) {
2415 ERR_EXIT(
2416 "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n",
2417 "vkGetProcAddr Failure");
2418 }
2419 demo->DebugReportMessage =
2420 (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(
2421 demo->inst, "vkDebugReportMessageEXT");
2422 if (!demo->DebugReportMessage) {
2423 ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n",
2424 "vkGetProcAddr Failure");
2425 }
2426
2427 PFN_vkDebugReportCallbackEXT callback;
2428
2429 if (!demo->use_break) {
2430 callback = dbgFunc;
2431 } else {
2432 callback = dbgFunc;
2433 // TODO add a break callback defined locally since there is no
2434 // longer
2435 // one included in the loader
2436 }
2437 VkDebugReportCallbackCreateInfoEXT dbgCreateInfo;
2438 dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
2439 dbgCreateInfo.pNext = NULL;
2440 dbgCreateInfo.pfnCallback = callback;
2441 dbgCreateInfo.pUserData = NULL;
2442 dbgCreateInfo.flags =
2443 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
2444 err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL,
2445 &demo->msg_callback);
2446 switch (err) {
2447 case VK_SUCCESS:
2448 break;
2449 case VK_ERROR_OUT_OF_HOST_MEMORY:
2450 ERR_EXIT("CreateDebugReportCallback: out of host memory\n",
2451 "CreateDebugReportCallback Failure");
2452 break;
2453 default:
2454 ERR_EXIT("CreateDebugReportCallback: unknown failure\n",
2455 "CreateDebugReportCallback Failure");
2456 break;
2457 }
2458 }
2459 vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props);
2460
2461 /* Call with NULL data to get count */
2462 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
2463 NULL);
2464 assert(demo->queue_count >= 1);
2465
2466 demo->queue_props = (VkQueueFamilyProperties *)malloc(
2467 demo->queue_count * sizeof(VkQueueFamilyProperties));
2468 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
2469 demo->queue_props);
2470 // Find a queue that supports gfx
2471 uint32_t gfx_queue_idx = 0;
2472 for (gfx_queue_idx = 0; gfx_queue_idx < demo->queue_count;
2473 gfx_queue_idx++) {
2474 if (demo->queue_props[gfx_queue_idx].queueFlags & VK_QUEUE_GRAPHICS_BIT)
2475 break;
2476 }
2477 assert(gfx_queue_idx < demo->queue_count);
2478 // Query fine-grained feature support for this device.
2479 // If app has specific feature requirements it should check supported
2480 // features based on this query
2481 VkPhysicalDeviceFeatures physDevFeatures;
2482 vkGetPhysicalDeviceFeatures(demo->gpu, &physDevFeatures);
2483
2484 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR);
2485 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR);
2486 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR);
2487 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR);
2488 GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR);
2489 }
2490
demo_create_device(struct demo * demo)2491 static void demo_create_device(struct demo *demo) {
2492 VkResult U_ASSERT_ONLY err;
2493 float queue_priorities[1] = {0.0};
2494 const VkDeviceQueueCreateInfo queue = {
2495 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
2496 .pNext = NULL,
2497 .queueFamilyIndex = demo->graphics_queue_node_index,
2498 .queueCount = 1,
2499 .pQueuePriorities = queue_priorities};
2500
2501 VkDeviceCreateInfo device = {
2502 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
2503 .pNext = NULL,
2504 .queueCreateInfoCount = 1,
2505 .pQueueCreateInfos = &queue,
2506 .enabledLayerCount = demo->enabled_layer_count,
2507 .ppEnabledLayerNames =
2508 (const char *const *)((demo->validate)
2509 ? demo->device_validation_layers
2510 : NULL),
2511 .enabledExtensionCount = demo->enabled_extension_count,
2512 .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
2513 .pEnabledFeatures =
2514 NULL, // If specific features are required, pass them in here
2515 };
2516
2517 err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device);
2518 assert(!err);
2519 }
2520
demo_init_vk_swapchain(struct demo * demo)2521 static void demo_init_vk_swapchain(struct demo *demo) {
2522 VkResult U_ASSERT_ONLY err;
2523 uint32_t i;
2524
2525 // Create a WSI surface for the window:
2526 #ifdef _WIN32
2527 VkWin32SurfaceCreateInfoKHR createInfo;
2528 createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
2529 createInfo.pNext = NULL;
2530 createInfo.flags = 0;
2531 createInfo.hinstance = demo->connection;
2532 createInfo.hwnd = demo->window;
2533
2534 err =
2535 vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
2536
2537 #else // _WIN32
2538 VkXcbSurfaceCreateInfoKHR createInfo;
2539 createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
2540 createInfo.pNext = NULL;
2541 createInfo.flags = 0;
2542 createInfo.connection = demo->connection;
2543 createInfo.window = demo->window;
2544
2545 err = vkCreateXcbSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
2546 #endif // _WIN32
2547
2548 // Iterate over each queue to learn whether it supports presenting:
2549 VkBool32 *supportsPresent =
2550 (VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32));
2551 for (i = 0; i < demo->queue_count; i++) {
2552 demo->fpGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface,
2553 &supportsPresent[i]);
2554 }
2555
2556 // Search for a graphics and a present queue in the array of queue
2557 // families, try to find one that supports both
2558 uint32_t graphicsQueueNodeIndex = UINT32_MAX;
2559 uint32_t presentQueueNodeIndex = UINT32_MAX;
2560 for (i = 0; i < demo->queue_count; i++) {
2561 if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
2562 if (graphicsQueueNodeIndex == UINT32_MAX) {
2563 graphicsQueueNodeIndex = i;
2564 }
2565
2566 if (supportsPresent[i] == VK_TRUE) {
2567 graphicsQueueNodeIndex = i;
2568 presentQueueNodeIndex = i;
2569 break;
2570 }
2571 }
2572 }
2573 if (presentQueueNodeIndex == UINT32_MAX) {
2574 // If didn't find a queue that supports both graphics and present, then
2575 // find a separate present queue.
2576 for (uint32_t i = 0; i < demo->queue_count; ++i) {
2577 if (supportsPresent[i] == VK_TRUE) {
2578 presentQueueNodeIndex = i;
2579 break;
2580 }
2581 }
2582 }
2583 free(supportsPresent);
2584
2585 // Generate error if could not find both a graphics and a present queue
2586 if (graphicsQueueNodeIndex == UINT32_MAX ||
2587 presentQueueNodeIndex == UINT32_MAX) {
2588 ERR_EXIT("Could not find a graphics and a present queue\n",
2589 "Swapchain Initialization Failure");
2590 }
2591
2592 // TODO: Add support for separate queues, including presentation,
2593 // synchronization, and appropriate tracking for QueueSubmit.
2594 // NOTE: While it is possible for an application to use a separate graphics
2595 // and a present queues, this demo program assumes it is only using
2596 // one:
2597 if (graphicsQueueNodeIndex != presentQueueNodeIndex) {
2598 ERR_EXIT("Could not find a common graphics and a present queue\n",
2599 "Swapchain Initialization Failure");
2600 }
2601
2602 demo->graphics_queue_node_index = graphicsQueueNodeIndex;
2603
2604 demo_create_device(demo);
2605
2606 GET_DEVICE_PROC_ADDR(demo->device, CreateSwapchainKHR);
2607 GET_DEVICE_PROC_ADDR(demo->device, DestroySwapchainKHR);
2608 GET_DEVICE_PROC_ADDR(demo->device, GetSwapchainImagesKHR);
2609 GET_DEVICE_PROC_ADDR(demo->device, AcquireNextImageKHR);
2610 GET_DEVICE_PROC_ADDR(demo->device, QueuePresentKHR);
2611
2612 vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0,
2613 &demo->queue);
2614
2615 // Get the list of VkFormat's that are supported:
2616 uint32_t formatCount;
2617 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
2618 &formatCount, NULL);
2619 assert(!err);
2620 VkSurfaceFormatKHR *surfFormats =
2621 (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
2622 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
2623 &formatCount, surfFormats);
2624 assert(!err);
2625 // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
2626 // the surface has no preferred format. Otherwise, at least one
2627 // supported format will be returned.
2628 if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
2629 demo->format = VK_FORMAT_B8G8R8A8_UNORM;
2630 } else {
2631 assert(formatCount >= 1);
2632 demo->format = surfFormats[0].format;
2633 }
2634 demo->color_space = surfFormats[0].colorSpace;
2635
2636 demo->quit = false;
2637 demo->curFrame = 0;
2638
2639 // Get Memory information and properties
2640 vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
2641 }
2642
demo_init_connection(struct demo * demo)2643 static void demo_init_connection(struct demo *demo) {
2644 #ifndef _WIN32
2645 const xcb_setup_t *setup;
2646 xcb_screen_iterator_t iter;
2647 int scr;
2648
2649 demo->connection = xcb_connect(NULL, &scr);
2650 if (demo->connection == NULL) {
2651 printf("Cannot find a compatible Vulkan installable client driver "
2652 "(ICD).\nExiting ...\n");
2653 fflush(stdout);
2654 exit(1);
2655 }
2656
2657 setup = xcb_get_setup(demo->connection);
2658 iter = xcb_setup_roots_iterator(setup);
2659 while (scr-- > 0)
2660 xcb_screen_next(&iter);
2661
2662 demo->screen = iter.data;
2663 #endif // _WIN32
2664 }
2665
demo_init(struct demo * demo,int argc,char ** argv)2666 static void demo_init(struct demo *demo, int argc, char **argv) {
2667 vec3 eye = {0.0f, 3.0f, 5.0f};
2668 vec3 origin = {0, 0, 0};
2669 vec3 up = {0.0f, 1.0f, 0.0};
2670
2671 memset(demo, 0, sizeof(*demo));
2672 demo->frameCount = INT32_MAX;
2673
2674 for (int i = 1; i < argc; i++) {
2675 if (strcmp(argv[i], "--use_staging") == 0) {
2676 demo->use_staging_buffer = true;
2677 continue;
2678 }
2679 if (strcmp(argv[i], "--break") == 0) {
2680 demo->use_break = true;
2681 continue;
2682 }
2683 if (strcmp(argv[i], "--validate") == 0) {
2684 demo->validate = true;
2685 continue;
2686 }
2687 if (strcmp(argv[i], "--c") == 0 && demo->frameCount == INT32_MAX &&
2688 i < argc - 1 && sscanf(argv[i + 1], "%d", &demo->frameCount) == 1 &&
2689 demo->frameCount >= 0) {
2690 i++;
2691 continue;
2692 }
2693
2694 fprintf(stderr, "Usage:\n %s [--use_staging] [--validate] [--break] "
2695 "[--c <framecount>]\n",
2696 APP_SHORT_NAME);
2697 fflush(stderr);
2698 exit(1);
2699 }
2700
2701 demo_init_connection(demo);
2702 demo_init_vk(demo);
2703
2704 demo->width = 500;
2705 demo->height = 500;
2706
2707 demo->spin_angle = 0.01f;
2708 demo->spin_increment = 0.01f;
2709 demo->pause = false;
2710
2711 mat4x4_perspective(demo->projection_matrix, (float)degreesToRadians(45.0f),
2712 1.0f, 0.1f, 100.0f);
2713 mat4x4_look_at(demo->view_matrix, eye, origin, up);
2714 mat4x4_identity(demo->model_matrix);
2715 }
2716
2717 #ifdef _WIN32
2718 // Include header required for parsing the command line options.
2719 #include <shellapi.h>
2720
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR pCmdLine,int nCmdShow)2721 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine,
2722 int nCmdShow) {
2723 MSG msg; // message
2724 bool done; // flag saying when app is complete
2725 int argc;
2726 char **argv;
2727
2728 // Use the CommandLine functions to get the command line arguments.
2729 // Unfortunately, Microsoft outputs
2730 // this information as wide characters for Unicode, and we simply want the
2731 // Ascii version to be compatible
2732 // with the non-Windows side. So, we have to convert the information to
2733 // Ascii character strings.
2734 LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc);
2735 if (NULL == commandLineArgs) {
2736 argc = 0;
2737 }
2738
2739 if (argc > 0) {
2740 argv = (char **)malloc(sizeof(char *) * argc);
2741 if (argv == NULL) {
2742 argc = 0;
2743 } else {
2744 for (int iii = 0; iii < argc; iii++) {
2745 size_t wideCharLen = wcslen(commandLineArgs[iii]);
2746 size_t numConverted = 0;
2747
2748 argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1));
2749 if (argv[iii] != NULL) {
2750 wcstombs_s(&numConverted, argv[iii], wideCharLen + 1,
2751 commandLineArgs[iii], wideCharLen + 1);
2752 }
2753 }
2754 }
2755 } else {
2756 argv = NULL;
2757 }
2758
2759 demo_init(&demo, argc, argv);
2760
2761 // Free up the items we had to allocate for the command line arguments.
2762 if (argc > 0 && argv != NULL) {
2763 for (int iii = 0; iii < argc; iii++) {
2764 if (argv[iii] != NULL) {
2765 free(argv[iii]);
2766 }
2767 }
2768 free(argv);
2769 }
2770
2771 demo.connection = hInstance;
2772 strncpy(demo.name, "cube", APP_NAME_STR_LEN);
2773 demo_create_window(&demo);
2774 demo_init_vk_swapchain(&demo);
2775
2776 demo_prepare(&demo);
2777
2778 done = false; // initialize loop condition variable
2779
2780 // main message loop
2781 while (!done) {
2782 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
2783 if (msg.message == WM_QUIT) // check for a quit message
2784 {
2785 done = true; // if found, quit app
2786 } else {
2787 /* Translate and dispatch to event queue*/
2788 TranslateMessage(&msg);
2789 DispatchMessage(&msg);
2790 }
2791 RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT);
2792 }
2793
2794 demo_cleanup(&demo);
2795
2796 return (int)msg.wParam;
2797 }
2798 #else // _WIN32
main(int argc,char ** argv)2799 int main(int argc, char **argv) {
2800 struct demo demo;
2801
2802 demo_init(&demo, argc, argv);
2803 demo_create_window(&demo);
2804 demo_init_vk_swapchain(&demo);
2805
2806 demo_prepare(&demo);
2807 demo_run(&demo);
2808
2809 demo_cleanup(&demo);
2810
2811 return validation_error;
2812 }
2813 #endif // _WIN32
2814