package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen;

import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.GameCore;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.Attachment;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.Texture;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.TextureCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Pipeline;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineBuildInfo;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PushConstantsRange;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.*;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VertexBufferStructure;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.*;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.util.shaderc.Shaderc;
import org.lwjgl.vulkan.*;
import org.tinylog.Logger;

import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.util.Arrays;
import java.util.List;

import static org.lwjgl.vulkan.KHRSwapchain.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
import static org.lwjgl.vulkan.KHRSynchronization2.VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
import static org.lwjgl.vulkan.VK13.*;

public class SceneRender {//dynamic rendering

    private static final String DESCRIPTOR_ID_MAT = "SCN_DESC_ID_MAT";
    private static final String DESCRIPTOR_ID_PRJ = "SCN_DESC_ID_PRJ";
    private static final String DESCRIPTOR_ID_TEXT = "SCN_DESC_ID_TEXT";
    private static final String DESCRIPTOR_ID_VIEW = "SCN_DESC_ID_VIEW";
    private static final int PUSH_CONSTANTS_SIZE = VulkanUtils.MATRIX4X4_SIZE + VulkanUtils.INT_SIZE;
    private final VulkanBuffer BufferProjectionMatrix;
    private final DescriptorSetLayout descriptorLayoutFragStorage;
    private final DescriptorSetLayout descriptorLayoutTexture;
    private final DescriptorSetLayout descriptorLayoutVertexUniform;
    private final TextureSampler textureSampler;

    private static final int DEPTH_FORMAT = VK_FORMAT_D16_UNORM;
    private final VkClearValue ClearValueDepth;
    private final ByteBuffer PushConstBuffer;
    private Attachment[] AttDepth;
    private VkRenderingAttachmentInfo[] AttInfoDepth;
    private VkClearValue ClearValueColour;
    private VkRenderingAttachmentInfo.Buffer[] AttachmentInfoColour;
    private VkRenderingInfo[] RenderInfo;
    private float R = 0f;
    private float G = 0.5f;
    private float B = 0.7f;
    private boolean Rb = false;
    private boolean Gb = true;
    private boolean Bb = false;
    public int Frames = 0;
    private static String LastGPULog = "";

    private static final String FRAGMENT_SHADER_FILE_GLSL = "resources/shaders/scene_fragment.glsl";
    private static final String FRAGMENT_SHADER_FILE_SPV  = FRAGMENT_SHADER_FILE_GLSL + ".spv";
    private static final String VERTEX_SHADER_FILE_GLSL  = "resources/shaders/scene_vertex.glsl";
    private static final String VERTEX_SHADER_FILE_SPV  = VERTEX_SHADER_FILE_GLSL + ".spv";
    private final VulkanBuffer[] BufferViewMatrices;
    private final Pipeline VkPipeline;
    private Matrix4f ProjectionMatrix;

