updated, needs fixes
authorEleni Maria Stea <elene.mst@gmail.com>
Wed, 26 May 2021 09:50:45 +0000 (12:50 +0300)
committerEleni Maria Stea <elene.mst@gmail.com>
Wed, 26 May 2021 09:50:45 +0000 (12:50 +0300)
src/vk.c
src/vk.h

index a94e13a..d280e00 100644 (file)
--- a/src/vk.c
+++ b/src/vk.c
@@ -11,7 +11,61 @@ static VkRect2D scissor;
 
 /* static functions */
 static VkSampleCountFlagBits
-get_num_samples(uint32_t num_samples);
+get_num_samples(uint32_t num_samples)
+{
+       switch(num_samples) {
+       case 64:
+               return VK_SAMPLE_COUNT_64_BIT;
+       case 32:
+               return VK_SAMPLE_COUNT_32_BIT;
+       case 16:
+               return VK_SAMPLE_COUNT_16_BIT;
+       case 8:
+               return VK_SAMPLE_COUNT_8_BIT;
+       case 4:
+               return VK_SAMPLE_COUNT_4_BIT;
+       case 2:
+               return VK_SAMPLE_COUNT_2_BIT;
+       case 1:
+               break;
+       default:
+               fprintf(stderr, "Invalid number of samples in VkSampleCountFlagBits. Using one sample.\n");
+               break;
+       }
+       return VK_SAMPLE_COUNT_1_BIT;
+}
+
+static VkAccessFlagBits
+get_access_mask(const VkImageLayout layout)
+{
+       /* dstAccessMask of barriers must be supported from the pipeline
+        * stage, see also access scopes and this table:
+        * https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#synchronization-access-types-supported
+        */
+       switch (layout) {
+       case VK_IMAGE_LAYOUT_UNDEFINED:
+               return 0;
+       case VK_IMAGE_LAYOUT_GENERAL:
+               return VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
+       case VK_IMAGE_LAYOUT_PREINITIALIZED:
+               return VK_ACCESS_HOST_WRITE_BIT;
+       case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+               return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+                      VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+       case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+               return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
+       case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+               return VK_ACCESS_TRANSFER_READ_BIT;
+       case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+               return VK_ACCESS_TRANSFER_WRITE_BIT;
+       case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
+               return 0;
+       default:
+               return 0;
+       };
+
+       return 0;
+}
 
 static void
 enable_validation_layers(VkInstanceCreateInfo *info)
@@ -40,24 +94,6 @@ enable_validation_layers(VkInstanceCreateInfo *info)
        }
 }
 
