#include "../alice/model_file.h"
#include "../../l1/marie/geom_alg_utils.h"
#include "../margaret/vulkan_utils.h"
#include "../lucy/glyph_render.h"

typedef struct {
    U64 count;
    MargaretSubbuf staging;
    MargaretSubbuf device_local;
} PatriciaBuf;

typedef struct {
    VkImageView diffuse_view;
    VkImageView normal_view;
    VkImageView specular_view;
    /* Each generic model has its own descriptor set
    * It's because it has its own textures. But it also has copies of references to light UBO
    * Because I am to lazy to create two set layouts for generic model pipeline */
    VkDescriptorSet p_0a_set_0;
} AliceGenericMeshMemDependantVkObj;

typedef struct {
    size_t indexes;
    // Option<MargaretSubbuf>
    MargaretSubbuf staging_vbo;
    // Option<MargaretSubbuf>
    MargaretSubbuf staging_ebo;

    // todo: replace TextureDataXXX with MargaretPngPromises
    TextureDataR8G8B8A8 pixels_diffuse;
    TextureDataR8G8B8A8 pixels_normal;
    TextureDataR8 pixels_specular;

    MargaretSubbuf staging_diffuse_tex_buf;
    MargaretSubbuf staging_normal_tex_buf;
    MargaretSubbuf staging_specular_tex_buf;

    MargaretSubbuf vbo;
    MargaretSubbuf ebo;
    PatriciaBuf instance_attr;

    MargaretImg diffuse_texture;
    MargaretImg normal_texture;
    MargaretImg specular_texture;

    AliceGenericMeshMemDependantVkObj mem_dependant_vk_obj;

    bool scheduled_for_deletion;
} AliceGenericMeshHand;

#include "../../../gen/l1/eve/alice/ListAliceGenericMeshHand.h"

typedef struct {
    size_t indexes;

    MargaretSubbuf staging_vbo;
    MargaretSubbuf staging_ebo;

    MargaretSubbuf vbo;
    MargaretSubbuf ebo;
    PatriciaBuf instance_attr;

    bool scheduled_for_deletion;
} AliceShinyMeshHand;

#include "../../../gen/l1/eve/alice/ListAliceShinyMeshHand.h"

typedef struct {
    float fov;
    mat3 cam_basis;
    vec3 pos;
} AliceCam;

typedef struct{
    AliceCam cam;
    float sensitivity;
    float pitch_cap;
} AliceCamVerticalControl;

AliceCamVerticalControl AliceCamVerticalControl_new() {
    return (AliceCamVerticalControl){
        .cam.fov = 1.5f, .cam.cam_basis = marie_simple_camera_rot_m_basis_in_cols(0, 0, 0), .cam.pos = {0, 0, 0},
        .sensitivity = 0.5f * M_PIf / 180, .pitch_cap = M_PIf * 0.49f
    };
}

void AliceCamVerticalControl_update_direction(
    AliceCamVerticalControl* self, int win_width, int win_height, float pointer_x, float pointer_y) {
    float yaw = ((float)win_width / 2 - pointer_x) * self->sensitivity;
    float pitch = marie_clamp_float(
        ((float)win_height / 2 - pointer_y) * self->sensitivity,
        -self->pitch_cap, self->pitch_cap
    );
    self->cam.cam_basis = marie_simple_camera_rot_m_basis_in_cols(yaw, pitch, 0);
}

typedef struct {
    MargaretSubbuf staging;
    MargaretSubbuf device_local;
} AlicePipeline0UBO;

typedef struct{
    vec4 clear_color;
    float gamma_correction_factor;
    float hdr_factor;
    float lsd_factor;
} AliceRenderConfig;

AliceRenderConfig AliceRenderConfig_new(){
    return (AliceRenderConfig){
        .clear_color = {0, 0.5f, 0.9f, 1},
        .gamma_correction_factor = 2.2f, .hdr_factor = 1, .lsd_factor = 0};
}

/* Here ends the old r0_scene.h file
 * And begins old r0.c file */

#include <sys/wait.h>
#include "../../../gen/l1/margaret/png_pixel_masses.h"
#include "../lucy/glyph_cache.h"
#include <sys/mman.h>
//#include "../../l1/system/fileio.h"
#include <math.h>
#include <xkbcommon/xkbcommon.h>
#include "../../../gen/l_wl_protocols/xdg-shell-client.h"


VkRenderPass create_render_pass_0(VkDevice logical_device, VkFormat colorbuffer_format, VkFormat zbuffer_format) {
    VkAttachmentDescription all_attachments[2] = {
        {
            .format = colorbuffer_format,
            .samples = VK_SAMPLE_COUNT_1_BIT,
            .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
            .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        },
        {
            .format = zbuffer_format,
            .samples = VK_SAMPLE_COUNT_1_BIT,
            .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
            .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
            .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
        }
    };

    // For our one single render subpass
    VkAttachmentReference color_attachments_refs[1] = {
        {
            .attachment = 0,
            .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    } };

    VkAttachmentReference depth_attachment_ref = {
        .attachment = 1,
        .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
    };

    VkSubpassDescription subpasses_descr[1] = { (VkSubpassDescription){
        .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
        .colorAttachmentCount = ARRAY_SIZE(color_attachments_refs),
        .pColorAttachments = color_attachments_refs,
        .pDepthStencilAttachment = &depth_attachment_ref,
    } };

    VkSubpassDependency subpass_dependencies[1] = {
        // subpass_0_external
        (VkSubpassDependency) {
            .srcSubpass = VK_SUBPASS_EXTERNAL,
            .dstSubpass = 0,
            .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
            .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
            .srcAccessMask = 0,
            .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    }};

    VkRenderPassCreateInfo render_pass_crinfo = {
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
        .attachmentCount = ARRAY_SIZE(all_attachments),
        .pAttachments = all_attachments,
        .subpassCount = ARRAY_SIZE(subpasses_descr),
        .pSubpasses = subpasses_descr,
        .dependencyCount = ARRAY_SIZE(subpass_dependencies),
        .pDependencies = subpass_dependencies,
    };
    VkRenderPass render_pass;
    if (vkCreateRenderPass(logical_device, &render_pass_crinfo, NULL, &render_pass) != VK_SUCCESS)
        abortf("vkCreateRenderPass");
    return render_pass;
}

typedef struct {
    VkPipelineLayout pipeline_layout;
    VkPipeline pipeline;
    VkDescriptorSetLayout descriptor_set_layout;
} AlicePipeline0a;

void AlicePipeline0a_destroy(VkDevice device, AlicePipeline0a hands) {
    vkDestroyPipeline(device, hands.pipeline, NULL);
    vkDestroyPipelineLayout(device, hands.pipeline_layout, NULL);
    vkDestroyDescriptorSetLayout(device, hands.descriptor_set_layout, NULL);
}

AlicePipeline0a create_graphics_pipeline_0(
    VkDevice device, SpanU8 root_dir, VkRenderPass render_pass, uint32_t subpass
    ) {
    VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = {
        {
            .binding = 0,
            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            // our shader variable is not an array of descriptors, so this stays 1
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        },
        {
            .binding = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        },
        {
            .binding = 2,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        },
        {
            .binding = 3,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        },
    };
    VkDescriptorSetLayoutCreateInfo descriptor_set_layout_crinfo = {
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
        .bindingCount = ARRAY_SIZE(bindings_for_my_descr_set_layout),
        .pBindings = bindings_for_my_descr_set_layout,
    };
    VkDescriptorSetLayout my_descriptor_set_layout;
    if (vkCreateDescriptorSetLayout(device, &descriptor_set_layout_crinfo, NULL, &my_descriptor_set_layout) != VK_SUCCESS)
        abortf("vkCreateDescriptorSetLayout");

    VkPushConstantRange pc_ranges[] = {
        {
            .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
            .offset = 0, .size = sizeof(mat4)
        },
        {
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
            .offset = sizeof(mat4), .size = sizeof(vec3)
        },

    };

    VkPipelineLayoutCreateInfo layout_crinfo = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
        .setLayoutCount = 1,
        .pSetLayouts = &my_descriptor_set_layout,
        .pushConstantRangeCount = ARRAY_SIZE(pc_ranges),
        .pPushConstantRanges = pc_ranges,
    };
    VkPipelineLayout pipeline_layout;
    if (vkCreatePipelineLayout(device, &layout_crinfo, NULL, &pipeline_layout) != VK_SUCCESS)
        abortf("vkCreatePipelineLayout");


    VecU8 vert_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0gen/vert.spv", root_dir));
    VecU8 frag_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0gen/frag.spv", root_dir));

    VkVertexInputBindingDescription vertex_bindings[2] = {
        {
            .binding = 0,
            .stride = sizeof(GenericMeshVertex),
            .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
        },
        {
            .binding = 1,
            .stride = sizeof(GenericMeshInstance),
            .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE,
        }
    };
    VkVertexInputAttributeDescription vertex_attributes[2 + 3 + 4 + 3] = {
        {
            .location = 0, .binding = 0,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshVertexInc, pos),
        },
        {
            .location = 1, .binding = 0,
            .format = VK_FORMAT_R32G32_SFLOAT,
            .offset = offsetof(GenericMeshVertexInc, tex),
        },
        {
            .location = 2, .binding = 0,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshVertex, norm),
        },
        {
            .location = 3, .binding = 0,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshVertex, tang_U),
        },
        {
            .location = 4, .binding = 0,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshVertex, tang_V),
        },

        /* This is a mat4 datatype, so it will take 4 entire 'locations' */
        {
            .location = 5, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(GenericMeshInstanceInc, model_t) + offsetof(mat4, x)
        },
        {
            .location = 6, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(GenericMeshInstanceInc, model_t) + offsetof(mat4, y)
        },
        {
            .location = 7, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(GenericMeshInstanceInc, model_t) + offsetof(mat4, z)
        },
        {
            .location = 8, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(GenericMeshInstanceInc, model_t) + offsetof(mat4, w)
        },
        {
            .location = 9, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshInstance, normal_t) + offsetof(mat3, x)
        },
        {
            .location = 10, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshInstance, normal_t) + offsetof(mat3, y)
        },
        {
            .location = 11, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(GenericMeshInstance, normal_t) + offsetof(mat3, z)
        },
    };

    VkPipeline pipeline = margaret_create_triangle_pipeline_one_attachment(device, render_pass, subpass,
        (MargaretMostImportantPipelineOptions){
            .pipeline_layout = pipeline_layout,
            .vertex_shader_code = vert_bin_code, .fragment_shader_code = frag_bin_code,
            .vertexBindingDescriptionCount = ARRAY_SIZE(vertex_bindings), vertex_bindings,
            .vertexAttributeDescriptionCount = ARRAY_SIZE(vertex_attributes), vertex_attributes,
            .depthTestEnable = true, .depthWriteEnable = true, .blendEnable = false
        });
    return (AlicePipeline0a){.pipeline_layout = pipeline_layout, .pipeline = pipeline, .descriptor_set_layout = my_descriptor_set_layout};
}

typedef struct {
    VkPipelineLayout pipeline_layout;
    VkPipeline pipeline;
    VkDescriptorSetLayout descriptor_set_layout;
} AlicePipeline0b;

void AlicePipeline0b_destroy(VkDevice device, AlicePipeline0b hands) {
    vkDestroyPipeline(device, hands.pipeline, NULL);
    vkDestroyPipelineLayout(device, hands.pipeline_layout, NULL);
    vkDestroyDescriptorSetLayout(device, hands.descriptor_set_layout, NULL);
}

