working swapchain
authorEleni Maria Stea <estea@igalia.com>
Sat, 18 Sep 2021 11:04:11 +0000 (14:04 +0300)
committerEleni Maria Stea <estea@igalia.com>
Sat, 18 Sep 2021 11:04:11 +0000 (14:04 +0300)
+a hundred other fixes and changes

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

index 6309b41..eb1cf18 100644 (file)
@@ -20,31 +20,34 @@ clb_reshape(GLFWwindow *win, int width, int height);
 /* static functions */
 
 static bool
-init();
+init(void);
 
 static void
-cleanup();
+cleanup(void);
 
 static void
-display();
-
+display(void);
 
 /* static variables */
 
 static GLFWwindow *win;
+static bool redraw_pending;
+
+static bool vk_enable_layers;
 
 static int win_w = 800;
 static int win_h = 600;
 
 static struct vk_ctx vk_core;
 static VkSurfaceKHR vk_surf;
-static int vsz, fsz;
 static struct vk_renderer vk_rnd;
 static struct vk_swapchain vk_chain;
 static struct vk_semaphores vk_sema;
-static struct vk_image_attachment vk_color_att;
-static struct vk_image_attachment vk_depth_att;
-static struct vk_image_props vk_depth_att_props;
+static struct vk_attachment vk_depth_att;
+static float vk_fb_color[4] = { 0.0, 0.0, 0.5, 1.0 };
+
+/* make array later if many cmd buffers */
+static VkCommandBuffer vk_cmd_buf;
 
 /* empty for as long as we hardcode the vertices in the vertex shader */
 static struct vk_vertex_info vk_vert_info;
@@ -63,10 +66,14 @@ int main(int argc, char** argv)
     clb_reshape(win, win_w, win_h);
 
     /* event loop */
+    redraw_pending = true;
 
     while(!glfwWindowShouldClose(win)) {
-        display();
-        glfwPollEvents();
+        glfwWaitEvents();
+        if (redraw_pending) {
+            redraw_pending = false;
+            display();
+        }
     }
 
     return 0;
@@ -75,9 +82,11 @@ int main(int argc, char** argv)
 /* static functions */
 
 static bool
-init()
+init(void)
 {
-    char *vsdr, *fsdr;
+    char *vsdr = 0;
+    char *fsdr = 0;
+    int vsz, fsz;
 
     /* initialize GLFW */
 
@@ -101,9 +110,11 @@ init()
         return false;
     }
 
+    glfwSetKeyCallback(win, clb_key);
+
     /* initialize Vulkan context (instance) */
 
-    if (!vk_init_ctx_for_rendering(&vk_core)) {
+    if (!vk_init_ctx_for_rendering(&vk_core, true, vk_enable_layers)) {
         fprintf(stderr, "Failed to initialize Vulkan context.\n");
         return false;
     }
@@ -114,16 +125,25 @@ init()
     if (glfwCreateWindowSurface(vk_core.inst, win, 0, &vk_surf)
             != VK_SUCCESS) {
         fprintf(stderr, "Failed to create XCB surface.\n");
-        glfwTerminate();
-
         return false;
     }
 
     /* create semaphores */
-    vk_create_semaphores(&vk_core, false, &vk_sema);
+    if (!vk_create_semaphores(&vk_core, false, &vk_sema)) {
+        fprintf(stderr, "No semaphores were created.\n");
+        goto fail;
+    }
 
     /* create swapchain */
-    vk_create_swapchain(&vk_core, win_w, win_h, false, vk_surf, VK_NULL_HANDLE, &vk_chain);
+    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;
+    }
+
+    if (vk_chain.swapchain == VK_NULL_HANDLE) {
+        fprintf(stderr, "Invalid swapchain handle.\n");
+        goto fail;
+    }
 
     /* create shaders */
     vsdr = sdr_load("data/main.vert.spv", &vsz);
@@ -138,27 +158,48 @@ init()
                              VK_IMAGE_TILING_OPTIMAL,
                              VK_IMAGE_LAYOUT_UNDEFINED,
                              VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
-                             false, &vk_depth_att_props)) {
+                             false, true, false,
+                             &vk_depth_att.props)) {
         fprintf(stderr, "Unsupported depth image properties\n");
         return false;
     }
