fixed swapchain sync for standard win, needs recreation at resize
authorEleni Maria Stea <elene.mst@gmail.com>
Tue, 5 Oct 2021 16:37:52 +0000 (19:37 +0300)
committerEleni Maria Stea <elene.mst@gmail.com>
Tue, 5 Oct 2021 16:37:52 +0000 (19:37 +0300)
see abort in swapchain errors in main

src/main.cc
src/vk.c
src/vk.h

index ea70715..d642aab 100644 (file)
@@ -19,8 +19,8 @@
 #include "camera.h"
 
 /* defines */
-
-#define FENCE_TIMEOUT 100000000000
+#define FRAME_LAG 2
+#define FENCE_TIMEOUT UINT64_MAX
 
 /**************************/
 /* static glfw callbacks */
@@ -58,6 +58,9 @@ display();
 static bool
 vk_init();
 
+static bool
+vk_create_sync_objects();
+
 static void
 vk_cleanup();
 
@@ -88,13 +91,16 @@ static int win_h = 600;
 static bool vk_enable_layers = true;
 static struct vk_ctx vk_core;
 static VkSurfaceKHR vk_surf;
+
 static struct vk_swapchain vk_chain;
-static uint32_t vk_chain_idx;
-static struct vk_semaphores vk_sema;
+static uint32_t vk_current_image;
+static int vk_frame_idx;
+static std::vector<VkFramebuffer>vk_framebuffers;
 
 /* for the moment: one cmd buffer per swapchain image */
 static std::vector<VkCommandBuffer>vk_cmd_buffers;
-static std::vector<VkFence>vk_wait_fences;
+static std::vector<VkFence>vk_fences;
+static struct vk_semaphores vk_semas[FRAME_LAG];
 
 /* FIXME make them part of each object's functions/params */
 static struct vk_renderer vk_rnd;
@@ -102,65 +108,52 @@ static struct vk_attachment vk_depth_att;
 static float vk_fb_color[4] = { 0.0, 0.0, 0.5, 1.0 };
 
 /* empty for as long as we hardcode the vertices in the vertex shader */
+#if 0
 static struct vk_vertex_info vk_vert_info;
+#endif
 
 int main(int argc, char** argv)
 {
     atexit(cleanup);
 
+    /***********************************************************
+     * GLFW
+     ***********************************************************/
+
     if (!glfwInit()) {
         fprintf(stderr, "Failed to initialize GLFW.\n");
         return 1;
     }
 
-    if (!init()) {
-        return 1;
-    }
-
-#if 0
-    glfwGetWindowSize(win, &win_w, &win_h);
-    clb_reshape(win, win_w, win_h);
-#endif
-
-       while(!glfwWindowShouldClose(win)) {
-               glfwPollEvents();
-               display();
-       }
-    vkDeviceWaitIdle(vk_core.dev);
-
-    return 0;
-}
-
-/* static functions */
-
-static bool
-vk_init()
-{
     if (glfwVulkanSupported() != GLFW_TRUE) {
         fprintf(stderr, "Vulkan is not supported on this device.\n");
         return false;
     }
 
-    /* create window */
-
     glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
-    win = glfwCreateWindow(win_w, win_h, "helloworld rt", 0, 0);
-
-    if (!win) {
+    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
+    if (!(win = glfwCreateWindow(win_w, win_h, "helloworld rt", 0, 0))) {
         fprintf(stderr, "Failed to create GLFW window\n");
         return false;
     }
 
     glfwSetKeyCallback(win, clb_key);
+    glfwSetWindowSizeCallback(win, clb_reshape);
+    glfwSetCursorPosCallback(win, clb_motion);
+    glfwSetMouseButtonCallback(win, clb_mouse);
 
-    /* initialize Vulkan context (instance) */
+    /***********************************************************
+     * VULKAN context
+     ***********************************************************/
 
     if (!vk_init_ctx_for_rendering(&vk_core, true, vk_enable_layers)) {
         fprintf(stderr, "Failed to initialize Vulkan context.\n");
         return false;
     }
 
-    /* create (Xcb) surface */
+    /***********************************************************
+     * XCB/GLFW surface
+     ***********************************************************/
 
     glfwGetFramebufferSize(win, &win_h, &win_h);
     if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf)
@@ -169,29 +162,67 @@ vk_init()
         return false;
     }
 
-    /* load shaders */
-    int vsz, fsz;
-    char *vsdr = sdr_load("data/main.vert.spv", &vsz);
-    char *fsdr = sdr_load("data/main.frag.spv", &fsz);
+    /***********************************************************
+     * Initialize Vulkan structs
+     ***********************************************************/
 
-    /* create semaphores */
-    if (!vk_create_semaphores(&vk_core, false, &vk_sema)) {
-        fprintf(stderr, "No semaphores were created.\n");
-        goto fail;
+    if (!vk_init()) {
+        return 1;
     }
 
+    /***********************************************************
+     * Initialize program objects, classes etc
+     ***********************************************************/
+
+    if (!init()) {
+        return 1;
+    }
+
+    /***********************************************************
+     * Rendering loop
+     ***********************************************************/
+
+       while(!glfwWindowShouldClose(win)) {
+               glfwPollEvents();
+               display();
+       }
+
+    vkDeviceWaitIdle(vk_core.dev);
+    return 0;
+}
+
+/* static functions */
+
+static bool
+vk_init()
+{
     /* create swapchain */
     if (!vk_create_swapchain(&vk_core, win_w, win_h, false, vk_surf, 0, &vk_chain)) {
         fprintf(stderr, "No swapchain was created.\n");
-        goto fail;
+        return false;
     }
 
     if (vk_chain.swapchain == VK_NULL_HANDLE) {
         fprintf(stderr, "Invalid swapchain handle.\n");
-        goto fail;
+        return false;
     }
 
-    /* FIXME: this part is going to be part of each object's functions */
+    if (!vk_create_sync_objects()) {
+        fprintf(stderr, "Failed to create sync objects.\n");
+        return false;
+    }
+
+    /* FIXME for the moment one cmd buf.
+     * But most probably I need to change this later. */
+    VkCommandBuffer cmd_buf;
+    if (!(cmd_buf = vk_create_cmd_buffer(&vk_core))) {
+        fprintf(stderr, "Failed to create command buffer: %d.\n", 0);
+        return false;
+    }
+    vk_cmd_buffers.push_back(cmd_buf);
+
+    /* FIXME: this part is going to be part of each object's
+     * renderpass and pipeline */
 
     /* create depth attachment (for the moment we are going to use this
      * for all images */
@@ -209,78 +240,54 @@ vk_init()
     }
     if (!vk_create_image(&vk_core, &vk_depth_att.props, &vk_depth_att.obj)) {
         fprintf(stderr, "Failed to create depth attachment.\n");
-        goto fail;
+        return false;
+    }
+    if(!vk_create_image_view(&vk_core, vk_depth_att.obj.img, VK_IMAGE_VIEW_TYPE_2D,
+                             vk_depth_att.props.format, false,
+                             &vk_depth_att.obj.img_view)) {
+        fprintf(stderr, "Failed to create image view for depth attachment.\n");
+        return false;
     }
 
+    /* load shaders */
+    int vsz, fsz;
+    char *vsdr = sdr_load("data/main.vert.spv", &vsz);
+    char *fsdr = sdr_load("data/main.frag.spv", &fsz);
+
     /* create renderer */
     if (!vk_create_renderer(&vk_core,
                             vsdr, vsz, fsdr, fsz,
                             win_w, win_h, 1,
-                            false, false,
-                            vk_chain.num_atts, vk_chain.atts, &vk_depth_att,
-                            &vk_vert_info, &vk_rnd)) {
+                            true, false,
+                            1, &vk_chain.img_props, &vk_depth_att.props,
+                            0, &vk_rnd)) {
         goto fail;
     }
 