AlicePipeline0b create_graphics_pipeline_0_b(
    VkDevice device, SpanU8 root_dir, VkRenderPass render_pass, uint32_t subpass
    ) {
    VkDescriptorSetLayout my_descriptor_set_layout;
    check(vkCreateDescriptorSetLayout(device, &(VkDescriptorSetLayoutCreateInfo){
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
        .bindingCount = 1,
        .pBindings = (VkDescriptorSetLayoutBinding[]){{
            .binding = 0,
            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        }},
    }, NULL, &my_descriptor_set_layout) == VK_SUCCESS);

    VkPushConstantRange pc_ranges[] = {
        {
            .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
            .offset = 0, .size = sizeof(mat4)
        },
        {
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
            .offset = sizeof(mat4), .size = sizeof(vec3)
        }};

    VkPipelineLayout pipeline_layout;
    check(vkCreatePipelineLayout(device, &(VkPipelineLayoutCreateInfo){
        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
        .setLayoutCount = 1,
        .pSetLayouts = (VkDescriptorSetLayout[]){my_descriptor_set_layout},
        .pushConstantRangeCount = ARRAY_SIZE(pc_ranges),
        .pPushConstantRanges = pc_ranges,
    }, NULL, &pipeline_layout) == VK_SUCCESS);


    VecU8 vert_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0sh/vert.spv", root_dir));
    VecU8 frag_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0sh/frag.spv", root_dir));

    VkVertexInputBindingDescription vertex_bindings[2] = {
        {
            .binding = 0,
            .stride = sizeof(ShinyMeshVertex),
            .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
        },
        {
            .binding = 1,
            .stride = sizeof(ShinyMeshInstance),
            .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE,
        }
    };
    VkVertexInputAttributeDescription vertex_attributes[2 + 4 + 2 + 3] = {
        {
            .location = 0, .binding = 0,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshVertexInc, pos),
        },
        {
            .location = 1, .binding = 0,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshVertex, normal),
        },

        /* This is a mat4 datatype, so it will take 4 entire 'locations' */
        {
            .location = 2, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(ShinyMeshInstanceInc, model_t) + offsetof(mat4, x)
        },
        {
            .location = 3, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(ShinyMeshInstanceInc, model_t) + offsetof(mat4, y)
        },
        {
            .location = 4, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(ShinyMeshInstanceInc, model_t) + offsetof(mat4, z)
        },
        {
            .location = 5, .binding = 1,
            .format = VK_FORMAT_R32G32B32A32_SFLOAT,
            .offset = offsetof(ShinyMeshInstanceInc, model_t) + offsetof(mat4, w)
        },
        {
            .location = 6, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshInstanceInc, color_off)
        },
        {
            .location = 7, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshInstanceInc, color_on)
        },
        {
            .location = 8, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshInstance, normal_t) + offsetof(mat3, x)
        },
        {
            .location = 9, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshInstance, normal_t) + offsetof(mat3, y)
        },
        {
            .location = 10, .binding = 1,
            .format = VK_FORMAT_R32G32B32_SFLOAT,
            .offset = offsetof(ShinyMeshInstance, normal_t) + offsetof(mat3, z)
        },
    };

    VkPipeline pipeline = margaret_create_triangle_pipeline_one_attachment(device, render_pass, subpass,
        (MargaretMostImportantPipelineOptions){
            .pipeline_layout = pipeline_layout,
            .vertex_shader_code = vert_bin_code, .fragment_shader_code = frag_bin_code,
            .vertexBindingDescriptionCount = ARRAY_SIZE(vertex_bindings), vertex_bindings,
            .vertexAttributeDescriptionCount = ARRAY_SIZE(vertex_attributes), vertex_attributes,
            .depthTestEnable = true, .depthWriteEnable = true, .blendEnable = false
        });

    return (AlicePipeline0b){.pipeline_layout = pipeline_layout, .pipeline = pipeline, .descriptor_set_layout = my_descriptor_set_layout};
}

VkRenderPass create_render_pass_1(VkDevice logical_device, VkFormat image_format) {
    // Color attachments array for our render pass
    VkAttachmentDescription all_attachments[1] = { (VkAttachmentDescription){
        .format = image_format,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
    } };

    // For our one single render subpass
    VkAttachmentReference color_attachment_refs[1] = { (VkAttachmentReference){
        .attachment = 0,
        .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    } };

    VkSubpassDescription subpasses_descr[1] = { (VkSubpassDescription){
        .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
        .colorAttachmentCount = ARRAY_SIZE(color_attachment_refs),
        .pColorAttachments = color_attachment_refs,
    } };

    VkSubpassDependency subpass_dependencies[1] = {
        // subpass_0_external
        (VkSubpassDependency) {
            .srcSubpass = VK_SUBPASS_EXTERNAL,
            .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
            .srcAccessMask = 0,
            .dstSubpass = 0,
            .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
            .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    }};

    VkRenderPassCreateInfo render_pass_crinfo = {
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
        .attachmentCount = ARRAY_SIZE(all_attachments),
        .pAttachments = all_attachments,
        .subpassCount = ARRAY_SIZE(subpasses_descr),
        .pSubpasses = subpasses_descr,
        .dependencyCount = ARRAY_SIZE(subpass_dependencies),
        .pDependencies = subpass_dependencies,
    };
    VkRenderPass render_pass;
    if (vkCreateRenderPass(logical_device, &render_pass_crinfo, NULL, &render_pass) != VK_SUCCESS)
        abortf("vkCreateRenderPass");
    return render_pass;
}

typedef struct {
    VkPipelineLayout pipeline_layout;
    VkPipeline pipeline;
    VkDescriptorSetLayout descriptor_set_layout;
} AlicePipeline1;

void AlicePipeline1_destroy(VkDevice device, AlicePipeline1 hands) {
    vkDestroyPipeline(device, hands.pipeline, NULL);
    vkDestroyPipelineLayout(device, hands.pipeline_layout, NULL);
    vkDestroyDescriptorSetLayout(device, hands.descriptor_set_layout, NULL);
}

AlicePipeline1 create_graphics_pipeline_1(
    VkDevice device, SpanU8 root_dir, VkRenderPass render_pass, uint32_t subpass
    ) {

    VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = {
        {
            .binding = 0,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        },
    };
    VkDescriptorSetLayout my_descriptor_set_layout;
    check(vkCreateDescriptorSetLayout(device, &(VkDescriptorSetLayoutCreateInfo){
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
        .bindingCount = ARRAY_SIZE(bindings_for_my_descr_set_layout),
        .pBindings = bindings_for_my_descr_set_layout,
    }, NULL, &my_descriptor_set_layout) == VK_SUCCESS);

    VkPushConstantRange used_region_sz_push_const[2] = {
        { .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
            .offset = 0, .size = sizeof(Pipeline1PushRangeVertex) },
        { .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
            .offset = sizeof(Pipeline1PushRangeVertex), .size = sizeof(Pipeline1PushRangeFragment) },
    };

    VkPipelineLayout pipeline_layout;
    check(vkCreatePipelineLayout(device, &(VkPipelineLayoutCreateInfo){
        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
        .setLayoutCount = 1,
        .pSetLayouts = &my_descriptor_set_layout,
        .pushConstantRangeCount = ARRAY_SIZE(used_region_sz_push_const),
        .pPushConstantRanges = used_region_sz_push_const,
    }, NULL, &pipeline_layout) == VK_SUCCESS);


    VecU8 vert_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/1/vert.spv", root_dir));
    VecU8 frag_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/1/frag.spv", root_dir));

    VkPipeline pipeline = margaret_create_triangle_pipeline_one_attachment(device, render_pass, subpass,
        (MargaretMostImportantPipelineOptions){
            .pipeline_layout = pipeline_layout,
            .vertex_shader_code = vert_bin_code, .fragment_shader_code = frag_bin_code,
            .vertexBindingDescriptionCount = 0, .pVertexBindingDescriptions = NULL,
            .vertexAttributeDescriptionCount = 0, .pVertexAttributeDescriptions = NULL,
        });
    return (AlicePipeline1){.pipeline_layout = pipeline_layout, .pipeline = pipeline, .descriptor_set_layout = my_descriptor_set_layout};
}

VkFramebuffer create_IT1_framebuffer(VkDevice device, VkImageView IT1_view, VkImageView zbuffer_view, VkRenderPass render_pass_0,
    U32 MAX_WIN_WIDTH, U32 MAX_WIN_HEIGHT) {
    VkImageView attachments[2] = {IT1_view, zbuffer_view};
    VkFramebufferCreateInfo framebuffer_crinfo = {
        .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
        .renderPass = render_pass_0,
        .attachmentCount = ARRAY_SIZE(attachments),
        .pAttachments = attachments,
        .width = MAX_WIN_WIDTH,
        .height = MAX_WIN_HEIGHT,
        .layers = 1
    };
    VkFramebuffer framebuffer;
    if (vkCreateFramebuffer(device, &framebuffer_crinfo, NULL, &framebuffer) != VK_SUCCESS)
        abortf("vkCreateFramebuffer");
    return framebuffer;
}

void record_cmd_set_viewport_and_scissors(VkCommandBuffer command_buffer, VkExtent2D image_extent) {
    vkCmdSetViewport(command_buffer, 0, 1, &(VkViewport){
        .x = 0.0f, .y = 0.0f,
        .width = (float)(image_extent.width), .height = (float)(image_extent.height),
        .minDepth = 0.0f, .maxDepth = 1.0f,
    });
    vkCmdSetScissor(command_buffer, 0, 1, &(VkRect2D){ .offset = (VkOffset2D){0, 0}, .extent = image_extent});
}

typedef struct {
    VkSemaphore in_frame_transfer_complete;
    VkSemaphore image_available_semaphore;
    VkSemaphore rendered_to_IT1_semaphore;
    VkFence in_flight_fence;
    // For miscellaneous uses (for init)
    VkFence roxy;
} Jane_alice;

NODISCARD Jane_alice Jane_alice_create(VkDevice device) {
    return (Jane_alice){
        .in_frame_transfer_complete = margaret_create_semaphore(device),
        .image_available_semaphore = margaret_create_semaphore(device),
        .rendered_to_IT1_semaphore = margaret_create_semaphore(device),
        .in_flight_fence = margaret_create_fence(device, true),
        .roxy = margaret_create_fence(device, false),
    };
}

void Jane_alice_destroy(VkDevice device, Jane_alice jane) {
    vkDestroySemaphore(device, jane.rendered_to_IT1_semaphore, NULL);
    vkDestroySemaphore(device, jane.image_available_semaphore, NULL);
    vkDestroySemaphore(device, jane.in_frame_transfer_complete, NULL);
    vkDestroyFence(device, jane.in_flight_fence, NULL);
    vkDestroyFence(device, jane.roxy, NULL);
}

typedef struct {
    VkQueue graphics_queue;
    VkQueue presentation_queue;
} UsedVulkanQueues;


void alice_default_callback_on_wl_keyboard_key(void* d, U32 keysym, U32 key_action){}

void alice_default_callback_on_another_frame(void* d, float fl){}

typedef struct Alice Alice;

typedef struct{
    /* guest data, keysym, key action (wl_keyboard_key_state) */
    void (*on_wl_keyboard_key)(void*, U32, U32);
    void (*on_another_frame)(void*, float);
} AliceCallbacks;

void AliceCallbacks_set_default(AliceCallbacks* self){
    self->on_another_frame = alice_default_callback_on_another_frame;
    self->on_wl_keyboard_key = alice_default_callback_on_wl_keyboard_key;
}

typedef struct {
    /* Memory settings */
    VkExtent2D sane_image_extent_limit;
    /* Globals */
    struct wl_display *wl_display;
    struct wl_registry *wl_registry;
    struct wl_compositor *wl_compositor;
    struct xdg_wm_base *xdg_wm_base;
    struct wl_seat *wl_seat;
    /* Objects */
    struct wl_surface *wl_surface;
    struct wl_callback* wl_callback;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
    /* inputs */
    struct wl_pointer* pointer;
    struct wl_keyboard* keyboard;
    /* xkb */
    struct xkb_state* xkb_state;
    struct xkb_context* xkb_context;
    struct xkb_keymap* xkb_keymap;
    /* app state */
    uint32_t last_frame_time;
    uint32_t cur_frame_time;
    int32_t width_heard;
    int32_t height_heard;
    int32_t width_confirmed;
    int32_t height_confirmed;
    bool closed;
    bool first_0x80_keys[0x80];
    /* framerate counting */
    U32 frame_count_since_key;
    margaret_ns_time prev_key_frame_time;
} AliceWaylandApp;

float AliceWaylandApp_get_elapsed_time(const AliceWaylandApp* self){
    return (float)(self->cur_frame_time - self->last_frame_time) / 1000;
}

/* They are actually MutRef, but who cares lol, we won't have such distinction here anytime soon, so we shorten
 * it to Ref (whil keeping in kind that it is actually MutRef)
 */
typedef struct ListNodeAliceGenericMeshHand* RefListNodeAliceGenericMeshHand;
#include "../../../gen/l1/eve/alice/VecRefListNodeAliceGenericMeshHand.h"

typedef struct {
    ListAliceGenericMeshHand hands;
    /* Familiar pattern. Todo: move familiar pattern to l1 codegen. 3 matches so far */
    VecRefListNodeAliceGenericMeshHand to_be_freed_of_old_staging_next_cycle;
    VecRefListNodeAliceGenericMeshHand to_be_copied_to_device_next_cycle;
    VecRefListNodeAliceGenericMeshHand to_be_deleted;
} AliceAllMeshesGeneric;

AliceAllMeshesGeneric AliceAllMeshesGeneric_new(){
    return (AliceAllMeshesGeneric){
        .hands = ListAliceGenericMeshHand_new(),
        .to_be_freed_of_old_staging_next_cycle = VecRefListNodeAliceGenericMeshHand_new(),
        .to_be_copied_to_device_next_cycle = VecRefListNodeAliceGenericMeshHand_new(),
        .to_be_deleted = VecRefListNodeAliceGenericMeshHand_new(),
    };
}

typedef struct ListNodeAliceShinyMeshHand* RefListNodeAliceShinyMeshHand;
#include "../../../gen/l1/eve/alice/VecRefListNodeAliceShinyMeshHand.h"

