package com.mojang.blaze3d.vulkan; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.buffers.GpuFence; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.systems.CommandEncoderBackend; import com.mojang.blaze3d.systems.GpuQueryPool; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderPassBackend; import com.mojang.blaze3d.systems.RenderPassDescriptor; import com.mojang.blaze3d.textures.GpuTexture; import com.mojang.blaze3d.textures.GpuTextureView; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.OptionalDouble; import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import org.joml.Vector4fc; import org.jspecify.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MathUtil; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import org.lwjgl.vulkan.KHRDynamicRendering; import org.lwjgl.vulkan.KHRSynchronization2; import org.lwjgl.vulkan.VK12; import org.lwjgl.vulkan.VkBufferCopy; import org.lwjgl.vulkan.VkBufferImageCopy; import org.lwjgl.vulkan.VkClearAttachment; import org.lwjgl.vulkan.VkClearColorValue; import org.lwjgl.vulkan.VkClearDepthStencilValue; import org.lwjgl.vulkan.VkClearRect; import org.lwjgl.vulkan.VkClearValue; import org.lwjgl.vulkan.VkCommandBuffer; import org.lwjgl.vulkan.VkCommandBufferAllocateInfo; import org.lwjgl.vulkan.VkCommandBufferBeginInfo; import org.lwjgl.vulkan.VkCommandBufferInheritanceInfo; import org.lwjgl.vulkan.VkCommandBufferInheritanceRenderingInfo; import org.lwjgl.vulkan.VkCommandPoolCreateInfo; import org.lwjgl.vulkan.VkDependencyInfo; import org.lwjgl.vulkan.VkImageCopy; import org.lwjgl.vulkan.VkImageSubresourceLayers; import org.lwjgl.vulkan.VkImageSubresourceRange; import org.lwjgl.vulkan.VkMemoryBarrier2; import org.lwjgl.vulkan.VkRect2D; import org.lwjgl.vulkan.VkRenderingAttachmentInfo; import org.lwjgl.vulkan.VkRenderingInfo; import org.lwjgl.vulkan.VkSemaphoreCreateInfo; import org.lwjgl.vulkan.VkSemaphoreTypeCreateInfo; import org.lwjgl.vulkan.VkSemaphoreWaitInfo; import org.lwjgl.vulkan.VkMemoryBarrier2.Buffer; @Environment(EnvType.CLIENT) public class VulkanCommandEncoder implements CommandEncoderBackend, Destroyable { public static final int MAX_SUBMITS_IN_FLIGHT = 2; private final VulkanDevice device; private final long submitSemaphore; private long currentSubmitIndex = 2L; private long completedSubmitIndex = 0L; private VulkanQueue.Submission submissionBuilder; private final DestructionQueue destroyQueue = new DestructionQueue<>(2, Destroyable::destroy); private final DestructionQueue commandBufferDestroyQueue; private final long commandPool; @Nullable private VkCommandBuffer currentCommandBuffer; @Nullable private VulkanRenderPass currentRenderPass; public VulkanCommandEncoder(final VulkanDevice device) { this.device = device; MemoryStack baseStack = MemoryStack.stackGet(); try (MemoryStack stack = baseStack.push()) { VkSemaphoreTypeCreateInfo semaphoreTypeCreateInfo = VkSemaphoreTypeCreateInfo.calloc(stack).sType$Default(); semaphoreTypeCreateInfo.semaphoreType(1); semaphoreTypeCreateInfo.initialValue(this.currentSubmitIndex - 1L); VkSemaphoreCreateInfo semaphoreCreateInfo = VkSemaphoreCreateInfo.calloc(stack).sType$Default(); semaphoreCreateInfo.pNext(semaphoreTypeCreateInfo); LongBuffer semaphoreHandlePtr = stack.callocLong(1); VulkanUtils.crashIfFailure(VK12.vkCreateSemaphore(device.vkDevice(), semaphoreCreateInfo, null, semaphoreHandlePtr), "Failed to create submit VkSemaphore"); this.submitSemaphore = semaphoreHandlePtr.get(0); } try (MemoryStack stack = baseStack.push()) { VkCommandPoolCreateInfo commandPoolCreateInfo = VkCommandPoolCreateInfo.calloc(stack).sType$Default(); commandPoolCreateInfo.flags(1); commandPoolCreateInfo.queueFamilyIndex(device.graphicsQueue().queueFamilyIndex()); LongBuffer commandPoolHandlePtr = stack.callocLong(1); VulkanUtils.crashIfFailure(VK12.vkCreateCommandPool(device.vkDevice(), commandPoolCreateInfo, null, commandPoolHandlePtr), "Failed to create VkCommandPool"); this.commandPool = commandPoolHandlePtr.get(0); } this.submissionBuilder = device.graphicsQueue().beginSubmit(); this.commandBufferDestroyQueue = new DestructionQueue<>(2, new DestructionQueue.Destroyer() { private PointerBuffer commandBuffers; { Objects.requireNonNull(VulkanCommandEncoder.this); this.commandBuffers = PointerBuffer.allocateDirect(64); } @Override public void begin(final int count) { if (this.commandBuffers.capacity() < count) { this.commandBuffers = PointerBuffer.allocateDirect(MathUtil.mathRoundPoT(count)); } this.commandBuffers.clear(); } public void destroy(final VkCommandBuffer commandBuffer) { this.commandBuffers.put(commandBuffer); } @Override public void end() { this.commandBuffers.flip(); if (this.commandBuffers.limit() != 0) { VK12.vkFreeCommandBuffers(device.vkDevice(), VulkanCommandEncoder.this.commandPool, this.commandBuffers); } } }); } @Override public void destroy() { this.submissionBuilder.close(); this.device.graphicsQueue().waitIdle(); this.commandBufferDestroyQueue.close(); this.destroyQueue.close(); VK12.vkDestroyCommandPool(this.device.vkDevice(), this.commandPool, null); VK12.vkDestroySemaphore(this.device.vkDevice(), this.submitSemaphore, null); } public void queueForDestroy(final Destroyable destroyable) { this.destroyQueue.add(destroyable); } public void queueForDestroy(final VkCommandBuffer commandBuffer) { this.commandBufferDestroyQueue.add(commandBuffer); } private VulkanGpuBuffer createStagingBuffer(final ByteBuffer data) { int size = data.remaining(); VulkanGpuBuffer stagingBuffer = new VulkanGpuBuffer(this.device, () -> "Staging buffer", 22, size, false); try (GpuBufferSlice.MappedView mappedMemory = stagingBuffer.map(0L, data.remaining(), false, true)) { MemoryUtil.memCopy(data, mappedMemory.data()); } return stagingBuffer; } public VkCommandBuffer allocateTransientCommandBuffer(final boolean primary) { VkCommandBuffer var6; try (MemoryStack stack = MemoryStack.stackPush()) { VkCommandBufferAllocateInfo allocateInfo = VkCommandBufferAllocateInfo.calloc(stack).sType$Default(); allocateInfo.commandPool(this.commandPool); allocateInfo.level(primary ? 0 : 1); allocateInfo.commandBufferCount(1); PointerBuffer bufferPtr = stack.callocPointer(1); VulkanUtils.crashIfFailure(VK12.vkAllocateCommandBuffers(this.device.vkDevice(), allocateInfo, bufferPtr), "Failed to allocated VkCommandBuffer"); VkCommandBuffer commandBuffer = new VkCommandBuffer(bufferPtr.get(0), this.device.vkDevice()); this.queueForDestroy(commandBuffer); var6 = commandBuffer; } return var6; } private VkCommandBuffer commandBuffer() { if (this.currentCommandBuffer != null) { return this.currentCommandBuffer; } else { VkCommandBuffer var3; try (MemoryStack stack = MemoryStack.stackPush()) { this.currentCommandBuffer = this.allocateTransientCommandBuffer(true); VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack).sType$Default(); beginInfo.flags(1); VulkanUtils.crashIfFailure(VK12.vkBeginCommandBuffer(this.currentCommandBuffer, beginInfo), "Failed to begin VkCommandBuffer"); this.submissionBuilder.executeCommands(this.currentCommandBuffer); var3 = this.currentCommandBuffer; } return var3; } } private VkCommandBuffer renderpassCommandBuffer(final IntBuffer colorAttachmentFormats, final int depthFormat) { VkCommandBuffer var8; try (MemoryStack stack = MemoryStack.stackPush()) { VkCommandBuffer renderpassCommandBuffer = this.allocateTransientCommandBuffer(false); VkCommandBufferInheritanceRenderingInfo dynamicRenderinginheritanceInfo = VkCommandBufferInheritanceRenderingInfo.calloc(stack).sType$Default(); dynamicRenderinginheritanceInfo.pColorAttachmentFormats(colorAttachmentFormats); dynamicRenderinginheritanceInfo.depthAttachmentFormat(depthFormat); dynamicRenderinginheritanceInfo.rasterizationSamples(1); VkCommandBufferInheritanceInfo inheritanceInfo = VkCommandBufferInheritanceInfo.calloc(stack).sType$Default(); inheritanceInfo.pNext(dynamicRenderinginheritanceInfo); VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack).sType$Default(); beginInfo.flags(3); beginInfo.pInheritanceInfo(inheritanceInfo); VulkanUtils.crashIfFailure(VK12.vkBeginCommandBuffer(renderpassCommandBuffer, beginInfo), "Failed to begin VkCommandBuffer"); var8 = renderpassCommandBuffer; } return var8; } VkCommandBuffer textureInitCommandBuffer() { return this.commandBuffer(); } private void endCommandBuffer() { if (this.currentCommandBuffer != null) { VulkanUtils.crashIfFailure(VK12.vkEndCommandBuffer(this.currentCommandBuffer), "Failed to end VkCommandBuffer"); this.currentCommandBuffer = null; } } public void waitSemaphore(final long vkSemaphore, final long value, final long stageMask) { this.endCommandBuffer(); this.submissionBuilder.waitSemaphore(vkSemaphore, value, stageMask); } public void execute(final VkCommandBuffer commandBuffer) { this.endCommandBuffer(); this.submissionBuilder.executeCommands(commandBuffer); } public void signalSemaphore(final long vkSemaphore, final long value, final long stageMask) { this.endCommandBuffer(); this.submissionBuilder.signalSemaphore(vkSemaphore, value, stageMask); } private void memoryBarrier(final MemoryStack stack) { Buffer memoryBarrier = VkMemoryBarrier2.calloc(1, stack).sType$Default(); memoryBarrier.srcStageMask(65536L); memoryBarrier.srcAccessMask(98304L); memoryBarrier.dstStageMask(65536L); memoryBarrier.dstAccessMask(98304L); VkDependencyInfo depInfo = VkDependencyInfo.calloc(stack).sType$Default(); depInfo.pMemoryBarriers(memoryBarrier); KHRSynchronization2.vkCmdPipelineBarrier2KHR(this.commandBuffer(), depInfo); } @Override public void submit() { this.endCommandBuffer(); this.signalSemaphore(this.submitSemaphore, this.currentSubmitIndex, 65536L); this.submissionBuilder.close(); this.submissionBuilder = this.device.graphicsQueue().beginSubmit(); this.currentSubmitIndex++; if (!this.awaitSubmitCompletion(this.currentSubmitIndex - 2L, 5000L)) { throw new IllegalStateException("5s timeout reached when waiting for VK semaphore"); } else { this.commandBufferDestroyQueue.rotate(); this.destroyQueue.rotate(); } } @Override public RenderPassBackend createRenderPass(final RenderPassDescriptor descriptor) { List>> colorAttachments = descriptor.colorAttachments(); VulkanGpuTextureView[] colorTextures = new VulkanGpuTextureView[colorAttachments.size()]; for (int i = 0; i < colorAttachments.size(); i++) { RenderPassDescriptor.Attachment> attachment = (RenderPassDescriptor.Attachment>)colorAttachments.get(i); colorTextures[i] = attachment != null ? (VulkanGpuTextureView)attachment.textureView() : null; } RenderPassDescriptor.Attachment depthAttachment = descriptor.depthAttachment(); this.device.instance().debug().beginDebugGroup(this.commandBuffer(), descriptor.label()); try (MemoryStack stack = MemoryStack.stackPush()) { int width = 0; int height = 0; if (!colorAttachments.isEmpty()) { for (RenderPassDescriptor.Attachment> colorAttachment : colorAttachments) { if (colorAttachment != null) { GpuTextureView colorTexture = colorAttachment.textureView(); width = colorTexture.getWidth(0); height = colorTexture.getHeight(0); } } } else if (depthAttachment != null) { width = depthAttachment.textureView().getWidth(0); height = depthAttachment.textureView().getHeight(0); } VkRect2D vkRenderArea = VkRect2D.calloc(stack); if (descriptor.renderArea != null) { vkRenderArea.extent().set(descriptor.renderArea.width(), descriptor.renderArea.height()); vkRenderArea.offset().set(descriptor.renderArea.x(), descriptor.renderArea.y()); } else { vkRenderArea.extent().set(width, height); vkRenderArea.offset().set(0, 0); } org.lwjgl.vulkan.VkRenderingAttachmentInfo.Buffer colorAttachmentInfo = VkRenderingAttachmentInfo.calloc(colorAttachments.size(), stack); for (int i = 0; i < colorAttachments.size(); i++) { colorAttachmentInfo.position(i).sType$Default(); VulkanGpuTextureView colorTexture = colorTextures[i]; if (colorTexture != null) { colorAttachmentInfo.imageView(colorTexture.vkImageView()); colorAttachmentInfo.imageLayout(1); colorAttachmentInfo.storeOp(0); RenderPassDescriptor.Attachment> attachment = (RenderPassDescriptor.Attachment>)colorAttachments.get(i); Optional clearValue = attachment.clearValue(); if (clearValue.isPresent()) { Vector4fc color = (Vector4fc)clearValue.get(); VkClearColorValue vkClearColor = VulkanUtils.putArgb(VkClearColorValue.calloc(stack), color); colorAttachmentInfo.loadOp(1); colorAttachmentInfo.clearValue(VkClearValue.calloc(stack).color(vkClearColor)); } else { colorAttachmentInfo.loadOp(0); } } else { colorAttachmentInfo.imageView(0L); colorAttachmentInfo.imageLayout(0); colorAttachmentInfo.storeOp(1); colorAttachmentInfo.loadOp(2); } } colorAttachmentInfo.position(0); VkRenderingInfo renderingInfo = VkRenderingInfo.calloc(stack).sType$Default(); renderingInfo.renderArea(vkRenderArea); renderingInfo.flags(1); renderingInfo.layerCount(1); renderingInfo.viewMask(0); renderingInfo.pColorAttachments(colorAttachmentInfo); if (depthAttachment != null) { VkRenderingAttachmentInfo depthAttachmentInfo = VkRenderingAttachmentInfo.calloc(stack).sType$Default(); VulkanGpuTextureView vulkanDepthAttachment = (VulkanGpuTextureView)depthAttachment.textureView(); depthAttachmentInfo.imageView(vulkanDepthAttachment.vkImageView()); depthAttachmentInfo.imageLayout(1); depthAttachmentInfo.storeOp(0); OptionalDouble clearValue = depthAttachment.clearValue(); if (clearValue.isPresent()) { double color = clearValue.getAsDouble(); VkClearDepthStencilValue vkClearColor = VkClearDepthStencilValue.calloc(stack).depth((float)color); depthAttachmentInfo.loadOp(1); depthAttachmentInfo.clearValue(VkClearValue.calloc(stack).depthStencil(vkClearColor)); } else { depthAttachmentInfo.loadOp(0); } renderingInfo.pDepthAttachment(depthAttachmentInfo); } KHRDynamicRendering.vkCmdBeginRenderingKHR(this.commandBuffer(), renderingInfo); Supplier secondaryCommandBufferSupplier = () -> { VkCommandBuffer var8; try (MemoryStack memStack = MemoryStack.stackPush()) { IntBuffer colorFormats = memStack.mallocInt(colorTextures.length); for (int ix = 0; ix < colorTextures.length; ix++) { colorFormats.put(ix, colorTextures[ix] != null ? VulkanConst.toVk(colorTextures[ix].texture().getFormat()) : 0); } var8 = this.renderpassCommandBuffer(colorFormats, depthAttachment == null ? 0 : VulkanConst.toVk(depthAttachment.textureView().texture().getFormat())); } return var8; }; this.currentRenderPass = new VulkanRenderPass( this.device, this::queueForDestroy, this.commandBuffer(), secondaryCommandBufferSupplier, descriptor.renderArea, width, height, depthAttachment != null ); } return this.currentRenderPass; } @Override public void submitRenderPass() { if (this.currentRenderPass == null) { throw new IllegalStateException("Cannot submit a renderpass if one hasn't been started!"); } else { this.currentRenderPass.end(); KHRDynamicRendering.vkCmdEndRenderingKHR(this.commandBuffer()); this.device.instance().debug().endDebugGroup(this.commandBuffer()); try (MemoryStack stack = MemoryStack.stackPush()) { this.memoryBarrier(stack); } } } private void clearColorTextureUnsynced(final MemoryStack stack, final GpuTexture colorTexture, final Vector4fc clearColor) { VkClearColorValue vkClearColor = VulkanUtils.putArgb(VkClearColorValue.calloc(stack), clearColor); VkImageSubresourceRange subresourceRange = VkImageSubresourceRange.calloc(stack); subresourceRange.baseMipLevel(0); subresourceRange.levelCount(colorTexture.getMipLevels()); subresourceRange.baseArrayLayer(0); subresourceRange.layerCount(1); subresourceRange.aspectMask(1); VK12.vkCmdClearColorImage(this.commandBuffer(), ((VulkanGpuTexture)colorTexture).vkImage(), 1, vkClearColor, subresourceRange); } public void clearDepthTextureUnsynced(final MemoryStack stack, final GpuTexture depthTexture, final double clearDepth) { VkClearDepthStencilValue vkClearDepth = VkClearDepthStencilValue.calloc(stack).depth((float)clearDepth); VkImageSubresourceRange subresourceRange = VkImageSubresourceRange.calloc(stack); subresourceRange.baseMipLevel(0); subresourceRange.levelCount(depthTexture.getMipLevels()); subresourceRange.baseArrayLayer(0); subresourceRange.layerCount(1); subresourceRange.aspectMask(2); VK12.vkCmdClearDepthStencilImage(this.commandBuffer(), ((VulkanGpuTexture)depthTexture).vkImage(), 1, vkClearDepth, subresourceRange); } @Override public void clearColorTexture(final GpuTexture colorTexture, final Vector4fc clearColor) { try (MemoryStack stack = MemoryStack.stackPush()) { this.clearColorTextureUnsynced(stack, colorTexture, clearColor); this.memoryBarrier(stack); } } @Override public void clearColorAndDepthTextures(final GpuTexture colorTexture, final Vector4fc clearColor, final GpuTexture depthTexture, final double clearDepth) { try (MemoryStack stack = MemoryStack.stackPush()) { this.clearColorTextureUnsynced(stack, colorTexture, clearColor); this.clearDepthTextureUnsynced(stack, depthTexture, clearDepth); this.memoryBarrier(stack); } } @Override public void clearColorAndDepthTextures( final GpuTexture colorTexture, final Vector4fc clearColor, final GpuTexture depthTexture, final double clearDepth, final int regionX, final int regionY, final int regionWidth, final int regionHeight ) { try ( GpuTextureView colorTextureView = this.device.createTextureView(colorTexture); GpuTextureView depthTextureView = this.device.createTextureView(depthTexture); MemoryStack stack = MemoryStack.stackPush(); ) { this.createRenderPass( RenderPassDescriptor.create(() -> "ClearColorDepthTextures") .withColorAttachment(colorTextureView) .withDepthAttachment(depthTextureView) .withRenderArea(new RenderPass.RenderArea(0, 0, colorTexture.getWidth(0), colorTexture.getHeight(0))) ); assert this.currentRenderPass != null; org.lwjgl.vulkan.VkClearRect.Buffer rects = VkClearRect.calloc(1, stack); rects.baseArrayLayer(0); rects.layerCount(1); rects.rect().offset().set(regionX, regionY); rects.rect().extent().set(regionWidth, regionHeight); org.lwjgl.vulkan.VkClearAttachment.Buffer attachments = VkClearAttachment.calloc(2, stack); VkClearValue colorClearValue = VkClearValue.calloc(stack); VulkanUtils.putArgb(colorClearValue.color(), clearColor); attachments.aspectMask(1); attachments.clearValue(colorClearValue); VkClearValue depthClearValue = VkClearValue.calloc(stack); VkClearDepthStencilValue clearValue = depthClearValue.depthStencil(); clearValue.depth((float)clearDepth); attachments.position(1); attachments.aspectMask(2); attachments.clearValue(depthClearValue); attachments.position(0); VkCommandBuffer secondaryCommandBuffer = this.currentRenderPass.allocateTransientRenderpassCommandBuffer(); VK12.vkCmdClearAttachments(secondaryCommandBuffer, attachments, rects); VulkanUtils.crashIfFailure(VK12.vkEndCommandBuffer(secondaryCommandBuffer), "Failed to end VkCommandBuffer"); this.currentRenderPass.executeCommandBuffer(secondaryCommandBuffer); this.submitRenderPass(); } } @Override public void clearDepthTexture(final GpuTexture depthTexture, final double clearDepth) { try (MemoryStack stack = MemoryStack.stackPush()) { this.clearDepthTextureUnsynced(stack, depthTexture, clearDepth); this.memoryBarrier(stack); } } @Override public void writeToBuffer(final GpuBufferSlice destination, final ByteBuffer data) { VulkanGpuBuffer destBuffer = (VulkanGpuBuffer)destination.buffer(); try ( VulkanGpuBuffer stagingBuffer = this.createStagingBuffer(data); MemoryStack stack = MemoryStack.stackPush(); ) { org.lwjgl.vulkan.VkBufferCopy.Buffer regions = VkBufferCopy.calloc(1, stack).srcOffset(0L).dstOffset(destination.offset()).size(data.remaining()); VK12.vkCmdCopyBuffer(this.commandBuffer(), stagingBuffer.vkBuffer(), destBuffer.vkBuffer(), regions); this.memoryBarrier(stack); } } @Override public void copyToBuffer(final GpuBufferSlice source, final GpuBufferSlice target) { try (MemoryStack stack = MemoryStack.stackPush()) { org.lwjgl.vulkan.VkBufferCopy.Buffer copyInfo = VkBufferCopy.calloc(1, stack); copyInfo.srcOffset(source.offset()); copyInfo.dstOffset(target.offset()); copyInfo.size(source.length()); VK12.vkCmdCopyBuffer(this.commandBuffer(), ((VulkanGpuBuffer)source.buffer()).vkBuffer(), ((VulkanGpuBuffer)target.buffer()).vkBuffer(), copyInfo); this.memoryBarrier(stack); } } @Override public void writeToTexture( final GpuTexture destination, final NativeImage source, final int mipLevel, final int depthOrLayer, final int destX, final int destY, final int width, final int height, final int sourceX, final int sourceY ) { int stagingBufferSize = source.getWidth() * source.getHeight() * destination.getFormat().pixelSize(); int texelSize = destination.getFormat().pixelSize(); int skipTexels = sourceX + sourceY * source.getWidth(); long skipBytes = (long)skipTexels * texelSize; try (VulkanGpuBuffer stagingBuffer = this.createStagingBuffer(MemoryUtil.memByteBuffer(source.getPointer(), stagingBufferSize))) { this.writeToTexture( (VulkanGpuTexture)destination, stagingBuffer, skipBytes, mipLevel, depthOrLayer, destX, destY, width, height, source.getWidth(), source.getHeight() ); } } @Override public void writeToTexture( final GpuTexture destination, final ByteBuffer source, final NativeImage.Format format, final int mipLevel, final int depthOrLayer, final int destX, final int destY, final int width, final int height ) { try (VulkanGpuBuffer stagingBuffer = this.createStagingBuffer(source)) { this.writeToTexture((VulkanGpuTexture)destination, stagingBuffer, 0L, mipLevel, depthOrLayer, destX, destY, width, height, width, height); } } private void writeToTexture( final VulkanGpuTexture destination, final VulkanGpuBuffer stagingBuffer, final long bufferOffset, final int mipLevel, final int depthOrLayer, final int destX, final int destY, final int width, final int height, final int srcWidth, final int srcHeight ) { try (MemoryStack stack = MemoryStack.stackPush()) { org.lwjgl.vulkan.VkBufferImageCopy.Buffer region = VkBufferImageCopy.calloc(1, stack); region.bufferOffset(bufferOffset); region.bufferRowLength(srcWidth); region.bufferImageHeight(srcHeight); VkImageSubresourceLayers imageSubresource = region.imageSubresource(); imageSubresource.aspectMask(1); imageSubresource.mipLevel(mipLevel); imageSubresource.baseArrayLayer(depthOrLayer); imageSubresource.layerCount(1); region.imageOffset().set(destX, destY, 0); region.imageExtent().set(width, height, 1); VK12.vkCmdCopyBufferToImage(this.commandBuffer(), stagingBuffer.vkBuffer(), destination.vkImage(), 1, region); this.memoryBarrier(stack); } } @Override public void copyTextureToBuffer(final GpuTexture source, final GpuBuffer destination, final long offset, final Runnable callback, final int mipLevel) { this.copyTextureToBuffer(source, destination, offset, callback, mipLevel, 0, 0, source.getWidth(mipLevel), source.getHeight(mipLevel)); } @Override public void copyTextureToBuffer( final GpuTexture source, final GpuBuffer destination, final long offset, final Runnable callback, final int mipLevel, final int x, final int y, final int width, final int height ) { try (MemoryStack stack = MemoryStack.stackPush()) { org.lwjgl.vulkan.VkBufferImageCopy.Buffer copy = VkBufferImageCopy.calloc(1, stack); copy.bufferOffset(offset); VkImageSubresourceLayers subresource = copy.imageSubresource(); subresource.aspectMask(VulkanConst.formatAspectMask(source.getFormat())); subresource.mipLevel(mipLevel); subresource.baseArrayLayer(0); subresource.layerCount(1); copy.imageOffset().set(x, y, 0); copy.imageExtent().set(width, height, 1); copy.bufferRowLength(width); copy.bufferImageHeight(height); VK12.vkCmdCopyImageToBuffer(this.commandBuffer(), ((VulkanGpuTexture)source).vkImage(), 1, ((VulkanGpuBuffer)destination).vkBuffer(), copy); this.memoryBarrier(stack); } this.queueForDestroy(callback::run); } @Override public void copyTextureToTexture( final GpuTexture source, final GpuTexture destination, final int mipLevel, final int destX, final int destY, final int sourceX, final int sourceY, final int width, final int height ) { VulkanGpuTexture vulkanSrc = (VulkanGpuTexture)source; VulkanGpuTexture vulkanDst = (VulkanGpuTexture)destination; try (MemoryStack stack = MemoryStack.stackPush()) { VkImageSubresourceLayers subresourceLayers = VkImageSubresourceLayers.calloc(stack); subresourceLayers.mipLevel(mipLevel); subresourceLayers.baseArrayLayer(0); subresourceLayers.layerCount(1); subresourceLayers.aspectMask(VulkanConst.formatAspectMask(source.getFormat())); org.lwjgl.vulkan.VkImageCopy.Buffer regions = VkImageCopy.calloc(1, stack); regions.srcOffset().set(sourceX, sourceY, 0); regions.dstOffset().set(destX, destY, 0); regions.extent().set(width, height, 1); regions.srcSubresource(subresourceLayers); regions.dstSubresource(subresourceLayers); VK12.vkCmdCopyImage(this.commandBuffer(), vulkanSrc.vkImage(), 1, vulkanDst.vkImage(), 1, regions); this.memoryBarrier(stack); } } private boolean awaitSubmitCompletion(final long submitIndex, final long timeoutMs) { if (this.completedSubmitIndex >= submitIndex) { return true; } else if (submitIndex == this.currentSubmitIndex) { throw new IllegalStateException("Cannot wait on a fence for the current submit"); } else { boolean var9; try (MemoryStack stack = MemoryStack.stackPush()) { VkSemaphoreWaitInfo waitInfo = VkSemaphoreWaitInfo.calloc(stack).sType$Default(); waitInfo.pSemaphores(stack.longs(this.submitSemaphore)); waitInfo.pValues(stack.longs(submitIndex)); waitInfo.semaphoreCount(1); int result = VK12.vkWaitSemaphores(this.device.vkDevice(), waitInfo, timeoutMs * 1000000L); VulkanUtils.crashIfFailure(result, "Failed to wait for semaphore"); boolean completed = result == 0; if (completed) { this.completedSubmitIndex = submitIndex; } var9 = completed; } return var9; } } @Override public GpuFence createFence() { return new GpuFence() { private final long submitIndex; private boolean completed; { Objects.requireNonNull(VulkanCommandEncoder.this); this.submitIndex = VulkanCommandEncoder.this.currentSubmitIndex; this.completed = false; } @Override public boolean awaitCompletion(final long timeoutMs) { if (!this.completed) { this.completed = VulkanCommandEncoder.this.awaitSubmitCompletion(this.submitIndex, timeoutMs); } return this.completed; } @Override public void close() { this.completed = true; } }; } @Override public void writeTimestamp(final GpuQueryPool pool, final int index) { long queryPool = ((VulkanQueryPool)pool).vkQueryPool(); VK12.vkResetQueryPool(this.device.vkDevice(), queryPool, index, 1); KHRSynchronization2.vkCmdWriteTimestamp2KHR(this.commandBuffer(), 65536L, queryPool, index); } public long getTimestampNow() { long var6; try ( MemoryStack stack = MemoryStack.stackPush(); VulkanQueryPool queryPool = (VulkanQueryPool)this.device.createTimestampQueryPool(1); ) { VkCommandBuffer commandBuffer = this.allocateTransientCommandBuffer(true); VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.calloc(stack).sType$Default(); beginInfo.flags(1); VulkanUtils.crashIfFailure(VK12.vkBeginCommandBuffer(commandBuffer, beginInfo), "Failed to begin VkCommandBuffer"); KHRSynchronization2.vkCmdWriteTimestamp2KHR(commandBuffer, 0L, queryPool.vkQueryPool(), 0); VulkanUtils.crashIfFailure(VK12.vkEndCommandBuffer(commandBuffer), "Failed to end VkCommandBuffer"); try (VulkanQueue.Submission submit = this.device.graphicsQueue().beginSubmit()) { submit.executeCommands(commandBuffer); } LongBuffer timestampPtr = stack.callocLong(1); VulkanUtils.crashIfFailure( VK12.vkGetQueryPoolResults(this.device.vkDevice(), queryPool.vkQueryPool(), 0, 1, timestampPtr, 0L, 3), "Cannot fetch current timestamp" ); var6 = timestampPtr.get(0); } return var6; } }