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
88static 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 */
106struct 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
118static char *tex_files[] = {"lunarg.ppm"};
119
120static int validation_error = 0;
121
122struct 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
129struct 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
140struct Vertex
141{
142    float     posX, posY, posZ, posW;    // Position data
143    float     r, g, b, a;                // Color
144};
145
146struct 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
155static 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
199static 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
244void 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
255void 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
262VKAPI_ATTR VkBool32 VKAPI_CALL
263dbgFunc(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
307VkBool32 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
320typedef struct _SwapchainBuffers {
321    VkImage image;
322    VkCommandBuffer cmd;
323    VkImageView view;
324} SwapchainBuffers;
325
326struct 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:
435static void demo_resize(struct demo *demo);
436
437static 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
456static 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
487static 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
566static 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
651void 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
675static 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
766static 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
932static 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 */
1007bool 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
1055static 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
1148static 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
1155static 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, &copy_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
1275void 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
1344static 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
1387static 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
1449static VkShaderModule
1450demo_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
1467char *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
1491static 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
1505static 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
1519static 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
1636static 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
1663static 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
1703static 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
1732static 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
1788static 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
1844static 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()
1897struct demo demo;
1898
1899#ifdef _WIN32
1900static 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:
1922LRESULT 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
1946static 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
1992static 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
2038static 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
2068static 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 */
2113static 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
2132static 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
2491static 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
2521static 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
2643static 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
2666static 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
2721int 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
2799int 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