typedef struct {
    ListAliceShinyMeshHand hands;
    /* Ah */
    VecRefListNodeAliceShinyMeshHand to_be_freed_of_old_staging_next_cycle;
    VecRefListNodeAliceShinyMeshHand to_be_copied_to_device_next_cycle;
    VecRefListNodeAliceShinyMeshHand to_be_deleted;
} AliceAllMeshesShiny;

AliceAllMeshesShiny AliceAllMeshesShiny_new(){
    return (AliceAllMeshesShiny){
        .hands = ListAliceShinyMeshHand_new(),
        .to_be_freed_of_old_staging_next_cycle = VecRefListNodeAliceShinyMeshHand_new(),
        .to_be_copied_to_device_next_cycle = VecRefListNodeAliceShinyMeshHand_new(),
        .to_be_deleted = VecRefListNodeAliceShinyMeshHand_new(),
    };
}


struct Alice {
    AliceCallbacks callbacks;
    void* guest;
    AliceWaylandApp wl;

    MargaretInstanceAndItsDebug instance_and_debug;
    VkSurfaceKHR surface;
    VkPhysicalDevice physical_device;
    MargaretChosenQueueFamilies queue_fam;
    VkDevice device;
    UsedVulkanQueues queues;
    VkFormat zbuffer_format;
    VkFormat IT1_format;
    VkRenderPass render_pass_0;
    AlicePipeline0a pipeline_hands_0a;
    AlicePipeline0b pipeline_hands_0b;
    VkRenderPass render_pass_1;
    AlicePipeline1 pipeline_hands_1;
    VkSampler linear_sampler;
    VkSampler nearest_sampler;
    VkCommandPool command_pool;
    VkCommandBuffer rendering_command_buf_0;
    VkCommandBuffer rendering_command_buf_1;
    VkCommandBuffer transfer_command_buf;
    VkCommandBuffer device_local_mem_mv_command_buf;
    VkDescriptorPool descriptor_pool;  // todo: write dynamic allocator wrapper for descriptor pools

    MargaretImgAllocator dev_local_images;
    MargaretBufAllocator dev_local_buffers;
    MargaretBufAllocator staging_buffers;

    Jane_alice jane; // todo: figure out my own design
    MargaretSwapchainBundle swfb;

    AlicePipeline0UBO pipeline0_ubo;
    AliceAllMeshesGeneric generic_models;
    AliceAllMeshesShiny shiny_models;
    AliceCamVerticalControl cam_info;
    /* stuff like background color, hdr factor and
     * postprocessing kernel. Mostly related to renderpass 1 */
    AliceRenderConfig rendering_config;

    MargaretImg IT1_image;
    MargaretImg zbuffer_image;

    FT_Library ft_library;
    LucyGlyphCache lucy_cache;
    LucyRenderer lucy_renderer;

