+ image->size = mem_reqs.memoryRequirements.size;
+}
+
+void
+vk_create_image(struct vk_core *core, VkFormat format,
+ unsigned w, unsigned h, unsigned z, unsigned num_samples,
+ unsigned num_levels, unsigned num_layers,
+ VkImageUsageFlagBits usage, VkImageTiling tiling,
+ struct vk_image *image)
+{
+ const VkExternalMemoryImageCreateInfoKHR ext_image_info = {
+ .sType =
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR,
+ .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
+ };
+ const VkImageCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = &ext_image_info,
+ .imageType = vk_get_image_type(h, z),
+ .tiling = tiling,
+ .mipLevels = num_levels,
+ .arrayLayers = num_layers,
+ .samples = (VkSampleCountFlagBits)num_samples,
+ .format = format,
+ .extent = {
+ .width = w,
+ .height = h,
+ .depth = z,
+ },
+ .usage = usage,
+ };
+
+ image->image = VK_NULL_HANDLE;
+ image->mem = VK_NULL_HANDLE;
+
+ if (vkCreateImage(
+ core->dev, &info, NULL, &image->image) != VK_SUCCESS)
+ goto fail;
+
+ vk_alloc_image_mem(core, image);
+ if (image->mem == VK_NULL_HANDLE)
+ goto fail;
+
+ if (vkBindImageMemory(
+ core->dev, image->image, image->mem, 0) != VK_SUCCESS)
+ goto fail;
+
+ return;
+
+fail:
+ vk_destroy_image(core->dev, image);
+ image->image = VK_NULL_HANDLE;
+ image->mem = VK_NULL_HANDLE;
+}
+
+static VkImageView
+vk_create_image_view(VkDevice dev, VkImage image, VkFormat format,
+ VkImageAspectFlagBits aspect_mask,
+ unsigned base_level, unsigned level_count,
+ unsigned base_layer, unsigned layer_count)
+{
+ const VkImageViewType type = layer_count > 1 ?
+ VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
+
+ const VkImageViewCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .viewType = type,
+ .image = image,
+ .format = format,
+ .subresourceRange = {
+ .aspectMask = aspect_mask,
+ .baseMipLevel = base_level,
+ .levelCount = level_count,
+ .baseArrayLayer = base_layer,
+ .layerCount = layer_count,
+ },
+ };
+ VkImageView view = VK_NULL_HANDLE;
+
+ if (vkCreateImageView(dev, &info, NULL, &view) != VK_SUCCESS)
+ view = VK_NULL_HANDLE;
+
+ return view;
+}
+
+static void
+vk_create_attachment(struct vk_core *core, VkFormat format,
+ unsigned w, unsigned h, unsigned num_samples,
+ VkImageAspectFlagBits aspect_mask,
+ VkImageUsageFlagBits usage, VkImageTiling tiling,
+ unsigned base_level, unsigned level_count,
+ unsigned base_layer, unsigned layer_count,
+ struct vk_attachment *att)
+{
+ att->image.image = VK_NULL_HANDLE;
+ vk_create_image(core, format, w, h, 1, num_samples,
+ level_count, layer_count, usage, tiling, &att->image);
+ if (att->image.image == VK_NULL_HANDLE)
+ return;
+
+ att->view = vk_create_image_view(
+ core->dev, att->image.image, format, aspect_mask,
+ base_level, level_count, base_layer, layer_count);
+ if (att->view == VK_NULL_HANDLE) {
+ vk_destroy_image(core->dev, &att->image);
+ att->image.image = VK_NULL_HANDLE;
+ att->image.mem = VK_NULL_HANDLE;
+ }
+}
+
+static VkRenderPass
+vk_create_fb_render_pass(VkDevice dev, unsigned num_samples,
+ VkFormat color_format, VkFormat depth_format)
+{
+ const bool has_depth = depth_format != VK_FORMAT_UNDEFINED;
+ const VkAttachmentDescription attachments[] = {
+ {
+ .samples = (VkSampleCountFlagBits)num_samples,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .format = color_format
+ },
+ {
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .format = depth_format
+ }
+ };
+ const VkAttachmentReference color_ref = {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_GENERAL,
+ };
+ const VkAttachmentReference depth_ref = {
+ .attachment = 1,
+ .layout = VK_IMAGE_LAYOUT_GENERAL,
+ };
+ const VkSubpassDescription sub_pass = {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &color_ref,
+ .pDepthStencilAttachment = has_depth ? &depth_ref : NULL,
+ };
+ const VkRenderPassCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .attachmentCount = has_depth ? 2 : 1,
+ .pAttachments = attachments,
+ .subpassCount = 1,
+ .pSubpasses = &sub_pass,
+ };
+ VkRenderPass pass = VK_NULL_HANDLE;
+ if (vkCreateRenderPass(dev, &info, NULL, &pass) != VK_SUCCESS)
+ pass = VK_NULL_HANDLE;
+
+ return pass;
+}
+
+static VkFramebuffer
+vk_create_fb(VkDevice dev, VkRenderPass render_pass,
+ unsigned w, unsigned h, unsigned layers,
+ VkImageView color_view, VkImageView depth_view)
+{
+ const VkImageView attachments[2] = { color_view, depth_view };
+ const VkFramebufferCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .renderPass = render_pass,
+ .width = w,
+ .height = h,
+ .layers = layers,
+ .attachmentCount = depth_view != VK_NULL_HANDLE ? 2 : 1,
+ .pAttachments = attachments,
+ };
+ VkFramebuffer fb = VK_NULL_HANDLE;
+
+ if (vkCreateFramebuffer(dev, &info, NULL, &fb) != VK_SUCCESS)
+ fb = VK_NULL_HANDLE;
+
+ return fb;
+}
+
+static VkImageAspectFlagBits
+vk_get_depth_stencil_aspect_mask(VkFormat format)
+{
+ switch (format) {
+ case VK_FORMAT_D16_UNORM:
+ case VK_FORMAT_X8_D24_UNORM_PACK32:
+ case VK_FORMAT_D32_SFLOAT:
+ return VK_IMAGE_ASPECT_DEPTH_BIT;
+ case VK_FORMAT_S8_UINT:
+ return VK_IMAGE_ASPECT_STENCIL_BIT;
+ case VK_FORMAT_D16_UNORM_S8_UINT:
+ case VK_FORMAT_D24_UNORM_S8_UINT:
+ case VK_FORMAT_D32_SFLOAT_S8_UINT:
+ return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ default:
+ assert(!"Invalid depthstencil format");
+ }
+}
+
+static void
+vk_destroy_attachment(VkDevice dev, struct vk_attachment *att)
+{
+ if (att->view != VK_NULL_HANDLE)
+ vkDestroyImageView(dev, att->view, NULL);
+
+ vk_destroy_image(dev, &att->image);
+}
+
+void
+vk_fb_destroy(VkDevice dev, struct vk_fb *fb)
+{
+ if (fb->fb != VK_NULL_HANDLE)
+ vkDestroyFramebuffer(dev, fb->fb, NULL);
+
+ if (fb->render_pass != VK_NULL_HANDLE)
+ vkDestroyRenderPass(dev, fb->render_pass, NULL);
+
+ vk_destroy_attachment(dev, &fb->color);
+ vk_destroy_attachment(dev, &fb->depth);
+}
+
+void
+vk_setup_fb(struct vk_core *core,
+ unsigned w, unsigned h, unsigned num_samples,
+ VkFormat color_format, VkImageTiling color_tiling,
+ VkFormat depth_format, VkImageTiling depth_tiling,
+ unsigned layers, struct vk_fb *fb)
+{
+ const VkImageUsageFlagBits usage = VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+
+ (void)memset(fb, 0, sizeof(*fb));
+
+ if (color_format != VK_FORMAT_UNDEFINED) {
+ vk_create_attachment(
+ core, color_format, w, h, num_samples,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ usage | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ color_tiling, 0, 1, 0, layers, &fb->color);
+ if (fb->color.image.image == VK_NULL_HANDLE)
+ return;
+ }
+
+ if (depth_format != VK_FORMAT_UNDEFINED) {
+ const VkImageAspectFlagBits aspect_mask =
+ vk_get_depth_stencil_aspect_mask(depth_format);
+
+ vk_create_attachment(
+ core, depth_format, w, h, num_samples,
+ aspect_mask,
+ usage | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ depth_tiling, 0, 1, 0, layers, &fb->depth);
+ if (fb->depth.image.image == VK_NULL_HANDLE)
+ goto fail;
+ }
+
+ fb->render_pass = vk_create_fb_render_pass(
+ core->dev, num_samples, color_format, depth_format);
+ if (fb->render_pass == VK_NULL_HANDLE)
+ goto fail;
+
+ fb->fb = vk_create_fb(
+ core->dev, fb->render_pass, w, h, layers,
+ fb->color.view, fb->depth.view);
+ if (fb->fb != VK_NULL_HANDLE)
+ return;
+
+fail:
+ vk_fb_destroy(core->dev, fb);
+ (void)memset(fb, 0, sizeof(*fb));
+}