-    if (!vk_create_image(&vk_core, &vk_depth_att_props, &vk_depth_att.obj)) {
+    if (!vk_create_image(&vk_core, &vk_depth_att.props, &vk_depth_att.obj)) {
         fprintf(stderr, "Failed to create depth attachment.\n");
-        return false;
+        goto fail;
     }
 
     /* create renderer */
-    if (!vk_create_renderer(&vk_core, vsdr, vsz, fsdr, fsz,
-                            false, false, 0, &vk_depth_att,
+    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)) {
-        fprintf(stderr, "Failed to create renderer.\n");
-        return false;
+        goto fail;
+    }
+
+    /* create cmd buffer */
+    if ((vk_cmd_buf = vk_create_cmd_buffer(&vk_core)) == VK_NULL_HANDLE) {
+        fprintf(stderr, "Failed to create command buffer.\n");
+        goto fail;
+    }
+
+    /* record cmd buffer */
+    if (!vk_record_cmd_buffer(&vk_core, vk_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;
     }
 
+    free(vsdr);
+    free(fsdr);
+
     /* set GLFW callbacks */
 
-    glfwSetKeyCallback(win, clb_key);
-    glfwSetWindowSizeCallback(win, clb_reshape);
+    /* glfwSetWindowSizeCallback(win, clb_reshape); */
 
     /*
     glfwSetCursorPosCallback(win, clb_motion);
@@ -166,19 +207,75 @@ init()
     */
 
     return true;
+
+fail:
+    free(vsdr);
+    free(fsdr);
+
+    return false;
 }
 
 static void
-display()
+display(void)
 {
+    uint32_t img_idx;
+    VkSubmitInfo sinfo;
+    VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+       VkPresentInfoKHR pinfo;
+
+    vkAcquireNextImageKHR(vk_core.dev, vk_chain.swapchain,
+                          UINT64_MAX, vk_sema.frame_ready, 0, &img_idx);
+
+    memset(&sinfo, 0, sizeof sinfo);
+    sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    sinfo.waitSemaphoreCount = 1;
+    sinfo.pWaitSemaphores = &vk_sema.frame_ready;
+    sinfo.pWaitDstStageMask = &wait_stages;
+    sinfo.commandBufferCount = 1;
+    sinfo.pCommandBuffers = &vk_cmd_buf;
+    sinfo.signalSemaphoreCount = 1;
+       sinfo.pSignalSemaphores = &vk_sema.frame_done;
+
+    if (vkQueueSubmit(vk_core.queue, 1, &sinfo, 0) != 0) {
+        fprintf(stderr, "Failed to submit draw commands.\n");
+        abort();
+    }
+
+    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 = &img_idx;
+
+    vkQueuePresentKHR(vk_core.queue, &pinfo);
 }
 
 static void
-cleanup()
+cleanup(void)
 {
+    vkQueueWaitIdle(vk_core.queue);
+
+    if (vk_cmd_buf != VK_NULL_HANDLE) {
+        vkResetCommandBuffer(vk_cmd_buf, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
+    }
+
+    vk_destroy_image(&vk_core, &vk_depth_att.obj);
     vk_destroy_renderer(&vk_core, &vk_rnd);
+    vk_destroy_semaphores(&vk_core, &vk_sema);
+
+    if (vk_chain.swapchain) {
+        vk_destroy_swapchain(&vk_core, &vk_chain);
+        vkDestroySurfaceKHR(vk_core.inst, vk_surf, 0);
+    }
+
+    glfwDestroyWindow(win);
 
-    vk_cleanup_ctx(&vk_core);
+    if (!vk_enable_layers)
+        vkFreeCommandBuffers(vk_core.dev, vk_core.cmd_pool, 1, &vk_cmd_buf);
+
+    vk_cleanup_ctx(&vk_core, vk_enable_layers);
 
     glfwTerminate();
 }
@@ -201,8 +298,4 @@ clb_key(GLFWwindow *win, int key, int scancode,
 static void
 clb_reshape(GLFWwindow *win, int width, int height)
 {
-    /* set viewport */
-
-    win_w = width;
-    win_h = height;
 }
index b4e9eb7..9e80dd3 100644 (file)
--- a/src/vk.c
+++ b/src/vk.c
@@ -9,7 +9,18 @@
 
 static VkViewport viewport;
 static VkRect2D scissor;
-static bool enable_layers = true;
+static VkPipelineCache pipeline_cache;
+
+/* when I extend the program to use more than one buffers
+ * I might need to store them in this list and access it
+ * using multiple threads
+ *
+ * man realloc
+ */
+#if 0
+static VkCommandBuffer *command_buffers;
+static uint32_t num_command_buffers;
+#endif
 
 /* static functions */
 
@@ -251,80 +262,105 @@ create_cmd_pool(struct vk_ctx *ctx)
 {
     VkCommandPoolCreateInfo cmd_pool_info;
     VkCommandPool cmd_pool;
-    VkDevice dev = ctx->dev;
 
     memset(&cmd_pool_info, 0, sizeof cmd_pool_info);
     cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
     cmd_pool_info.queueFamilyIndex = ctx->qfam_idx;
     cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
 
-    if (vkCreateCommandPool(dev, &cmd_pool_info, 0, &cmd_pool) != VK_SUCCESS)
+    if (vkCreateCommandPool(ctx->dev, &cmd_pool_info, 0, &cmd_pool) != VK_SUCCESS)
         return 0;
 
     return cmd_pool;
 }
 
-static VkRenderPass
-create_renderpass(struct vk_ctx *ctx,
-                  struct vk_image_props *color_img_props,
-                  struct vk_image_props *depth_img_props)
+static VkPipelineCache
+create_pipeline_cache(struct vk_ctx *ctx)
 {
-    uint32_t num_attachments = 2;
-    VkAttachmentDescription att_dsc[2];
-    VkAttachmentReference att_rfc[2];
-    VkSubpassDescription subpass_dsc[1];
-    VkRenderPassCreateInfo rpass_info;
+    VkPipelineCacheCreateInfo pc_info;
+    VkPipelineCache pcache;
 
-    /* VkAttachmentDescription */
-    memset(att_dsc, 0, num_attachments * sizeof att_dsc[0]);
+    memset(&pc_info, 0, sizeof pc_info);
+    pc_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
 
-    att_dsc[0].samples = get_num_samples(color_img_props->num_samples);
-    att_dsc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
-    att_dsc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-    att_dsc[0].initialLayout = color_img_props->in_layout;
-    att_dsc[0].finalLayout = color_img_props->end_layout;
-    att_dsc[0].format = color_img_props->format;
-    att_dsc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-    att_dsc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-
-    att_dsc[1].samples = get_num_samples(depth_img_props->num_samples);
-
-    /* We might want to reuse a depth buffer */
-    if (depth_img_props->in_layout != VK_IMAGE_LAYOUT_UNDEFINED) {
-        att_dsc[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-        att_dsc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-    }
-    else {
-        att_dsc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
-        att_dsc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+    if (vkCreatePipelineCache(ctx->dev, &pc_info, 0, &pcache) != VK_SUCCESS) {
+        fprintf(stderr, "Failed to create a pipeline cache.\n");
+        return VK_NULL_HANDLE;
     }
 
-    att_dsc[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-    att_dsc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
-    att_dsc[1].initialLayout = depth_img_props->in_layout;
-    att_dsc[1].finalLayout = depth_img_props->end_layout;
-    att_dsc[1].format = depth_img_props->format;
+    return pcache;
+}
 
-    /* VkAttachmentReference */
-    memset(att_rfc, 0, num_attachments * sizeof att_rfc[0]);
+static VkRenderPass
+create_renderpass(struct vk_ctx *ctx,
+                  uint32_t num_color_atts,
+                  struct vk_attachment *color_atts,
+                  struct vk_attachment *depth_att)
+{
+    VkAttachmentDescription *att_dsc;
+    VkAttachmentReference *att_rfc;
 
-    att_rfc[0].layout = color_img_props->tiling == VK_IMAGE_TILING_OPTIMAL ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
-    att_rfc[0].attachment = 0;
+    /* one subpass for the moment: */
+    VkSubpassDescription subpass_dsc[1];
+    VkRenderPassCreateInfo rpass_info;
 
-    att_rfc[1].layout = depth_img_props->tiling == VK_IMAGE_TILING_OPTIMAL ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
-    att_rfc[1].attachment = 1;
+    int i;
+    uint32_t num_atts = num_color_atts + 1;
+    bool has_layout = depth_att->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]);
+
+    memset(att_dsc, 0, num_atts * sizeof att_dsc[0]);
+    memset(att_rfc, 0, num_atts * sizeof att_rfc[0]);
+
+    /* 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].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+        att_dsc[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+        att_dsc[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        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;
+
+    }
+
+    /* 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;
+    /* 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 :
+                               VK_ATTACHMENT_LOAD_OP_CLEAR;
+    att_dsc[num_color_atts].stencilLoadOp = has_layout ?
+                                      VK_ATTACHMENT_LOAD_OP_LOAD :
+                                      VK_ATTACHMENT_LOAD_OP_CLEAR;
+    att_dsc[num_color_atts].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+    att_rfc[num_color_atts].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+    att_rfc[num_color_atts].attachment = num_color_atts;
 
     /* VkSubpassDescription */
     memset(&subpass_dsc, 0, sizeof subpass_dsc);
     subpass_dsc[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-    subpass_dsc[0].colorAttachmentCount = 1;
+    subpass_dsc[0].colorAttachmentCount = num_color_atts;
     subpass_dsc[0].pColorAttachments = &att_rfc[0];
-    subpass_dsc[0].pDepthStencilAttachment = &att_rfc[1];
+    subpass_dsc[0].pDepthStencilAttachment = &att_rfc[num_color_atts];
 
     /* VkRenderPassCreateInfo */
     memset(&rpass_info, 0, sizeof rpass_info);
     rpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-    rpass_info.attachmentCount = num_attachments;
+    rpass_info.attachmentCount = num_atts;
     rpass_info.pAttachments = att_dsc;
     rpass_info.subpassCount = 1;
     rpass_info.pSubpasses = subpass_dsc;
@@ -335,6 +371,9 @@ create_renderpass(struct vk_ctx *ctx,
         rpass = VK_NULL_HANDLE;
     }
 
+    free(att_dsc);
+    free(att_rfc);
+
     return rpass;
 }
 
@@ -350,8 +389,9 @@ get_image_type(uint32_t h, uint32_t d)
     return VK_IMAGE_TYPE_2D;
 }
 
+#if 0
 static VkImageViewType
-get_image_view_type(struct vk_image_props *props)
+get_image_view_type(struct vk_att_props *props)
 {
     VkImageType type = get_image_type(props->h, props->depth);
     switch(type) {
@@ -381,6 +421,7 @@ get_image_view_type(struct vk_image_props *props)
             return VK_IMAGE_VIEW_TYPE_2D;
     }
 }
+#endif
 
 static VkImageAspectFlagBits
 get_aspect_from_depth_format(VkFormat depth_format)
@@ -464,91 +505,82 @@ create_image_view(struct vk_ctx *ctx,
     return true;
 }
 
-static void
-create_framebuffer(struct vk_ctx *ctx,
-                   struct vk_image_attachment *color_att,
-                   struct vk_image_attachment *depth_att,
-                   struct vk_renderer *renderer)
+static bool
+create_attachment_views(struct vk_ctx *ctx, int num_color_att,
+                        struct vk_attachment *color_att,
+                        struct vk_attachment *depth_att)
 {
     VkImageSubresourceRange sr;
-    VkFramebufferCreateInfo fb_info;
-    VkImageView atts[2];
-    VkImageViewType view_type = get_image_view_type(&color_att->props);
-
-    if (!color_att->obj.img || !depth_att->obj.img) {
-        fprintf(stderr, "Invalid framebuffer attachment image.\n");
-        goto fail;
-    }
-
-    /* create image views */
+    int i;
 
-    /* VKImageSubresourceRange */
+    /* depth attachments */
     memset(&sr, 0, sizeof sr);
-    sr.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    /* If an application wants to use all mip levels
-     * or layers in an image after the baseMipLevel
-     * or baseArrayLayer, it can set levelCount and
-     * layerCount to the special values
-     * VK_REMAINING_MIP_LEVELS and
-     * VK_REMAINING_ARRAY_LAYERS without knowing the
-     * exact number of mip levels or layers.
-     */
+    sr.aspectMask = get_aspect_from_depth_format(depth_att->props.format);
     sr.baseMipLevel = 0;
-    sr.levelCount = color_att->props.num_levels;
+    sr.levelCount = 1;
     sr.baseArrayLayer = 0;
-    sr.layerCount = color_att->props.num_layers;
+    sr.layerCount = 1;
 
-    /* color view */
-    if (!color_att->obj.img_view) {
-        if (!create_image_view(ctx, color_att->obj.img, view_type, color_att->props.format, sr, false, &color_att->obj.img_view)) {
-            fprintf(stderr, "Failed to create color image view for framebuffer.\n");
-            vk_destroy_image(ctx, &color_att->obj);
-            goto fail;
-        }
-    }
+    create_image_view(ctx, depth_att->obj.img, VK_IMAGE_VIEW_TYPE_2D,
+                      depth_att->props.format, sr, false,
+                      &depth_att->obj.img_view);
 
-    /* depth view */
     memset(&sr, 0, sizeof sr);
-    sr.aspectMask = get_aspect_from_depth_format(depth_att->props.format);
-    sr.baseMipLevel = 0;
-    sr.levelCount = depth_att->props.num_levels ? depth_att->props.num_levels : 1;
-    sr.baseArrayLayer = 0;
-    sr.layerCount = depth_att->props.num_layers ? depth_att->props.num_layers : 1;
+    sr.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 
-    if (!depth_att->obj.img_view) {
-        if (!create_image_view(ctx, depth_att->obj.img,
-                               depth_att->props.num_layers > 1 ?
-                               VK_IMAGE_VIEW_TYPE_2D_ARRAY :
-                               VK_IMAGE_VIEW_TYPE_2D,
-                               depth_att->props.format,
-                               sr,
-                               false,
-                               &depth_att->obj.img_view)) {
-            fprintf(stderr, "Failed to create depth image view for framebuffer.\n");
-            vk_destroy_image(ctx, &depth_att->obj);
-            goto fail;
-        }
+    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);
     }
 
-    atts[0] = color_att->obj.img_view;
-    atts[1] = depth_att->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)
+{
+    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_att[i].obj.img_view;
+    }
+    atts[num_color_atts] = depth_att->obj.img_view;
 
     memset(&fb_info, 0, sizeof fb_info);
     fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
     fb_info.renderPass = renderer->renderpass;
-    fb_info.width = color_att->props.w;
-    fb_info.height = color_att->props.h;
-    fb_info.layers = color_att->props.num_layers ? color_att->props.num_layers : 1;
-    fb_info.attachmentCount = 2;
+    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, &renderer->fb) != VK_SUCCESS)
         goto fail;
 
+    free(atts);
     return;
 
 fail:
     fprintf(stderr, "Failed to create framebuffer.\n");
+    free(atts);
     renderer->fb = VK_NULL_HANDLE;
 }
 