    VkImageView zbuffer_view;
    VkImageView IT1_view;
    VkFramebuffer IT1_framebuffer;
    VkDescriptorSet descriptor_set_for_pipeline_0b;
    VkDescriptorSet descriptor_set_for_pipeline_1;
};

ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, AliceGenericMeshPath paths){
    GenericMeshTopology topology = alice_expect_read_generic_mesh_from_file(paths.topology_path);

    ListNodeAliceGenericMeshHand* mm_node = safe_calloc(1, sizeof(ListNodeAliceGenericMeshHand));
    AliceGenericMeshHand* mm = &mm_node->el;
    mm->indexes = topology.indexes.len;
    mm->instance_attr.count = 0;
    mm->instance_attr.staging = MargaretBufAllocator_alloc(&alice->staging_buffers, 200);
    mm->instance_attr.device_local = MargaretBufAllocator_alloc(&alice->dev_local_buffers, 200);

    mm->staging_vbo = MargaretBufAllocator_alloc(&alice->staging_buffers,
        topology.vertices.len * sizeof(GenericMeshVertex));
    mm->staging_ebo = MargaretBufAllocator_alloc(&alice->staging_buffers,
        topology.indexes.len * sizeof(U32));

    // todo: change this, I don't like this at all :(
    mm->pixels_diffuse = TextureDataR8G8B8A8_read_from_png_nofail(VecU8_to_span(&paths.diffuse_texture_path));
    mm->pixels_normal = TextureDataR8G8B8A8_read_from_png_nofail(VecU8_to_span(&paths.normal_texture_path));
    mm->pixels_specular = TextureDataR8_read_from_png_nofail(VecU8_to_span(&paths.specular_texture_path));
    VecU8_drop(paths.diffuse_texture_path);
    VecU8_drop(paths.normal_texture_path);
    VecU8_drop(paths.specular_texture_path);

    mm->staging_diffuse_tex_buf = MargaretBufAllocator_alloc(&alice->staging_buffers,
        mm->pixels_diffuse.pixels.len * sizeof(cvec4));
    mm->staging_normal_tex_buf = MargaretBufAllocator_alloc(&alice->staging_buffers,
        mm->pixels_normal.pixels.len * sizeof(cvec4));
    mm->staging_specular_tex_buf = MargaretBufAllocator_alloc(&alice->staging_buffers,
        mm->pixels_specular.pixels.len * sizeof(U8));

    mm->vbo = MargaretBufAllocator_alloc(&alice->dev_local_buffers,
        topology.vertices.len * sizeof(GenericMeshVertex));
    mm->ebo = MargaretBufAllocator_alloc(&alice->dev_local_buffers,
        topology.indexes.len * sizeof(U32));

    mm->diffuse_texture = MargaretImgAllocator_alloc(&alice->dev_local_images,
        mm->pixels_diffuse.width, mm->pixels_diffuse.height,
        VK_FORMAT_R8G8B8A8_SRGB,
        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    mm->normal_texture = MargaretImgAllocator_alloc(&alice->dev_local_images,
        mm->pixels_normal.width, mm->pixels_normal.height,
        VK_FORMAT_R8G8B8A8_UNORM,
        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    mm->specular_texture = MargaretImgAllocator_alloc(&alice->dev_local_images,
        mm->pixels_specular.width, mm->pixels_specular.height,
        VK_FORMAT_R8_UNORM,
        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);

    mm->scheduled_for_deletion = false;

    /* We allocated enough memory, but now it's time to actually fill staging buffers */
    /* Filleing staging VBO */
    assert(mm->staging_vbo.len >= topology.vertices.len * sizeof(GenericMeshVertex));
    assert(mm->vbo.len >= topology.vertices.len * sizeof(GenericMeshVertex));
    GenericMeshVertex* staging_vbo = (GenericMeshVertex*)MargaretSubbuf_get_mapped(&mm->staging_vbo);
    for (U64 i = 0; i < topology.vertices.len; i++) {
        staging_vbo[i].base = topology.vertices.buf[i];
    }
    assert(topology.indexes.len % 3 == 0);
    for (size_t ti = 0; ti * 3 < topology.indexes.len; ti++) {
        U32 v0 = topology.indexes.buf[ti * 3 + 0];
        U32 v1 = topology.indexes.buf[ti * 3 + 1];
        U32 v2 = topology.indexes.buf[ti * 3 + 2];
        const GenericMeshVertexInc* A0 = VecGenericMeshVertexInc_at(&topology.vertices, v0);
        const GenericMeshVertexInc* A1 = VecGenericMeshVertexInc_at(&topology.vertices, v1);
        const GenericMeshVertexInc* A2 = VecGenericMeshVertexInc_at(&topology.vertices, v2);
        vec3 dp1 = vec3_minus_vec3(A1->pos, A0->pos);
        vec3 dp2 = vec3_minus_vec3(A2->pos, A0->pos);
        float du1 = A1->tex.x - A0->tex.x;
        float dv1 = A1->tex.y - A0->tex.y;
        float du2 = A2->tex.x - A0->tex.x;
        float dv2 = A2->tex.y - A0->tex.y;
        vec3 norm = vec3_normalize(vec3_cross(dp1, dp2));
        mat2x3 tang_U_V = mat3x2_transpose(mat2_mul_mat3x2(
            mat2_inverse(mat2_new(du1, dv1, du2, dv2)),
            mat2x3_transpose((mat2x3){.x = dp1, .y = dp2})
            ));
        staging_vbo[v0].norm = staging_vbo[v1].norm = staging_vbo[v2].norm = norm;
        staging_vbo[v0].tang_U = staging_vbo[v1].tang_U = staging_vbo[v2].tang_U = tang_U_V.x;
        staging_vbo[v0].tang_V = staging_vbo[v1].tang_V = staging_vbo[v2].tang_V = tang_U_V.y;
    }
    /* Filling EBO is easy */
    assert(topology.indexes.len == mm->indexes);
    size_t ebo_len = topology.indexes.len * sizeof(U32);
    assert(mm->ebo.len >= ebo_len);
    U32* staging_ebo = (U32*)MargaretSubbuf_get_mapped(&mm->staging_ebo);
    memcpy(staging_ebo, topology.indexes.buf, ebo_len);
    /* Filling staging textures from memory pixel data */
    /* todo: do it immediately ON THE READ */
    memcpy(MargaretSubbuf_get_mapped(&mm->staging_diffuse_tex_buf), mm->pixels_diffuse.pixels.buf,
    TextureDataR8G8B8A8_get_size_in_bytes(&mm->pixels_diffuse));
    memcpy(MargaretSubbuf_get_mapped(&mm->staging_normal_tex_buf), mm->pixels_normal.pixels.buf,
        TextureDataR8G8B8A8_get_size_in_bytes(&mm->pixels_normal));
    memcpy(MargaretSubbuf_get_mapped(&mm->staging_specular_tex_buf), mm->pixels_specular.pixels.buf,
        TextureDataR8_get_size_in_bytes(&mm->pixels_specular));
    // todo: that is about time when we can delete mm->pixels_* buffers. They are still wasting space out there
    // todo:  BUt I won't do it, because I chose inplace texture reading (from png)
    /* But now we are filling AliceGenericMeshMemDependantVkObj. If we ever gonna do defragmentation,
     * this step would have to be repeated */
    AliceGenericMeshMemDependantVkObj P = (AliceGenericMeshMemDependantVkObj){
        .diffuse_view = margaret_create_view_for_image(alice->device, mm->diffuse_texture.a.image,
            VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT),
        .normal_view = margaret_create_view_for_image(alice->device, mm->normal_texture.a.image,
            VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT),
        .specular_view = margaret_create_view_for_image(alice->device, mm->specular_texture.a.image,
            VK_FORMAT_R8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT),
        .p_0a_set_0 = margaret_allocate_descriptor_set(alice->device,
            alice->descriptor_pool, alice->pipeline_hands_0a.descriptor_set_layout),
    };
    mm->mem_dependant_vk_obj = P;

    VkDescriptorBufferInfo buffer_info_for_descriptor_0_in_set_0a = {
        .buffer = MargaretSubbuf_get_buffer(&alice->pipeline0_ubo.device_local),
        .offset = alice->pipeline0_ubo.device_local.start, .range = sizeof(Pipeline0UBO),
    };
    VkDescriptorImageInfo image_info_for_descriptor_1_in_set_0a = {
        .sampler = alice->linear_sampler, .imageView = P.diffuse_view,
        .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    };
    VkDescriptorImageInfo image_info_for_descriptor_2_in_set_0a = {
        .sampler = alice->nearest_sampler, .imageView = P.normal_view,
        .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    };
    VkDescriptorImageInfo image_info_for_descriptor_3_in_set_0a = {
        .sampler = alice->nearest_sampler, .imageView = P.specular_view,
        .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    };
    VkWriteDescriptorSet writes_in_descriptor_set[] = {
        {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = P.p_0a_set_0,
            .dstBinding = 0,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            .pBufferInfo = &buffer_info_for_descriptor_0_in_set_0a,
        },
        {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = P.p_0a_set_0,
            .dstBinding = 1,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .pImageInfo = &image_info_for_descriptor_1_in_set_0a,
        },
        {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = P.p_0a_set_0,
            .dstBinding = 2,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .pImageInfo = &image_info_for_descriptor_2_in_set_0a,
        },
        {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = P.p_0a_set_0,
            .dstBinding = 3,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .pImageInfo = &image_info_for_descriptor_3_in_set_0a,
        },
    };
    vkUpdateDescriptorSets(alice->device, ARRAY_SIZE(writes_in_descriptor_set), writes_in_descriptor_set, 0, NULL);

    ListAliceGenericMeshHand_insert_node(&alice->generic_models.hands, mm_node);
    VecRefListNodeAliceGenericMeshHand_append(&alice->generic_models.to_be_copied_to_device_next_cycle, mm_node);
    return mm_node;
}

ListNodeAliceShinyMeshHand* Alice_add_shiny_mesh(Alice* alice, VecU8 path){
    ShinyMeshTopology topology = alice_expect_read_shiny_mesh_from_file(path);

    ListNodeAliceShinyMeshHand* mm_node = safe_calloc(1, sizeof(ListNodeAliceShinyMeshHand));
    AliceShinyMeshHand* mm = &mm_node->el;

    mm->indexes = topology.indexes.len;

    mm->instance_attr.count = 0;
    mm->instance_attr.staging = MargaretBufAllocator_alloc(&alice->staging_buffers, 128);
    mm->instance_attr.device_local = MargaretBufAllocator_alloc(&alice->dev_local_buffers, 128);

    mm->staging_vbo = MargaretBufAllocator_alloc(&alice->staging_buffers,
        topology.vertices.len * sizeof(ShinyMeshVertex));
    mm->staging_ebo = MargaretBufAllocator_alloc(&alice->staging_buffers,
        topology.indexes.len * sizeof(U32));

    mm->vbo = MargaretBufAllocator_alloc(&alice->dev_local_buffers,
        topology.vertices.len * sizeof(ShinyMeshVertex));
    mm->ebo = MargaretBufAllocator_alloc(&alice->dev_local_buffers,
        topology.indexes.len * sizeof(U32));

    ListAliceShinyMeshHand_insert_node(&alice->shiny_models.hands, mm_node);
    VecRefListNodeAliceShinyMeshHand_append(&alice->shiny_models.to_be_copied_to_device_next_cycle, mm_node);
    /* We allocated enough memory, now it's time to actually fill staging buffers */
    /* And we start by filling staging VBO */
    assert(mm->staging_vbo.len >= topology.vertices.len * sizeof(ShinyMeshVertex));
    assert(mm->vbo.len >= topology.vertices.len * sizeof(ShinyMeshVertex));
    ShinyMeshVertex* staging_vbo = (ShinyMeshVertex*)MargaretSubbuf_get_mapped(&mm->staging_vbo);
    for (U64 i = 0; i < topology.vertices.len; i++) {
        staging_vbo[i].base = topology.vertices.buf[i];
    }
    assert(topology.indexes.len % 3 == 0);
    for (size_t ti = 0; ti * 3 < topology.indexes.len; ti++) {
        U32 v0 = topology.indexes.buf[ti * 3 + 0];
        U32 v1 = topology.indexes.buf[ti * 3 + 1];
        U32 v2 = topology.indexes.buf[ti * 3 + 2];
        vec3 p0 = VecShinyMeshVertexInc_at(&topology.vertices, v0)->pos;
        vec3 p1 = VecShinyMeshVertexInc_at(&topology.vertices, v1)->pos;
        vec3 p2 = VecShinyMeshVertexInc_at(&topology.vertices, v2)->pos;
        vec3 norm = vec3_normalize(vec3_cross(vec3_minus_vec3(p1, p0), vec3_minus_vec3(p2, p0)));
        staging_vbo[v0].normal = staging_vbo[v1].normal = staging_vbo[v2].normal = norm;
    }
    /* Filling staging EBO is super-duper easy */

    assert(topology.indexes.len == mm->indexes);
    size_t ebo_len = topology.indexes.len * sizeof(U32);
    assert(mm->ebo.len >= ebo_len);
    U32* staging_ebo = (U32*)MargaretSubbuf_get_mapped(&mm->staging_ebo);
    memcpy(staging_ebo, topology.indexes.buf, ebo_len);
    return mm_node;
}

void AliceGenericMeshHand_resize_instance_arr(Alice* alice, AliceGenericMeshHand* self, U64 new_count){
    U64 needed_length = new_count * sizeof(GenericMeshInstance);
    if (self->instance_attr.staging.len < needed_length) {
        printf("Alice generic model instance staging Buffer: Gotta replace %lu with %lu\n",
            self->staging_vbo.len, needed_length);
        MargaretBufAllocator_expand_or_move_old_host_visible(
            &alice->staging_buffers, &self->instance_attr.staging, needed_length);
    }
    self->instance_attr.count = new_count;
}

void AliceShinyMeshHand_resize_instance_arr(Alice* alice, AliceShinyMeshHand* self, U64 new_count){
    U64 needed_length = new_count * sizeof(ShinyMeshInstance);
    if (self->instance_attr.staging.len < needed_length) {
        MargaretBufAllocator_expand_or_move_old_host_visible(
            &alice->staging_buffers, &self->instance_attr.staging, needed_length);
    }
    self->instance_attr.count = new_count;
}

void AliceGenericMeshHand_set_inst(AliceGenericMeshHand* self, size_t instance, GenericMeshInstanceInc uncomp){
    assert(instance < self->instance_attr.count);
    GenericMeshInstance* staging = (GenericMeshInstance*)MargaretSubbuf_get_mapped(&self->instance_attr.staging);
    staging[instance].base = uncomp;
    mat4 tr_inv = mat4_transpose(mat4_inverse(uncomp.model_t));
    staging[instance].normal_t = mat3_new(
        tr_inv.x.x, tr_inv.y.x, tr_inv.z.x,
        tr_inv.x.y, tr_inv.y.y, tr_inv.z.y,
        tr_inv.x.z, tr_inv.y.z, tr_inv.z.z );
}

void AliceShinyMeshHand_set_inst(AliceShinyMeshHand* self, size_t instance, ShinyMeshInstanceInc uncomp){
    assert(instance < self->instance_attr.count);
    ShinyMeshInstance* staging = (ShinyMeshInstance*)MargaretSubbuf_get_mapped(&self->instance_attr.staging);
    staging[instance].base = uncomp;
    mat4 tr_inv = mat4_transpose(mat4_inverse(uncomp.model_t));
    staging[instance].normal_t = mat3_new(
        tr_inv.x.x, tr_inv.y.x, tr_inv.z.x,
        tr_inv.x.y, tr_inv.y.y, tr_inv.z.y,
        tr_inv.x.z, tr_inv.y.z, tr_inv.z.z );
}

void AliceAllMeshesGeneric__resolve_mesh_creation(Alice* alice, AliceAllMeshesGeneric* self){
    for (size_t i = 0; i < self->to_be_freed_of_old_staging_next_cycle.len; i++) {
        AliceGenericMeshHand* mm = &self->to_be_freed_of_old_staging_next_cycle.buf[i]->el;
        assert(mm->staging_vbo.len != 0);
        assert(mm->staging_ebo.len != 0);
        MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_vbo);
        MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_ebo);
        mm->staging_vbo.len = 0;
        mm->staging_ebo.len = 0;
    }

    for (size_t i = 0; i < self->to_be_copied_to_device_next_cycle.len; i++) {
        ListNodeAliceGenericMeshHand* mm_node = self->to_be_copied_to_device_next_cycle.buf[i];
        AliceGenericMeshHand* mm = &mm_node->el;
        assert(mm->staging_vbo.len != 0);
        assert(mm->staging_ebo.len != 0);
        if (mm->scheduled_for_deletion)
            continue;
        margaret_rec_cmd_copy_buffer_one_to_one(alice->transfer_command_buf, &mm->staging_vbo, &mm->vbo);
        margaret_rec_cmd_copy_buffer_one_to_one(alice->transfer_command_buf, &mm->staging_ebo, &mm->ebo);
        margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(alice->transfer_command_buf,
            &mm->staging_diffuse_tex_buf, &mm->diffuse_texture, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
            VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
        margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(alice->transfer_command_buf,
            &mm->staging_normal_tex_buf, &mm->normal_texture, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
            VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
        margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(alice->transfer_command_buf,
            &mm->staging_specular_tex_buf, &mm->specular_texture, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
            VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
        VecRefListNodeAliceGenericMeshHand_append(&self->to_be_freed_of_old_staging_next_cycle, mm_node);
    }
    for (size_t i = 0; i < self->to_be_deleted.len; i++) {
        ListNodeAliceGenericMeshHand* mm_node = self->to_be_deleted.buf[i];
        AliceGenericMeshHand* mm = &mm_node->el;
        assert(mm->scheduled_for_deletion);
        if (mm->staging_vbo.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_vbo);
        if (mm->staging_ebo.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_ebo);
        if (mm->staging_diffuse_tex_buf.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_diffuse_tex_buf);
        if (mm->staging_normal_tex_buf.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_normal_tex_buf);
        if (mm->staging_specular_tex_buf.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_specular_tex_buf);

        MargaretBufAllocator_free(&alice->dev_local_buffers, mm->vbo);
        MargaretBufAllocator_free(&alice->dev_local_buffers, mm->ebo);
        MargaretImgAllocator_free(&alice->dev_local_images, mm->diffuse_texture.a);
        MargaretImgAllocator_free(&alice->dev_local_images, mm->normal_texture.a);
        MargaretImgAllocator_free(&alice->dev_local_images, mm->specular_texture.a);

        // todo: the problem is, here we don't free some memory stored in AliceGenericMeshHand to store texture.
        // todo: but the truth is: I am gonna get rid of pixels_{,,} fields very soon, so AliceGenericMeshHand would
        // todo: be primitive again, without stupid crap
        ListAliceGenericMeshHand_erase_by_it(&self->hands, mm_node);
    }

    self->to_be_freed_of_old_staging_next_cycle.len = 0;
    self->to_be_copied_to_device_next_cycle.len = 0;
    self->to_be_deleted.len = 0;
}

void AliceAllMeshesShiny__resolve_mesh_creation(Alice* alice, AliceAllMeshesShiny* self){
    for (size_t i = 0; i < self->to_be_freed_of_old_staging_next_cycle.len; i++) {
        AliceShinyMeshHand* mm = &self->to_be_freed_of_old_staging_next_cycle.buf[i]->el;
        assert(mm->staging_vbo.len != 0);
        assert(mm->staging_ebo.len != 0);
        MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_vbo);
        MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_ebo);
        mm->staging_vbo.len = 0;
        mm->staging_ebo.len = 0;
    }

    for (size_t i = 0; i < self->to_be_copied_to_device_next_cycle.len; i++) {
        ListNodeAliceShinyMeshHand* mm_node = self->to_be_copied_to_device_next_cycle.buf[i];
        AliceShinyMeshHand* mm = &mm_node->el;
        assert(mm->staging_vbo.len != 0);
        assert(mm->staging_ebo.len != 0);
        if (mm->scheduled_for_deletion)
            continue;
        margaret_rec_cmd_copy_buffer_one_to_one(alice->transfer_command_buf, &mm->staging_vbo, &mm->vbo);
        margaret_rec_cmd_copy_buffer_one_to_one(alice->transfer_command_buf, &mm->staging_ebo, &mm->ebo);
        VecRefListNodeAliceShinyMeshHand_append(&self->to_be_freed_of_old_staging_next_cycle, mm_node);
    }
    for (size_t i = 0; i < self->to_be_deleted.len; i++) {
        ListNodeAliceShinyMeshHand* mm_node = self->to_be_deleted.buf[i];
        AliceShinyMeshHand* mm = &mm_node->el;
        assert(mm->scheduled_for_deletion);
        if (mm->staging_vbo.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_vbo);
        if (mm->staging_ebo.len != 0)
            MargaretBufAllocator_free(&alice->staging_buffers, mm->staging_ebo);

        MargaretBufAllocator_free(&alice->dev_local_buffers, mm->vbo);
        MargaretBufAllocator_free(&alice->dev_local_buffers, mm->ebo);

        ListAliceShinyMeshHand_erase_by_it(&self->hands, mm_node);
    }

    self->to_be_freed_of_old_staging_next_cycle.len = 0;
    self->to_be_copied_to_device_next_cycle.len = 0;
    self->to_be_deleted.len = 0;
}

/* No buffer rerecording, no buffer beginning, no buffer ending,
 * 1) It copies initial generic model and shiny model topology. Textures of generic models included
 * 2) For all models and for all light sources it copies EVERYTHING.
 * You won't normally need to copy every single thing, but here you have the most bold solution.
 * 3) As mentioned before, Pipeline0UBO also gets copied
 */
void AliceScene__another_frame(Alice* alice) {
    AliceAllMeshesGeneric__resolve_mesh_creation(alice, &alice->generic_models);
    AliceAllMeshesShiny__resolve_mesh_creation(alice, &alice->shiny_models);

    for (ListNodeAliceGenericMeshHand* mm_node = alice->generic_models.hands.first; mm_node; mm_node = mm_node->next) {
        AliceGenericMeshHand* mm = &mm_node->el;
        assert(mm->instance_attr.count * sizeof(GenericMeshInstance) <= mm->instance_attr.staging.len);
        if (mm->instance_attr.count * sizeof(GenericMeshInstance) > mm->instance_attr.device_local.len) {
            MargaretBufAllocator_expand_or_free_old(&alice->dev_local_buffers, &mm->instance_attr.device_local,
                mm->instance_attr.count * sizeof(GenericMeshInstance));
        }
        assert(mm->instance_attr.count * sizeof(GenericMeshInstance) <= mm->instance_attr.device_local.len);
        if (mm->instance_attr.count) {
            margaret_rec_cmd_copy_buffer_one_to_one_part(alice->transfer_command_buf,
                &mm->instance_attr.staging, &mm->instance_attr.device_local, 0,
                mm->instance_attr.count * sizeof(GenericMeshInstance));
        }
    }
    for (ListNodeAliceShinyMeshHand* mm_node = alice->shiny_models.hands.first; mm_node; mm_node = mm_node->next) {
        AliceShinyMeshHand* mm = &mm_node->el;
        assert(mm->instance_attr.count * sizeof(ShinyMeshInstance) <= mm->instance_attr.staging.len);
        if (mm->instance_attr.count * sizeof(ShinyMeshInstance) > mm->instance_attr.device_local.len) {
            MargaretBufAllocator_expand_or_free_old(&alice->dev_local_buffers, &mm->instance_attr.device_local,
                mm->instance_attr.count * sizeof(ShinyMeshInstance));
        }
        assert(mm->instance_attr.count * sizeof(ShinyMeshInstance) <= mm->instance_attr.device_local.len);
        if (mm->instance_attr.count) {
            margaret_rec_cmd_copy_buffer_one_to_one_part(alice->transfer_command_buf,
                &mm->instance_attr.staging, &mm->instance_attr.device_local, 0,
                mm->instance_attr.count * sizeof(ShinyMeshInstance));
        }
    }
    Pipeline0UBO* mem = (Pipeline0UBO*)MargaretSubbuf_get_mapped(&alice->pipeline0_ubo.staging);
    assert(mem->point_light_count <= pipeline_0_ubo_point_light_max_count);
    assert(mem->spotlight_count <= pipeline_0_ubo_spotlight_max_count);

    // todo: I will probably replace pipeline0ubo with ubo for length + two readonly storage buffers for light sources
    // all of this is basically useless

    const MargaretSubbuf* ubo_staging = &alice->pipeline0_ubo.staging;
    const MargaretSubbuf* ubo_device_local = &alice->pipeline0_ubo.device_local;

    margaret_rec_cmd_copy_buffer_one_to_one_part(alice->transfer_command_buf, ubo_staging, ubo_device_local,
        offsetof(Pipeline0UBO, point_light_count), sizeof(int));
    margaret_rec_cmd_copy_buffer_one_to_one_part(alice->transfer_command_buf, ubo_staging, ubo_device_local,
        offsetof(Pipeline0UBO, spotlight_count), sizeof(int));
    if (mem->point_light_count) {
        margaret_rec_cmd_copy_buffer_one_to_one_part(alice->transfer_command_buf, ubo_staging, ubo_device_local,
            offsetof(Pipeline0UBO, point_light_arr), sizeof(Pipeline0PointLight) * mem->point_light_count);
    }
    if (mem->spotlight_count) {
        margaret_rec_cmd_copy_buffer_one_to_one_part(alice->transfer_command_buf, ubo_staging, ubo_device_local,
            offsetof(Pipeline0UBO, spotlight_arr), sizeof(Pipeline0Spotlight) * mem->spotlight_count);
    }
}


void alice_reset_and_record_command_buffer_0(Alice* alice, mat4 proj_cam_t) {
    margaret_reset_and_begin_command_buffer(alice->rendering_command_buf_0);

    vec4 clear_color = alice->rendering_config.clear_color;
    VkExtent2D image_extent = alice->swfb.extent;
    vkCmdBeginRenderPass(alice->rendering_command_buf_0, &(VkRenderPassBeginInfo){
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
        .renderPass = alice->render_pass_0,
        .framebuffer = alice->IT1_framebuffer,
        .renderArea.offset = (VkOffset2D){0, 0},
        .renderArea.extent = image_extent,
        .clearValueCount = 2,
        .pClearValues = (VkClearValue[]){
            {.color.float32 = {clear_color.x, clear_color.y, clear_color.z, clear_color.w}},
            {.depthStencil = {.depth = 1, .stencil = 0}}
        },
    }, VK_SUBPASS_CONTENTS_INLINE);

    vkCmdBindPipeline(alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0a.pipeline);
    record_cmd_set_viewport_and_scissors(alice->rendering_command_buf_0, image_extent);
    vkCmdPushConstants(alice->rendering_command_buf_0, alice->pipeline_hands_0a.pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
            0, sizeof(mat4), &proj_cam_t);
    vkCmdPushConstants(alice->rendering_command_buf_0, alice->pipeline_hands_0a.pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT,
        sizeof(mat4), sizeof(vec3), &alice->cam_info.cam.pos);

    for (ListNodeAliceGenericMeshHand* mm_node = alice->generic_models.hands.first; mm_node; mm_node = mm_node->next) {
        AliceGenericMeshHand* model = &mm_node->el;
        VkDescriptorSet model_indiv_descriptor_set_0 = model->mem_dependant_vk_obj.p_0a_set_0;

        const MargaretSubbuf* dev_local_vbo = &model->vbo;
        const MargaretSubbuf* dev_local_inst_attr = &model->instance_attr.device_local;
        const MargaretSubbuf* dev_local_ebo = &model->ebo;
        // const
        vkCmdBindVertexBuffers(alice->rendering_command_buf_0, 0,
        2, (VkBuffer[]){
            MargaretSubbuf_get_buffer(dev_local_vbo),
            MargaretSubbuf_get_buffer(dev_local_inst_attr),
        }, (VkDeviceSize[]){ dev_local_vbo->start, dev_local_inst_attr->start });
        vkCmdBindIndexBuffer(alice->rendering_command_buf_0, MargaretSubbuf_get_buffer(dev_local_ebo), dev_local_ebo->start,
            VK_INDEX_TYPE_UINT32);
        vkCmdBindDescriptorSets(
            alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0a.pipeline_layout, 0,
            1, &model_indiv_descriptor_set_0, 0, NULL);
        vkCmdDrawIndexed(alice->rendering_command_buf_0, model->indexes, model->instance_attr.count, 0, 0, 0);
    }

    vkCmdBindPipeline(alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0b.pipeline);
    record_cmd_set_viewport_and_scissors(alice->rendering_command_buf_0, image_extent);
    vkCmdPushConstants(alice->rendering_command_buf_0, alice->pipeline_hands_0b.pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
            0, sizeof(mat4), &proj_cam_t);
    vkCmdPushConstants(alice->rendering_command_buf_0, alice->pipeline_hands_0b.pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT,
        sizeof(mat4), sizeof(vec3), &alice->cam_info.cam.pos);
    vkCmdBindDescriptorSets(
            alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0b.pipeline_layout, 0,
            1, &alice->descriptor_set_for_pipeline_0b, 0, NULL);
    for (ListNodeAliceShinyMeshHand* mm_node = alice->shiny_models.hands.first; mm_node; mm_node = mm_node->next) {
        const AliceShinyMeshHand* model = &mm_node->el;
        const MargaretSubbuf* dev_local_vbo = &model->vbo;
        const MargaretSubbuf* dev_local_inst_attr = &model->instance_attr.device_local;
        const MargaretSubbuf* dev_local_ebo = &model->ebo;
        vkCmdBindVertexBuffers(alice->rendering_command_buf_0, 0, 2, (VkBuffer[]){
            MargaretSubbuf_get_buffer(dev_local_vbo), MargaretSubbuf_get_buffer(dev_local_inst_attr)
        }, (VkDeviceSize[]){ dev_local_vbo->start, dev_local_inst_attr->start });
        vkCmdBindIndexBuffer(alice->rendering_command_buf_0, MargaretSubbuf_get_buffer(dev_local_ebo), dev_local_ebo->start,
            VK_INDEX_TYPE_UINT32);
        vkCmdDrawIndexed(alice->rendering_command_buf_0, model->indexes, model->instance_attr.count, 0, 0, 0);
    }

    vkCmdEndRenderPass(alice->rendering_command_buf_0);
    margaret_end_command_buffer(alice->rendering_command_buf_0);
}

void alice_reset_and_record_command_buffer_1(
    Alice* alice, VkFramebuffer swapchain_image_framebuffer
    ) {
    margaret_reset_and_begin_command_buffer(alice->rendering_command_buf_1);

    VkExtent2D image_extent = alice->swfb.extent;
    vkCmdBeginRenderPass(alice->rendering_command_buf_1, &(VkRenderPassBeginInfo){
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
        .renderPass = alice->render_pass_1,
        .framebuffer = swapchain_image_framebuffer,
        .renderArea.offset = (VkOffset2D){0, 0},
        .renderArea.extent = image_extent,
    }, VK_SUBPASS_CONTENTS_INLINE);
    vkCmdBindPipeline(alice->rendering_command_buf_1, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_1.pipeline);

    record_cmd_set_viewport_and_scissors(alice->rendering_command_buf_1, image_extent);
    vkCmdBindDescriptorSets(
        alice->rendering_command_buf_1, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_1.pipeline_layout, 0,
        1, &alice->descriptor_set_for_pipeline_1, 0, NULL);

    Pipeline1PushRangeVertex reg_and_params_vertex = {.win_scale = {
        (float)image_extent.width / (float)alice->wl.sane_image_extent_limit.width,
        (float)image_extent.height / (float)alice->wl.sane_image_extent_limit.height},
    };
    Pipeline1PushRangeFragment reg_and_params_fragment = {
        .gamma_correction_factor = alice->rendering_config.gamma_correction_factor,
        .hdr_factor = alice->rendering_config.hdr_factor,
        .lsd_factor = alice->rendering_config.lsd_factor,
        .anim_time = 0 /* Not used */
    };
    vkCmdPushConstants(alice->rendering_command_buf_1, alice->pipeline_hands_1.pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
        0, sizeof(Pipeline1PushRangeVertex), &reg_and_params_vertex);
    vkCmdPushConstants(alice->rendering_command_buf_1, alice->pipeline_hands_1.pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT,
        sizeof(Pipeline1PushRangeVertex), sizeof(Pipeline1PushRangeFragment), &reg_and_params_fragment);
    vkCmdDraw(alice->rendering_command_buf_1, 3, 1, 0, 0);

    LucyRenderer_another_frame_rec_drawing(&alice->lucy_renderer, alice->rendering_command_buf_1, image_extent);

    vkCmdEndRenderPass(alice->rendering_command_buf_1);
    margaret_end_command_buffer(alice->rendering_command_buf_1);
}

void recreate_swapchain(Alice *alice) {
    // We are stopping program and rebuilding our sem+sem+fence synchronization mechanism
    vkDeviceWaitIdle(alice->device);  // todo: AAAAAAAAAAAAAAA this is all so so wrong AAAAAAAA
    Jane_alice_destroy(alice->device, alice->jane);
    alice->jane = Jane_alice_create(alice->device);
    VkSwapchainKHR old_swapchain = MargaretSwapchainBundle_pop_swapchain_drop_rest(
        alice->device, alice->swfb);
    // old swfb is 83% dropped
    VkExtent2D extent = alice->wl.width_confirmed <= 0 || alice->wl.height_confirmed <= 0 ?
    alice->wl.sane_image_extent_limit : (VkExtent2D){ alice->wl.width_confirmed, alice->wl.height_confirmed };

    ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(
        alice->physical_device, alice->surface, extent);
    if (swapchain_details_res.variant != Result_Ok)
        abortf("swapchain_details_res.variant != Result_Ok");
    MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok;
    MargaretSwapchainBundle new_swfb = MargaretSwapchainBundle_new(alice->device, alice->queue_fam,
        swapchain_details, alice->surface, alice->render_pass_1, alice->swfb.swapchain);
    vkDestroySwapchainKHR(alice->device, old_swapchain, NULL);
    // Now old swfb is 100% dropped
    alice->swfb = new_swfb;
}

/* It creates image views, descriptor sets, framebuffers. But not for generic models.
 * If we ever gonna do defragmentation, this step would have to be repeated */
void alice_create_mem_dependant_vk_obj(Alice* alice){
    alice->zbuffer_view = margaret_create_view_for_image(alice->device, alice->zbuffer_image.a.image,
        alice->zbuffer_format, VK_IMAGE_ASPECT_DEPTH_BIT);
    alice->IT1_view = margaret_create_view_for_image(alice->device, alice->IT1_image.a.image,
        alice->IT1_format, VK_IMAGE_ASPECT_COLOR_BIT);
    alice->IT1_framebuffer = create_IT1_framebuffer(alice->device,
        alice->IT1_view, alice->zbuffer_view, alice->render_pass_0,
        alice->wl.sane_image_extent_limit.width, alice->wl.sane_image_extent_limit.height);

    alice->descriptor_set_for_pipeline_0b = margaret_allocate_descriptor_set(
        alice->device, alice->descriptor_pool, alice->pipeline_hands_0b.descriptor_set_layout);
    alice->descriptor_set_for_pipeline_1 = margaret_allocate_descriptor_set(
        alice->device, alice->descriptor_pool, alice->pipeline_hands_1.descriptor_set_layout);

    // todo: separate set0 and set 1
    VkDescriptorBufferInfo buffer_info_for_descriptor_0_in_set_0b = {
        .buffer = MargaretSubbuf_get_buffer(&alice->pipeline0_ubo.device_local),
        .offset = alice->pipeline0_ubo.device_local.start, .range = sizeof(Pipeline0UBO),
    };
    VkDescriptorImageInfo image_info_for_descriptor_0_in_set_1 = {
        .sampler = alice->nearest_sampler, .imageView = alice->IT1_view,
        .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    };
    VkWriteDescriptorSet writes_in_descriptor_sets[] = {
        {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = alice->descriptor_set_for_pipeline_0b,
            .dstBinding = 0,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            .pBufferInfo = &buffer_info_for_descriptor_0_in_set_0b,
        },

        {
            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            .dstSet = alice->descriptor_set_for_pipeline_1,
            .dstBinding = 0,
            .dstArrayElement = 0,
            .descriptorCount = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .pImageInfo = &image_info_for_descriptor_0_in_set_1,
        },
    };
    vkUpdateDescriptorSets(alice->device, ARRAY_SIZE(writes_in_descriptor_sets), writes_in_descriptor_sets, 0, NULL);
}

/* Needed for defragmentation of memory */
void destroy_vulkan_reference_objects(Alice* alice){
    abortf("Please don't\n");
    // todo: vkdestro all the views all the framebuffers
    // todo: drop everything,
}

void alice_frame_drawing(Alice* alice) {
    check(vkWaitForFences(alice->device, 1, &alice->jane.in_flight_fence, VK_TRUE, UINT64_MAX) == VK_SUCCESS);
    check(vkResetFences(alice->device, 1, &alice->jane.in_flight_fence) == VK_SUCCESS);
    uint32_t ij;
    VkResult aq_ret;
    and_again:
    aq_ret = vkAcquireNextImageKHR(
        alice->device, alice->swfb.swapchain,
        UINT64_MAX, alice->jane.image_available_semaphore, VK_NULL_HANDLE, &ij
    );
    if (aq_ret == VK_ERROR_OUT_OF_DATE_KHR || aq_ret == VK_SUBOPTIMAL_KHR) {
        fprintf(stderr, "vkAcquireNextImageKHR in { VK_ERROR_OUT_OF_DATE_KHR, VK_SUBOPTIMAL_KHR }\n");
        recreate_swapchain(alice);
        goto and_again;
    } else if (aq_ret != VK_SUCCESS) {
        abortf("vkAcquireNextImageKHR");
    }

    mat4 projection_matrix = marie_perspective_projection_fov_mat4(
        (float)alice->wl.width_confirmed, (float)alice->wl.height_confirmed,
        alice->cam_info.cam.fov, 0.01f, 1000);
    mat4 camera_rotation_matrix = marie_mat3_to_mat4_transposed(alice->cam_info.cam.cam_basis);
    mat4 camera_translation_matrix = marie_translation_mat4(vec3_minus(alice->cam_info.cam.pos));
    mat4 t_mat = mat4_mul_mat4(projection_matrix, mat4_mul_mat4(camera_rotation_matrix, camera_translation_matrix));

    alice->callbacks.on_another_frame(alice->guest, (float)(alice->wl.cur_frame_time - alice->wl.last_frame_time) / 1000);

    margaret_reset_and_begin_command_buffer(alice->transfer_command_buf);
    AliceScene__another_frame(alice);
    LucyGlyphCache_another_frame(&alice->lucy_cache);
    LucyRenderer_another_frame(&alice->lucy_renderer);
    margaret_end_command_buffer(alice->transfer_command_buf);
    alice_reset_and_record_command_buffer_0(alice, t_mat);
    alice_reset_and_record_command_buffer_1(alice, *VecVkFramebuffer_at(&alice->swfb.framebuffers, ij));

    check(vkQueueSubmit(alice->queues.graphics_queue, 1, &(VkSubmitInfo){
        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
        .commandBufferCount = 1,
        .pCommandBuffers = (VkCommandBuffer[]){ alice->transfer_command_buf },
        .signalSemaphoreCount = 1,
        .pSignalSemaphores = (VkSemaphore[]){ alice->jane.in_frame_transfer_complete },
    }, NULL) == VK_SUCCESS);

    check(vkQueueSubmit(alice->queues.graphics_queue, 1, &(VkSubmitInfo){
        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,

        .waitSemaphoreCount = 1,
        .pWaitSemaphores = (VkSemaphore[]){alice->jane.in_frame_transfer_complete},
        .pWaitDstStageMask = (VkPipelineStageFlags[]){
            VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
        },

        .commandBufferCount = 1,
        .pCommandBuffers = (VkCommandBuffer[]){ alice->rendering_command_buf_0 },

        .signalSemaphoreCount = 1,
        .pSignalSemaphores = (VkSemaphore[]){ alice->jane.rendered_to_IT1_semaphore },
    }, NULL) == VK_SUCCESS);

    check(vkQueueSubmit(alice->queues.graphics_queue, 1, &(VkSubmitInfo){
        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
        .waitSemaphoreCount = 2,
        .pWaitSemaphores = (VkSemaphore[]){
            alice->jane.image_available_semaphore, alice->jane.rendered_to_IT1_semaphore },
        .pWaitDstStageMask = (VkPipelineStageFlags[]){
            VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT },
        .commandBufferCount = 1,
        .pCommandBuffers = (VkCommandBuffer[]){ alice->rendering_command_buf_1 },
        .signalSemaphoreCount = 1,
        .pSignalSemaphores = (VkSemaphore[]){
            *VecVkSemaphore_at(&alice->swfb.rendering_finished_here_semaphores, ij) },
    }, alice->jane.in_flight_fence) == VK_SUCCESS);

    VkPresentInfoKHR present_info = {
        .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
        .waitSemaphoreCount = 1,
        .pWaitSemaphores = (VkSemaphore[]){
            *VecVkSemaphore_at(&alice->swfb.rendering_finished_here_semaphores, ij) },
        .swapchainCount = 1,
        .pSwapchains = (VkSwapchainKHR[]){ alice->swfb.swapchain },
        .pImageIndices = (U32[]){ ij },
        .pResults = NULL,
    };
    VkResult present_ret = vkQueuePresentKHR(alice->queues.presentation_queue, &present_info);
    if (present_ret == VK_ERROR_OUT_OF_DATE_KHR || present_ret == VK_SUBOPTIMAL_KHR) {
        fprintf(stderr, "vkQueuePresentKHR: VK_ERROR_OUT_OF_DATE_KHR or \n");
        check(vkWaitForFences(alice->device, 1, &alice->jane.in_flight_fence, VK_TRUE, UINT64_MAX) == VK_SUCCESS);
        check(vkResetFences(alice->device, 1, &alice->jane.in_flight_fence) == VK_SUCCESS);
        recreate_swapchain(alice);
        goto and_again;
    } else if (present_ret != VK_SUCCESS) {
        abortf("vkQueuePresentKHR");
    }

    margaret_ns_time frame_B0 = margaret_clock_gettime_monotonic_raw();
    alice->wl.frame_count_since_key++;
    if (margaret_ns_time_sec_diff(alice->wl.prev_key_frame_time, frame_B0) > 1.0) {
        float fps = (float)alice->wl.frame_count_since_key / margaret_ns_time_sec_diff(alice->wl.prev_key_frame_time, frame_B0);
        printf("FPS: %0.1lf\n", fps);
        alice->wl.frame_count_since_key = 0;
        alice->wl.prev_key_frame_time = frame_B0;
    }
}

static void alice_mainloop_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){
    Alice* alice = data;
    printf("XDG surface configured! (%d %d)\n", alice->wl.width_heard, alice->wl.height_heard);
    alice->wl.width_confirmed = alice->wl.width_heard;
    alice->wl.height_confirmed = alice->wl.height_heard;
    xdg_surface_ack_configure(xdg_surface, serial);
    // todo: omg, I need to fix this crap for real, like, this is all so wrong
    recreate_swapchain(alice);
    alice_frame_drawing(alice);
}

