package net.vulkanmod.render.engine;

import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.GpuFence;
import com.mojang.blaze3d.opengl.GlConst;
import com.mojang.blaze3d.opengl.GlStateManager;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.DepthTestFunction;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.textures.GpuTexture;
import com.mojang.blaze3d.textures.GpuTextureView;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat.class_5595;
import com.mojang.logging.LogUtils;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import net.minecraft.class_1011;
import net.minecraft.class_10866;
import net.minecraft.class_10868;
import net.minecraft.class_284;
import net.minecraft.class_310;
import net.minecraft.class_5944;
import net.minecraft.class_9848;
import net.vulkanmod.gl.VkGlFramebuffer;
import net.vulkanmod.gl.VkGlTexture;
import net.vulkanmod.interfaces.shader.ExtendedRenderPipeline;
import net.vulkanmod.vulkan.Renderer;
import net.vulkanmod.vulkan.Synchronization;
import net.vulkanmod.vulkan.VRenderSystem;
import net.vulkanmod.vulkan.Vulkan;
import net.vulkanmod.vulkan.device.DeviceManager;
import net.vulkanmod.vulkan.framebuffer.Framebuffer;
import net.vulkanmod.vulkan.memory.buffer.StagingBuffer;
import net.vulkanmod.vulkan.memory.buffer.index.AutoIndexBuffer;
import net.vulkanmod.vulkan.queue.CommandPool;
import net.vulkanmod.vulkan.queue.GraphicsQueue;
import net.vulkanmod.vulkan.shader.GraphicsPipeline;
import net.vulkanmod.vulkan.shader.Pipeline;
import net.vulkanmod.vulkan.shader.descriptor.ImageDescriptor;
import net.vulkanmod.vulkan.shader.descriptor.UBO;
import net.vulkanmod.vulkan.texture.ImageUtil;
import net.vulkanmod.vulkan.texture.VTextureSelector;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.vulkan.VK10;
import org.lwjgl.vulkan.VK11;
import org.lwjgl.vulkan.VkBufferCopy;
import org.lwjgl.vulkan.VkCommandBuffer;
import org.slf4j.Logger;

public class VkCommandEncoder implements CommandEncoder {
   private static final Logger LOGGER = LogUtils.getLogger();
   private final VkGpuDevice device;
   private @Nullable RenderPipeline lastPipeline;
   private boolean inRenderPass;
   private @Nullable EGlProgram lastProgram;
   private int framebufferId = VkGlFramebuffer.genFramebufferId();

   protected VkCommandEncoder(VkGpuDevice glDevice) {
      this.device = glDevice;
   }

   public RenderPass createRenderPass(Supplier<String> supplier, GpuTextureView gpuTexture, OptionalInt optionalInt) {
      return this.createRenderPass(supplier, gpuTexture, optionalInt, (GpuTextureView)null, OptionalDouble.empty());
   }