@@ -574,19 +606,20 @@ create_shader_module(struct vk_ctx *ctx,
     return sm;
 }
 
-static void
-create_pipeline(struct vk_ctx *ctx,
-                uint32_t width,
-                uint32_t height,
-                uint32_t num_samples,
-                bool enable_depth,
-                bool enable_stencil,
-                struct vk_renderer *renderer)
+static bool
+create_graphics_pipeline(struct vk_ctx *ctx,
+                         uint32_t width,
+                         uint32_t height,
+                         uint32_t num_samples,
+                         uint32_t num_color_atts,
+                         bool enable_depth,
+                         bool enable_stencil,
+                         struct vk_renderer *renderer)
 {
     VkVertexInputBindingDescription vert_bind_dsc[1];
     VkVertexInputAttributeDescription vert_att_dsc[1];
 
-    VkPipelineColorBlendAttachmentState cb_att_state[1];
+    VkPipelineColorBlendAttachmentState *cb_att_state;
     VkPipelineVertexInputStateCreateInfo vert_input_info;
     VkPipelineInputAssemblyStateCreateInfo asm_info;
     VkPipelineViewportStateCreateInfo viewport_info;
@@ -681,7 +714,7 @@ create_pipeline(struct vk_ctx *ctx,
     /* VkPipelineMultisampleStateCreateInfo */
     memset(&ms_info, 0, sizeof ms_info);
     ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
-    ms_info.rasterizationSamples = num_samples;
+    ms_info.rasterizationSamples = get_num_samples(num_samples);
 
     /* VkStencilOpState */
     /* The default values for ES are taken by Topi Pohjolainen's code */
@@ -727,16 +760,23 @@ create_pipeline(struct vk_ctx *ctx,
     ds_info.front = ds_info.back;
 
     /* VkPipelineColorBlendAttachmentState */
-    memset(&cb_att_state[0], 0, sizeof cb_att_state[0]);
-    cb_att_state[0].colorWriteMask = (VK_COLOR_COMPONENT_R_BIT |
-                                      VK_COLOR_COMPONENT_G_BIT |
-                                      VK_COLOR_COMPONENT_B_BIT |
-                                      VK_COLOR_COMPONENT_A_BIT);
+    cb_att_state = malloc(num_color_atts * sizeof cb_att_state[0]);
+    if (!cb_att_state) {
+        fprintf(stderr, "Failed to allocate color blend attachment state for each attachment.\n");
+        goto fail;
+    }
+    memset(cb_att_state, 0, num_color_atts * sizeof cb_att_state[0]);
+    for (i = 0; i < num_color_atts; i++) {
+        cb_att_state[i].colorWriteMask = (VK_COLOR_COMPONENT_R_BIT |
+                                          VK_COLOR_COMPONENT_G_BIT |
+                                          VK_COLOR_COMPONENT_B_BIT |
+                                          VK_COLOR_COMPONENT_A_BIT);
+    }
 
     /* VkPipelineColorBlendStateCreateInfo */
     memset(&cb_info, 0, sizeof cb_info);
     cb_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
-    cb_info.attachmentCount = 1;
+    cb_info.attachmentCount = num_color_atts;
     cb_info.pAttachments = cb_att_state;
     /* default in ES 3.1 */
     for (i = 0; i < 4; i++) {
@@ -770,7 +810,7 @@ create_pipeline(struct vk_ctx *ctx,
     if (vkCreatePipelineLayout(ctx->dev, &layout_info, 0, &pipeline_layout) != VK_SUCCESS) {
         fprintf(stderr, "Failed to create pipeline layout\n");
         renderer->pipeline = VK_NULL_HANDLE;
-        return;
+        goto fail;
     }
 
     renderer->pipeline_layout = pipeline_layout;
@@ -795,25 +835,15 @@ create_pipeline(struct vk_ctx *ctx,
                                   &renderer->pipeline) != VK_SUCCESS) {
         fprintf(stderr, "Failed to create graphics pipeline.\n");
         renderer->pipeline = VK_NULL_HANDLE;
+        goto fail;
     }
-}
 
-static VkCommandBuffer
-create_cmd_buf(VkDevice dev, VkCommandPool cmd_pool)
-{
-    VkCommandBuffer cmd_buf;
-    VkCommandBufferAllocateInfo alloc_info;
-
-    memset(&alloc_info, 0, sizeof alloc_info);
-    alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
-    alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
-    alloc_info.commandBufferCount = 1;
-    alloc_info.commandPool = cmd_pool;
-
-    if (vkAllocateCommandBuffers(dev, &alloc_info, &cmd_buf) != VK_SUCCESS)
-        return 0;
+    free(cb_att_state);
+    return true;
 
-    return cmd_buf;
+fail:
+    free(cb_att_state);
+    return false;
 }
 
 static uint32_t
@@ -933,7 +963,7 @@ alloc_image_memory(struct vk_ctx *ctx, bool is_external, struct vk_image_obj *im
 }
 
 static bool
-are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
+are_props_supported(struct vk_ctx *ctx, struct vk_att_props *props)
 {
     VkPhysicalDeviceExternalImageFormatInfo ext_img_fmt_info;
     VkExternalImageFormatProperties ext_img_fmt_props;
@@ -941,6 +971,7 @@ are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
     int i;
     VkPhysicalDeviceImageFormatInfo2 img_fmt_info;
     VkImageFormatProperties2 img_fmt_props;
+
     VkImageUsageFlagBits all_flags[] = {
         VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
         VK_IMAGE_USAGE_TRANSFER_DST_BIT,
@@ -962,28 +993,30 @@ are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
     };
     VkImageUsageFlagBits flags = 0;
 
-    VkExternalMemoryFeatureFlagBits export_feature_flags =
-        VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT;
-    VkExternalMemoryHandleTypeFlagBits handle_type =
-        VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+    VkExternalMemoryFeatureFlagBits export_feature_flags = props->need_export ?
+        VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT : 0;
+    VkExternalMemoryHandleTypeFlagBits handle_type = props->need_export ?
+        VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR : 0;
 
-    memset(&ext_img_fmt_info, 0, sizeof ext_img_fmt_info);
-    ext_img_fmt_info.sType =
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
-    ext_img_fmt_info.handleType = handle_type;
+    if (props->need_export) {
+        memset(&ext_img_fmt_info, 0, sizeof ext_img_fmt_info);
+        ext_img_fmt_info.sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
+        ext_img_fmt_info.handleType = handle_type;
 
-    memset(&ext_img_fmt_props, 0, sizeof ext_img_fmt_props);
-    ext_img_fmt_props.sType =
-        VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
+        memset(&ext_img_fmt_props, 0, sizeof ext_img_fmt_props);
+        ext_img_fmt_props.sType =
+            VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
+    }
 
     memset(&img_fmt_props, 0, sizeof img_fmt_props);
     img_fmt_props.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
-    img_fmt_props.pNext = &ext_img_fmt_props;
+    img_fmt_props.pNext = props->need_export ? &ext_img_fmt_props : 0;
 
     memset(&img_fmt_info, 0, sizeof img_fmt_info);
     img_fmt_info.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
-    img_fmt_info.pNext = &ext_img_fmt_info;
+    img_fmt_info.pNext = props->need_export ? &ext_img_fmt_info : 0;
     img_fmt_info.format = props->format;
     img_fmt_info.type = get_image_type(props->h, props->depth);
     img_fmt_info.tiling = props->tiling;
@@ -991,8 +1024,8 @@ are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
     for (i = 0; i < ARRAY_SIZE(all_flags); i++) {
         img_fmt_info.usage = all_flags[i];
         if (vkGetPhysicalDeviceImageFormatProperties2(ctx->pdev,
-                    &img_fmt_info,
-                    &img_fmt_props) == VK_SUCCESS) {
+                                                      &img_fmt_info,
+                                                      &img_fmt_props) == VK_SUCCESS) {
             flags |= all_flags[i];
         }
     }
@@ -1002,8 +1035,11 @@ are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
         img_fmt_info.usage = flags;
     }
     else {
-        fprintf(stderr, "Unsupported Vulkan format properties: usage.\n");
-        return false;
+        if (!props->is_swapchain) {
+            fprintf(stderr, "Unsupported Vulkan format properties: usage.\n");
+            return false;
+        }
+        img_fmt_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
     }
 
     if (vkGetPhysicalDeviceImageFormatProperties2
@@ -1137,7 +1173,8 @@ success:
 /* exposed Vulkan functions */
 
 bool
-vk_init_ctx(struct vk_ctx *ctx)
+vk_init_ctx(struct vk_ctx *ctx,
+            bool enable_layers)
 {
     if ((ctx->inst = create_instance(enable_layers)) == VK_NULL_HANDLE) {
         fprintf(stderr, "Failed to create Vulkan instance.\n");
@@ -1158,14 +1195,16 @@ vk_init_ctx(struct vk_ctx *ctx)
     return true;
 
 fail:
-    vk_cleanup_ctx(ctx);
+    vk_cleanup_ctx(ctx, enable_layers);
     return false;
 }
 
 bool
-vk_init_ctx_for_rendering(struct vk_ctx *ctx)
+vk_init_ctx_for_rendering(struct vk_ctx *ctx,
+                          bool enable_cache,
+                          bool enable_layers)
 {
-    if (!vk_init_ctx(ctx)) {
+    if (!vk_init_ctx(ctx, enable_layers)) {
         fprintf(stderr, "Failed to initialize Vulkan.\n");
         return false;
     }
@@ -1175,37 +1214,45 @@ vk_init_ctx_for_rendering(struct vk_ctx *ctx)
         goto fail;
     }
 
-    if ((ctx->cmd_buf = create_cmd_buf(ctx->dev, ctx->cmd_pool)) ==
-                                       VK_NULL_HANDLE) {
-        fprintf(stderr, "Failed to create command buffer.\n");
-        goto fail;
-    }
-
     vkGetDeviceQueue(ctx->dev, ctx->qfam_idx, 0, &ctx->queue);
     if (!ctx->queue) {
         fprintf(stderr, "Failed to get command queue.\n");
         goto fail;
     }
 
+    if (enable_cache) {
+        if (!(pipeline_cache = create_pipeline_cache(ctx))) {
+            fprintf(stderr, "Failed to create pipeline cache.\n");
+            goto fail;
+        }
+    }
+
     return true;
 
 fail:
-    vk_cleanup_ctx(ctx);
+    vk_cleanup_ctx(ctx, enable_layers);
     return false;
 }
 
 void
-vk_cleanup_ctx(struct vk_ctx *ctx)
+vk_destroy_cmd_bufs(struct vk_ctx *ctx,
+                    uint32_t num_buffers,
+                    VkCommandBuffer *buffers)
 {
-    if (enable_layers) {
-        return;
+    int i;
+    for (i = 0; i < num_buffers; i++) {
+        vkResetCommandBuffer(buffers[i],
+                             VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
     }
+    vkFreeCommandBuffers(ctx->dev, ctx->cmd_pool, num_buffers, &buffers[i]);
+}
 
-    if (ctx->cmd_buf != VK_NULL_HANDLE) {
-        vkResetCommandBuffer(ctx->cmd_buf,
-                             VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
-        vkFreeCommandBuffers(ctx->dev, ctx->cmd_pool, 1, &ctx->cmd_buf);
-        ctx->cmd_buf = VK_NULL_HANDLE;
+void
+vk_cleanup_ctx(struct vk_ctx *ctx,
+               bool enable_layers)
+{
+    if (enable_layers) {
+        return;
     }
 
     if (ctx->cmd_pool != VK_NULL_HANDLE) {
@@ -1215,6 +1262,11 @@ vk_cleanup_ctx(struct vk_ctx *ctx)
         ctx->cmd_pool = VK_NULL_HANDLE;
     }
 
+    if (pipeline_cache != VK_NULL_HANDLE) {
+        vkDestroyPipelineCache(ctx->dev, pipeline_cache, 0);
+        pipeline_cache = VK_NULL_HANDLE;
+    }
+
     if (ctx->dev != VK_NULL_HANDLE) {
         vkDestroyDevice(ctx->dev, 0);
         ctx->dev = VK_NULL_HANDLE;
@@ -1228,7 +1280,7 @@ vk_cleanup_ctx(struct vk_ctx *ctx)
 
 bool
 vk_create_image(struct vk_ctx *ctx,
-                struct vk_image_props *props,
+                struct vk_att_props *props,
                 struct vk_image_obj *img)
 {
     VkImageCreateInfo img_info;
@@ -1283,7 +1335,7 @@ vk_destroy_image(struct vk_ctx *ctx, struct vk_image_obj *img_obj)
 
 bool
 vk_create_ext_image(struct vk_ctx *ctx,
-                    struct vk_image_props *props, struct vk_image_obj *img)
+                    struct vk_att_props *props, struct vk_image_obj *img)
 {
     VkExternalMemoryImageCreateInfo ext_img_info;
     VkImageCreateInfo img_info;
@@ -1342,8 +1394,10 @@ vk_fill_image_props(struct vk_ctx *ctx,
                     VkImageTiling tiling,
                     VkImageLayout in_layout,
                     VkImageLayout end_layout,
+                    bool is_swapchain,
+                    bool is_depth,
                     bool need_export,
-                    struct vk_image_props *props)
+                    struct vk_att_props *props)
 {
     props->w = w;
     props->h = h;
@@ -1358,6 +1412,9 @@ vk_fill_image_props(struct vk_ctx *ctx,
 
     props->in_layout = in_layout;
     props->end_layout = end_layout;
+
+    props->is_swapchain = is_swapchain;
+    props->is_depth = is_depth;
     props->need_export = need_export;
 
     if (!are_props_supported(ctx, props))
@@ -1372,10 +1429,13 @@ vk_create_renderer(struct vk_ctx *ctx,
                    unsigned int vs_size,
                    const char *fs_src,
                    unsigned int fs_size,
+                   int w, int h,
+                   uint32_t num_samples,
                    bool enable_depth,
                    bool enable_stencil,
-                   struct vk_image_attachment *color_att,
-                   struct vk_image_attachment *depth_att,
+                   int num_color_att,
+                   struct vk_attachment *color_att,
+                   struct vk_attachment *depth_att,
                    struct vk_vertex_info *vert_info,
                    struct vk_renderer *renderer)
 {
@@ -1383,17 +1443,22 @@ vk_create_renderer(struct vk_ctx *ctx,
     if (vert_info)
         renderer->vertex_info = *vert_info;
 
-    if (!color_att || depth_att) {
-        fprintf(stderr, "Empty attachment.\n");
+    /* create image views for each attachment */
+    if (!create_attachment_views(ctx, num_color_att, color_att, depth_att))
         goto fail;
-    }
 
-    renderer->renderpass = create_renderpass(ctx, &color_att->props,
-                                             &depth_att->props);
+    renderer->renderpass = create_renderpass(ctx,
+                                             num_color_att,
+                                             color_att,
+                                             depth_att);
+
     if (renderer->renderpass == VK_NULL_HANDLE)
         goto fail;
 
-    create_framebuffer(ctx, color_att, depth_att, renderer);
+    create_framebuffer(ctx,
+                       w, h,
+                       num_color_att, color_att,
+                       depth_att, renderer);
     if (renderer->fb == VK_NULL_HANDLE)
         goto fail;
 
@@ -1405,9 +1470,13 @@ vk_create_renderer(struct vk_ctx *ctx,
     if (renderer->fs == VK_NULL_HANDLE)
         goto fail;
 
-    create_pipeline(ctx, color_att->props.w, color_att->props.h,
-                    color_att->props.num_samples, enable_depth,
-                    enable_stencil, renderer);
+    /* FIXME this is only for graphics atm */
+    if(!create_graphics_pipeline(ctx, w, h,
+                                 num_samples,
+                                 num_color_att,
+                                 enable_depth,
+                                 enable_stencil, renderer))
+        goto fail;
 
     if (renderer->pipeline == VK_NULL_HANDLE)
         goto fail;
@@ -1415,7 +1484,7 @@ vk_create_renderer(struct vk_ctx *ctx,
     return true;
 
 fail:
-    fprintf(stderr, "Failed to create graphics pipeline.\n");
+    fprintf(stderr, "Failed to create renderer.\n");
     vk_destroy_renderer(ctx, renderer);
     return false;
 }
@@ -1570,29 +1639,55 @@ vk_destroy_buffer(struct vk_ctx *ctx,
     bo->mobj.mem = VK_NULL_HANDLE;
 }
 
-void
-vk_draw(struct vk_ctx *ctx,
-        struct vk_buf *vbo,
-        struct vk_renderer *renderer,
-        float *vk_fb_color,
-        uint32_t vk_fb_color_count,
-        struct vk_semaphores *semaphores,
-        struct vk_image_attachment *attachments,
-        uint32_t n_attachments,
-        float x, float y,
-        float w, float h)
+VkCommandBuffer
+vk_create_cmd_buffer(struct vk_ctx *ctx)
+{
+    VkCommandBuffer cmd_buf;
+    VkCommandBufferAllocateInfo alloc_info;
+
+    memset(&alloc_info, 0, sizeof alloc_info);
+    alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    alloc_info.commandBufferCount = 1;
+    alloc_info.commandPool = ctx->cmd_pool;
+
+    if (vkAllocateCommandBuffers(ctx->dev, &alloc_info, &cmd_buf) != VK_SUCCESS)
+        return 0;
+
+    return cmd_buf;
+}
+
+bool
+vk_record_cmd_buffer(struct vk_ctx *ctx,
+                     VkCommandBuffer cmd_buf,
+                     struct vk_renderer *renderer,
+                     struct vk_buf *vbo,
+                     uint32_t vk_fb_color_count,
+                     float *vk_fb_color,
+                     uint32_t num_atts,
+                     struct vk_attachment *atts,
+                     float x, float y,
+                     float w, float h)
 {
     VkCommandBufferBeginInfo cmd_begin_info;
     VkRenderPassBeginInfo rp_begin_info;
     VkRect2D rp_area;
-    VkClearValue clear_values[2];
-    VkSubmitInfo submit_info;
+    VkClearValue *clear_values; int i;
     VkDeviceSize offsets[] = {0};
-    VkPipelineStageFlagBits stage_flags;
+    int num_vertices;
     struct vk_dims img_size;
 
     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;
+        }
+    }
+
     /* VkCommandBufferBeginInfo */
     memset(&cmd_begin_info, 0, sizeof cmd_begin_info);
     cmd_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
@@ -1606,15 +1701,17 @@ vk_draw(struct vk_ctx *ctx,
     rp_area.offset.y = y;
 
     /* VkClearValue */
-    memset(&clear_values[0], 0, sizeof clear_values[0]);
-    clear_values[0].color.float32[0] = vk_fb_color[0]; /* red */
-    clear_values[0].color.float32[1] = vk_fb_color[1]; /* green */
-    clear_values[0].color.float32[2] = vk_fb_color[2]; /* blue */
-    clear_values[0].color.float32[3] = vk_fb_color[3]; /* alpha */
+    clear_values = malloc(num_atts * sizeof clear_values[0]);
+    memset(clear_values, 0, num_atts * sizeof clear_values[0]);
 
-    memset(&clear_values[1], 0, sizeof clear_values[1]);
-    clear_values[1].depthStencil.depth = 1.0;
-    clear_values[1].depthStencil.stencil = 0;
+    for (i = 0; i < num_atts - 1; i++) {
+        clear_values[i].color.float32[0] = vk_fb_color[0];
+        clear_values[i].color.float32[1] = vk_fb_color[1];
+        clear_values[i].color.float32[2] = vk_fb_color[2];
+        clear_values[i].color.float32[3] = vk_fb_color[3];
+    }
+    clear_values[num_atts - 1].depthStencil.depth = 1.0f;
+    clear_values[num_atts - 1].depthStencil.stencil = 0;
 
     /* VkRenderPassBeginInfo */
     memset(&rp_begin_info, 0, sizeof rp_begin_info);
@@ -1622,32 +1719,11 @@ vk_draw(struct vk_ctx *ctx,
     rp_begin_info.renderPass = renderer->renderpass;
     rp_begin_info.framebuffer = renderer->fb;
     rp_begin_info.renderArea = rp_area;
-    rp_begin_info.clearValueCount = 2;
+    rp_begin_info.clearValueCount = num_atts;
     rp_begin_info.pClearValues = clear_values;
 
-    /* VkSubmitInfo */
-    stage_flags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
-
-    memset(&submit_info, 0, sizeof submit_info);
-    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-    submit_info.commandBufferCount = 1;
-    submit_info.pCommandBuffers = &ctx->cmd_buf;
-
-    /* semaphores */
-    if (semaphores) {
-        assert(semaphores->frame_ready);
-        assert(semaphores->frame_done);
-
-        submit_info.pWaitDstStageMask = &stage_flags;
-        submit_info.waitSemaphoreCount = 1;
-        submit_info.pWaitSemaphores = &semaphores->frame_done;
-
-        submit_info.signalSemaphoreCount = 1;
-        submit_info.pSignalSemaphores = &semaphores->frame_ready;
-    }
-
-    vkBeginCommandBuffer(ctx->cmd_buf, &cmd_begin_info);
-    vkCmdBeginRenderPass(ctx->cmd_buf, &rp_begin_info, VK_SUBPASS_CONTENTS_INLINE);
+    vkBeginCommandBuffer(cmd_buf, &cmd_begin_info);
+    vkCmdBeginRenderPass(cmd_buf, &rp_begin_info, VK_SUBPASS_CONTENTS_INLINE);
 
     viewport.x = x;
     viewport.y = y;
@@ -1659,31 +1735,35 @@ vk_draw(struct vk_ctx *ctx,
     scissor.extent.width = w;
     scissor.extent.height = h;
 
-    vkCmdSetViewport(ctx->cmd_buf, 0, 1, &viewport);
-    vkCmdSetScissor(ctx->cmd_buf, 0, 1, &scissor);
+    vkCmdSetViewport(cmd_buf, 0, 1, &viewport);
+    vkCmdSetScissor(cmd_buf, 0, 1, &scissor);
 
     img_size.w = (float)w;
     img_size.h = (float)h;
-    vkCmdPushConstants(ctx->cmd_buf,
-               renderer->pipeline_layout,
-               VK_SHADER_STAGE_FRAGMENT_BIT,
-               0, sizeof (struct vk_dims),
-               &img_size);
+    vkCmdPushConstants(cmd_buf,
+                       renderer->pipeline_layout,
+                       VK_SHADER_STAGE_FRAGMENT_BIT,
+                       0, sizeof (struct vk_dims),
+                       &img_size);
 
     if (vbo) {
-        vkCmdBindVertexBuffers(ctx->cmd_buf, 0, 1, &vbo->buf, offsets);
+        vkCmdBindVertexBuffers(cmd_buf, 0, 1, &vbo->buf, offsets);
     }
-    vkCmdBindPipeline(ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->pipeline);
 
-    int num_vertices = vbo ? renderer->vertex_info.num_verts : 4;
-    vkCmdDraw(ctx->cmd_buf, num_vertices, 1, 0, 0);
-    vkCmdEndRenderPass(ctx->cmd_buf);
-    if (attachments) {
+    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);
+    vkCmdEndRenderPass(cmd_buf);
+
+    free(clear_values);
+#if 0
+    if (atts) {
         VkImageMemoryBarrier *barriers =
-            calloc(n_attachments, sizeof(VkImageMemoryBarrier));
+            calloc(num_atts, sizeof(VkImageMemoryBarrier));
         VkImageMemoryBarrier *barrier = barriers;
-        for (uint32_t n = 0; n < n_attachments; n++, barrier++) {
-            struct vk_image_attachment *att = &attachments[n];
+        for (uint32_t n = 0; n < num_atts; n++, barrier++) {
+            struct vk_attachment *att = &atts[n];
             VkImageAspectFlagBits depth_stencil_flags =
                 get_aspect_from_depth_format(att->props.format);
             bool is_depth = (depth_stencil_flags != 0);
@@ -1708,35 +1788,69 @@ vk_draw(struct vk_ctx *ctx,
             barrier->subresourceRange.layerCount = 1;
         }
 
-        vkCmdPipelineBarrier(ctx->cmd_buf,
+        vkCmdPipelineBarrier(cmd_buf,
                      VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
                      VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
                      0,
                      0, NULL,
                      0, NULL,
-                     n_attachments, barriers);
+                     num_atts, barriers);
         free(barriers);
     }
+#endif
+
+    vkEndCommandBuffer(cmd_buf);
+    return true;
+}
+
+void
+vk_draw(struct vk_ctx *ctx,
+        struct vk_semaphores *semaphores,
+        uint32_t num_buffers,
+        VkCommandBuffer *cmd_buf)
+{
+    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;
+    submit_info.commandBufferCount = num_buffers;
+    submit_info.pCommandBuffers = cmd_buf;
+
+    /* semaphores */
+    if (semaphores) {
+        assert(semaphores->frame_ready);
+        assert(semaphores->frame_done);
+
+        submit_info.pWaitDstStageMask = &stage_flags;
+        submit_info.waitSemaphoreCount = 1;
+        submit_info.pWaitSemaphores = &semaphores->frame_done;
+
+        submit_info.signalSemaphoreCount = 1;
+        submit_info.pSignalSemaphores = &semaphores->frame_ready;
+    }
 
-    vkEndCommandBuffer(ctx->cmd_buf);
 
     if (vkQueueSubmit(ctx->queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
         fprintf(stderr, "Failed to submit queue.\n");
     }
 
-    if (!semaphores)
-        vkQueueWaitIdle(ctx->queue);
+    vkQueueWaitIdle(ctx->queue);
 }
 
 void
 vk_clear_color(struct vk_ctx *ctx,
+               VkCommandBuffer cmd_buf,
                struct vk_buf *vbo,
                struct vk_renderer *renderer,
                float *vk_fb_color,
                uint32_t vk_fb_color_count,
                struct vk_semaphores *semaphores,
                bool has_wait, bool has_signal,
-               struct vk_image_attachment *attachments,
+               struct vk_attachment *attachments,
                uint32_t n_attachments,
                float x, float y,
                float w, float h)
@@ -1789,7 +1903,7 @@ vk_clear_color(struct vk_ctx *ctx,
     memset(&submit_info, 0, sizeof submit_info);
     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
     submit_info.commandBufferCount = 1;
-    submit_info.pCommandBuffers = &ctx->cmd_buf;
+    submit_info.pCommandBuffers = &cmd_buf;
 
     /* FIXME */
     if (has_wait) {
@@ -1809,21 +1923,21 @@ vk_clear_color(struct vk_ctx *ctx,
     img_range.baseArrayLayer = 0;
     img_range.layerCount = 1;
 
-    vkBeginCommandBuffer(ctx->cmd_buf, &cmd_begin_info);
+    vkBeginCommandBuffer(cmd_buf, &cmd_begin_info);
     vk_transition_image_layout(&attachments[0],
-                               ctx->cmd_buf,
+                               cmd_buf,
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                                VK_IMAGE_LAYOUT_GENERAL,
                                VK_QUEUE_FAMILY_EXTERNAL,
                                ctx->qfam_idx);
-    vkCmdClearColorImage(ctx->cmd_buf,
+    vkCmdClearColorImage(cmd_buf,
                          attachments[0].obj.img,
                          VK_IMAGE_LAYOUT_GENERAL,
                          &clear_values[0].color,
                          1,
                          &img_range);
 
-    vkCmdBeginRenderPass(ctx->cmd_buf, &rp_begin_info, VK_SUBPASS_CONTENTS_INLINE);
+    vkCmdBeginRenderPass(cmd_buf, &rp_begin_info, VK_SUBPASS_CONTENTS_INLINE);
 
     viewport.x = x;
     viewport.y = y;
@@ -1835,12 +1949,12 @@ vk_clear_color(struct vk_ctx *ctx,
     scissor.extent.width = w;
     scissor.extent.height = h;
 
-    vkCmdSetViewport(ctx->cmd_buf, 0, 1, &viewport);
-    vkCmdSetScissor(ctx->cmd_buf, 0, 1, &scissor);
+    vkCmdSetViewport(cmd_buf, 0, 1, &viewport);
+    vkCmdSetScissor(cmd_buf, 0, 1, &scissor);
 
-    vkCmdBindPipeline(ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->pipeline);
+    vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->pipeline);
 
-    vkCmdEndRenderPass(ctx->cmd_buf);
+    vkCmdEndRenderPass(cmd_buf);
 
     if (attachments) {
         VkImageMemoryBarrier *barriers =
@@ -1848,7 +1962,7 @@ vk_clear_color(struct vk_ctx *ctx,
         VkImageMemoryBarrier *barrier = barriers;
 
         for (uint32_t n = 0; n < n_attachments; n++, barrier++) {
-            struct vk_image_attachment *att = &attachments[n];
+            struct vk_attachment *att = &attachments[n];
 
             /* Insert barrier to mark ownership transfer. */
             barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@@ -1874,7 +1988,7 @@ vk_clear_color(struct vk_ctx *ctx,
             barrier->subresourceRange.layerCount = 1;
         }
 
-        vkCmdPipelineBarrier(ctx->cmd_buf,
+        vkCmdPipelineBarrier(cmd_buf,
                      VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
                      VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
                      0,
@@ -1884,7 +1998,7 @@ vk_clear_color(struct vk_ctx *ctx,
         free(barriers);
     }
 
-    vkEndCommandBuffer(ctx->cmd_buf);
+    vkEndCommandBuffer(cmd_buf);
 
     if (vkQueueSubmit(ctx->queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
         fprintf(stderr, "Failed to submit queue.\n");
@@ -1922,25 +2036,18 @@ vk_create_swapchain(struct vk_ctx *ctx,
         return false;
     }
 
-    /* allocate and init an empty struct vk_swapchain */
-    swapchain = malloc(sizeof *swapchain);
     memset(swapchain, 0, sizeof *swapchain);
-
     swapchain->surface = surf;
 
     memset(&s_info, 0, sizeof s_info);
     s_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
     s_info.flags = 0;
-    /* surface format */
     if (!sc_select_format(ctx, surf, &s_info)) {
         fprintf(stderr, "Failed to determine the surface format.\n");
         return false;
     }
     s_info.surface = surf;
-
-    /* number of images */
     s_info.minImageCount = surf_cap.minImageCount;
-    /* swapchain images dims */
     {
         extent.width = width;
         extent.height = height;
@@ -1950,8 +2057,6 @@ vk_create_swapchain(struct vk_ctx *ctx,
     }
     s_info.imageExtent = extent;
     s_info.imageArrayLayers = 1;
-    /* enable color attachment bit and transfer src and transfer dst bits
-     * too if they are supported */
     {
         s_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
         if (surf_cap.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)
@@ -1964,16 +2069,15 @@ vk_create_swapchain(struct vk_ctx *ctx,
 
     /* we might want to use this function when we recreate the swapchain too */
     s_info.preTransform = surf_cap.supportedTransforms &
-                            VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ?
-                            VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR :
-                            surf_cap.currentTransform;
+                          VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ?
+                          VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR :
+                          surf_cap.currentTransform;
 
     /* we could also write a sc_select_supported_composite_alpha
      * later but VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR is universally
      * supported */
     s_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
     s_info.clipped = VK_TRUE;
-    s_info.oldSwapchain = old_swapchain ? old_swapchain->swapchain : 0;
 
     if (vkCreateSwapchainKHR(ctx->dev, &s_info, 0,
                              &swapchain->swapchain) != VK_SUCCESS) {
@@ -1981,58 +2085,71 @@ vk_create_swapchain(struct vk_ctx *ctx,
         return false;
     }
 
-    /* if an existing swapchain is recreated we need to destroy
-     * the old swapchain and clean up the images */
-    if (old_swapchain) {
-        for (i = 0; i < old_swapchain->num_images; i++) {
-            vkDestroyImageView(ctx->dev, old_swapchain->images[i].img_view, 0);
-        }
-        vk_destroy_swapchain(ctx, old_swapchain);
+    if (!swapchain->swapchain) {
+        fprintf(stderr, "The swapchain seems null\n");
+        return false;
     }
 
     /* get the number of swapchain images and the swapchain images
      * and store the new swapchain images
      */
-    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain, &swapchain->num_images, 0);
-    printf("number of swapchain images: %d\n", swapchain->num_images);
+    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain, &swapchain->num_atts, 0);
+    printf("number of swapchain images: %d\n", swapchain->num_atts);
 
     /* create images */
-    s_images = malloc(swapchain->num_images * sizeof(VkImage));
-    vkGetSwapchainImagesKHR(ctx->dev, swapchain->swapchain, &swapchain->num_images, s_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);
 
     swapchain->image_fmt = s_info.imageFormat;
-    swapchain->images = malloc(swapchain->num_images * sizeof(struct vk_image_obj));
+    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_images; i++) {
-        swapchain->images[i].img = s_images[i];
-        if (!(create_image_view(ctx,
-                                swapchain->images[i].img,
-                                VK_IMAGE_VIEW_TYPE_2D,
-                                swapchain->image_fmt,
-                                sr,
-                                true,
-                                &swapchain->images[i].img_view))) {
-            fprintf(stderr, "Fail to create an image view from the swapchain image: i=%d\n", i);
-            break;
-        }
-        if (i < swapchain->num_images - 1) {
-            int j;
-            for (j = 0; j < i; j++) {
-                vkDestroyImageView(ctx->dev, swapchain->images[i].img_view, 0);
-            }
-            return false;
-        }
+    for (i = 0; i < swapchain->num_atts; i++) {
+        /* we store the image */
+        swapchain->atts[i].obj.img = s_images[i];
 
-        memset(&swapchain->images[i].mobj, 0, sizeof swapchain->images[i].mobj);
+        /* 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)) {
+            fprintf(stderr, "Failed to create image view for image: %d\n", i);
+            goto fail;
+        }
     }
 
     free(s_images);
     return true;
+
+fail:
+    free(s_images);
+    return false;
 }
 
 void
@@ -2046,9 +2163,36 @@ vk_destroy_swapchain(struct vk_ctx *ctx,
     swapchain = 0;
 }
 
+bool
+vk_present_queue(struct vk_swapchain *swapchain,
+                 VkQueue queue,
+                 uint32_t image_idx,
+                 VkSemaphore wait_sema)
+{
+    VkPresentInfoKHR pinfo;
+
+    memset(&pinfo, 0, sizeof pinfo);
+    pinfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+    pinfo.swapchainCount = 1;
+    pinfo.pImageIndices = &image_idx;
+
+    if (wait_sema != VK_NULL_HANDLE) {
+        pinfo.pWaitSemaphores = &wait_sema;
+        pinfo.waitSemaphoreCount = 1;
+    }
+
+    if (vkQueuePresentKHR(queue, &pinfo) != VK_SUCCESS) {
+        fprintf(stderr, "Failed to present queue.\n");
+        return false;
+    }
+
+    return true;
+}
+
 void
 vk_copy_image_to_buffer(struct vk_ctx *ctx,
-                        struct vk_image_attachment *src_img,
+                        VkCommandBuffer cmd_buf,
+                        struct vk_attachment *src_img,
                         struct vk_buf *dst_bo,
                         float w, float h)
 {
@@ -2064,12 +2208,12 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
     memset(&submit_info, 0, sizeof submit_info);
     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
     submit_info.commandBufferCount = 1;
-    submit_info.pCommandBuffers = &ctx->cmd_buf;
+    submit_info.pCommandBuffers = &cmd_buf;
 
-    vkBeginCommandBuffer(ctx->cmd_buf, &cmd_begin_info);
+    vkBeginCommandBuffer(cmd_buf, &cmd_begin_info);
     if (src_img->props.end_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && dst_bo) {
         vk_transition_image_layout(src_img,
-                                   ctx->cmd_buf,
+                                   cmd_buf,
                                    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                                    VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
                                    VK_QUEUE_FAMILY_EXTERNAL,
@@ -2091,13 +2235,13 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
             .imageExtent = { w, h, 1 }
                 };
 
-        vkCmdCopyImageToBuffer(ctx->cmd_buf,
+        vkCmdCopyImageToBuffer(cmd_buf,
                                src_img->obj.img,
                                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
                                dst_bo->buf, 1, &copy_region);
 
         vk_transition_image_layout(src_img,
-                                   ctx->cmd_buf,
+                                   cmd_buf,
                                    VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
                                    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                                    VK_QUEUE_FAMILY_EXTERNAL,
@@ -2114,14 +2258,14 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
             .size = VK_WHOLE_SIZE
         };
 
-        vkCmdPipelineBarrier(ctx->cmd_buf,
+        vkCmdPipelineBarrier(cmd_buf,
                              VK_PIPELINE_STAGE_TRANSFER_BIT,
                              VK_PIPELINE_STAGE_HOST_BIT,
                              (VkDependencyFlags) 0, 0, NULL,
                              1, &write_finish_buffer_memory_barrier,
                              0, NULL);
     }
-    vkEndCommandBuffer(ctx->cmd_buf);
+    vkEndCommandBuffer(cmd_buf);
 
     if (vkQueueSubmit(ctx->queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
         fprintf(stderr, "Failed to submit queue.\n");
@@ -2217,7 +2361,7 @@ vk_destroy_fences(struct vk_ctx *ctx,
 }
 
 void
-vk_transition_image_layout(struct vk_image_attachment *img_att,
+vk_transition_image_layout(struct vk_attachment *img_att,
                            VkCommandBuffer cmd_buf,
                            VkImageLayout old_layout,
                            VkImageLayout new_layout,
@@ -2225,7 +2369,7 @@ vk_transition_image_layout(struct vk_image_attachment *img_att,
                            uint32_t dst_queue_fam_idx)
 {
     VkImageMemoryBarrier barrier;
-    struct vk_image_props props = img_att->props;
+    struct vk_att_props props = img_att->props;
     VkImageAspectFlagBits aspect_mask = get_aspect_from_depth_format(props.format);
 
     memset(&barrier, 0, sizeof barrier);
index 259240b..dfe6aa6 100644 (file)
--- a/src/vk.h
+++ b/src/vk.h
@@ -12,7 +12,8 @@ struct vk_ctx
     VkDevice dev;
 
     VkCommandPool cmd_pool;
-    VkCommandBuffer cmd_buf;
+    VkCommandBuffer *cmd_buffers;
+    uint32_t num_cmd_buffers;
 
     VkQueue queue;
     int qfam_idx;
@@ -21,6 +22,12 @@ struct vk_ctx
     uint8_t driverUUID[VK_UUID_SIZE];
 };
 
+struct vk_cmd_buffer
+{
+    VkCommandBuffer buffer;
+    VkSubmitInfo submit_info;
+};
+
 struct vk_swapchain
 {
     VkSwapchainKHR swapchain;
@@ -31,11 +38,11 @@ struct vk_swapchain
     VkFormat image_fmt;
     VkExtent2D extent2d;
 
-    uint32_t num_images;
-    struct vk_image_obj *images;
+    uint32_t num_atts;
+    struct vk_attachment *atts;
 };
 
-struct vk_image_props
+struct vk_att_props
 {
     uint32_t w;
     uint32_t h;
@@ -52,6 +59,8 @@ struct vk_image_props
     VkImageLayout in_layout;
     VkImageLayout end_layout;
 
+    bool is_depth;
+    bool is_swapchain;
     bool need_export;
 };
 
@@ -69,9 +78,9 @@ struct vk_image_obj {
     struct vk_mem_obj mobj;
 };
 
-struct vk_image_attachment {
+struct vk_attachment {
     struct vk_image_obj obj;
-    struct vk_image_props props;
+    struct vk_att_props props;
 };
 
 struct vk_vertex_info
@@ -114,15 +123,21 @@ struct vk_semaphores
 
 /* context */
 
-bool vk_init_ctx(struct vk_ctx *ctx);
-bool vk_init_ctx_for_rendering(struct vk_ctx *ctx);
-void vk_cleanup_ctx(struct vk_ctx *ctx);
+bool vk_init_ctx(struct vk_ctx *ctx,
+                 bool enable_layers);
+
+bool vk_init_ctx_for_rendering(struct vk_ctx *ctx,
+                               bool enable_cache,
+                               bool enable_layers);
+
+void vk_cleanup_ctx(struct vk_ctx *ctx,
+                    bool enable_layers);
 
 /* images */
 
 bool
 vk_create_image(struct vk_ctx *ctx,
-                struct vk_image_props *props,
+                struct vk_att_props *props,
                 struct vk_image_obj *img_obj);
 void
 vk_destroy_image(struct vk_ctx *ctx,
@@ -139,15 +154,10 @@ vk_fill_image_props(struct vk_ctx *ctx,
                     VkImageTiling tiling,
                     VkImageLayout in_layout,
                     VkImageLayout end_layout,
+                    bool is_swapchain,
+                    bool is_depth,
                     bool need_export,
-                    struct vk_image_props *props);
-
-bool
-vk_create_attachment_from_swapchain_image(struct vk_ctx *ctx,
-                                          VkImage *swapchain_img,
-                                          VkImageView *swapchain_view,
-                                          struct vk_image_props *swapchain_props,
-                                          struct vk_image_attachment *color_att);
+                    struct vk_att_props *props);
 
 /* buffers */
 
@@ -203,10 +213,13 @@ vk_create_renderer(struct vk_ctx *ctx,
                    unsigned int vs_size,
                    const char *fs_src,
                    unsigned int fs_size,
+                   int w, int h,
+                   uint32_t num_samples,
                    bool enable_depth,
                    bool enable_stencil,
-                   struct vk_image_attachment *color_att,
-                   struct vk_image_attachment *depth_att,
+                   int num_color_att,
+                   struct vk_attachment *color_att,
+                   struct vk_attachment *depth_att,
                    struct vk_vertex_info *vert_info,
                    struct vk_renderer *renderer);
 
@@ -215,27 +228,45 @@ vk_destroy_renderer(struct vk_ctx *ctx,
                     struct vk_renderer *pipeline);
 
 /* draw */
+VkCommandBuffer
+vk_create_cmd_buffer(struct vk_ctx *ctx);
+
+bool
+vk_record_cmd_buffer(struct vk_ctx *ctx,
+                     VkCommandBuffer cmd_buf,
+                     struct vk_renderer *renderer,
+                     struct vk_buf *vbo,
+                     uint32_t vk_fb_color_count,
+                     float *vk_fb_color,
+                     uint32_t num_atts,
+                     struct vk_attachment *atts,
+                     float x, float y,
+                     float w, float h);
+
+void
+vk_reset_cmd_buf(struct vk_cmd_buffer *cmd_buf);
+
+void
+vk_destroy_cmd_bufs(struct vk_ctx *ctx,
+                    uint32_t num_buffers,
+                    VkCommandBuffer *buffers);
 
 void
 vk_draw(struct vk_ctx *ctx,
-        struct vk_buf *vbo,
-        struct vk_renderer *renderer,
-        float *vk_fb_color,
-        uint32_t vk_fb_color_count,
         struct vk_semaphores *semaphores,
-        struct vk_image_attachment *attachments,
-        uint32_t n_attachments,
-        float x, float y, float w, float h);
+        uint32_t num_buffers,
+        VkCommandBuffer *cmd_buf);
 
 void
 vk_clear_color(struct vk_ctx *ctx,
+               VkCommandBuffer cmd_buf,
                struct vk_buf *vbo,
                struct vk_renderer *renderer,
                float *vk_fb_color,
                uint32_t vk_fb_color_count,
                struct vk_semaphores *semaphores,
                bool has_wait, bool has_signal,
-               struct vk_image_attachment *attachments,
+               struct vk_attachment *attachments,
                uint32_t n_attachments,
                float x, float y, float w, float h);
 
@@ -252,16 +283,23 @@ void
 vk_destroy_swapchain(struct vk_ctx *ctx,
                      struct vk_swapchain *swapchain);
 
+bool
+vk_present_queue(struct vk_swapchain *swapchain,
+                 VkQueue queue,
+                 uint32_t image_idx,
+                 VkSemaphore wait_sema);
+
 /* transitions */
 
 void
 vk_copy_image_to_buffer(struct vk_ctx *ctx,
-                        struct vk_image_attachment *src_img,
+                        VkCommandBuffer cmd_buf,
+                        struct vk_attachment *src_img,
                         struct vk_buf *dst_bo,
                         float w, float h);
 
 void
-vk_transition_image_layout(struct vk_image_attachment *img_att,
+vk_transition_image_layout(struct vk_attachment *img_att,
                            VkCommandBuffer cmd_buf,
                            VkImageLayout old_layout,
                            VkImageLayout new_layout,