    public SceneRender(VulkanContext vulkanContext){
        ClearValueColour = VkClearValue.calloc().color(
                c -> c.float32(0, 0.0f).float32(1, 0.0f).float32(2, 0.0f).float32(3, 0.0f));
        //ClearValueDepth = VkClearValue.calloc().color(c -> c.float32(0, 1.0f));
        ClearValueDepth = VkClearValue.calloc().depthStencil(c -> c.depth(1.0f).stencil());
        AttDepth = createDepthAttachments(vulkanContext);
        AttInfoDepth = createDepthAttachmentsInfo(vulkanContext, AttDepth, ClearValueDepth);
        ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,R).float32(1,G).float32(2,B).float32(3,1.0f));
        AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour);
        RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour, AttInfoDepth);
        ShaderModule[] shaderModules = SceneRender.CreateShaderModules(vulkanContext);
        PushConstBuffer = (MemoryUtil.memAlloc(PUSH_CONSTANTS_SIZE));

        descriptorLayoutVertexUniform = new DescriptorSetLayout(vulkanContext, new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
                0,1,VK_SHADER_STAGE_VERTEX_BIT));
        BufferProjectionMatrix = VulkanUtils.CreateHostVisibleBuffer(vulkanContext, VulkanUtils.MATRIX4X4_SIZE,
                VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, DESCRIPTOR_ID_PRJ, descriptorLayoutVertexUniform);
        ProjectionMatrix = PrimaryRuntime.GetEngineInstance().scene().GetProjection().GetProjectionMatrix();
        VulkanUtils.CopyMatrixToBuffer(vulkanContext, BufferProjectionMatrix, PrimaryRuntime.GetEngineInstance().scene().GetProjection().GetProjectionMatrix(),0);
        descriptorLayoutFragStorage = new DescriptorSetLayout(vulkanContext, new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
                0,1,VK_SHADER_STAGE_FRAGMENT_BIT));
        var textureSamplerInfo = new TextureSamplerInfo(VK_SAMPLER_ADDRESS_MODE_REPEAT,
                VK_BORDER_COLOR_INT_OPAQUE_BLACK,1,true);
        textureSampler = new TextureSampler(vulkanContext,textureSamplerInfo);
        descriptorLayoutTexture = new DescriptorSetLayout(vulkanContext, new DescriptorSetLayout.LayoutInformation(
                VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, TextureCache.MAX_TEXTURES, VK_SHADER_STAGE_FRAGMENT_BIT
        ));
        DescriptorSetLayout[] layouts = new DescriptorSetLayout[]{
                descriptorLayoutVertexUniform,
                descriptorLayoutVertexUniform,
                descriptorLayoutFragStorage,
                descriptorLayoutTexture
        };

        BufferViewMatrices = VulkanUtils.CreateHostVisibleBuffers(vulkanContext, VulkanUtils.MATRIX4X4_SIZE, VulkanUtils.MAX_IN_FLIGHT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, DESCRIPTOR_ID_VIEW, descriptorLayoutVertexUniform);

        VkPipeline = CreatePipeline(vulkanContext, shaderModules, layouts);
        Arrays.asList(shaderModules).forEach(shaderModule -> shaderModule.CleanUp(vulkanContext));
    }

    private static Attachment[] createDepthAttachments(VulkanContext VkCtx){
        SwapChain swapChain = VkCtx.GetSwapChain();
        int ImageCount = swapChain.GetImageCount();
        VkExtent2D swapChainExtent = swapChain.GetSwapChainExtent();
        Attachment[] DepthAttachments = new Attachment[ImageCount];
        for(int i = 0; i < ImageCount; i++){
            DepthAttachments[i] = new Attachment(VkCtx, swapChainExtent.width(), swapChainExtent.height(),
                    DEPTH_FORMAT,VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
        }
        return DepthAttachments;
    }

    private static VkRenderingAttachmentInfo[] createDepthAttachmentsInfo(VulkanContext VkCtx, Attachment[] DepthAttachments, VkClearValue clearValue){
        SwapChain swapChain = VkCtx.GetSwapChain();
        int ImageCount = swapChain.GetImageCount();
        var Result = new VkRenderingAttachmentInfo[ImageCount];
        for(int i = 0; i < ImageCount; i++){
            var Attachments = VkRenderingAttachmentInfo.calloc()
                    .sType$Default()
                    .imageView(DepthAttachments[i].GetVkImageView().GetVulkanImageView())
                    .imageLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
                    .loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR)
                    .storeOp(VK_ATTACHMENT_STORE_OP_DONT_CARE)
                    .clearValue(clearValue);
            Result[i] = Attachments;
        }
        return Result;
    }

    private static ShaderModule[] CreateShaderModules(VulkanContext VkCtx){
        if(EngineConfig.getInstance().RecompileShaders()){
            ShaderCompiler.CompileGLSLShaderOnChange(VERTEX_SHADER_FILE_GLSL, Shaderc.shaderc_glsl_vertex_shader);
            ShaderCompiler.CompileGLSLShaderOnChange(FRAGMENT_SHADER_FILE_GLSL, Shaderc.shaderc_glsl_fragment_shader);
        }
        return new ShaderModule[]{
                new ShaderModule(VkCtx, VK_SHADER_STAGE_VERTEX_BIT, VERTEX_SHADER_FILE_SPV),
                new ShaderModule(VkCtx, VK_SHADER_STAGE_FRAGMENT_BIT, FRAGMENT_SHADER_FILE_SPV)
        };
    }

    private static Pipeline CreatePipeline(VulkanContext VkCtx, ShaderModule[] ShaderModules, DescriptorSetLayout[] DescriptorSetLayouts){
        var vertexBufferStructure = new VertexBufferStructure();
        var BuildInfo = new PipelineBuildInfo(ShaderModules,vertexBufferStructure.getVertexInput(),
                VkCtx.GetSurface().GetSurfaceFormat().ImageFormat())
                .SetDepthFormat(DEPTH_FORMAT)
                .SetPushConstantRanges(
                         new PushConstantsRange[]{
                                new PushConstantsRange(VK_SHADER_STAGE_VERTEX_BIT,0,VulkanUtils.MATRIX4X4_SIZE),
                                new PushConstantsRange(VK_SHADER_STAGE_FRAGMENT_BIT,VulkanUtils.MATRIX4X4_SIZE,VulkanUtils.INT_SIZE)
                        })
                .SetDescriptorSetLayouts(DescriptorSetLayouts)
                .BlendingIsUsed(true);
        var pipeline = new Pipeline(VkCtx, BuildInfo);
        vertexBufferStructure.cleanup();
        return pipeline;
    }

    public void UpdateColours(VulkanContext vulkanContext){
        if (Gb){
            if (G < 1.0f && Math.random() <0.5) G+=0.0005f;
            else if (G >= 1.0f) Gb = false;
        } else{
            if (G >0.0f&& Math.random() <0.5) G-=0.0005f;
            else if (G <= 0.0f)  Gb = true;
        }
        if (Rb){
            if (R < 1.0f&& Math.random() <0.3) R+=0.0005f;
            else if (R >= 1.0f)  Rb = false;
        } else{
            if (R >0.0f&& Math.random() <0.3) R-=0.0005f;
            else if (R <= 0.0f)  Rb = true;
        }
        if (Bb){
            if (B < 1.0f&& Math.random() <0.7) B+=0.0005f;
            else if (B >= 1.0f)  Bb = false;
        } else{
            if (B >0.0f&& Math.random() <0.7) B-=0.0005f;
            else if (B <= 0.0f)  Bb = true;
        }
        //ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,R).float32(1,G).float32(2,B).float32(3,1.0f));
        //AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour);
        //RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour, AttInfoDepth);
    }

    public void Render(EngineInstance engineInstance,VulkanContext vulkanContext, CommandBuffer commandBuffer, ModelsCache modelsCache,MaterialsCache materialsCache, int ImageIndex, int CurrentFrame){
        UpdateColours(vulkanContext);
        try(var MemStack = MemoryStack.stackPush()){
            SwapChain swapChain = vulkanContext.GetSwapChain();
            long SwapChainImage = swapChain.GetVulkanImageView(ImageIndex).GetVulkanImage();
            VkCommandBuffer CommandHandle = commandBuffer.GetVulkanCommandBuffer();
            VulkanUtils.ImageBarrier(MemStack, CommandHandle, SwapChainImage,
                    VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                    VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
                    VK_ACCESS_2_NONE, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
            VulkanUtils.ImageBarrier(MemStack, CommandHandle, AttDepth[ImageIndex].GetVkImage().getVulkanImage(),
                    VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
                    VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
                    VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
                    VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
                    VK_IMAGE_ASPECT_DEPTH_BIT);

            vkCmdBeginRendering(CommandHandle, RenderInfo[ImageIndex]);

            vkCmdBindPipeline(CommandHandle, VK_PIPELINE_BIND_POINT_GRAPHICS, VkPipeline.GetVulkanPipeline());

            VkExtent2D swapChainExtent = swapChain.GetSwapChainExtent();
            int width = swapChainExtent.width();
            int height = swapChainExtent.height();
            var Viewport = VkViewport.calloc(1,MemStack)
                            .x(0)
                            .y(height)
                            .height(-height)
                            .width(width)
                            .minDepth(0.0F)
                            .maxDepth(1.0F);
            vkCmdSetViewport(CommandHandle, 0, Viewport);
            var Scissor = VkRect2D.calloc(1,MemStack)
                            .extent(it -> it.width(width).height(height))
                            .offset(it->it.x(0).y(0));
            vkCmdSetScissor(CommandHandle, 0, Scissor);

            VulkanUtils.CopyMatrixToBuffer(vulkanContext, BufferViewMatrices[CurrentFrame],engineInstance.scene().GetCamera().GetViewMatrix(), 0); // here
            DescriptorAllocator descriptorAllocator = vulkanContext.GetDescriptorAllocator();
            LongBuffer DescriptorSets = MemStack.mallocLong(4)
                    .put(0,descriptorAllocator.GetDescriptorSet(DESCRIPTOR_ID_PRJ).GetVkDescriptorSet())
                    .put(1,descriptorAllocator.GetDescriptorSet(DESCRIPTOR_ID_VIEW).GetVkDescriptorSet())
                    .put(2,descriptorAllocator.GetDescriptorSet(DESCRIPTOR_ID_MAT).GetVkDescriptorSet())
                    .put(3,descriptorAllocator.GetDescriptorSet(DESCRIPTOR_ID_TEXT).GetVkDescriptorSet());
            vkCmdBindDescriptorSets(CommandHandle, VK_PIPELINE_BIND_POINT_GRAPHICS, VkPipeline.GetVulkanPipelineLayout(),0,DescriptorSets,null);

            RenderActors(engineInstance, CommandHandle, modelsCache, materialsCache, false);
            RenderActors(engineInstance, CommandHandle, modelsCache, materialsCache, true);

            vkCmdEndRendering(CommandHandle);

            VulkanUtils.ImageBarrier(MemStack, CommandHandle, SwapChainImage,
                    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                    VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT,
                    VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_2_NONE,
                    VK_IMAGE_ASPECT_COLOR_BIT);
        }
    }

    private void RenderActors(EngineInstance instance, VkCommandBuffer CommandHandle, ModelsCache modelsCache, MaterialsCache materialsCache, boolean Transparent){
        try(var MemStack = MemoryStack.stackPush()){
            LongBuffer offsets = MemStack.mallocLong(1).put(0,0L);
            LongBuffer vertexBuffer = MemStack.mallocLong(1);
            Scene scene = instance.scene();
            List<Actor> Actors = scene.GetActors();
            int ActorCount = Actors.size();

            for(int i = 0; i < ActorCount; i++){
                var Actor = Actors.get(i);
                VulkanModel model = modelsCache.GetModel(Actor.GetModelID());
                List<VulkanMesh> VkMeshList = model.GetVkMeshList();
                int MeshCount = VkMeshList.size();
                for(int j = 0; j < MeshCount; j++){
                    var VkMesh = VkMeshList.get(j);
                    String MaterialID = VkMesh.MaterialID();
                    int MaterialIndex = materialsCache.GetPosition(MaterialID);
                    VulkanMaterial vulkanMaterial = materialsCache.GetMaterial(MaterialID);
                    if(vulkanMaterial == null){
                        Logger.warn("Mesh [{}] in model [{}] does not have material",j,model.GetID());
                        continue;
                    }
                    if(vulkanMaterial.HasTransparency() == Transparent) {
                        SetPushConstants(CommandHandle, Actor.GetModelMatrix(), MaterialIndex);
                        vertexBuffer.put(0, VkMesh.VerticesBuffer().GetBuffer());
                        vkCmdBindVertexBuffers(CommandHandle, 0, vertexBuffer, offsets);
                        vkCmdBindIndexBuffer(CommandHandle, VkMesh.IndicesBuffer().GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
                        vkCmdDrawIndexed(CommandHandle, VkMesh.IndicesCount(), 1, 0, 0, 0);
                    }
                }
            }
        }
    }

    private void SetPushConstants(VkCommandBuffer CmdHandle, Matrix4f ModelMatrix, int MaterialIndex){
        ModelMatrix.get(0,PushConstBuffer);
        PushConstBuffer.putInt(VulkanUtils.MATRIX4X4_SIZE, MaterialIndex);
        vkCmdPushConstants(CmdHandle, VkPipeline.GetVulkanPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, PushConstBuffer.slice(0,VulkanUtils.MATRIX4X4_SIZE));
        vkCmdPushConstants(CmdHandle, VkPipeline.GetVulkanPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT, VulkanUtils.MATRIX4X4_SIZE, PushConstBuffer.slice(VulkanUtils.MATRIX4X4_SIZE, VulkanUtils.INT_SIZE));
    }

    public void Resize(EngineInstance instance, VulkanContext VkCtx){
        Arrays.asList(RenderInfo).forEach(VkRenderingInfo::free);
        Arrays.asList(AttInfoDepth).forEach(VkRenderingAttachmentInfo::free);
        Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free);
        Arrays.asList(AttDepth).forEach(Attachment->Attachment.CleanUp(VkCtx));
        AttDepth = createDepthAttachments(VkCtx);
        AttachmentInfoColour = CreateColourAttachmentsInfo(VkCtx, ClearValueColour);
        AttInfoDepth = createDepthAttachmentsInfo(VkCtx, AttDepth, ClearValueDepth);
        RenderInfo = CreateRenderInfo(VkCtx, AttachmentInfoColour, AttInfoDepth);
        VulkanUtils.CopyMatrixToBuffer(VkCtx, BufferProjectionMatrix, instance.scene().GetProjection().GetProjectionMatrix(),0);
    }

    public void LoadMaterials(VulkanContext VkCtx, MaterialsCache materialsCache, TextureCache textureCache){
        DescriptorAllocator descriptorAllocator = VkCtx.GetDescriptorAllocator();
        Device device = VkCtx.GetDevice();
        DescriptorSet descriptorSet = descriptorAllocator.AddDescriptorSet(device,DESCRIPTOR_ID_MAT,descriptorLayoutFragStorage);
        DescriptorSetLayout.LayoutInformation LayoutInfo = descriptorLayoutFragStorage.GetLayoutInfo();
        var Buffer = materialsCache.GetMaterialsBuffer();
        descriptorSet.SetBuffer(device,Buffer,Buffer.GetRequestedSize(),LayoutInfo.Binding(),LayoutInfo.DescriptorType());
        List<ImageView> imageViews = textureCache.GetTextureList().stream().map(Texture::GetImageView).toList();
        descriptorSet = VkCtx.GetDescriptorAllocator().AddDescriptorSet(device,DESCRIPTOR_ID_TEXT,descriptorLayoutTexture);
        descriptorSet.SetImageArray(device,imageViews,textureSampler,0);
    }

    private static VkRenderingInfo[] CreateRenderInfo(VulkanContext vulkanContext, VkRenderingAttachmentInfo.Buffer[] ColourAttachments, VkRenderingAttachmentInfo[] DepthAttachments){
        SwapChain swapChain = vulkanContext.GetSwapChain();
        int ImageCount = swapChain.GetImageCount();
        var Result = new VkRenderingInfo[ImageCount];

        try(var MemStack = MemoryStack.stackPush()){
            VkExtent2D Extent = swapChain.GetSwapChainExtent();
            var RenderArea = VkRect2D.calloc(MemStack).extent(Extent);

            for(int i = 0; i < ImageCount; i++){
                var RenderInfo = VkRenderingInfo.calloc()
                        .sType$Default()
                        .renderArea(RenderArea)
                        .layerCount(1)
                        .pColorAttachments(ColourAttachments[i])
                        .pDepthAttachment(DepthAttachments[i]);
                Result[i] = RenderInfo;
            }
        }
        return Result;
    }
    private static VkRenderingAttachmentInfo.Buffer[] CreateColourAttachmentsInfo(VulkanContext vulkanContext, VkClearValue CLearValue){
        SwapChain swapChain = vulkanContext.GetSwapChain();
        int ImageCount = swapChain.GetImageCount();
        var result = new VkRenderingAttachmentInfo.Buffer[ImageCount];
        for(int i = 0; i < ImageCount; i++){
            var Attachments = VkRenderingAttachmentInfo.calloc(1)
                    .sType$Default()
                    .imageView(swapChain.GetVulkanImageView(i).GetVulkanImageView())
                    .imageLayout(VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR)
                    .loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR)
                    .storeOp(VK_ATTACHMENT_STORE_OP_STORE)
                    .clearValue(CLearValue);
            result[i] = Attachments;
        }
        return result;
    }
    public void cleanup(VulkanContext VkCtx){
        VkPipeline.CleanUp(VkCtx);
        Arrays.asList(RenderInfo).forEach(VkRenderingInfo::free);
        Arrays.asList(BufferViewMatrices).forEach(buffer -> buffer.cleanup(VkCtx));
        Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free);
        Arrays.asList(AttInfoDepth).forEach(VkRenderingAttachmentInfo::free);
        Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free);
        Arrays.asList(AttDepth).forEach(Attachment->Attachment.CleanUp(VkCtx));
        MemoryUtil.memFree(PushConstBuffer);
        ClearValueDepth.free();
        ClearValueColour.free();
        BufferProjectionMatrix.cleanup(VkCtx);
        descriptorLayoutFragStorage.CleanUp(VkCtx);
        descriptorLayoutTexture.CleanUp(VkCtx);
        descriptorLayoutVertexUniform.CleanUp(VkCtx);
        textureSampler.CleanUp(VkCtx);
    }
}