-    /* create cmd buffers */
-    for (size_t i = 0; i < vk_chain.num_atts; i++) {
-        VkCommandBuffer cmd_buf;
-        VkFence fence;
-        if(!(vk_create_fence(&vk_core, &fence))) {
-            fprintf(stderr, "Failed to create fence: %d.\n", (int)i);
-            goto fail;
-        }
-        vk_wait_fences.push_back(fence);
-
-        if (!(cmd_buf = vk_create_cmd_buffer(&vk_core))) {
-            fprintf(stderr, "Failed to create command buffer: %d.\n", (int)i);
-            goto fail;
-        }
-
-        /* record cmd buffer FIXME:
-         * part of each objects draw? loop for each
-         * renderer */
-        if (!vk_record_cmd_buffer(&vk_core, cmd_buf,
-                                  &vk_rnd, 0,
-                                  4, vk_fb_color,
-                                  vk_chain.num_atts + 1, 0,
-                                  0, 0, win_w, win_h)) {
-            fprintf(stderr, "Failed to record command buffer.\n");
-            goto fail;
-        }
-
-        vk_cmd_buffers.push_back(cmd_buf);
+    vk_framebuffers.resize(vk_chain.num_images, VK_NULL_HANDLE);
+    for (uint32_t i = 0; i < vk_chain.num_images; i++) {
+        if (!vk_create_framebuffer(&vk_core,
+                                   win_w, win_h, 1,
+                                   &vk_chain.views[i],
+                                   &vk_depth_att.obj.img_view,
+                                   vk_rnd.renderpass,
+                                   &vk_framebuffers[i]))
+        goto fail;
     }
 
-    vkResetFences(vk_core.dev, vk_wait_fences.size(), vk_wait_fences.data());
+    /* record cmd buffer FIXME:
+     * should move this and make it part of each renderer
+     * also add all other stuff needed like uniforms
+     * descriptor sets spec constants push constants etc
+     * renderer
+     */
 
     free(vsdr);
     free(fsdr);
 
-    /* set GLFW callbacks */
-
-    glfwSetWindowSizeCallback(win, clb_reshape);
-
-    glfwSetCursorPosCallback(win, clb_motion);
-    glfwSetMouseButtonCallback(win, clb_mouse);
-
     return true;
 
 fail:
-    for (size_t i = 0; i < vk_wait_fences.size(); i++) {
-        vk_destroy_fences(&vk_core, vk_wait_fences.size(), vk_wait_fences.data());
-    }
-    vk_wait_fences.clear();
-
-    for (size_t i = 0; i < vk_cmd_buffers.size(); i++) {
-        vk_destroy_cmd_buffers(&vk_core, vk_cmd_buffers.size(), vk_cmd_buffers.data());
-    }
-    vk_cmd_buffers.clear();
-
-    if (vk_depth_att.obj.img != VK_NULL_HANDLE) {
-        vk_destroy_image(&vk_core, &vk_depth_att.obj);
-    }
-
     free(vsdr);
     free(fsdr);
 
@@ -290,11 +297,6 @@ fail:
 static bool
 init()
 {
-    if (!vk_init()) {
-        fprintf(stderr, "Failed to initialize Vulkan structs.\n");
-        return false;
-    }
-
     camera = new OrbitCamera;
     return true;
 }
@@ -307,28 +309,69 @@ cleanup()
     vk_cleanup();
 }
 
+/* FIXME */
 static int count;
 static void
 display()
 {
+    vkWaitForFences(vk_core.dev, 1, &vk_fences[vk_frame_idx], VK_TRUE, UINT64_MAX);
+    vkResetFences(vk_core.dev, 1, &vk_fences[vk_frame_idx]);
+
+    VkResult err;
+    do {
+        err = vkAcquireNextImageKHR(vk_core.dev, vk_chain.swapchain,
+                                    UINT64_MAX,
+                                    vk_semas[vk_frame_idx].frame_ready,
+                                    0, &vk_current_image);
+        switch(err) {
+        case VK_ERROR_OUT_OF_DATE_KHR:
+            fprintf(stderr, "acquire next image error: VK_ERROR_OUT_OF_DATE_KHR.\n");
+            abort();
+            break;
+        case VK_SUBOPTIMAL_KHR:
+            fprintf(stderr, "AcquireNextImageKHR returned VK_SUBOPTIMAL_KHR, ignored.\n");
+            abort();
+            break;
+        case VK_ERROR_SURFACE_LOST_KHR:
+            vkDestroySurfaceKHR(vk_core.inst, vk_surf, 0);
+            if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf) !=
+                    VK_SUCCESS) {
+                fprintf(stderr, "Failed to recreate GLFW/XCB surface.\n");
+            }
+            abort();
+            break;
+        default:
+            assert(!err);
+            break;
+        }
+    } while (err != VK_SUCCESS);
+
+    /* FIXME update buffer data */
+    if (!vk_record_cmd_buffer(&vk_core, vk_cmd_buffers[0],
+                &vk_rnd, 0,
+                4, vk_fb_color,
+                vk_framebuffers[vk_current_image],
+                2, 0,
+                0, 0, win_w, win_h)) {
+        fprintf(stderr, "Failed to record command buffer.\n");
+        abort();
+    }
+
     /* each object should have a command buffer renderpass etc? */
     VkSubmitInfo sinfo;
     VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
 
-    vkAcquireNextImageKHR(vk_core.dev, vk_chain.swapchain,
-                          UINT64_MAX, vk_sema.frame_ready, 0, &vk_chain_idx);
-
     memset(&sinfo, 0, sizeof sinfo);
     sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
     sinfo.waitSemaphoreCount = 1;
-    sinfo.pWaitSemaphores = &vk_sema.frame_ready;
+    sinfo.pWaitSemaphores = &vk_semas[vk_frame_idx].frame_ready;
     sinfo.pWaitDstStageMask = &wait_stages;
     sinfo.commandBufferCount = 1;
-    sinfo.pCommandBuffers = &vk_cmd_buffers[vk_chain_idx];
+    sinfo.pCommandBuffers = &vk_cmd_buffers[0];
     sinfo.signalSemaphoreCount = 1;
-       sinfo.pSignalSemaphores = &vk_sema.frame_done;
+       sinfo.pSignalSemaphores = &vk_semas[vk_frame_idx].frame_done;
 
-    if (vkQueueSubmit(vk_core.queue, 1, &sinfo, vk_wait_fences[vk_chain_idx]) != 0) {
+    if (vkQueueSubmit(vk_core.queue, 1, &sinfo, vk_fences[vk_frame_idx]) != 0) {
         fprintf(stderr, "Failed to submit draw commands.\n");
         abort();
     }
@@ -338,38 +381,28 @@ display()
         abort();
     }
 
