/*
 * Copyright (c) 2026-present, the Ladybird developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

// clang-format off
#include <vulkan/vulkan.h>
// clang-format on

#include <AK/Math.h>
#include <AK/Optional.h>
#include <LibGfx/SharedImageBuffer.h>
#include <UI/Qt/NativeWindowContainer.h>
#include <UI/Qt/WebContentView.h>

#include <QByteArrayList>
#include <QCursor>
#include <QGuiApplication>
#include <QRegion>
#include <QTimer>
#include <QVersionNumber>
#include <QVulkanInstance>
#include <QVulkanWindow>
#include <QWidget>
#include <libdrm/drm_fourcc.h>
#include <sys/stat.h>
#include <unistd.h>

namespace Ladybird {

static QByteArrayList vulkan_dmabuf_device_extensions()
{
    return {
        "VK_KHR_external_memory",
        "VK_KHR_external_memory_fd",
        "VK_EXT_external_memory_dma_buf",
        "VK_EXT_image_drm_format_modifier",
        "VK_KHR_image_format_list",
    };
}

static QVulkanInstance* vulkan_instance()
{
    static QVulkanInstance* instance = []() -> QVulkanInstance* {
        auto* instance = new QVulkanInstance;
        auto supported_version = instance->supportedApiVersion();
        if (supported_version >= QVersionNumber(1, 4))
            instance->setApiVersion(QVersionNumber(1, 4));
        else if (supported_version >= QVersionNumber(1, 3))
            instance->setApiVersion(QVersionNumber(1, 3));
        else if (supported_version >= QVersionNumber(1, 2))
            instance->setApiVersion(QVersionNumber(1, 2));
        else if (supported_version >= QVersionNumber(1, 1))
            instance->setApiVersion(QVersionNumber(1, 1));
        else
            instance->setApiVersion(QVersionNumber(1, 0));

        if (!instance->create()) {
            delete instance;
            return nullptr;
        }
        return instance;
    }();
    return instance;
}

#include <WebContentViewLinuxFragShader.h>
#include <WebContentViewLinuxVertShader.h>

static Optional<u32> find_memory_type_index(VkPhysicalDevice physical_device, u32 memory_type_bits)
{
    VkPhysicalDeviceMemoryProperties memory_properties;
    vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);

    for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) {
        if (memory_type_bits & (1u << i))
            return i;
    }

    return {};
}

static VkFormat vk_format_from_drm_format(u32 drm_format)
{
    switch (drm_format) {
    case DRM_FORMAT_ARGB8888:
        return VK_FORMAT_B8G8R8A8_UNORM;
    default:
        return VK_FORMAT_UNDEFINED;
    }
}

struct WebContentView::VulkanRenderer {
    struct DmaBufIdentity {
        u64 device { 0 };
        u64 inode { 0 };
        Gfx::IntSize size;
        u32 drm_format { 0 };
        size_t pitch { 0 };
        u64 modifier { 0 };

        bool operator==(DmaBufIdentity const&) const = default;
    };

    struct ImportedTexture {
        VkDevice device { VK_NULL_HANDLE };
        VkDescriptorPool descriptor_pool { VK_NULL_HANDLE };
        DmaBufIdentity dmabuf_identity;
        VkImage image { VK_NULL_HANDLE };
        VkDeviceMemory memory { VK_NULL_HANDLE };
        VkImageView image_view { VK_NULL_HANDLE };
        VkDescriptorSet descriptor_set { VK_NULL_HANDLE };
        Gfx::IntSize size;

        ~ImportedTexture()
        {
            if (descriptor_set != VK_NULL_HANDLE)
                vkFreeDescriptorSets(device, descriptor_pool, 1, &descriptor_set);
            if (image_view != VK_NULL_HANDLE)
                vkDestroyImageView(device, image_view, nullptr);
            if (image != VK_NULL_HANDLE)
                vkDestroyImage(device, image, nullptr);
            if (memory != VK_NULL_HANDLE)
                vkFreeMemory(device, memory, nullptr);
        }
    };

    struct PushConstants {
        float target_size[2];
        float content_size[2];
        float source_size[2];
    };

    ~VulkanRenderer()
    {
        release();
    }

    void release_imported_textures()
    {
        for (auto& texture : imported_textures)
            texture = nullptr;
        next_imported_texture_slot = 0;
    }

    void release_pipeline()
    {
        if (pipeline != VK_NULL_HANDLE) {
            vkDestroyPipeline(device, pipeline, nullptr);
            pipeline = VK_NULL_HANDLE;
        }
        render_pass = VK_NULL_HANDLE;
    }

    void release()
    {
        if (device != VK_NULL_HANDLE)
            vkDeviceWaitIdle(device);

        release_imported_textures();
        release_pipeline();

        if (descriptor_pool != VK_NULL_HANDLE) {
            vkDestroyDescriptorPool(device, descriptor_pool, nullptr);
            descriptor_pool = VK_NULL_HANDLE;
        }
        if (pipeline_layout != VK_NULL_HANDLE) {
            vkDestroyPipelineLayout(device, pipeline_layout, nullptr);
            pipeline_layout = VK_NULL_HANDLE;
        }
        if (descriptor_set_layout != VK_NULL_HANDLE) {
            vkDestroyDescriptorSetLayout(device, descriptor_set_layout, nullptr);
            descriptor_set_layout = VK_NULL_HANDLE;
        }
        if (sampler != VK_NULL_HANDLE) {
            vkDestroySampler(device, sampler, nullptr);
            sampler = VK_NULL_HANDLE;
        }

        get_memory_fd_properties = nullptr;
        physical_device = VK_NULL_HANDLE;
        device = VK_NULL_HANDLE;
    }

    bool prepare(QVulkanWindow& window)
    {
        auto new_device = window.device();
        auto new_physical_device = window.physicalDevice();
        auto new_render_pass = window.defaultRenderPass();

        if (new_device == VK_NULL_HANDLE || new_physical_device == VK_NULL_HANDLE || new_render_pass == VK_NULL_HANDLE)
            return false;

        if (device != new_device || physical_device != new_physical_device) {
            release();
            device = new_device;
            physical_device = new_physical_device;
            get_memory_fd_properties = reinterpret_cast<PFN_vkGetMemoryFdPropertiesKHR>(vkGetDeviceProcAddr(device, "vkGetMemoryFdPropertiesKHR"));
            if (!get_memory_fd_properties)
                return false;
        }

        if (!ensure_sampler() || !ensure_descriptor_set_layout() || !ensure_pipeline_layout() || !ensure_descriptor_pool())
            return false;

        if (render_pass != new_render_pass) {
            release_pipeline();
            render_pass = new_render_pass;
        }

        return ensure_pipeline();
    }

    bool ensure_sampler()
    {
        if (sampler != VK_NULL_HANDLE)
            return true;

        VkSamplerCreateInfo sampler_info {
            .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .magFilter = VK_FILTER_NEAREST,
            .minFilter = VK_FILTER_NEAREST,
            .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
            .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
            .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
            .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
            .mipLodBias = 0.0f,
            .anisotropyEnable = VK_FALSE,
            .maxAnisotropy = 1.0f,
            .compareEnable = VK_FALSE,
            .compareOp = VK_COMPARE_OP_ALWAYS,
            .minLod = 0.0f,
            .maxLod = 0.0f,
            .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
            .unnormalizedCoordinates = VK_FALSE,
        };
        return vkCreateSampler(device, &sampler_info, nullptr, &sampler) == VK_SUCCESS;
    }

    bool ensure_descriptor_set_layout()
    {
        if (descriptor_set_layout != VK_NULL_HANDLE)
            return true;

        VkDescriptorSetLayoutBinding binding {
            .binding = 0,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
            .pImmutableSamplers = nullptr,
        };
        VkDescriptorSetLayoutCreateInfo layout_info {
            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .bindingCount = 1,
            .pBindings = &binding,
        };
        return vkCreateDescriptorSetLayout(device, &layout_info, nullptr, &descriptor_set_layout) == VK_SUCCESS;
    }

    bool ensure_pipeline_layout()
    {
        if (pipeline_layout != VK_NULL_HANDLE)
            return true;

        VkPushConstantRange push_constant_range {
            .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
            .offset = 0,
            .size = sizeof(PushConstants),
        };
        VkPipelineLayoutCreateInfo pipeline_layout_info {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .setLayoutCount = 1,
            .pSetLayouts = &descriptor_set_layout,
            .pushConstantRangeCount = 1,
            .pPushConstantRanges = &push_constant_range,
        };
        return vkCreatePipelineLayout(device, &pipeline_layout_info, nullptr, &pipeline_layout) == VK_SUCCESS;
    }

    bool ensure_descriptor_pool()
    {
        if (descriptor_pool != VK_NULL_HANDLE)
            return true;

        VkDescriptorPoolSize pool_size {
            .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 8,
        };
        VkDescriptorPoolCreateInfo pool_info {
            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
            .pNext = nullptr,
            .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
            .maxSets = 8,
            .poolSizeCount = 1,
            .pPoolSizes = &pool_size,
        };
        return vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptor_pool) == VK_SUCCESS;
    }

    VkShaderModule create_shader_module(u32 const* code, size_t code_size)
    {
        VkShaderModuleCreateInfo shader_module_info {
            .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .codeSize = code_size,
            .pCode = code,
        };

        VkShaderModule shader_module { VK_NULL_HANDLE };
        if (vkCreateShaderModule(device, &shader_module_info, nullptr, &shader_module) != VK_SUCCESS)
            return VK_NULL_HANDLE;
        return shader_module;
    }

    bool ensure_pipeline()
    {
        if (pipeline != VK_NULL_HANDLE)
            return true;

        auto vertex_shader = create_shader_module(webcontent_vert_shader_spv, sizeof(webcontent_vert_shader_spv));
        auto fragment_shader = create_shader_module(webcontent_frag_shader_spv, sizeof(webcontent_frag_shader_spv));
        if (vertex_shader == VK_NULL_HANDLE || fragment_shader == VK_NULL_HANDLE) {
            if (vertex_shader != VK_NULL_HANDLE)
                vkDestroyShaderModule(device, vertex_shader, nullptr);
            if (fragment_shader != VK_NULL_HANDLE)
                vkDestroyShaderModule(device, fragment_shader, nullptr);
            return false;
        }

        VkPipelineShaderStageCreateInfo shader_stages[] {
            {
                .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
                .pNext = nullptr,
                .flags = 0,
                .stage = VK_SHADER_STAGE_VERTEX_BIT,
                .module = vertex_shader,
                .pName = "main",
                .pSpecializationInfo = nullptr,
            },
            {
                .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
                .pNext = nullptr,
                .flags = 0,
                .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
                .module = fragment_shader,
                .pName = "main",
                .pSpecializationInfo = nullptr,
            },
        };

        VkPipelineVertexInputStateCreateInfo vertex_input {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .vertexBindingDescriptionCount = 0,
            .pVertexBindingDescriptions = nullptr,
            .vertexAttributeDescriptionCount = 0,
            .pVertexAttributeDescriptions = nullptr,
        };
        VkPipelineInputAssemblyStateCreateInfo input_assembly {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
            .primitiveRestartEnable = VK_FALSE,
        };
        VkPipelineViewportStateCreateInfo viewport_state {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .viewportCount = 1,
            .pViewports = nullptr,
            .scissorCount = 1,
            .pScissors = nullptr,
        };
        VkPipelineRasterizationStateCreateInfo rasterization_state {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .depthClampEnable = VK_FALSE,
            .rasterizerDiscardEnable = VK_FALSE,
            .polygonMode = VK_POLYGON_MODE_FILL,
            .cullMode = VK_CULL_MODE_NONE,
            .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
            .depthBiasEnable = VK_FALSE,
            .depthBiasConstantFactor = 0.0f,
            .depthBiasClamp = 0.0f,
            .depthBiasSlopeFactor = 0.0f,
            .lineWidth = 1.0f,
        };
        VkPipelineMultisampleStateCreateInfo multisample_state {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
            .sampleShadingEnable = VK_FALSE,
            .minSampleShading = 1.0f,
            .pSampleMask = nullptr,
            .alphaToCoverageEnable = VK_FALSE,
            .alphaToOneEnable = VK_FALSE,
        };
        VkPipelineColorBlendAttachmentState color_blend_attachment {
            .blendEnable = VK_FALSE,
            .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
            .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
            .colorBlendOp = VK_BLEND_OP_ADD,
            .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
            .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
            .alphaBlendOp = VK_BLEND_OP_ADD,
            .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
        };
        VkPipelineColorBlendStateCreateInfo color_blend_state {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .logicOpEnable = VK_FALSE,
            .logicOp = VK_LOGIC_OP_COPY,
            .attachmentCount = 1,
            .pAttachments = &color_blend_attachment,
            .blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f },
        };
        VkDynamicState dynamic_states[] {
            VK_DYNAMIC_STATE_VIEWPORT,
            VK_DYNAMIC_STATE_SCISSOR,
        };
        VkPipelineDynamicStateCreateInfo dynamic_state {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .dynamicStateCount = 2,
            .pDynamicStates = dynamic_states,
        };
        VkPipelineDepthStencilStateCreateInfo depth_stencil_state {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .depthTestEnable = VK_FALSE,
            .depthWriteEnable = VK_FALSE,
            .depthCompareOp = VK_COMPARE_OP_ALWAYS,
            .depthBoundsTestEnable = VK_FALSE,
            .stencilTestEnable = VK_FALSE,
            .front = {},
            .back = {},
            .minDepthBounds = 0.0f,
            .maxDepthBounds = 1.0f,
        };
        VkGraphicsPipelineCreateInfo pipeline_info {
            .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .stageCount = 2,
            .pStages = shader_stages,
            .pVertexInputState = &vertex_input,
            .pInputAssemblyState = &input_assembly,
            .pTessellationState = nullptr,
            .pViewportState = &viewport_state,
            .pRasterizationState = &rasterization_state,
            .pMultisampleState = &multisample_state,
            .pDepthStencilState = &depth_stencil_state,
            .pColorBlendState = &color_blend_state,
            .pDynamicState = &dynamic_state,
            .layout = pipeline_layout,
            .renderPass = render_pass,
            .subpass = 0,
            .basePipelineHandle = VK_NULL_HANDLE,
            .basePipelineIndex = -1,
        };

        auto result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &pipeline);
        vkDestroyShaderModule(device, vertex_shader, nullptr);
        vkDestroyShaderModule(device, fragment_shader, nullptr);
        return result == VK_SUCCESS;
    }

    static Optional<DmaBufIdentity> dmabuf_identity_for(Gfx::LinuxDmaBufHandle const& dmabuf)
    {
        struct stat statbuf {};
        if (fstat(dmabuf.file.fd(), &statbuf) < 0)
            return {};

        return DmaBufIdentity {
            .device = static_cast<u64>(statbuf.st_dev),
            .inode = static_cast<u64>(statbuf.st_ino),
            .size = dmabuf.size,
            .drm_format = dmabuf.drm_format,
            .pitch = dmabuf.pitch,
            .modifier = dmabuf.modifier,
        };
    }

    ImportedTexture* imported_texture_for(Gfx::SharedImageBuffer const& shared_image_buffer)
    {
        auto const* dmabuf = shared_image_buffer.linux_dmabuf_handle();
        if (!dmabuf)
            return nullptr;

        auto dmabuf_identity = dmabuf_identity_for(*dmabuf);
        if (!dmabuf_identity.has_value())
            return nullptr;

        for (auto& imported_texture : imported_textures) {
            if (imported_texture && imported_texture->dmabuf_identity == *dmabuf_identity)
                return imported_texture.ptr();
        }

        auto imported_texture = import_texture(*dmabuf, *dmabuf_identity);
        if (!imported_texture)
            return nullptr;

        auto slot = next_imported_texture_slot;
        imported_textures[slot] = AK::move(imported_texture);
        next_imported_texture_slot = (next_imported_texture_slot + 1) % imported_texture_slot_count;
        return imported_textures[slot].ptr();
    }

    bool can_render(Gfx::SharedImageBuffer const& shared_image_buffer)
    {
        return imported_texture_for(shared_image_buffer);
    }

    OwnPtr<ImportedTexture> import_texture(Gfx::LinuxDmaBufHandle const& dmabuf, DmaBufIdentity const& dmabuf_identity)
    {
        auto vk_format = vk_format_from_drm_format(dmabuf.drm_format);
        if (vk_format == VK_FORMAT_UNDEFINED || dmabuf.modifier != DRM_FORMAT_MOD_LINEAR)
            return {};

        VkImageDrmFormatModifierListCreateInfoEXT modifier_list {
            .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
            .pNext = nullptr,
            .drmFormatModifierCount = 1,
            .pDrmFormatModifiers = &dmabuf.modifier,
        };
        VkExternalMemoryImageCreateInfo external_memory_image_info {
            .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
            .pNext = &modifier_list,
            .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
        };
        VkImageCreateInfo image_info {
            .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
            .pNext = &external_memory_image_info,
            .flags = 0,
            .imageType = VK_IMAGE_TYPE_2D,
            .format = vk_format,
            .extent = {
                .width = static_cast<u32>(dmabuf.size.width()),
                .height = static_cast<u32>(dmabuf.size.height()),
                .depth = 1,
            },
            .mipLevels = 1,
            .arrayLayers = 1,
            .samples = VK_SAMPLE_COUNT_1_BIT,
            .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
            .usage = VK_IMAGE_USAGE_SAMPLED_BIT,
            .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
            .queueFamilyIndexCount = 0,
            .pQueueFamilyIndices = nullptr,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        };

        VkImage image { VK_NULL_HANDLE };
        if (vkCreateImage(device, &image_info, nullptr, &image) != VK_SUCCESS)
            return {};

        VkImageSubresource subresource { VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT, 0, 0 };
        VkSubresourceLayout subresource_layout {};
        vkGetImageSubresourceLayout(device, image, &subresource, &subresource_layout);
        if (subresource_layout.rowPitch != dmabuf.pitch) {
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        VkMemoryRequirements memory_requirements;
        vkGetImageMemoryRequirements(device, image, &memory_requirements);

        VkMemoryFdPropertiesKHR memory_fd_properties {
            .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR,
            .pNext = nullptr,
            .memoryTypeBits = 0,
        };
        if (get_memory_fd_properties(device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, dmabuf.file.fd(), &memory_fd_properties) != VK_SUCCESS) {
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        auto memory_type_index = find_memory_type_index(physical_device, memory_requirements.memoryTypeBits & memory_fd_properties.memoryTypeBits);
        if (!memory_type_index.has_value()) {
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        int imported_fd = dup(dmabuf.file.fd());
        if (imported_fd < 0) {
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        VkMemoryDedicatedAllocateInfo dedicated_allocate_info {
            .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
            .pNext = nullptr,
            .image = image,
            .buffer = VK_NULL_HANDLE,
        };
        VkImportMemoryFdInfoKHR import_memory_fd_info {
            .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
            .pNext = &dedicated_allocate_info,
            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
            .fd = imported_fd,
        };
        VkMemoryAllocateInfo memory_allocate_info {
            .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
            .pNext = &import_memory_fd_info,
            .allocationSize = memory_requirements.size,
            .memoryTypeIndex = *memory_type_index,
        };

        VkDeviceMemory memory { VK_NULL_HANDLE };
        if (vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory) != VK_SUCCESS) {
            ::close(imported_fd);
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        if (vkBindImageMemory(device, image, memory, 0) != VK_SUCCESS) {
            vkFreeMemory(device, memory, nullptr);
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        VkImageViewCreateInfo image_view_info {
            .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .image = image,
            .viewType = VK_IMAGE_VIEW_TYPE_2D,
            .format = vk_format,
            .components = {
                .r = VK_COMPONENT_SWIZZLE_IDENTITY,
                .g = VK_COMPONENT_SWIZZLE_IDENTITY,
                .b = VK_COMPONENT_SWIZZLE_IDENTITY,
                .a = VK_COMPONENT_SWIZZLE_IDENTITY,
            },
            .subresourceRange = {
                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                .baseMipLevel = 0,
                .levelCount = 1,
                .baseArrayLayer = 0,
                .layerCount = 1,
            },
        };
        VkImageView image_view { VK_NULL_HANDLE };
        if (vkCreateImageView(device, &image_view_info, nullptr, &image_view) != VK_SUCCESS) {
            vkFreeMemory(device, memory, nullptr);
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        VkDescriptorSet descriptor_set { VK_NULL_HANDLE };
        VkDescriptorSetAllocateInfo descriptor_set_allocate_info {
            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
            .pNext = nullptr,
            .descriptorPool = descriptor_pool,
            .descriptorSetCount = 1,
            .pSetLayouts = &descriptor_set_layout,
        };
        if (vkAllocateDescriptorSets(device, &descriptor_set_allocate_info, &descriptor_set) != VK_SUCCESS) {
            vkDestroyImageView(device, image_view, nullptr);
            vkFreeMemory(device, memory, nullptr);
            vkDestroyImage(device, image, nullptr);
            return {};
        }

        VkDescriptorImageInfo descriptor_image_info {
            .sampler = sampler,
            .imageView = image_view,
            .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
        };
        VkWriteDescriptorSet descriptor_write {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .pNext = nullptr,
            .dstSet = descriptor_set,
            .dstBinding = 0,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .pImageInfo = &descriptor_image_info,
            .pBufferInfo = nullptr,
            .pTexelBufferView = nullptr,
        };
        vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr);

        auto imported_texture = make<ImportedTexture>();
        imported_texture->device = device;
        imported_texture->descriptor_pool = descriptor_pool;
        imported_texture->dmabuf_identity = dmabuf_identity;
        imported_texture->image = image;
        imported_texture->memory = memory;
        imported_texture->image_view = image_view;
        imported_texture->descriptor_set = descriptor_set;
        imported_texture->size = dmabuf.size;
        return imported_texture;
    }

    bool render(VkCommandBuffer command_buffer, Gfx::SharedImageBuffer const& shared_image_buffer, Gfx::IntSize bitmap_size, QSize target_size)
    {
        auto* imported_texture = imported_texture_for(shared_image_buffer);
        if (!imported_texture)
            return false;

        if (command_buffer == VK_NULL_HANDLE)
            return false;

        auto content_width = min(bitmap_size.width(), target_size.width());
        auto content_height = min(bitmap_size.height(), target_size.height());
        if (content_width <= 0 || content_height <= 0)
            return false;

        PushConstants push_constants {
            { static_cast<float>(target_size.width()), static_cast<float>(target_size.height()) },
            { static_cast<float>(content_width), static_cast<float>(content_height) },
            { static_cast<float>(imported_texture->size.width()), static_cast<float>(imported_texture->size.height()) },
        };

        VkViewport viewport {
            .x = 0.0f,
            .y = 0.0f,
            .width = static_cast<float>(target_size.width()),
            .height = static_cast<float>(target_size.height()),
            .minDepth = 0.0f,
            .maxDepth = 1.0f,
        };
        VkRect2D scissor {
            .offset = { 0, 0 },
            .extent = { static_cast<u32>(target_size.width()), static_cast<u32>(target_size.height()) },
        };

        vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
        vkCmdSetViewport(command_buffer, 0, 1, &viewport);
        vkCmdSetScissor(command_buffer, 0, 1, &scissor);
        vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &imported_texture->descriptor_set, 0, nullptr);
        vkCmdPushConstants(command_buffer, pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_constants), &push_constants);
        vkCmdDraw(command_buffer, 4, 1, 0, 0);
        return true;
    }

    VkDevice device { VK_NULL_HANDLE };
    VkPhysicalDevice physical_device { VK_NULL_HANDLE };
    VkRenderPass render_pass { VK_NULL_HANDLE };
    VkSampler sampler { VK_NULL_HANDLE };
    VkDescriptorSetLayout descriptor_set_layout { VK_NULL_HANDLE };
    VkDescriptorPool descriptor_pool { VK_NULL_HANDLE };
    VkPipelineLayout pipeline_layout { VK_NULL_HANDLE };
    VkPipeline pipeline { VK_NULL_HANDLE };
    PFN_vkGetMemoryFdPropertiesKHR get_memory_fd_properties { nullptr };
    static constexpr size_t imported_texture_slot_count { 2 };
    OwnPtr<ImportedTexture> imported_textures[imported_texture_slot_count];
    size_t next_imported_texture_slot { 0 };
};

struct WebContentView::VulkanWindowRenderer final : public QVulkanWindowRenderer {
    VulkanWindowRenderer(WebContentView& view, QVulkanWindow& window)
        : m_view(view)
        , m_window(window)
    {
    }

    virtual void releaseSwapChainResources() override
    {
        m_renderer.release_pipeline();
    }

    virtual void releaseResources() override
    {
        m_renderer.release();
    }

    virtual void logicalDeviceLost() override
    {
        m_renderer.release();
    }

    virtual void physicalDeviceLost() override
    {
        m_renderer.release();
    }

    virtual void startNextFrame() override
    {
        m_view.update_vulkan_alpha_blending_support();

        auto command_buffer = m_window.currentCommandBuffer();
        auto target_size = m_window.swapChainImageSize();
        auto render_pass = m_window.defaultRenderPass();
        auto framebuffer = m_window.currentFramebuffer();

        if (command_buffer == VK_NULL_HANDLE || render_pass == VK_NULL_HANDLE || framebuffer == VK_NULL_HANDLE || target_size.isEmpty()) {
            m_window.frameReady();
            return;
        }

        auto paintable = m_view.current_paintable();
        bool can_render = paintable.has_value()
            && !paintable->bitmap_size.is_empty()
            && m_renderer.prepare(m_window)
            && m_renderer.can_render(*paintable->shared_image_buffer);
        if (!can_render) {
            m_window.frameReady();
            m_view.fall_back_to_bitmap_rendering();
            return;
        }

        auto background_color = m_view.page_background_color();
        VkClearColorValue clear_color {
            {
                static_cast<float>(background_color.red()) / 255.0f,
                static_cast<float>(background_color.green()) / 255.0f,
                static_cast<float>(background_color.blue()) / 255.0f,
                1.0f,
            },
        };
        VkClearDepthStencilValue clear_depth_stencil { 1.0f, 0 };
        VkClearValue clear_values[2] {};
        clear_values[0].color = clear_color;
        clear_values[1].depthStencil = clear_depth_stencil;

        VkRenderPassBeginInfo render_pass_begin_info {
            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
            .pNext = nullptr,
            .renderPass = render_pass,
            .framebuffer = framebuffer,
            .renderArea = {
                .offset = { 0, 0 },
                .extent = {
                    .width = static_cast<u32>(target_size.width()),
                    .height = static_cast<u32>(target_size.height()),
                },
            },
            .clearValueCount = 2,
            .pClearValues = clear_values,
        };
        vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);

        bool rendered = m_renderer.render(command_buffer, *paintable->shared_image_buffer, paintable->bitmap_size, target_size);

        if (rendered && m_view.m_vulkan_window_supports_alpha_blending.value_or(false)) {
            // Clear the strips overlaid by hover-expanded vertical tabs to transparent so the tab column painted in the
            // widget backing store shows through this native window.
            auto device_pixel_ratio = m_view.device_pixel_ratio();

            auto punch_transparent_strip = [&](int logical_width, bool from_right) {
                if (logical_width <= 0)
                    return;

                auto strip_width = min(qRound(logical_width * device_pixel_ratio), target_size.width());
                if (strip_width <= 0)
                    return;

                VkClearAttachment clear_attachment {
                    .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                    .colorAttachment = 0,
                    .clearValue = { .color = { .float32 = { 0.0f, 0.0f, 0.0f, 0.0f } } },
                };

                VkClearRect clear_rect {
                    .rect = {
                        .offset = { from_right ? target_size.width() - strip_width : 0, 0 },
                        .extent = { static_cast<u32>(strip_width), static_cast<u32>(target_size.height()) },
                    },
                    .baseArrayLayer = 0,
                    .layerCount = 1,
                };

                vkCmdClearAttachments(command_buffer, 1, &clear_attachment, 1, &clear_rect);
            };

            punch_transparent_strip(m_view.m_vertical_tab_overlay_left, false);
            punch_transparent_strip(m_view.m_vertical_tab_overlay_right, true);
        }

        vkCmdEndRenderPass(command_buffer);
        m_window.frameReady();
        if (!rendered)
            m_view.fall_back_to_bitmap_rendering();
    }

    WebContentView& m_view;
    QVulkanWindow& m_window;
    VulkanRenderer m_renderer;
};

struct WebContentView::VulkanWindow final : public QVulkanWindow {
    explicit VulkanWindow(WebContentView& view)
        : m_view(&view)
    {
    }

    void clear_view()
    {
        m_view = nullptr;
    }

    virtual QVulkanWindowRenderer* createRenderer() override
    {
        if (!m_view)
            return nullptr;
        return new VulkanWindowRenderer(*m_view, *this);
    }

    virtual bool event(QEvent* event) override
    {
        if (m_view && m_view->handle_vulkan_window_event(event))
            return true;
        return QVulkanWindow::event(event);
    }

    WebContentView* m_view { nullptr };
};

void WebContentView::create_vulkan_window()
{
    auto* instance = vulkan_instance();
    if (!instance)
        return;

    m_vulkan_window = new VulkanWindow(*this);
    m_vulkan_window->setVulkanInstance(instance);
    m_vulkan_window->setDeviceExtensions(vulkan_dmabuf_device_extensions());

    // Request an alpha channel so QVulkanWindow builds a blending swapchain (pre/post-multiplied composite alpha). This
    // lets us punch transparent holes for hover-expanded vertical tabs, which this native window would otherwise occlude.
    auto surface_format = m_vulkan_window->requestedFormat();
    surface_format.setAlphaBufferSize(8);
    m_vulkan_window->setFormat(surface_format);

    m_vulkan_window->setPreferredColorFormats({
        VK_FORMAT_B8G8R8A8_UNORM,
        VK_FORMAT_R8G8B8A8_UNORM,
    });

    m_vulkan_window_container = QWidget::createWindowContainer(m_vulkan_window, this);
    m_vulkan_window_container->setAcceptDrops(true);
    m_vulkan_window_container->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
    m_vulkan_window_container->setMouseTracking(true);
    m_vulkan_window_container->setGeometry(rect());
    m_vulkan_window_container->hide();

    install_native_window_container_focus_forwarding(*this, *m_vulkan_window, *m_vulkan_window_container);
}

bool WebContentView::vulkan_window_has_native_focus() const
{
    return m_vulkan_window && QGuiApplication::focusWindow() == m_vulkan_window;
}

void WebContentView::set_vulkan_window_container_visible(bool visible)
{
    if (m_vulkan_window_container)
        set_native_window_container_visible(*this, *m_vulkan_window_container, visible);
}

void WebContentView::fall_back_to_bitmap_rendering()
{
    // This may be invoked from within QVulkanWindow's startNextFrame(), i.e. while a frame is in flight. Hiding the
    // container synchronously destroys the window's swap chain and nulls out its Vulkan function pointers before Qt's
    // endFrame() finishes presenting the current frame, which crashes inside Qt. Defer the hide to the next event loop
    // iteration so the in-flight frame first completes on a valid swap chain.
    QTimer::singleShot(0, this, [this]() {
        set_vulkan_window_container_visible(false);
        update();
    });
}

void WebContentView::destroy_vulkan_window()
{
    setFocusProxy(nullptr);
    if (m_vulkan_window)
        m_vulkan_window->clear_view();

    if (m_vulkan_window_container) {
        delete m_vulkan_window_container;
        m_vulkan_window_container = nullptr;
        m_vulkan_window = nullptr;
        return;
    }

    delete m_vulkan_window;
    m_vulkan_window = nullptr;
}

bool WebContentView::current_paintable_can_use_vulkan_window() const
{
    auto paintable = current_paintable();
    if (!paintable.has_value())
        return false;

    return paintable->shared_image_buffer->linux_dmabuf_handle();
}

void WebContentView::schedule_vulkan_window_update()
{
    if (m_vulkan_window) {
        if (!current_paintable_can_use_vulkan_window()) {
            set_vulkan_window_container_visible(false);
            update();
            return;
        }

        set_vulkan_window_container_visible(true);
        m_vulkan_window->requestUpdate();
        return;
    }

    update();
}

void WebContentView::update_vulkan_window_geometry()
{
    if (m_vulkan_window_container)
        m_vulkan_window_container->setGeometry(rect());
    update_vulkan_window_input_region();
}

void WebContentView::update_vulkan_window_input_region()
{
    if (!m_vulkan_window || !m_vulkan_window_container)
        return;

    // The strips overlaid by hover-expanded vertical tabs are painted transparent, but this native window still captures
    // the pointer there. Exclude those strips from the input region so clicks fall through to the tab column beneath.
    auto size = m_vulkan_window_container->size();
    QRegion input_region { QRect { QPoint { 0, 0 }, size } };

    if (m_vulkan_window_supports_alpha_blending.value_or(false)) {
        if (m_vertical_tab_overlay_left > 0)
            input_region -= QRect { 0, 0, m_vertical_tab_overlay_left, size.height() };
        if (m_vertical_tab_overlay_right > 0)
            input_region -= QRect { size.width() - m_vertical_tab_overlay_right, 0, m_vertical_tab_overlay_right, size.height() };
    }

    m_vulkan_window->setMask(input_region);
}

void WebContentView::update_vulkan_alpha_blending_support()
{
    if (m_vulkan_window_supports_alpha_blending.has_value())
        return;

    if (!m_vulkan_window)
        return;

    auto* physical_device = m_vulkan_window->physicalDevice();
    if (physical_device == VK_NULL_HANDLE)
        return;

    auto* surface = QVulkanInstance::surfaceForWindow(m_vulkan_window);
    if (surface == VK_NULL_HANDLE)
        return;

    VkSurfaceCapabilitiesKHR capabilities {};
    if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities) != VK_SUCCESS)
        return;

    // Our transparent tab-column holes only reach the screen if the surface is blended per-pixel. QVulkanWindow (given
    // our alpha format) picks a pre/post-multiplied mode when offered (the Wayland case), otherwise INHERIT, which
    // blends against the window's ARGB visual on X11 while a compositor runs. If only OPAQUE is offered there is no
    // blending at all, so the feature stays disabled.
    static constexpr auto blending_modes = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
    m_vulkan_window_supports_alpha_blending = (capabilities.supportedCompositeAlpha & blending_modes) != 0;

    // The input mask was computed while support was still unknown; refresh it now that any stored insets can apply.
    update_vulkan_window_input_region();
}

void WebContentView::set_vulkan_window_cursor(QCursor const& cursor)
{
    if (m_vulkan_window_container)
        m_vulkan_window_container->setCursor(cursor);
    if (m_vulkan_window)
        m_vulkan_window->setCursor(cursor);
}

}