static const struct xdg_surface_listener xdg_surface_listener = {
    .configure = alice_mainloop_h_xdg_surface_configure,
};

static void alice_mainloop_h_xdg_toplevel_configure(
    void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states
    ){
    Alice* alice = data;
    uint32_t *conf_state;
    wl_array_for_each(conf_state, states) {
        printf("A state of xdg toplevel: %u\n", *conf_state);
    }
    printf("XDG toplevel configured to (%d %d)\n", width, height);
    if (width <= 0 || height <= 0)
        return;
    alice->wl.width_heard = width;
    alice->wl.height_heard = height;
}

static void alice_mainloop_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){
    Alice* alice = data;
    alice->wl.closed = true;
}

static const struct xdg_toplevel_listener alice_mainloop_h_xdg_toplevel_listener = {
    .configure = alice_mainloop_h_xdg_toplevel_configure,
    .close = alice_mainloop_h_xdg_toplevel_close,
};

static void alice_mainloop_h_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial){
    xdg_wm_base_pong(xdg_wm_base, serial);
}

static const struct xdg_wm_base_listener alice_mainloop_h_xdg_wm_base_listener = {
    .ping = alice_mainloop_h_xdg_wm_base_ping,
};

static void alice_mainloop_h_wl_keyboard_keymap(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size
    ) {
    Alice* alice = data;
    if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
        abortf("O_o");
    char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
    if (map_shm == MAP_FAILED)
        abortf("Couldn't mmap new keymap ha-ha\n");

    xkb_keymap_unref(alice->wl.xkb_keymap);
    xkb_state_unref(alice->wl.xkb_state);
    alice->wl.xkb_keymap = xkb_keymap_new_from_string(
         alice->wl.xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
    munmap(map_shm, size);
    close(fd);
    alice->wl.xkb_state = xkb_state_new(alice->wl.xkb_keymap);
    printf("Keymap changed!\n");
    memset(&alice->wl.first_0x80_keys, 0, sizeof(alice->wl.first_0x80_keys));
}

static void alice_mainloop_h_wl_keyboard_enter(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys
    ) {
    Alice* alice = data;
    printf("keyboard enter; keys pressed are:\n");
    uint32_t *key;
    wl_array_for_each(key, keys) {
        uint32_t actual_key = *key + 8;
        xkb_keysym_t sym = xkb_state_key_get_one_sym(alice->wl.xkb_state, actual_key);
        if (sym < 0x80)
            alice->wl.first_0x80_keys[sym] = true;
    }
}

static void alice_mainloop_h_wl_keyboard_leave(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface
    ) {
    Alice* alice = data;
    memset(&alice->wl.first_0x80_keys, 0, sizeof(alice->wl.first_0x80_keys));
}

static void alice_mainloop_h_wl_keyboard_key(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action
    ) {
    Alice* alice = data;
    uint32_t actual_key = key + 8;
    /* It actually could be nokey keysym. You never know without checking */
    xkb_keysym_t keysym = xkb_state_key_get_one_sym(alice->wl.xkb_state, actual_key);
    if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_RELEASED) {
        alice->wl.first_0x80_keys[keysym] = false;
    } else if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) {
        alice->wl.first_0x80_keys[keysym] = true;
    }
    alice->callbacks.on_wl_keyboard_key(alice->guest, keysym, key_action);
    if (key_action == WL_KEYBOARD_KEY_STATE_RELEASED) {
        if (keysym == XKB_KEY_1) {
            // vec3 p = alice->cam_info.pos;
            // p.y += 1.5f;
            // ShinyModelOnSceneMem* model = VecShinyModelOnSceneMem_mat(&alice->scene.shiny_models, 0);
            // assert(model->instance_attr.count >= 1);
            // VecObjectInfo_mat(&alice->scene.smeshnyavka_3, 0)->pos = p;
            // Scene_update_smeshnyavka_3(&alice->scene, 0);
            //
            // Pipeline0UBO* ubo = (Pipeline0UBO*)MargaretSubbuf_get_mapped(&alice->scene.pipeline0_ubo.staging_updatable);
            // assert(ubo->point_light_count >= 1);
            // ubo->point_light_arr[0].pos = p;
            //
            // printf("Point light source pos set to %f %f %f\n", p.x, p.y, p.z);
        } else if (keysym == XKB_KEY_2) {
            alice->rendering_config.hdr_factor /= 1.05f;
            printf("hdr factor decreased to %f\n", alice->rendering_config.hdr_factor);
        } else if (keysym == XKB_KEY_3) {
            alice->rendering_config.hdr_factor *= 1.05f;
            printf("hdr factor increased to %f\n", alice->rendering_config.hdr_factor);
        }
    }
}