   public RenderPass createRenderPass(Supplier<String> supplier, GpuTextureView colorTexture, OptionalInt optionalInt, @Nullable GpuTextureView depthTexture, OptionalDouble optionalDouble) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before creating a new one!");
      } else {
         if (optionalDouble.isPresent() && depthTexture == null) {
            LOGGER.warn("Depth clear value was provided but no depth texture is being used");
         }

         if (class_310.method_1551().method_1522().method_30277() == colorTexture.texture()) {
            Renderer.getInstance().getMainPass().rebindMainTarget();
            int j = 0;
            if (optionalInt.isPresent()) {
               int k = optionalInt.getAsInt();
               GL11.glClearColor(class_9848.method_65101(k), class_9848.method_65102(k), class_9848.method_65103(k), class_9848.method_65100(k));
               j |= 16384;
            }

            if (depthTexture != null && optionalDouble.isPresent()) {
               GL11.glClearDepth(optionalDouble.getAsDouble());
               j |= 256;
            }

            if (j != 0) {
               GlStateManager._disableScissorTest();
               GlStateManager._depthMask(true);
               GlStateManager._colorMask(true, true, true, true);
               GlStateManager._clear(j);
            }

            return new VkRenderPass(this, depthTexture != null);
         } else if (colorTexture.isClosed()) {
            throw new IllegalStateException("Color texture is closed");
         } else if (depthTexture != null && depthTexture.isClosed()) {
            throw new IllegalStateException("Depth texture is closed");
         } else {
            this.inRenderPass = true;
            GpuTexture depthTexture1 = depthTexture != null ? depthTexture.texture() : null;
            VkFbo fbo = ((VkGpuTexture)colorTexture.texture()).getFbo(depthTexture1);
            fbo.bind();
            int j = 0;
            if (optionalInt.isPresent()) {
               int k = optionalInt.getAsInt();
               GL11.glClearColor(class_9848.method_65101(k), class_9848.method_65102(k), class_9848.method_65103(k), class_9848.method_65100(k));
               j |= 16384;
            }

            if (depthTexture != null && optionalDouble.isPresent()) {
               GL11.glClearDepth(optionalDouble.getAsDouble());
               j |= 256;
            }

            if (j != 0) {
               GlStateManager._disableScissorTest();
               GlStateManager._depthMask(true);
               GlStateManager._colorMask(true, true, true, true);
               GlStateManager._clear(j);
            }

            GlStateManager._viewport(0, 0, colorTexture.getWidth(0), colorTexture.getHeight(0));
            this.lastPipeline = null;
            return new VkRenderPass(this, depthTexture != null);
         }
      }
   }

   public void clearColorTexture(GpuTexture colorAttachment, int clearColor) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before creating a new one!");
      } else {
         if (Renderer.isRecording()) {
            if (class_310.method_1551().method_1522().method_30277() == colorAttachment) {
               Renderer.getInstance().getMainPass().rebindMainTarget();
               VRenderSystem.setClearColor(class_9848.method_65101(clearColor), class_9848.method_65102(clearColor), class_9848.method_65103(clearColor), class_9848.method_65100(clearColor));
               Renderer.clearAttachments(16384);
            } else {
               VkGpuTexture vkGpuTexture = (VkGpuTexture)colorAttachment;
               VkGlFramebuffer.bindFramebuffer(36160, this.framebufferId);
               VkGlFramebuffer.framebufferTexture2D(36160, 36064, 3553, vkGpuTexture.method_68427(), 0);
               VkGlFramebuffer.beginRendering(VkGlFramebuffer.getFramebuffer(this.framebufferId));
               VRenderSystem.setClearColor(class_9848.method_65101(clearColor), class_9848.method_65102(clearColor), class_9848.method_65103(clearColor), class_9848.method_65100(clearColor));
               Renderer.clearAttachments(16384);
               Renderer.getInstance().endRenderPass();
               VkFbo fbo = ((VkGpuTexture)colorAttachment).getFbo((GpuTexture)null);
               ((VkGpuTexture)colorAttachment).setClearColor(clearColor);
               Framebuffer boundFramebuffer = Renderer.getInstance().getBoundFramebuffer();
               if (boundFramebuffer != null && boundFramebuffer.getColorAttachment() == ((VkGpuTexture)colorAttachment).getVulkanImage()) {
                  fbo.clearAttachments();
               }
            }
         } else {
            GraphicsQueue graphicsQueue = DeviceManager.getGraphicsQueue();
            CommandPool.CommandBuffer commandBuffer = graphicsQueue.getCommandBuffer();
            VkGpuTexture vkGpuTexture = (VkGpuTexture)colorAttachment;
            VkGlFramebuffer glFramebuffer = VkGlFramebuffer.getFramebuffer(this.framebufferId);
            glFramebuffer.setAttachmentTexture(36064, vkGpuTexture.method_68427());
            glFramebuffer.create();
            Framebuffer framebuffer = glFramebuffer.getFramebuffer();
            net.vulkanmod.vulkan.framebuffer.RenderPass renderPass = glFramebuffer.getRenderPass();
            MemoryStack stack = MemoryStack.stackPush();

            try {
               framebuffer.beginRenderPass(commandBuffer.handle, renderPass, stack);
            } catch (Throwable var13) {
               if (stack != null) {
                  try {
                     stack.close();
                  } catch (Throwable var12) {
                     var13.addSuppressed(var12);
                  }
               }

               throw var13;
            }

            if (stack != null) {
               stack.close();
            }

            VRenderSystem.setClearColor(class_9848.method_65101(clearColor), class_9848.method_65102(clearColor), class_9848.method_65103(clearColor), class_9848.method_65100(clearColor));
            Renderer.clearAttachments(commandBuffer.handle, 16384, 0, 0, framebuffer.getWidth(), framebuffer.getHeight());
            renderPass.endRenderPass(commandBuffer.handle);
            long fence = graphicsQueue.submitCommands(commandBuffer);
            Synchronization.waitFence(fence);
         }

      }
   }

   public void clearColorAndDepthTextures(GpuTexture colorAttachment, int clearColor, GpuTexture depthAttachment, double clearDepth) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before creating a new one!");
      } else {
         if (class_310.method_1551().method_1522().method_30277() == colorAttachment) {
            Renderer.getInstance().getMainPass().rebindMainTarget();
            VRenderSystem.clearDepth(clearDepth);
            VRenderSystem.setClearColor(class_9848.method_65101(clearColor), class_9848.method_65102(clearColor), class_9848.method_65103(clearColor), class_9848.method_65100(clearColor));
            Renderer.clearAttachments(16640);
         } else {
            VkFbo fbo = ((VkGpuTexture)colorAttachment).getFbo(depthAttachment);
            ((VkGpuTexture)colorAttachment).setClearColor(clearColor);
            ((VkGpuTexture)depthAttachment).setDepthClearValue((float)clearDepth);
            Framebuffer boundFramebuffer = Renderer.getInstance().getBoundFramebuffer();
            if (boundFramebuffer != null && boundFramebuffer.getColorAttachment() == ((VkGpuTexture)colorAttachment).getVulkanImage() && boundFramebuffer.getDepthAttachment() == ((VkGpuTexture)depthAttachment).getVulkanImage()) {
               fbo.clearAttachments();
            }
         }

      }
   }

   public void clearColorAndDepthTextures(GpuTexture colorAttachment, int clearColor, GpuTexture depthAttachment, double clearDepth, int x0, int y0, int width, int height) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before creating a new one!");
      } else {
         VRenderSystem.clearDepth(clearDepth);
         VRenderSystem.setClearColor(class_9848.method_65101(clearColor), class_9848.method_65102(clearColor), class_9848.method_65103(clearColor), class_9848.method_65100(clearColor));
         int framebufferHeight = colorAttachment.getHeight(0);
         y0 = framebufferHeight - height - y0;
         Framebuffer boundFramebuffer = Renderer.getInstance().getBoundFramebuffer();
         if (boundFramebuffer != null && boundFramebuffer.getColorAttachment() == ((VkGpuTexture)colorAttachment).getVulkanImage() && boundFramebuffer.getDepthAttachment() == ((VkGpuTexture)depthAttachment).getVulkanImage()) {
            Renderer.clearAttachments(16640, x0, y0, width, height);
         }

      }
   }

   public void clearDepthTexture(GpuTexture depthAttachment, double clearDepth) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before creating a new one!");
      } else {
         Framebuffer boundFramebuffer = Renderer.getInstance().getBoundFramebuffer();
         if (boundFramebuffer != null && boundFramebuffer.getDepthAttachment() == ((VkGpuTexture)depthAttachment).getVulkanImage()) {
            VRenderSystem.clearDepth(clearDepth);
            Renderer.clearAttachments(256);
         } else {
            ((VkGpuTexture)depthAttachment).setDepthClearValue((float)clearDepth);
         }

      }
   }

   public void writeToBuffer(GpuBufferSlice gpuBufferSlice, ByteBuffer byteBuffer) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else {
         VkGpuBuffer vkGpuBuffer = (VkGpuBuffer)gpuBufferSlice.buffer();
         if (vkGpuBuffer.closed) {
            throw new IllegalStateException("Buffer already closed");
         } else {
            int size = byteBuffer.remaining();
            if (size + gpuBufferSlice.offset() > vkGpuBuffer.size()) {
               throw new IllegalArgumentException("Cannot write more data than this buffer can hold (attempting to write " + size + " bytes at offset " + gpuBufferSlice.offset() + " to " + gpuBufferSlice.length() + " slice size)");
            } else {
               int dstOffset = gpuBufferSlice.offset();
               CommandPool.CommandBuffer commandBuffer = Renderer.getInstance().getTransferCb();
               StagingBuffer stagingBuffer = Vulkan.getStagingBuffer();
               stagingBuffer.copyBuffer(size, byteBuffer);
               long srcOffset = stagingBuffer.getOffset();
               MemoryStack stack = MemoryStack.stackPush();

               try {
                  if (!commandBuffer.isRecording()) {
                     commandBuffer.begin(stack);
                  }

                  VkBufferCopy.Buffer copyRegion = VkBufferCopy.calloc(1, stack);
                  copyRegion.size((long)size);
                  copyRegion.srcOffset(srcOffset);
                  copyRegion.dstOffset((long)dstOffset);
                  VK10.vkCmdCopyBuffer(commandBuffer.handle, stagingBuffer.getId(), vkGpuBuffer.buffer.getId(), copyRegion);
               } catch (Throwable var14) {
                  if (stack != null) {
                     try {
                        stack.close();
                     } catch (Throwable var13) {
                        var14.addSuppressed(var13);
                     }
                  }

                  throw var14;
               }

               if (stack != null) {
                  stack.close();
               }

            }
         }
      }
   }

   public GpuBuffer.MappedView mapBuffer(GpuBuffer gpuBuffer, boolean readable, boolean writable) {
      return this.mapBuffer(gpuBuffer.slice(), readable, writable);
   }

   public GpuBuffer.MappedView mapBuffer(GpuBufferSlice gpuBufferSlice, boolean readable, boolean writable) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else {
         VkGpuBuffer gpuBuffer = (VkGpuBuffer)gpuBufferSlice.buffer();
         if (gpuBuffer.closed) {
            throw new IllegalStateException("Buffer already closed");
         } else if (!readable && !writable) {
            throw new IllegalArgumentException("At least read or write must be true");
         } else if (readable && (gpuBuffer.usage() & 1) == 0) {
            throw new IllegalStateException("Buffer is not readable");
         } else if (writable && (gpuBuffer.usage() & 2) == 0) {
            throw new IllegalStateException("Buffer is not writable");
         } else if (gpuBufferSlice.offset() + gpuBufferSlice.length() > gpuBuffer.size()) {
            int var10002 = gpuBufferSlice.length();
            throw new IllegalArgumentException("Cannot map more data than this buffer can hold (attempting to map " + var10002 + " bytes at offset " + gpuBufferSlice.offset() + " from " + gpuBuffer.size() + " size buffer)");
         } else {
            int i = 0;
            if (readable) {
               i |= 1;
            }

            if (writable) {
               i |= 34;
            }

            ByteBuffer byteBuffer = MemoryUtil.memByteBuffer(gpuBuffer.getBuffer().getDataPtr() + (long)gpuBufferSlice.offset(), gpuBufferSlice.length());
            return new VkGpuBuffer.MappedView(0, byteBuffer);
         }
      }
   }

   public void copyToBuffer(GpuBufferSlice gpuBufferSlice, GpuBufferSlice gpuBufferSlice2) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else {
         VkGpuBuffer vkGpuBuffer = (VkGpuBuffer)gpuBufferSlice.buffer();
         if (vkGpuBuffer.closed) {
            throw new IllegalStateException("Source buffer already closed");
         } else if ((vkGpuBuffer.usage() & 8) == 0) {
            throw new IllegalStateException("Source buffer needs USAGE_COPY_DST to be a destination for a copy");
         } else {
            VkGpuBuffer vkGpuBuffer2 = (VkGpuBuffer)gpuBufferSlice2.buffer();
            if (vkGpuBuffer2.closed) {
               throw new IllegalStateException("Target buffer already closed");
            } else if ((vkGpuBuffer2.usage() & 8) == 0) {
               throw new IllegalStateException("Target buffer needs USAGE_COPY_DST to be a destination for a copy");
            } else if (gpuBufferSlice.length() != gpuBufferSlice2.length()) {
               int var6 = gpuBufferSlice.length();
               throw new IllegalArgumentException("Cannot copy from slice of size " + var6 + " to slice of size " + gpuBufferSlice2.length() + ", they must be equal");
            } else if (gpuBufferSlice.offset() + gpuBufferSlice.length() > vkGpuBuffer.size()) {
               int var5 = gpuBufferSlice.length();
               throw new IllegalArgumentException("Cannot copy more data than the source buffer holds (attempting to copy " + var5 + " bytes at offset " + gpuBufferSlice.offset() + " from " + vkGpuBuffer.size() + " size buffer)");
            } else if (gpuBufferSlice2.offset() + gpuBufferSlice2.length() > vkGpuBuffer2.size()) {
               int var10002 = gpuBufferSlice2.length();
               throw new IllegalArgumentException("Cannot copy more data than the target buffer can hold (attempting to copy " + var10002 + " bytes at offset " + gpuBufferSlice2.offset() + " to " + vkGpuBuffer2.size() + " size buffer)");
            } else {
               throw new UnsupportedOperationException();
            }
         }
      }
   }

   public void writeToTexture(GpuTexture gpuTexture, class_1011 nativeImage) {
      int i = gpuTexture.getWidth(0);
      int j = gpuTexture.getHeight(0);
      if (nativeImage.method_4307() == i && nativeImage.method_4323() == j) {
         if (gpuTexture.isClosed()) {
            throw new IllegalStateException("Destination texture is closed");
         } else {
            this.writeToTexture(gpuTexture, nativeImage, 0, 0, 0, 0, i, j, 0, 0);
         }
      } else {
         throw new IllegalArgumentException("Cannot replace texture of size " + i + "x" + j + " with image of size " + nativeImage.method_4307() + "x" + nativeImage.method_4323());
      }
   }

   public void writeToTexture(GpuTexture gpuTexture, class_1011 nativeImage, int level, int arrayLayer, int xOffset, int yOffset, int width, int height, int unpackSkipPixels, int unpackSkipRows) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else if (level >= 0 && level < gpuTexture.getMipLevels()) {
         if (unpackSkipPixels + width <= nativeImage.method_4307() && unpackSkipRows + height <= nativeImage.method_4323()) {
            if (xOffset + width <= gpuTexture.getWidth(level) && yOffset + height <= gpuTexture.getHeight(level)) {
               if (gpuTexture.isClosed()) {
                  throw new IllegalStateException("Destination texture is closed");
               } else {
                  VTextureSelector.setActiveTexture(0);
                  VkGlTexture glTexture = VkGlTexture.getTexture(((class_10868)gpuTexture).method_68427());
                  VTextureSelector.bindTexture(glTexture.getVulkanImage());
                  VTextureSelector.uploadSubTexture(level, arrayLayer, width, height, xOffset, yOffset, unpackSkipRows, unpackSkipPixels, nativeImage.method_4307(), nativeImage.method_67769());
               }
            } else {
               throw new IllegalArgumentException("Dest texture (" + width + "x" + height + ") is not large enough to write a rectangle of " + width + "x" + height + " at " + xOffset + "x" + yOffset + " (at mip level " + level + ")");
            }
         } else {
            int var10002 = nativeImage.method_4307();
            throw new IllegalArgumentException("Copy source (" + var10002 + "x" + nativeImage.method_4323() + ") is not large enough to read a rectangle of " + width + "x" + height + " from " + unpackSkipPixels + "x" + unpackSkipRows);
         }
      } else {
         throw new IllegalArgumentException("Invalid mipLevel " + level + ", must be >= 0 and < " + gpuTexture.getMipLevels());
      }
   }

   public void writeToTexture(GpuTexture gpuTexture, ByteBuffer byteBuffer, class_1011.class_1012 format, int level, int j, int xOffset, int yOffset, int width, int height) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else if (level >= 0 && level < gpuTexture.getMipLevels()) {
         if (width * height * format.method_4335() > byteBuffer.remaining()) {
            throw new IllegalArgumentException("Copy would overrun the source buffer (remaining length of " + byteBuffer.remaining() + ", but copy is " + width + "x" + height + " of format " + String.valueOf(format) + ")");
         } else if (xOffset + width <= gpuTexture.getWidth(level) && yOffset + height <= gpuTexture.getHeight(level)) {
            if (gpuTexture.isClosed()) {
               throw new IllegalStateException("Destination texture is closed");
            } else if ((gpuTexture.usage() & 1) == 0) {
               throw new IllegalStateException("Color texture must have USAGE_COPY_DST to be a destination for a write");
            } else if (j >= gpuTexture.getDepthOrLayers()) {
               throw new UnsupportedOperationException("Depth or layer is out of range, must be >= 0 and < " + gpuTexture.getDepthOrLayers());
            } else {
               GlStateManager._bindTexture(((VkGpuTexture)gpuTexture).id);
               GlStateManager._pixelStore(3314, width);
               GlStateManager._pixelStore(3316, 0);
               GlStateManager._pixelStore(3315, 0);
               GlStateManager._pixelStore(3317, format.method_4335());
               GlStateManager._texSubImage2D(3553, level, xOffset, yOffset, width, height, GlConst.toGl(format), 5121, byteBuffer);
            }
         } else {
            throw new IllegalArgumentException("Dest texture (" + gpuTexture.getWidth(level) + "x" + gpuTexture.getHeight(level) + ") is not large enough to write a rectangle of " + width + "x" + height + " at " + xOffset + "x" + yOffset);
         }
      } else {
         throw new IllegalArgumentException("Invalid mipLevel, must be >= 0 and < " + gpuTexture.getMipLevels());
      }
   }

   public void copyTextureToBuffer(GpuTexture gpuTexture, GpuBuffer gpuBuffer, int i, Runnable runnable, int j) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else {
         this.copyTextureToBuffer(gpuTexture, gpuBuffer, i, runnable, j, 0, 0, gpuTexture.getWidth(j), gpuTexture.getHeight(j));
      }
   }

   public void copyTextureToBuffer(GpuTexture gpuTexture, GpuBuffer gpuBuffer, int dstOffset, Runnable runnable, int mipLevel, int xOffset, int yOffset, int width, int height) {
      VkGpuBuffer vkGpuBuffer = (VkGpuBuffer)gpuBuffer;
      VkGpuTexture vkGpuTexture = (VkGpuTexture)gpuTexture;
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else if (mipLevel >= 0 && mipLevel < gpuTexture.getMipLevels()) {
         if (gpuTexture.getWidth(mipLevel) * gpuTexture.getHeight(mipLevel) * vkGpuTexture.getVulkanImage().formatSize + dstOffset > gpuBuffer.size()) {
            throw new IllegalArgumentException("Buffer of size " + gpuBuffer.size() + " is not large enough to hold " + width + "x" + height + " pixels (" + vkGpuTexture.getVulkanImage().formatSize + " bytes each) starting from offset " + dstOffset);
         } else if (xOffset + width <= gpuTexture.getWidth(mipLevel) && yOffset + height <= gpuTexture.getHeight(mipLevel)) {
            if (gpuTexture.isClosed()) {
               throw new IllegalStateException("Source texture is closed");
            } else if (gpuBuffer.isClosed()) {
               throw new IllegalStateException("Destination buffer is closed");
            } else {
               ImageUtil.copyImageToBuffer(vkGpuTexture.getVulkanImage(), vkGpuBuffer.getBuffer(), mipLevel, width, height, xOffset, yOffset, dstOffset, width, height);
               runnable.run();
            }
         } else {
            throw new IllegalArgumentException("Copy source texture (" + gpuTexture.getWidth(mipLevel) + "x" + gpuTexture.getHeight(mipLevel) + ") is not large enough to read a rectangle of " + width + "x" + height + " from " + xOffset + "," + yOffset);
         }
      } else {
         throw new IllegalArgumentException("Invalid mipLevel " + mipLevel + ", must be >= 0 and < " + gpuTexture.getMipLevels());
      }
   }

   public void copyTextureToTexture(GpuTexture gpuTexture, GpuTexture gpuTexture2, int mipLevel, int j, int k, int l, int m, int n, int o) {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else if (mipLevel >= 0 && mipLevel < gpuTexture.getMipLevels() && mipLevel < gpuTexture2.getMipLevels()) {
         if (j + n <= gpuTexture2.getWidth(mipLevel) && k + o <= gpuTexture2.getHeight(mipLevel)) {
            if (l + n <= gpuTexture.getWidth(mipLevel) && m + o <= gpuTexture.getHeight(mipLevel)) {
               if (gpuTexture.isClosed()) {
                  throw new IllegalStateException("Source texture is closed");
               } else if (gpuTexture2.isClosed()) {
                  throw new IllegalStateException("Destination texture is closed");
               }
            } else {
               throw new IllegalArgumentException("Source texture (" + gpuTexture.getWidth(mipLevel) + "x" + gpuTexture.getHeight(mipLevel) + ") is not large enough to read a rectangle of " + n + "x" + o + " at " + l + "x" + m);
            }
         } else {
            throw new IllegalArgumentException("Dest texture (" + gpuTexture2.getWidth(mipLevel) + "x" + gpuTexture2.getHeight(mipLevel) + ") is not large enough to write a rectangle of " + n + "x" + o + " at " + j + "x" + k);
         }
      } else {
         throw new IllegalArgumentException("Invalid mipLevel " + mipLevel + ", must be >= 0 and < " + gpuTexture.getMipLevels() + " and < " + gpuTexture2.getMipLevels());
      }
   }

   public GpuFence createFence() {
      if (this.inRenderPass) {
         throw new IllegalStateException("Close the existing render pass before performing additional commands");
      } else {
         return new GpuFence() {
            public void close() {
            }

            public boolean awaitCompletion(long l) {
               return true;
            }
         };
      }
   }

   public void presentTexture(GpuTextureView gpuTexture) {
      throw new UnsupportedOperationException();
   }

   protected <T> void executeDrawMultiple(VkRenderPass renderPass, Collection<RenderPass.class_10884<T>> collection, @Nullable GpuBuffer gpuBuffer, VertexFormat.@Nullable class_5595 indexType, Collection<String> collection2, T object) {
      if (this.trySetup(renderPass)) {
         if (indexType == null) {
            indexType = class_5595.field_27372;
         }

         Pipeline pipeline = ExtendedRenderPipeline.of(renderPass.getPipeline()).getPipeline();

         for(RenderPass.class_10884 draw : collection) {
            VertexFormat.class_5595 indexType2 = draw.comp_3807() == null ? indexType : draw.comp_3807();
            renderPass.setIndexBuffer(draw.comp_3806() == null ? gpuBuffer : draw.comp_3806(), indexType2);
            renderPass.setVertexBuffer(draw.comp_3804(), draw.comp_3805());
            if (class_10866.field_57867) {
               if (renderPass.indexBuffer == null) {
                  throw new IllegalStateException("Missing index buffer");
               }

               if (renderPass.indexBuffer.isClosed()) {
                  throw new IllegalStateException("Index buffer has been closed!");
               }

               if (renderPass.vertexBuffers[0] == null) {
                  throw new IllegalStateException("Missing vertex buffer at slot 0");
               }

               if (renderPass.vertexBuffers[0].isClosed()) {
                  throw new IllegalStateException("Vertex buffer at slot 0 has been closed!");
               }
            }

            BiConsumer<T, RenderPass.class_10885> biConsumer = draw.comp_3810();
            if (biConsumer != null) {
               biConsumer.accept(object, (RenderPass.class_10885)(string, gpuBufferSlice) -> {
                  EGlProgram glProgram = ExtendedRenderPipeline.of(renderPass.pipeline).getProgram();
                  class_284 patt0$temp = glProgram.getUniform(string);
                  if (patt0$temp instanceof class_284.class_11272 ubo) {
                     try {
                        int blockBinding = ubo.comp_4150();
                     } catch (Throwable var7) {
                        throw new MatchException(var7.toString(), var7);
                     }
                  }

               });
               Renderer.getInstance().uploadAndBindUBOs(pipeline);
            }

            this.drawFromBuffers(renderPass, 0, draw.comp_3808(), draw.comp_3809(), indexType2, renderPass.pipeline, 1);
         }
      }

   }

   protected void executeDraw(VkRenderPass renderPass, int i, int j, int k, VertexFormat.@Nullable class_5595 indexType, int l) {
      if (this.trySetup(renderPass)) {
         if (class_10866.field_57867) {
            if (indexType != null) {
               if (renderPass.indexBuffer == null) {
                  throw new IllegalStateException("Missing index buffer");
               }

               if (renderPass.indexBuffer.isClosed()) {
                  throw new IllegalStateException("Index buffer has been closed!");
               }
            }

            if (renderPass.vertexBuffers[0] == null) {
               throw new IllegalStateException("Missing vertex buffer at slot 0");
            }

            if (renderPass.vertexBuffers[0].isClosed()) {
               throw new IllegalStateException("Vertex buffer at slot 0 has been closed!");
            }
         }

         this.drawFromBuffers(renderPass, i, j, k, indexType, renderPass.pipeline, l);
      }

   }

   public void drawFromBuffers(VkRenderPass renderPass, int vertexOffset, int firstIndex, int vertexCount, VertexFormat.@Nullable class_5595 indexType, RenderPipeline renderPipeline, int instanceCount) {
      if (instanceCount < 1) {
         instanceCount = 1;
      }

      if (vertexOffset < 0) {
         vertexOffset = 0;
      }

      VkCommandBuffer vkCommandBuffer = Renderer.getCommandBuffer();
      VkGpuBuffer vertexBuffer = (VkGpuBuffer)renderPass.vertexBuffers[0];
      MemoryStack stack = MemoryStack.stackPush();

      try {
         if (vertexBuffer != null) {
            VK11.vkCmdBindVertexBuffers(vkCommandBuffer, 0, stack.longs(vertexBuffer.buffer.getId()), stack.longs(0L));
         }

         if (renderPass.indexBuffer != null) {
            VkGpuBuffer indexBuffer = (VkGpuBuffer)renderPass.indexBuffer;
            byte var10000;
            switch (indexType) {
               case field_27372 -> var10000 = 0;
               case field_27373 -> var10000 = 1;
               default -> throw new MatchException((String)null, (Throwable)null);
            }

            int vkIndexType = var10000;
            VK11.vkCmdBindIndexBuffer(vkCommandBuffer, indexBuffer.buffer.getId(), 0L, vkIndexType);
            VK11.vkCmdDrawIndexed(vkCommandBuffer, vertexCount, instanceCount, firstIndex, vertexOffset, 0);
         } else {
            AutoIndexBuffer autoIndexBuffer = Renderer.getDrawer().getAutoIndexBuffer(renderPipeline.getVertexFormatMode(), vertexCount);
            if (autoIndexBuffer != null) {
               int indexCount = autoIndexBuffer.getIndexCount(vertexCount);
               VK11.vkCmdBindIndexBuffer(vkCommandBuffer, autoIndexBuffer.getIndexBuffer().getId(), 0L, autoIndexBuffer.getIndexBuffer().indexType.value);
               VK11.vkCmdDrawIndexed(vkCommandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, 0);
            } else {
               VK11.vkCmdDraw(vkCommandBuffer, vertexCount, instanceCount, vertexOffset, 0);
            }
         }
      } catch (Throwable var14) {
         if (stack != null) {
            try {
               stack.close();
            } catch (Throwable var13) {
               var14.addSuppressed(var13);
            }
         }

         throw var14;
      }

      if (stack != null) {
         stack.close();
      }

   }

   public boolean trySetup(VkRenderPass renderPass) {
      if (VkRenderPass.VALIDATION) {
         if (renderPass.pipeline == null) {
            throw new IllegalStateException("Can't draw without a render pipeline");
         }

         for(RenderPipeline.UniformDescription uniformDescription : renderPass.pipeline.getUniforms()) {
            Object object = renderPass.uniforms.get(uniformDescription.name());
            if (object == null && !class_5944.field_57863.contains(uniformDescription.name())) {
               String var10002 = uniformDescription.name();
               throw new IllegalStateException("Missing uniform " + var10002 + " (should be " + String.valueOf(uniformDescription.type()) + ")");
            }
         }
      }

      this.applyPipelineState(renderPass.pipeline);
      this.setupUniforms(renderPass);
      if (renderPass.isScissorEnabled()) {
         GlStateManager._enableScissorTest();
         GlStateManager._scissorBox(renderPass.getScissorX(), renderPass.getScissorY(), renderPass.getScissorWidth(), renderPass.getScissorHeight());
      } else {
         GlStateManager._disableScissorTest();
      }

      return this.bindPipeline(renderPass.pipeline);
   }

   public void setupUniforms(VkRenderPass renderPass) {
      RenderPipeline renderPipeline = renderPass.pipeline;
      EGlProgram glProgram = ExtendedRenderPipeline.of(renderPass.pipeline).getProgram();
      Pipeline pipeline = ExtendedRenderPipeline.of(renderPass.pipeline).getPipeline();

      for(UBO ubo : pipeline.getBuffers()) {
         String uniformName = ubo.name;
         glProgram.getUniform(uniformName);
         GpuBufferSlice gpuBufferSlice = (GpuBufferSlice)renderPass.uniforms.get(uniformName);
         if (gpuBufferSlice == null) {
            ubo.setUseGlobalBuffer(true);
            ubo.setUpdate(true);
         } else {
            VkGpuBuffer gpuBuffer = (VkGpuBuffer)gpuBufferSlice.buffer();

            assert ubo != null;

            ubo.setUseGlobalBuffer(false);
            ubo.getBufferSlice().set(gpuBuffer.buffer, gpuBufferSlice.offset(), gpuBufferSlice.length());
         }
      }

      for(ImageDescriptor imageDescriptor : pipeline.getImageDescriptors()) {
         String uniformName = imageDescriptor.name;
         int samplerIndex = imageDescriptor.imageIdx;
         VkTextureView textureView = (VkTextureView)renderPass.samplers.get(uniformName);
         if (textureView != null) {
            VkGpuTexture gpuTexture = textureView.texture();
            if (!gpuTexture.isClosed()) {
               GlStateManager._activeTexture('蓀' + samplerIndex);
               GlStateManager._bindTexture(gpuTexture.id);
               GlStateManager._texParameter(3553, 33084, textureView.baseMipLevel());
               GlStateManager._texParameter(3553, 33085, textureView.baseMipLevel() + textureView.mipLevels() - 1);
               gpuTexture.flushModeChanges();
            }
         }
      }

   }

   public boolean bindPipeline(RenderPipeline renderPipeline) {
      Pipeline pipeline = ExtendedRenderPipeline.of(renderPipeline).getPipeline();
      if (pipeline == null) {
         return false;
      } else {
         Renderer renderer = Renderer.getInstance();
         renderer.bindGraphicsPipeline((GraphicsPipeline)pipeline);
         renderer.uploadAndBindUBOs(pipeline);
         return true;
      }
   }

   public void applyPipelineState(RenderPipeline renderPipeline) {
      if (this.lastPipeline != renderPipeline) {
         this.lastPipeline = renderPipeline;
         if (renderPipeline.getDepthTestFunction() != DepthTestFunction.NO_DEPTH_TEST) {
            GlStateManager._enableDepthTest();
            GlStateManager._depthFunc(GlConst.toGl(renderPipeline.getDepthTestFunction()));
         } else {
            GlStateManager._disableDepthTest();
         }

         if (renderPipeline.isCull()) {
            GlStateManager._enableCull();
         } else {
            GlStateManager._disableCull();
         }

         if (renderPipeline.getBlendFunction().isPresent()) {
            GlStateManager._enableBlend();
            BlendFunction blendFunction = (BlendFunction)renderPipeline.getBlendFunction().get();
            GlStateManager._blendFuncSeparate(GlConst.toGl(blendFunction.sourceColor()), GlConst.toGl(blendFunction.destColor()), GlConst.toGl(blendFunction.sourceAlpha()), GlConst.toGl(blendFunction.destAlpha()));
         } else {
            GlStateManager._disableBlend();
         }

         GlStateManager._polygonMode(1032, GlConst.toGl(renderPipeline.getPolygonMode()));
         GlStateManager._depthMask(renderPipeline.isWriteDepth());
         GlStateManager._colorMask(renderPipeline.isWriteColor(), renderPipeline.isWriteColor(), renderPipeline.isWriteColor(), renderPipeline.isWriteAlpha());
         if (renderPipeline.getDepthBiasConstant() == 0.0F && renderPipeline.getDepthBiasScaleFactor() == 0.0F) {
            GlStateManager._disablePolygonOffset();
         } else {
            GlStateManager._polygonOffset(renderPipeline.getDepthBiasScaleFactor(), renderPipeline.getDepthBiasConstant());
            GlStateManager._enablePolygonOffset();
         }

         switch (renderPipeline.getColorLogic()) {
            case NONE:
               GlStateManager._disableColorLogicOp();
               break;
            case OR_REVERSE:
               GlStateManager._enableColorLogicOp();
               GlStateManager._logicOp(5387);
         }

         VRenderSystem.setPrimitiveTopologyGL(GlConst.toGl(renderPipeline.getVertexFormatMode()));
      }

   }

   public void finishRenderPass() {
      this.inRenderPass = false;
   }

   protected VkGpuDevice getDevice() {
      return this.device;
   }
}