-static void
-fill_uuid(VkPhysicalDevice pdev, uint8_t *deviceUUID, uint8_t *driverUUID)
-{
-       VkPhysicalDeviceIDProperties devProp;
-       VkPhysicalDeviceProperties2 prop2;
-
-       memset(&devProp, 0, sizeof devProp);
-       devProp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
-
-       memset(&prop2, 0, sizeof prop2);
-       prop2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
-       prop2.pNext = &devProp;
-
-       vkGetPhysicalDeviceProperties2(pdev, &prop2);
-       memcpy(deviceUUID, devProp.deviceUUID, VK_UUID_SIZE);
-       memcpy(driverUUID, devProp.driverUUID, VK_UUID_SIZE);
-}
-
 static VkInstance
 create_instance(bool enable_layers)
 {
@@ -78,7 +114,7 @@ create_instance(bool enable_layers)
                enable_validation_layers(&inst_info);
 
        if (vkCreateInstance(&inst_info, 0, &inst) != VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               return 0;
 
        return inst;
 }
@@ -93,12 +129,12 @@ select_physical_device(VkInstance inst)
 
        if ((res =
             vkEnumeratePhysicalDevices(inst, &dev_count, 0)) != VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               return 0;
 
        pdevices = malloc(dev_count * sizeof(VkPhysicalDevice));
        if (vkEnumeratePhysicalDevices(inst, &dev_count, pdevices) !=
            VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               return 0;
 
        pdevice0 = pdevices[0];
        free(pdevices);
@@ -109,6 +145,7 @@ select_physical_device(VkInstance inst)
 static VkDevice
 create_device(struct vk_ctx *ctx, VkPhysicalDevice pdev)
 {
+    /* FIXME: swapchain */
        const char *deviceExtensions[] = { "VK_KHR_external_memory_fd",
                                           "VK_KHR_external_semaphore_fd" };
        VkDeviceQueueCreateInfo dev_queue_info;
@@ -123,7 +160,7 @@ create_device(struct vk_ctx *ctx, VkPhysicalDevice pdev)
        vkGetPhysicalDeviceQueueFamilyProperties(pdev, &prop_count, 0);
        if (prop_count < 0) {
                fprintf(stderr, "Invalid queue family properties.\n");
-               return VK_NULL_HANDLE;
+               return 0;
        }
 
        fam_props = malloc(prop_count * sizeof *fam_props);
@@ -151,24 +188,27 @@ create_device(struct vk_ctx *ctx, VkPhysicalDevice pdev)
        dev_info.ppEnabledExtensionNames = deviceExtensions;
 
        if (vkCreateDevice(pdev, &dev_info, 0, &dev) != VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               return 0;
 
        return dev;
 }
 
-static VkPipelineCache
-create_pipeline_cache(VkDevice dev)
+static void
+fill_uuid(VkPhysicalDevice pdev, uint8_t *deviceUUID, uint8_t *driverUUID)
 {
-       VkPipelineCacheCreateInfo pcache_info;
-       VkPipelineCache pcache;
+       VkPhysicalDeviceIDProperties devProp;
+       VkPhysicalDeviceProperties2 prop2;
 
-       memset(&pcache_info, 0, sizeof pcache_info);
-       pcache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+       memset(&devProp, 0, sizeof devProp);
+       devProp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
 
-       if (vkCreatePipelineCache(dev, &pcache_info, 0, &pcache) != VK_SUCCESS)
-               return VK_NULL_HANDLE;
+       memset(&prop2, 0, sizeof prop2);
+       prop2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+       prop2.pNext = &devProp;
 
-       return pcache;
+       vkGetPhysicalDeviceProperties2(pdev, &prop2);
+       memcpy(deviceUUID, devProp.deviceUUID, VK_UUID_SIZE);
+       memcpy(driverUUID, devProp.driverUUID, VK_UUID_SIZE);
 }
 
 static VkCommandPool
@@ -184,15 +224,15 @@ create_cmd_pool(struct vk_ctx *ctx)
        cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
 
        if (vkCreateCommandPool(dev, &cmd_pool_info, 0, &cmd_pool) != VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               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)
+                  struct vk_image_props *color_img_props,
+                  struct vk_image_props *depth_img_props)
 {
        uint32_t num_attachments = 2;
        VkAttachmentDescription att_dsc[2];
@@ -209,14 +249,23 @@ create_renderpass(struct vk_ctx *ctx,
        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)
+       if (depth_img_props->in_layout != VK_IMAGE_LAYOUT_UNDEFINED) {
                att_dsc[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-       else
+               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;
+    }
+
        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;
@@ -315,7 +364,7 @@ get_aspect_from_depth_format(VkFormat depth_format)
        default:
                break;
        }
-       return VK_NULL_HANDLE;
+       return 0;
 }
 
 static VkPipelineStageFlags
@@ -339,7 +388,7 @@ get_pipeline_stage_flags(const VkImageLayout layout)
        case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
                return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
        default:
-               return 0;
+        break;
        }
        return 0;
 }
@@ -389,7 +438,7 @@ create_framebuffer(struct vk_ctx *ctx,
        color_info.format = color_att->props.format;
        color_info.subresourceRange = sr;
 
-       if (vkCreateImageView(ctx->dev, &color_info, 0, &atts[0]) != VK_SUCCESS) {
+    if (vkCreateImageView(ctx->dev, &color_info, 0, &color_att->obj.img_view) != VK_SUCCESS) {
                fprintf(stderr, "Failed to create color image view for framebuffer.\n");
                vk_destroy_image(ctx, &color_att->obj);
                goto fail;
@@ -411,12 +460,16 @@ create_framebuffer(struct vk_ctx *ctx,
        depth_info.format = depth_att->props.format;
        depth_info.subresourceRange = sr;
 
-       if (vkCreateImageView(ctx->dev, &depth_info, 0, &atts[1]) != VK_SUCCESS) {
+    if (vkCreateImageView(ctx->dev, &depth_info, 0, &depth_att->obj.img_view) != VK_SUCCESS) {
+
                fprintf(stderr, "Failed to create depth image view for framebuffer.\n");
                vk_destroy_image(ctx, &depth_att->obj);
                goto fail;
        }
 
+    atts[0] = color_att->obj.img_view;
+       atts[1] = 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;
@@ -460,12 +513,12 @@ create_shader_module(struct vk_ctx *ctx,
 
 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)
+                uint32_t width,
+                uint32_t height,
+                uint32_t num_samples,
+                bool enable_depth,
+                bool enable_stencil,
+                struct vk_renderer *renderer)
 {
        VkVertexInputBindingDescription vert_bind_dsc[1];
        VkVertexInputAttributeDescription vert_att_dsc[1];
@@ -515,19 +568,23 @@ create_pipeline(struct vk_ctx *ctx,
        vert_bind_dsc[0].stride = stride;
        vert_bind_dsc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
 
+    /* If using vbo, we have setup vertex_info in the renderer. */
+       bool use_vbo = renderer->vertex_info.num_verts > 0;
+
        /* VkPipelineVertexInputStateCreateInfo */
        memset(&vert_input_info, 0, sizeof vert_input_info);
        vert_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
-       vert_input_info.vertexBindingDescriptionCount = 1;
+       vert_input_info.vertexBindingDescriptionCount = use_vbo ? 1 : 0;
        vert_input_info.pVertexBindingDescriptions = vert_bind_dsc;
-       vert_input_info.vertexAttributeDescriptionCount = 1;
+       vert_input_info.vertexAttributeDescriptionCount = use_vbo ? 1 : 0;
        vert_input_info.pVertexAttributeDescriptions = vert_att_dsc;
 
        /* VkPipelineInputAssemblyStateCreateInfo */
        memset(&asm_info, 0, sizeof asm_info);
        asm_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
        asm_info.topology = renderer->vertex_info.topology ?
-                           renderer->vertex_info.topology : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+                        renderer->vertex_info.topology
+                        : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
        asm_info.primitiveRestartEnable = false;
 
        /* VkViewport */
@@ -609,9 +666,9 @@ create_pipeline(struct vk_ctx *ctx,
        /* 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);
+                                      VK_COLOR_COMPONENT_G_BIT |
+                                      VK_COLOR_COMPONENT_B_BIT |
+                                      VK_COLOR_COMPONENT_A_BIT);
 
        /* VkPipelineColorBlendStateCreateInfo */
        memset(&cb_info, 0, sizeof cb_info);
@@ -670,49 +727,14 @@ create_pipeline(struct vk_ctx *ctx,
        pipeline_info.stageCount = 2;
        pipeline_info.pStages = sdr_stages;
 
-       if (vkCreateGraphicsPipelines(ctx->dev, ctx->cache, 1,
-                                     &pipeline_info, 0, &renderer->pipeline) !=
-                       VK_SUCCESS) {
+       if (vkCreateGraphicsPipelines(ctx->dev, 0, 1,
+                                  &pipeline_info, 0,
+                                  &renderer->pipeline) != VK_SUCCESS) {
                fprintf(stderr, "Failed to create graphics pipeline.\n");
                renderer->pipeline = VK_NULL_HANDLE;
        }
 }
 
-static VkAccessFlagBits
-get_access_mask(const VkImageLayout layout)
-{
-       switch (layout) {
-       case VK_IMAGE_LAYOUT_UNDEFINED:
-               return 0;
-       case VK_IMAGE_LAYOUT_GENERAL:
-               return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
-                      VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
-                      VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT |
-                      VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT |
-                      VK_ACCESS_HOST_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
-                      VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
-                      VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
-                      VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
-       case VK_IMAGE_LAYOUT_PREINITIALIZED:
-               return VK_ACCESS_HOST_WRITE_BIT;
-       case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
-               return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
-                      VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-       case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
-               return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
-       case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
-               return VK_ACCESS_TRANSFER_READ_BIT;
-       case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
-               return VK_ACCESS_TRANSFER_WRITE_BIT;
-       case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
-               return 0;
-       default:
-               return 0;
-       };
-
-       return 0;
-}
-
 static VkCommandBuffer
 create_cmd_buf(VkDevice dev, VkCommandPool cmd_pool)
 {
@@ -726,7 +748,7 @@ create_cmd_buf(VkDevice dev, VkCommandPool cmd_pool)
        alloc_info.commandPool = cmd_pool;
 
        if (vkAllocateCommandBuffers(dev, &alloc_info, &cmd_buf) != VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               return 0;
 
        return cmd_buf;
 }
@@ -753,47 +775,25 @@ get_memory_type_idx(VkPhysicalDevice pdev,
        return UINT32_MAX;
 }
 
-static VkSampleCountFlagBits
-get_num_samples(uint32_t num_samples)
-{
-       switch(num_samples) {
-       case 64:
-               return VK_SAMPLE_COUNT_64_BIT;
-       case 32:
-               return VK_SAMPLE_COUNT_32_BIT;
-       case 16:
-               return VK_SAMPLE_COUNT_16_BIT;
-       case 8:
-               return VK_SAMPLE_COUNT_8_BIT;
-       case 4:
-               return VK_SAMPLE_COUNT_4_BIT;
-       case 2:
-               return VK_SAMPLE_COUNT_2_BIT;
-       case 1:
-               break;
-       default:
-               fprintf(stderr, "Invalid number of samples in VkSampleCountFlagBits. Using one sample.\n");
-               break;
-       }
-       return VK_SAMPLE_COUNT_1_BIT;
-}
-
 static VkDeviceMemory
 alloc_memory(struct vk_ctx *ctx,
-            const VkMemoryRequirements *mem_reqs,
-            VkImage image,
-            VkBuffer buffer,
-            VkMemoryPropertyFlagBits prop_flags)
+             bool is_external,
+             const VkMemoryRequirements *mem_reqs,
+             VkImage image,
+             VkBuffer buffer,
+             VkMemoryPropertyFlagBits prop_flags)
 {
        VkExportMemoryAllocateInfo exp_mem_info;
        VkMemoryAllocateInfo mem_alloc_info;
        VkDeviceMemory mem;
        VkMemoryDedicatedAllocateInfoKHR ded_info;
 
-       memset(&exp_mem_info, 0, sizeof exp_mem_info);
-       exp_mem_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO;
-       exp_mem_info.handleTypes =
-               VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+    if (is_external) {
+        memset(&exp_mem_info, 0, sizeof exp_mem_info);
+        exp_mem_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO;
+        exp_mem_info.handleTypes =
+            VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+    }
 
        memset(&mem_alloc_info, 0, sizeof mem_alloc_info);
        mem_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
@@ -804,7 +804,7 @@ alloc_memory(struct vk_ctx *ctx,
 
        if (mem_alloc_info.memoryTypeIndex == UINT32_MAX) {
                fprintf(stderr, "No suitable memory type index found.\n");
-               return VK_NULL_HANDLE;
+               return 0;
        }
 
        if (image || buffer) {
@@ -818,7 +818,7 @@ alloc_memory(struct vk_ctx *ctx,
 
        if (vkAllocateMemory(ctx->dev, &mem_alloc_info, 0, &mem) !=
            VK_SUCCESS)
-               return VK_NULL_HANDLE;
+               return 0;
 
        return mem;
 }
@@ -845,11 +845,13 @@ alloc_image_memory(struct vk_ctx *ctx, struct vk_image_obj *img_obj)
 
        vkGetImageMemoryRequirements2(ctx->dev, &req_info2, &mem_reqs2);
        img_obj->mobj.mem = alloc_memory(ctx,
-                                        &mem_reqs2.memoryRequirements,
-                                        ded_reqs.requiresDedicatedAllocation? img_obj->img : VK_NULL_HANDLE,
-                                        VK_NULL_HANDLE,
-                                        mem_reqs2.memoryRequirements.memoryTypeBits &
-                                        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+                                     true, /* is_external = FIXME */
+                                     &mem_reqs2.memoryRequirements,
+                                     ded_reqs.requiresDedicatedAllocation ?
+                                        img_obj->img : VK_NULL_HANDLE,
+                                     VK_NULL_HANDLE,
+                                     mem_reqs2.memoryRequirements.memoryTypeBits &
+                                        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
 
        img_obj->mobj.mem_sz = mem_reqs2.memoryRequirements.size;
        img_obj->mobj.dedicated = ded_reqs.requiresDedicatedAllocation;
@@ -858,7 +860,8 @@ alloc_image_memory(struct vk_ctx *ctx, struct vk_image_obj *img_obj)
                return false;
        }
 
-       if (vkBindImageMemory(ctx->dev, img_obj->img, img_obj->mobj.mem, 0) != VK_SUCCESS) {
+       if (vkBindImageMemory(ctx->dev, img_obj->img, img_obj->mobj.mem, 0) !=
+            VK_SUCCESS) {
                fprintf(stderr, "Failed to bind image memory.\n");
                return false;
        }
@@ -869,20 +872,76 @@ alloc_image_memory(struct vk_ctx *ctx, struct vk_image_obj *img_obj)
 static bool
 are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
 {
+    VkPhysicalDeviceExternalImageFormatInfo ext_img_fmt_info;
+       VkExternalImageFormatProperties ext_img_fmt_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,
+               VK_IMAGE_USAGE_SAMPLED_BIT,
+               VK_IMAGE_USAGE_STORAGE_BIT,
+               VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+               VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+               VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
+               /* Shouldn't be used together with COLOR, DEPTH_STENCIL
+                * attachment bits:
+                * VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
+                * Provided by VK_EXT_fragment_density_map
+                * VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT,
+                * Provided by VK_NV_shading_rate_image
+                * VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV,
+                * Provided by VK_KHR_fragment_shading_rate
+                * VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR,
+                */
+       };
+       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;
+
+       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(&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;
 
        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 = 0;
+       img_fmt_info.pNext = &ext_img_fmt_info;
        img_fmt_info.format = props->format;
        img_fmt_info.type = get_image_type(props->h, props->depth);
        img_fmt_info.tiling = props->tiling;
-       img_fmt_info.usage = props->usage ? props->usage : VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
 
-       memset(&img_fmt_props, 0, sizeof img_fmt_props);
-       img_fmt_props.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+       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) {
+                       flags |= all_flags[i];
+               }
+       }
+
+       /* usage can't be null */
+       if (flags) {
+               img_fmt_info.usage = flags;
+       }
+       else {
+               fprintf(stderr, "Unsupported Vulkan format properties: usage.\n");
+               return false;
+       }
 
        if (vkGetPhysicalDeviceImageFormatProperties2
            (ctx->pdev, &img_fmt_info, &img_fmt_props) != VK_SUCCESS) {
@@ -890,10 +949,20 @@ are_props_supported(struct vk_ctx *ctx, struct vk_image_props *props)
                        "Unsupported Vulkan format properties.\n");
                return false;
        }
+       props->usage = flags;
+
+       if (props->need_export &&
+           !(ext_img_fmt_props.externalMemoryProperties.externalMemoryFeatures
+                   & export_feature_flags)) {
+               fprintf(stderr, "Unsupported Vulkan external memory features.\n");
+               return false;
+       }
 
        return true;
 }
 
+/* exposed Vulkan functions */
+
 bool
 vk_init_ctx(struct vk_ctx *ctx)
 {
@@ -928,18 +997,13 @@ vk_init_ctx_for_rendering(struct vk_ctx *ctx)
                return false;
        }
 
-       if ((ctx->cache = create_pipeline_cache(ctx->dev)) == VK_NULL_HANDLE) {
-               fprintf(stderr, "Failed to create pipeline cache.\n");
-               goto fail;
-       }
-
        if ((ctx->cmd_pool = create_cmd_pool(ctx)) == VK_NULL_HANDLE) {
                fprintf(stderr, "Failed to create command pool.\n");
                goto fail;
        }
 
        if ((ctx->cmd_buf = create_cmd_buf(ctx->dev, ctx->cmd_pool)) ==
-                       VK_NULL_HANDLE) {
+                                       VK_NULL_HANDLE) {
                fprintf(stderr, "Failed to create command buffer.\n");
                goto fail;
        }
@@ -960,20 +1024,29 @@ fail:
 void
 vk_cleanup_ctx(struct vk_ctx *ctx)
 {
-       if (ctx->cmd_buf != VK_NULL_HANDLE)
+    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;
+       }
 
-       if (ctx->cmd_pool != VK_NULL_HANDLE)
+       if (ctx->cmd_pool != VK_NULL_HANDLE) {
+               vkResetCommandPool(ctx->dev, ctx->cmd_pool,
+                           VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT);
                vkDestroyCommandPool(ctx->dev, ctx->cmd_pool, 0);
+               ctx->cmd_pool = VK_NULL_HANDLE;
+       }
 
-       if (ctx->cache != VK_NULL_HANDLE)
-               vkDestroyPipelineCache(ctx->dev, ctx->cache, 0);
-
-       if (ctx->dev != VK_NULL_HANDLE)
+       if (ctx->dev != VK_NULL_HANDLE) {
                vkDestroyDevice(ctx->dev, 0);
+               ctx->dev = VK_NULL_HANDLE;
+       }
 
-       if (ctx->inst != VK_NULL_HANDLE)
+       if (ctx->inst != VK_NULL_HANDLE) {
                vkDestroyInstance(ctx->inst, 0);
+               ctx->inst = VK_NULL_HANDLE;
+       }
 }
 
 bool
@@ -992,10 +1065,12 @@ vk_create_image(struct vk_ctx *ctx,
        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 ? props->usage : VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+       img_info.usage = props->usage ?
+                     props->usage : VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
        img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
 
@@ -1030,19 +1105,68 @@ 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)
+{
+       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.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.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.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, img))
+               goto fail;
+
+       return true;
+
+fail:
+       fprintf(stderr, "Failed to create external image.\n");
+       vk_destroy_image(ctx, img);
+       img->img = VK_NULL_HANDLE;
+       img->mobj.mem = VK_NULL_HANDLE;
+       return false;
+}
+
+bool
 vk_fill_ext_image_props(struct vk_ctx *ctx,
-                       uint32_t w,
-                       uint32_t h,
-                       uint32_t d,
-                       uint32_t num_samples,
-                       uint32_t num_levels,
-                       uint32_t num_layers,
-                       VkFormat format,
-                       VkImageTiling tiling,
-                       VkImageUsageFlagBits usage,
-                       VkImageLayout in_layout,
-                       VkImageLayout end_layout,
-                       struct vk_image_props *props)
+                        uint32_t w,
+                        uint32_t h,
+                        uint32_t d,
+                        uint32_t num_samples,
+                        uint32_t num_levels,
+                        uint32_t num_layers,
+                        VkFormat format,
+                        VkImageTiling tiling,
+                        VkImageLayout in_layout,
+                        VkImageLayout end_layout,
+                        bool need_export,
+                        struct vk_image_props *props)
 {
        props->w = w;
        props->h = h;
@@ -1053,11 +1177,11 @@ vk_fill_ext_image_props(struct vk_ctx *ctx,
        props->num_layers = num_layers;
 
        props->format = format;
-       props->usage = usage;
        props->tiling = tiling;
 
        props->in_layout = in_layout;
        props->end_layout = end_layout;
+    props->need_export = need_export;
 
        if (!are_props_supported(ctx, props))
                return false;
@@ -1067,22 +1191,23 @@ vk_fill_ext_image_props(struct vk_ctx *ctx,
 
 bool
 vk_create_renderer(struct vk_ctx *ctx,
-                  const char *vs_src,
-                  unsigned int vs_size,
-                  const char *fs_src,
-                  unsigned int fs_size,
-                  bool enable_depth,
-                  bool enable_stencil,
-                  struct vk_image_att *color_att,
-                  struct vk_image_att *depth_att,
-                  struct vk_vertex_info *vert_info,
-                  struct vk_renderer *renderer)
+                   const char *vs_src,
+                   unsigned int vs_size,
+                   const char *fs_src,
+                   unsigned int fs_size,
+                   bool enable_depth,
+                   bool enable_stencil,
+                   struct vk_image_att *color_att,
+                   struct vk_image_att *depth_att,
+                   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;
 
-       renderer->renderpass = create_renderpass(ctx, &color_att->props, &depth_att->props);
+       renderer->renderpass = create_renderpass(ctx, &color_att->props,
+                                             &depth_att->props);
        if (renderer->renderpass == VK_NULL_HANDLE)
                goto fail;
 
@@ -1099,7 +1224,8 @@ vk_create_renderer(struct vk_ctx *ctx,
                goto fail;
 
        create_pipeline(ctx, color_att->props.w, color_att->props.h,
-                       color_att->props.num_samples, enable_depth, enable_stencil, renderer);
+                    color_att->props.num_samples, enable_depth,
+                    enable_stencil, renderer);
 
        if (renderer->pipeline == VK_NULL_HANDLE)
                goto fail;
@@ -1114,29 +1240,62 @@ fail:
 
 void
 vk_destroy_renderer(struct vk_ctx *ctx,
-                   struct vk_renderer *renderer)
+                    struct vk_renderer *renderer)
 {
-       if (renderer->renderpass != VK_NULL_HANDLE)
+    if (renderer->renderpass != VK_NULL_HANDLE) {
                vkDestroyRenderPass(ctx->dev, renderer->renderpass, 0);
+               renderer->renderpass = VK_NULL_HANDLE;
+       }
 
-       if (renderer->vs != VK_NULL_HANDLE)
+       if (renderer->vs != VK_NULL_HANDLE) {
                vkDestroyShaderModule(ctx->dev, renderer->vs, 0);
+               renderer->vs = VK_NULL_HANDLE;
+       }
 
-       if (renderer->fs != VK_NULL_HANDLE)
+       if (renderer->fs != VK_NULL_HANDLE) {
                vkDestroyShaderModule(ctx->dev, renderer->fs, 0);
+               renderer->fs = VK_NULL_HANDLE;
+       }
 
-       if (renderer->pipeline != VK_NULL_HANDLE)
-               vkDestroyPipeline(ctx->dev, renderer->pipeline, 0);
-
-       if (renderer->fb != VK_NULL_HANDLE)
+       if (renderer->fb != VK_NULL_HANDLE) {
                vkDestroyFramebuffer(ctx->dev, renderer->fb, 0);
+               renderer->fb = VK_NULL_HANDLE;
+       }
 
-       if (renderer->pipeline_layout != VK_NULL_HANDLE)
+       if (renderer->pipeline != VK_NULL_HANDLE) {
+               vkDestroyPipeline(ctx->dev, renderer->pipeline, 0);
+               renderer->pipeline = VK_NULL_HANDLE;
+       }
+
+       if (renderer->pipeline_layout != VK_NULL_HANDLE) {
                vkDestroyPipelineLayout(ctx->dev, renderer->pipeline_layout, 0);
+               renderer->pipeline_layout = VK_NULL_HANDLE;
+       }
+}
+
+bool
+vk_create_ext_buffer(struct vk_ctx *ctx,
+                     uint32_t sz,
+                     VkBufferUsageFlagBits usage,
+                     struct vk_buf *bo)
+{
+       VkExternalMemoryBufferCreateInfo ext_bo_info;
+
+       memset(&ext_bo_info, 0, sizeof ext_bo_info);
+       ext_bo_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO;
+       ext_bo_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+       if (!vk_create_buffer(ctx, true, sz, usage, &ext_bo_info, bo)) {
+               fprintf(stderr, "Failed to allocate external buffer.\n");
+               return false;
+       }
+
+       return true;
 }
 
 bool
 vk_create_buffer(struct vk_ctx *ctx,
+                 bool is_external,
                                 uint32_t sz,
                                 VkBufferUsageFlagBits usage,
                                 void *pnext,
@@ -1166,9 +1325,10 @@ vk_create_buffer(struct vk_ctx *ctx,
         * vkInvalidateMappedMemoryRanges are not needed to flush host
         * writes to the device or make device writes visible to the
         * host, respectively. */
-       bo->mobj.mem = alloc_memory(ctx, &mem_reqs, VK_NULL_HANDLE, VK_NULL_HANDLE,
-                                   VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
-                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+       bo->mobj.mem = alloc_memory(ctx, is_external, &mem_reqs, VK_NULL_HANDLE,
+                                VK_NULL_HANDLE,
+                                VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
+                                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
 
        if (bo->mobj.mem == VK_NULL_HANDLE)
                goto fail;
@@ -1335,11 +1495,176 @@ vk_draw(struct vk_ctx *ctx,
 
        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) {
+               VkImageMemoryBarrier *barriers =
+                       calloc(n_attachments, sizeof(VkImageMemoryBarrier));
+               VkImageMemoryBarrier *barrier = barriers;
+               for (uint32_t n = 0; n < n_attachments; n++, barrier++) {
+                       struct vk_image_att *att = &attachments[n];
+                       VkImageAspectFlagBits depth_stencil_flags =
+                               get_aspect_from_depth_format(att->props.format);
+                       bool is_depth = (depth_stencil_flags != 0);
+
+                       /* Insert barrier to mark ownership transfer. */
+                       barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+                       barrier->oldLayout = is_depth ?
+                               VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+                       barrier->newLayout = VK_IMAGE_LAYOUT_GENERAL;
+                       barrier->srcAccessMask = get_access_mask(barrier->oldLayout);
+                       barrier->dstAccessMask = get_access_mask(barrier->newLayout);
+                       barrier->srcQueueFamilyIndex = ctx->qfam_idx;
+                       barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
+                       barrier->image = att->obj.img;
+                       barrier->subresourceRange.aspectMask = is_depth ?
+                               depth_stencil_flags :
+                               VK_IMAGE_ASPECT_COLOR_BIT;
+                       barrier->subresourceRange.baseMipLevel = 0;
+                       barrier->subresourceRange.levelCount = 1;
+                       barrier->subresourceRange.baseArrayLayer = 0;
+                       barrier->subresourceRange.layerCount = 1;
+               }
+
+               vkCmdPipelineBarrier(ctx->cmd_buf,
+                                    VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+                                    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+                                    0,
+                                    0, NULL,
+                                    0, NULL,
+                                    n_attachments, barriers);
+               free(barriers);
+       }
+
+       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);
+}
+
+void
+vk_clear_color(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,
+               bool has_wait, bool has_signal,
+               struct vk_image_att *attachments,
+               uint32_t n_attachments,
+               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;
+       VkPipelineStageFlagBits stage_flags;
+       VkImageSubresourceRange img_range;
+
+       assert(vk_fb_color_count == 4);
+
+       /* VkCommandBufferBeginInfo */
+       memset(&cmd_begin_info, 0, sizeof cmd_begin_info);
+       cmd_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+       cmd_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+
+       /* VkRect2D render area */
+       memset(&rp_area, 0, sizeof rp_area);
+       rp_area.extent.width = (uint32_t)w;
+       rp_area.extent.height = (uint32_t)h;
+       rp_area.offset.x = x;
+       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 */
+
+       memset(&clear_values[1], 0, sizeof clear_values[1]);
+       clear_values[1].depthStencil.depth = 1.0;
+       clear_values[1].depthStencil.stencil = 0;
+
+       /* VkRenderPassBeginInfo */
+       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.renderArea = rp_area;
+       rp_begin_info.clearValueCount = 2;
+       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;
+
+    /* FIXME */
+       if (has_wait) {
+               submit_info.pWaitDstStageMask = &stage_flags;
+               submit_info.waitSemaphoreCount = 1;
+               submit_info.pWaitSemaphores = &semaphores->frame_done;
+       }
+
+       if (has_signal) {
+               submit_info.signalSemaphoreCount = 1;
+               submit_info.pSignalSemaphores = &semaphores->frame_ready;
+       }
+
+       img_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+       img_range.baseMipLevel = 0;
+       img_range.levelCount = 1;
+       img_range.baseArrayLayer = 0;
+       img_range.layerCount = 1;
+
+       vkBeginCommandBuffer(ctx->cmd_buf, &cmd_begin_info);
+       vk_transition_image_layout(&attachments[0],
+                                  ctx->cmd_buf,
+                                  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                                  VK_IMAGE_LAYOUT_GENERAL,
+                                  VK_QUEUE_FAMILY_EXTERNAL,
+                                  ctx->qfam_idx);
+       vkCmdClearColorImage(ctx->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);
+
+       viewport.x = x;
+       viewport.y = y;
+       viewport.width = w;
+       viewport.height = h;
+
+       scissor.offset.x = x;
+       scissor.offset.y = y;
+       scissor.extent.width = w;
+       scissor.extent.height = h;
+
+       vkCmdSetViewport(ctx->cmd_buf, 0, 1, &viewport);
+       vkCmdSetScissor(ctx->cmd_buf, 0, 1, &scissor);
+
+       vkCmdBindPipeline(ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->pipeline);
+
+       vkCmdEndRenderPass(ctx->cmd_buf);
 
        if (attachments) {
                VkImageMemoryBarrier *barriers =
                        calloc(n_attachments, sizeof(VkImageMemoryBarrier));
                VkImageMemoryBarrier *barrier = barriers;
+
                for (uint32_t n = 0; n < n_attachments; n++, barrier++) {
                        struct vk_image_att *att = &attachments[n];
 
@@ -1353,6 +1678,8 @@ vk_draw(struct vk_ctx *ctx,
                                VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
                        barrier->newLayout = VK_IMAGE_LAYOUT_GENERAL;
+                       barrier->srcAccessMask = get_access_mask(barrier->oldLayout);
+                       barrier->dstAccessMask = get_access_mask(barrier->newLayout);
                        barrier->srcQueueFamilyIndex = ctx->qfam_idx;
                        barrier->dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
                        barrier->image = att->obj.img;
@@ -1366,7 +1693,7 @@ vk_draw(struct vk_ctx *ctx,
                }
 
                vkCmdPipelineBarrier(ctx->cmd_buf,
-                                    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+                                    VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
                                     VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
                                     0,
                                     0, NULL,
@@ -1375,22 +1702,21 @@ vk_draw(struct vk_ctx *ctx,
                free(barriers);
        }
 
-       vkCmdEndRenderPass(ctx->cmd_buf);
        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)
+       if (!semaphores && !has_wait && !has_signal)
                vkQueueWaitIdle(ctx->queue);
 }
 
 void
 vk_copy_image_to_buffer(struct vk_ctx *ctx,
-                                           struct vk_image_att *src_img,
-                                               struct vk_buf *dst_bo,
-                                               float w, float h)
+                        struct vk_image_att *src_img,
+                        struct vk_buf *dst_bo,
+                        float w, float h)
 {
        VkCommandBufferBeginInfo cmd_begin_info;
        VkSubmitInfo submit_info;
@@ -1409,11 +1735,11 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
        vkBeginCommandBuffer(ctx->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,
-                                          VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-                                          VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                                          VK_QUEUE_FAMILY_EXTERNAL,
-                                          ctx->qfam_idx);
+                                   ctx->cmd_buf,
+                                   VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                                   VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                                   VK_QUEUE_FAMILY_EXTERNAL,
+                                   ctx->qfam_idx);
 
                /* copy image to buf */
                VkBufferImageCopy copy_region = {
@@ -1421,7 +1747,8 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
                        .bufferRowLength = w,
                        .bufferImageHeight = h,
                        .imageSubresource = {
-                               .aspectMask = aspect_mask ? aspect_mask : VK_IMAGE_ASPECT_COLOR_BIT,
+                               .aspectMask = aspect_mask ? aspect_mask
+                              : VK_IMAGE_ASPECT_COLOR_BIT,
                                .mipLevel = 0,
                                .baseArrayLayer = 0,
                                .layerCount = 1,
@@ -1431,16 +1758,16 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
                 };
 
                vkCmdCopyImageToBuffer(ctx->cmd_buf,
-                                      src_img->obj.img,
-                                      VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                                      dst_bo->buf, 1, &copy_region);
+                               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,
-                                          VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                                          VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-                                          VK_QUEUE_FAMILY_EXTERNAL,
-                                          ctx->qfam_idx);
+                                   ctx->cmd_buf,
+                                   VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                                   VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                                   VK_QUEUE_FAMILY_EXTERNAL,
+                                   ctx->qfam_idx);
 
                VkBufferMemoryBarrier write_finish_buffer_memory_barrier = {
                        .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
@@ -1454,12 +1781,12 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
                };
 
                vkCmdPipelineBarrier(ctx->cmd_buf,
-                               VK_PIPELINE_STAGE_TRANSFER_BIT,
-                               VK_PIPELINE_STAGE_HOST_BIT,
-                               (VkDependencyFlags) 0, 0, NULL,
-                               1, &write_finish_buffer_memory_barrier,
-                               0, NULL);
-       }
+                             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);
 
        if (vkQueueSubmit(ctx->queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) {
@@ -1468,6 +1795,7 @@ vk_copy_image_to_buffer(struct vk_ctx *ctx,
        vkQueueWaitIdle(ctx->queue);
 }
 
+// FIXME: external
 bool
 vk_create_semaphores(struct vk_ctx *ctx,
                                         struct vk_semaphores *semaphores)
@@ -1529,12 +1857,13 @@ vk_transition_image_layout(struct vk_image_att *img_att,
        barrier.srcQueueFamilyIndex = src_queue_fam_idx;
        barrier.dstQueueFamilyIndex = dst_queue_fam_idx;
        barrier.image = img_att->obj.img;
-       barrier.subresourceRange.aspectMask = aspect_mask ? aspect_mask : VK_IMAGE_ASPECT_COLOR_BIT;
+       barrier.subresourceRange.aspectMask = aspect_mask ? aspect_mask :
+                                          VK_IMAGE_ASPECT_COLOR_BIT;
        barrier.subresourceRange.levelCount = 1;
        barrier.subresourceRange.layerCount = 1;
 
        vkCmdPipelineBarrier(cmd_buf,
-                            get_pipeline_stage_flags(old_layout),
-                            get_pipeline_stage_flags(new_layout),
-                            0, 0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, 1, &barrier);
+                         get_pipeline_stage_flags(old_layout),
+                         get_pipeline_stage_flags(new_layout),
+                         0, 0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, 1, &barrier);
 }
index 97496b8..f28930f 100644 (file)
--- a/src/vk.h
+++ b/src/vk.h
@@ -10,7 +10,6 @@ struct vk_ctx
        VkInstance inst;
        VkPhysicalDevice pdev;
        VkDevice dev;
-       VkPipelineCache cache;
 
        VkCommandPool cmd_pool;
        VkCommandBuffer cmd_buf;
@@ -38,6 +37,8 @@ struct vk_image_props
 
        VkImageLayout in_layout;
        VkImageLayout end_layout;
+
+    bool need_export;
 };
 
 struct vk_mem_obj {
@@ -48,12 +49,7 @@ struct vk_mem_obj {
 
 struct vk_image_obj {
        VkImage img;
-       struct vk_mem_obj mobj;
-};
-
-struct vk_buf
-{
-       VkBuffer buf;
+    VkImageView img_view;
        struct vk_mem_obj mobj;
 };
 
@@ -70,6 +66,13 @@ struct vk_vertex_info
        VkPrimitiveTopology topology;
 };
 
+struct vk_buf
+{
+       VkBuffer buf;
+       struct vk_mem_obj mobj;
+};
+
+
 struct vk_renderer
 {
        VkPipeline pipeline;
@@ -120,15 +123,16 @@ vk_fill_ext_image_props(struct vk_ctx *ctx,
                                                uint32_t num_layers,
                                                VkFormat format,
                                                VkImageTiling tiling,
-                                               VkImageUsageFlagBits usage,
                                                VkImageLayout in_layout,
                                                VkImageLayout end_layout,
+                        bool need_export,
                                                struct vk_image_props *props);
 
 /* buffers */
 
 bool
 vk_create_buffer(struct vk_ctx *ctx,
+                 bool is_external,
                                 uint32_t sz,
                                 VkBufferUsageFlagBits usage,
                                 void *pnext,
@@ -142,6 +146,13 @@ vk_update_buffer_data(struct vk_ctx *ctx,
                                          uint32_t data_sz,
                                          struct vk_buf *bo);
 
+bool
+vk_create_ext_buffer(struct vk_ctx *ctx,
+                    uint32_t sz,
+                    VkBufferUsageFlagBits usage,
+                    struct vk_buf *bo);
+
+
 /* semaphores */
 
 bool
@@ -183,6 +194,18 @@ vk_draw(struct vk_ctx *ctx,
                uint32_t n_attachments,
                float x, float y, float w, float h);
 
+void
+vk_clear_color(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,
+               bool has_wait, bool has_signal,
+               struct vk_image_att *attachments,
+               uint32_t n_attachments,
+               float x, float y, float w, float h);
+
 /* transitions */
 
 void