static void alice_mainloop_h_wl_keyboard_modifiers(
    void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
    uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group
    ) {
    Alice* alice = data;
    xkb_state_update_mask(alice->wl.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
}

static void alice_mainloop_h_wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){
    printf("Repeat timings changed: rate = %d, delay = %d\n", rate, delay);
}

static const struct wl_keyboard_listener alice_mainloop_h_wl_keyboard_listener = {
    .keymap = alice_mainloop_h_wl_keyboard_keymap,
    .enter = alice_mainloop_h_wl_keyboard_enter,
    .leave = alice_mainloop_h_wl_keyboard_leave,
    .key = alice_mainloop_h_wl_keyboard_key,
    .modifiers = alice_mainloop_h_wl_keyboard_modifiers,
    .repeat_info = alice_mainloop_h_wl_keyboard_repeat_info,
};


static void alice_mainloop_h_wl_pointer_enter(
    void *data, struct wl_pointer *wl_pointer, uint32_t serial,
    struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y
    ) {
}

static void alice_mainloop_h_wl_pointer_leave(
    void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface
    ) {
}

static void alice_mainloop_h_wl_pointer_motion(
    void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y
    ) {
    Alice* alice = data;
    AliceCamVerticalControl_update_direction(&alice->cam_info,
        alice->wl.width_confirmed, alice->wl.height_confirmed,
        (float)surface_x / 256.f, (float)surface_y / 256.f);
}

