#include "Pipeline.hpp"

#include "PhysicalDeviceInfo.hpp"
#include "../../../core/data/file.hpp"
#include "../Renderer.hpp"
#include "api/Models.hpp"

#define CONTENT_DIR "content/"
#define SHADER_DIR CONTENT_DIR "shaders/"

using namespace render::vk;

Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render::renderOptions &options): device(device) {
    const auto hasSamples = info.samples > 1;
    { // Render pass
        VkAttachmentDescription colorAttachment{};
        colorAttachment.format = info.getSurfaceFormat().format;
        colorAttachment.samples = info.samples;
        colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
        colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
        colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
        // NOTE: UI RenderPass do VK_IMAGE_LAYOUT_PRESENT_SRC_KHR

        VkAttachmentReference colorAttachmentRef{};
        colorAttachmentRef.attachment = 0; //TODO: layouts bind
        colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        VkAttachmentDescription depthAttachment{};
        depthAttachment.format = info.findDepthFormat();
        depthAttachment.samples = info.samples;
        depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
        depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

        VkAttachmentReference depthAttachmentRef{};
        depthAttachmentRef.attachment = 1;
        depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

        VkAttachmentDescription colorAttachmentResolve{};
        colorAttachmentResolve.format = colorAttachment.format;
        colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
        colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
        colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
        // NOTE: UI RenderPass do VK_IMAGE_LAYOUT_PRESENT_SRC_KHR

        VkAttachmentReference colorAttachmentResolveRef{};
        colorAttachmentResolveRef.attachment = 2;
        colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        VkSubpassDescription colorDepthSubpass{};
        colorDepthSubpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
        colorDepthSubpass.colorAttachmentCount = 1;
        colorDepthSubpass.pColorAttachments = &colorAttachmentRef;
        colorDepthSubpass.pDepthStencilAttachment = &depthAttachmentRef;
        if (hasSamples) {
            colorDepthSubpass.pResolveAttachments = &colorAttachmentResolveRef;
        }
        std::array<VkSubpassDescription, 4> subpasses = {colorDepthSubpass, colorDepthSubpass, colorDepthSubpass, colorDepthSubpass};

        std::array<VkSubpassDependency, 7> dependencies{};
        dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
        dependencies[0].dstSubpass = 0;
        dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[0].srcAccessMask = 0;
        dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[0].dependencyFlags = 0;

        dependencies[1].srcSubpass = 0;
        dependencies[1].dstSubpass = 1;
        dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

        dependencies[2].srcSubpass = 0;
        dependencies[2].dstSubpass = 1;
        dependencies[2].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
        dependencies[2].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
        dependencies[2].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
        dependencies[2].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
        dependencies[2].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

        dependencies[3].srcSubpass = 1;
        dependencies[3].dstSubpass = 2;
        dependencies[3].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[3].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[3].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[3].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[3].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

        dependencies[4].srcSubpass = 1;
        dependencies[4].dstSubpass = 2;
        dependencies[4].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
        dependencies[4].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
        dependencies[4].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
        dependencies[4].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
        dependencies[4].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

        dependencies[5].srcSubpass = 2;
        dependencies[5].dstSubpass = 3;
        dependencies[5].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[5].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[5].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependencies[5].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependencies[5].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

        dependencies[6].srcSubpass = 2;
        dependencies[6].dstSubpass = 3;
        dependencies[6].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
        dependencies[6].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
        dependencies[6].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
        dependencies[6].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
        dependencies[6].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

        VkRenderPassCreateInfo renderPassInfo{};
        renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
        std::vector<VkAttachmentDescription> attachments = {colorAttachment, depthAttachment};
        if (hasSamples) {
            attachments.push_back(colorAttachmentResolve);
        }
        renderPassInfo.attachmentCount = attachments.size();
        renderPassInfo.pAttachments = attachments.data();
        renderPassInfo.subpassCount = subpasses.size();
        renderPassInfo.pSubpasses = subpasses.data();
        renderPassInfo.dependencyCount = dependencies.size();
        renderPassInfo.pDependencies = dependencies.data();

        if (vkCreateRenderPass(device, &renderPassInfo, ALLOC, &renderPass) != VK_SUCCESS) {
            FATAL("Failed to create render pass!");
        }
    }
    { // UI Render pass
        VkAttachmentDescription attachment = {};
        attachment.format = info.getSurfaceFormat().format;
        attachment.samples = VK_SAMPLE_COUNT_1_BIT;
        attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
        attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
        attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
        attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

        VkAttachmentReference color_attachment = {};
        color_attachment.attachment = 0;
        color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        VkSubpassDescription subpass = {};
        subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
        subpass.colorAttachmentCount = 1;
        subpass.pColorAttachments = &color_attachment;

        VkSubpassDependency dependency = {};
        dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
        dependency.dstSubpass = 0;
        dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dependency.srcAccessMask = 0;  // or VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
        dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

        VkRenderPassCreateInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
        info.attachmentCount = 1;
        info.pAttachments = &attachment;
        info.subpassCount = 1;
        info.pSubpasses = &subpass;
        info.dependencyCount = 1;
        info.pDependencies = &dependency;
        if (vkCreateRenderPass(device, &info, nullptr, &uiRenderPass) != VK_SUCCESS) {
            FATAL("Failed to create UI render pass!");
        }
    }
    //MAYBE: use sets (ubo, (samplers, passInfos))
    //MAYBE: reuse sub UBO
    { // Voxel descriptor
        std::array<VkDescriptorSetLayoutBinding, 4> bindings{};
        bindings[0] = VoxelUBO::getLayoutBinding();
        bindings[1].binding = 1;
        bindings[1].descriptorCount = 1;
        bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        bindings[1].pImmutableSamplers = nullptr; //TODO: set immuable samplers
        bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
        bindings[2].binding = 2;
        bindings[2].descriptorCount = 1;
        bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        bindings[2].pImmutableSamplers = nullptr;
        bindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
        bindings[3].binding = 3;
        bindings[3].descriptorCount = 1;
        bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        bindings[3].pImmutableSamplers = nullptr;
        bindings[3].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

        VkDescriptorSetLayoutCreateInfo layoutInfo{};
        layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        layoutInfo.bindingCount = bindings.size();
        layoutInfo.pBindings = bindings.data();

        if (vkCreateDescriptorSetLayout(device, &layoutInfo, ALLOC, &voxelDescriptorSet) != VK_SUCCESS) {
            FATAL("Failed to create descriptor set layout!");
        }
    }
    { // Indicator descriptor
        VkDescriptorSetLayoutCreateInfo layoutInfo{};
        layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        auto binding = ViewProjUBO::getLayoutBinding();
        layoutInfo.bindingCount = 1;
        layoutInfo.pBindings = &binding;

        if (vkCreateDescriptorSetLayout(device, &layoutInfo, ALLOC, &indicDescriptorSet) != VK_SUCCESS) {
            FATAL("Failed to create descriptor set layout!");
        }
    }
    { // Sky descriptor
        VkDescriptorSetLayoutBinding samplerLayoutBinding{};
        samplerLayoutBinding.binding = 1;
        samplerLayoutBinding.descriptorCount = 1;
        samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        samplerLayoutBinding.pImmutableSamplers = nullptr;
        samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

        std::vector<VkDescriptorSetLayoutBinding> bindings = {ViewProjUBO::getLayoutBinding(), samplerLayoutBinding};
        VkDescriptorSetLayoutCreateInfo layoutInfo{};
        layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        layoutInfo.bindingCount = bindings.size();
        layoutInfo.pBindings = bindings.data();

        if (vkCreateDescriptorSetLayout(device, &layoutInfo, ALLOC, &skyDescriptorSet) != VK_SUCCESS) {
            FATAL("Failed to create descriptor set layout!");
        }
    }
    // Common pipeline settings
    auto setShaders = [&](Subpass &pass, const std::string &shaderName, bool geometry = false, const VkSpecializationInfo* specialization = nullptr) -> std::vector<VkPipelineShaderStageCreateInfo>
    {
        auto createShaderModule = [&](const data::file_content &code) {
            VkShaderModuleCreateInfo createInfo{};
            createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
            createInfo.codeSize = code.size();
            createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data());

            VkShaderModule shaderModule;
            if (vkCreateShaderModule(device, &createInfo, ALLOC, &shaderModule) != VK_SUCCESS) {
                FATAL("Failed to create shader module!");
            }
            return shaderModule;
        };

        VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
        vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
        vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
        data::file_content vsFile({SHADER_DIR + shaderName + ".vs.spv"});
        vertShaderStageInfo.module = pass.vsShader = createShaderModule(vsFile);
        vertShaderStageInfo.pName = "main";
        vertShaderStageInfo.pSpecializationInfo = specialization;

        VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
        fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
        fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
        data::file_content fsFile({SHADER_DIR + shaderName + ".fs.spv"});
        fragShaderStageInfo.module = pass.fsShader = createShaderModule(fsFile);
        fragShaderStageInfo.pName = "main";
        fragShaderStageInfo.pSpecializationInfo = specialization;

        if (geometry) {
            VkPipelineShaderStageCreateInfo geomShaderStageInfo{};
            geomShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
            geomShaderStageInfo.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
            data::file_content gsFile({SHADER_DIR + shaderName + ".gs.spv"});
            geomShaderStageInfo.module = pass.gsShader = createShaderModule(gsFile);
            geomShaderStageInfo.pName = "main";
            geomShaderStageInfo.pSpecializationInfo = specialization;

            return {vertShaderStageInfo, geomShaderStageInfo, fragShaderStageInfo};
        } else {
            return {vertShaderStageInfo, fragShaderStageInfo};
        }
    };
    auto setLayout = [&](Subpass& pass, const std::vector<VkDescriptorSetLayout>& layout, const std::vector<VkPushConstantRange>& push = {}) {
        VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
        pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
        pipelineLayoutInfo.setLayoutCount = layout.size();
        pipelineLayoutInfo.pSetLayouts = layout.data();
        pipelineLayoutInfo.pushConstantRangeCount = push.size();
        pipelineLayoutInfo.pPushConstantRanges = push.data();

        if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, ALLOC, &pass.layout) != VK_SUCCESS) {
            FATAL("Failed to create pipeline layout!");
        }
    };

    VkPipelineDepthStencilStateCreateInfo depthStencil{};
    depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    depthStencil.depthTestEnable = VK_TRUE;
    depthStencil.depthWriteEnable = VK_TRUE;
    depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
    depthStencil.depthBoundsTestEnable = VK_FALSE; // Restrict sub depth area
    depthStencil.minDepthBounds = 0.0f;
    depthStencil.maxDepthBounds = 1.0f;

    depthStencil.stencilTestEnable = VK_FALSE;
    // Stencil options front/back

    VkPipelineColorBlendAttachmentState solidColorBlendAttachment{};
    solidColorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
    solidColorBlendAttachment.blendEnable = VK_FALSE;

    VkPipelineColorBlendAttachmentState blendColorBlendAttachment{};
    blendColorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
    blendColorBlendAttachment.blendEnable = VK_TRUE;
    blendColorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
    blendColorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
    blendColorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
    blendColorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
    blendColorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
    blendColorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;

    VkPipelineColorBlendStateCreateInfo colorBlending{};
    colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    colorBlending.attachmentCount = 1; //NOTE: For multitarget
    colorBlending.pAttachments = options.voxel.transparency ? &blendColorBlendAttachment : &solidColorBlendAttachment;
    colorBlending.logicOpEnable = VK_FALSE;
    colorBlending.logicOp = VK_LOGIC_OP_COPY;
    colorBlending.blendConstants[0] = 0.0f;
    colorBlending.blendConstants[1] = 0.0f;
    colorBlending.blendConstants[2] = 0.0f;
    colorBlending.blendConstants[3] = 0.0f;

    VkPipelineInputAssemblyStateCreateInfo trisInputAssembly{};
    trisInputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    trisInputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    trisInputAssembly.primitiveRestartEnable = VK_FALSE;

    // Viewport
    VkViewport viewport{};
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = (float)info.swapDetails.capabilities.currentExtent.width;
    viewport.height = (float)info.swapDetails.capabilities.currentExtent.height;
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;

    VkRect2D scissor{};
    scissor.offset = {0, 0};
    scissor.extent = info.swapDetails.capabilities.currentExtent;

    VkPipelineViewportStateCreateInfo viewportState{};
    viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    viewportState.viewportCount = 1;
    viewportState.pViewports = &viewport;
    viewportState.scissorCount = 1;
    viewportState.pScissors = &scissor;

    VkPipelineRasterizationStateCreateInfo rasterizer{};
    rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rasterizer.depthClampEnable = VK_FALSE;
    rasterizer.rasterizerDiscardEnable = VK_FALSE;
    rasterizer.polygonMode = options.wireframe && info.features.fillModeNonSolid ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL;
    rasterizer.lineWidth = 1.0f;
    rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
    rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
    rasterizer.depthBiasEnable = VK_FALSE;
    rasterizer.depthBiasConstantFactor = 0.0f;
    rasterizer.depthBiasClamp = 0.0f;
    rasterizer.depthBiasSlopeFactor = 0.0f;

    VkPipelineMultisampleStateCreateInfo multisampling{};
    multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    multisampling.rasterizationSamples = info.samples;
    //MAYBE: add option
    multisampling.sampleShadingEnable = info.features.sampleRateShading && hasSamples;
    multisampling.minSampleShading = .2f;
    multisampling.pSampleMask = nullptr;
    multisampling.alphaToCoverageEnable = VK_FALSE;
    multisampling.alphaToOneEnable = VK_FALSE;

    struct SpeData {
        bool fog;
        bool pbr;
        bool biplanar;
        bool triplanar;
        bool stochastic;
        bool blend;
        bool curvature;
        bool curv_depth;
        int32_t unitSize;
    } speData;
    std::array<VkSpecializationMapEntry, 9> speIndex;
    speData.fog = options.voxel.fog;
    speIndex[0].constantID = 0;
    speIndex[0].offset = offsetof(SpeData, fog);
    speIndex[0].size = sizeof(SpeData::fog);
    speData.pbr = options.voxel.pbr;
    speIndex[1].constantID = 1;
    speIndex[1].offset = offsetof(SpeData, pbr);
    speIndex[1].size = sizeof(SpeData::pbr);
    speData.biplanar = options.voxel.planar == render::Planar::Biplanar;
    speIndex[2].constantID = 2;
    speIndex[2].offset = offsetof(SpeData, biplanar);
    speIndex[2].size = sizeof(SpeData::biplanar);
    speData.triplanar = options.voxel.planar == render::Planar::Triplanar;
    speIndex[3].constantID = 3;
    speIndex[3].offset = offsetof(SpeData, triplanar);
    speIndex[3].size = sizeof(SpeData::triplanar);
    speData.stochastic = options.voxel.stochastic;
    speIndex[4].constantID = 4;
    speIndex[4].offset = offsetof(SpeData, stochastic);
    speIndex[4].size = sizeof(SpeData::stochastic);
    speData.blend = options.voxel.blend;
    speIndex[5].constantID = 5;
    speIndex[5].offset = offsetof(SpeData, blend);
    speIndex[5].size = sizeof(SpeData::blend);
    speData.curvature = options.voxel.curvature;
    speIndex[6].constantID = 8;
    speIndex[6].offset = offsetof(SpeData, curvature);
    speIndex[6].size = sizeof(SpeData::curvature);
    speData.curv_depth = options.voxel.curv_depth;
    speIndex[7].constantID = 9;
    speIndex[7].offset = offsetof(SpeData, curv_depth);
    speIndex[7].size = sizeof(SpeData::curv_depth);
    speData.unitSize = 8; //TODO: load from world.voxel_density
    speIndex[8].constantID = 16;
    speIndex[8].offset = offsetof(SpeData, unitSize);
    speIndex[8].size = sizeof(SpeData::unitSize);
    VkSpecializationInfo voxelSpecialization{};
    voxelSpecialization.dataSize = sizeof(SpeData);
    voxelSpecialization.pData = &speData;
    voxelSpecialization.mapEntryCount = speIndex.size();
    voxelSpecialization.pMapEntries = speIndex.data();

    { // World pipeline
        VkPushConstantRange pushRange{};
        pushRange.offset = 0;
        pushRange.size = sizeof(UniqueCurvaturePush);
        pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
        setLayout(worldPass, {voxelDescriptorSet}, {pushRange});

        auto withGeometry = options.voxel.geometry && info.features.geometryShader;
        auto shaderStages = setShaders(worldPass, withGeometry ? "Voxel.geo" : "Voxel", withGeometry, &voxelSpecialization);

        VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;

        auto bindingDescription = Model::getBindingDescription();
        vertexInputInfo.vertexBindingDescriptionCount = 1;
        vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
        auto attributeDescriptions = Model::getAttributeDescriptions();
        vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size();
        vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

        VkGraphicsPipelineCreateInfo pipelineInfo{};
        pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
        pipelineInfo.stageCount = shaderStages.size();
        pipelineInfo.pStages = shaderStages.data();
        pipelineInfo.pVertexInputState = &vertexInputInfo;
        pipelineInfo.pInputAssemblyState = &trisInputAssembly;
        pipelineInfo.pViewportState = &viewportState;
        pipelineInfo.pRasterizationState = &rasterizer;
        pipelineInfo.pMultisampleState = &multisampling;
        pipelineInfo.pDepthStencilState = &depthStencil;
        pipelineInfo.pColorBlendState = &colorBlending;
        pipelineInfo.pDynamicState = nullptr;

        pipelineInfo.layout = worldPass.layout;
        pipelineInfo.renderPass = renderPass;
        pipelineInfo.subpass = 0;

        pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
        pipelineInfo.basePipelineIndex = -1;

        if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &worldPass.pipeline) != VK_SUCCESS) {
            FATAL("Failed to create graphics pipeline!");
        }
    }
    { // Indicator pipeline
        VkPushConstantRange pushRange{};
        pushRange.offset = 0;
        pushRange.size = sizeof(ModelColorPush);
        pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
        setLayout(indicPass, {indicDescriptorSet}, {pushRange});
        auto shaderStages = setShaders(indicPass, "Color");

        VkPipelineInputAssemblyStateCreateInfo lineInputAssembly{};
        lineInputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
        lineInputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
        lineInputAssembly.primitiveRestartEnable = VK_FALSE;

        VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
        auto bindingDescription = Shape::getBindingDescription();
        vertexInputInfo.vertexBindingDescriptionCount = 1;
        vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
        auto attributeDescription = Shape::getAttributeDescription();
        vertexInputInfo.vertexAttributeDescriptionCount = 1;
        vertexInputInfo.pVertexAttributeDescriptions = &attributeDescription;

        VkGraphicsPipelineCreateInfo pipelineInfo{};
        pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
        pipelineInfo.stageCount = shaderStages.size();
        pipelineInfo.pStages = shaderStages.data();
        pipelineInfo.pVertexInputState = &vertexInputInfo;
        pipelineInfo.pInputAssemblyState = &lineInputAssembly;
        pipelineInfo.pViewportState = &viewportState;
        pipelineInfo.pRasterizationState = &rasterizer;
        pipelineInfo.pMultisampleState = &multisampling;
        pipelineInfo.pDepthStencilState = &depthStencil;
        pipelineInfo.pColorBlendState = &colorBlending;
        pipelineInfo.pDynamicState = nullptr;

        pipelineInfo.layout = indicPass.layout;
        pipelineInfo.renderPass = renderPass;
        pipelineInfo.subpass = 1;

        pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
        pipelineInfo.basePipelineIndex = -1;

        if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &indicPass.pipeline) != VK_SUCCESS) {
            FATAL("Failed to create graphics pipeline!");
        }
    }
    { // Transparent World pipeline
        VkPushConstantRange pushRange{};
        pushRange.offset = 0;
        pushRange.size = sizeof(UniqueCurvaturePush);
        pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
        setLayout(transparentWorldPass, {voxelDescriptorSet}, {pushRange});

        auto withGeometry = options.voxel.geometry && info.features.geometryShader;
        auto shaderStages = setShaders(transparentWorldPass, withGeometry ? "Voxel.geo" : "Voxel", withGeometry, &voxelSpecialization);

        VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;

        auto bindingDescription = Model::getBindingDescription();
        vertexInputInfo.vertexBindingDescriptionCount = 1;
        vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
        auto attributeDescriptions = Model::getAttributeDescriptions();
        vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size();
        vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

        VkGraphicsPipelineCreateInfo pipelineInfo{};
        pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
        pipelineInfo.stageCount = shaderStages.size();
        pipelineInfo.pStages = shaderStages.data();
        pipelineInfo.pVertexInputState = &vertexInputInfo;
        pipelineInfo.pInputAssemblyState = &trisInputAssembly;
        pipelineInfo.pViewportState = &viewportState;
        pipelineInfo.pRasterizationState = &rasterizer;
        pipelineInfo.pMultisampleState = &multisampling;
        pipelineInfo.pDepthStencilState = &depthStencil;
        pipelineInfo.pColorBlendState = &colorBlending;
        pipelineInfo.pDynamicState = nullptr;

        pipelineInfo.layout = transparentWorldPass.layout;
        pipelineInfo.renderPass = renderPass;
        pipelineInfo.subpass = 2;

        pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
        pipelineInfo.basePipelineIndex = -1;

        if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &transparentWorldPass.pipeline) != VK_SUCCESS) {
            FATAL("Failed to create graphics pipeline!");
        }
    }
    { // Sky pipeline
        setLayout(skyPass, {skyDescriptorSet}, {});
        auto shaderStages = setShaders(skyPass, "Sky");

        depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;

        VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
        auto bindingDescription = Shape::getBindingDescription();
        vertexInputInfo.vertexBindingDescriptionCount = 1;
        vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
        auto attributeDescription = Shape::getAttributeDescription();
        vertexInputInfo.vertexAttributeDescriptionCount = 1;
        vertexInputInfo.pVertexAttributeDescriptions = &attributeDescription;

        VkGraphicsPipelineCreateInfo pipelineInfo{};
        pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
        pipelineInfo.stageCount = shaderStages.size();
        pipelineInfo.pStages = shaderStages.data();
        pipelineInfo.pVertexInputState = &vertexInputInfo;
        pipelineInfo.pInputAssemblyState = &trisInputAssembly;
        pipelineInfo.pViewportState = &viewportState;
        pipelineInfo.pRasterizationState = &rasterizer;
        pipelineInfo.pMultisampleState = &multisampling;
        pipelineInfo.pDepthStencilState = &depthStencil;
        pipelineInfo.pColorBlendState = &colorBlending;
        pipelineInfo.pDynamicState = nullptr;

        pipelineInfo.layout = skyPass.layout;
        pipelineInfo.renderPass = renderPass;
        pipelineInfo.subpass = 3;

        pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
        pipelineInfo.basePipelineIndex = -1;

        if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &skyPass.pipeline) != VK_SUCCESS) {
            FATAL("Failed to create graphics pipeline!");
        }
    }
}
Pipeline::~Pipeline() {
    auto destroy = [&](Subpass &pass) {
        if(pass.gsShader)
            vkDestroyShaderModule(device, pass.gsShader, ALLOC);

        vkDestroyShaderModule(device, pass.fsShader, ALLOC);
        vkDestroyShaderModule(device, pass.vsShader, ALLOC);
        vkDestroyPipeline(device, pass.pipeline, ALLOC);
        vkDestroyPipelineLayout(device, pass.layout, ALLOC);
    };

    vkDestroyRenderPass(device, uiRenderPass, ALLOC);

    destroy(worldPass);
    destroy(indicPass);
    destroy(skyPass);

    vkDestroyDescriptorSetLayout(device, voxelDescriptorSet, ALLOC);
    vkDestroyDescriptorSetLayout(device, indicDescriptorSet, ALLOC);
    vkDestroyDescriptorSetLayout(device, skyDescriptorSet, ALLOC);
    vkDestroyRenderPass(device, renderPass, ALLOC);
}