Home | History | Annotate | Download | only in demos
      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 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 *
     18 * Author: Jeremy Hayes <jeremy (at) lunarg.com>
     19 */
     20 
     21 #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
     22 #include <X11/Xutil.h>
     23 #endif
     24 
     25 #include <cassert>
     26 #include <cstdio>
     27 #include <cstdlib>
     28 #include <cstring>
     29 #include <csignal>
     30 #include <memory>
     31 
     32 #define VULKAN_HPP_NO_EXCEPTIONS
     33 #include <vulkan/vulkan.hpp>
     34 #include <vulkan/vk_sdk_platform.h>
     35 
     36 #include "linmath.h"
     37 
     38 #ifndef NDEBUG
     39 #define VERIFY(x) assert(x)
     40 #else
     41 #define VERIFY(x) ((void)(x))
     42 #endif
     43 
     44 #define APP_SHORT_NAME "cube"
     45 #ifdef _WIN32
     46 #define APP_NAME_STR_LEN 80
     47 #endif
     48 
     49 // Allow a maximum of two outstanding presentation operations.
     50 #define FRAME_LAG 2
     51 
     52 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
     53 
     54 #ifdef _WIN32
     55 #define ERR_EXIT(err_msg, err_class)                                           \
     56     do {                                                                       \
     57         if (!suppress_popups)                                                  \
     58             MessageBox(nullptr, err_msg, err_class, MB_OK);                    \
     59         exit(1);                                                               \
     60     } while (0)
     61 #else
     62 #define ERR_EXIT(err_msg, err_class)                                           \
     63     do {                                                                       \
     64         printf(err_msg);                                                       \
     65         fflush(stdout);                                                        \
     66         exit(1);                                                               \
     67     } while (0)
     68 #endif
     69 
     70 struct texture_object {
     71     vk::Sampler sampler;
     72 
     73     vk::Image image;
     74     vk::ImageLayout imageLayout;
     75 
     76     vk::MemoryAllocateInfo mem_alloc;
     77     vk::DeviceMemory mem;
     78     vk::ImageView view;
     79 
     80     int32_t tex_width;
     81     int32_t tex_height;
     82 };
     83 
     84 static char const *const tex_files[] = {"lunarg.ppm"};
     85 
     86 static int validation_error = 0;
     87 
     88 struct vkcube_vs_uniform {
     89     // Must start with MVP
     90     float mvp[4][4];
     91     float position[12 * 3][4];
     92     float color[12 * 3][4];
     93 };
     94 
     95 struct vktexcube_vs_uniform {
     96     // Must start with MVP
     97     float mvp[4][4];
     98     float position[12 * 3][4];
     99     float attr[12 * 3][4];
    100 };
    101 
    102 //--------------------------------------------------------------------------------------
    103 // Mesh and VertexFormat Data
    104 //--------------------------------------------------------------------------------------
    105 // clang-format off
    106 static const float g_vertex_buffer_data[] = {
    107     -1.0f,-1.0f,-1.0f,  // -X side
    108     -1.0f,-1.0f, 1.0f,
    109     -1.0f, 1.0f, 1.0f,
    110     -1.0f, 1.0f, 1.0f,
    111     -1.0f, 1.0f,-1.0f,
    112     -1.0f,-1.0f,-1.0f,
    113 
    114     -1.0f,-1.0f,-1.0f,  // -Z side
    115      1.0f, 1.0f,-1.0f,
    116      1.0f,-1.0f,-1.0f,
    117     -1.0f,-1.0f,-1.0f,
    118     -1.0f, 1.0f,-1.0f,
    119      1.0f, 1.0f,-1.0f,
    120 
    121     -1.0f,-1.0f,-1.0f,  // -Y side
    122      1.0f,-1.0f,-1.0f,
    123      1.0f,-1.0f, 1.0f,
    124     -1.0f,-1.0f,-1.0f,
    125      1.0f,-1.0f, 1.0f,
    126     -1.0f,-1.0f, 1.0f,
    127 
    128     -1.0f, 1.0f,-1.0f,  // +Y side
    129     -1.0f, 1.0f, 1.0f,
    130      1.0f, 1.0f, 1.0f,
    131     -1.0f, 1.0f,-1.0f,
    132      1.0f, 1.0f, 1.0f,
    133      1.0f, 1.0f,-1.0f,
    134 
    135      1.0f, 1.0f,-1.0f,  // +X side
    136      1.0f, 1.0f, 1.0f,
    137      1.0f,-1.0f, 1.0f,
    138      1.0f,-1.0f, 1.0f,
    139      1.0f,-1.0f,-1.0f,
    140      1.0f, 1.0f,-1.0f,
    141 
    142     -1.0f, 1.0f, 1.0f,  // +Z side
    143     -1.0f,-1.0f, 1.0f,
    144      1.0f, 1.0f, 1.0f,
    145     -1.0f,-1.0f, 1.0f,
    146      1.0f,-1.0f, 1.0f,
    147      1.0f, 1.0f, 1.0f,
    148 };
    149 
    150 static const float g_uv_buffer_data[] = {
    151     0.0f, 1.0f,  // -X side
    152     1.0f, 1.0f,
    153     1.0f, 0.0f,
    154     1.0f, 0.0f,
    155     0.0f, 0.0f,
    156     0.0f, 1.0f,
    157 
    158     1.0f, 1.0f,  // -Z side
    159     0.0f, 0.0f,
    160     0.0f, 1.0f,
    161     1.0f, 1.0f,
    162     1.0f, 0.0f,
    163     0.0f, 0.0f,
    164 
    165     1.0f, 0.0f,  // -Y side
    166     1.0f, 1.0f,
    167     0.0f, 1.0f,
    168     1.0f, 0.0f,
    169     0.0f, 1.0f,
    170     0.0f, 0.0f,
    171 
    172     1.0f, 0.0f,  // +Y side
    173     0.0f, 0.0f,
    174     0.0f, 1.0f,
    175     1.0f, 0.0f,
    176     0.0f, 1.0f,
    177     1.0f, 1.0f,
    178 
    179     1.0f, 0.0f,  // +X side
    180     0.0f, 0.0f,
    181     0.0f, 1.0f,
    182     0.0f, 1.0f,
    183     1.0f, 1.0f,
    184     1.0f, 0.0f,
    185 
    186     0.0f, 0.0f,  // +Z side
    187     0.0f, 1.0f,
    188     1.0f, 0.0f,
    189     0.0f, 1.0f,
    190     1.0f, 1.0f,
    191     1.0f, 0.0f,
    192 };
    193 // clang-format on
    194 
    195 typedef struct {
    196     vk::Image image;
    197     vk::CommandBuffer cmd;
    198     vk::CommandBuffer graphics_to_present_cmd;
    199     vk::ImageView view;
    200 } SwapchainBuffers;
    201 
    202 #ifdef _WIN32
    203 // MS-Windows event handling function:
    204 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    205 #endif
    206 
    207 struct Demo {
    208     Demo()
    209         :
    210 #if defined(VK_USE_PLATFORM_WIN32_KHR)
    211           connection{nullptr},
    212           window{nullptr},
    213           minsize(POINT{
    214               0, 0}), // Use explicit construction to avoid MSVC error C2797.
    215 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
    216           display{nullptr},
    217           xlib_window{0}, xlib_wm_delete_window{0}, connection{nullptr},
    218           screen{nullptr}, xcb_window{0}, atom_wm_delete_window{nullptr},
    219 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
    220           display{nullptr},
    221           registry{nullptr}, compositor{nullptr}, window{nullptr},
    222           shell{nullptr}, shell_surface{nullptr},
    223 #endif
    224           prepared{false},
    225           use_staging_buffer{false}, use_xlib{false},
    226           graphics_queue_family_index{0}, present_queue_family_index{0},
    227           enabled_extension_count{0}, enabled_layer_count{0}, width{0},
    228           height{0}, swapchainImageCount{0}, frame_index{0}, spin_angle{0.0f},
    229           spin_increment{0.0f}, pause{false}, quit{false}, curFrame{0},
    230           frameCount{0}, validate{false}, use_break{false},
    231           suppress_popups{false}, current_buffer{0}, queue_family_count{0} {
    232 #if defined(VK_USE_PLATFORM_WIN32_KHR)
    233         memset(name, '\0', APP_NAME_STR_LEN);
    234 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
    235 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
    236 #endif
    237         memset(fencesInited, 0, sizeof(bool) * FRAME_LAG);
    238         memset(projection_matrix, 0, sizeof(projection_matrix));
    239         memset(view_matrix, 0, sizeof(view_matrix));
    240         memset(model_matrix, 0, sizeof(model_matrix));
    241     }
    242 
    243     void build_image_ownership_cmd(uint32_t const &i) {
    244         auto const cmd_buf_info = vk::CommandBufferBeginInfo().setFlags(
    245             vk::CommandBufferUsageFlagBits::eSimultaneousUse);
    246         auto result = buffers[i].graphics_to_present_cmd.begin(&cmd_buf_info);
    247         VERIFY(result == vk::Result::eSuccess);
    248 
    249         auto const image_ownership_barrier =
    250             vk::ImageMemoryBarrier()
    251                 .setSrcAccessMask(vk::AccessFlags())
    252                 .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
    253                 .setOldLayout(vk::ImageLayout::ePresentSrcKHR)
    254                 .setNewLayout(vk::ImageLayout::ePresentSrcKHR)
    255                 .setSrcQueueFamilyIndex(graphics_queue_family_index)
    256                 .setDstQueueFamilyIndex(present_queue_family_index)
    257                 .setImage(buffers[i].image)
    258                 .setSubresourceRange(vk::ImageSubresourceRange(
    259                     vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
    260 
    261         buffers[i].graphics_to_present_cmd.pipelineBarrier(
    262             vk::PipelineStageFlagBits::eColorAttachmentOutput,
    263             vk::PipelineStageFlagBits::eColorAttachmentOutput,
    264             vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1,
    265             &image_ownership_barrier);
    266 
    267         result = buffers[i].graphics_to_present_cmd.end();
    268         VERIFY(result == vk::Result::eSuccess);
    269     }
    270 
    271     vk::Bool32 check_layers(uint32_t check_count,
    272                             char const *const *const check_names,
    273                             uint32_t layer_count, vk::LayerProperties *layers) {
    274         for (uint32_t i = 0; i < check_count; i++) {
    275             vk::Bool32 found = VK_FALSE;
    276             for (uint32_t j = 0; j < layer_count; j++) {
    277                 if (!strcmp(check_names[i], layers[j].layerName)) {
    278                     found = VK_TRUE;
    279                     break;
    280                 }
    281             }
    282             if (!found) {
    283                 fprintf(stderr, "Cannot find layer: %s\n", check_names[i]);
    284                 return 0;
    285             }
    286         }
    287         return VK_TRUE;
    288     }
    289 
    290     void cleanup() {
    291         prepared = false;
    292         device.waitIdle();
    293 
    294         // Wait for fences from present operations
    295         for (uint32_t i = 0; i < FRAME_LAG; i++) {
    296             if (fencesInited[i]) {
    297                 device.waitForFences(1, &fences[i], VK_TRUE, UINT64_MAX);
    298             }
    299             device.destroyFence(fences[i], nullptr);
    300             device.destroySemaphore(image_acquired_semaphores[i], nullptr);
    301             device.destroySemaphore(draw_complete_semaphores[i], nullptr);
    302             if (separate_present_queue) {
    303                 device.destroySemaphore(image_ownership_semaphores[i], nullptr);
    304             }
    305         }
    306 
    307         for (uint32_t i = 0; i < swapchainImageCount; i++) {
    308             device.destroyFramebuffer(framebuffers[i], nullptr);
    309         }
    310         device.destroyDescriptorPool(desc_pool, nullptr);
    311 
    312         device.destroyPipeline(pipeline, nullptr);
    313         device.destroyPipelineCache(pipelineCache, nullptr);
    314         device.destroyRenderPass(render_pass, nullptr);
    315         device.destroyPipelineLayout(pipeline_layout, nullptr);
    316         device.destroyDescriptorSetLayout(desc_layout, nullptr);
    317 
    318         for (uint32_t i = 0; i < texture_count; i++) {
    319             device.destroyImageView(textures[i].view, nullptr);
    320             device.destroyImage(textures[i].image, nullptr);
    321             device.freeMemory(textures[i].mem, nullptr);
    322             device.destroySampler(textures[i].sampler, nullptr);
    323         }
    324         device.destroySwapchainKHR(swapchain, nullptr);
    325 
    326         device.destroyImageView(depth.view, nullptr);
    327         device.destroyImage(depth.image, nullptr);
    328         device.freeMemory(depth.mem, nullptr);
    329 
    330         device.destroyBuffer(uniform_data.buf, nullptr);
    331         device.freeMemory(uniform_data.mem, nullptr);
    332 
    333         for (uint32_t i = 0; i < swapchainImageCount; i++) {
    334             device.destroyImageView(buffers[i].view, nullptr);
    335             device.freeCommandBuffers(cmd_pool, 1, &buffers[i].cmd);
    336         }
    337 
    338         device.destroyCommandPool(cmd_pool, nullptr);
    339 
    340         if (separate_present_queue) {
    341             device.destroyCommandPool(present_cmd_pool, nullptr);
    342         }
    343 
    344         device.destroy(nullptr);
    345         inst.destroySurfaceKHR(surface, nullptr);
    346         inst.destroy(nullptr);
    347 
    348 #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
    349         if (use_xlib) {
    350             XDestroyWindow(display, xlib_window);
    351             XCloseDisplay(display);
    352         } else {
    353             xcb_destroy_window(connection, xcb_window);
    354             xcb_disconnect(connection);
    355         }
    356         free(atom_wm_delete_window);
    357 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
    358         wl_shell_surface_destroy(shell_surface);
    359         wl_surface_destroy(window);
    360         wl_shell_destroy(shell);
    361         wl_compositor_destroy(compositor);
    362         wl_registry_destroy(registry);
    363         wl_display_disconnect(display);
    364 #endif
    365     }
    366 
    367     void create_device() {
    368         float const priorities[1] = {0.0};
    369 
    370         vk::DeviceQueueCreateInfo queues[2];
    371         queues[0].setQueueFamilyIndex(graphics_queue_family_index);
    372         queues[0].setQueueCount(1);
    373         queues[0].setPQueuePriorities(priorities);
    374 
    375         auto deviceInfo = vk::DeviceCreateInfo()
    376                               .setQueueCreateInfoCount(1)
    377                               .setPQueueCreateInfos(queues)
    378                               .setEnabledLayerCount(0)
    379                               .setPpEnabledLayerNames(nullptr)
    380                               .setEnabledExtensionCount(enabled_extension_count)
    381                               .setPpEnabledExtensionNames(
    382                                   (const char *const *)extension_names)
    383                               .setPEnabledFeatures(nullptr);
    384 
    385         if (separate_present_queue) {
    386             queues[1].setQueueFamilyIndex(present_queue_family_index);
    387             queues[1].setQueueCount(1);
    388             queues[1].setPQueuePriorities(priorities);
    389             deviceInfo.setQueueCreateInfoCount(2);
    390         }
    391 
    392         auto result = gpu.createDevice(&deviceInfo, nullptr, &device);
    393         VERIFY(result == vk::Result::eSuccess);
    394     }
    395 
    396     void destroy_texture_image(texture_object *tex_objs) {
    397         // clean up staging resources
    398         device.freeMemory(tex_objs->mem, nullptr);
    399         device.destroyImage(tex_objs->image, nullptr);
    400     }
    401 
    402     void draw() {
    403         if (fencesInited[frame_index]) {
    404             // Ensure no more than FRAME_LAG presentations are outstanding
    405             device.waitForFences(1, &fences[frame_index], VK_TRUE, UINT64_MAX);
    406             device.resetFences(1, &fences[frame_index]);
    407         }
    408 
    409         // Get the index of the next available swapchain image:
    410         auto result = device.acquireNextImageKHR(
    411             swapchain, UINT64_MAX, image_acquired_semaphores[frame_index],
    412             fences[frame_index], &current_buffer);
    413         fencesInited[frame_index] = true;
    414         if (result == vk::Result::eErrorOutOfDateKHR) {
    415             // swapchain is out of date (e.g. the window was resized) and
    416             // must be recreated:
    417             frame_index += 1;
    418             frame_index %= FRAME_LAG;
    419 
    420             resize();
    421             draw();
    422             return;
    423         } else if (result == vk::Result::eSuboptimalKHR) {
    424             // swapchain is not as optimal as it could be, but the platform's
    425             // presentation engine will still present the image correctly.
    426         } else {
    427             VERIFY(result == vk::Result::eSuccess);
    428         }
    429 
    430         // Wait for the image acquired semaphore to be signaled to ensure
    431         // that the image won't be rendered to until the presentation
    432         // engine has fully released ownership to the application, and it is
    433         // okay to render to the image.
    434         vk::PipelineStageFlags const pipe_stage_flags =
    435             vk::PipelineStageFlagBits::eColorAttachmentOutput;
    436         auto const submit_info =
    437             vk::SubmitInfo()
    438                 .setPWaitDstStageMask(&pipe_stage_flags)
    439                 .setWaitSemaphoreCount(1)
    440                 .setPWaitSemaphores(&image_acquired_semaphores[frame_index])
    441                 .setCommandBufferCount(1)
    442                 .setPCommandBuffers(&buffers[current_buffer].cmd)
    443                 .setSignalSemaphoreCount(1)
    444                 .setPSignalSemaphores(&draw_complete_semaphores[frame_index]);
    445 
    446         result = graphics_queue.submit(1, &submit_info, vk::Fence());
    447         VERIFY(result == vk::Result::eSuccess);
    448 
    449         if (separate_present_queue) {
    450             // If we are using separate queues, change image ownership to the
    451             // present queue before presenting, waiting for the draw complete
    452             // semaphore and signalling the ownership released semaphore when
    453             // finished
    454             auto const submit_info =
    455                 vk::SubmitInfo()
    456                     .setPWaitDstStageMask(&pipe_stage_flags)
    457                     .setWaitSemaphoreCount(1)
    458                     .setPWaitSemaphores(&draw_complete_semaphores[frame_index])
    459                     .setCommandBufferCount(1)
    460                     .setPCommandBuffers(
    461                         &buffers[current_buffer].graphics_to_present_cmd)
    462                     .setSignalSemaphoreCount(1)
    463                     .setPSignalSemaphores(
    464                         &image_ownership_semaphores[frame_index]);
    465 
    466             result = present_queue.submit(1, &submit_info, vk::Fence());
    467             VERIFY(result == vk::Result::eSuccess);
    468         }
    469 
    470         // If we are using separate queues we have to wait for image ownership,
    471         // otherwise wait for draw complete
    472         auto const presentInfo =
    473             vk::PresentInfoKHR()
    474                 .setWaitSemaphoreCount(1)
    475                 .setPWaitSemaphores(
    476                     separate_present_queue
    477                         ? &image_ownership_semaphores[frame_index]
    478                         : &draw_complete_semaphores[frame_index])
    479                 .setSwapchainCount(1)
    480                 .setPSwapchains(&swapchain)
    481                 .setPImageIndices(&current_buffer);
    482 
    483         result = present_queue.presentKHR(&presentInfo);
    484         frame_index += 1;
    485         frame_index %= FRAME_LAG;
    486         if (result == vk::Result::eErrorOutOfDateKHR) {
    487             // swapchain is out of date (e.g. the window was resized) and
    488             // must be recreated:
    489             resize();
    490         } else if (result == vk::Result::eSuboptimalKHR) {
    491             // swapchain is not as optimal as it could be, but the platform's
    492             // presentation engine will still present the image correctly.
    493         } else {
    494             VERIFY(result == vk::Result::eSuccess);
    495         }
    496     }
    497 
    498     void draw_build_cmd(vk::CommandBuffer commandBuffer) {
    499         auto const commandInfo = vk::CommandBufferBeginInfo().setFlags(
    500             vk::CommandBufferUsageFlagBits::eSimultaneousUse);
    501 
    502         vk::ClearValue const clearValues[2] = {
    503             vk::ClearColorValue(std::array<float, 4>({0.2f, 0.2f, 0.2f, 0.2f})),
    504             vk::ClearDepthStencilValue(1.0f, 0u)};
    505 
    506         auto const passInfo =
    507             vk::RenderPassBeginInfo()
    508                 .setRenderPass(render_pass)
    509                 .setFramebuffer(framebuffers[current_buffer])
    510                 .setRenderArea(
    511                     vk::Rect2D(vk::Offset2D(0, 0),
    512                                vk::Extent2D((uint32_t)width, (uint32_t)height)))
    513                 .setClearValueCount(2)
    514                 .setPClearValues(clearValues);
    515 
    516         auto result = commandBuffer.begin(&commandInfo);
    517         VERIFY(result == vk::Result::eSuccess);
    518 
    519         auto const image_memory_barrier =
    520             vk::ImageMemoryBarrier()
    521                 .setSrcAccessMask(vk::AccessFlagBits::eMemoryRead)
    522                 .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
    523                 .setOldLayout(vk::ImageLayout::ePresentSrcKHR)
    524                 .setNewLayout(vk::ImageLayout::eColorAttachmentOptimal)
    525                 .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
    526                 .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
    527                 .setImage(buffers[current_buffer].image)
    528                 .setSubresourceRange(vk::ImageSubresourceRange(
    529                     vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
    530 
    531         commandBuffer.pipelineBarrier(
    532             vk::PipelineStageFlagBits::eColorAttachmentOutput,
    533             vk::PipelineStageFlagBits::eColorAttachmentOutput,
    534             vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1,
    535             &image_memory_barrier);
    536 
    537         commandBuffer.beginRenderPass(&passInfo, vk::SubpassContents::eInline);
    538 
    539         commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
    540         commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
    541                                          pipeline_layout, 0, 1, &desc_set, 0,
    542                                          nullptr);
    543 
    544         auto const viewport = vk::Viewport()
    545                                   .setWidth((float)width)
    546                                   .setHeight((float)height)
    547                                   .setMinDepth((float)0.0f)
    548                                   .setMaxDepth((float)1.0f);
    549         commandBuffer.setViewport(0, 1, &viewport);
    550 
    551         vk::Rect2D const scissor(vk::Offset2D(0, 0),
    552                                  vk::Extent2D(width, height));
    553         commandBuffer.setScissor(0, 1, &scissor);
    554         commandBuffer.draw(12 * 3, 1, 0, 0);
    555         // Note that ending the renderpass changes the image's layout from
    556         // COLOR_ATTACHMENT_OPTIMAL to PRESENT_SRC_KHR
    557         commandBuffer.endRenderPass();
    558 
    559         if (separate_present_queue) {
    560             // We have to transfer ownership from the graphics queue family to
    561             // the
    562             // present queue family to be able to present.  Note that we don't
    563             // have
    564             // to transfer from present queue family back to graphics queue
    565             // family at
    566             // the start of the next frame because we don't care about the
    567             // image's
    568             // contents at that point.
    569             auto const image_ownership_barrier =
    570                 vk::ImageMemoryBarrier()
    571                     .setSrcAccessMask(vk::AccessFlags())
    572                     .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
    573                     .setOldLayout(vk::ImageLayout::ePresentSrcKHR)
    574                     .setNewLayout(vk::ImageLayout::ePresentSrcKHR)
    575                     .setSrcQueueFamilyIndex(graphics_queue_family_index)
    576                     .setDstQueueFamilyIndex(present_queue_family_index)
    577                     .setImage(buffers[current_buffer].image)
    578                     .setSubresourceRange(vk::ImageSubresourceRange(
    579                         vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
    580 
    581             commandBuffer.pipelineBarrier(
    582                 vk::PipelineStageFlagBits::eColorAttachmentOutput,
    583                 vk::PipelineStageFlagBits::eColorAttachmentOutput,
    584                 vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1,
    585                 &image_ownership_barrier);
    586         }
    587 
    588         result = commandBuffer.end();
    589         VERIFY(result == vk::Result::eSuccess);
    590     }
    591 
    592     void flush_init_cmd() {
    593         // TODO: hmm.
    594         // This function could get called twice if the texture uses a staging
    595         // buffer
    596         // In that case the second call should be ignored
    597         if (!cmd) {
    598             return;
    599         }
    600 
    601         auto result = cmd.end();
    602         VERIFY(result == vk::Result::eSuccess);
    603 
    604         auto const fenceInfo =
    605             vk::FenceCreateInfo().setFlags(vk::FenceCreateFlagBits(0));
    606         vk::Fence fence;
    607         device.createFence(&fenceInfo, nullptr, &fence);
    608 
    609         vk::CommandBuffer const commandBuffers[] = {cmd};
    610         auto const submitInfo =
    611             vk::SubmitInfo().setCommandBufferCount(1).setPCommandBuffers(
    612                 commandBuffers);
    613 
    614         result = graphics_queue.submit(1, &submitInfo, fence);
    615         VERIFY(result == vk::Result::eSuccess);
    616 
    617         result = device.waitForFences(1, &fence, VK_TRUE, UINT64_MAX);
    618         VERIFY(result == vk::Result::eSuccess);
    619 
    620         device.freeCommandBuffers(cmd_pool, 1, commandBuffers);
    621         device.destroyFence(fence, nullptr);
    622 
    623         cmd = vk::CommandBuffer();
    624     }
    625 
    626     void init(int argc, char **argv) {
    627         vec3 eye = {0.0f, 3.0f, 5.0f};
    628         vec3 origin = {0, 0, 0};
    629         vec3 up = {0.0f, 1.0f, 0.0};
    630 
    631         frameCount = UINT32_MAX;
    632         use_xlib = false;
    633 
    634         for (int i = 1; i < argc; i++) {
    635             if (strcmp(argv[i], "--use_staging") == 0) {
    636                 use_staging_buffer = true;
    637                 continue;
    638             }
    639             if (strcmp(argv[i], "--break") == 0) {
    640                 use_break = true;
    641                 continue;
    642             }
    643             if (strcmp(argv[i], "--validate") == 0) {
    644                 validate = true;
    645                 continue;
    646             }
    647 #if defined(VK_USE_PLATFORM_XLIB_KHR)
    648             if (strcmp(argv[i], "--xlib") == 0) {
    649                 use_xlib = true;
    650                 continue;
    651             }
    652 #endif
    653             if (strcmp(argv[i], "--c") == 0 && frameCount == UINT32_MAX &&
    654                 i < argc - 1 && sscanf(argv[i + 1], "%d", &frameCount) == 1) {
    655                 i++;
    656                 continue;
    657             }
    658             if (strcmp(argv[i], "--suppress_popups") == 0) {
    659                 suppress_popups = true;
    660                 continue;
    661             }
    662 
    663             fprintf(stderr,
    664                     "Usage:\n  %s [--use_staging] [--validate] [--break] "
    665 #if defined(VK_USE_PLATFORM_XLIB_KHR)
    666                     "[--xlib] "
    667 #endif
    668                     "[--c <framecount>] [--suppress_popups]\n",
    669                     APP_SHORT_NAME);
    670             fflush(stderr);
    671             exit(1);
    672         }
    673 
    674         if (!use_xlib) {
    675             init_connection();
    676         }
    677 
    678         init_vk();
    679 
    680         width = 500;
    681         height = 500;
    682 
    683         spin_angle = 4.0f;
    684         spin_increment = 0.2f;
    685         pause = false;
    686 
    687         mat4x4_perspective(projection_matrix, (float)degreesToRadians(45.0f),
    688                            1.0f, 0.1f, 100.0f);
    689         mat4x4_look_at(view_matrix, eye, origin, up);
    690         mat4x4_identity(model_matrix);
    691 
    692         projection_matrix[1][1] *=
    693             -1; // Flip projection matrix from GL to Vulkan orientation.
    694     }
    695 
    696     void init_connection() {
    697 #if defined(VK_USE_PLATFORM_XCB_KHR)
    698         const xcb_setup_t *setup;
    699         xcb_screen_iterator_t iter;
    700         int scr;
    701 
    702         connection = xcb_connect(nullptr, &scr);
    703         if (xcb_connection_has_error(connection) > 0) {
    704             printf("Cannot find a compatible Vulkan installable client driver "
    705                    "(ICD).\nExiting ...\n");
    706             fflush(stdout);
    707             exit(1);
    708         }
    709 
    710         setup = xcb_get_setup(connection);
    711         iter = xcb_setup_roots_iterator(setup);
    712         while (scr-- > 0)
    713             xcb_screen_next(&iter);
    714 
    715         screen = iter.data;
    716 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
    717         display = wl_display_connect(nullptr);
    718 
    719         if (display == nullptr) {
    720             printf("Cannot find a compatible Vulkan installable client driver "
    721                    "(ICD).\nExiting ...\n");
    722             fflush(stdout);
    723             exit(1);
    724         }
    725 
    726         registry = wl_display_get_registry(display);
    727         wl_registry_add_listener(registry, &registry_listener, this);
    728         wl_display_dispatch(display);
    729 #endif
    730     }
    731 
    732     void init_vk() {
    733         uint32_t instance_extension_count = 0;
    734         uint32_t instance_layer_count = 0;
    735         uint32_t validation_layer_count = 0;
    736         char const *const *instance_validation_layers = nullptr;
    737         enabled_extension_count = 0;
    738         enabled_layer_count = 0;
    739 
    740         char const *const instance_validation_layers_alt1[] = {
    741             "VK_LAYER_LUNARG_standard_validation"};
    742 
    743         char const *const instance_validation_layers_alt2[] = {
    744             "VK_LAYER_GOOGLE_threading",
    745             "VK_LAYER_LUNARG_parameter_validation",
    746             "VK_LAYER_LUNARG_object_tracker",
    747             "VK_LAYER_LUNARG_image",
    748             "VK_LAYER_LUNARG_core_validation",
    749             "VK_LAYER_LUNARG_swapchain",
    750             "VK_LAYER_GOOGLE_unique_objects"};
    751 
    752         // Look for validation layers
    753         vk::Bool32 validation_found = VK_FALSE;
    754         if (validate) {
    755             auto result = vk::enumerateInstanceLayerProperties(
    756                 &instance_layer_count, nullptr);
    757             VERIFY(result == vk::Result::eSuccess);
    758 
    759             instance_validation_layers = instance_validation_layers_alt1;
    760             if (instance_layer_count > 0) {
    761                 std::unique_ptr<vk::LayerProperties[]> instance_layers(
    762                     new vk::LayerProperties[instance_layer_count]);
    763                 result = vk::enumerateInstanceLayerProperties(
    764                     &instance_layer_count, instance_layers.get());
    765                 VERIFY(result == vk::Result::eSuccess);
    766 
    767                 validation_found =
    768                     check_layers(ARRAY_SIZE(instance_validation_layers_alt1),
    769                                  instance_validation_layers,
    770                                  instance_layer_count, instance_layers.get());
    771                 if (validation_found) {
    772                     enabled_layer_count =
    773                         ARRAY_SIZE(instance_validation_layers_alt1);
    774                     enabled_layers[0] = "VK_LAYER_LUNARG_standard_validation";
    775                     validation_layer_count = 1;
    776                 } else {
    777                     // use alternative set of validation layers
    778                     instance_validation_layers =
    779                         instance_validation_layers_alt2;
    780                     enabled_layer_count =
    781                         ARRAY_SIZE(instance_validation_layers_alt2);
    782                     validation_found = check_layers(
    783                         ARRAY_SIZE(instance_validation_layers_alt2),
    784                         instance_validation_layers, instance_layer_count,
    785                         instance_layers.get());
    786                     validation_layer_count =
    787                         ARRAY_SIZE(instance_validation_layers_alt2);
    788                     for (uint32_t i = 0; i < validation_layer_count; i++) {
    789                         enabled_layers[i] = instance_validation_layers[i];
    790                     }
    791                 }
    792             }
    793 
    794             if (!validation_found) {
    795                 ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find "
    796                          "required validation layer.\n\n"
    797                          "Please look at the Getting Started guide for "
    798                          "additional information.\n",
    799                          "vkCreateInstance Failure");
    800             }
    801         }
    802 
    803         /* Look for instance extensions */
    804         vk::Bool32 surfaceExtFound = VK_FALSE;
    805         vk::Bool32 platformSurfaceExtFound = VK_FALSE;
    806 #if defined(VK_USE_PLATFORM_XLIB_KHR)
    807         vk::Bool32 xlibSurfaceExtFound = VK_FALSE;
    808 #endif
    809         memset(extension_names, 0, sizeof(extension_names));
    810 
    811         auto result = vk::enumerateInstanceExtensionProperties(
    812             nullptr, &instance_extension_count, nullptr);
    813         VERIFY(result == vk::Result::eSuccess);
    814 
    815         if (instance_extension_count > 0) {
    816             std::unique_ptr<vk::ExtensionProperties[]> instance_extensions(
    817                 new vk::ExtensionProperties[instance_extension_count]);
    818             result = vk::enumerateInstanceExtensionProperties(
    819                 nullptr, &instance_extension_count, instance_extensions.get());
    820             VERIFY(result == vk::Result::eSuccess);
    821 
    822             for (uint32_t i = 0; i < instance_extension_count; i++) {
    823                 if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME,
    824                             instance_extensions[i].extensionName)) {
    825                     surfaceExtFound = 1;
    826                     extension_names[enabled_extension_count++] =
    827                         VK_KHR_SURFACE_EXTENSION_NAME;
    828                 }
    829 #if defined(VK_USE_PLATFORM_WIN32_KHR)
    830                 if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
    831                             instance_extensions[i].extensionName)) {
    832                     platformSurfaceExtFound = 1;
    833                     extension_names[enabled_extension_count++] =
    834                         VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
    835                 }
    836 #endif
    837 #if defined(VK_USE_PLATFORM_XLIB_KHR)
    838                 if (!strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
    839                             instance_extensions[i].extensionName)) {
    840                     platformSurfaceExtFound = 1;
    841                     xlibSurfaceExtFound = 1;
    842                     extension_names[enabled_extension_count++] =
    843                         VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
    844                 }
    845 #endif
    846 #if defined(VK_USE_PLATFORM_XCB_KHR)
    847                 if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME,
    848                             instance_extensions[i].extensionName)) {
    849                     platformSurfaceExtFound = 1;
    850                     extension_names[enabled_extension_count++] =
    851                         VK_KHR_XCB_SURFACE_EXTENSION_NAME;
    852                 }
    853 #endif
    854 #if defined(VK_USE_PLATFORM_WAYLAND_KHR)
    855                 if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
    856                             instance_extensions[i].extensionName)) {
    857                     platformSurfaceExtFound = 1;
    858                     extension_names[enabled_extension_count++] =
    859                         VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
    860                 }
    861 #endif
    862                 assert(enabled_extension_count < 64);
    863             }
    864         }
    865 
    866         if (!surfaceExtFound) {
    867             ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
    868                      "the " VK_KHR_SURFACE_EXTENSION_NAME " extension.\n\n"
    869                      "Do you have a compatible Vulkan installable client "
    870                      "driver (ICD) installed?\n"
    871                      "Please look at the Getting Started guide for additional "
    872                      "information.\n",
    873                      "vkCreateInstance Failure");
    874         }
    875 
    876         if (!platformSurfaceExtFound) {
    877 #if defined(VK_USE_PLATFORM_WIN32_KHR)
    878             ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
    879                      "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME
    880                      " extension.\n\n"
    881                      "Do you have a compatible Vulkan installable client "
    882                      "driver (ICD) installed?\n"
    883                      "Please look at the Getting Started guide for additional "
    884                      "information.\n",
    885                      "vkCreateInstance Failure");
    886 #elif defined(VK_USE_PLATFORM_XCB_KHR)
    887             ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
    888                      "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension.\n\n"
    889                      "Do you have a compatible Vulkan installable client "
    890                      "driver (ICD) installed?\n"
    891                      "Please look at the Getting Started guide for additional "
    892                      "information.\n",
    893                      "vkCreateInstance Failure");
    894 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
    895             ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
    896                      "the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
    897                      " extension.\n\n"
    898                      "Do you have a compatible Vulkan installable client "
    899                      "driver (ICD) installed?\n"
    900                      "Please look at the Getting Started guide for additional "
    901                      "information.\n",
    902                      "vkCreateInstance Failure");
    903 #endif
    904         }
    905 
    906 #if defined(VK_USE_PLATFORM_XLIB_KHR)
    907         if (use_xlib && !xlibSurfaceExtFound) {
    908             ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
    909                      "the " VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension.\n\n"
    910                      "Do you have a compatible Vulkan installable client "
    911                      "driver (ICD) installed?\n"
    912                      "Please look at the Getting Started guide for additional "
    913                      "information.\n",
    914                      "vkCreateInstance Failure");
    915         }
    916 #endif
    917 
    918         auto const app = vk::ApplicationInfo()
    919                              .setPApplicationName(APP_SHORT_NAME)
    920                              .setApplicationVersion(0)
    921                              .setPEngineName(APP_SHORT_NAME)
    922                              .setEngineVersion(0)
    923                              .setApiVersion(VK_API_VERSION_1_0);
    924         auto const inst_info =
    925             vk::InstanceCreateInfo()
    926                 .setPApplicationInfo(&app)
    927                 .setEnabledLayerCount(enabled_layer_count)
    928                 .setPpEnabledLayerNames(instance_validation_layers)
    929                 .setEnabledExtensionCount(enabled_extension_count)
    930                 .setPpEnabledExtensionNames(extension_names);
    931 
    932         result = vk::createInstance(&inst_info, nullptr, &inst);
    933         if (result == vk::Result::eErrorIncompatibleDriver) {
    934             ERR_EXIT("Cannot find a compatible Vulkan installable client "
    935                      "driver (ICD).\n\n"
    936                      "Please look at the Getting Started guide for additional "
    937                      "information.\n",
    938                      "vkCreateInstance Failure");
    939         } else if (result == vk::Result::eErrorExtensionNotPresent) {
    940             ERR_EXIT("Cannot find a specified extension library.\n"
    941                      "Make sure your layers path is set appropriately.\n",
    942                      "vkCreateInstance Failure");
    943         } else if (result != vk::Result::eSuccess) {
    944             ERR_EXIT("vkCreateInstance failed.\n\n"
    945                      "Do you have a compatible Vulkan installable client "
    946                      "driver (ICD) installed?\n"
    947                      "Please look at the Getting Started guide for additional "
    948                      "information.\n",
    949                      "vkCreateInstance Failure");
    950         }
    951 
    952         /* Make initial call to query gpu_count, then second call for gpu info*/
    953         uint32_t gpu_count;
    954         result = inst.enumeratePhysicalDevices(&gpu_count, nullptr);
    955         VERIFY(result == vk::Result::eSuccess);
    956         assert(gpu_count > 0);
    957 
    958         if (gpu_count > 0) {
    959             std::unique_ptr<vk::PhysicalDevice[]> physical_devices(
    960                 new vk::PhysicalDevice[gpu_count]);
    961             result = inst.enumeratePhysicalDevices(&gpu_count,
    962                                                    physical_devices.get());
    963             VERIFY(result == vk::Result::eSuccess);
    964             /* For cube demo we just grab the first physical device */
    965             gpu = physical_devices[0];
    966         } else {
    967             ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible "
    968                      "devices.\n\n"
    969                      "Do you have a compatible Vulkan installable client "
    970                      "driver (ICD) installed?\n"
    971                      "Please look at the Getting Started guide for additional "
    972                      "information.\n",
    973                      "vkEnumeratePhysicalDevices Failure");
    974         }
    975 
    976         /* Look for device extensions */
    977         uint32_t device_extension_count = 0;
    978         vk::Bool32 swapchainExtFound = VK_FALSE;
    979         enabled_extension_count = 0;
    980         memset(extension_names, 0, sizeof(extension_names));
    981 
    982         result = gpu.enumerateDeviceExtensionProperties(
    983             nullptr, &device_extension_count, nullptr);
    984         VERIFY(result == vk::Result::eSuccess);
    985 
    986         if (device_extension_count > 0) {
    987             std::unique_ptr<vk::ExtensionProperties[]> device_extensions(
    988                 new vk::ExtensionProperties[device_extension_count]);
    989             result = gpu.enumerateDeviceExtensionProperties(
    990                 nullptr, &device_extension_count, device_extensions.get());
    991             VERIFY(result == vk::Result::eSuccess);
    992 
    993             for (uint32_t i = 0; i < device_extension_count; i++) {
    994                 if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
    995                             device_extensions[i].extensionName)) {
    996                     swapchainExtFound = 1;
    997                     extension_names[enabled_extension_count++] =
    998                         VK_KHR_SWAPCHAIN_EXTENSION_NAME;
    999                 }
   1000                 assert(enabled_extension_count < 64);
   1001             }
   1002         }
   1003 
   1004         if (!swapchainExtFound) {
   1005             ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find "
   1006                      "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME " extension.\n\n"
   1007                      "Do you have a compatible Vulkan installable client "
   1008                      "driver (ICD) installed?\n"
   1009                      "Please look at the Getting Started guide for additional "
   1010                      "information.\n",
   1011                      "vkCreateInstance Failure");
   1012         }
   1013 
   1014         gpu.getProperties(&gpu_props);
   1015 
   1016         /* Call with nullptr data to get count */
   1017         gpu.getQueueFamilyProperties(&queue_family_count, nullptr);
   1018         assert(queue_family_count >= 1);
   1019 
   1020         queue_props.reset(new vk::QueueFamilyProperties[queue_family_count]);
   1021         gpu.getQueueFamilyProperties(&queue_family_count, queue_props.get());
   1022 
   1023         // Query fine-grained feature support for this device.
   1024         //  If app has specific feature requirements it should check supported
   1025         //  features based on this query
   1026         vk::PhysicalDeviceFeatures physDevFeatures;
   1027         gpu.getFeatures(&physDevFeatures);
   1028     }
   1029 
   1030     void init_vk_swapchain() {
   1031 // Create a WSI surface for the window:
   1032 #if defined(VK_USE_PLATFORM_WIN32_KHR)
   1033         {
   1034             auto const createInfo = vk::Win32SurfaceCreateInfoKHR()
   1035                                         .setHinstance(connection)
   1036                                         .setHwnd(window);
   1037 
   1038             auto result =
   1039                 inst.createWin32SurfaceKHR(&createInfo, nullptr, &surface);
   1040             VERIFY(result == vk::Result::eSuccess);
   1041         }
   1042 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) && !defined(VK_USE_PLATFORM_XCB_KHR)
   1043         {
   1044             auto const createInfo = vk::WaylandSurfaceCreateInfoKHR()
   1045                                         .setDisplay(display)
   1046                                         .setSurface(window);
   1047 
   1048             auto result =
   1049                 inst.createWaylandSurfaceKHR(&createInfo, nullptr, &surface);
   1050             VERIFY(result == vk::Result::eSuccess);
   1051         }
   1052 #endif
   1053         if (use_xlib) {
   1054 #if defined(VK_USE_PLATFORM_XLIB_KHR)
   1055             auto const createInfo =
   1056                 vk::XlibSurfaceCreateInfoKHR().setDpy(display).setWindow(
   1057                     xlib_window);
   1058 
   1059             auto result =
   1060                 inst.createXlibSurfaceKHR(&createInfo, nullptr, &surface);
   1061             VERIFY(result == vk::Result::eSuccess);
   1062 #endif
   1063         } else {
   1064 #if defined(VK_USE_PLATFORM_XCB_KHR)
   1065             auto const createInfo = vk::XcbSurfaceCreateInfoKHR()
   1066                                         .setConnection(connection)
   1067                                         .setWindow(xcb_window);
   1068 
   1069             auto result =
   1070                 inst.createXcbSurfaceKHR(&createInfo, nullptr, &surface);
   1071             VERIFY(result == vk::Result::eSuccess);
   1072 #endif
   1073         }
   1074 
   1075         // Iterate over each queue to learn whether it supports presenting:
   1076         std::unique_ptr<vk::Bool32[]> supportsPresent(
   1077             new vk::Bool32[queue_family_count]);
   1078         for (uint32_t i = 0; i < queue_family_count; i++) {
   1079             gpu.getSurfaceSupportKHR(i, surface, &supportsPresent[i]);
   1080         }
   1081 
   1082         uint32_t graphicsQueueFamilyIndex = UINT32_MAX;
   1083         uint32_t presentQueueFamilyIndex = UINT32_MAX;
   1084         for (uint32_t i = 0; i < queue_family_count; i++) {
   1085             if (queue_props[i].queueFlags & vk::QueueFlagBits::eGraphics) {
   1086                 if (graphicsQueueFamilyIndex == UINT32_MAX) {
   1087                     graphicsQueueFamilyIndex = i;
   1088                 }
   1089 
   1090                 if (supportsPresent[i] == VK_TRUE) {
   1091                     graphicsQueueFamilyIndex = i;
   1092                     presentQueueFamilyIndex = i;
   1093                     break;
   1094                 }
   1095             }
   1096         }
   1097 
   1098         if (presentQueueFamilyIndex == UINT32_MAX) {
   1099             // If didn't find a queue that supports both graphics and present,
   1100             // then
   1101             // find a separate present queue.
   1102             for (uint32_t i = 0; i < queue_family_count; ++i) {
   1103                 if (supportsPresent[i] == VK_TRUE) {
   1104                     presentQueueFamilyIndex = i;
   1105                     break;
   1106                 }
   1107             }
   1108         }
   1109 
   1110         // Generate error if could not find both a graphics and a present queue
   1111         if (graphicsQueueFamilyIndex == UINT32_MAX ||
   1112             presentQueueFamilyIndex == UINT32_MAX) {
   1113             ERR_EXIT("Could not find both graphics and present queues\n",
   1114                      "Swapchain Initialization Failure");
   1115         }
   1116 
   1117         graphics_queue_family_index = graphicsQueueFamilyIndex;
   1118         present_queue_family_index = presentQueueFamilyIndex;
   1119         separate_present_queue =
   1120             (graphics_queue_family_index != present_queue_family_index);
   1121 
   1122         create_device();
   1123 
   1124         device.getQueue(graphics_queue_family_index, 0, &graphics_queue);
   1125         if (!separate_present_queue) {
   1126             present_queue = graphics_queue;
   1127         } else {
   1128             device.getQueue(present_queue_family_index, 0, &present_queue);
   1129         }
   1130 
   1131         // Get the list of VkFormat's that are supported:
   1132         uint32_t formatCount;
   1133         auto result = gpu.getSurfaceFormatsKHR(surface, &formatCount, nullptr);
   1134         VERIFY(result == vk::Result::eSuccess);
   1135 
   1136         std::unique_ptr<vk::SurfaceFormatKHR[]> surfFormats(
   1137             new vk::SurfaceFormatKHR[formatCount]);
   1138         result =
   1139             gpu.getSurfaceFormatsKHR(surface, &formatCount, surfFormats.get());
   1140         VERIFY(result == vk::Result::eSuccess);
   1141 
   1142         // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
   1143         // the surface has no preferred format.  Otherwise, at least one
   1144         // supported format will be returned.
   1145         if (formatCount == 1 &&
   1146             surfFormats[0].format == vk::Format::eUndefined) {
   1147             format = vk::Format::eB8G8R8A8Unorm;
   1148         } else {
   1149             assert(formatCount >= 1);
   1150             format = surfFormats[0].format;
   1151         }
   1152         color_space = surfFormats[0].colorSpace;
   1153 
   1154         quit = false;
   1155         curFrame = 0;
   1156 
   1157         // Create semaphores to synchronize acquiring presentable buffers before
   1158         // rendering and waiting for drawing to be complete before presenting
   1159         auto const semaphoreCreateInfo = vk::SemaphoreCreateInfo();
   1160 
   1161         // Create fences that we can use to throttle if we get too far
   1162         // ahead of the image presents
   1163         vk::FenceCreateInfo const fence_ci;
   1164         for (uint32_t i = 0; i < FRAME_LAG; i++) {
   1165             device.createFence(&fence_ci, nullptr, &fences[i]);
   1166             fencesInited[i] = false;
   1167             result = device.createSemaphore(&semaphoreCreateInfo, nullptr,
   1168                                             &image_acquired_semaphores[i]);
   1169             VERIFY(result == vk::Result::eSuccess);
   1170 
   1171             result = device.createSemaphore(&semaphoreCreateInfo, nullptr,
   1172                                             &draw_complete_semaphores[i]);
   1173             VERIFY(result == vk::Result::eSuccess);
   1174 
   1175             if (separate_present_queue) {
   1176                 result = device.createSemaphore(&semaphoreCreateInfo, nullptr,
   1177                                                 &image_ownership_semaphores[i]);
   1178                 VERIFY(result == vk::Result::eSuccess);
   1179             }
   1180         }
   1181         frame_index = 0;
   1182 
   1183         // Get Memory information and properties
   1184         gpu.getMemoryProperties(&memory_properties);
   1185     }
   1186 
   1187     void prepare() {
   1188         auto const cmd_pool_info =
   1189             vk::CommandPoolCreateInfo().setQueueFamilyIndex(
   1190                 graphics_queue_family_index);
   1191         auto result =
   1192             device.createCommandPool(&cmd_pool_info, nullptr, &cmd_pool);
   1193         VERIFY(result == vk::Result::eSuccess);
   1194 
   1195         auto const cmd = vk::CommandBufferAllocateInfo()
   1196                              .setCommandPool(cmd_pool)
   1197                              .setLevel(vk::CommandBufferLevel::ePrimary)
   1198                              .setCommandBufferCount(1);
   1199 
   1200         prepare_buffers();
   1201         prepare_depth();
   1202         prepare_textures();
   1203         prepare_cube_data_buffer();
   1204 
   1205         prepare_descriptor_layout();
   1206         prepare_render_pass();
   1207         prepare_pipeline();
   1208 
   1209         for (uint32_t i = 0; i < swapchainImageCount; ++i) {
   1210             result = device.allocateCommandBuffers(&cmd, &buffers[i].cmd);
   1211             VERIFY(result == vk::Result::eSuccess);
   1212         }
   1213 
   1214         if (separate_present_queue) {
   1215             auto const cmd_pool_info =
   1216                 vk::CommandPoolCreateInfo().setQueueFamilyIndex(
   1217                     present_queue_family_index);
   1218 
   1219             result = device.createCommandPool(&cmd_pool_info, nullptr,
   1220                                               &present_cmd_pool);
   1221             VERIFY(result == vk::Result::eSuccess);
   1222 
   1223             auto const cmd = vk::CommandBufferAllocateInfo()
   1224                                  .setCommandPool(present_cmd_pool)
   1225                                  .setLevel(vk::CommandBufferLevel::ePrimary)
   1226                                  .setCommandBufferCount(1);
   1227 
   1228             for (uint32_t i = 0; i < swapchainImageCount; i++) {
   1229                 result = device.allocateCommandBuffers(
   1230                     &cmd, &buffers[i].graphics_to_present_cmd);
   1231                 VERIFY(result == vk::Result::eSuccess);
   1232 
   1233                 build_image_ownership_cmd(i);
   1234             }
   1235         }
   1236 
   1237         prepare_descriptor_pool();
   1238         prepare_descriptor_set();
   1239 
   1240         prepare_framebuffers();
   1241 
   1242         for (uint32_t i = 0; i < swapchainImageCount; ++i) {
   1243             current_buffer = i;
   1244             draw_build_cmd(buffers[i].cmd);
   1245         }
   1246 
   1247         /*
   1248          * Prepare functions above may generate pipeline commands
   1249          * that need to be flushed before beginning the render loop.
   1250          */
   1251         flush_init_cmd();
   1252 
   1253         current_buffer = 0;
   1254         prepared = true;
   1255     }
   1256 
   1257     void prepare_buffers() {
   1258         vk::SwapchainKHR oldSwapchain = swapchain;
   1259 
   1260         // Check the surface capabilities and formats
   1261         vk::SurfaceCapabilitiesKHR surfCapabilities;
   1262         auto result = gpu.getSurfaceCapabilitiesKHR(surface, &surfCapabilities);
   1263         VERIFY(result == vk::Result::eSuccess);
   1264 
   1265         uint32_t presentModeCount;
   1266         result =
   1267             gpu.getSurfacePresentModesKHR(surface, &presentModeCount, nullptr);
   1268         VERIFY(result == vk::Result::eSuccess);
   1269 
   1270         std::unique_ptr<vk::PresentModeKHR[]> presentModes(
   1271             new vk::PresentModeKHR[presentModeCount]);
   1272         result = gpu.getSurfacePresentModesKHR(surface, &presentModeCount,
   1273                                                presentModes.get());
   1274         VERIFY(result == vk::Result::eSuccess);
   1275 
   1276         vk::Extent2D swapchainExtent;
   1277         // width and height are either both -1, or both not -1.
   1278         if (surfCapabilities.currentExtent.width == (uint32_t)-1) {
   1279             // If the surface size is undefined, the size is set to
   1280             // the size of the images requested.
   1281             swapchainExtent.width = width;
   1282             swapchainExtent.height = height;
   1283         } else {
   1284             // If the surface size is defined, the swap chain size must match
   1285             swapchainExtent = surfCapabilities.currentExtent;
   1286             width = surfCapabilities.currentExtent.width;
   1287             height = surfCapabilities.currentExtent.height;
   1288         }
   1289 
   1290         // The FIFO present mode is guaranteed by the spec to be supported
   1291         // and to have no tearing.  It's a great default present mode to use.
   1292         vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
   1293 
   1294 //  There are times when you may wish to use another present mode.  The
   1295 //  following code shows how to select them, and the comments provide some
   1296 //  reasons you may wish to use them.
   1297 //
   1298 // It should be noted that Vulkan 1.0 doesn't provide a method for
   1299 // synchronizing rendering with the presentation engine's display.  There
   1300 // is a method provided for throttling rendering with the display, but
   1301 // there are some presentation engines for which this method will not work.
   1302 // If an application doesn't throttle its rendering, and if it renders much
   1303 // faster than the refresh rate of the display, this can waste power on
   1304 // mobile devices.  That is because power is being spent rendering images
   1305 // that may never be seen.
   1306 //#define DESIRE_VK_PRESENT_MODE_IMMEDIATE_KHR
   1307 //#define DESIRE_VK_PRESENT_MODE_MAILBOX_KHR
   1308 //#define DESIRE_VK_PRESENT_MODE_FIFO_RELAXED_KHR
   1309 #if defined(DESIRE_VK_PRESENT_MODE_IMMEDIATE_KHR)
   1310         // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care
   1311         // about
   1312         // tearing, or have some way of synchronizing their rendering with the
   1313         // display.
   1314         for (size_t i = 0; i < presentModeCount; ++i) {
   1315             if (presentModes[i] == vk::PresentModeKHR::eImmediate) {
   1316                 swapchainPresentMode = vk::PresentModeKHR::eImmediate;
   1317                 break;
   1318             }
   1319         }
   1320 #elif defined(DESIRE_VK_PRESENT_MODE_MAILBOX_KHR)
   1321         // VK_PRESENT_MODE_MAILBOX_KHR may be useful for applications that
   1322         // generally render a new presentable image every refresh cycle, but are
   1323         // occasionally early.  In this case, the application wants the new
   1324         // image
   1325         // to be displayed instead of the previously-queued-for-presentation
   1326         // image
   1327         // that has not yet been displayed.
   1328         for (size_t i = 0; i < presentModeCount; ++i) {
   1329             if (presentModes[i] == vk::PresentModeKHR::eMailbox) {
   1330                 swapchainPresentMode = vk::PresentModeKHR::eMailbox;
   1331                 break;
   1332             }
   1333         }
   1334 #elif defined(DESIRE_VK_PRESENT_MODE_FIFO_RELAXED_KHR)
   1335         // VK_PRESENT_MODE_FIFO_RELAXED_KHR is for applications that generally
   1336         // render a new presentable image every refresh cycle, but are
   1337         // occasionally
   1338         // late.  In this case (perhaps because of stuttering/latency concerns),
   1339         // the application wants the late image to be immediately displayed,
   1340         // even
   1341         // though that may mean some tearing.
   1342         for (size_t i = 0; i < presentModeCount; ++i) {
   1343             if (presentModes[i] == vk::PresentModeKHR::eFifoRelaxed) {
   1344                 swapchainPresentMode = vk::PresentModeKHR::eFifoRelaxed;
   1345                 break;
   1346             }
   1347         }
   1348 #endif
   1349 
   1350         // Determine the number of VkImage's to use in the swap chain (we desire
   1351         // to
   1352         // own only 1 image at a time, besides the images being displayed and
   1353         // queued for display):
   1354         uint32_t desiredNumberOfSwapchainImages =
   1355             surfCapabilities.minImageCount + 1;
   1356         // If maxImageCount is 0, we can ask for as many images as we want,
   1357         // otherwise
   1358         // we're limited to maxImageCount
   1359         if ((surfCapabilities.maxImageCount > 0) &&
   1360             (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) {
   1361             // Application must settle for fewer images than desired:
   1362             desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount;
   1363         }
   1364 
   1365         vk::SurfaceTransformFlagBitsKHR preTransform;
   1366         if (surfCapabilities.supportedTransforms &
   1367             vk::SurfaceTransformFlagBitsKHR::eIdentity) {
   1368             preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
   1369         } else {
   1370             preTransform = surfCapabilities.currentTransform;
   1371         }
   1372 
   1373         auto const swapchain_ci =
   1374             vk::SwapchainCreateInfoKHR()
   1375                 .setSurface(surface)
   1376                 .setMinImageCount(desiredNumberOfSwapchainImages)
   1377                 .setImageFormat(format)
   1378                 .setImageColorSpace(color_space)
   1379                 .setImageExtent({swapchainExtent.width, swapchainExtent.height})
   1380                 .setImageArrayLayers(1)
   1381                 .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
   1382                 .setImageSharingMode(vk::SharingMode::eExclusive)
   1383                 .setQueueFamilyIndexCount(0)
   1384                 .setPQueueFamilyIndices(nullptr)
   1385                 .setPreTransform(preTransform)
   1386                 .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
   1387                 .setPresentMode(swapchainPresentMode)
   1388                 .setClipped(true)
   1389                 .setOldSwapchain(oldSwapchain);
   1390 
   1391         result = device.createSwapchainKHR(&swapchain_ci, nullptr, &swapchain);
   1392         VERIFY(result == vk::Result::eSuccess);
   1393 
   1394         // If we just re-created an existing swapchain, we should destroy the
   1395         // old
   1396         // swapchain at this point.
   1397         // Note: destroying the swapchain also cleans up all its associated
   1398         // presentable images once the platform is done with them.
   1399         if (oldSwapchain) {
   1400             device.destroySwapchainKHR(oldSwapchain, nullptr);
   1401         }
   1402 
   1403         result = device.getSwapchainImagesKHR(swapchain, &swapchainImageCount,
   1404                                               nullptr);
   1405         VERIFY(result == vk::Result::eSuccess);
   1406 
   1407         std::unique_ptr<vk::Image[]> swapchainImages(
   1408             new vk::Image[swapchainImageCount]);
   1409         result = device.getSwapchainImagesKHR(swapchain, &swapchainImageCount,
   1410                                               swapchainImages.get());
   1411         VERIFY(result == vk::Result::eSuccess);
   1412 
   1413         buffers.reset(new SwapchainBuffers[swapchainImageCount]);
   1414 
   1415         for (uint32_t i = 0; i < swapchainImageCount; ++i) {
   1416             auto const color_image_view =
   1417                 vk::ImageViewCreateInfo()
   1418                     .setImage(swapchainImages[i])
   1419                     .setViewType(vk::ImageViewType::e2D)
   1420                     .setFormat(format)
   1421                     .setSubresourceRange(vk::ImageSubresourceRange(
   1422                         vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
   1423 
   1424             buffers[i].image = swapchainImages[i];
   1425 
   1426             result = device.createImageView(&color_image_view, nullptr,
   1427                                             &buffers[i].view);
   1428             VERIFY(result == vk::Result::eSuccess);
   1429 
   1430             // The draw loop will be expecting the presentable images to be in
   1431             // LAYOUT_PRESENT_SRC_KHR since that's how they're left at the end
   1432             // of every frame.
   1433             set_image_layout(buffers[i].image, vk::ImageAspectFlagBits::eColor,
   1434                              vk::ImageLayout::eUndefined,
   1435                              vk::ImageLayout::ePresentSrcKHR,
   1436                              vk::AccessFlagBits());
   1437         }
   1438     }
   1439 
   1440     void prepare_cube_data_buffer() {
   1441         mat4x4 VP;
   1442         mat4x4_mul(VP, projection_matrix, view_matrix);
   1443 
   1444         mat4x4 MVP;
   1445         mat4x4_mul(MVP, VP, model_matrix);
   1446 
   1447         vktexcube_vs_uniform data;
   1448         memcpy(data.mvp, MVP, sizeof(MVP));
   1449         //    dumpMatrix("MVP", MVP)
   1450         for (int32_t i = 0; i < 12 * 3; i++) {
   1451             data.position[i][0] = g_vertex_buffer_data[i * 3];
   1452             data.position[i][1] = g_vertex_buffer_data[i * 3 + 1];
   1453             data.position[i][2] = g_vertex_buffer_data[i * 3 + 2];
   1454             data.position[i][3] = 1.0f;
   1455             data.attr[i][0] = g_uv_buffer_data[2 * i];
   1456             data.attr[i][1] = g_uv_buffer_data[2 * i + 1];
   1457             data.attr[i][2] = 0;
   1458             data.attr[i][3] = 0;
   1459         }
   1460 
   1461         auto const buf_info =
   1462             vk::BufferCreateInfo()
   1463                 .setSize(sizeof(data))
   1464                 .setUsage(vk::BufferUsageFlagBits::eUniformBuffer);
   1465         auto result =
   1466             device.createBuffer(&buf_info, nullptr, &uniform_data.buf);
   1467         VERIFY(result == vk::Result::eSuccess);
   1468 
   1469         vk::MemoryRequirements mem_reqs;
   1470         device.getBufferMemoryRequirements(uniform_data.buf, &mem_reqs);
   1471 
   1472         uniform_data.mem_alloc.setAllocationSize(mem_reqs.size);
   1473         uniform_data.mem_alloc.setMemoryTypeIndex(0);
   1474 
   1475         bool const pass = memory_type_from_properties(
   1476             mem_reqs.memoryTypeBits,
   1477             vk::MemoryPropertyFlagBits::eHostVisible |
   1478                 vk::MemoryPropertyFlagBits::eHostCoherent,
   1479             &uniform_data.mem_alloc.memoryTypeIndex);
   1480         VERIFY(pass);
   1481 
   1482         result = device.allocateMemory(&uniform_data.mem_alloc, nullptr,
   1483                                        &(uniform_data.mem));
   1484         VERIFY(result == vk::Result::eSuccess);
   1485 
   1486         auto pData = device.mapMemory(uniform_data.mem, 0,
   1487                                       uniform_data.mem_alloc.allocationSize,
   1488                                       vk::MemoryMapFlags());
   1489         VERIFY(pData.result == vk::Result::eSuccess);
   1490 
   1491         memcpy(pData.value, &data, sizeof data);
   1492 
   1493         device.unmapMemory(uniform_data.mem);
   1494 
   1495         result = device.bindBufferMemory(uniform_data.buf, uniform_data.mem, 0);
   1496         VERIFY(result == vk::Result::eSuccess);
   1497 
   1498         uniform_data.buffer_info.buffer = uniform_data.buf;
   1499         uniform_data.buffer_info.offset = 0;
   1500         uniform_data.buffer_info.range = sizeof(data);
   1501     }
   1502 
   1503     void prepare_depth() {
   1504         depth.format = vk::Format::eD16Unorm;
   1505 
   1506         auto const image =
   1507             vk::ImageCreateInfo()
   1508                 .setImageType(vk::ImageType::e2D)
   1509                 .setFormat(depth.format)
   1510                 .setExtent({(uint32_t)width, (uint32_t)height, 1})
   1511                 .setMipLevels(1)
   1512                 .setArrayLayers(1)
   1513                 .setSamples(vk::SampleCountFlagBits::e1)
   1514                 .setTiling(vk::ImageTiling::eOptimal)
   1515                 .setUsage(vk::ImageUsageFlagBits::eDepthStencilAttachment)
   1516                 .setSharingMode(vk::SharingMode::eExclusive)
   1517                 .setQueueFamilyIndexCount(0)
   1518                 .setPQueueFamilyIndices(nullptr)
   1519                 .setInitialLayout(vk::ImageLayout::eUndefined);
   1520 
   1521         auto result = device.createImage(&image, nullptr, &depth.image);
   1522         VERIFY(result == vk::Result::eSuccess);
   1523 
   1524         vk::MemoryRequirements mem_reqs;
   1525         device.getImageMemoryRequirements(depth.image, &mem_reqs);
   1526 
   1527         depth.mem_alloc.setAllocationSize(mem_reqs.size);
   1528         depth.mem_alloc.setMemoryTypeIndex(0);
   1529 
   1530         auto const pass = memory_type_from_properties(
   1531             mem_reqs.memoryTypeBits, vk::MemoryPropertyFlagBits(0),
   1532             &depth.mem_alloc.memoryTypeIndex);
   1533         VERIFY(pass);
   1534 
   1535         result = device.allocateMemory(&depth.mem_alloc, nullptr, &depth.mem);
   1536         VERIFY(result == vk::Result::eSuccess);
   1537 
   1538         result = device.bindImageMemory(depth.image, depth.mem, 0);
   1539         VERIFY(result == vk::Result::eSuccess);
   1540 
   1541         set_image_layout(depth.image, vk::ImageAspectFlagBits::eDepth,
   1542                          vk::ImageLayout::eUndefined,
   1543                          vk::ImageLayout::eDepthStencilAttachmentOptimal,
   1544                          vk::AccessFlagBits());
   1545 
   1546         auto const view = vk::ImageViewCreateInfo()
   1547                               .setImage(depth.image)
   1548                               .setViewType(vk::ImageViewType::e2D)
   1549                               .setFormat(depth.format)
   1550                               .setSubresourceRange(vk::ImageSubresourceRange(
   1551                                   vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1));
   1552         result = device.createImageView(&view, nullptr, &depth.view);
   1553         VERIFY(result == vk::Result::eSuccess);
   1554     }
   1555 
   1556     void prepare_descriptor_layout() {
   1557         vk::DescriptorSetLayoutBinding const layout_bindings[2] = {
   1558             vk::DescriptorSetLayoutBinding()
   1559                 .setBinding(0)
   1560                 .setDescriptorType(vk::DescriptorType::eUniformBuffer)
   1561                 .setDescriptorCount(1)
   1562                 .setStageFlags(vk::ShaderStageFlagBits::eVertex)
   1563                 .setPImmutableSamplers(nullptr),
   1564             vk::DescriptorSetLayoutBinding()
   1565                 .setBinding(1)
   1566                 .setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
   1567                 .setDescriptorCount(texture_count)
   1568                 .setStageFlags(vk::ShaderStageFlagBits::eFragment)
   1569                 .setPImmutableSamplers(nullptr)};
   1570 
   1571         auto const descriptor_layout =
   1572             vk::DescriptorSetLayoutCreateInfo().setBindingCount(2).setPBindings(
   1573                 layout_bindings);
   1574 
   1575         auto result = device.createDescriptorSetLayout(&descriptor_layout,
   1576                                                        nullptr, &desc_layout);
   1577         VERIFY(result == vk::Result::eSuccess);
   1578 
   1579         auto const pPipelineLayoutCreateInfo =
   1580             vk::PipelineLayoutCreateInfo().setSetLayoutCount(1).setPSetLayouts(
   1581                 &desc_layout);
   1582 
   1583         result = device.createPipelineLayout(&pPipelineLayoutCreateInfo,
   1584                                              nullptr, &pipeline_layout);
   1585         VERIFY(result == vk::Result::eSuccess);
   1586     }
   1587 
   1588     void prepare_descriptor_pool() {
   1589         vk::DescriptorPoolSize const poolSizes[2] = {
   1590             vk::DescriptorPoolSize()
   1591                 .setType(vk::DescriptorType::eUniformBuffer)
   1592                 .setDescriptorCount(1),
   1593             vk::DescriptorPoolSize()
   1594                 .setType(vk::DescriptorType::eCombinedImageSampler)
   1595                 .setDescriptorCount(texture_count)};
   1596 
   1597         auto const descriptor_pool = vk::DescriptorPoolCreateInfo()
   1598                                          .setMaxSets(1)
   1599                                          .setPoolSizeCount(2)
   1600                                          .setPPoolSizes(poolSizes);
   1601 
   1602         auto result =
   1603             device.createDescriptorPool(&descriptor_pool, nullptr, &desc_pool);
   1604         VERIFY(result == vk::Result::eSuccess);
   1605     }
   1606 
   1607     void prepare_descriptor_set() {
   1608         auto const alloc_info = vk::DescriptorSetAllocateInfo()
   1609                                     .setDescriptorPool(desc_pool)
   1610                                     .setDescriptorSetCount(1)
   1611                                     .setPSetLayouts(&desc_layout);
   1612         auto result = device.allocateDescriptorSets(&alloc_info, &desc_set);
   1613         VERIFY(result == vk::Result::eSuccess);
   1614 
   1615         vk::DescriptorImageInfo tex_descs[texture_count];
   1616         for (uint32_t i = 0; i < texture_count; i++) {
   1617             tex_descs[i].setSampler(textures[i].sampler);
   1618             tex_descs[i].setImageView(textures[i].view);
   1619             tex_descs[i].setImageLayout(vk::ImageLayout::eGeneral);
   1620         }
   1621 
   1622         vk::WriteDescriptorSet writes[2];
   1623 
   1624         writes[0].setDstSet(desc_set);
   1625         writes[0].setDescriptorCount(1);
   1626         writes[0].setDescriptorType(vk::DescriptorType::eUniformBuffer);
   1627         writes[0].setPBufferInfo(&uniform_data.buffer_info);
   1628 
   1629         writes[1].setDstSet(desc_set);
   1630         writes[1].setDstBinding(1);
   1631         writes[1].setDescriptorCount(texture_count);
   1632         writes[1].setDescriptorType(vk::DescriptorType::eCombinedImageSampler);
   1633         writes[1].setPImageInfo(tex_descs);
   1634 
   1635         device.updateDescriptorSets(2, writes, 0, nullptr);
   1636     }
   1637 
   1638     void prepare_framebuffers() {
   1639         vk::ImageView attachments[2];
   1640         attachments[1] = depth.view;
   1641 
   1642         auto const fb_info = vk::FramebufferCreateInfo()
   1643                                  .setRenderPass(render_pass)
   1644                                  .setAttachmentCount(2)
   1645                                  .setPAttachments(attachments)
   1646                                  .setWidth((uint32_t)width)
   1647                                  .setHeight((uint32_t)height)
   1648                                  .setLayers(1);
   1649 
   1650         framebuffers.reset(new vk::Framebuffer[swapchainImageCount]);
   1651 
   1652         for (uint32_t i = 0; i < swapchainImageCount; i++) {
   1653             attachments[0] = buffers[i].view;
   1654             auto const result =
   1655                 device.createFramebuffer(&fb_info, nullptr, &framebuffers[i]);
   1656             VERIFY(result == vk::Result::eSuccess);
   1657         }
   1658     }
   1659 
   1660     vk::ShaderModule prepare_fs() {
   1661         size_t size = 0;
   1662         void *fragShaderCode = read_spv("cube-frag.spv", &size);
   1663 
   1664         frag_shader_module = prepare_shader_module(fragShaderCode, size);
   1665 
   1666         free(fragShaderCode);
   1667 
   1668         return frag_shader_module;
   1669     }
   1670 
   1671     void prepare_pipeline() {
   1672         vk::PipelineCacheCreateInfo const pipelineCacheInfo;
   1673         auto result = device.createPipelineCache(&pipelineCacheInfo, nullptr,
   1674                                                  &pipelineCache);
   1675         VERIFY(result == vk::Result::eSuccess);
   1676 
   1677         vk::PipelineShaderStageCreateInfo const shaderStageInfo[2] = {
   1678             vk::PipelineShaderStageCreateInfo()
   1679                 .setStage(vk::ShaderStageFlagBits::eVertex)
   1680                 .setModule(prepare_vs())
   1681                 .setPName("main"),
   1682             vk::PipelineShaderStageCreateInfo()
   1683                 .setStage(vk::ShaderStageFlagBits::eFragment)
   1684                 .setModule(prepare_fs())
   1685                 .setPName("main")};
   1686 
   1687         vk::PipelineVertexInputStateCreateInfo const vertexInputInfo;
   1688 
   1689         auto const inputAssemblyInfo =
   1690             vk::PipelineInputAssemblyStateCreateInfo().setTopology(
   1691                 vk::PrimitiveTopology::eTriangleList);
   1692 
   1693         // TODO: Where are pViewports and pScissors set?
   1694         auto const viewportInfo = vk::PipelineViewportStateCreateInfo()
   1695                                       .setViewportCount(1)
   1696                                       .setScissorCount(1);
   1697 
   1698         auto const rasterizationInfo =
   1699             vk::PipelineRasterizationStateCreateInfo()
   1700                 .setDepthClampEnable(VK_FALSE)
   1701                 .setRasterizerDiscardEnable(VK_FALSE)
   1702                 .setPolygonMode(vk::PolygonMode::eFill)
   1703                 .setCullMode(vk::CullModeFlagBits::eBack)
   1704                 .setFrontFace(vk::FrontFace::eCounterClockwise)
   1705                 .setDepthBiasEnable(VK_FALSE)
   1706                 .setLineWidth(1.0f);
   1707 
   1708         auto const multisampleInfo = vk::PipelineMultisampleStateCreateInfo();
   1709 
   1710         auto const stencilOp = vk::StencilOpState()
   1711                                    .setFailOp(vk::StencilOp::eKeep)
   1712                                    .setPassOp(vk::StencilOp::eKeep)
   1713                                    .setCompareOp(vk::CompareOp::eAlways);
   1714 
   1715         auto const depthStencilInfo =
   1716             vk::PipelineDepthStencilStateCreateInfo()
   1717                 .setDepthTestEnable(VK_TRUE)
   1718                 .setDepthWriteEnable(VK_TRUE)
   1719                 .setDepthCompareOp(vk::CompareOp::eLessOrEqual)
   1720                 .setDepthBoundsTestEnable(VK_FALSE)
   1721                 .setStencilTestEnable(VK_FALSE)
   1722                 .setFront(stencilOp)
   1723                 .setBack(stencilOp);
   1724 
   1725         vk::PipelineColorBlendAttachmentState const colorBlendAttachments[1] = {
   1726             vk::PipelineColorBlendAttachmentState().setColorWriteMask(
   1727                 vk::ColorComponentFlagBits::eR |
   1728                 vk::ColorComponentFlagBits::eG |
   1729                 vk::ColorComponentFlagBits::eB |
   1730                 vk::ColorComponentFlagBits::eA)};
   1731 
   1732         auto const colorBlendInfo = vk::PipelineColorBlendStateCreateInfo()
   1733                                         .setAttachmentCount(1)
   1734                                         .setPAttachments(colorBlendAttachments);
   1735 
   1736         vk::DynamicState const dynamicStates[2] = {vk::DynamicState::eViewport,
   1737                                                    vk::DynamicState::eScissor};
   1738 
   1739         auto const dynamicStateInfo = vk::PipelineDynamicStateCreateInfo()
   1740                                           .setPDynamicStates(dynamicStates)
   1741                                           .setDynamicStateCount(2);
   1742 
   1743         auto const pipeline = vk::GraphicsPipelineCreateInfo()
   1744                                   .setStageCount(2)
   1745                                   .setPStages(shaderStageInfo)
   1746                                   .setPVertexInputState(&vertexInputInfo)
   1747                                   .setPInputAssemblyState(&inputAssemblyInfo)
   1748                                   .setPViewportState(&viewportInfo)
   1749                                   .setPRasterizationState(&rasterizationInfo)
   1750                                   .setPMultisampleState(&multisampleInfo)
   1751                                   .setPDepthStencilState(&depthStencilInfo)
   1752                                   .setPColorBlendState(&colorBlendInfo)
   1753                                   .setPDynamicState(&dynamicStateInfo)
   1754                                   .setLayout(pipeline_layout)
   1755                                   .setRenderPass(render_pass);
   1756 
   1757         result = device.createGraphicsPipelines(pipelineCache, 1, &pipeline,
   1758                                                 nullptr, &this->pipeline);
   1759         VERIFY(result == vk::Result::eSuccess);
   1760 
   1761         device.destroyShaderModule(frag_shader_module, nullptr);
   1762         device.destroyShaderModule(vert_shader_module, nullptr);
   1763     }
   1764 
   1765     void prepare_render_pass() {
   1766         const vk::AttachmentDescription attachments[2] = {
   1767             vk::AttachmentDescription()
   1768                 .setFlags(vk::AttachmentDescriptionFlagBits::eMayAlias)
   1769                 .setFormat(format)
   1770                 .setSamples(vk::SampleCountFlagBits::e1)
   1771                 .setLoadOp(vk::AttachmentLoadOp::eClear)
   1772                 .setStoreOp(vk::AttachmentStoreOp::eStore)
   1773                 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
   1774                 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
   1775                 .setInitialLayout(vk::ImageLayout::eColorAttachmentOptimal)
   1776                 .setFinalLayout(vk::ImageLayout::ePresentSrcKHR),
   1777             vk::AttachmentDescription()
   1778                 .setFlags(vk::AttachmentDescriptionFlagBits::eMayAlias)
   1779                 .setFormat(depth.format)
   1780                 .setSamples(vk::SampleCountFlagBits::e1)
   1781                 .setLoadOp(vk::AttachmentLoadOp::eClear)
   1782                 .setStoreOp(vk::AttachmentStoreOp::eDontCare)
   1783                 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
   1784                 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
   1785                 .setInitialLayout(
   1786                     vk::ImageLayout::eDepthStencilAttachmentOptimal)
   1787                 .setFinalLayout(
   1788                     vk::ImageLayout::eDepthStencilAttachmentOptimal)};
   1789 
   1790         auto const color_reference =
   1791             vk::AttachmentReference().setAttachment(0).setLayout(
   1792                 vk::ImageLayout::eColorAttachmentOptimal);
   1793 
   1794         auto const depth_reference =
   1795             vk::AttachmentReference().setAttachment(1).setLayout(
   1796                 vk::ImageLayout::eDepthStencilAttachmentOptimal);
   1797 
   1798         auto const subpass =
   1799             vk::SubpassDescription()
   1800                 .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
   1801                 .setInputAttachmentCount(0)
   1802                 .setPInputAttachments(nullptr)
   1803                 .setColorAttachmentCount(1)
   1804                 .setPColorAttachments(&color_reference)
   1805                 .setPResolveAttachments(nullptr)
   1806                 .setPDepthStencilAttachment(&depth_reference)
   1807                 .setPreserveAttachmentCount(0)
   1808                 .setPPreserveAttachments(nullptr);
   1809 
   1810         auto const rp_info = vk::RenderPassCreateInfo()
   1811                                  .setAttachmentCount(2)
   1812                                  .setPAttachments(attachments)
   1813                                  .setSubpassCount(1)
   1814                                  .setPSubpasses(&subpass)
   1815                                  .setDependencyCount(0)
   1816                                  .setPDependencies(nullptr);
   1817 
   1818         auto result = device.createRenderPass(&rp_info, nullptr, &render_pass);
   1819         VERIFY(result == vk::Result::eSuccess);
   1820     }
   1821 
   1822     vk::ShaderModule prepare_shader_module(const void *code, size_t size) {
   1823         auto const moduleCreateInfo =
   1824             vk::ShaderModuleCreateInfo().setCodeSize(size).setPCode(
   1825                 (uint32_t const *)code);
   1826 
   1827         vk::ShaderModule module;
   1828         auto result =
   1829             device.createShaderModule(&moduleCreateInfo, nullptr, &module);
   1830         VERIFY(result == vk::Result::eSuccess);
   1831 
   1832         return module;
   1833     }
   1834 
   1835     void prepare_texture_image(const char *filename, texture_object *tex_obj,
   1836                                vk::ImageTiling tiling,
   1837                                vk::ImageUsageFlags usage,
   1838                                vk::MemoryPropertyFlags required_props) {
   1839         int32_t tex_width;
   1840         int32_t tex_height;
   1841         if (!loadTexture(filename, nullptr, nullptr, &tex_width, &tex_height)) {
   1842             ERR_EXIT("Failed to load textures", "Load Texture Failure");
   1843         }
   1844 
   1845         tex_obj->tex_width = tex_width;
   1846         tex_obj->tex_height = tex_height;
   1847 
   1848         auto const image_create_info =
   1849             vk::ImageCreateInfo()
   1850                 .setImageType(vk::ImageType::e2D)
   1851                 .setFormat(vk::Format::eR8G8B8A8Unorm)
   1852                 .setExtent({(uint32_t)tex_width, (uint32_t)tex_height, 1})
   1853                 .setMipLevels(1)
   1854                 .setArrayLayers(1)
   1855                 .setSamples(vk::SampleCountFlagBits::e1)
   1856                 .setTiling(tiling)
   1857                 .setUsage(usage)
   1858                 .setSharingMode(vk::SharingMode::eExclusive)
   1859                 .setQueueFamilyIndexCount(0)
   1860                 .setPQueueFamilyIndices(nullptr)
   1861                 .setInitialLayout(vk::ImageLayout::ePreinitialized);
   1862 
   1863         auto result =
   1864             device.createImage(&image_create_info, nullptr, &tex_obj->image);
   1865         VERIFY(result == vk::Result::eSuccess);
   1866 
   1867         vk::MemoryRequirements mem_reqs;
   1868         device.getImageMemoryRequirements(tex_obj->image, &mem_reqs);
   1869 
   1870         tex_obj->mem_alloc.setAllocationSize(mem_reqs.size);
   1871         tex_obj->mem_alloc.setMemoryTypeIndex(0);
   1872 
   1873         auto pass =
   1874             memory_type_from_properties(mem_reqs.memoryTypeBits, required_props,
   1875                                         &tex_obj->mem_alloc.memoryTypeIndex);
   1876         VERIFY(pass == true);
   1877 
   1878         result = device.allocateMemory(&tex_obj->mem_alloc, nullptr,
   1879                                        &(tex_obj->mem));
   1880         VERIFY(result == vk::Result::eSuccess);
   1881 
   1882         result = device.bindImageMemory(tex_obj->image, tex_obj->mem, 0);
   1883         VERIFY(result == vk::Result::eSuccess);
   1884 
   1885         if (required_props & vk::MemoryPropertyFlagBits::eHostVisible) {
   1886             auto const subres =
   1887                 vk::ImageSubresource()
   1888                     .setAspectMask(vk::ImageAspectFlagBits::eColor)
   1889                     .setMipLevel(0)
   1890                     .setArrayLayer(0);
   1891             vk::SubresourceLayout layout;
   1892             device.getImageSubresourceLayout(tex_obj->image, &subres, &layout);
   1893 
   1894             auto data = device.mapMemory(tex_obj->mem, 0,
   1895                                          tex_obj->mem_alloc.allocationSize);
   1896             VERIFY(data.result == vk::Result::eSuccess);
   1897 
   1898             if (!loadTexture(filename, (uint8_t *)data.value, &layout,
   1899                              &tex_width, &tex_height)) {
   1900                 fprintf(stderr, "Error loading texture: %s\n", filename);
   1901             }
   1902 
   1903             device.unmapMemory(tex_obj->mem);
   1904         }
   1905 
   1906         tex_obj->imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
   1907         set_image_layout(tex_obj->image, vk::ImageAspectFlagBits::eColor,
   1908                          vk::ImageLayout::ePreinitialized, tex_obj->imageLayout,
   1909                          vk::AccessFlagBits::eHostWrite);
   1910     }
   1911 
   1912     void prepare_textures() {
   1913         vk::Format const tex_format = vk::Format::eR8G8B8A8Unorm;
   1914         vk::FormatProperties props;
   1915         gpu.getFormatProperties(tex_format, &props);
   1916 
   1917         for (uint32_t i = 0; i < texture_count; i++) {
   1918             if ((props.linearTilingFeatures &
   1919                  vk::FormatFeatureFlagBits::eSampledImage) &&
   1920                 !use_staging_buffer) {
   1921                 /* Device can texture using linear textures */
   1922                 prepare_texture_image(
   1923                     tex_files[i], &textures[i], vk::ImageTiling::eLinear,
   1924                     vk::ImageUsageFlagBits::eSampled,
   1925                     vk::MemoryPropertyFlagBits::eHostVisible |
   1926                         vk::MemoryPropertyFlagBits::eHostCoherent);
   1927             } else if (props.optimalTilingFeatures &
   1928                        vk::FormatFeatureFlagBits::eSampledImage) {
   1929                 /* Must use staging buffer to copy linear texture to optimized
   1930                  */
   1931                 texture_object staging_texture;
   1932 
   1933                 prepare_texture_image(
   1934                     tex_files[i], &staging_texture, vk::ImageTiling::eLinear,
   1935                     vk::ImageUsageFlagBits::eTransferSrc,
   1936                     vk::MemoryPropertyFlagBits::eHostVisible |
   1937                         vk::MemoryPropertyFlagBits::eHostCoherent);
   1938 
   1939                 prepare_texture_image(tex_files[i], &textures[i],
   1940                                       vk::ImageTiling::eOptimal,
   1941                                       vk::ImageUsageFlagBits::eTransferDst |
   1942                                           vk::ImageUsageFlagBits::eSampled,
   1943                                       vk::MemoryPropertyFlagBits::eDeviceLocal);
   1944 
   1945                 set_image_layout(
   1946                     staging_texture.image, vk::ImageAspectFlagBits::eColor,
   1947                     staging_texture.imageLayout,
   1948                     vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlags());
   1949 
   1950                 set_image_layout(
   1951                     textures[i].image, vk::ImageAspectFlagBits::eColor,
   1952                     textures[i].imageLayout,
   1953                     vk::ImageLayout::eTransferDstOptimal, vk::AccessFlags());
   1954 
   1955                 auto const subresource =
   1956                     vk::ImageSubresourceLayers()
   1957                         .setAspectMask(vk::ImageAspectFlagBits::eColor)
   1958                         .setMipLevel(0)
   1959                         .setBaseArrayLayer(0)
   1960                         .setLayerCount(1);
   1961 
   1962                 auto const copy_region =
   1963                     vk::ImageCopy()
   1964                         .setSrcSubresource(subresource)
   1965                         .setSrcOffset({0, 0, 0})
   1966                         .setDstSubresource(subresource)
   1967                         .setDstOffset({0, 0, 0})
   1968                         .setExtent({(uint32_t)staging_texture.tex_width,
   1969                                     (uint32_t)staging_texture.tex_height, 1});
   1970 
   1971                 cmd.copyImage(
   1972                     staging_texture.image, vk::ImageLayout::eTransferSrcOptimal,
   1973                     textures[i].image, vk::ImageLayout::eTransferDstOptimal, 1,
   1974                     &copy_region);
   1975 
   1976                 set_image_layout(textures[i].image,
   1977                                  vk::ImageAspectFlagBits::eColor,
   1978                                  vk::ImageLayout::eTransferDstOptimal,
   1979                                  textures[i].imageLayout, vk::AccessFlags());
   1980 
   1981                 flush_init_cmd();
   1982 
   1983                 destroy_texture_image(&staging_texture);
   1984             } else {
   1985                 assert(
   1986                     !"No support for R8G8B8A8_UNORM as texture image format");
   1987             }
   1988 
   1989             auto const samplerInfo =
   1990                 vk::SamplerCreateInfo()
   1991                     .setMagFilter(vk::Filter::eNearest)
   1992                     .setMinFilter(vk::Filter::eNearest)
   1993                     .setMipmapMode(vk::SamplerMipmapMode::eNearest)
   1994                     .setAddressModeU(vk::SamplerAddressMode::eClampToEdge)
   1995                     .setAddressModeV(vk::SamplerAddressMode::eClampToEdge)
   1996                     .setAddressModeW(vk::SamplerAddressMode::eClampToEdge)
   1997                     .setMipLodBias(0.0f)
   1998                     .setAnisotropyEnable(VK_FALSE)
   1999                     .setMaxAnisotropy(1)
   2000                     .setCompareEnable(VK_FALSE)
   2001                     .setCompareOp(vk::CompareOp::eNever)
   2002                     .setMinLod(0.0f)
   2003                     .setMaxLod(0.0f)
   2004                     .setBorderColor(vk::BorderColor::eFloatOpaqueWhite)
   2005                     .setUnnormalizedCoordinates(VK_FALSE);
   2006 
   2007             auto result = device.createSampler(&samplerInfo, nullptr,
   2008                                                &textures[i].sampler);
   2009             VERIFY(result == vk::Result::eSuccess);
   2010 
   2011             auto const viewInfo =
   2012                 vk::ImageViewCreateInfo()
   2013                     .setImage(textures[i].image)
   2014                     .setViewType(vk::ImageViewType::e2D)
   2015                     .setFormat(tex_format)
   2016                     .setSubresourceRange(vk::ImageSubresourceRange(
   2017                         vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
   2018 
   2019             result =
   2020                 device.createImageView(&viewInfo, nullptr, &textures[i].view);
   2021             VERIFY(result == vk::Result::eSuccess);
   2022         }
   2023     }
   2024 
   2025     vk::ShaderModule prepare_vs() {
   2026         size_t size = 0;
   2027         void *vertShaderCode = read_spv("cube-vert.spv", &size);
   2028 
   2029         vert_shader_module = prepare_shader_module(vertShaderCode, size);
   2030 
   2031         free(vertShaderCode);
   2032 
   2033         return vert_shader_module;
   2034     }
   2035 
   2036     char *read_spv(const char *filename, size_t *psize) {
   2037         FILE *fp = fopen(filename, "rb");
   2038         if (!fp) {
   2039             return nullptr;
   2040         }
   2041 
   2042         fseek(fp, 0L, SEEK_END);
   2043         long int size = ftell(fp);
   2044 
   2045         fseek(fp, 0L, SEEK_SET);
   2046 
   2047         void *shader_code = malloc(size);
   2048         size_t retval = fread(shader_code, size, 1, fp);
   2049         VERIFY(retval == 1);
   2050 
   2051         *psize = size;
   2052 
   2053         fclose(fp);
   2054 
   2055         return (char *)shader_code;
   2056     }
   2057 
   2058     void resize() {
   2059         uint32_t i;
   2060 
   2061         // Don't react to resize until after first initialization.
   2062         if (!prepared) {
   2063             return;
   2064         }
   2065 
   2066         // In order to properly resize the window, we must re-create the
   2067         // swapchain
   2068         // AND redo the command buffers, etc.
   2069         //
   2070         // First, perform part of the cleanup() function:
   2071         prepared = false;
   2072         auto result = device.waitIdle();
   2073         VERIFY(result == vk::Result::eSuccess);
   2074 
   2075         for (i = 0; i < swapchainImageCount; i++) {
   2076             device.destroyFramebuffer(framebuffers[i], nullptr);
   2077         }
   2078 
   2079         device.destroyDescriptorPool(desc_pool, nullptr);
   2080 
   2081         device.destroyPipeline(pipeline, nullptr);
   2082         device.destroyPipelineCache(pipelineCache, nullptr);
   2083         device.destroyRenderPass(render_pass, nullptr);
   2084         device.destroyPipelineLayout(pipeline_layout, nullptr);
   2085         device.destroyDescriptorSetLayout(desc_layout, nullptr);
   2086 
   2087         for (i = 0; i < texture_count; i++) {
   2088             device.destroyImageView(textures[i].view, nullptr);
   2089             device.destroyImage(textures[i].image, nullptr);
   2090             device.freeMemory(textures[i].mem, nullptr);
   2091             device.destroySampler(textures[i].sampler, nullptr);
   2092         }
   2093 
   2094         device.destroyImageView(depth.view, nullptr);
   2095         device.destroyImage(depth.image, nullptr);
   2096         device.freeMemory(depth.mem, nullptr);
   2097 
   2098         device.destroyBuffer(uniform_data.buf, nullptr);
   2099         device.freeMemory(uniform_data.mem, nullptr);
   2100 
   2101         for (i = 0; i < swapchainImageCount; i++) {
   2102             device.destroyImageView(buffers[i].view, nullptr);
   2103             device.freeCommandBuffers(cmd_pool, 1, &buffers[i].cmd);
   2104         }
   2105 
   2106         device.destroyCommandPool(cmd_pool, nullptr);
   2107         if (separate_present_queue) {
   2108             device.destroyCommandPool(present_cmd_pool, nullptr);
   2109         }
   2110 
   2111         // Second, re-perform the prepare() function, which will re-create the
   2112         // swapchain.
   2113         prepare();
   2114     }
   2115 
   2116     void set_image_layout(vk::Image image, vk::ImageAspectFlags aspectMask,
   2117                           vk::ImageLayout oldLayout, vk::ImageLayout newLayout,
   2118                           vk::AccessFlags srcAccessMask) {
   2119         if (!cmd) {
   2120             auto const cmd = vk::CommandBufferAllocateInfo()
   2121                                  .setCommandPool(cmd_pool)
   2122                                  .setLevel(vk::CommandBufferLevel::ePrimary)
   2123                                  .setCommandBufferCount(1);
   2124 
   2125             auto result = device.allocateCommandBuffers(&cmd, &this->cmd);
   2126             VERIFY(result == vk::Result::eSuccess);
   2127 
   2128             auto const cmd_buf_info =
   2129                 vk::CommandBufferBeginInfo().setPInheritanceInfo(nullptr);
   2130 
   2131             result = this->cmd.begin(&cmd_buf_info);
   2132             VERIFY(result == vk::Result::eSuccess);
   2133         }
   2134 
   2135         auto DstAccessMask = [](vk::ImageLayout const &layout) {
   2136             vk::AccessFlags flags;
   2137 
   2138             switch (layout) {
   2139             case vk::ImageLayout::eTransferDstOptimal:
   2140                 // Make sure anything that was copying from this image has
   2141                 // completed
   2142                 flags = vk::AccessFlagBits::eTransferRead;
   2143                 break;
   2144             case vk::ImageLayout::eColorAttachmentOptimal:
   2145                 flags = vk::AccessFlagBits::eColorAttachmentWrite;
   2146                 break;
   2147             case vk::ImageLayout::eDepthStencilAttachmentOptimal:
   2148                 flags = vk::AccessFlagBits::eDepthStencilAttachmentWrite;
   2149                 break;
   2150             case vk::ImageLayout::eShaderReadOnlyOptimal:
   2151                 // Make sure any Copy or CPU writes to image are flushed
   2152                 flags = vk::AccessFlagBits::eShaderRead |
   2153                         vk::AccessFlagBits::eInputAttachmentRead;
   2154                 break;
   2155             case vk::ImageLayout::ePresentSrcKHR:
   2156                 flags = vk::AccessFlagBits::eMemoryRead;
   2157                 break;
   2158             default:
   2159                 break;
   2160             }
   2161 
   2162             return flags;
   2163         };
   2164 
   2165         auto const barrier = vk::ImageMemoryBarrier()
   2166                                  .setSrcAccessMask(srcAccessMask)
   2167                                  .setDstAccessMask(DstAccessMask(newLayout))
   2168                                  .setOldLayout(oldLayout)
   2169                                  .setNewLayout(newLayout)
   2170                                  .setSrcQueueFamilyIndex(0)
   2171                                  .setDstQueueFamilyIndex(0)
   2172                                  .setImage(image)
   2173                                  .setSubresourceRange(vk::ImageSubresourceRange(
   2174                                      aspectMask, 0, 1, 0, 1));
   2175 
   2176         cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
   2177                             vk::PipelineStageFlagBits::eTopOfPipe,
   2178                             vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1,
   2179                             &barrier);
   2180     }
   2181 
   2182     void update_data_buffer() {
   2183         mat4x4 VP;
   2184         mat4x4_mul(VP, projection_matrix, view_matrix);
   2185 
   2186         // Rotate 22.5 degrees around the Y axis
   2187         mat4x4 Model;
   2188         mat4x4_dup(Model, model_matrix);
   2189         mat4x4_rotate(model_matrix, Model, 0.0f, 1.0f, 0.0f,
   2190                       (float)degreesToRadians(spin_angle));
   2191 
   2192         mat4x4 MVP;
   2193         mat4x4_mul(MVP, VP, model_matrix);
   2194 
   2195         auto data = device.mapMemory(uniform_data.mem, 0,
   2196                                      uniform_data.mem_alloc.allocationSize,
   2197                                      vk::MemoryMapFlags());
   2198         VERIFY(data.result == vk::Result::eSuccess);
   2199 
   2200         memcpy(data.value, (const void *)&MVP[0][0], sizeof(MVP));
   2201 
   2202         device.unmapMemory(uniform_data.mem);
   2203     }
   2204 
   2205     bool loadTexture(const char *filename, uint8_t *rgba_data,
   2206                      vk::SubresourceLayout *layout, int32_t *width,
   2207                      int32_t *height) {
   2208         FILE *fPtr = fopen(filename, "rb");
   2209         if (!fPtr) {
   2210             return false;
   2211         }
   2212 
   2213         char header[256];
   2214         char *cPtr = fgets(header, 256, fPtr); // P6
   2215         if (cPtr == nullptr || strncmp(header, "P6\n", 3)) {
   2216             fclose(fPtr);
   2217             return false;
   2218         }
   2219 
   2220         do {
   2221             cPtr = fgets(header, 256, fPtr);
   2222             if (cPtr == nullptr) {
   2223                 fclose(fPtr);
   2224                 return false;
   2225             }
   2226         } while (!strncmp(header, "#", 1));
   2227 
   2228         sscanf(header, "%u %u", width, height);
   2229         if (rgba_data == nullptr) {
   2230             fclose(fPtr);
   2231             return true;
   2232         }
   2233 
   2234         char *result = fgets(header, 256, fPtr); // Format
   2235         VERIFY(result != nullptr);
   2236         if (cPtr == nullptr || strncmp(header, "255\n", 3)) {
   2237             fclose(fPtr);
   2238             return false;
   2239         }
   2240 
   2241         for (int y = 0; y < *height; y++) {
   2242             uint8_t *rowPtr = rgba_data;
   2243 
   2244             for (int x = 0; x < *width; x++) {
   2245                 size_t s = fread(rowPtr, 3, 1, fPtr);
   2246                 (void)s;
   2247                 rowPtr[3] = 255; /* Alpha of 1 */
   2248                 rowPtr += 4;
   2249             }
   2250 
   2251             rgba_data += layout->rowPitch;
   2252         }
   2253 
   2254         fclose(fPtr);
   2255         return true;
   2256     }
   2257 
   2258     bool memory_type_from_properties(uint32_t typeBits,
   2259                                      vk::MemoryPropertyFlags requirements_mask,
   2260                                      uint32_t *typeIndex) {
   2261         // Search memtypes to find first index with those properties
   2262         for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
   2263             if ((typeBits & 1) == 1) {
   2264                 // Type is available, does it match user properties?
   2265                 if ((memory_properties.memoryTypes[i].propertyFlags &
   2266                      requirements_mask) == requirements_mask) {
   2267                     *typeIndex = i;
   2268                     return true;
   2269                 }
   2270             }
   2271             typeBits >>= 1;
   2272         }
   2273 
   2274         // No memory types matched, return failure
   2275         return false;
   2276     }
   2277 
   2278 #if defined(VK_USE_PLATFORM_WIN32_KHR)
   2279     void run() {
   2280         if (!prepared) {
   2281             return;
   2282         }
   2283 
   2284         update_data_buffer();
   2285         draw();
   2286         curFrame++;
   2287 
   2288         if (frameCount != INT_MAX && curFrame == frameCount) {
   2289             PostQuitMessage(validation_error);
   2290         }
   2291     }
   2292 
   2293     void create_window() {
   2294         WNDCLASSEX win_class;
   2295 
   2296         // Initialize the window class structure:
   2297         win_class.cbSize = sizeof(WNDCLASSEX);
   2298         win_class.style = CS_HREDRAW | CS_VREDRAW;
   2299         win_class.lpfnWndProc = WndProc;
   2300         win_class.cbClsExtra = 0;
   2301         win_class.cbWndExtra = 0;
   2302         win_class.hInstance = connection; // hInstance
   2303         win_class.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
   2304         win_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
   2305         win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
   2306         win_class.lpszMenuName = nullptr;
   2307         win_class.lpszClassName = name;
   2308         win_class.hIconSm = LoadIcon(nullptr, IDI_WINLOGO);
   2309 
   2310         // Register window class:
   2311         if (!RegisterClassEx(&win_class)) {
   2312             // It didn't work, so try to give a useful error:
   2313             printf("Unexpected error trying to start the application!\n");
   2314             fflush(stdout);
   2315             exit(1);
   2316         }
   2317 
   2318         // Create window with the registered class:
   2319         RECT wr = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)};
   2320         AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
   2321         window = CreateWindowEx(0,
   2322                                 name,                 // class name
   2323                                 name,                 // app name
   2324                                 WS_OVERLAPPEDWINDOW | // window style
   2325                                     WS_VISIBLE | WS_SYSMENU,
   2326                                 100, 100,           // x/y coords
   2327                                 wr.right - wr.left, // width
   2328                                 wr.bottom - wr.top, // height
   2329                                 nullptr,            // handle to parent
   2330                                 nullptr,            // handle to menu
   2331                                 connection,         // hInstance
   2332                                 nullptr);           // no extra parameters
   2333 
   2334         if (!window) {
   2335             // It didn't work, so try to give a useful error:
   2336             printf("Cannot create a window in which to draw!\n");
   2337             fflush(stdout);
   2338             exit(1);
   2339         }
   2340 
   2341         // Window client area size must be at least 1 pixel high, to prevent
   2342         // crash.
   2343         minsize.x = GetSystemMetrics(SM_CXMINTRACK);
   2344         minsize.y = GetSystemMetrics(SM_CYMINTRACK) + 1;
   2345     }
   2346 
   2347 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
   2348 #if defined(VK_USE_PLATFORM_XLIB_KHR)
   2349 
   2350     void create_xlib_window() {
   2351         display = XOpenDisplay(nullptr);
   2352         long visualMask = VisualScreenMask;
   2353         int numberOfVisuals;
   2354         XVisualInfo vInfoTemplate = {};
   2355         vInfoTemplate.screen = DefaultScreen(display);
   2356         XVisualInfo *visualInfo = XGetVisualInfo(
   2357             display, visualMask, &vInfoTemplate, &numberOfVisuals);
   2358 
   2359         Colormap colormap =
   2360             XCreateColormap(display, RootWindow(display, vInfoTemplate.screen),
   2361                             visualInfo->visual, AllocNone);
   2362 
   2363         XSetWindowAttributes windowAttributes = {};
   2364         windowAttributes.colormap = colormap;
   2365         windowAttributes.background_pixel = 0xFFFFFFFF;
   2366         windowAttributes.border_pixel = 0;
   2367         windowAttributes.event_mask =
   2368             KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
   2369 
   2370         xlib_window = XCreateWindow(
   2371             display, RootWindow(display, vInfoTemplate.screen), 0, 0, width,
   2372             height, 0, visualInfo->depth, InputOutput, visualInfo->visual,
   2373             CWBackPixel | CWBorderPixel | CWEventMask | CWColormap,
   2374             &windowAttributes);
   2375 
   2376         XSelectInput(display, xlib_window, ExposureMask | KeyPressMask);
   2377         XMapWindow(display, xlib_window);
   2378         XFlush(display);
   2379         xlib_wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
   2380     }
   2381 
   2382     void handle_xlib_event(const XEvent *event) {
   2383         switch (event->type) {
   2384         case ClientMessage:
   2385             if ((Atom)event->xclient.data.l[0] == xlib_wm_delete_window) {
   2386                 quit = true;
   2387             }
   2388             break;
   2389         case KeyPress:
   2390             switch (event->xkey.keycode) {
   2391             case 0x9: // Escape
   2392                 quit = true;
   2393                 break;
   2394             case 0x71: // left arrow key
   2395                 spin_angle += spin_increment;
   2396                 break;
   2397             case 0x72: // right arrow key
   2398                 spin_angle -= spin_increment;
   2399                 break;
   2400             case 0x41:
   2401                 pause = !pause;
   2402                 break;
   2403             }
   2404             break;
   2405         case ConfigureNotify:
   2406             if (((int32_t)width != event->xconfigure.width) ||
   2407                 ((int32_t)height != event->xconfigure.height)) {
   2408                 width = event->xconfigure.width;
   2409                 height = event->xconfigure.height;
   2410                 resize();
   2411             }
   2412             break;
   2413         default:
   2414             break;
   2415         }
   2416     }
   2417 
   2418     void run_xlib() {
   2419         while (!quit) {
   2420             XEvent event;
   2421 
   2422             if (pause) {
   2423                 XNextEvent(display, &event);
   2424                 handle_xlib_event(&event);
   2425             } else {
   2426                 while (XPending(display) > 0) {
   2427                     XNextEvent(display, &event);
   2428                     handle_xlib_event(&event);
   2429                 }
   2430             }
   2431 
   2432             update_data_buffer();
   2433             draw();
   2434             curFrame++;
   2435 
   2436             if (frameCount != UINT32_MAX && curFrame == frameCount) {
   2437                 quit = true;
   2438             }
   2439         }
   2440     }
   2441 
   2442 #endif
   2443 #if defined(VK_USE_PLATFORM_XCB_KHR)
   2444 
   2445     void handle_xcb_event(const xcb_generic_event_t *event) {
   2446         uint8_t event_code = event->response_type & 0x7f;
   2447         switch (event_code) {
   2448         case XCB_EXPOSE:
   2449             // TODO: Resize window
   2450             break;
   2451         case XCB_CLIENT_MESSAGE:
   2452             if ((*(xcb_client_message_event_t *)event).data.data32[0] ==
   2453                 (*atom_wm_delete_window).atom) {
   2454                 quit = true;
   2455             }
   2456             break;
   2457         case XCB_KEY_RELEASE: {
   2458             const xcb_key_release_event_t *key =
   2459                 (const xcb_key_release_event_t *)event;
   2460 
   2461             switch (key->detail) {
   2462             case 0x9: // Escape
   2463                 quit = true;
   2464                 break;
   2465             case 0x71: // left arrow key
   2466                 spin_angle += spin_increment;
   2467                 break;
   2468             case 0x72: // right arrow key
   2469                 spin_angle -= spin_increment;
   2470                 break;
   2471             case 0x41:
   2472                 pause = !pause;
   2473                 break;
   2474             }
   2475         } break;
   2476         case XCB_CONFIGURE_NOTIFY: {
   2477             const xcb_configure_notify_event_t *cfg =
   2478                 (const xcb_configure_notify_event_t *)event;
   2479             if ((width != cfg->width) || (height != cfg->height)) {
   2480                 width = cfg->width;
   2481                 height = cfg->height;
   2482                 resize();
   2483             }
   2484         } break;
   2485         default:
   2486             break;
   2487         }
   2488     }
   2489 
   2490     void run_xcb() {
   2491         xcb_flush(connection);
   2492 
   2493         while (!quit) {
   2494             xcb_generic_event_t *event;
   2495 
   2496             if (pause) {
   2497                 event = xcb_wait_for_event(connection);
   2498             } else {
   2499                 event = xcb_poll_for_event(connection);
   2500                 while (event) {
   2501                     handle_xcb_event(event);
   2502                     free(event);
   2503                     event = xcb_poll_for_event(connection);
   2504                 }
   2505             }
   2506 
   2507             update_data_buffer();
   2508             draw();
   2509             curFrame++;
   2510             if (frameCount != UINT32_MAX && curFrame == frameCount) {
   2511                 quit = true;
   2512             }
   2513         }
   2514     }
   2515 
   2516     void create_xcb_window() {
   2517         uint32_t value_mask, value_list[32];
   2518 
   2519         xcb_window = xcb_generate_id(connection);
   2520 
   2521         value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
   2522         value_list[0] = screen->black_pixel;
   2523         value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE |
   2524                         XCB_EVENT_MASK_STRUCTURE_NOTIFY;
   2525 
   2526         xcb_create_window(connection, XCB_COPY_FROM_PARENT, xcb_window,
   2527                           screen->root, 0, 0, width, height, 0,
   2528                           XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
   2529                           value_mask, value_list);
   2530 
   2531         /* Magic code that will send notification when window is destroyed */
   2532         xcb_intern_atom_cookie_t cookie =
   2533             xcb_intern_atom(connection, 1, 12, "WM_PROTOCOLS");
   2534         xcb_intern_atom_reply_t *reply =
   2535             xcb_intern_atom_reply(connection, cookie, 0);
   2536 
   2537         xcb_intern_atom_cookie_t cookie2 =
   2538             xcb_intern_atom(connection, 0, 16, "WM_DELETE_WINDOW");
   2539         atom_wm_delete_window = xcb_intern_atom_reply(connection, cookie2, 0);
   2540 
   2541         xcb_change_property(connection, XCB_PROP_MODE_REPLACE, xcb_window,
   2542                             (*reply).atom, 4, 32, 1,
   2543                             &(*atom_wm_delete_window).atom);
   2544 
   2545         free(reply);
   2546 
   2547         xcb_map_window(connection, xcb_window);
   2548 
   2549         // Force the x/y coordinates to 100,100 results are identical in
   2550         // consecutive
   2551         // runs
   2552         const uint32_t coords[] = {100, 100};
   2553         xcb_configure_window(connection, xcb_window,
   2554                              XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords);
   2555     }
   2556 
   2557 #endif
   2558 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
   2559 
   2560     void run() {
   2561         while (!quit) {
   2562             update_data_buffer();
   2563             draw();
   2564             curFrame++;
   2565             if (frameCount != UINT32_MAX && curFrame == frameCount) {
   2566                 quit = true;
   2567             }
   2568         }
   2569     }
   2570 
   2571     void create_window() {
   2572         window = wl_compositor_create_surface(compositor);
   2573         if (!window) {
   2574             printf("Can not create wayland_surface from compositor!\n");
   2575             fflush(stdout);
   2576             exit(1);
   2577         }
   2578 
   2579         shell_surface = wl_shell_get_shell_surface(shell, window);
   2580         if (!shell_surface) {
   2581             printf("Can not get shell_surface from wayland_surface!\n");
   2582             fflush(stdout);
   2583             exit(1);
   2584         }
   2585 
   2586         wl_shell_surface_add_listener(shell_surface, &shell_surface_listener,
   2587                                       this);
   2588         wl_shell_surface_set_toplevel(shell_surface);
   2589         wl_shell_surface_set_title(shell_surface, APP_SHORT_NAME);
   2590     }
   2591 
   2592 #endif
   2593 
   2594 #if defined(VK_USE_PLATFORM_WIN32_KHR)
   2595     HINSTANCE connection;        // hInstance - Windows Instance
   2596     HWND window;                 // hWnd - window handle
   2597     POINT minsize;               // minimum window size
   2598     char name[APP_NAME_STR_LEN]; // Name to put on the window/icon
   2599 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
   2600     Display *display;
   2601     Window xlib_window;
   2602     Atom xlib_wm_delete_window;
   2603 
   2604     xcb_connection_t *connection;
   2605     xcb_screen_t *screen;
   2606     xcb_window_t xcb_window;
   2607     xcb_intern_atom_reply_t *atom_wm_delete_window;
   2608 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
   2609     wl_display *display;
   2610     wl_registry *registry;
   2611     wl_compositor *compositor;
   2612     wl_surface *window;
   2613     wl_shell *shell;
   2614     wl_shell_surface *shell_surface;
   2615 #endif
   2616 
   2617     vk::SurfaceKHR surface;
   2618     bool prepared;
   2619     bool use_staging_buffer;
   2620     bool use_xlib;
   2621     bool separate_present_queue;
   2622 
   2623     vk::Instance inst;
   2624     vk::PhysicalDevice gpu;
   2625     vk::Device device;
   2626     vk::Queue graphics_queue;
   2627     vk::Queue present_queue;
   2628     uint32_t graphics_queue_family_index;
   2629     uint32_t present_queue_family_index;
   2630     vk::Semaphore image_acquired_semaphores[FRAME_LAG];
   2631     vk::Semaphore draw_complete_semaphores[FRAME_LAG];
   2632     vk::Semaphore image_ownership_semaphores[FRAME_LAG];
   2633     vk::PhysicalDeviceProperties gpu_props;
   2634     std::unique_ptr<vk::QueueFamilyProperties[]> queue_props;
   2635     vk::PhysicalDeviceMemoryProperties memory_properties;
   2636 
   2637     uint32_t enabled_extension_count;
   2638     uint32_t enabled_layer_count;
   2639     char const *extension_names[64];
   2640     char const *enabled_layers[64];
   2641 
   2642     uint32_t width;
   2643     uint32_t height;
   2644     vk::Format format;
   2645     vk::ColorSpaceKHR color_space;
   2646 
   2647     uint32_t swapchainImageCount;
   2648     vk::SwapchainKHR swapchain;
   2649     std::unique_ptr<SwapchainBuffers[]> buffers;
   2650     vk::Fence fences[FRAME_LAG];
   2651     bool fencesInited[FRAME_LAG];
   2652     uint32_t frame_index;
   2653 
   2654     vk::CommandPool cmd_pool;
   2655     vk::CommandPool present_cmd_pool;
   2656 
   2657     struct {
   2658         vk::Format format;
   2659         vk::Image image;
   2660         vk::MemoryAllocateInfo mem_alloc;
   2661         vk::DeviceMemory mem;
   2662         vk::ImageView view;
   2663     } depth;
   2664 
   2665     static int32_t const texture_count = 1;
   2666     texture_object textures[texture_count];
   2667 
   2668     struct {
   2669         vk::Buffer buf;
   2670         vk::MemoryAllocateInfo mem_alloc;
   2671         vk::DeviceMemory mem;
   2672         vk::DescriptorBufferInfo buffer_info;
   2673     } uniform_data;
   2674 
   2675     vk::CommandBuffer cmd; // Buffer for initialization commands
   2676     vk::PipelineLayout pipeline_layout;
   2677     vk::DescriptorSetLayout desc_layout;
   2678     vk::PipelineCache pipelineCache;
   2679     vk::RenderPass render_pass;
   2680     vk::Pipeline pipeline;
   2681 
   2682     mat4x4 projection_matrix;
   2683     mat4x4 view_matrix;
   2684     mat4x4 model_matrix;
   2685 
   2686     float spin_angle;
   2687     float spin_increment;
   2688     bool pause;
   2689 
   2690     vk::ShaderModule vert_shader_module;
   2691     vk::ShaderModule frag_shader_module;
   2692 
   2693     vk::DescriptorPool desc_pool;
   2694     vk::DescriptorSet desc_set;
   2695 
   2696     std::unique_ptr<vk::Framebuffer[]> framebuffers;
   2697 
   2698     bool quit;
   2699     uint32_t curFrame;
   2700     uint32_t frameCount;
   2701     bool validate;
   2702     bool use_break;
   2703     bool suppress_popups;
   2704 
   2705     uint32_t current_buffer;
   2706     uint32_t queue_family_count;
   2707 };
   2708 
   2709 #if _WIN32
   2710 // Include header required for parsing the command line options.
   2711 #include <shellapi.h>
   2712 
   2713 Demo demo;
   2714 
   2715 // MS-Windows event handling function:
   2716 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
   2717     switch (uMsg) {
   2718     case WM_CLOSE:
   2719         PostQuitMessage(validation_error);
   2720         break;
   2721     case WM_PAINT:
   2722         demo.run();
   2723         break;
   2724     case WM_GETMINMAXINFO: // set window's minimum size
   2725         ((MINMAXINFO *)lParam)->ptMinTrackSize = demo.minsize;
   2726         return 0;
   2727     case WM_SIZE:
   2728         // Resize the application to the new window size, except when
   2729         // it was minimized. Vulkan doesn't support images or swapchains
   2730         // with width=0 and height=0.
   2731         if (wParam != SIZE_MINIMIZED) {
   2732             demo.width = lParam & 0xffff;
   2733             demo.height = (lParam & 0xffff0000) >> 16;
   2734             demo.resize();
   2735         }
   2736         break;
   2737     default:
   2738         break;
   2739     }
   2740 
   2741     return (DefWindowProc(hWnd, uMsg, wParam, lParam));
   2742 }
   2743 
   2744 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine,
   2745                    int nCmdShow) {
   2746     // TODO: Gah.. refactor. This isn't 1989.
   2747     MSG msg;   // message
   2748     bool done; // flag saying when app is complete
   2749     int argc;
   2750     char **argv;
   2751 
   2752     // Use the CommandLine functions to get the command line arguments.
   2753     // Unfortunately, Microsoft outputs
   2754     // this information as wide characters for Unicode, and we simply want the
   2755     // Ascii version to be compatible
   2756     // with the non-Windows side.  So, we have to convert the information to
   2757     // Ascii character strings.
   2758     LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc);
   2759     if (nullptr == commandLineArgs) {
   2760         argc = 0;
   2761     }
   2762 
   2763     if (argc > 0) {
   2764         argv = (char **)malloc(sizeof(char *) * argc);
   2765         if (argv == nullptr) {
   2766             argc = 0;
   2767         } else {
   2768             for (int iii = 0; iii < argc; iii++) {
   2769                 size_t wideCharLen = wcslen(commandLineArgs[iii]);
   2770                 size_t numConverted = 0;
   2771 
   2772                 argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1));
   2773                 if (argv[iii] != nullptr) {
   2774                     wcstombs_s(&numConverted, argv[iii], wideCharLen + 1,
   2775                                commandLineArgs[iii], wideCharLen + 1);
   2776                 }
   2777             }
   2778         }
   2779     } else {
   2780         argv = nullptr;
   2781     }
   2782 
   2783     demo.init(argc, argv);
   2784 
   2785     // Free up the items we had to allocate for the command line arguments.
   2786     if (argc > 0 && argv != nullptr) {
   2787         for (int iii = 0; iii < argc; iii++) {
   2788             if (argv[iii] != nullptr) {
   2789                 free(argv[iii]);
   2790             }
   2791         }
   2792         free(argv);
   2793     }
   2794 
   2795     demo.connection = hInstance;
   2796     strncpy(demo.name, "cube", APP_NAME_STR_LEN);
   2797     demo.create_window();
   2798     demo.init_vk_swapchain();
   2799 
   2800     demo.prepare();
   2801 
   2802     done = false; // initialize loop condition variable
   2803 
   2804     // main message loop
   2805     while (!done) {
   2806         PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
   2807         if (msg.message == WM_QUIT) // check for a quit message
   2808         {
   2809             done = true; // if found, quit app
   2810         } else {
   2811             /* Translate and dispatch to event queue*/
   2812             TranslateMessage(&msg);
   2813             DispatchMessage(&msg);
   2814         }
   2815         RedrawWindow(demo.window, nullptr, nullptr, RDW_INTERNALPAINT);
   2816     }
   2817 
   2818     demo.cleanup();
   2819 
   2820     return (int)msg.wParam;
   2821 }
   2822 
   2823 #elif __linux__
   2824 
   2825 #if defined(VK_USE_PLATFORM_WAYLAND_KHR)
   2826 static void handle_ping(void *data, wl_shell_surface *shell_surface,
   2827                         uint32_t serial) {
   2828     wl_shell_surface_pong(shell_surface, serial);
   2829 }
   2830 
   2831 static void handle_configure(void *data,
   2832                              wl_shell_surface *shell_surface,
   2833                              uint32_t edges, int32_t width,
   2834                              int32_t height) {}
   2835 
   2836 static void handle_popup_done(void *data,
   2837                               wl_shell_surface *shell_surface) {}
   2838 
   2839 static const wl_shell_surface_listener shell_surface_listener = {
   2840     handle_ping, handle_configure, handle_popup_done};
   2841 #endif
   2842 
   2843 int main(int argc, char **argv) {
   2844     Demo demo;
   2845 
   2846     demo.init(argc, argv);
   2847 
   2848 #if defined(VK_USE_PLATFORM_XLIB_KHR) && defined(VK_USE_PLATFORM_XCB_KHR)
   2849     if (demo.use_xlib) {
   2850         demo.create_xlib_window();
   2851     } else {
   2852         demo.create_xcb_window();
   2853 #elif defined(VK_USE_PLATFORM_XCB_KHR)
   2854     demo.create_xcb_window();
   2855 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
   2856     demo.create_xlib_window();
   2857 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
   2858     demo.create_window();
   2859 #endif
   2860     }
   2861 
   2862     demo.init_vk_swapchain();
   2863 
   2864     demo.prepare();
   2865 
   2866 #if defined(VK_USE_PLATFORM_XLIB_KHR) && defined(VK_USE_PLATFORM_XCB_KHR)
   2867     if (demo.use_xlib) {
   2868         demo.run_xlib();
   2869     } else {
   2870         demo.run_xcb();
   2871 #elif defined(VK_USE_PLATFORM_XCB_KHR)
   2872 demo.run_xcb();
   2873 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
   2874 demo.run_xlib();
   2875 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
   2876 demo.run();
   2877 #endif
   2878     }
   2879 
   2880     demo.cleanup();
   2881 
   2882     return validation_error;
   2883 }
   2884 
   2885 #else
   2886 #error "Platform not supported"
   2887 #endif
   2888