diff --git a/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java b/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java index 5ab8bee72..eb32cb336 100644 --- a/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java +++ b/src/main/java/net/vulkanmod/render/chunk/buffer/UploadManager.java @@ -107,7 +107,7 @@ public void copyBuffer(Buffer src, long srcOffset, Buffer dst, long dstOffset, l public void syncUploads() { submitUploads(); - Synchronization.INSTANCE.waitFences(); + Synchronization.INSTANCE.recycleCmdBuffers(); } private void beginCommands() { diff --git a/src/main/java/net/vulkanmod/render/texture/ImageUploadHelper.java b/src/main/java/net/vulkanmod/render/texture/ImageUploadHelper.java index 9e5f80493..66037e431 100644 --- a/src/main/java/net/vulkanmod/render/texture/ImageUploadHelper.java +++ b/src/main/java/net/vulkanmod/render/texture/ImageUploadHelper.java @@ -21,7 +21,7 @@ public void submitCommands() { return; } - long fence = queue.submitCommands(this.currentCmdBuffer); + queue.submitCommands(this.currentCmdBuffer); Synchronization.INSTANCE.addCommandBuffer(this.currentCmdBuffer); this.currentCmdBuffer = null; diff --git a/src/main/java/net/vulkanmod/vulkan/Renderer.java b/src/main/java/net/vulkanmod/vulkan/Renderer.java index fc21d3a8e..bbae77d88 100644 --- a/src/main/java/net/vulkanmod/vulkan/Renderer.java +++ b/src/main/java/net/vulkanmod/vulkan/Renderer.java @@ -19,6 +19,7 @@ import net.vulkanmod.vulkan.memory.MemoryManager; import net.vulkanmod.vulkan.pass.DefaultMainPass; import net.vulkanmod.vulkan.pass.MainPass; +import net.vulkanmod.vulkan.queue.Queue; import net.vulkanmod.vulkan.shader.GraphicsPipeline; import net.vulkanmod.vulkan.shader.Pipeline; import net.vulkanmod.vulkan.shader.PipelineState; @@ -54,6 +55,7 @@ public class Renderer { private static boolean swapChainUpdate = false; public static boolean skipRendering = false; + private static final boolean sync2 = DeviceManager.checkExt(KHRSynchronization2.VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); public static void initRenderer() { INSTANCE = new Renderer(); @@ -88,7 +90,7 @@ public static int getCurrentImage() { private List commandBuffers; private ArrayList imageAvailableSemaphores; private ArrayList renderFinishedSemaphores; - private ArrayList inFlightFences; + private ArrayList inFlightSubmits; private Framebuffer boundFramebuffer; private RenderPass boundRenderPass; @@ -164,33 +166,27 @@ private void allocateCommandBuffers() { private void createSyncObjects() { imageAvailableSemaphores = new ArrayList<>(framesNum); renderFinishedSemaphores = new ArrayList<>(framesNum); - inFlightFences = new ArrayList<>(framesNum); + inFlightSubmits = new ArrayList<>(framesNum); try (MemoryStack stack = stackPush()) { VkSemaphoreCreateInfo semaphoreInfo = VkSemaphoreCreateInfo.calloc(stack); semaphoreInfo.sType(VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO); - VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.calloc(stack); - fenceInfo.sType(VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); - fenceInfo.flags(VK_FENCE_CREATE_SIGNALED_BIT); - LongBuffer pImageAvailableSemaphore = stack.mallocLong(1); LongBuffer pRenderFinishedSemaphore = stack.mallocLong(1); - LongBuffer pFence = stack.mallocLong(1); for (int i = 0; i < framesNum; i++) { if (vkCreateSemaphore(device, semaphoreInfo, null, pImageAvailableSemaphore) != VK_SUCCESS - || vkCreateSemaphore(device, semaphoreInfo, null, pRenderFinishedSemaphore) != VK_SUCCESS - || vkCreateFence(device, fenceInfo, null, pFence) != VK_SUCCESS) { + || vkCreateSemaphore(device, semaphoreInfo, null, pRenderFinishedSemaphore) != VK_SUCCESS) { throw new RuntimeException("Failed to create synchronization objects for the frame: " + i); } imageAvailableSemaphores.add(pImageAvailableSemaphore.get(0)); renderFinishedSemaphores.add(pRenderFinishedSemaphore.get(0)); - inFlightFences.add(pFence.get(0)); + inFlightSubmits.add(0L); } @@ -206,7 +202,7 @@ public void preInitFrame() { // runTick might be called recursively, // this check forces sync to avoid upload corruption if (lastReset == currentFrame) { - waitFences(); + submitPending(); } lastReset = currentFrame; @@ -238,20 +234,22 @@ public void beginFrame() { if (skipRendering || recordingCmds) return; - vkWaitForFences(device, inFlightFences.get(currentFrame), true, VUtil.UINT64_MAX); + try (MemoryStack stack = stackPush()) { - p.pop(); - p.push("Begin_rendering"); + DeviceManager.getGraphicsQueue().waitSubmits(stack, inFlightSubmits.get(currentFrame)); + //Uses Graphics Timeline as a substitute for inFlightFences + //Aggregates frame fences and Graphics Queue fences together as one - MemoryManager.getInstance().initFrame(currentFrame); - drawer.setCurrentFrame(currentFrame); - resetDescriptors(); + p.pop(); + p.push("Begin_rendering"); - currentCmdBuffer = commandBuffers.get(currentFrame); - vkResetCommandBuffer(currentCmdBuffer, 0); + MemoryManager.getInstance().initFrame(currentFrame); + drawer.setCurrentFrame(currentFrame); - try (MemoryStack stack = stackPush()) { + resetDescriptors(); + + currentCmdBuffer = commandBuffers.get(currentFrame); IntBuffer pImageIndex = stack.mallocInt(1); @@ -303,7 +301,7 @@ public void endFrame() { mainPass.end(currentCmdBuffer); - waitFences(); + submitPending(); submitFrame(); recordingCmds = false; @@ -317,23 +315,12 @@ private void submitFrame() { return; try (MemoryStack stack = stackPush()) { - int vkResult; - VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); - submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + //Wait Async Transfers on host to avoid invalid frees (Destroy Buffer during use) + DeviceManager.getTransferQueue().waitSubmits(stack); - submitInfo.waitSemaphoreCount(1); - submitInfo.pWaitSemaphores(stack.longs(imageAvailableSemaphores.get(currentFrame))); - submitInfo.pWaitDstStageMask(stack.ints(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)); - submitInfo.pSignalSemaphores(stack.longs(renderFinishedSemaphores.get(currentFrame))); - submitInfo.pCommandBuffers(stack.pointers(currentCmdBuffer)); - - vkResetFences(device, inFlightFences.get(currentFrame)); - if ((vkResult = vkQueueSubmit(DeviceManager.getGraphicsQueue().queue(), submitInfo, inFlightFences.get(currentFrame))) != VK_SUCCESS) { - vkResetFences(device, inFlightFences.get(currentFrame)); - throw new RuntimeException("Failed to submit draw command buffer: %s".formatted(VkResult.decode(vkResult))); - } + final long submitId = sync2 ? getSubmitId2(stack) : getSubmitId(stack); VkPresentInfoKHR presentInfo = VkPresentInfoKHR.calloc(stack); presentInfo.sType(VK_STRUCTURE_TYPE_PRESENT_INFO_KHR); @@ -345,7 +332,7 @@ private void submitFrame() { presentInfo.pImageIndices(stack.ints(imageIndex)); - vkResult = vkQueuePresentKHR(DeviceManager.getPresentQueue().queue(), presentInfo); + final int vkResult = vkQueuePresentKHR(DeviceManager.getPresentQueue().queue(), presentInfo); if (vkResult == VK_ERROR_OUT_OF_DATE_KHR || vkResult == VK_SUBOPTIMAL_KHR || swapChainUpdate) { swapChainUpdate = true; @@ -355,9 +342,85 @@ private void submitFrame() { } currentFrame = (currentFrame + 1) % framesNum; + + inFlightSubmits.set(currentFrame, submitId); + } } + /** + * Fallback if Sync2 is unsupported (macOS and some EOL Devices) + */ + private long getSubmitId(MemoryStack stack) { + Queue graphicsQueue = DeviceManager.getGraphicsQueue(); + + final long submitId = graphicsQueue.submitCount(); + + VkTimelineSemaphoreSubmitInfo mainSemaphoreSubmitInfo = VkTimelineSemaphoreSubmitInfo.calloc(stack) + .sType$Default() + .pWaitSemaphoreValues(stack.longs(0, graphicsQueue.submitCount())) + .pSignalSemaphoreValues(stack.longs(0, graphicsQueue.submitCountAdd())); + + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); + submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + submitInfo.pNext(mainSemaphoreSubmitInfo); + submitInfo.waitSemaphoreCount(2); + submitInfo.pWaitSemaphores(stack.longs(imageAvailableSemaphores.get(currentFrame), graphicsQueue.getTmSemaphore())); + submitInfo.pWaitDstStageMask(stack.ints(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT)); //Vertex Bit used for LightMap Sampler Transitions + submitInfo.pSignalSemaphores(stack.longs(renderFinishedSemaphores.get(currentFrame), graphicsQueue.getTmSemaphore())); + submitInfo.pCommandBuffers(stack.pointers(currentCmdBuffer)); + + final int vkResult; + if ((vkResult = vkQueueSubmit(graphicsQueue.queue(), submitInfo, 0)) != VK_SUCCESS) { + throw new RuntimeException("Failed to submit draw command buffer: %s".formatted(VkResult.decode(vkResult))); + } + return submitId; + } + + private long getSubmitId2(MemoryStack stack) { + + Queue graphicsQueue = DeviceManager.getGraphicsQueue(); + final long submitId = graphicsQueue.submitCount(); + + VkCommandBufferSubmitInfo.Buffer commandBufferSubmitInfo = VkCommandBufferSubmitInfo.calloc(1, stack) + .sType$Default() + .commandBuffer(currentCmdBuffer); + + VkSemaphoreSubmitInfo.Buffer waitSemaphoreSubmitInfo = VkSemaphoreSubmitInfo.calloc(2, stack); + waitSemaphoreSubmitInfo.get(0).sType$Default() + .semaphore(imageAvailableSemaphores.get(currentFrame)) + .stageMask(VK13.VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT) + .value(0); + + waitSemaphoreSubmitInfo.get(1).sType$Default() + .semaphore(graphicsQueue.getTmSemaphore()) + .stageMask(VK13.VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT) //LightMap Sampler Transitions + .value(graphicsQueue.submitCount()); + + VkSemaphoreSubmitInfo.Buffer mainSemaphoreSubmitInfo = VkSemaphoreSubmitInfo.calloc(2, stack); + mainSemaphoreSubmitInfo.get(0).sType$Default() + .semaphore(renderFinishedSemaphores.get(currentFrame)) + .stageMask(VK13.VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT) + .value(0); + + mainSemaphoreSubmitInfo.get(1).sType$Default() + .semaphore(graphicsQueue.getTmSemaphore()) + .stageMask(VK13.VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT) + .value(graphicsQueue.submitCountAdd()); + + VkSubmitInfo2.Buffer submitInfo = VkSubmitInfo2.calloc(1, stack) + .sType$Default() + .pWaitSemaphoreInfos(waitSemaphoreSubmitInfo) + .pSignalSemaphoreInfos(mainSemaphoreSubmitInfo) + .pCommandBufferInfos(commandBufferSubmitInfo); + + final int vkResult; + if ((vkResult = KHRSynchronization2.vkQueueSubmit2KHR(graphicsQueue.queue(), submitInfo, 0)) != VK_SUCCESS) { + throw new RuntimeException("Failed to submit draw command buffer: %s".formatted(VkResult.decode(vkResult))); + } + return submitId; + } + /** * Called in case draw results are needed before the of the frame */ @@ -371,21 +434,30 @@ public void flushCmds() { this.endRenderPass(currentCmdBuffer); vkEndCommandBuffer(currentCmdBuffer); + DeviceManager.getTransferQueue().waitSubmits(stack); + + Queue graphicsQueue = DeviceManager.getGraphicsQueue(); + + VkTimelineSemaphoreSubmitInfo mainSemaphoreSubmitInfo = VkTimelineSemaphoreSubmitInfo.calloc(stack) + .sType$Default() + .pWaitSemaphoreValues(stack.longs(graphicsQueue.submitCount())) + .pSignalSemaphoreValues(stack.longs(graphicsQueue.submitCountAdd())); + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); - + submitInfo.pNext(mainSemaphoreSubmitInfo); + submitInfo.waitSemaphoreCount(1); + submitInfo.pWaitSemaphores(stack.longs(graphicsQueue.getTmSemaphore())); + submitInfo.pSignalSemaphores(stack.longs(graphicsQueue.getTmSemaphore())); submitInfo.pCommandBuffers(stack.pointers(currentCmdBuffer)); - vkResetFences(device, inFlightFences.get(currentFrame)); - - waitFences(); + submitPending(); - if ((vkResult = vkQueueSubmit(DeviceManager.getGraphicsQueue().queue(), submitInfo, inFlightFences.get(currentFrame))) != VK_SUCCESS) { - vkResetFences(device, inFlightFences.get(currentFrame)); + if ((vkResult = vkQueueSubmit(DeviceManager.getGraphicsQueue().queue(), submitInfo, 0)) != VK_SUCCESS) { throw new RuntimeException("Failed to submit draw command buffer: %s".formatted(VkResult.decode(vkResult))); } - vkWaitForFences(device, inFlightFences.get(currentFrame), true, VUtil.UINT64_MAX); + graphicsQueue.waitSubmits(stack); this.beginRenderPass(stack); } @@ -433,11 +505,10 @@ public void addUsedPipeline(Pipeline pipeline) { public void removeUsedPipeline(Pipeline pipeline) { usedPipelines.remove(pipeline); } - - private void waitFences() { - // Make sure there are no uploads/transitions scheduled + //Synchronization fences are merged into vkQueueSubmit2 submit Barrier, reducing sync overhead and improving frametime + private void submitPending() { ImageUploadHelper.INSTANCE.submitCommands(); - Synchronization.INSTANCE.waitFences(); + Synchronization.INSTANCE.recycleCmdBuffers(); Vulkan.getStagingBuffer().reset(); } @@ -451,25 +522,9 @@ private void resetDescriptors() { boundPipelineHandle = 0; } - void waitForSwapChain() { - vkResetFences(device, inFlightFences.get(currentFrame)); - -// constexpr VkPipelineStageFlags t=VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - try (MemoryStack stack = MemoryStack.stackPush()) { - //Empty Submit - VkSubmitInfo info = VkSubmitInfo.calloc(stack) - .sType$Default() - .pWaitSemaphores(stack.longs(imageAvailableSemaphores.get(currentFrame))) - .pWaitDstStageMask(stack.ints(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); - - vkQueueSubmit(DeviceManager.getGraphicsQueue().queue(), info, inFlightFences.get(currentFrame)); - vkWaitForFences(device, inFlightFences.get(currentFrame), true, -1); - } - } - @SuppressWarnings("UnreachableCode") private void recreateSwapChain() { - waitFences(); + submitPending(); Vulkan.waitIdle(); commandBuffers.forEach(commandBuffer -> vkResetCommandBuffer(commandBuffer, 0)); @@ -518,7 +573,6 @@ public void cleanUpResources() { private void destroySyncObjects() { for (int i = 0; i < framesNum; ++i) { - vkDestroyFence(device, inFlightFences.get(i), null); vkDestroySemaphore(device, imageAvailableSemaphores.get(i), null); vkDestroySemaphore(device, renderFinishedSemaphores.get(i), null); } diff --git a/src/main/java/net/vulkanmod/vulkan/Synchronization.java b/src/main/java/net/vulkanmod/vulkan/Synchronization.java index 609052db3..c37b8d6d8 100644 --- a/src/main/java/net/vulkanmod/vulkan/Synchronization.java +++ b/src/main/java/net/vulkanmod/vulkan/Synchronization.java @@ -3,55 +3,24 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.vulkanmod.vulkan.queue.CommandPool; import net.vulkanmod.vulkan.util.VUtil; -import org.lwjgl.system.MemoryUtil; import org.lwjgl.vulkan.VkDevice; -import java.nio.LongBuffer; - import static org.lwjgl.vulkan.VK10.*; public class Synchronization { - private static final int ALLOCATION_SIZE = 50; - - public static final Synchronization INSTANCE = new Synchronization(ALLOCATION_SIZE); - private final LongBuffer fences; - private int idx = 0; + public static final Synchronization INSTANCE = new Synchronization(); - private ObjectArrayList commandBuffers = new ObjectArrayList<>(); + private final ObjectArrayList commandBuffers = new ObjectArrayList<>(); - Synchronization(int allocSize) { - this.fences = MemoryUtil.memAllocLong(allocSize); - } public synchronized void addCommandBuffer(CommandPool.CommandBuffer commandBuffer) { - this.addFence(commandBuffer.getFence()); this.commandBuffers.add(commandBuffer); } - public synchronized void addFence(long fence) { - if (idx == ALLOCATION_SIZE) - waitFences(); - - fences.put(idx, fence); - idx++; - } - - public synchronized void waitFences() { - if (idx == 0) - return; - - VkDevice device = Vulkan.getVkDevice(); - - fences.limit(idx); - - vkWaitForFences(device, fences, true, VUtil.UINT64_MAX); - + public synchronized void recycleCmdBuffers() { this.commandBuffers.forEach(CommandPool.CommandBuffer::reset); this.commandBuffers.clear(); - - fences.limit(ALLOCATION_SIZE); - idx = 0; } public static void waitFence(long fence) { diff --git a/src/main/java/net/vulkanmod/vulkan/Vulkan.java b/src/main/java/net/vulkanmod/vulkan/Vulkan.java index 9c99cc24d..10ff8a100 100644 --- a/src/main/java/net/vulkanmod/vulkan/Vulkan.java +++ b/src/main/java/net/vulkanmod/vulkan/Vulkan.java @@ -34,6 +34,7 @@ import static org.lwjgl.vulkan.EXTDebugUtils.*; import static org.lwjgl.vulkan.KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME; import static org.lwjgl.vulkan.KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME; +import static org.lwjgl.vulkan.KHRSynchronization2.VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME; import static org.lwjgl.vulkan.VK10.*; import static org.lwjgl.vulkan.VK12.VK_API_VERSION_1_2; @@ -60,6 +61,7 @@ public class Vulkan { } public static final Set REQUIRED_EXTENSION = getRequiredExtensionSet(); + public static final Set OPTIONAL_EXTENSION = new HashSet<>(List.of(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)); private static Set getRequiredExtensionSet() { ArrayList extensions = new ArrayList<>(List.of(VK_KHR_SWAPCHAIN_EXTENSION_NAME)); diff --git a/src/main/java/net/vulkanmod/vulkan/device/Device.java b/src/main/java/net/vulkanmod/vulkan/device/Device.java index da850d3b9..e5ee6f8f8 100644 --- a/src/main/java/net/vulkanmod/vulkan/device/Device.java +++ b/src/main/java/net/vulkanmod/vulkan/device/Device.java @@ -30,11 +30,12 @@ public class Device { public final VkPhysicalDeviceFeatures2 availableFeatures; public final VkPhysicalDeviceVulkan11Features availableFeatures11; + public final VkPhysicalDeviceVulkan12Features availableFeatures12; // public final VkPhysicalDeviceVulkan13Features availableFeatures13; // public final boolean vulkan13Support; - private boolean drawIndirectSupported; + private final boolean drawIndirectSupported, isTimelineSemaphoreSupported; public Device(VkPhysicalDevice device) { this.physicalDevice = device; @@ -53,7 +54,10 @@ public Device(VkPhysicalDevice device) { this.availableFeatures11 = VkPhysicalDeviceVulkan11Features.malloc(); this.availableFeatures11.sType$Default(); - this.availableFeatures.pNext(this.availableFeatures11); + + this.availableFeatures12 = VkPhysicalDeviceVulkan12Features.malloc(); + this.availableFeatures12.sType$Default(); + this.availableFeatures.pNext(this.availableFeatures11).pNext(this.availableFeatures12); //Vulkan 1.3 // this.availableFeatures13 = VkPhysicalDeviceVulkan13Features.malloc(); @@ -64,8 +68,9 @@ public Device(VkPhysicalDevice device) { vkGetPhysicalDeviceFeatures2(this.physicalDevice, this.availableFeatures); - if (this.availableFeatures.features().multiDrawIndirect() && this.availableFeatures11.shaderDrawParameters()) - this.drawIndirectSupported = true; + this.drawIndirectSupported = this.availableFeatures.features().multiDrawIndirect() && this.availableFeatures11.shaderDrawParameters(); + + this.isTimelineSemaphoreSupported = availableFeatures12.timelineSemaphore(); } @@ -146,6 +151,10 @@ public boolean isDrawIndirectSupported() { return drawIndirectSupported; } + public boolean isTimelineSemaphoreSupported() { + return isTimelineSemaphoreSupported; + } + // Added these to allow detecting GPU vendor, to allow handling vendor specific circumstances: // (e.g. such as in case we encounter a vendor specific driver bug) public boolean isAMD() { diff --git a/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java b/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java index 18fd202a0..7e7f5c611 100644 --- a/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java +++ b/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java @@ -12,6 +12,7 @@ import java.nio.IntBuffer; import java.util.ArrayList; import java.util.List; +import java.util.Set; import static java.util.stream.Collectors.toSet; import static net.vulkanmod.vulkan.queue.Queue.findQueueFamilies; @@ -25,6 +26,7 @@ import static org.lwjgl.vulkan.VK12.VK_API_VERSION_1_2; public abstract class DeviceManager { + private static final Set loadedExtensions = Vulkan.REQUIRED_EXTENSION; public static List availableDevices; public static List suitableDevices; @@ -154,6 +156,13 @@ else if (!otherDevices.isEmpty()) public static void createLogicalDevice() { try (MemoryStack stack = stackPush()) { + for (var extensionProperties : getAvailableExtension(stack, physicalDevice)) { + String extensionNameString = extensionProperties.extensionNameString(); + if (Vulkan.OPTIONAL_EXTENSION.contains(extensionNameString)) { + loadedExtensions.add(extensionNameString); + } + } + net.vulkanmod.vulkan.queue.Queue.QueueFamilyIndices indices = findQueueFamilies(physicalDevice); int[] uniqueQueueFamilies = indices.unique(); @@ -171,6 +180,10 @@ public static void createLogicalDevice() { deviceVulkan11Features.sType$Default(); deviceVulkan11Features.shaderDrawParameters(device.isDrawIndirectSupported()); + VkPhysicalDeviceVulkan12Features deviceVulkan12Features = VkPhysicalDeviceVulkan12Features.calloc(stack); + deviceVulkan12Features.sType$Default(); + deviceVulkan12Features.timelineSemaphore(device.isTimelineSemaphoreSupported()); + VkPhysicalDeviceFeatures2 deviceFeatures = VkPhysicalDeviceFeatures2.calloc(stack); deviceFeatures.sType$Default(); deviceFeatures.features().samplerAnisotropy(device.availableFeatures.features().samplerAnisotropy()); @@ -189,7 +202,16 @@ public static void createLogicalDevice() { createInfo.sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO); createInfo.pQueueCreateInfos(queueCreateInfos); createInfo.pEnabledFeatures(deviceFeatures.features()); - createInfo.pNext(deviceVulkan11Features); + createInfo.pNext(deviceVulkan11Features).pNext(deviceVulkan12Features); + + //Using Extension version of Sync2 to avoid raising min req to VK13 + if (loadedExtensions.contains(KHRSynchronization2.VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)) { + VkPhysicalDeviceSynchronization2Features synchronization2Features = VkPhysicalDeviceSynchronization2Features.calloc(stack); + synchronization2Features.sType$Default(); + synchronization2Features.synchronization2(true); + + createInfo.pNext(synchronization2Features); + } if (Vulkan.DYNAMIC_RENDERING) { VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeaturesKHR = VkPhysicalDeviceDynamicRenderingFeaturesKHR.calloc(stack); @@ -209,7 +231,7 @@ public static void createLogicalDevice() { // deviceVulkan13Features.pNext(deviceVulkan11Features.address()); } - createInfo.ppEnabledExtensionNames(asPointerBuffer(Vulkan.REQUIRED_EXTENSION)); + createInfo.ppEnabledExtensionNames(asPointerBuffer(loadedExtensions)); // Configuration.DEBUG_FUNCTIONS.set(true); @@ -229,6 +251,11 @@ public static void createLogicalDevice() { } } + //Allows checking for Extension availability (e.g. for feature specific code paths) + public static boolean checkExt(String ExtName) { + return loadedExtensions.contains(ExtName); + } + private static PointerBuffer getRequiredExtensions() { PointerBuffer glfwExtensions = glfwGetRequiredInstanceExtensions(); diff --git a/src/main/java/net/vulkanmod/vulkan/memory/MemoryTypes.java b/src/main/java/net/vulkanmod/vulkan/memory/MemoryTypes.java index 020670220..eaa4c52bd 100644 --- a/src/main/java/net/vulkanmod/vulkan/memory/MemoryTypes.java +++ b/src/main/java/net/vulkanmod/vulkan/memory/MemoryTypes.java @@ -84,12 +84,12 @@ public void copyFromBuffer(Buffer buffer, long bufferSize, ByteBuffer byteBuffer // TODO } - public long copyBuffer(Buffer src, Buffer dst) { + public void copyBuffer(Buffer src, Buffer dst) { if (dst.getBufferSize() < src.getBufferSize()) { throw new IllegalArgumentException("dst size is less than src size."); } - return DeviceManager.getTransferQueue().copyBufferCmd(src.getId(), 0, dst.getId(), 0, src.getBufferSize()); + DeviceManager.getTransferQueue().copyBufferCmd(src.getId(), 0, dst.getId(), 0, src.getBufferSize()); } @Override diff --git a/src/main/java/net/vulkanmod/vulkan/memory/buffer/StagingBuffer.java b/src/main/java/net/vulkanmod/vulkan/memory/buffer/StagingBuffer.java index d45835fff..f68d95bfd 100644 --- a/src/main/java/net/vulkanmod/vulkan/memory/buffer/StagingBuffer.java +++ b/src/main/java/net/vulkanmod/vulkan/memory/buffer/StagingBuffer.java @@ -57,7 +57,7 @@ private void submitUploads() { // Submit and wait all recorded uploads before resetting the buffer UploadManager.INSTANCE.submitUploads(); ImageUploadHelper.INSTANCE.submitCommands(); - Synchronization.INSTANCE.waitFences(); + Synchronization.INSTANCE.recycleCmdBuffers(); this.reset(); } diff --git a/src/main/java/net/vulkanmod/vulkan/queue/CommandPool.java b/src/main/java/net/vulkanmod/vulkan/queue/CommandPool.java index d6de2982e..63f7dc400 100644 --- a/src/main/java/net/vulkanmod/vulkan/queue/CommandPool.java +++ b/src/main/java/net/vulkanmod/vulkan/queue/CommandPool.java @@ -2,6 +2,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.vulkanmod.vulkan.Vulkan; +import net.vulkanmod.vulkan.util.VUtil; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.*; @@ -14,21 +15,18 @@ import static org.lwjgl.vulkan.VK10.*; public class CommandPool { - long id; + private final long id; private final List commandBuffers = new ObjectArrayList<>(); private final java.util.Queue availableCmdBuffers = new ArrayDeque<>(); CommandPool(int queueFamilyIndex) { - this.createCommandPool(queueFamilyIndex); - } - public void createCommandPool(int queueFamily) { try (MemoryStack stack = stackPush()) { VkCommandPoolCreateInfo poolInfo = VkCommandPoolCreateInfo.calloc(stack); poolInfo.sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO); - poolInfo.queueFamilyIndex(queueFamily); + poolInfo.queueFamilyIndex(queueFamilyIndex); poolInfo.flags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); LongBuffer pCommandPool = stack.mallocLong(1); @@ -62,22 +60,9 @@ private void allocateCommandBuffers(MemoryStack stack) { PointerBuffer pCommandBuffer = stack.mallocPointer(size); vkAllocateCommandBuffers(Vulkan.getVkDevice(), allocInfo, pCommandBuffer); - VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.calloc(stack); - fenceInfo.sType$Default(); - fenceInfo.flags(VK_FENCE_CREATE_SIGNALED_BIT); - - VkSemaphoreCreateInfo semaphoreCreateInfo = VkSemaphoreCreateInfo.calloc(stack); - semaphoreCreateInfo.sType$Default(); - for (int i = 0; i < size; ++i) { - LongBuffer pFence = stack.mallocLong(1); - vkCreateFence(Vulkan.getVkDevice(), fenceInfo, null, pFence); - - LongBuffer pSemaphore = stack.mallocLong(1); - vkCreateSemaphore(Vulkan.getVkDevice(), semaphoreCreateInfo, null, pSemaphore); - VkCommandBuffer vkCommandBuffer = new VkCommandBuffer(pCommandBuffer.get(i), Vulkan.getVkDevice()); - CommandBuffer commandBuffer = new CommandBuffer(this, vkCommandBuffer, pFence.get(0), pSemaphore.get(0)); + CommandBuffer commandBuffer = new CommandBuffer(this, vkCommandBuffer); commandBuffers.add(commandBuffer); availableCmdBuffers.add(commandBuffer); } @@ -88,10 +73,6 @@ public void addToAvailable(CommandBuffer commandBuffer) { } public void cleanUp() { - for (CommandBuffer commandBuffer : commandBuffers) { - vkDestroyFence(Vulkan.getVkDevice(), commandBuffer.fence, null); - vkDestroySemaphore(Vulkan.getVkDevice(), commandBuffer.semaphore, null); - } vkResetCommandPool(Vulkan.getVkDevice(), id, VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT); vkDestroyCommandPool(Vulkan.getVkDevice(), id, null); } @@ -103,25 +84,34 @@ public long getId() { public static class CommandBuffer { public final CommandPool commandPool; public final VkCommandBuffer handle; - public final long fence; - public final long semaphore; + public long submitId; //Emulates fence Functionality: boolean submitted; boolean recording; - public CommandBuffer(CommandPool commandPool, VkCommandBuffer handle, long fence, long semaphore) { + public CommandBuffer(CommandPool commandPool, VkCommandBuffer handle) { this.commandPool = commandPool; this.handle = handle; - this.fence = fence; - this.semaphore = semaphore; + this.submitId = 0; } public VkCommandBuffer getHandle() { return handle; } - public long getFence() { - return fence; + public long getSubmitId() { + return submitId; + } + + //Emulates functionality of vkWaitForFences() + public void wait(Queue queue, MemoryStack stack) { + VkSemaphoreWaitInfo vkSemaphoreWaitInfo = VkSemaphoreWaitInfo.calloc(stack); + vkSemaphoreWaitInfo.sType$Default(); + vkSemaphoreWaitInfo.semaphoreCount(1); + vkSemaphoreWaitInfo.pSemaphores(stack.longs(queue.getTmSemaphore())); + vkSemaphoreWaitInfo.pValues(stack.longs(this.submitId)); + + VK12.vkWaitSemaphores(Vulkan.getVkDevice(), vkSemaphoreWaitInfo, VUtil.UINT64_MAX); } public boolean isSubmitted() { @@ -142,26 +132,31 @@ public void begin(MemoryStack stack) { this.recording = true; } - public long submitCommands(MemoryStack stack, VkQueue queue, boolean useSemaphore) { - long fence = this.fence; + public void submitCommands(MemoryStack stack, Queue queue) { vkEndCommandBuffer(this.handle); - vkResetFences(Vulkan.getVkDevice(), this.fence); + long submitId = queue.submitCountAdd(); //Has same function as individual fence + + VkTimelineSemaphoreSubmitInfo timelineSemaphoreSubmitInfo = VkTimelineSemaphoreSubmitInfo.calloc(stack) + .sType$Default() + .pSignalSemaphoreValues(stack.longs(submitId)); + VkSubmitInfo submitInfo = VkSubmitInfo.calloc(stack); submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO); + submitInfo.pNext(timelineSemaphoreSubmitInfo); + submitInfo.pSignalSemaphores(stack.longs(queue.getTmSemaphore())); + submitInfo.pWaitDstStageMask(stack.ints(VK13.VK_PIPELINE_STAGE_NONE)); //No wait stage submitInfo.pCommandBuffers(stack.pointers(this.handle)); - if (useSemaphore) { - submitInfo.pSignalSemaphores(stack.longs(this.semaphore)); - } - - vkQueueSubmit(queue, submitInfo, fence); + vkQueueSubmit(queue.queue(), submitInfo, 0); this.recording = false; this.submitted = true; - return fence; + + + this.submitId = submitId; } public void reset() { diff --git a/src/main/java/net/vulkanmod/vulkan/queue/GraphicsQueue.java b/src/main/java/net/vulkanmod/vulkan/queue/GraphicsQueue.java index e449f617b..dcd0ab324 100644 --- a/src/main/java/net/vulkanmod/vulkan/queue/GraphicsQueue.java +++ b/src/main/java/net/vulkanmod/vulkan/queue/GraphicsQueue.java @@ -23,7 +23,7 @@ public void startRecording() { } public void endRecordingAndSubmit() { - long fence = submitCommands(currentCmdBuffer); + submitCommands(currentCmdBuffer); Synchronization.INSTANCE.addCommandBuffer(currentCmdBuffer); currentCmdBuffer = null; @@ -36,13 +36,12 @@ public CommandPool.CommandBuffer getCommandBuffer() { return beginCommands(); } } - - public long endIfNeeded(CommandPool.CommandBuffer commandBuffer) { + //true if submitted + public boolean endIfNeeded(CommandPool.CommandBuffer commandBuffer) { if (currentCmdBuffer != null) { - return VK_NULL_HANDLE; - } else { - return submitCommands(commandBuffer); + submitCommands(commandBuffer); } + return currentCmdBuffer != null; } } diff --git a/src/main/java/net/vulkanmod/vulkan/queue/Queue.java b/src/main/java/net/vulkanmod/vulkan/queue/Queue.java index c6bb61182..46c2707f1 100644 --- a/src/main/java/net/vulkanmod/vulkan/queue/Queue.java +++ b/src/main/java/net/vulkanmod/vulkan/queue/Queue.java @@ -3,14 +3,13 @@ import net.vulkanmod.Initializer; import net.vulkanmod.vulkan.Vulkan; import net.vulkanmod.vulkan.device.DeviceManager; +import net.vulkanmod.vulkan.util.VUtil; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.VkDevice; -import org.lwjgl.vulkan.VkPhysicalDevice; -import org.lwjgl.vulkan.VkQueue; -import org.lwjgl.vulkan.VkQueueFamilyProperties; +import org.lwjgl.vulkan.*; import java.nio.IntBuffer; +import java.nio.LongBuffer; import java.util.stream.IntStream; import static org.lwjgl.system.MemoryStack.stackPush; @@ -22,8 +21,10 @@ public abstract class Queue { private static QueueFamilyIndices queueFamilyIndices; private final VkQueue queue; + private final long tmSemaphore; - protected CommandPool commandPool; + private final CommandPool commandPool; + private long submits; public synchronized CommandPool.CommandBuffer beginCommands() { try (MemoryStack stack = stackPush()) { @@ -43,13 +44,30 @@ public synchronized CommandPool.CommandBuffer beginCommands() { vkGetDeviceQueue(DeviceManager.vkDevice, familyIndex, 0, pQueue); this.queue = new VkQueue(pQueue.get(0), DeviceManager.vkDevice); - if (initCommandPool) - this.commandPool = new CommandPool(familyIndex); + this.commandPool = initCommandPool ? new CommandPool(familyIndex) : null; + + this.tmSemaphore = initCommandPool ? getQueueSemaphore(stack) : VK_NULL_HANDLE; } - public synchronized long submitCommands(CommandPool.CommandBuffer commandBuffer) { + private static long getQueueSemaphore(MemoryStack stack) { + VkSemaphoreTypeCreateInfo semaphoreTypeCreateInfo = VkSemaphoreTypeCreateInfo.calloc(stack) + .sType$Default() + .semaphoreType(VK12.VK_SEMAPHORE_TYPE_TIMELINE) + .initialValue(0); + + VkSemaphoreCreateInfo semaphoreCreateInfo = VkSemaphoreCreateInfo.calloc(stack) + .sType$Default() + .pNext(semaphoreTypeCreateInfo); + + LongBuffer pPointer = stack.mallocLong(1); + + VK12.vkCreateSemaphore(Vulkan.getVkDevice(), semaphoreCreateInfo, null, pPointer); + return pPointer.get(0); + } + + public synchronized void submitCommands(CommandPool.CommandBuffer commandBuffer) { try (MemoryStack stack = stackPush()) { - return commandBuffer.submitCommands(stack, queue, false); + commandBuffer.submitCommands(stack, this); } } @@ -58,8 +76,10 @@ public VkQueue queue() { } public void cleanUp() { - if (commandPool != null) + if (commandPool != null) { commandPool.cleanUp(); + vkDestroySemaphore(Vulkan.getVkDevice(), this.tmSemaphore, null); + } } public void waitIdle() { @@ -70,6 +90,33 @@ public CommandPool getCommandPool() { return commandPool; } + public long submitCountAdd() { + return ++submits; + } + + public long submitCount() { + return submits; + } + + public long getTmSemaphore() { + return this.tmSemaphore; + } + + public void waitSubmits(MemoryStack stack) { + waitSubmits(stack, this.submits); + } + + //Functionally identical to Synchronisation.waitFences(), but for a specific queue + public void waitSubmits(MemoryStack stack, long submitId) { + + VkSemaphoreWaitInfo vkSemaphoreWaitInfo = VkSemaphoreWaitInfo.calloc(stack) + .sType$Default() + .semaphoreCount(1) + .pSemaphores(stack.longs(this.tmSemaphore)) + .pValues(stack.longs(submitId)); + VK12.vkWaitSemaphores(device, vkSemaphoreWaitInfo, VUtil.UINT64_MAX); + } + public enum Family { Graphics, Transfer, diff --git a/src/main/java/net/vulkanmod/vulkan/queue/TransferQueue.java b/src/main/java/net/vulkanmod/vulkan/queue/TransferQueue.java index 4cd17c4d6..a26b39862 100644 --- a/src/main/java/net/vulkanmod/vulkan/queue/TransferQueue.java +++ b/src/main/java/net/vulkanmod/vulkan/queue/TransferQueue.java @@ -2,7 +2,6 @@ import net.vulkanmod.vulkan.Synchronization; import net.vulkanmod.vulkan.Vulkan; -import net.vulkanmod.vulkan.util.VUtil; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.VkBufferCopy; import org.lwjgl.vulkan.VkCommandBuffer; @@ -19,7 +18,7 @@ public TransferQueue(MemoryStack stack, int familyIndex) { super(stack, familyIndex); } - public long copyBufferCmd(long srcBuffer, long srcOffset, long dstBuffer, long dstOffset, long size) { + public void copyBufferCmd(long srcBuffer, long srcOffset, long dstBuffer, long dstOffset, long size) { try (MemoryStack stack = stackPush()) { @@ -35,7 +34,6 @@ public long copyBufferCmd(long srcBuffer, long srcOffset, long dstBuffer, long d this.submitCommands(commandBuffer); Synchronization.INSTANCE.addCommandBuffer(commandBuffer); - return commandBuffer.fence; } } @@ -52,7 +50,7 @@ public void uploadBufferImmediate(long srcBuffer, long srcOffset, long dstBuffer vkCmdCopyBuffer(commandBuffer.getHandle(), srcBuffer, dstBuffer, copyRegion); this.submitCommands(commandBuffer); - vkWaitForFences(DEVICE, commandBuffer.fence, true, VUtil.UINT64_MAX); + commandBuffer.wait(this, stack); commandBuffer.reset(); } } diff --git a/src/main/java/net/vulkanmod/vulkan/texture/ImageUtil.java b/src/main/java/net/vulkanmod/vulkan/texture/ImageUtil.java index 6ec57980f..a4d72796a 100644 --- a/src/main/java/net/vulkanmod/vulkan/texture/ImageUtil.java +++ b/src/main/java/net/vulkanmod/vulkan/texture/ImageUtil.java @@ -51,8 +51,8 @@ public static void downloadTexture(VulkanImage image, long ptr) { image.height, 0, 0, 0, 0, 0); image.transitionImageLayout(stack, commandBuffer.getHandle(), prevLayout); - long fence = DeviceManager.getGraphicsQueue().submitCommands(commandBuffer); - vkWaitForFences(DeviceManager.vkDevice, fence, true, VUtil.UINT64_MAX); + DeviceManager.getGraphicsQueue().submitCommands(commandBuffer); + commandBuffer.wait(DeviceManager.getGraphicsQueue(), stack); MemoryManager.MapAndCopy(pStagingAllocation.get(0), (data) -> VUtil.memcpy(data.getByteBuffer(0, (int) imageSize), ptr)); @@ -209,9 +209,9 @@ public static void generateMipmaps(VulkanImage image) { image.setCurrentLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - long fence = DeviceManager.getGraphicsQueue().submitCommands(commandBuffer); + DeviceManager.getGraphicsQueue().submitCommands(commandBuffer); - vkWaitForFences(DeviceManager.vkDevice, fence, true, VUtil.UINT64_MAX); + commandBuffer.wait(DeviceManager.getGraphicsQueue(), stack); } } }