static void alice_mainloop_h_wl_pointer_button(
    void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action
    ) {
    Alice* alice = data;
}

static void alice_mainloop_h_wl_pointer_axis(
    void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value
    ) {
    Alice* alice = data;
}

static void alice_mainloop_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
    Alice* alice = data;
}

const struct wl_pointer_listener alice_mainloop_h_wl_pointer_listener = {
    .enter = alice_mainloop_h_wl_pointer_enter,
    .leave = alice_mainloop_h_wl_pointer_leave,
    .motion = alice_mainloop_h_wl_pointer_motion,
    .button = alice_mainloop_h_wl_pointer_button,
    .axis = alice_mainloop_h_wl_pointer_axis,
    .frame = alice_mainloop_h_wl_pointer_frame
};

static void alice_mainloop_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) {
    Alice* alice = data;
    if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
        alice->wl.pointer = wl_seat_get_pointer(wl_seat);
        if (!alice->wl.pointer)
            abortf("wl_seat_get_pointer\n");
        wl_pointer_add_listener(alice->wl.pointer, &alice_mainloop_h_wl_pointer_listener, alice);
    }
    if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
        alice->wl.keyboard = wl_seat_get_keyboard(wl_seat);
        if (!alice->wl.keyboard)
            abortf("wl_seat_get_keyboard\n");
        wl_keyboard_add_listener(alice->wl.keyboard, &alice_mainloop_h_wl_keyboard_listener, alice);
    }
}

static void alice_mainloop_h_wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) {
    printf("Our seat name: %s\n", name);
}

static const struct wl_seat_listener alice_mainloop_h_wl_seat_listener = {
    .capabilities = alice_mainloop_h_wl_seat_capabilities,
    .name = alice_mainloop_h_wl_seat_name,
};

static void alice_mainloop_h_wl_registry_global(
    void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version
    ) {
    Alice* alice = data;
    if (strcmp(interface, wl_compositor_interface.name) == 0) {
        alice->wl.wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
        if (!alice->wl.wl_compositor)
            abortf("wl_registry_bind\n");
    } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
        alice->wl.xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1);
        if (!alice->wl.xdg_wm_base)
            abortf("wl_registry_bind\n");
        xdg_wm_base_add_listener(alice->wl.xdg_wm_base, &alice_mainloop_h_xdg_wm_base_listener, alice);
    } else if (strcmp(interface, wl_seat_interface.name) == 0) {
        if (alice->wl.wl_seat) {
            printf("We got second seat, but we only need one\n");
            return;
        }
        alice->wl.wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 4);
        if (!alice->wl.wl_seat)
            abortf("wl_registry_bind\n");
        wl_seat_add_listener(alice->wl.wl_seat, &alice_mainloop_h_wl_seat_listener, alice);
    }
}

static void alice_mainloop_h_wl_registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name){

}

static const struct wl_registry_listener alice_mainloop_h_wl_registry_listener = {
    .global = alice_mainloop_h_wl_registry_global,
    .global_remove = alice_mainloop_h_wl_registry_global_remove,
};

static const struct wl_callback_listener alice_mainloop_h_wl_surface_frame_listener;

static void alice_mainloop_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){
    Alice* alice = data;
    wl_callback_destroy(cb);
    alice->wl.wl_callback = wl_surface_frame(alice->wl.wl_surface);
    if (!alice->wl.wl_callback)
        abortf("wl_surface_frame\n");
    wl_callback_add_listener(alice->wl.wl_callback, &alice_mainloop_h_wl_surface_frame_listener, alice);

    alice->wl.cur_frame_time = time;
    alice_frame_drawing(alice);
    alice->wl.last_frame_time = time;
}

static const struct wl_callback_listener alice_mainloop_h_wl_surface_frame_listener = {
    .done = alice_mainloop_h_wl_surface_frame_done,
};