-    if (vkWaitForFences(vk_core.dev, 1, &vk_wait_fences[vk_chain_idx],
-                        VK_TRUE, FENCE_TIMEOUT) != VK_SUCCESS) {
-        fprintf(stderr, "Waiting for fence: %u failed.\n", vk_chain_idx);
+    if (!vk_queue_present(&vk_chain, vk_core.queue, vk_current_image,
+                          vk_semas[vk_frame_idx].frame_done)) {
         abort();
     }
 
-#if 0
-    memset(&pinfo, 0, sizeof pinfo);
-    pinfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
-    pinfo.waitSemaphoreCount = 1;
-    pinfo.pWaitSemaphores = &vk_sema.frame_done;
-    pinfo.swapchainCount = 1;
-    pinfo.pSwapchains = &vk_chain.swapchain;
-    pinfo.pImageIndices = &vk_chain_idx;
-#endif
-
-    if (!vk_queue_present(&vk_chain, vk_core.queue, vk_chain_idx, vk_sema.frame_done)) {
-        abort();
-    }
-    vkResetFences(vk_core.dev, 1, &vk_wait_fences[vk_chain_idx]);
+    vk_frame_idx = (vk_frame_idx + 1) % FRAME_LAG;
 
     printf("display %d\n", count++);
+    printf("current image %u\n", vk_current_image);
+    printf("frame idx %d\n", vk_frame_idx);
 }
 
 static void
 vk_cleanup()
 {
-    vkQueueWaitIdle(vk_core.queue);
-
     vk_destroy_image(&vk_core, &vk_depth_att.obj);
     vk_destroy_renderer(&vk_core, &vk_rnd);
-    vk_destroy_semaphores(&vk_core, &vk_sema);
+
+    for (size_t i = 0; i < vk_framebuffers.size(); i++) {
+        vkDestroyFramebuffer(vk_core.dev, vk_framebuffers[i], 0);
+    }
+    vk_framebuffers.clear();
 
     glfwDestroyWindow(win);
 
@@ -382,11 +415,57 @@ vk_cleanup()
         return;
 
     vk_destroy_cmd_buffers(&vk_core, vk_cmd_buffers.size(), vk_cmd_buffers.data());
+    vk_cmd_buffers.clear();
+
+    vk_destroy_fences(&vk_core, vk_fences.size(), vk_fences.data());
+    vk_fences.clear();
+
+    for (int i = 0; i < FRAME_LAG; i++)
+        vk_destroy_semaphores(&vk_core, &vk_semas[i]);
+
+    for (size_t i = 0; i < vk_cmd_buffers.size(); i++)
+        vk_destroy_cmd_buffers(&vk_core, vk_cmd_buffers.size(), vk_cmd_buffers.data());
+    vk_cmd_buffers.clear();
+
     glfwTerminate();
 
     vk_cleanup_ctx(&vk_core);
 }
 
+static bool
+vk_create_sync_objects()
+{
+    VkFenceCreateInfo finfo;
+    memset(&finfo, 0, sizeof finfo);
+    finfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    finfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+    VkSemaphoreCreateInfo sinfo;
+    memset(&sinfo, 0, sizeof sinfo);
+    sinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+
+    vk_fences.resize(FRAME_LAG, VK_NULL_HANDLE);
+
+    for (int i = 0; i < FRAME_LAG; i++) {
+        if (vkCreateFence(vk_core.dev, &finfo, 0, &vk_fences[i]) != VK_SUCCESS) {
+            fprintf(stderr, "Failed to create fence: %d\n", i);
+            return false;
+        }
+
+        if (vkCreateSemaphore(vk_core.dev, &sinfo, 0, &vk_semas[i].frame_ready) != VK_SUCCESS) {
+            fprintf(stderr, "Failed to create frame_ready semaphore: %d\n", i);
+            return false;
+        }
+
+        if (vkCreateSemaphore(vk_core.dev, &sinfo, 0, &vk_semas[i].frame_done) != VK_SUCCESS) {
+            fprintf(stderr, "Failed to create frame_done semaphore: %d\n", i);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 /* glfw callbacks */
 
 static void
@@ -398,7 +477,7 @@ clb_reshape(GLFWwindow *win,
     mproj = calc_projection_matrix(45, aspect, 0.5, 1000.0f);
 
     /* FIXME near and far clipping planes */
-    vk_set_viewport(&vk_core, vk_cmd_buffers[vk_chain_idx],
+    vk_set_viewport(&vk_core, vk_cmd_buffers[0],
                     0, 0, win_w, win_h, 0.0f, 1.0f);
     win_w = width;
     win_h = height;
index a62fc18..56d4c6e 100644 (file)
--- a/src/vk.c
+++ b/src/vk.c
@@ -148,7 +148,7 @@ create_instance(bool enable_layers)
     /* VkApplicationInfo */
     memset(&app_info, 0, sizeof app_info);
     app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
-    app_info.pApplicationName = "vktest";
+    app_info.pApplicationName = "hikikornd";
     app_info.apiVersion = VK_API_VERSION_1_1;
 
     /* VkInstanceCreateInfo */
@@ -291,23 +291,25 @@ create_pipeline_cache(struct vk_ctx *ctx)
     return pcache;
 }
 
-static VkRenderPass
-create_renderpass(struct vk_ctx *ctx,
-                  uint32_t num_color_atts,
-                  struct vk_attachment *color_atts,
-                  struct vk_attachment *depth_att)
+bool
+vk_create_renderpass(struct vk_ctx *ctx,
+                     uint32_t num_color_atts,
+                     struct vk_att_props *color_props,
+                     struct vk_att_props *depth_props,
+                     VkRenderPass *renderpass)
 {
     VkAttachmentDescription *att_dsc;
     VkAttachmentReference *att_rfc;
 
     /* one subpass for the moment: */
     VkSubpassDescription subpass_dsc[1];
+    VkSubpassDependency subpass_dep;
+
     VkRenderPassCreateInfo rpass_info;
 
     int i;
     uint32_t num_atts = num_color_atts + 1;
-    bool has_layout = depth_att->props.in_layout !=
-                      VK_IMAGE_LAYOUT_UNDEFINED;
+    bool has_layout = depth_props->in_layout != VK_IMAGE_LAYOUT_UNDEFINED;
 
     att_dsc = malloc(num_atts * sizeof att_dsc[0]);
     att_rfc = malloc(num_atts * sizeof att_rfc[0]);
@@ -317,10 +319,10 @@ create_renderpass(struct vk_ctx *ctx,
 
     /* color attachments description (we can have many) */
     for (i = 0; i < num_color_atts; i++) {
-        att_dsc[i].samples = get_num_samples(color_atts[i].props.num_samples);
-        att_dsc[i].initialLayout = color_atts[i].props.in_layout;
-        att_dsc[i].finalLayout = color_atts[i].props.end_layout;
-        att_dsc[i].format = color_atts[i].props.format;
+        att_dsc[i].samples = get_num_samples(color_props[i].num_samples);
+        att_dsc[i].initialLayout = color_props[i].in_layout;
+        att_dsc[i].finalLayout = color_props[i].end_layout;
+        att_dsc[i].format = color_props[i].format;
 
         att_dsc[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
         att_dsc[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@@ -328,17 +330,17 @@ create_renderpass(struct vk_ctx *ctx,
         att_dsc[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
 
         att_rfc[i].attachment = i;
-        att_rfc[i].layout = color_atts[i].props.tiling != VK_IMAGE_TILING_OPTIMAL ?
-                            VK_IMAGE_LAYOUT_GENERAL :
-                            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        att_rfc[i].layout = color_props[i].tiling != VK_IMAGE_TILING_OPTIMAL ?
+                                                     VK_IMAGE_LAYOUT_GENERAL :
+                                                     VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
     }
 
     /* depth attachment description (we can have only one) */
-    att_dsc[num_color_atts].samples = get_num_samples(depth_att->props.num_samples);
-    att_dsc[num_color_atts].initialLayout = depth_att->props.in_layout;
-    att_dsc[num_color_atts].finalLayout = depth_att->props.end_layout;
-    att_dsc[num_color_atts].format = depth_att->props.format;
+    att_dsc[num_color_atts].samples = get_num_samples(depth_props->num_samples);
+    att_dsc[num_color_atts].initialLayout = depth_props->in_layout;
+    att_dsc[num_color_atts].finalLayout = depth_props->end_layout;
+    att_dsc[num_color_atts].format = depth_props->format;
     /* we might want to reuse the depth buffer when it's filled */
     att_dsc[num_color_atts].loadOp = has_layout ?
                                VK_ATTACHMENT_LOAD_OP_LOAD :
@@ -357,6 +359,35 @@ create_renderpass(struct vk_ctx *ctx,
     subpass_dsc[0].pColorAttachments = &att_rfc[0];
     subpass_dsc[0].pDepthStencilAttachment = &att_rfc[num_color_atts];
 
+    /* The subpasses in a render pass automatically take care of
+     * image layout transitions. These transitions are controlled
+     * by subpass dependencies, which specify memory and execution
+     * dependencies between subpasses. We have only a single subpass
+     * right now, but the operations right before and right after
+     * this subpass also count as implicit "subpasses". There are two
+     * built-in dependencies that take care of the transition at the
+     * start of the render pass and at the end of the render pass,
+     * but the former does not occur at the right time. It assumes
+     * that the transition occurs at the start of the pipeline,
+     * but we haven't acquired the image yet at that point! There are
+     * two ways to deal with this problem.
+     *
+     * We could change the waitStages for the frame_ready semaphore
+     * to VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT to ensure that the render
+     * passes don't begin until the image is available, or we can make
+     * the render pass wait for the
+     * VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT stage.
+     */
+
+    /* VkSubpassDependency */
+    memset(&subpass_dep, 0, sizeof subpass_dep);
+    subpass_dep.srcSubpass = VK_SUBPASS_EXTERNAL;
+    subpass_dep.dstSubpass = 0;
+    subpass_dep.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+    subpass_dep.srcAccessMask = 0;
+    subpass_dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    subpass_dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
     /* VkRenderPassCreateInfo */
     memset(&rpass_info, 0, sizeof rpass_info);
     rpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
@@ -364,17 +395,23 @@ create_renderpass(struct vk_ctx *ctx,
     rpass_info.pAttachments = att_dsc;
     rpass_info.subpassCount = 1;
     rpass_info.pSubpasses = subpass_dsc;
+    rpass_info.dependencyCount = 1;
+    rpass_info.pDependencies = &subpass_dep;
 
-    VkRenderPass rpass;
-    if (vkCreateRenderPass(ctx->dev, &rpass_info, 0, &rpass) != VK_SUCCESS) {
+    if (vkCreateRenderPass(ctx->dev, &rpass_info, 0, renderpass) != VK_SUCCESS) {
         fprintf(stderr, "Failed to create renderpass.\n");
-        rpass = VK_NULL_HANDLE;
+        *renderpass = VK_NULL_HANDLE;
+
+        free(att_dsc);
+        free(att_rfc);
+
+        return false;
     }
 
     free(att_dsc);
     free(att_rfc);
 
-    return rpass;
+    return true;
 }
 
 static inline VkImageType
@@ -469,42 +506,7 @@ get_pipeline_stage_flags(const VkImageLayout layout)
     return 0;
 }
 
-static bool
-create_image_view(struct vk_ctx *ctx,
-                  VkImage image,
-                  VkImageViewType view_type,
-                  VkFormat format,
-                  VkImageSubresourceRange sr,
-                  bool is_swapchain,
-                  VkImageView *image_view)
-{
-    VkImageViewCreateInfo info;
-
-    memset(&info, 0, sizeof info);
-    info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-    info.image = image;
-    info.viewType = view_type;
-    info.viewType = VK_IMAGE_VIEW_TYPE_2D;
-    info.format = format;
-    info.subresourceRange = sr;
-
-    if (is_swapchain) {
-        info.components.r = VK_COMPONENT_SWIZZLE_R;
-        info.components.g = VK_COMPONENT_SWIZZLE_G;
-        info.components.b = VK_COMPONENT_SWIZZLE_B;
-        info.components.a = VK_COMPONENT_SWIZZLE_A;
-    }
-
-    if (vkCreateImageView(ctx->dev, &info, 0, image_view) != VK_SUCCESS) {
-        fprintf(stderr, "Failed to create image view.\n");
-        image_view = VK_NULL_HANDLE;
-
-        return false;
-    }
-
-    return true;
-}
-
+#if 0
 static bool
 create_attachment_views(struct vk_ctx *ctx, int num_color_att,
                         struct vk_attachment *color_att,
@@ -513,43 +515,30 @@ create_attachment_views(struct vk_ctx *ctx, int num_color_att,
     VkImageSubresourceRange sr;
     int i;
 
-    /* depth attachments */
-    memset(&sr, 0, sizeof sr);
-    sr.aspectMask = get_aspect_from_depth_format(depth_att->props.format);
-    sr.baseMipLevel = 0;
-    sr.levelCount = 1;
-    sr.baseArrayLayer = 0;
-    sr.layerCount = 1;
-
-    create_image_view(ctx, depth_att->obj.img, VK_IMAGE_VIEW_TYPE_2D,
-                      depth_att->props.format, sr, false,
-                      &depth_att->obj.img_view);
+    vk_create_image_view(ctx, depth_att->obj.img, VK_IMAGE_VIEW_TYPE_2D,
+                         depth_att->props.format, false,
+                         &depth_att->obj.img_view);
 
     memset(&sr, 0, sizeof sr);
     sr.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 
     for (i = 0; i < num_color_att; i++) {
-        /* FIXME: in case of mipmaps */
-        sr.baseMipLevel = 0;
-        sr.levelCount = color_att[i].props.num_levels;
-        sr.baseArrayLayer = 0;
-        sr.layerCount = color_att[i].props.num_layers;
-
-        create_image_view(ctx, color_att[i].obj.img, VK_IMAGE_VIEW_TYPE_2D,
-                          color_att[i].props.format, sr, color_att[i].props.is_swapchain,
-                          &color_att[i].obj.img_view);
+        vk_create_image_view(ctx, color_att[i].obj.img, VK_IMAGE_VIEW_TYPE_2D,
+                             color_att[i].props.format,
+                             color_att[i].props.is_swapchain,
+                             &color_att[i].obj.img_view);
     }
 
     return true;
 }
 
-static void
-create_framebuffer(struct vk_ctx *ctx,
-                   int width, int height,
-                   int num_color_atts,
-                   struct vk_attachment *color_att,
-                   struct vk_attachment *depth_att,
-                   struct vk_renderer *renderer)
+static bool
+create_framebuffer_from_attachments(struct vk_ctx *ctx,
+                                    int width, int height,
+                                    int num_color_atts,
+                                    struct vk_attachment *color_att,
+                                    struct vk_attachment *depth_att,
+                                    struct vk_renderer *renderer)
 {
     VkFramebufferCreateInfo fb_info;
     VkImageView *atts;
@@ -572,22 +561,23 @@ create_framebuffer(struct vk_ctx *ctx,
     fb_info.attachmentCount = num_atts;
     fb_info.pAttachments = atts;
 
-    if (vkCreateFramebuffer(ctx->dev, &fb_info, 0, &renderer->fb) != VK_SUCCESS)
-        goto fail;
+    if (vkCreateFramebuffer(ctx->dev, &fb_info, 0, &renderer->fb) != VK_SUCCESS) {
+        fprintf(stderr, "Failed to create framebuffer.\n");
+        free(atts);
+        renderer->fb = VK_NULL_HANDLE;
 
-    free(atts);
-    return;
+        return false;
+    }
 
-fail:
-    fprintf(stderr, "Failed to create framebuffer.\n");
     free(atts);
-    renderer->fb = VK_NULL_HANDLE;
+    return true;
 }
+#endif
 
 static VkShaderModule
 create_shader_module(struct vk_ctx *ctx,
-             const char *src,
-             unsigned int size)
+                     const char *src,
+                     unsigned int size)
 {
     VkShaderModuleCreateInfo sm_info;
     VkShaderModule sm;
@@ -807,7 +797,8 @@ create_graphics_pipeline(struct vk_ctx *ctx,
     layout_info.pushConstantRangeCount = 1;
     layout_info.pPushConstantRanges = pc_range;
 
-    if (vkCreatePipelineLayout(ctx->dev, &layout_info, 0, &pipeline_layout) != VK_SUCCESS) {
+    if (vkCreatePipelineLayout(ctx->dev, &layout_info,
+                               0, &pipeline_layout) != VK_SUCCESS) {
         fprintf(stderr, "Failed to create pipeline layout\n");
         renderer->pipeline = VK_NULL_HANDLE;
         goto fail;
@@ -1074,7 +1065,8 @@ sc_validate_surface(struct vk_ctx *ctx,
         return false;
     }
 
-    if(vkGetPhysicalDeviceSurfaceSupportKHR(ctx->pdev, ctx->qfam_idx, surf, &supported) != VK_SUCCESS) {
+    if(vkGetPhysicalDeviceSurfaceSupportKHR(ctx->pdev, ctx->qfam_idx,
+                                            surf, &supported) != VK_SUCCESS) {
         fprintf(stderr, "Failed to validate surface.\n");
         return false;
     }
@@ -1095,7 +1087,9 @@ sc_select_format(struct vk_ctx *ctx,
     VkSurfaceFormatKHR *formats;
     uint32_t num_formats;
 
-    if (vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->pdev, surf, &num_formats, 0) != VK_SUCCESS) {
+    if (vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->pdev,
+                                             surf,
+                                             &num_formats, 0) != VK_SUCCESS) {
         fprintf(stderr, "Failed to get the number of surface formats.\n");
         return false;
     }
@@ -1107,7 +1101,8 @@ sc_select_format(struct vk_ctx *ctx,
 
     formats = malloc(num_formats * sizeof *formats);
 
-    if (vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->pdev, surf, &num_formats, formats) != VK_SUCCESS) {
+    if (vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->pdev, surf, &num_formats,
+                                             formats) != VK_SUCCESS) {
         fprintf(stderr, "Failed to get the supported surface formats.\n");
         return false;
     }
@@ -1134,17 +1129,21 @@ sc_select_supported_present_modes(struct vk_ctx *ctx,
     uint32_t num_present_modes;
 
     /* find supported present modes */
-    if (vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->pdev, surf, &num_present_modes, 0) != VK_SUCCESS || !num_present_modes) {
+    if (vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->pdev, surf,
+                                                  &num_present_modes, 0) != VK_SUCCESS || !num_present_modes) {
         fprintf(stderr, "Failed to get the number of the supported presentation modes.\n");
         return false;
     }
 
     present_modes = malloc(num_present_modes * sizeof *present_modes);
-    if (vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->pdev, surf, &num_present_modes, present_modes) != VK_SUCCESS) {
+    if (vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->pdev, surf,
+                                                  &num_present_modes,
+                                                  present_modes) != VK_SUCCESS) {
         fprintf(stderr, "Failed to get the number of supported presentation modes.\n");
         return false;
     }
-    if (vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->pdev, surf, &num_present_modes,
+    if (vkGetPhysicalDeviceSurfacePresentModesKHR(ctx->pdev, surf,
+                                                  &num_present_modes,
                                                   present_modes) != VK_SUCCESS) {
         fprintf(stderr, "Failed to get the supported presentation modes.\n");
         return false;
@@ -1276,15 +1275,20 @@ vk_cleanup_ctx(struct vk_ctx *ctx)
 }
 
 bool
-vk_create_image(struct vk_ctx *ctx,
-                struct vk_att_props *props,
-                struct vk_image_obj *img)
+vk_create_ext_image(struct vk_ctx *ctx,
+                    struct vk_att_props *props,
+                    struct vk_image_obj *img)
 {
+    VkExternalMemoryImageCreateInfo ext_img_info;
     VkImageCreateInfo img_info;
 
+    memset(&ext_img_info, 0, sizeof ext_img_info);
+    ext_img_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+    ext_img_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
     memset(&img_info, 0, sizeof img_info);
     img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-    img_info.pNext = 0; /* do something if external */
+    img_info.pNext = &ext_img_info;
     img_info.imageType = get_image_type(props->h, props->depth);
     img_info.format = props->format;
     img_info.extent.width = props->w;
@@ -1295,15 +1299,20 @@ vk_create_image(struct vk_ctx *ctx,
                            props->num_layers : VK_SAMPLE_COUNT_1_BIT;
     img_info.samples = get_num_samples(props->num_samples);
     img_info.tiling = props->tiling;
-    img_info.usage = props->usage ?
-                     props->usage : VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
-    img_info.initialLayout = props->in_layout;
+    img_info.usage = props->usage;
+    img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
     img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    /* issue 17 of EXT_external_objects
+     * Required in OpenGL implementations that support
+     * ARB_texture_view, OES_texture_view, EXT_texture_view,
+     * or OpenGL 4.3 and above.
+     */
+    img_info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
 
     if (vkCreateImage(ctx->dev, &img_info, 0, &img->img) != VK_SUCCESS)
         goto fail;
 
-    if(!alloc_image_memory(ctx, props->need_export, img))
+    if(!alloc_image_memory(ctx, true, img))
         goto fail;
 
     return true;
@@ -1316,57 +1325,35 @@ fail:
     return false;
 }
 
-void
-vk_destroy_image(struct vk_ctx *ctx, struct vk_image_obj *img_obj)
-{
-    if (img_obj->img != VK_NULL_HANDLE) {
-        vkDestroyImage(ctx->dev, img_obj->img, 0);
-        img_obj->img = VK_NULL_HANDLE;
-    }
-
-    if (img_obj->mobj.mem != VK_NULL_HANDLE) {
-        vkFreeMemory(ctx->dev, img_obj->mobj.mem, 0);
-        img_obj->mobj.mem = VK_NULL_HANDLE;
-    }
-}
-
 bool
-vk_create_ext_image(struct vk_ctx *ctx,
-                    struct vk_att_props *props, struct vk_image_obj *img)
+vk_create_image(struct vk_ctx *ctx,
+                struct vk_att_props *props,
+                struct vk_image_obj *img)
 {
-    VkExternalMemoryImageCreateInfo ext_img_info;
     VkImageCreateInfo img_info;
 
-    memset(&ext_img_info, 0, sizeof ext_img_info);
-    ext_img_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
-    ext_img_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
-
     memset(&img_info, 0, sizeof img_info);
     img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-    img_info.pNext = &ext_img_info;
+    img_info.pNext = 0; /* do something if external */
     img_info.imageType = get_image_type(props->h, props->depth);
     img_info.format = props->format;
     img_info.extent.width = props->w;
     img_info.extent.height = props->h;
     img_info.extent.depth = props->depth;
     img_info.mipLevels = props->num_levels ? props->num_levels : 1;
-    img_info.arrayLayers = props->num_layers ? props->num_layers : VK_SAMPLE_COUNT_1_BIT;
+    img_info.arrayLayers = props->num_layers ?
+                           props->num_layers : VK_SAMPLE_COUNT_1_BIT;
     img_info.samples = get_num_samples(props->num_samples);
     img_info.tiling = props->tiling;
-    img_info.usage = props->usage;
-    img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    img_info.usage = props->usage ?
+                     props->usage : VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+    img_info.initialLayout = props->in_layout;
     img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-    /* issue 17 of EXT_external_objects
-     * Required in OpenGL implementations that support
-     * ARB_texture_view, OES_texture_view, EXT_texture_view,
-     * or OpenGL 4.3 and above.
-     */
-    img_info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
 
     if (vkCreateImage(ctx->dev, &img_info, 0, &img->img) != VK_SUCCESS)
         goto fail;
 
-    if(!alloc_image_memory(ctx, true, img))
+    if(!alloc_image_memory(ctx, props->need_export, img))
         goto fail;
 
     return true;
@@ -1380,6 +1367,63 @@ fail:
 }
 
 bool
+vk_create_image_view(struct vk_ctx *ctx,
+                     VkImage image,
+                     VkImageViewType view_type,
+                     VkFormat format,
+                     bool is_swapchain,
+                     VkImageView *image_view)
+{
+    VkImageViewCreateInfo info;
+    VkImageSubresourceRange sr;
+    VkImageAspectFlagBits aspect = get_aspect_from_depth_format(format);
+
+    /* VkSubresourceRange */
+    memset(&sr, 0, sizeof sr);
+    sr.aspectMask = aspect ? aspect : VK_IMAGE_ASPECT_COLOR_BIT;
+    sr.levelCount = 1;
+    sr.layerCount = 1;
+
+    memset(&info, 0, sizeof info);
+    info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    info.image = image;
+    info.viewType = view_type;
+    info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    info.format = format;
+    info.subresourceRange = sr;
+
+    if (is_swapchain) {
+        info.components.r = VK_COMPONENT_SWIZZLE_R;
+        info.components.g = VK_COMPONENT_SWIZZLE_G;
+        info.components.b = VK_COMPONENT_SWIZZLE_B;
+        info.components.a = VK_COMPONENT_SWIZZLE_A;
+    }
+
+    if (vkCreateImageView(ctx->dev, &info, 0, image_view) != VK_SUCCESS) {
+        fprintf(stderr, "Failed to create image view.\n");
+        image_view = VK_NULL_HANDLE;
+
+        return false;
+    }
+
+    return true;
+}
+
+void
+vk_destroy_image(struct vk_ctx *ctx, struct vk_image_obj *img_obj)
+{
+    if (img_obj->img != VK_NULL_HANDLE) {
+        vkDestroyImage(ctx->dev, img_obj->img, 0);
+        img_obj->img = VK_NULL_HANDLE;
+    }
+
+    if (img_obj->mobj.mem != VK_NULL_HANDLE) {
+        vkFreeMemory(ctx->dev, img_obj->mobj.mem, 0);
+        img_obj->mobj.mem = VK_NULL_HANDLE;
+    }
+}
+
+bool
 vk_fill_image_props(struct vk_ctx *ctx,
                     uint32_t w,
                     uint32_t h,
@@ -1420,6 +1464,76 @@ vk_fill_image_props(struct vk_ctx *ctx,
     return true;
 }
 
+struct vk_attachment
+vk_create_attachment_from_obj(struct vk_image_obj *obj,
+                              struct vk_att_props *props)
+{
+    struct vk_attachment att;
+
+    att.obj = *obj;
+    att.props = *props;
+
+    return att;
+}
+
+struct vk_attachment
+vk_create_attachment(VkImage image,
+                     VkImageView view,
+                     struct vk_att_props *props)
+{
+    struct vk_attachment att;
+
+    att.obj.img = image;
+    att.obj.img_view = view;
+    att.props = *props;
+
+    memset(&att.obj.mobj, 0, sizeof att.obj.mobj);
+
+    return att;
+}
+
+bool
+vk_create_framebuffer(struct vk_ctx *ctx,
+                      int width, int height,
+                      int num_color_atts,
+                      VkImageView *color_views,
+                      VkImageView *depth_view,
+                      VkRenderPass rpass,
+                      VkFramebuffer *fb)
+{
+    VkFramebufferCreateInfo fb_info;
+    VkImageView *atts;
+    int i;
+    int num_atts = num_color_atts + 1;
+
+    atts = malloc(num_atts * sizeof atts[0]);
+    for (i = 0; i < num_color_atts; i++) {
+        atts[i] = color_views[i];
+    }
+    atts[num_color_atts] = *depth_view;
+
+    memset(&fb_info, 0, sizeof fb_info);
+    fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+    fb_info.renderPass = rpass;
+    fb_info.width = width;
+    fb_info.height = height;
+    fb_info.layers = 1;
+    fb_info.attachmentCount = num_atts;
+    fb_info.pAttachments = atts;
+
+    if (vkCreateFramebuffer(ctx->dev, &fb_info, 0, fb) != VK_SUCCESS) {
+        fprintf(stderr, "Failed to create framebuffer.\n");
+
+        fb = VK_NULL_HANDLE;
+        free(atts);
+
+        return false;
+    }
+
+    free(atts);
+    return true;
+}
+
 bool
 vk_create_renderer(struct vk_ctx *ctx,
                    const char *vs_src,
@@ -1431,41 +1545,37 @@ vk_create_renderer(struct vk_ctx *ctx,
                    bool enable_depth,
                    bool enable_stencil,
                    int num_color_att,
-                   struct vk_attachment *color_att,
-                   struct vk_attachment *depth_att,
+                   struct vk_att_props *color_props,
+                   struct vk_att_props *depth_props,
                    struct vk_vertex_info *vert_info,
                    struct vk_renderer *renderer)
 {
-    memset(&renderer->vertex_info, 0, sizeof renderer->vertex_info);
-    if (vert_info)
-        renderer->vertex_info = *vert_info;
-
     /* create image views for each attachment */
+#if 0
     if (!create_attachment_views(ctx, num_color_att, color_att, depth_att))
         goto fail;
+#endif
 
-    renderer->renderpass = create_renderpass(ctx,
-                                             num_color_att,
-                                             color_att,
-                                             depth_att);
-
-    if (renderer->renderpass == VK_NULL_HANDLE)
+    VkRenderPass rpass;
+    if (!vk_create_renderpass(ctx, num_color_att, color_props, depth_props,
+                              &rpass)) {
         goto fail;
+    }
+    renderer->renderpass = rpass;
 
-    create_framebuffer(ctx,
-                       w, h,
-                       num_color_att, color_att,
-                       depth_att, renderer);
-    if (renderer->fb == VK_NULL_HANDLE)
+    VkShaderModule vs = create_shader_module(ctx, vs_src, vs_size);
+    if (vs == VK_NULL_HANDLE)
         goto fail;
+    renderer->vs = vs;
 
-    renderer->vs = create_shader_module(ctx, vs_src, vs_size);
-    if (renderer->vs == VK_NULL_HANDLE)
+    VkShaderModule fs = create_shader_module(ctx, fs_src, fs_size);
+    if (fs == VK_NULL_HANDLE)
         goto fail;
+    renderer->fs = fs;
+
+    if (vert_info)
+        renderer->vertex_info = *vert_info;
 
-    renderer->fs = create_shader_module(ctx, fs_src, fs_size);
-    if (renderer->fs == VK_NULL_HANDLE)
-        goto fail;
 
     /* FIXME this is only for graphics atm */
     if(!create_graphics_pipeline(ctx, w, h,
@@ -1475,9 +1585,6 @@ vk_create_renderer(struct vk_ctx *ctx,
                                  enable_stencil, renderer))
         goto fail;
 
-    if (renderer->pipeline == VK_NULL_HANDLE)
-        goto fail;
-
     return true;
 
 fail:
@@ -1505,11 +1612,6 @@ vk_destroy_renderer(struct vk_ctx *ctx,
         renderer->fs = VK_NULL_HANDLE;
     }
 
-    if (renderer->fb != VK_NULL_HANDLE) {
-        vkDestroyFramebuffer(ctx->dev, renderer->fb, 0);
-        renderer->fb = VK_NULL_HANDLE;
-    }
-
     if (renderer->pipeline != VK_NULL_HANDLE) {
         vkDestroyPipeline(ctx->dev, renderer->pipeline, 0);
         renderer->pipeline = VK_NULL_HANDLE;
@@ -1644,7 +1746,6 @@ vk_create_fence(struct vk_ctx *ctx,
 
     memset(&finfo, 0, sizeof finfo);
     finfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
-    finfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
 
     if (vkCreateFence(ctx->dev, &finfo, 0, fence) != VK_SUCCESS) {
         fprintf(stderr, "Failed to create fence.\n");
@@ -1667,7 +1768,8 @@ vk_create_cmd_buffer(struct vk_ctx *ctx)
     alloc_info.commandBufferCount = 1;
     alloc_info.commandPool = ctx->cmd_pool;
 
-    if (vkAllocateCommandBuffers(ctx->dev, &alloc_info, &cmd_buf) != VK_SUCCESS)
+    if (vkAllocateCommandBuffers(ctx->dev, &alloc_info,
+                                 &cmd_buf) != VK_SUCCESS)
         return 0;
 
     return cmd_buf;
@@ -1680,6 +1782,7 @@ vk_record_cmd_buffer(struct vk_ctx *ctx,
                      struct vk_buf *vbo,
                      uint32_t vk_fb_color_count,
                      float *vk_fb_color,
+                     VkFramebuffer fb,
                      uint32_t num_atts,
                      struct vk_attachment *atts,
                      float x, float y,
@@ -1696,14 +1799,9 @@ vk_record_cmd_buffer(struct vk_ctx *ctx,
 
     assert(vk_fb_color_count == 4);
 
-    /* if cmd_buf is null create it */
     if (!cmd_buf) {
-        if ((cmd_buf = vk_create_cmd_buffer(ctx)) ==
-                VK_NULL_HANDLE) {
-            fprintf(stderr, "Failed to create command buffer.\n");
-            return false;
-        }
-        create_cmd_buf = true;
+        fprintf(stderr, "Can't record an empty command buffer.\n");
+        return false;
     }
 
     /* VkCommandBufferBeginInfo */
@@ -1735,7 +1833,7 @@ vk_record_cmd_buffer(struct vk_ctx *ctx,
     memset(&rp_begin_info, 0, sizeof rp_begin_info);
     rp_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
     rp_begin_info.renderPass = renderer->renderpass;
-    rp_begin_info.framebuffer = renderer->fb;
+    rp_begin_info.framebuffer = fb;
     rp_begin_info.renderArea = rp_area;
     rp_begin_info.clearValueCount = num_atts;
     rp_begin_info.pClearValues = clear_values;
@@ -1768,7 +1866,8 @@ vk_record_cmd_buffer(struct vk_ctx *ctx,
         vkCmdBindVertexBuffers(cmd_buf, 0, 1, &vbo->buf, offsets);
     }
 
-    vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->pipeline);
+    vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                      renderer->pipeline);
 
     num_vertices = vbo ? renderer->vertex_info.num_verts : 4;
     vkCmdDraw(cmd_buf, num_vertices, 1, 0, 0);
@@ -1833,8 +1932,6 @@ vk_draw(struct vk_ctx *ctx,
     VkSubmitInfo submit_info;
     VkPipelineStageFlagBits stage_flags;
 
-    stage_flags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
-
     /* VkSubmitInfo */
     memset(&submit_info, 0, sizeof submit_info);
     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@@ -1846,6 +1943,27 @@ vk_draw(struct vk_ctx *ctx,
         assert(semaphores->frame_ready);
         assert(semaphores->frame_done);
 
+        /* The subpasses in a render pass automatically take care of
+         * image layout transitions. These transitions are controlled
+         * by subpass dependencies, which specify memory and execution
+         * dependencies between subpasses. We have only a single subpass
+         * right now, but the operations right before and right after
+         * this subpass also count as implicit "subpasses". There are two
+         * built-in dependencies that take care of the transition at the
+         * start of the render pass and at the end of the render pass,
+         * but the former does not occur at the right time. It assumes
+         * that the transition occurs at the start of the pipeline,
+         * but we haven't acquired the image yet at that point! There are
+         * two ways to deal with this problem.
+         *
+         * We could change the waitStages for the frame_ready semaphore
+         * to VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT to ensure that the render
+         * passes don't begin until the image is available, or we can make
+         * the render pass wait for the
+         * VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT stage.
+         */
+        stage_flags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+
         submit_info.pWaitDstStageMask = &stage_flags;
         submit_info.waitSemaphoreCount = 1;
         submit_info.pWaitSemaphores = &semaphores->frame_done;
@@ -1855,7 +1973,8 @@ vk_draw(struct vk_ctx *ctx,
     }
 
 
-    if (vkQueueSubmit(ctx->queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
+    if (vkQueueSubmit(ctx->queue, 1, &submit_info,
+                      VK_NULL_HANDLE) != VK_SUCCESS) {
         fprintf(stderr, "Failed to submit queue.\n");
         abort();
     }
@@ -1873,6 +1992,7 @@ vk_clear_color(struct vk_ctx *ctx,
                struct vk_renderer *renderer,
                float *vk_fb_color,
                uint32_t vk_fb_color_count,
+               VkFramebuffer fb,
                struct vk_semaphores *semaphores,
                bool has_wait, bool has_signal,
                struct vk_attachment *attachments,
@@ -1917,7 +2037,7 @@ vk_clear_color(struct vk_ctx *ctx,
     memset(&rp_begin_info, 0, sizeof rp_begin_info);
     rp_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
     rp_begin_info.renderPass = renderer->renderpass;
-    rp_begin_info.framebuffer = renderer->fb;
+    rp_begin_info.framebuffer = fb;
     rp_begin_info.renderArea = rp_area;
     rp_begin_info.clearValueCount = 2;
     rp_begin_info.pClearValues = clear_values;
@@ -1977,7 +2097,8 @@ vk_clear_color(struct vk_ctx *ctx,
     vkCmdSetViewport(cmd_buf, 0, 1, &viewport);
     vkCmdSetScissor(cmd_buf, 0, 1, &scissor);
 
-    vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->pipeline);
+    vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                      renderer->pipeline);
 
     vkCmdEndRenderPass(cmd_buf);
 
@@ -1993,7 +2114,7 @@ vk_clear_color(struct vk_ctx *ctx,
             barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 
             bool is_depth =
-                get_aspect_from_depth_format(att->props.format) != VK_NULL_HANDLE;
+                get_aspect_from_depth_format(att->props.format) != 0;
 
             barrier->oldLayout = is_depth ?
                 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
@@ -2025,7 +2146,8 @@ vk_clear_color(struct vk_ctx *ctx,
 
     vkEndCommandBuffer(cmd_buf);
 
-    if (vkQueueSubmit(ctx->queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
+    if (vkQueueSubmit(ctx->queue, 1, &submit_info,
+                      VK_NULL_HANDLE) != VK_SUCCESS) {
         fprintf(stderr, "Failed to submit queue.\n");
     }
 
@@ -2071,9 +2193,7 @@ vk_create_swapchain(struct vk_ctx *ctx,
     VkSurfaceCapabilitiesKHR surf_cap;
     VkSwapchainCreateInfoKHR s_info;
     VkExtent2D extent;
-    VkImageSubresourceRange sr;
-    VkImage *s_images;
-    int i;
+    int i, j;
 
     if (!sc_validate_surface(ctx, surf)) {
         fprintf(stderr, "Failed to validate surface!\n");
@@ -2144,62 +2264,47 @@ vk_create_swapchain(struct vk_ctx *ctx,
     /* get the number of swapchain images and the swapchain images
      * and store the new swapchain images
      */
-    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain, &swapchain->num_atts, 0);
-    printf("number of swapchain images: %d\n", swapchain->num_atts);
+    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain,
+                            &swapchain->num_images, 0);
+    printf("number of swapchain images: %d\n", swapchain->num_images);
 
-    /* create images */
-    s_images = malloc(swapchain->num_atts * sizeof(VkImage));
-    if (!s_images) {
-        fprintf(stderr, "Failed to allocate swapchain images.\n");
-        return false;
-    }
-
-    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain, &swapchain->num_atts, s_images);
+    /* get the images */
+    swapchain->images = (VkImage*)alloca(swapchain->num_images * sizeof(VkImage));
+    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain,
+                            &swapchain->num_images,
+                            swapchain->images);
 
+    /* create attributes from those images */
     swapchain->image_fmt = s_info.imageFormat;
-    swapchain->atts = malloc(swapchain->num_atts * sizeof swapchain->atts[0]);
-    if (!swapchain->atts) {
-        fprintf(stderr, "Failed to allocate swapchain images.\n");
-        goto fail;
-    }
-    memset(swapchain->atts, 0, sizeof swapchain->atts[0]);
 
-    memset(&sr, 0, sizeof sr);
-    sr.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    sr.levelCount = 1;
-    sr.layerCount = 1;
-
-    for (i = 0; i < swapchain->num_atts; i++) {
-        /* we store the image */
-        swapchain->atts[i].obj.img = s_images[i];
-
-        /* filling attachment properties here where that info
-         * is available */
-
-        vk_fill_image_props(ctx, width, height, 1,
-                            1, 1, 1,
-                            s_info.imageFormat,
-                            0,
-                            VK_IMAGE_LAYOUT_UNDEFINED,
-                            VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
-                            true, false, false,
-                            &swapchain->atts[i].props);
-        swapchain->atts[i].props.usage = s_info.imageUsage;
-
-        if (!create_image_view(ctx, s_images[i],
-                               VK_IMAGE_VIEW_TYPE_2D,
-                               s_info.imageFormat, sr, true,
-                               &swapchain->atts[i].obj.img_view)) {
+    vk_fill_image_props(ctx, width, height, 1,
+                        1, 1, 1,
+                        s_info.imageFormat,
+                        0,
+                        VK_IMAGE_LAYOUT_UNDEFINED,
+                        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+                        true, false, false,
+                        &swapchain->img_props);
+
+    swapchain->img_props.usage = s_info.imageUsage;
+    swapchain->views = malloc(swapchain->num_images * sizeof(VkImageView));
+
+    for (i = 0; i < swapchain->num_images; i++) {
+        if (!vk_create_image_view(ctx, swapchain->images[i],
+                                  VK_IMAGE_VIEW_TYPE_2D,
+                                  s_info.imageFormat, true,
+                                  &swapchain->views[i])) {
             fprintf(stderr, "Failed to create image view for image: %d\n", i);
             goto fail;
         }
     }
 
-    free(s_images);
     return true;
 
 fail:
-    free(s_images);
+    for (j = 0; j < i; j++) {
+        vkDestroyImageView(ctx->dev, swapchain->views[j], 0);
+    }
     return false;
 }
 
@@ -2207,6 +2312,10 @@ void
 vk_destroy_swapchain(struct vk_ctx *ctx,
                      struct vk_swapchain *swapchain)
 {
+    int i;
+    for (i = 0; i < swapchain->num_images; i++) {
+        vkDestroyImageView(ctx->dev, swapchain->views[i], 0);
+    }
     vkDestroySwapchainKHR(ctx->dev, swapchain->swapchain, 0);
 }
 
@@ -2227,6 +2336,8 @@ vk_queue_present(struct vk_swapchain *swapchain,
     pinfo.pWaitSemaphores = &wait_sema;
     pinfo.waitSemaphoreCount = 1;
 
+    /* FIXME: add code for VK_KHR_incremental_present_enabled!! */
+
     if (vkQueuePresentKHR(queue, &pinfo) != VK_SUCCESS) {
         fprintf(stderr, "Failed to present queue.\n");
         return false;
@@ -2356,10 +2467,11 @@ void
 vk_destroy_semaphores(struct vk_ctx *ctx,
                       struct vk_semaphores *semaphores)
 {
-    if (semaphores->frame_ready)
-        vkDestroySemaphore(ctx->dev, semaphores->frame_ready, 0);
-    if (semaphores->frame_done)
-        vkDestroySemaphore(ctx->dev, semaphores->frame_done, 0);
+    vkDestroySemaphore(ctx->dev, semaphores->frame_ready, 0);
+    semaphores->frame_ready = VK_NULL_HANDLE;
+
+    vkDestroySemaphore(ctx->dev, semaphores->frame_done, 0);
+    semaphores->frame_done = VK_NULL_HANDLE;
 }
 
 bool
index 5adfbd7..58a88ef 100644 (file)
--- a/src/vk.h
+++ b/src/vk.h
@@ -22,19 +22,6 @@ struct vk_ctx
     uint8_t driverUUID[VK_UUID_SIZE];
 };
 
-struct vk_swapchain
-{
-    VkSwapchainKHR swapchain;
-    VkSurfaceFormatKHR surf_fmt;
-
-    /* image properties */
-    VkFormat image_fmt;
-    VkExtent2D extent2d;
-
-    uint32_t num_atts;
-    struct vk_attachment *atts;
-};
-
 struct vk_att_props
 {
     uint32_t w;
@@ -57,6 +44,25 @@ struct vk_att_props
     bool need_export;
 };
 
+struct vk_swapchain
+{
+    VkSwapchainKHR swapchain;
+    VkSurfaceFormatKHR surf_fmt;
+
+    /* image properties */
+    /* FIXME: do I really need those 2?*/
+    VkFormat image_fmt;
+    VkExtent2D extent2d;
+
+    struct vk_att_props img_props;
+
+    uint32_t num_images;
+    VkImage *images;
+    VkImageView *views;
+};
+
+/* for allocated images */
+
 struct vk_mem_obj {
     VkDeviceMemory mem;
     VkDeviceSize mem_sz;
@@ -97,7 +103,6 @@ struct vk_renderer
     VkRenderPass renderpass;
     VkShaderModule vs;
     VkShaderModule fs;
-    VkFramebuffer fb;
 
     struct vk_vertex_info vertex_info;
 };
@@ -130,6 +135,10 @@ bool vk_init_ctx_for_rendering(struct vk_ctx *ctx,
 void vk_cleanup_ctx(struct vk_ctx *ctx);
 
 /* images */
+bool
+vk_create_ext_image(struct vk_ctx *ctx,
+                    struct vk_att_props *props,
+                    struct vk_image_obj *img);
 
 bool
 vk_create_image(struct vk_ctx *ctx,
@@ -140,6 +149,14 @@ vk_destroy_image(struct vk_ctx *ctx,
                  struct vk_image_obj *img_obj);
 
 bool
+vk_create_image_view(struct vk_ctx *ctx,
+                     VkImage image,
+                     VkImageViewType view_type,
+                     VkFormat format,
+                     bool is_swapchain,
+                     VkImageView *image_view);
+
+bool
 vk_fill_image_props(struct vk_ctx *ctx,
                     uint32_t w, uint32_t h,
                     uint32_t depth,
@@ -155,6 +172,15 @@ vk_fill_image_props(struct vk_ctx *ctx,
                     bool need_export,
                     struct vk_att_props *props);
 
+struct vk_attachment
+vk_create_attachment_from_obj(struct vk_image_obj *obj,
+                              struct vk_att_props *props);
+
+struct vk_attachment
+vk_create_attachment(VkImage image,
+                     VkImageView view,
+                     struct vk_att_props *props);
+
 /* buffers */
 
 bool
@@ -196,6 +222,21 @@ vk_destroy_fences(struct vk_ctx *ctc,
                   VkFence *fences);
 
 /* renderer */
+bool
+vk_create_framebuffer(struct vk_ctx *ctx,
+                      int width, int height,
+                      int num_color_atts,
+                      VkImageView *color_views,
+                      VkImageView *depth_view,
+                      VkRenderPass rb,
+                      VkFramebuffer *fb);
+
+bool
+vk_create_renderpass(struct vk_ctx *ctx,
+                     uint32_t num_color_atts,
+                     struct vk_att_props *color_props,
+                     struct vk_att_props *depth_props,
+                     VkRenderPass *renderpass);
 
 bool
 vk_create_renderer(struct vk_ctx *ctx,
@@ -208,8 +249,8 @@ vk_create_renderer(struct vk_ctx *ctx,
                    bool enable_depth,
                    bool enable_stencil,
                    int num_color_att,
-                   struct vk_attachment *color_att,
-                   struct vk_attachment *depth_att,
+                   struct vk_att_props *color_props,
+                   struct vk_att_props *depth_props,
                    struct vk_vertex_info *vert_info,
                    struct vk_renderer *renderer);
 
@@ -232,6 +273,7 @@ vk_record_cmd_buffer(struct vk_ctx *ctx,
                      struct vk_buf *vbo,
                      uint32_t vk_fb_color_count,
                      float *vk_fb_color,
+                     VkFramebuffer fb,
                      uint32_t num_atts,
                      struct vk_attachment *atts,
                      float x, float y,
@@ -256,6 +298,7 @@ vk_clear_color(struct vk_ctx *ctx,
                struct vk_renderer *renderer,
                float *vk_fb_color,
                uint32_t vk_fb_color_count,
+               VkFramebuffer fb,
                struct vk_semaphores *semaphores,
                bool has_wait, bool has_signal,
                struct vk_attachment *attachments,