Alice* Alice_new(){
    Alice* alice = safe_malloc(sizeof(Alice));
    SpanU8 root_dir = cstr(".");
    SpanU8 GPU = cstr("nvidia");
    SpanU8 bugged_GPU = cstr("nothere");
    bool ENABLE_VALIDATION_LAYERS = true;
    const U32 MAX_WIN_WIDTH = 1920;
    const U32 MAX_WIN_HEIGHT = 1080;
    AliceCallbacks_set_default(&alice->callbacks);

    alice->wl.sane_image_extent_limit = (VkExtent2D){MAX_WIN_WIDTH, MAX_WIN_HEIGHT};

    alice->wl.wl_display = wl_display_connect(NULL);
    if (!alice->wl.wl_display)
        abortf("Could not connect");
    alice->wl.wl_registry = wl_display_get_registry(alice->wl.wl_display);
    if (!alice->wl.wl_registry)
        abortf("wl_display_get_registry");
    wl_registry_add_listener(alice->wl.wl_registry, &alice_mainloop_h_wl_registry_listener, alice);
    wl_display_roundtrip(alice->wl.wl_display);
    if (!alice->wl.wl_compositor)
        abortf("No wl_compositor");
    if (!alice->wl.xdg_wm_base)
        abortf("No xdg_wm_base");
    alice->wl.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
    if (!alice->wl.xkb_context)
        abortf("xkb_context_new\n");
    alice->wl.wl_surface = wl_compositor_create_surface(alice->wl.wl_compositor);
    if (!alice->wl.wl_surface)
        abortf("wl_compositor_create_surface\n");
    alice->wl.xdg_surface = xdg_wm_base_get_xdg_surface(
            alice->wl.xdg_wm_base, alice->wl.wl_surface);
    if (!alice->wl.xdg_surface)
        abortf("xdg_wm_base_get_xdg_surface\n");
    xdg_surface_add_listener(alice->wl.xdg_surface, &xdg_surface_listener, alice);
    alice->wl.xdg_toplevel = xdg_surface_get_toplevel(alice->wl.xdg_surface);
    if (!alice->wl.xdg_toplevel)
        abortf("xdg_surface_get_toplevel\n");
    xdg_toplevel_add_listener(alice->wl.xdg_toplevel, &alice_mainloop_h_xdg_toplevel_listener, alice);
    xdg_toplevel_set_title(alice->wl.xdg_toplevel, "r3");
    xdg_toplevel_set_app_id(alice->wl.xdg_toplevel, "r3");
    wl_surface_commit(alice->wl.wl_surface);

    alice->wl.wl_callback = wl_surface_frame(alice->wl.wl_surface);
    if (!alice->wl.wl_callback)
        abortf("wl_surface_frame\n");
    wl_callback_add_listener(alice->wl.wl_callback, &alice_mainloop_h_wl_surface_frame_listener, alice);
    alice->wl.cur_frame_time = alice->wl.last_frame_time = 0;

    alice->instance_and_debug = MargaretInstanceAndItsDebug_new(ENABLE_VALIDATION_LAYERS);
    VkInstance instance = alice->instance_and_debug.instance;

    // print_instance_available_extensions(instance);
    // print_instance_available_layers(instance);

    alice->surface = margaret_create_surface(instance, alice->wl.wl_display, alice->wl.wl_surface);

    alice->physical_device = margaret_select_one_physical_device(
        instance, alice->surface, GPU, bugged_GPU, alice->wl.sane_image_extent_limit);

    // print_physical_device_available_extensions(physical_device);

    ResultMargaretChosenQueueFamiliesOrSpanU8 queue_fam_res = margaret_choose_good_queue_families(
        alice->physical_device, alice->surface);
    if (queue_fam_res.variant != Result_Ok)
        abortf("queue_fam_res.variant != Result_Ok");
    alice->queue_fam = queue_fam_res.ok;

    alice->device = margaret_create_logical_device(alice->physical_device, alice->queue_fam);

    if (alice->queue_fam.for_graphics == alice->queue_fam.for_presentation) {
        vkGetDeviceQueue(alice->device, alice->queue_fam.for_graphics, 0, &alice->queues.graphics_queue);
        alice->queues.presentation_queue = alice->queues.graphics_queue;
    } else {
        vkGetDeviceQueue(alice->device, alice->queue_fam.for_graphics, 0, &alice->queues.graphics_queue);
        vkGetDeviceQueue(alice->device, alice->queue_fam.for_presentation, 0, &alice->queues.presentation_queue);
    }

    ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(
        alice->physical_device, alice->surface, alice->wl.sane_image_extent_limit);
    if (swapchain_details_res.variant != Result_Ok)
        abortf("swapchain_details_res.variant != Result_Ok");

    OptionVkFormat zbuffer_format_found = margaret_find_supported_zbuffer_format(alice->physical_device);
    alice->zbuffer_format = OptionVkFormat_expect(zbuffer_format_found);
    OptionVkFormat IT1_format_found = margaret_find_supported_hdr_buffer_format(alice->physical_device);
    alice->IT1_format = OptionVkFormat_expect(IT1_format_found);

    alice->render_pass_0 = create_render_pass_0(alice->device, alice->IT1_format, alice->zbuffer_format);
    alice->pipeline_hands_0a = create_graphics_pipeline_0(alice->device, root_dir, alice->render_pass_0, 0);
    alice->pipeline_hands_0b = create_graphics_pipeline_0_b(alice->device, root_dir, alice->render_pass_0, 0);

    alice->render_pass_1 = create_render_pass_1(alice->device, swapchain_details_res.ok.surface_format.format);
    alice->pipeline_hands_1 = create_graphics_pipeline_1(alice->device, root_dir, alice->render_pass_1, 0);

    // These samplers are global for a lot of my future textures
    alice->linear_sampler = margaret_create_sampler(alice->physical_device, alice->device, true);
    alice->nearest_sampler = margaret_create_sampler(alice->physical_device, alice->device, false);

    alice->command_pool = margaret_create_resettable_command_pool(alice->device, alice->queue_fam.for_graphics);
    alice->rendering_command_buf_0 = margaret_allocate_command_buffer(alice->device, alice->command_pool);
    alice->rendering_command_buf_1 = margaret_allocate_command_buffer(alice->device, alice->command_pool);
    alice->transfer_command_buf = margaret_allocate_command_buffer(alice->device, alice->command_pool);
    alice->device_local_mem_mv_command_buf = margaret_allocate_command_buffer(alice->device, alice->command_pool);

    // todo: write a descriptor set allocator (in Margaret) that manages dynamic descript or pool allocatrinonsasdasdasd
    alice->descriptor_pool = margaret_create_descriptor_set_pool(alice->device, 100, 100, 100);

    /* Here we search physical device memory types for the one with host-visible flag and the other with device-local flag */
    VkPhysicalDeviceMemoryProperties mem_properties;
    vkGetPhysicalDeviceMemoryProperties(alice->physical_device, &mem_properties);
    const VkMemoryPropertyFlags host_visible_coherent = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
    const VkMemoryPropertyFlags device_local = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    int mem_type_id_host_visible_coherent = -1;
    for (int i = 0; i < (int)mem_properties.memoryTypeCount; i++) {
        if ((mem_properties.memoryTypes[i].propertyFlags & host_visible_coherent) == host_visible_coherent) {
            mem_type_id_host_visible_coherent = i;
            break;
        }
    }
    if (mem_type_id_host_visible_coherent == -1) {
        abortf("Can't find host visible + host coherent memory\n");
    }
    int mem_type_id_device_local = -1;
    for (int i = 0; i < (int)mem_properties.memoryTypeCount; i++) {
        if ((mem_properties.memoryTypes[i].propertyFlags & device_local) == device_local) {
            mem_type_id_device_local = i;
            break;
        }
    }
    if (mem_type_id_device_local == -1) {
        abortf("Can't find device local memory\n");
    }

    alice->staging_buffers = MargaretBufAllocator_new(alice->device, alice->physical_device,
        VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
        mem_type_id_host_visible_coherent, 3, true, 16);

    // todo: inquire about the uniform buffer alignment and storage buffer alignment
    alice->dev_local_buffers = MargaretBufAllocator_new(alice->device, alice->physical_device,
        VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
        VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
        /* | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT */,
        mem_type_id_device_local, 6, false, 10000);

    alice->dev_local_images = MargaretImgAllocator_new(alice->device, alice->physical_device,
        mem_type_id_device_local, 16000000);

    alice->jane = Jane_alice_create(alice->device);
    /* Luckily, swapchain image allocation is not managed by me  */
    alice->swfb = MargaretSwapchainBundle_new(
        alice->device, alice->queue_fam, swapchain_details_res.ok,
        alice->surface, alice->render_pass_1, NULL);

    alice->generic_models = AliceAllMeshesGeneric_new();
    alice->shiny_models = AliceAllMeshesShiny_new();
    alice->pipeline0_ubo.staging = MargaretBufAllocator_alloc(&alice->staging_buffers, sizeof(Pipeline0UBO));
    alice->pipeline0_ubo.device_local = MargaretBufAllocator_alloc(&alice->dev_local_buffers, sizeof(Pipeline0UBO));
    Pipeline0UBO* ubo0 = (Pipeline0UBO*)MargaretSubbuf_get_mapped(&alice->pipeline0_ubo.staging);
    ubo0->point_light_count = ubo0->spotlight_count = 0;

    alice->cam_info = AliceCamVerticalControl_new();
    alice->rendering_config = AliceRenderConfig_new();

    alice->IT1_image = MargaretImgAllocator_alloc(&alice->dev_local_images,
        MAX_WIN_WIDTH, MAX_WIN_HEIGHT, alice->IT1_format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
    alice->zbuffer_image = MargaretImgAllocator_alloc(&alice->dev_local_images,
        MAX_WIN_WIDTH, MAX_WIN_HEIGHT, alice->zbuffer_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);

    MargaretEngineReference engine_reference = {
        .device = alice->device, .physical_device = alice->physical_device, .transfer_cmd_buffer = alice->transfer_command_buf,
        .dev_local_images = &alice->dev_local_images, .dev_local_buffers = &alice->dev_local_buffers,
        .staging_buffers = &alice->staging_buffers, .descriptor_pool = alice->descriptor_pool,
        .linear_sampler = alice->linear_sampler, .nearest_sampler = alice->nearest_sampler
    };

    FT_Error ft_init_err = FT_Init_FreeType(&alice->ft_library);
    if (ft_init_err)
        abortf("Can't init free type library\n");

    alice->lucy_cache = LucyGlyphCache_new(engine_reference);

    alice->lucy_renderer = LucyRenderer_new(engine_reference, &alice->lucy_cache, root_dir, alice->render_pass_1, 0);

    // Creating framebuffer for IT1, image views for IT1 and zbuffer
    // Creating descriptor set for pipeline 0b and for pipeline 1 (containing IT1 sampler)
    alice_create_mem_dependant_vk_obj(alice);

    alice->wl.prev_key_frame_time = margaret_clock_gettime_monotonic_raw();
    alice->wl.frame_count_since_key = 0;
    /* Alice initialization complete  */
    return alice;
}

void Alice_set_point_light_count(Alice* alice, int new_count){
    Pipeline0UBO* ubo = (Pipeline0UBO*)MargaretSubbuf_get_mapped(&alice->pipeline0_ubo.staging);
    assert(new_count <= pipeline_0_ubo_point_light_max_count);
    ubo->point_light_count = new_count;
}

void Alice_set_point_light(Alice* alice, int index, Pipeline0PointLight data){
    Pipeline0UBO* ubo = (Pipeline0UBO*)MargaretSubbuf_get_mapped(&alice->pipeline0_ubo.staging);
    assert(index < pipeline_0_ubo_point_light_max_count);
    ubo->point_light_arr[index] = data;
}


void Alice_clear_screen_text(Alice* alice){
    LucyRenderer_clear(&alice->lucy_renderer);
}

void Alice_add_screen_text(Alice* alice, RBTreeNodeLucyFaceFixedSize* ffs, vec4 color, SpanU8 text, ivec2 start_pos){
    LucyRenderer_add_text(&alice->lucy_renderer, ffs, color, 0, text, start_pos);
}

/* This function actually consumes alice handler. Alice must not be used after */
void Alice_mainloop(Alice* alice, const AliceCallbacks* callbacks) {
    alice->callbacks.on_wl_keyboard_key = callbacks->on_wl_keyboard_key;
    alice->callbacks.on_another_frame = callbacks->on_another_frame;
    printf("ENTERING WAYLAND MAINLOOP\n");
    while (wl_display_dispatch(alice->wl.wl_display) >= 0) {
        if (alice->wl.closed)
            break;
    }
    vkDeviceWaitIdle(alice->device);

    // The End
    // vkDestroyDescriptorPool(vk->device, vk->descriptor_pool, NULL);
    // vkDestroySampler(vk->device, vk->linear_sampler, NULL);
    // vkDestroySampler(vk->device, vk->nearest_sampler, NULL);
    //
    //
    // Scene_drop(vk->scene);
    //
    // vkDestroyCommandPool(vk->device, vk->command_pool, NULL);
    //
    //
    // MargaretSwapchainBundle_drop_with_device(vk->device, vk->swfb);
    // Jane_alice_destroy(vk->device, vk->jane);
    // destroy_graphics_pipeline_hands(vk->device, vk->pipeline_hands_1);
    // vkDestroyRenderPass(vk->device, vk->render_pass_1, NULL);
    // destroy_graphics_pipeline_hands(vk->device, vk->pipeline_hands_0b);
    // destroy_graphics_pipeline_hands(vk->device, vk->pipeline_hands_0a);
    // vkDestroyRenderPass(vk->device, vk->render_pass_0, NULL);
    // vkDestroyDevice(vk->device, NULL);
    // vkDestroySurfaceKHR(instance, vk->surface, NULL);
    // MargaretInstanceAndItsDebug_drop(vk->instance_and_debug);
    //
    // if (alice->wl.wl_callback)
    //     wl_callback_destroy(alice->wl.wl_callback);
    // xdg_toplevel_destroy(alice->wl.xdg_toplevel);
    // xdg_surface_destroy(alice->wl.xdg_surface);
    // xdg_wm_base_destroy(alice->wl.xdg_wm_base);
    // wl_surface_destroy(alice->wl.wl_surface);
    // wl_compositor_destroy(alice->wl.wl_compositor);
    // if (alice->wl.pointer)
    //     wl_pointer_destroy(alice->wl.pointer);
    // xkb_context_unref(alice->wl.xkb_context);
    // xkb_keymap_unref(alice->wl.xkb_keymap);
    // xkb_state_unref(alice->wl.xkb_state);
    // if (alice->wl.keyboard)
    //     wl_keyboard_destroy(alice->wl.keyboard);
    // if (alice->wl.wl_seat)
    //     wl_seat_destroy(alice->wl.wl_seat);
    // wl_registry_destroy(alice->wl.wl_registry);
    wl_display_disconnect(alice->wl.wl_display);
}

void allie_alice_set_sky_color(Alice* alice, float x, float y, float z, float w){
    alice->rendering_config.clear_color = (vec4){x, y, z, w};
}

Alice* allie_alice_new(){
    return Alice_new();
}

void allie_alice_mainloop(Alice* alice, const AliceCallbacks* callbacks){
    Alice_mainloop(alice, callbacks);
}

LucyFace* allie_alice_new_lucy_face(Alice* alice, const U8* path_data, U64 path_len){
    return LucyFace_new(alice->ft_library, &alice->lucy_cache, VecU8_from_span((SpanU8){path_data, path_len}));
}

RBTreeNodeLucyFaceFixedSize* allie_alice_lucy_face_of_size(LucyFace* face, U32 height){
    return LucyFace_of_size(face, height);
}

/* lol */
void allie_lucy_face_add_glyphs(RBTreeNodeLucyFaceFixedSize* face_fs, U32 start, U32 seg_len){
    VecLucyGlyphCachingRequest lucy_requests = VecLucyGlyphCachingRequest_new();
    VecU32Segment ranges_needed = VecU32Segment_new();
    VecU32Segment_append(&ranges_needed, (U32Segment){.start = start, .len = seg_len});
    VecLucyGlyphCachingRequest_append(&lucy_requests, (LucyGlyphCachingRequest){
        .sized_face = face_fs, .codepoint_ranges = ranges_needed,
    });
    LucyGlyphCache_add_glyphs(lucy_requests);
}

void allie_alice_clear_text(Alice* alice){
    LucyRenderer_clear(&alice->lucy_renderer);
}

void allie_alice_add_text(Alice* alice, RBTreeNodeLucyFaceFixedSize* ffs,
    float color_x, float color_y, float color_z, float color_w, const U8* text_data, U64 text_len,
    S32 start_pos_x, S32 start_pos_y){
    LucyRenderer_add_text(&alice->lucy_renderer, ffs, (vec4){color_x, color_y, color_z, color_w}, 0,
        (SpanU8){text_data, text_len}, (ivec2){start_pos_x, start_pos_y});
}

ListNodeAliceGenericMeshHand* allie_alice_add_generic_mesh_hand(
    Alice* alice, const U8* topology_path_data, U64 topology_path_len,
    const U8* diffuse_path_data, U64 diffuse_path_len,
    const U8* normal_path_data, U64 normal_path_len,
    const U8* specular_path_data, U64 specular_path_len){
    /* Todo: figure out how to pass vectors properly in haskell */
    return Alice_add_generic_mesh(alice, (AliceGenericMeshPath){
        VecU8_from_span((SpanU8){topology_path_data, topology_path_len}),
        VecU8_from_span((SpanU8){diffuse_path_data, diffuse_path_len}),
        VecU8_from_span((SpanU8){normal_path_data, normal_path_len}),
        VecU8_from_span((SpanU8){specular_path_data, specular_path_len}),
    });
}

ListNodeAliceShinyMeshHand* allie_alice_add_shiny_mesh_hand(Alice* alice, const U8* mesh_path_data, U64 mesh_path_len){
    return Alice_add_shiny_mesh(alice, VecU8_from_span((SpanU8){mesh_path_data, mesh_path_len}));
}

void allie_alice_generic_mesh_resize_instance_arr(Alice* alice, ListNodeAliceGenericMeshHand* mesh_hand, U64 new_count){
    AliceGenericMeshHand_resize_instance_arr(alice, &mesh_hand->el, new_count);
}

void allie_alice_shiny_mesh_resize_instance_arr(Alice* alice, ListNodeAliceShinyMeshHand* mesh_hand, U64 new_count){
    AliceShinyMeshHand_resize_instance_arr(alice, &mesh_hand->el, new_count);
}

void allie_alice_generic_mesh_set_inst(
    ListNodeAliceGenericMeshHand* mesh_hand, U64 index, const GenericMeshInstanceInc* inst){
    AliceGenericMeshHand_set_inst(&mesh_hand->el, index, *inst);
}

void allie_alice_shiny_mesh_set_inst(
    ListNodeAliceShinyMeshHand* mesh_hand, U64 index, const ShinyMeshInstanceInc* inst){
    AliceShinyMeshHand_set_inst(&mesh_hand->el, index, *inst);
}

void allie_alice_get_cam_back(Alice* alice, vec3* ret){
    *ret = alice->cam_info.cam.cam_basis.z;
}

void allie_alice_get_cam_right(Alice* alice, vec3* ret){
    *ret = alice->cam_info.cam.cam_basis.x;
}

void allie_alice_get_cam_up(Alice* alice, vec3* ret){
    *ret = alice->cam_info.cam.cam_basis.y;
}

void allie_alice_get_cam_pos(Alice* alice, vec3* ret){
    *ret = alice->cam_info.cam.pos;
}

void allie_alice_set_cam_pos(Alice* alice, float x, float y, float z){
    alice->cam_info.cam.pos = (vec3){x, y, z};
}

void allie_alice_set_fov(Alice* alice, float fov){
    alice->cam_info.cam.fov = MIN_float(MAX_float(fov, 0.001), M_PIf - 0.01);
}

void allie_alice_set_point_light_count(Alice* alice, int new_count){
    Alice_set_point_light_count(alice, new_count);
}

void allie_alice_set_point_light(Alice* alice, int index,
    float pos_x, float pos_y, float pos_z, float color_x, float color_y, float color_z){
    Alice_set_point_light(alice, index, (Pipeline0PointLight){
        .pos = {pos_x, pos_y, pos_z}, .color = {color_x, color_y, color_z}});
}

/* Works incorrectly */
bool alice_is_pressed(Alice* alice, U32 keysym){
    return keysym >= 0x80 ? false : alice->wl.first_0x80_keys[keysym];
}